1var D=Object.defineProperty;var L=(r,s,t)=>s in r?D(r,s,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[s]=t;var o=(r,s,t)=>L(r,typeof s!="symbol"?s+"":s,t);import{r as g,j as e}from"./framer-motion-CQxuJa5n.js";import{s as C,n as O,u as F,b as I,p as U,C as S,f as T,g as A,h as M,i as R,B as N,A as $,M as P}from"./index-CSx7K-7R.js";import{V as K}from"./VoiceAgentPrivacyConsent-CQ0JH9Ts.js";import{M as J}from"./mic-off-mo8vRT9e.js";import{P as E}from"./phone-DfeYL_-y.js";import{P as Q}from"./phone-off-DWhMqWaf.js";import"./recharts-CFjfazL9.js";import"./alert-dialog-DED_7G_3.js";import"./checkbox-vurG2JZY.js";import"./index-srxnih4L.js";import"./check-BKeeD-3t.js";import"./scroll-area-THRv4UVm.js";import"./index-CwKDC_mg.js";import"./index-BdQq_4o_.js";import"./lock-CwA9FnEu.js";import"./globe-B3q7zUSQ.js";class B{constructor(s,t,c,p,x){o(this,"pc",null);o(this,"dc",null);o(this,"audioEl");o(this,"localStream",null);o(this,"firstMessage","Hej! Hvordan kan jeg hjælpe dig i dag?");o(this,"sessionReady",!1);o(this,"customKnowledge",[]);o(this,"currentResponseSource","ai_generated");o(this,"currentQueryText","");o(this,"responseStartTime",0);o(this,"analyser",null);o(this,"audioContext",null);o(this,"animationFrame",null);o(this,"metrics",{responseTimeMs:0,audioChunksReceived:0,knowledgeBaseHits:0,aiResponses:0,connectionQuality:"good",avgResponseTimeMs:0,totalExchanges:0,sessionStartTime:0,lastUpdateTime:0});o(this,"responseTimes",[]);o(this,"onMetricsUpdate");o(this,"onAudioLevelUpdate");this.agentId=s,this.onMessage=t,this.onSpeakingChange=c,this.audioEl=document.createElement("audio"),this.audioEl.autoplay=!0,this.onMetricsUpdate=p,this.onAudioLevelUpdate=x}getMetrics(){return{...this.metrics}}updateMetrics(){this.metrics.lastUpdateTime=Date.now(),this.onMetricsUpdate&&this.onMetricsUpdate({...this.metrics})}calculateConnectionQuality(){const s=this.metrics.avgResponseTimeMs;return s<500?"excellent":s<1e3?"good":s<2e3?"fair":"poor"}async init(s=!1){try{console.log("Initializing RealtimeChat for agent:",this.agentId,"Public test:",s);let t;if(s){const a=await fetch("https://lhplkrosdckrjrfeaoan.supabase.co/functions/v1/get-realtime-token",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({agentId:this.agentId})});if(!a.ok)throw new Error("Failed to get ephemeral token");t=await a.json()}else{const{data:a,error:n}=await C.functions.invoke("get-realtime-token",{body:{agentId:this.agentId}});if(n)throw n;t=a}if(!t?.client_secret?.value)throw new Error("Failed to get ephemeral token");const c=t.client_secret.value;if(t.agent_config?.first_message&&(this.firstMessage=t.agent_config.first_message),t.agent_config?.custom_knowledge&&(this.customKnowledge=t.agent_config.custom_knowledge),console.log("Got ephemeral token, first message, and",this.customKnowledge.length,"knowledge items"),this.pc=new RTCPeerConnection,this.pc.ontrack=async a=>{console.log("Received remote track"),this.audioEl.srcObject=a.streams[0]},this.localStream=await navigator.mediaDevices.getUserMedia({audio:{sampleRate:24e3,channelCount:1,echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0}}),this.pc.addTrack(this.localStream.getTracks()[0]),console.log("Added local audio track"),this.onAudioLevelUpdate){this.audioContext=new AudioContext({sampleRate:24e3});const a=this.audioContext.createMediaStreamSource(this.localStream);this.analyser=this.audioContext.createAnalyser(),this.analyser.fftSize=256,this.analyser.smoothingTimeConstant=.8,a.connect(this.analyser),this.startAudioLevelMonitoring()}this.dc=this.pc.createDataChannel("oai-events"),this.dc.addEventListener("message",async a=>{const n=JSON.parse(a.data);if(console.log("Received event:",n.type),this.onMessage(n),n.type==="conversation.item.input_audio_transcription.completed"&&(this.currentQueryText=n.transcript||"",this.responseStartTime=Date.now()),n.type==="response.audio.delta"&&(this.metrics.audioChunksReceived++,this.updateMetrics()),n.type==="response.function_call_arguments.done"){const m=n;if(m.name==="search_knowledge_base")try{const u=JSON.parse(m.arguments).query.toLowerCase();let y=null,f=0;for(const w of this.customKnowledge){const k=w.key.toLowerCase(),j=w.value.toLowerCase();let i=0;const l=u.split(" ");for(const _ of l)k.includes(_)&&(i+=3),j.includes(_)&&(i+=1);i>f&&(f=i,y=w)}y&&f>0?(this.currentResponseSource="knowledge_base",this.metrics.knowledgeBaseHits++,this.dc?.send(JSON.stringify({type:"conversation.item.create",item:{type:"function_call_output",call_id:m.call_id,output:JSON.stringify({found:!0,answer:y.value,source:"knowledge_base",confidence:f>5?"high":"medium"})}})),console.log("â Knowledge base hit:",y.key,"(score:",f,")")):(this.currentResponseSource="ai_generated",this.metrics.aiResponses++,this.dc?.send(JSON.stringify({type:"conversation.item.create",item:{type:"function_call_output",call_id:m.call_id,output:JSON.stringify({found:!1,message:"Ingen relevant information i videnbasen. Brug AI-viden."})}})),console.log("â ï¸ No knowledge match, using AI")),this.updateMetrics(),this.dc?.send(JSON.stringify({type:"response.create"}))}catch(h){console.error("Error searching knowledge base:",h)}}if(n.type==="response.done"){const m=this.responseStartTime>0?Date.now()-this.responseStartTime:0;m>0&&m<3e4&&(this.metrics.responseTimeMs=m,this.metrics.totalExchanges++,this.responseTimes.push(m),this.responseTimes.length>
150&&this.responseTimes.shift(),this.metrics.avgResponseTimeMs=Math.round(this.responseTimes.reduce((h,u)=>h+u,0)/this.responseTimes.length),this.metrics.connectionQuality=this.calculateConnectionQuality()),console.log("Response completed:",{source:this.currentResponseSource,time:m+"ms",query:this.currentQueryText,avgTime:this.metrics.avgResponseTimeMs+"ms",quality:this.metrics.connectionQuality}),this.updateMetrics(),this.currentQueryText="",this.currentResponseSource="ai_generated"}n.type==="session.created"&&!this.sessionReady&&(this.sessionReady=!0,console.log("Session created, sending first message:",this.firstMessage),this.dc?.send(JSON.stringify({type:"response.create",response:{modalities:["audio","text"],instructions:`Start samtalen med præcis denne sætning, og ingenting andet: "${this.firstMessage}"`}}))),n.type==="response.audio.delta"?this.onSpeakingChange(!0):n.type==="response.audio.done"&&this.onSpeakingChange(!1)});const p=await this.pc.createOffer();await this.pc.setLocalDescription(p),console.log("Created offer");const d=await fetch("https://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview-2024-12-17",{method:"POST",body:p.sdp,headers:{Authorization:`Bearer ${c}`,"Content-Type":"application/sdp"}});if(!d.ok)throw new Error(`Failed to connect to OpenAI: ${d.status}`);const b={type:"answer",sdp:await d.text()};await this.pc.setRemoteDescription(b),console.log("WebRTC connection established"),this.metrics.sessionStartTime=Date.now(),this.metrics.lastUpdateTime=Date.now(),this.updateMetrics()}catch(t){throw console.error("Error initializing chat:",t),t}}startAudioLevelMonitoring(){if(!this.analyser||!this.onAudioLevelUpdate)return;const s=new Uint8Array(this.analyser.frequencyBinCount),t=()=>{if(!this.analyser||!this.onAudioLevelUpdate)return;this.analyser.getByteFrequencyData(s);const c=s.reduce((x,v)=>x+v,0)/s.length,p=Math.min(100,c/255*100);this.onAudioLevelUpdate(p),this.animationFrame=requestAnimationFrame(t)};t()}disconnect(){this.animationFrame&&(cancelAnimationFrame(this.animationFrame),this.animationFrame=null),this.analyser&&(this.analyser.disconnect(),this.analyser=null),this.audioContext&&(this.audioContext.close(),this.audioContext=null),this.localStream&&(this.localStream.getTracks().forEach(s=>s.stop()),this.localStream=null),this.dc?.close(),this.pc?.close(),this.audioEl.srcObject&&(this.audioEl.srcObject.getTracks().forEach(t=>t.stop()),this.audioEl.srcObject=null),console.log("Disconnected and cleaned up all resources")}}function ce(){const{id:r}=O(),s=F(),{toast:t}=I(),[c,p]=g.useState(null),[x,v]=g.useState(!0),[d,b]=g.useState(!1),[a,n]=g.useState(!1),[m,h]=g.useState(!1),u=g.useRef(null);g.useEffect(()=>{f(),y()},[r]);const y=async()=>{if(r)try{await C.from("voice_agent_test_opens").insert({agent_id:r,referrer:document.referrer||null,user_agent:navigator.userAgent,source:document.referrer.includes("email")?"email":"direct",session_id:`session_${Date.now()}_${Math.random().toString(36).substr(2,9)}`})}catch(i){console.error("Error logging test open:",i)}},f=async()=>{if(!r){v(!1);return}try{console.log("Loading voice agent with id:",r);const{data:i,error:l}=await C.rpc("get_public_voice_agent",{p_agent_id:r});if(l)throw console.error("RPC error:",l),l;if(console.log("RPC result:",i),!i||i.length===0){console.warn("No voice agent found or agent is not active"),t({title:"Agent ikke tilgængelig",description:"Denne voice agent eksisterer ikke, er ikke aktiv, eller er blevet slettet",variant:"destructive"});return}console.log("Voice agent loaded successfully:",i[0].name),p(i[0])}catch(i){console.error("Error loading agent:",i),t({title:"Fejl",description:"Kunne ikke hente agent. Prøv venligst igen.",variant:"destructive"})}finally{v(!1)}},w=()=>{if(!(!c||!r))try{localStorage.getItem(`voice_privacy_accepted_${r}`)==="true"?k():h(!0)}catch(i){console.warn("Kunne ikke læse lokal godkendelse:",i),h(!0)}},k=async()=>{if(!(!c||!r))try{try{localStorage.setItem(`voice_privacy_accepted_${r}`,"true")}catch(l){console.warn("Kunne ikke gemme lokal godkendelse:",l)}const i=`session_${Date.now()}_${Math.random().toString(36).substr(2,9)}`;try{await fetch("https://lhplkrosdckrjrfeaoan.supabase.co/functions/v1/track-privacy-consent",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({agentId:r,sessionId:i})})}catch(l){console.error("Error tracking privacy consent:",l)}h(!1),t({title:"Forbinder...",description:"Vent venligst mens vi opretter forbindelse"}
1),u.current=new B(c.id,l=>{console.log("Agent event:",l),l.type==="response.audio.delta"?n(!0):l.type==="response.audio.done"&&n(!1)},n),await u.current.init(!0),b(!0),t({title:"â Forbundet",description:"Du kan nu tale med voice agenten"})}catch(i){console.error("Error connecting:",i),t({title:"Fejl",description:i.message||"Kunne ikke forbinde til agent",variant:"destructive"}),h(!1)}},j=()=>{u.current&&(u.current.disconnect(),u.current=null),b(!1),n(!1),t({title:"Afbrudt",description:"Forbindelsen er afbrudt"})};return g.useEffect(()=>()=>{u.current&&u.current.disconnect()},[]),x?e.jsx("div",{className:"min-h-screen flex items-center justify-center bg-gradient-to-br from-background via-background to-primary/5",children:e.jsx(U,{className:"w-12 h-12 animate-spin text-primary"})}):c?e.jsxs(e.Fragment,{children:[e.jsx(K,{open:m,onAccept:k,onDecline:()=>h(!1),agentName:c?.name||"Voice Agent",agentId:c?.id||""}),e.jsx("div",{className:"min-h-screen bg-gradient-to-br from-background via-background to-primary/5 p-3 sm:p-4 flex items-center justify-center",children:e.jsxs(S,{className:"max-w-2xl w-full shadow-2xl",children:[e.jsxs(T,{className:"text-center space-y-2 pb-6 sm:pb-8 px-4 sm:px-6",children:[e.jsx(A,{className:"text-2xl sm:text-3xl font-bold",children:c.name}),e.jsx(M,{className:"text-sm sm:text-base",children:"AI Voice Agent Test"})]}),e.jsxs(R,{className:"space-y-6 sm:space-y-8 pb-6 sm:pb-8 px-4 sm:px-6",children:[e.jsxs("div",{className:"flex flex-col items-center gap-4 sm:gap-6",children:[e.jsxs("div",{className:"relative w-48 h-48 sm:w-64 sm:h-64 flex items-center justify-center",children:[a&&e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"absolute inset-0 rounded-full bg-primary/40 animate-ping",style:{animationDuration:"1s"}}),e.jsx("div",{className:"absolute inset-4 rounded-full bg-primary/30 animate-ping",style:{animationDuration:"1.5s"}}),e.jsx("div",{className:"absolute inset-8 rounded-full bg-primary/20 animate-ping",style:{animationDuration:"2s"}})]}),d&&e.jsx("div",{className:`absolute inset-12 rounded-full transition-all duration-700 ${a?"bg-primary/30 shadow-[0_0_60px_rgba(132,204,22,0.5)] scale-110":"bg-primary/10 shadow-[0_0_30px_rgba(132,204,22,0.3)] scale-100"}`,style:{animation:a?"pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite":"none"}}),e.jsx("div",{className:`relative w-24 h-24 sm:w-32 sm:h-32 rounded-full flex items-center justify-center transition-all duration-500 ${a?"bg-primary scale-125 shadow-2xl shadow-primary/50":d?"bg-primary/80 scale-100":"bg-muted scale-90"}`,style:{transform:a?"scale(1.25) rotate(5deg)":"scale(1)",transition:"all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1)"},children:d?a?e.jsxs("div",{className:"relative",children:[e.jsx(P,{className:"w-12 h-12 sm:w-16 sm:h-16 text-white animate-bounce"}),e.jsx("div",{className:"absolute inset-0 w-12 h-12 sm:w-16 sm:h-16",children:e.jsx("div",{className:"absolute inset-0 rounded-full bg-white/30 animate-ping"})})]}):e.jsx(J,{className:"w-12 h-12 sm:w-16 sm:h-16 text-white"}):e.jsx(E,{className:"w-12 h-12 sm:w-16 sm:h-16 text-muted-foreground"})})]}),e.jsxs("div",{className:"text-center space-y-2 px-4",children:[e.jsx("p",{className:`text-xl sm:text-2xl font-bold ${a?"text-primary":d?"text-green-500":"text-muted-foreground"}`,children:a?"ð£ï¸ Agenten taler...":d?"ð Lytter - tal nu":"ð Klar til at starte"}),d&&!a&&e.jsx("p",{className:"text-xs sm:text-sm text-muted-foreground",children:"Tal tydeligt til mikrofonen"}),!d&&e.jsx("p",{className:"text-xs sm:text-sm text-muted-foreground",children:"Tryk pÃ¥ knappen for at starte samtalen"})]})]}),e.jsx("div",{className:"flex justify-center px-4",children:d?e.jsxs(N,{onClick:j,variant:"destructive",size:"lg",className:"px-6 py-5 sm:px-8 sm:py-6 text-base sm:text-lg w-full sm:w-auto transition-all duration-200 active:scale-95 active:shadow-lg active:brightness-110 touch-manipulation",style:{WebkitTapHighlightColor:"transparent"},children:[e.jsx(Q,{className:"w-5 h-5 sm:w-6 sm:h-6 mr-2"}),"Afslut"]}):e.jsxs(N,{onClick:w,size:"lg",className:"px-6 py-5 sm:px-8 sm:py-6 text-base sm:text-lg w-full sm:w-auto transition-all duration-200 active:scale-95 active:shadow-lg active:brightness-110 touch-manipulation",style:{WebkitTapHighlightColor:"transparent"},children:[e.jsx(E,{className:"w-5 h-5 sm:w-6 sm:h-6 mr-2"}),"Start samtale"]})}),e.jsxs("div",{className:"bg-muted/50 p-4 sm:p-6 rounded-lg space-y-2 sm:space-y-3 text-xs sm:text-sm mx-4",children:[e.jsxs("div",{className:"flex items-start gap-2",children:[e.jsx("div",{className:"w-2 h-2 rounded-full bg-primary mt-1.5"}),e.jsx("p",{children:"Tillad mikrofon adgang nÃ¥r browseren spørger"})]}),e.jsxs("div",{className:"flex items-start gap-2",children:[e.jsx("div",{className:"w-2 h-2 rounded-full bg-primary mt-1.5"}),e.jsx("p",{children:"Tal tydeligt og vent pÃ¥ agentens svar"})]}),e.jsxs("div",{className:"flex items-start gap-2",children:[e.jsx("div",{className:"w-2 h-2 rounded-full bg-primary mt-1.5"}),e.jsx("p",{children:"Forbindelsen er sikret med WebRTC"})]})]}),e.jsx("div",{className:"text-center text-xs text-muted-foreground pt-4 border-t mx-4",children:e.jsxs("p",{className:"break-words",children:["Baseret pÃ¥ information fra: ",e.jsx("span",{className:"font-semibold",children:c.url})]})})]})]})})]}):e.jsx("div",{className:"min-h-screen flex items-center justify-center bg-gradient-to-br from-background via-background to-primary/5 p-4",children:e.jsxs(S,{className:"max-w-md w-full",children:[e.jsxs(T,{children:[e.jsx(A,{children:"Agent ikke fundet"}),e.jsx(M,{children:"Denne voice agent eksisterer ikke eller er ikke aktiv"})]}),e.jsx(R,{children:e.jsxs(N,{onClick:()=>s("/"),className:"w-full",children:[e.jsx($,{className:"w-4 h-4 mr-2"}),"Tilbage til forsiden"]})})]})})}export{ce as default};
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.