PageSourceSearch

https://tastecle.com/assets/admin.assistant-DO7Oy3r8.js

js tastecle.com collected 2026-10-04 22:03:40 UTC 15,322 bytes, 1 lines download raw bytes

1import{d as T,aj as X,bP as F,r,l as I,j as e,au as oe,av as le,aw as ce,ax as de,ay as me,C as ue,a5 as G,at as he,i as k,s as pe}from"./index-Z8OYoERx.js";import{A as xe,B as H}from"./AdminLayout-cebij1I5.js";import{B as x}from"./button-CVYROETx.js";import{I as fe}from"./input-Be0Y1fTO.js";import{T as ge}from"./textarea-YtSeHJVd.js";import{B as ye}from"./badge-etkNkTb9.js";import{D as ve,a as je,b as we,c as Ne,d as be,e as ke}from"./dialog-BpxqFGz7.js";import{c as O,u as U,d as Te,a as Me,b as Se,e as Ae,f as qe,g as Ce,h as De,i as Ee}from"./assistant.functions-C7VXXqW2.js";import{P as Y}from"./plus-UCi9dswe.js";import{T as Z}from"./trash-2-5ryvPBeq.js";import{S as Pe}from"./sparkles-t1GM9hXc.js";import{X as Ie}from"./x-4eepEkSW.js";import{M as Ke}from"./index-CiyPGNmJ.js";import"./tooltip-C6WpO2yJ.js";import"./index-0oZPZrwO.js";import"./index-CgWPSUZS.js";import"./staff.functions-xonF4BMA.js";import"./chart-column-BtWwUrhJ.js";import"./shield-alert-DjiCrxvw.js";import"./file-text-BaWhqMxQ.js";import"./gift-DcxLIQBR.js";import"./store-BQlk9HrF.js";import"./calendar-days-Ba-n3f_-.js";import"./qr-code-D-4VJn_o.js";import"./users-BDcCVPIp.js";import"./handshake-CBjCdAFT.js";import"./truck-iTONpbPi.js";import"./refresh-cw-BF4WkChs.js";import"./trending-up-uP8L36RK.js";import"./triangle-alert-CsK-sZw0.js";import"./tag-GxaYkT32.js";import"./index-Ck2XWYsK.js";const Re=[["path",{d:"M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z",key:"l5xja"}],["path",{d:"M9 13a4.5 4.5 0 0 0 3-4",key:"10igwf"}],["path",{d:"M6.003 5.125A3 3 0 0 0 6.401 6.5",key:"105sqy"}],["path",{d:"M3.477 10.896a4 4 0 0 1 .585-.396",key:"ql3yin"}],["path",{d:"M6 18a4 4 0 0 1-1.967-.516",key:"2e4loj"}],["path",{d:"M12 13h4",key:"1ku699"}],["path",{d:"M12 18h6a2 2 0 0 1 2 2v1",key:"105ag5"}],["path",{d:"M12 8h8",key:"1lhi5i"}],["path",{d:"M16 8V5a2 2 0 0 1 2-2",key:"u6izg6"}],["circle",{cx:"16",cy:"13",r:".5",key:"ry7gng"}],["circle",{cx:"18",cy:"3",r:".5",key:"1aiba7"}],["circle",{cx:"20",cy:"21",r:".5",key:"yhc1fs"}],["circle",{cx:"20",cy:"8",r:".5",key:"1e43v0"}]],_e=T("brain-circuit",Re);const $e=[["path",{d:"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z",key:"18887p"}]],Qe=T("message-square",$e);const ze=[["path",{d:"M12 19v3",key:"npa21l"}],["path",{d:"M15 9.34V5a3 3 0 0 0-5.68-1.33",key:"1gzdoj"}],["path",{d:"M16.95 16.95A7 7 0 0 1 5 12v-2",key:"cqa7eg"}],["path",{d:"M18.89 13.23A7 7 0 0 0 19 12v-2",key:"16hl24"}],["path",{d:"m2 2 20 20",key:"1ooewy"}],["path",{d:"M9 9v3a3 3 0 0 0 5.12 2.12",key:"r2i35w"}]],Le=T("mic-off",ze);const Ve=[["path",{d:"M12 19v3",key:"npa21l"}],["path",{d:"M19 10v2a7 7 0 0 1-14 0v-2",key:"1vc78b"}],["rect",{x:"9",y:"2",width:"6",height:"13",rx:"3",key:"s6n7sd"}]],Be=T("mic",Ve);const Fe=[["path",{d:"M12 17v5",key:"bb1du9"}],["path",{d:"M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z",key:"1nkz8b"}]],J=T("pin",Fe),Ge=[{id:"google/gemini-3-flash-preview",label:"Gemini 3 Flash (fast, default)"},{id:"google/gemini-3.5-flash",label:"Gemini 3.5 Flash"},{id:"google/gemini-3.1-pro-preview",label:"Gemini 3.1 Pro"},{id:"google/gemini-2.5-pro",label:"Gemini 2.5 Pro"},{id:"openai/gpt-5.4-mini",label:"GPT-5.4 mini"},{id:"openai/gpt-5.4",label:"GPT-5.4"},{id:"openai/gpt-5.6-terra",label:"GPT-5.6 Terra (balanced)"},{id:"openai/gpt-5.6-sol",label:"GPT-5.6 Sol (flagship)"}];function Ts(){const o=X(),{thread:M}=F.useSearch(),d=F.useNavigate(),a=M??null,f=s=>{d({search:{thread:s??void 0},replace:!0})},[g,i]=r.useState("google/gemini-3-flash-preview"),[S,K]=r.useState(""),[c,R]=r.useState(!1),[b,A]=r.useState(!1),[W,_]=r.useState(!0),[q,h]=r.useState([]),$=r.useRef(null),Q=r.useRef(null),C=r.useRef(null),D=r.useRef(null),{data:m=[]}=I({queryKey:["assistant","threads"],queryFn:()=>qe()}),{data:E=[]}=I({queryKey:["assistant","messages",a],queryFn:()=>a?Ce({data:{thread_id:a}}):Promise.resolve([]),enabled:!!a}),{data:z=[]}=I({queryKey:["assistant","pending",a],queryFn:()=>a?De({data:{thread_id:a}}):Promise.resolve([]),enabled:!!a,refetchInterval:3e3}),{data:ee=[]}=I({queryKey:["assistant","memories"],queryFn:()=>Ee()});r.useEffect(()=>{const s=m.find(t=>
1t.id===a);s&&i(s.model)},[a,m]),r.useEffect(()=>{!a&&!c&&m.length>0&&d({search:{thread:m[0].id},replace:!0})},[a,c,m,d]);const P=r.useMemo(()=>[...E.filter(t=>t.role==="user"||t.role==="assistant").map(t=>({id:t.id,role:t.role,content:t.content})),...q],[E,q]);r.useEffect(()=>{const s=Q.current;s&&(s.scrollTop=s.scrollHeight)},[P,c]),r.useEffect(()=>{C.current?.focus()},[a,c]),r.useEffect(()=>{const s=q.find(t=>t.role==="assistant")?.content;s&&E.some(t=>t.role==="assistant"&&t.content===s)&&h([])},[E,q]),r.useEffect(()=>{if(typeof window>"u")return;const s=window;return _(!!(s.SpeechRecognition||s.webkitSpeechRecognition)),()=>{D.current?.stop(),D.current=null}},[]);async function se(){const s=await O({data:{model:g}});f(s.id),h([]),o.invalidateQueries({queryKey:["assistant","threads"]})}async function te(s){confirm("Delete this conversation? This can't be undone.")&&(await Te({data:{id:s}}),a===s&&(h([]),f(null)),o.invalidateQueries({queryKey:["assistant","threads"]}))}async function ae(s,t){await U({data:{id:s,pinned:!t}}),o.invalidateQueries({queryKey:["assistant","threads"]})}async function ie(s){i(s),a&&(await U({data:{id:a,model:s}}),o.invalidateQueries({queryKey:["assistant","threads"]}))}async function L(){const s=S.trim();if(!s||c)return;R(!0);let t=a;const u=!a;try{if(!t){const l=await O({data:{title:s.slice(0,80),model:g}});t=l.id,$.current=l.id,await d({search:{thread:l.id},replace:!0})}const n={role:"user",content:s},p=[...P,n];h(l=>[...l,n]),K("");const{data:y}=await pe.auth.getSession(),v=y.session?.access_token;if(!v)throw new Error("Not signed in");const j=await fetch("/api/chat-assistant",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${v}`},body:JSON.stringify({threadId:t,messages:p.map(l=>({role:l.role,content:l.content})),model:g})}),w=await j.json().catch(()=>null);if(!j.ok||!w?.threadId)throw new Error(w?.error??`Assistant request failed (${j.status})`);const N=w.text;N&&h(l=>[...l,{role:"assistant",content:N,id:`local-${Date.now()}`}]),await Promise.all([o.invalidateQueries({queryKey:["assistant","threads"]}),o.invalidateQueries({queryKey:["assistant","messages",w.threadId]}),o.invalidateQueries({queryKey:["assistant","pending",w.threadId]}),o.invalidateQueries({queryKey:["assistant","memories"]})])}catch(n){const p=n instanceof Error?n.message:String(n);k.error(p),h(y=>[...y,{role:"assistant",content:`I couldn't complete that request: ${p}`}]),t&&(u&&a!==t&&await d({search:{thread:t},replace:!0}),await Promise.all([o.invalidateQueries({queryKey:["assistant","threads"]}),o.invalidateQueries({queryKey:["assistant","messages",t]})]))}finally{$.current=null,R(!1),window.setTimeout(()=>C.current?.focus(),0)}}function re(){if(typeof window>"u")return;const s=D.current;if(s&&b){s.stop(),A(!1);return}const t=window,u=t.SpeechRecognition??t.webkitSpeechRecognition;if(!u){_(!1),k.error("Voice typing is not supported in this browser");return}const n=new u;n.continuous=!0,n.interimResults=!0,n.lang="en-US";let p="";const y=S.trimEnd();n.onresult=v=>{let j="";for(let N=v.resultIndex;N<v.results.length;N+=1){const l=v.results[N],B=l?.[0]?.transcript??"";l?.isFinal?p+=B:j+=B}K(`${y}${y?" ":""}${p}${j}`.trimStart())},n.onerror=()=>{A(!1),k.error("Voice typing stopped")},n.onend=()=>{A(!1),C.current?.focus()},D.current=n,n.start(),A(!0)}async function V(s,t){try{await Me({data:{id:s,decision:t}}),k.success(t==="approved"?"Approved":"Rejected"),o.invalidateQueries({queryKey:["assistant","pending",a]})}catch(u){k.error(u instanceof Error?u.message:String(u))}}const ne=m.find(s=>s.id===a);return e.jsx(xe,{title:"Assistant AI",description:"Your super agent for TasteCLE. It reads site data, drafts content, and queues mutating actions for your approval.",children:e.jsxs("div",{className:"grid h-[calc(100vh-16rem)] grid-cols-1 gap-4 md:grid-cols-[280px_1fr]",children:[e.jsxs("div",{className:"flex flex-col overflow-hidden rounded-lg border bg-card",children:[e.jsxs("div",{className:"flex items-center gap-2 border-b p-3",children:[e.jsxs(x,{size:"sm",onClick:se,className:"flex-1",children:[e.jsx(Y,{className:"mr-1 h-4 w-4"})," New chat"]}),e.jsx(He,{memories:ee})]}),e.jsxs("div",{className:"flex-1 overflow-y-auto",children:[m.length===0&&e.jsx("div",{className:"p-4 text-sm text-muted-foreground",children:"No conversations yet. Start a new chat."}),m.map(s=>e.jsxs("div",{className:`group flex items-start gap-2 border-b px-3 py-2 hover:bg-accent/50 ${s.id===a?"bg-accent":""}`,children:[e.jsxs("button",{onClick:()=>{h([]),f(s.id)},className:"flex-1 text-left",children:[e.jsxs("div",{className:"flex items-center gap-1.5",children:[s.pinned&&e.jsx(J,{className:"h-3 w-3 text-primary"}),e.jsx(Qe,{className:"h-3 w-3 text-muted-foreground"}),e.jsx("span",{className:"line-clamp-1 text-sm font-medium",children:s.title})]}),e.jsx("div",{className:"mt-0.5 text-xs text-muted-foreground",children:new Date(s.last_message_at).toLocaleStr
1ing()})]}),e.jsxs("div",{className:"flex flex-col gap-1 opacity-0 group-hover:opacity-100",children:[e.jsx("button",{onClick:()=>ae(s.id,s.pinned),className:"text-muted-foreground hover:text-primary",title:s.pinned?"Unpin":"Pin",children:e.jsx(J,{className:"h-3.5 w-3.5"})}),e.jsx("button",{onClick:()=>te(s.id),className:"text-muted-foreground hover:text-destructive",title:"Delete",children:e.jsx(Z,{className:"h-3.5 w-3.5"})})]})]},s.id))]})]}),e.jsxs("div",{className:"flex flex-col overflow-hidden rounded-lg border bg-card",children:[e.jsxs("div",{className:"flex items-center gap-2 border-b p-3",children:[e.jsx(H,{className:"h-5 w-5 text-primary"}),e.jsxs("div",{className:"flex-1",children:[e.jsx("div",{className:"text-sm font-semibold",children:ne?.title??"TasteCLE Super Assistant"}),e.jsx("div",{className:"text-xs text-muted-foreground",children:"Threaded chat with long-term memory across all conversations."})]}),e.jsxs(oe,{value:g,onValueChange:ie,children:[e.jsx(le,{className:"w-[220px]",children:e.jsx(ce,{})}),e.jsx(de,{children:Ge.map(s=>e.jsx(me,{value:s.id,children:s.label},s.id))})]})]}),z.filter(s=>s.status==="pending").length>0&&e.jsx("div",{className:"border-b bg-amber-50 dark:bg-amber-950/30",children:z.filter(s=>s.status==="pending").map(s=>e.jsxs("div",{className:"flex items-start gap-3 p-3",children:[e.jsx(Pe,{className:"mt-0.5 h-4 w-4 text-amber-600"}),e.jsxs("div",{className:"flex-1",children:[e.jsxs("div",{className:"text-xs font-semibold uppercase tracking-wide text-amber-700 dark:text-amber-300",children:["Approval needed · ",s.tool_name]}),e.jsx("div",{className:"mt-1 text-sm",children:s.preview})]}),e.jsxs("div",{className:"flex gap-1",children:[e.jsx(x,{size:"sm",variant:"default",onClick:()=>V(s.id,"approved"),children:e.jsx(ue,{className:"h-3.5 w-3.5"})}),e.jsx(x,{size:"sm",variant:"outline",onClick:()=>V(s.id,"rejected"),children:e.jsx(Ie,{className:"h-3.5 w-3.5"})})]})]},s.id))}),e.jsxs("div",{ref:Q,className:"flex-1 overflow-y-auto p-4",children:[P.length===0&&e.jsxs("div",{className:"mx-auto max-w-md py-12 text-center",children:[e.jsx(H,{className:"mx-auto mb-3 h-10 w-10 text-primary"}),e.jsx("div",{className:"text-lg font-semibold",children:"How can I help you run TasteCLE?"}),e.jsx("p",{className:"mt-2 text-sm text-muted-foreground",children:"Ask about listings, events, orders, or ask me to draft blog posts, schedule social, or comp tickets. I'll queue mutating actions for your approval."})]}),e.jsxs("div",{className:"mx-auto flex max-w-3xl flex-col gap-4",children:[P.map((s,t)=>e.jsx("div",{className:s.role==="user"?"flex justify-end":"flex justify-start",children:e.jsx("div",{className:`max-w-[85%] whitespace-pre-wrap rounded-lg px-3 py-2 text-sm ${s.role==="user"?"bg-primary text-primary-foreground":"bg-muted"}`,children:s.role==="assistant"?e.jsx("div",{className:"prose prose-sm max-w-none dark:prose-invert",children:e.jsx(Ke,{children:s.content})}):s.content})},s.id??`${s.role}-${t}`)),c&&e.jsx("div",{className:"flex justify-start",children:e.jsxs("div",{className:"rounded-lg bg-muted px-3 py-2 text-sm text-muted-foreground",children:[e.jsx(G,{className:"inline h-3.5 w-3.5 animate-spin"})," Thinking…"]})})]})]}),e.jsxs("div",{className:"border-t p-3",children:[e.jsxs("div",{className:"mx-auto flex max-w-3xl items-end gap-2",children:[e.jsx(ge,{ref:C,"aria-label":"Assistant message",value:S,onChange:s=>K(s.target.value),onKeyDown:s=>{s.key==="Enter"&&!s.shiftKey&&(s.preventDefault(),L())},placeholder:"Ask anything — the assistant can search listings, draft blog posts, schedule social, and more…",className:"min-h-[52px] flex-1 resize-none",disabled:c}),e.jsx(x,{type:"button",onClick:re,disabled:c||!W,variant:b?"default":"outline",size:"lg",title:b?"Stop voice typing":"Talk to text","aria-label":b?"Stop voice typing":"Talk to text",children:b?e.jsx(Le,{className:"h-4 w-4"}):e.jsx(Be,{className:"h-4 w-4"})}),e.jsx(x,{type:"button",onClick:L,disabled:c||!S.trim(),size:"lg",children:c?e.jsx(G,{className:"h-4 w-4 animate-spin"}):e.jsx(he,{className:"h-4 w-4"})})]}),e.jsx("div",{className:"mx-auto mt-1.5 max-w-3xl text-xs text-muted-foreground",children:"Enter to send · Shift+Enter for newline · Mic for talk to text"})]})]})]})})}function He({memories:o}){const M=X(),[d,a]=r.useState("");async function f(){const i=d.trim();i&&(await Se({data:{content:i,source:"manual",importance:4}}),a(""),M.invalidateQueries({queryKey:["assistant","memories"]}))}async function g(i){await Ae({data:{id:i}}),M.invalidateQueries({queryKey:["assistant","memories"]})}return e.jsxs(ve,{children:[e.jsx(je,{asChild:!0,children:e.jsx(x,{size:"icon",variant:"outline",title:"Long-term memory",children:e.jsx(_e,{className:"h-4 w-4"})})}),e.jsxs(we,{className:"max-w-2xl",children:[e.jsxs(Ne,{children:[e.jsx(be,{children:"Assistant memory"}),e.jsx(ke,{children:"Durable facts and preferences the assistant remembers across every new conversation. Auto-extracted after each chat, plus anything you pin here manually."})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(fe,{value:d,onChange:i=>a(i.target.value),placeholder:"Add a fact or preference the assistant should always remember…",onKeyDown:i=>{i.key==="Enter"&&(i.preventDefault(),f())}}),e.jsx(x,{onClick:f,children:e.jsx(Y,{className:"h-4 w-4"})})]}),e.jsxs("div",{className:"max-h-[50vh] space-y-2 overflow-y-auto",children:[o.length===0&&e.jsx("div",{className:"py-6 text-center text-sm text-muted-foreground",children:"No memories yet. The assistant will start extracting them from your chats."}),o.map(i=>e.jsxs("div",{className:"flex items-start gap-2 rounded-md border p-2",children:[e.jsx(ye,{variant:i.source==="manual"?"default":"secondary",className:"mt-0.5",children:i.category??i.source}),e.jsx("div",{className:"flex-1 text-sm",children:i.content}),e.jsx("button",{onClick:()=>g(i.id),className:"text-muted-foreground hover:text-destructive",children:e.jsx(Z,{className:"h-3.5 w-3.5"})})]},i.id))]})]})]})}export{Ts as component};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.