@kavchat/widget 0.0.0-stage → 0.0.1
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/LICENSE +21 -0
- package/README.md +162 -2
- package/dist/index.mjs +901 -0
- package/dist/kavchat.js +65 -0
- package/dist/types/api.d.ts +68 -0
- package/dist/types/boot.d.ts +7 -0
- package/dist/types/color.d.ts +15 -0
- package/dist/types/dom.d.ts +22 -0
- package/dist/types/embed.d.ts +1 -0
- package/dist/types/global.d.ts +36 -0
- package/dist/types/index.d.ts +11 -0
- package/dist/types/storage.d.ts +26 -0
- package/dist/types/styles.d.ts +5 -0
- package/dist/types/widget.d.ts +134 -0
- package/package.json +51 -4
package/dist/kavchat.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";(()=>{var $e=["he","en","ar","ru","es","fr","de","pt","it","tr"],Qe={ך:"כ",ם:"מ",ן:"נ",ף:"פ",ץ:"צ",أ:"ا",إ:"ا",آ:"ا",ٱ:"ا",ة:"ه",ى:"ي",ؤ:"و",ئ:"ي",ё:"е",ß:"ss",ı:"i"};function Ve(n){return n>=768&&n<=879||n>=1425&&n<=1479||n>=1552&&n<=1562||n>=1611&&n<=1631||n===1648||n>=1750&&n<=1773}function Ge(n){let e=n.charCodeAt(0);return e>=48&&e<=57||e>=1632&&e<=1641||e>=1488&&e<=1514||e>=1568&&e<=1610||e>=1649&&e<=1747||e>=19968&&e<=40959?!0:n.toLowerCase()!==n.toUpperCase()}var Ye=n=>{try{return n.normalize("NFD")}catch(e){return n}};function ne(n){var e;let t=Ye(String(n!=null?n:"").toLowerCase()),u="";for(let o of t){let r=o.charCodeAt(0);if(Ve(r))continue;let a=(e=Qe[o])!=null?e:o;u+=a.length===1&&!Ge(a)?" ":a}let i=u.replace(/\s+/g," ").trim();return i?i.split(" "):[]}var We={he:["איך","מה","אני","של","עם","את","זה","יש","אפשר","לי","על","או","גם","רוצה","צריך","האם","למה","מתי","איפה","כמה","אתם","שלי","לכם","אין","לא"],en:["how","what","i","my","me","the","a","an","is","are","do","does","can","to","of","in","on","for","with","and","or","it","you","your","want","need","please","when","where","why","which","there","this","that","be","get","not","no"],ar:["كيف","ماذا","ما","انا","في","على","من","الى","عن","هل","هذا","هذه","او","و","لي","متى","اين","لماذا","كم","اريد","ممكن","لا"],ru:["как","что","я","мне","мой","моя","в","на","с","и","или","не","это","где","когда","почему","сколько","можно","хочу","нужно","у","по","для","ли","есть"],es:["como","que","yo","mi","me","el","la","los","las","un","una","de","del","en","con","por","para","y","o","es","se","puedo","quiero","cuando","donde","cual","hay","no"],fr:["comment","que","quoi","je","mon","ma","mes","le","la","les","un","une","de","des","du","en","avec","pour","et","ou","est","il","elle","puis","veux","quand","ou","pourquoi","combien","ne","pas","j","l","d","qu"],de:["wie","was","ich","mein","meine","mir","der","die","das","ein","eine","und","oder","in","mit","fur","von","zu","ist","kann","mochte","wann","wo","warum","nicht","es","den","dem"],pt:["como","que","eu","meu","minha","o","a","os","as","um","uma","de","do","da","em","no","na","com","para","por","e","ou","posso","quero","quando","onde","porque","nao"],it:["come","che","cosa","io","mio","mia","il","lo","la","i","gli","le","un","una","di","del","della","in","con","per","e","o","posso","voglio","quando","dove","perche","non","l"],tr:["nasil","ne","ben","benim","bir","ve","veya","ile","icin","bu","su","mi","mu","nerede","ne zaman","neden","kac","istiyorum","var","yok","de","da"]},q={};for(let n of $e)q[n]=new Set(We[n].flatMap(e=>ne(e)));var Je=["ו","ה","ב","ל","מ","ש","כ"],Xe=["وال","بال","لل","كال","فال","ال","و","ب","ل","ف"],Ze=n=>n>=97&&n<=122||n>=224&&n<=591;function et(n){let e=n,t=e.charCodeAt(0);if(Ze(t))return e.length>3&&e.endsWith("s")&&!/(ss|us|is)$/.test(e)?e.slice(0,-1):e;if(t>=1488&&t<=1514){for(let u=0;u<2&&(e.length>3&&Je.includes(e[0]));u++)e=e.slice(1);return e}if(t>=1568&&t<=1610){for(let u of Xe)if(e.startsWith(u)&&e.length-u.length>=3)return e.slice(u.length)}return e}function ue(n,e="en"){var t;let u=(t=q[e])!=null?t:q.en,i=q.en;return ne(n).filter(o=>o.length>1&&!u.has(o)&&!i.has(o)).map(et)}function tt(n,e){if(n===e)return!0;if(Math.abs(n.length-e.length)>1||Math.min(n.length,e.length)<4)return!1;if(n.length===e.length){let o=[];for(let r=0;r<n.length&&o.length<=2;r++)n[r]!==e[r]&&o.push(r);if(o.length===2&&o[1]===o[0]+1&&n[o[0]]===e[o[1]]&&n[o[1]]===e[o[0]])return!0}let t=0,u=0,i=0;for(;t<n.length&&u<e.length;){if(n[t]===e[u]){t++,u++;continue}if(++i>1)return!1;n.length>e.length?t++:(e.length>n.length||t++,u++)}return i+(n.length-t)+(e.length-u)<=1}var ut=n=>String(n!=null?n:"").split(/\n\s*\n/).map(e=>e.trim()).filter(Boolean);function ie(n,e,t="en"){var u,i;let o=Array.from(new Set(ue(e,t)));if(o.length===0)return{kind:"none",matches:[]};let r=l=>l.flatMap(h=>ue(h,t)),a=[];for(let l of n){if(l.kind==="opening")continue;let h=l.content,w=new Map;for(let f of h.steps)for(let x of f.buttons){let g=x.action;g&&(g.type==="step"||g.type==="goto"&&!g.scriptId)&&w.set(g.stepId,[...(u=w.get(g.stepId))!=null?u:[],x.label])}for(let f of h.steps){let x=f.id===h.rootStepId,g=r([...f.keywords,...x?h.keywords:[]]),m=r([...x?[h.name]:[],...(i=w.get(f.id))!=null?i:[]]),d=0,v=[];for(let p of o)g.includes(p)?(d+=3,v.push({kind:"keyword",term:p})):m.includes(p)?(d+=2,v.push({kind:"title",term:p})):[...g,...m].some(k=>tt(p,k))&&(d+=.5,v.push({kind:"typo",term:p}));d>0&&a.push({scriptId:l.id,stepId:f.id,score:d,reasons:v})}}if(a.sort((l,h)=>h.score-l.score),a.length===0)return{kind:"none",matches:[]};let[c,b]=a;return!b||c.score>=b.score*2?{kind:"single",matches:[c]}:{kind:"multi",matches:a.filter(l=>l.score>=c.score/2).slice(0,3)}}var H={"chat.askAnswered":"Did that answer your question?","chat.yes":"Yes, thanks","chat.no":"No, I need help","chat.thanks":"Glad I could help!","chat.newQuestion":"New question","chat.back":"Back","chat.home":"Main menu","chat.didYouMean":"What did you mean?","chat.notFound":"I could not find an answer to that. Pick a topic from the menu, or talk to our team.","chat.notFoundTwice":"I could not find an answer. Our team will be happy to help, and your question is already in the form.","chat.contactChip":"Talk to our team","chat.openingContact":"Opening the contact form. This conversation will be attached to your request.","chat.chooseTopic":"How can I help?","chat.aiLabel":"AI answer","widget.open":"Open chat","widget.close":"Close chat","widget.placeholder":"Type your question...","widget.send":"Send","widget.poweredBy":"Powered by Kavchat","widget.offline":"You are offline. Showing saved answers.","widget.loading":"Loading...","widget.unavailable":"The assistant is not available right now.","form.title":"Contact us","form.name":"Name","form.email":"Email","form.phone":"Phone","form.topic":"Topic","form.message":"Message","form.submit":"Send","form.cancel":"Cancel","form.sending":"Sending...","form.sent":"Thanks! Your request #{number} was received. We will get back to you soon.","form.error":"Something went wrong. Please try again.","form.required":"Required","form.contactRequired":"Leave an email or a phone number so we can get back to you","form.emailInvalid":"Invalid email address","form.backToChat":"Back to chat"},nt={"chat.askAnswered":"קיבלת מענה?","chat.yes":"כן, תודה","chat.no":"לא, אני צריך עזרה","chat.thanks":"שמחתי לעזור!","chat.newQuestion":"שאלה חדשה","chat.back":"חזרה","chat.home":"תפריט ראשי","chat.didYouMean":"למה התכוונת?","chat.notFound":"לא מצאתי תשובה לשאלה הזו. אפשר לבחור נושא מהתפריט, או לפנות לצוות.","chat.notFoundTwice":"לא הצלחתי למצוא תשובה. הצוות ישמח לעזור, והשאלה שלך כבר תהיה כתובה בטופס.","chat.contactChip":"פנייה לצוות","chat.openingContact":"פותח את טופס הפנייה. מסלול השיחה יצורף לפנייה.","chat.chooseTopic":"במה אפשר לעזור?","chat.aiLabel":"תשובת AI","widget.open":"פתיחת הצ'אט","widget.close":"סגירת הצ'אט","widget.placeholder":"כתבו את השאלה...","widget.send":"שליחה","widget.poweredBy":"מופעל על ידי Kavchat","widget.offline":"אין חיבור לאינטרנט. מוצגות תשובות שמורות.","widget.loading":"טוען...","widget.unavailable":"העוזר לא זמין כרגע.","form.title":"פנייה לצוות","form.name":"שם","form.email":"אימייל","form.phone":"טלפון","form.topic":"נושא","form.message":"הודעה","form.submit":"שליחה","form.cancel":"ביטול","form.sending":"שולח...","form.sent":"תודה! פנייה מספר {number} התקבלה. נחזור אליכם בקרוב.","form.error":"משהו השתבש. נסו שוב.","form.required":"שדה חובה","form.contactRequired":"השאירו אימייל או טלפון כדי שנוכל לחזור אליכם","form.emailInvalid":"כתובת אימייל לא תקינה","form.backToChat":"חזרה לצ'אט"},it={"chat.askAnswered":"هل أجبت عن سؤالك؟","chat.yes":"نعم، شكرًا","chat.no":"لا، أحتاج مساعدة","chat.thanks":"سعدت بمساعدتك!","chat.newQuestion":"سؤال جديد","chat.back":"رجوع","chat.home":"القائمة الرئيسية","chat.didYouMean":"ماذا تقصد؟","chat.notFound":"لم أجد إجابة عن هذا السؤال. اختر موضوعًا من القائمة، أو تواصل مع فريقنا.","chat.notFoundTwice":"لم أتمكن من العثور على إجابة. يسعد فريقنا مساعدتك، وسؤالك مكتوب بالفعل في النموذج.","chat.contactChip":"تواصل مع الفريق","chat.openingContact":"جارٍ فتح نموذج التواصل. ستُرفق هذه المحادثة بطلبك.","chat.chooseTopic":"كيف يمكنني مساعدتك؟","chat.aiLabel":"إجابة من الذكاء الاصطناعي","widget.open":"فتح المحادثة","widget.close":"إغلاق المحادثة","widget.placeholder":"اكتب سؤالك...","widget.send":"إرسال","widget.poweredBy":"بدعم من Kavchat","widget.offline":"أنت غير متصل بالإنترنت. نعرض الإجابات المحفوظة.","widget.loading":"جارٍ التحميل...","widget.unavailable":"المساعد غير متاح حاليًا.","form.title":"تواصل معنا","form.name":"الاسم","form.email":"البريد الإلكتروني","form.phone":"الهاتف","form.topic":"الموضوع","form.message":"الرسالة","form.submit":"إرسال","form.cancel":"إلغاء","form.sending":"جارٍ الإرسال...","form.sent":"شكرًا! تم استلام طلبك رقم {number}. سنعود إليك قريبًا.","form.error":"حدث خطأ ما. يرجى المحاولة مرة أخرى.","form.required":"مطلوب","form.contactRequired":"اترك بريدًا إلكترونيًا أو رقم هاتف لنتمكن من الرد عليك","form.emailInvalid":"عنوان البريد الإلكتروني غير صالح","form.backToChat":"العودة إلى المحادثة"},ot={"chat.askAnswered":"Это ответило на ваш вопрос?","chat.yes":"Да, спасибо","chat.no":"Нет, нужна помощь","chat.thanks":"Рад помочь!","chat.newQuestion":"Новый вопрос","chat.back":"Назад","chat.home":"Главное меню","chat.didYouMean":"Что вы имели в виду?","chat.notFound":"Я не нашёл ответа на этот вопрос. Выберите тему в меню или напишите нашей команде.","chat.notFoundTwice":"Не удалось найти ответ. Наша команда с радостью поможет, а ваш вопрос уже добавлен в форму.","chat.contactChip":"Связаться с нами","chat.openingContact":"Открываю форму обращения. Этот диалог будет приложен к вашему запросу.","chat.chooseTopic":"Чем могу помочь?","chat.aiLabel":"Ответ ИИ","widget.open":"Открыть чат","widget.close":"Закрыть чат","widget.placeholder":"Введите ваш вопрос...","widget.send":"Отправить","widget.poweredBy":"Работает на Kavchat","widget.offline":"Нет подключения к интернету. Показываем сохранённые ответы.","widget.loading":"Загрузка...","widget.unavailable":"Ассистент сейчас недоступен.","form.title":"Связаться с нами","form.name":"Имя","form.email":"Email","form.phone":"Телефон","form.topic":"Тема","form.message":"Сообщение","form.submit":"Отправить","form.cancel":"Отмена","form.sending":"Отправка...","form.sent":"Спасибо! Ваше обращение №{number} получено. Мы скоро с вами свяжемся.","form.error":"Что-то пошло не так. Попробуйте ещё раз.","form.required":"Обязательное поле","form.contactRequired":"Укажите email или номер телефона, чтобы мы могли с вами связаться","form.emailInvalid":"Неверный адрес email","form.backToChat":"Вернуться в чат"},rt={"chat.askAnswered":"¿Resolvimos tu duda?","chat.yes":"Sí, gracias","chat.no":"No, necesito ayuda","chat.thanks":"¡Me alegra haberte ayudado!","chat.newQuestion":"Nueva pregunta","chat.back":"Atrás","chat.home":"Menú principal","chat.didYouMean":"¿Qué quisiste decir?","chat.notFound":"No encontré una respuesta para eso. Elige un tema del menú o habla con nuestro equipo.","chat.notFoundTwice":"No pude encontrar una respuesta. Nuestro equipo te ayudará con gusto, y tu pregunta ya está en el formulario.","chat.contactChip":"Hablar con el equipo","chat.openingContact":"Abriendo el formulario de contacto. Esta conversación se adjuntará a tu solicitud.","chat.chooseTopic":"¿En qué te puedo ayudar?","chat.aiLabel":"Respuesta de IA","widget.open":"Abrir chat","widget.close":"Cerrar chat","widget.placeholder":"Escribe tu pregunta...","widget.send":"Enviar","widget.poweredBy":"Con la tecnología de Kavchat","widget.offline":"Estás sin conexión. Mostrando respuestas guardadas.","widget.loading":"Cargando...","widget.unavailable":"El asistente no está disponible en este momento.","form.title":"Contáctanos","form.name":"Nombre","form.email":"Correo electrónico","form.phone":"Teléfono","form.topic":"Tema","form.message":"Mensaje","form.submit":"Enviar","form.cancel":"Cancelar","form.sending":"Enviando...","form.sent":"¡Gracias! Recibimos tu solicitud n.º {number}. Te responderemos pronto.","form.error":"Algo salió mal. Inténtalo de nuevo.","form.required":"Obligatorio","form.contactRequired":"Deja un correo o un teléfono para que podamos responderte","form.emailInvalid":"Correo electrónico no válido","form.backToChat":"Volver al chat"},at={"chat.askAnswered":"Avez-vous trouvé votre réponse ?","chat.yes":"Oui, merci","chat.no":"Non, aidez-moi","chat.thanks":"Ravi d'avoir pu vous aider !","chat.newQuestion":"Nouvelle question","chat.back":"Retour","chat.home":"Menu principal","chat.didYouMean":"Que vouliez-vous dire ?","chat.notFound":"Je n'ai pas trouvé de réponse à cette question. Choisissez un sujet dans le menu ou contactez notre équipe.","chat.notFoundTwice":"Je n'ai pas trouvé de réponse. Notre équipe se fera un plaisir de vous aider, et votre question figure déjà dans le formulaire.","chat.contactChip":"Contacter l'équipe","chat.openingContact":"Ouverture du formulaire de contact. Cette conversation sera jointe à votre demande.","chat.chooseTopic":"Comment puis-je vous aider ?","chat.aiLabel":"Réponse de l'IA","widget.open":"Ouvrir le chat","widget.close":"Fermer le chat","widget.placeholder":"Écrivez votre question...","widget.send":"Envoyer","widget.poweredBy":"Propulsé par Kavchat","widget.offline":"Vous êtes hors ligne. Affichage des réponses enregistrées.","widget.loading":"Chargement...","widget.unavailable":"L'assistant n'est pas disponible pour le moment.","form.title":"Nous contacter","form.name":"Nom","form.email":"E-mail","form.phone":"Téléphone","form.topic":"Sujet","form.message":"Message","form.submit":"Envoyer","form.cancel":"Annuler","form.sending":"Envoi en cours...","form.sent":"Merci ! Votre demande n° {number} a bien été reçue. Nous vous répondrons rapidement.","form.error":"Une erreur s'est produite. Veuillez réessayer.","form.required":"Obligatoire","form.contactRequired":"Indiquez un e-mail ou un numéro de téléphone pour que nous puissions vous répondre","form.emailInvalid":"Adresse e-mail invalide","form.backToChat":"Retour au chat"},st={"chat.askAnswered":"Hat Ihnen das weitergeholfen?","chat.yes":"Ja, danke","chat.no":"Nein, Hilfe bitte","chat.thanks":"Freut mich, dass ich helfen konnte!","chat.newQuestion":"Neue Frage","chat.back":"Zurück","chat.home":"Hauptmenü","chat.didYouMean":"Was meinten Sie?","chat.notFound":"Dazu habe ich leider keine Antwort gefunden. Wählen Sie ein Thema aus dem Menü oder wenden Sie sich an unser Team.","chat.notFoundTwice":"Ich konnte keine Antwort finden. Unser Team hilft Ihnen gerne weiter, und Ihre Frage steht bereits im Formular.","chat.contactChip":"Team kontaktieren","chat.openingContact":"Das Kontaktformular wird geöffnet. Dieser Chatverlauf wird Ihrer Anfrage beigefügt.","chat.chooseTopic":"Wie kann ich helfen?","chat.aiLabel":"KI-Antwort","widget.open":"Chat öffnen","widget.close":"Chat schließen","widget.placeholder":"Ihre Frage eingeben...","widget.send":"Senden","widget.poweredBy":"Bereitgestellt von Kavchat","widget.offline":"Sie sind offline. Gespeicherte Antworten werden angezeigt.","widget.loading":"Wird geladen...","widget.unavailable":"Der Assistent ist gerade nicht verfügbar.","form.title":"Kontakt","form.name":"Name","form.email":"E-Mail","form.phone":"Telefon","form.topic":"Thema","form.message":"Nachricht","form.submit":"Senden","form.cancel":"Abbrechen","form.sending":"Wird gesendet...","form.sent":"Vielen Dank! Ihre Anfrage Nr. {number} ist eingegangen. Wir melden uns in Kürze bei Ihnen.","form.error":"Etwas ist schiefgelaufen. Bitte versuchen Sie es erneut.","form.required":"Pflichtfeld","form.contactRequired":"Bitte geben Sie eine E-Mail-Adresse oder Telefonnummer an, damit wir uns melden können","form.emailInvalid":"Ungültige E-Mail-Adresse","form.backToChat":"Zurück zum Chat"},ct={"chat.askAnswered":"Isso respondeu à sua pergunta?","chat.yes":"Sim, obrigado","chat.no":"Não, preciso de ajuda","chat.thanks":"Que bom que pude ajudar!","chat.newQuestion":"Nova pergunta","chat.back":"Voltar","chat.home":"Menu principal","chat.didYouMean":"O que você quis dizer?","chat.notFound":"Não encontrei uma resposta para isso. Escolha um assunto no menu ou fale com a nossa equipe.","chat.notFoundTwice":"Não consegui encontrar uma resposta. Nossa equipe vai ajudar com prazer, e sua pergunta já está no formulário.","chat.contactChip":"Falar com a equipe","chat.openingContact":"Abrindo o formulário de contato. Esta conversa será anexada à sua solicitação.","chat.chooseTopic":"Como posso ajudar?","chat.aiLabel":"Resposta de IA","widget.open":"Abrir chat","widget.close":"Fechar chat","widget.placeholder":"Digite sua pergunta...","widget.send":"Enviar","widget.poweredBy":"Desenvolvido por Kavchat","widget.offline":"Você está offline. Mostrando respostas salvas.","widget.loading":"Carregando...","widget.unavailable":"O assistente não está disponível no momento.","form.title":"Fale conosco","form.name":"Nome","form.email":"E-mail","form.phone":"Telefone","form.topic":"Assunto","form.message":"Mensagem","form.submit":"Enviar","form.cancel":"Cancelar","form.sending":"Enviando...","form.sent":"Obrigado! Sua solicitação nº {number} foi recebida. Retornaremos em breve.","form.error":"Algo deu errado. Tente novamente.","form.required":"Obrigatório","form.contactRequired":"Informe um e-mail ou telefone para que possamos retornar","form.emailInvalid":"Endereço de e-mail inválido","form.backToChat":"Voltar ao chat"},lt={"chat.askAnswered":"Hai trovato la risposta?","chat.yes":"Sì, grazie","chat.no":"No, mi serve aiuto","chat.thanks":"Felice di esserti stato utile!","chat.newQuestion":"Nuova domanda","chat.back":"Indietro","chat.home":"Menu principale","chat.didYouMean":"Cosa intendevi?","chat.notFound":"Non ho trovato una risposta. Scegli un argomento dal menu o parla con il nostro team.","chat.notFoundTwice":"Non sono riuscito a trovare una risposta. Il nostro team sarà felice di aiutarti, e la tua domanda è già nel modulo.","chat.contactChip":"Parla con il team","chat.openingContact":"Apro il modulo di contatto. Questa conversazione sarà allegata alla tua richiesta.","chat.chooseTopic":"Come posso aiutarti?","chat.aiLabel":"Risposta dell'IA","widget.open":"Apri la chat","widget.close":"Chiudi la chat","widget.placeholder":"Scrivi la tua domanda...","widget.send":"Invia","widget.poweredBy":"Powered by Kavchat","widget.offline":"Sei offline. Mostro le risposte salvate.","widget.loading":"Caricamento...","widget.unavailable":"L'assistente non è disponibile al momento.","form.title":"Contattaci","form.name":"Nome","form.email":"Email","form.phone":"Telefono","form.topic":"Argomento","form.message":"Messaggio","form.submit":"Invia","form.cancel":"Annulla","form.sending":"Invio in corso...","form.sent":"Grazie! La tua richiesta n. {number} è stata ricevuta. Ti risponderemo presto.","form.error":"Qualcosa è andato storto. Riprova.","form.required":"Obbligatorio","form.contactRequired":"Lascia un'email o un numero di telefono per poterti ricontattare","form.emailInvalid":"Indirizzo email non valido","form.backToChat":"Torna alla chat"},dt={"chat.askAnswered":"Sorunuza yanıt bulabildiniz mi?","chat.yes":"Evet, teşekkürler","chat.no":"Hayır, yardım lazım","chat.thanks":"Yardımcı olabildiğime sevindim!","chat.newQuestion":"Yeni soru","chat.back":"Geri","chat.home":"Ana menü","chat.didYouMean":"Ne demek istediniz?","chat.notFound":"Bununla ilgili bir yanıt bulamadım. Menüden bir konu seçin veya ekibimizle görüşün.","chat.notFoundTwice":"Bir yanıt bulamadım. Ekibimiz size memnuniyetle yardımcı olur, sorunuz forma zaten eklendi.","chat.contactChip":"Ekibe ulaşın","chat.openingContact":"İletişim formu açılıyor. Bu konuşma talebinize eklenecek.","chat.chooseTopic":"Size nasıl yardımcı olabilirim?","chat.aiLabel":"Yapay zekâ yanıtı","widget.open":"Sohbeti aç","widget.close":"Sohbeti kapat","widget.placeholder":"Sorunuzu yazın...","widget.send":"Gönder","widget.poweredBy":"Kavchat ile desteklenmektedir","widget.offline":"Çevrimdışısınız. Kayıtlı yanıtlar gösteriliyor.","widget.loading":"Yükleniyor...","widget.unavailable":"Asistan şu anda kullanılamıyor.","form.title":"Bize ulaşın","form.name":"Ad","form.email":"E-posta","form.phone":"Telefon","form.topic":"Konu","form.message":"Mesaj","form.submit":"Gönder","form.cancel":"İptal","form.sending":"Gönderiliyor...","form.sent":"Teşekkürler! {number} numaralı talebiniz alındı. En kısa sürede size dönüş yapacağız.","form.error":"Bir sorun oluştu. Lütfen tekrar deneyin.","form.required":"Zorunlu","form.contactRequired":"Size dönüş yapabilmemiz için bir e-posta veya telefon numarası bırakın","form.emailInvalid":"Geçersiz e-posta adresi","form.backToChat":"Sohbete dön"},N={en:H,he:nt,ar:it,ru:ot,es:rt,fr:at,de:st,pt:ct,it:lt,tr:dt},pt=["he","ar"],oe=n=>pt.includes(re(n)),re=n=>String(n||"en").toLowerCase().split(/[-_]/)[0].replace(/^iw$/,"he");function O(n,e){var t;let u=(t=N[re(n)])!=null?t:H;return{...H,...u,...e!=null?e:{}}}var ae=(n,e)=>n.replace(/\{(\w+)\}/g,(t,u)=>e[u]!==void 0?String(e[u]):t),A={now:()=>Date.now()};function j(n){A={...A,...n}}var D=n=>O(n.locale,A.texts),$=()=>new Date(A.now()).toISOString();function se(n,e,t){let u=n.scripts[e],i=u==null?void 0:u.content.steps.find(o=>o.id===t);return u&&i?{script:u,step:i}:null}var Q=n=>Object.values(n.scripts).filter(e=>e.kind!=="opening").sort((e,t)=>e.sortOrder-t.sortOrder),ce=n=>Q(n).map(e=>({id:`script:${e.id}`,label:e.content.name,kind:"button",action:{type:"script",scriptId:e.id}})),I=n=>({id:"home",label:D(n)["chat.home"],kind:"home"}),U=n=>{let e=[];return n.history.length>0&&e.push({id:"back",label:D(n)["chat.back"],kind:"back"}),e.push(I(n)),e},L=(n,e)=>({id:"contact",label:D(n)["chat.contactChip"],kind:"contact",...e?{prefill:e}:{}});function E(n,e,t,u={}){return(e==="bot"?ut(t):[t]).reduce((o,r)=>({...o,seq:o.seq+1,messages:[...o.messages,{id:`m${o.seq+1}`,from:e,text:r,at:A.now(),...u}]}),n)}var le=(n,e,t,u)=>({...n,path:[...n.path,{scriptId:e,stepId:t,via:u,at:$()}]});function y(n,e){let t=n.scripts[n.openingId],u=t==null?void 0:t.content.steps.find(r=>r.id===t.content.rootStepId),i=n.userName?`, ${n.userName}`:"",o={...n,position:null,history:[],outcome:"open"};if(e==="start"){let r=((u==null?void 0:u.text)||D(n)["chat.chooseTopic"]).replace(/\{name\}/g,i);o=E(o,"bot",r,u!=null&&u.imageUrl?{imageUrl:u.imageUrl}:{})}else o=E(o,"bot",D(n)["chat.chooseTopic"]);return o=le(o,n.openingId,(u==null?void 0:u.id)||"welcome",e),{...o,chips:[...ce(o),L(o)]}}function C(n,e,t,u,i=!0){let o=se(n,e,t);if(!o)return y(n,"home");let{step:r}=o,a={...n,history:i&&n.position?[...n.history,n.position]:n.history,position:{scriptId:e,stepId:t},lastScriptId:e};if(a=E(a,"bot",r.text,{stepId:t,scriptId:e,...r.imageUrl?{imageUrl:r.imageUrl}:{}}),a=le(a,e,t,u),r.buttons.length>0){let c=r.buttons.map(b=>({id:`btn:${b.id}`,label:b.label,kind:"button",action:b.action}));return{...a,chips:[...c,...U(a)]}}return a=E(a,"bot",D(a)["chat.askAnswered"]),{...a,chips:[{id:"yes",label:D(a)["chat.yes"],kind:"yes"},{id:"no",label:D(a)["chat.no"],kind:"no"},...U(a)]}}function ht(n){return n.messages.map(e=>({from:e.from,text:e.text,...e.stepId?{stepId:e.stepId}:{},at:new Date(e.at).toISOString()}))}function de(n,e){let t=n.lastScriptId?n.scripts[n.lastScriptId]:null,u={type:"contact",contactTopicId:(t==null?void 0:t.content.contactTopicId)||null,scriptId:n.lastScriptId,prefill:e||"",transcript:ht(n)},i=E({...n,outcome:"contact"},"bot",D(n)["chat.openingContact"]);return{state:{...i,chips:[I(i)]},effect:u}}function pe(n){let e={scripts:Object.fromEntries(n.scripts.map(t=>[t.id,t])),openingId:n.openingScriptId,locale:n.locale||"en",userName:n.userName||"",messages:[],chips:[],position:null,history:[],path:[],unmatched:[],misses:0,outcome:"open",lastScriptId:null,lastMatch:null,seq:0};return y(e,"start")}function ft(n,e,t){var u;let i=(u=n.position)==null?void 0:u.scriptId;if(!e)return{state:y(n,"home")};switch(e.type){case"script":{let o=n.scripts[e.scriptId];return o?{state:C(n,o.id,o.content.rootStepId,"button")}:{state:y(n,"home")}}case"step":return{state:C(n,i||"",e.stepId,"button")};case"goto":return{state:C(n,e.scriptId||i||"",e.stepId,"button")};case"contact":return de(n);case"screen":case"link":return{state:{...n,chips:t.length?t:U(n)},effect:e};default:return{state:y(n,"home")}}}function he(n,e){let t={...E(n,"user",e.label),chips:[],lastMatch:null};switch(e.kind){case"home":return{state:y(t,"home")};case"back":{let u=t.history[t.history.length-1];return u?(t={...t,history:t.history.slice(0,-1),position:null},{state:C(t,u.scriptId,u.stepId,"back",!1)}):{state:y(t,"home")}}case"yes":return t=E({...t,outcome:"answered"},"bot",D(t)["chat.thanks"]),{state:{...t,chips:[{id:"new",label:D(t)["chat.newQuestion"],kind:"home"}]}};case"no":case"contact":return de(t,e.prefill);case"pick":return e.target?{state:C(t,e.target.scriptId,e.target.stepId,"text")}:{state:y(t,"home")};default:return ft(t,e.action,n.chips)}}function mt(n,e){let t=se(n,e.scriptId,e.stepId);if(!t)return"";let{script:u,step:i}=t;if(i.id===u.content.rootStepId)return u.content.name;for(let o of u.content.steps){let r=o.buttons.find(a=>a.action&&"stepId"in a.action&&a.action.stepId===i.id);if(r)return r.label}return i.text.slice(0,25)}function V(n,e){let t=String(e!=null?e:"").trim().slice(0,500);if(!t)return{state:n};let u={...E(n,"user",t),chips:[]},i=ie(Q(u),t,u.locale);if(u={...u,lastMatch:i},i.kind==="single"){let o=i.matches[0];return{state:{...C(u,o.scriptId,o.stepId,"text"),misses:0,lastMatch:i}}}if(i.kind==="multi"){u=E({...u,misses:0},"bot",D(u)["chat.didYouMean"]);let o=i.matches.map(r=>({id:`pick:${r.scriptId}:${r.stepId}`,label:mt(u,r),kind:"pick",target:{scriptId:r.scriptId,stepId:r.stepId}}));return{state:{...u,chips:[...o,I(u)]}}}return u={...u,misses:u.misses+1,unmatched:[...u.unmatched,{text:t,at:$()}]},u.misses>=2?(u=E(u,"bot",D(u)["chat.notFoundTwice"]),{state:{...u,chips:[L(u,t),I(u)]}}):(u=E(u,"bot",D(u)["chat.notFound"]),{state:{...u,chips:[...ce(u),L(u,t)]}})}var gt=300,Dt=600;function fe(n,e){let t=String(e!=null?e:"").trim().slice(0,500);return!t||ie(Q(n),t,n.locale).kind!=="none"?null:t.slice(0,gt)}function me(n,e){let t=String(e!=null?e:"").trim().slice(0,500);return t?{...E(n,"user",t),chips:[]}:n}function ge(n,e,t,u){let i=String(e!=null?e:"").trim().slice(0,500),o=String(t!=null?t:"").trim().slice(0,Dt);if(!i||!o)return V(n,e);let r={...E(n,"user",i),chips:[],lastMatch:{kind:"none",matches:[]},misses:0,unmatched:[...n.unmatched,{text:i,at:$()}]};r={...r,seq:r.seq+1,messages:[...r.messages,{id:`m${r.seq+1}`,from:"bot",text:o,at:A.now(),ai:!0}]};let a=u?r.scripts[u]:void 0,c=[];return a&&a.kind!=="opening"&&c.push({id:`script:${a.id}`,label:a.content.name,kind:"button",action:{type:"script",scriptId:a.id}}),{state:{...r,chips:[...c,L(r,i),I(r)]}}}function De(n,e,t,u){return{sessionId:e,releaseVersion:t,startedAt:u,outcome:n.outcome,path:n.path.slice(-200),unmatched:n.unmatched.slice(-20)}}var Ee=(n,e)=>`${n.apiBase.replace(/\/+$/,"")}/v1/client/${encodeURIComponent(n.publicKey)}/${e}`;function be(n,e={}){let t=n.getToken();return t?{...e,Authorization:`Bearer ${t}`}:e}async function ve(n,e){let t;try{t=await fetch(Ee(n,"release"),{method:"GET",cache:"no-store",credentials:"omit",headers:be(n,e?{"If-None-Match":e}:{})})}catch(i){return{kind:"offline"}}if(t.status===304)return{kind:"not-modified"};if(t.status>=500||t.status===429)return{kind:"offline"};if(!t.ok)return{kind:"denied",status:t.status};let u=await t.json().catch(()=>null);return!u||u.success!==!0||!u.data||typeof u.data.enabled!="boolean"?{kind:"offline"}:{kind:"ok",release:u.data,etag:t.headers.get("ETag")||""}}function z(n,e,t,u=!1,i){return fetch(Ee(n,e),{method:"POST",cache:"no-store",credentials:"omit",keepalive:u,signal:i,headers:be(n,{"Content-Type":"application/json"}),body:JSON.stringify(t)})}var Et=2e4;async function xe(n,e,t){let u=new AbortController,i=setTimeout(()=>u.abort(),Et);try{let o=await z(n,"ai/answer",{sessionId:e,question:t},!1,u.signal),r=o.ok&&(await o.json()).data;return r&&typeof r.answer=="string"&&r.answer.trim()?{answer:r.answer,scriptId:r.scriptId||null}:null}catch(o){return null}finally{clearTimeout(i)}}async function we(n,e,t=!1){try{return(await z(n,"events",e,t)).ok}catch(u){return!1}}async function ye(n,e){try{return(await z(n,"screens",{platform:"web",screens:e})).ok}catch(t){return!1}}async function ke(n,e){try{let t=await z(n,"tickets",e),u=await t.json().catch(()=>null);return t.ok&&u&&u.success&&u.data?{ok:!0,id:String(u.data.id),number:Number(u.data.number)}:{ok:!1,code:String((u==null?void 0:u.code)||`http_${t.status}`),message:String((u==null?void 0:u.message)||"")}}catch(t){return{ok:!1,code:"network",message:""}}}var bt="#2563eb",G="#111827",Y="#ffffff";function J(n){let e=/^#([0-9a-f]{6})$/i.exec(String(n||"").trim());if(!e)return null;let t=parseInt(e[1],16);return[t>>16&255,t>>8&255,t&255]}function Ce(n){let[e,t,u]=n.map(i=>{let o=i/255;return o<=.03928?o/12.92:Math.pow((o+.055)/1.055,2.4)});return .2126*e+.7152*t+.0722*u}function W(n,e){var i,o;let t=Ce((i=J(n))!=null?i:[0,0,0]),u=Ce((o=J(e))!=null?o:[0,0,0]);return(Math.max(t,u)+.05)/(Math.min(t,u)+.05)}function Ae(n){let e=J(n)?n.toLowerCase():bt,t=W(e,Y)>=W(e,G)?Y:G,u=W(e,Y)>=3?e:G;return{primary:e,onPrimary:t,accent:u}}function s(n,e,...t){let u=document.createElement(n);if(e)for(let[i,o]of Object.entries(e))o==null||o===!1||u.setAttribute(i,o===!0?"":o);return K(u,t),u}function K(n,e){for(let t of e)t==null||t===!1||n.appendChild(typeof t=="string"?document.createTextNode(t):t)}var Fe="http://www.w3.org/2000/svg";function F(n){let e=document.createElementNS(Fe,"svg");e.setAttribute("viewBox","0 0 24 24"),e.setAttribute("width","24"),e.setAttribute("height","24"),e.setAttribute("aria-hidden","true"),e.setAttribute("focusable","false");let t=document.createElementNS(Fe,"path");return t.setAttribute("d",n),t.setAttribute("fill","currentColor"),e.appendChild(t),e}var S={chat:"M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z",close:"M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z",send:"M2.01 21 23 12 2.01 3 2 10l15 2-15 2z"};function B(n,e){try{let t=new URL(String(n||"").trim());return e.includes(t.protocol)?t.href:null}catch(t){return null}}var X=/https:\/\/[^\s<>"'`]+/g,vt=/[.,;:!?)\]}'"»]+$/;function Se(n){let e=document.createDocumentFragment(),t=0;X.lastIndex=0;let u;for(;u=X.exec(n);){let i=u[0].replace(vt,""),o=B(i,["https:"]);if(!o||!i)continue;u.index>t&&e.appendChild(document.createTextNode(n.slice(t,u.index)));let r=s("a",{href:o,target:"_blank",rel:"noopener noreferrer"},i);e.appendChild(r),t=u.index+i.length,X.lastIndex=t}return t<n.length&&e.appendChild(document.createTextNode(n.slice(t))),e}var Te=n=>`kavchat:release:${n}`,Ie=n=>`kavchat:session:${n}`,Be=n=>`kavchat:screens:${n}`;function Z(n,e){try{let t=n().getItem(e);return t?JSON.parse(t):null}catch(t){return null}}function ee(n,e,t){try{n().setItem(e,JSON.stringify(t))}catch(u){}}var _=()=>window.localStorage,Re=()=>window.sessionStorage,Me=n=>Z(_,Te(n)),qe=(n,e)=>ee(_,Te(n),e),Le=n=>Z(_,Be(n)),Ne=(n,e)=>ee(_,Be(n),e),Oe=n=>Z(Re,Ie(n)),ze=(n,e)=>ee(Re,Ie(n),e),xt="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";function Ke(n=22){let e=new Uint8Array(n);if(typeof crypto!="undefined"&&crypto.getRandomValues)crypto.getRandomValues(e);else for(let u=0;u<n;u++)e[u]=Math.floor(Math.random()*256);let t="";for(let u=0;u<n;u++)t+=xt[e[u]&63];return t}var _e=`
|
|
2
|
+
:host{all:initial}
|
|
3
|
+
.kc{font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Noto Sans Hebrew","Noto Sans Arabic",Arial,sans-serif;font-size:15px;line-height:1.45;color:#1f2328;-webkit-font-smoothing:antialiased}
|
|
4
|
+
.kc *,.kc *::before,.kc *::after{box-sizing:border-box}
|
|
5
|
+
.kc [hidden]{display:none!important}
|
|
6
|
+
.kc button,.kc input,.kc select,.kc textarea{font:inherit;color:inherit;margin:0}
|
|
7
|
+
.kc :focus-visible{outline:2px solid var(--kc-accent);outline-offset:2px}
|
|
8
|
+
.launcher{position:fixed;z-index:2147483000;inset-block-end:20px;inset-inline-end:20px;width:56px;height:56px;border-radius:50%;border:0;padding:0;background:var(--kc-primary);color:var(--kc-on-primary);cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 6px 20px rgba(0,0,0,.22);transition:transform .15s ease}
|
|
9
|
+
.launcher:hover{transform:scale(1.05)}
|
|
10
|
+
.launcher svg{width:28px;height:28px}
|
|
11
|
+
.kc[data-pos=start] .launcher,.kc[data-pos=start] .panel{inset-inline-end:auto;inset-inline-start:20px}
|
|
12
|
+
.panel{position:fixed;z-index:2147483001;inset-block-end:88px;inset-inline-end:20px;width:380px;max-width:calc(100vw - 40px);height:620px;max-height:calc(100vh - 108px);display:flex;flex-direction:column;background:#fff;border-radius:16px;box-shadow:0 12px 40px rgba(0,0,0,.24);overflow:hidden;animation:kc-in .18s ease-out}
|
|
13
|
+
@keyframes kc-in{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}
|
|
14
|
+
.header{display:flex;align-items:center;gap:10px;padding:12px 14px;background:var(--kc-primary);color:var(--kc-on-primary);flex:none}
|
|
15
|
+
.avatar{width:38px;height:38px;border-radius:50%;flex:none;display:flex;align-items:center;justify-content:center;overflow:hidden;background:rgba(255,255,255,.22);font-weight:600;font-size:15px}
|
|
16
|
+
.avatar img{width:100%;height:100%;object-fit:cover}
|
|
17
|
+
.titles{min-width:0;flex:1}
|
|
18
|
+
.name{font-weight:600;font-size:16px;margin:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
19
|
+
.subtitle{font-size:13px;opacity:.85;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
20
|
+
.close{flex:none;width:36px;height:36px;border:0;border-radius:50%;background:transparent;color:inherit;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0}
|
|
21
|
+
.close:hover{background:rgba(127,127,127,.2)}
|
|
22
|
+
.close svg{width:22px;height:22px}
|
|
23
|
+
.banner{flex:none;background:#fff4e5;color:#7a4100;font-size:13px;padding:6px 14px}
|
|
24
|
+
.view{flex:1;min-height:0;display:flex;flex-direction:column;background:#f6f7f9}
|
|
25
|
+
.messages{flex:1;min-height:0;overflow-y:auto;padding:16px 14px 8px;display:flex;flex-direction:column;gap:6px;overscroll-behavior:contain}
|
|
26
|
+
.msg{max-width:85%;padding:9px 13px;border-radius:16px;white-space:pre-wrap;overflow-wrap:anywhere}
|
|
27
|
+
.msg-bot{align-self:flex-start;background:#fff;border:1px solid #e3e6ea;border-end-start-radius:4px}
|
|
28
|
+
.msg-user{align-self:flex-end;background:var(--kc-primary);color:var(--kc-on-primary);border-end-end-radius:4px}
|
|
29
|
+
.msg-user+.msg-bot,.msg-bot+.msg-user{margin-block-start:6px}
|
|
30
|
+
.msg a{color:inherit;text-decoration:underline}
|
|
31
|
+
.ai{display:block;font-size:11px;font-weight:600;color:#6b7280}
|
|
32
|
+
.typing{color:#9ca3af;font-weight:700;animation:kc-dot 1s infinite}
|
|
33
|
+
@keyframes kc-dot{50%{opacity:.3}}
|
|
34
|
+
.msg img{display:block;max-width:100%;height:auto;border-radius:10px;margin-block-end:6px}
|
|
35
|
+
.chips{flex:none;display:flex;flex-wrap:wrap;gap:6px;padding:4px 14px 10px;max-height:40%;overflow-y:auto}
|
|
36
|
+
.chip{border:1px solid var(--kc-accent);background:#fff;color:var(--kc-accent);border-radius:999px;padding:6px 13px;cursor:pointer;font-size:14px;line-height:1.3;text-align:start}
|
|
37
|
+
.chip:hover{background:var(--kc-primary);color:var(--kc-on-primary);border-color:var(--kc-primary)}
|
|
38
|
+
.chip-home,.chip-back{border-color:#cfd4da;color:#4b5563}
|
|
39
|
+
.composer{flex:none;display:flex;align-items:center;gap:8px;padding:8px 10px;border-block-start:1px solid #e3e6ea;background:#fff}
|
|
40
|
+
.composer input{flex:1;min-width:0;border:0;outline:0;background:transparent;padding:8px 6px;font-size:16px}
|
|
41
|
+
.kc .composer input:focus-visible{outline:0}
|
|
42
|
+
.composer:focus-within{box-shadow:inset 0 2px 0 var(--kc-accent)}
|
|
43
|
+
.send{flex:none;width:40px;height:40px;border:0;border-radius:50%;background:var(--kc-primary);color:var(--kc-on-primary);cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0}
|
|
44
|
+
.send svg{width:20px;height:20px}
|
|
45
|
+
.kc[dir=rtl] .send svg{transform:scaleX(-1)}
|
|
46
|
+
.form{flex:1;min-height:0;overflow-y:auto;padding:16px 16px 12px;background:#fff;display:flex;flex-direction:column;gap:10px}
|
|
47
|
+
.form h2{margin:0 0 4px;font-size:17px;font-weight:600}
|
|
48
|
+
.field label{display:block;font-size:13px;color:#4b5563;margin-block-end:3px}
|
|
49
|
+
.field input,.field select,.field textarea{display:block;width:100%;border:1px solid #cfd4da;border-radius:8px;padding:8px 10px;background:#fff;font-size:16px}
|
|
50
|
+
.field textarea{min-height:96px;resize:vertical}
|
|
51
|
+
.field input:focus,.field select:focus,.field textarea:focus{border-color:var(--kc-accent);outline:0;box-shadow:0 0 0 2px color-mix(in srgb,var(--kc-accent) 25%,transparent)}
|
|
52
|
+
.error{color:#b42318;font-size:14px}
|
|
53
|
+
.actions{display:flex;gap:8px;flex-wrap:wrap;margin-block-start:4px}
|
|
54
|
+
.btn{border-radius:8px;padding:9px 16px;cursor:pointer;border:1px solid #cfd4da;background:#fff}
|
|
55
|
+
.btn-primary{background:var(--kc-primary);color:var(--kc-on-primary);border-color:var(--kc-primary)}
|
|
56
|
+
.btn:disabled{opacity:.6;cursor:default}
|
|
57
|
+
.sent{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;padding:24px;text-align:center;background:#fff}
|
|
58
|
+
.badge{flex:none;display:block;text-align:center;font-size:12px;color:#6b7280;padding:6px;background:#fff;border-block-start:1px solid #eef0f2;text-decoration:none}
|
|
59
|
+
.badge:hover{color:#1f2328}
|
|
60
|
+
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
|
|
61
|
+
@media (max-width:480px){.panel,.kc[data-pos=start] .panel{inset:0;width:100%;max-width:none;height:100%;max-height:none;border-radius:0}}
|
|
62
|
+
@media (prefers-reduced-motion:reduce){.kc *{animation:none!important;transition:none!important}}
|
|
63
|
+
`;var wt="https://api.kavchat.com",yt="https://kavchat.com",kt=2e3,Ct=/^[A-Za-z0-9_.-]{1,40}$/,At=10080*60*1e3;function Ft(n){let e=new Set,t=[];for(let u of n!=null?n:[]){let i=typeof(u==null?void 0:u.id)=="string"?u.id:"",o=typeof(u==null?void 0:u.label)=="string"?u.label.trim().slice(0,60):"";if(!(!Ct.test(i)||!o||e.has(i))&&(e.add(i),t.push({id:i,label:o,needsTarget:u.needsTarget===!0}),t.length===100))break}return t}function St(n){let e=[...n].sort((u,i)=>u.id<i.id?-1:u.id>i.id?1:0).map(u=>`${u.id}|${u.label}|${u.needsTarget?1:0}`).join(`
|
|
64
|
+
`),t=2166136261;for(let u=0;u<e.length;u++)t^=e.charCodeAt(u),t=Math.imul(t,16777619)>>>0;return t.toString(16)}var Pe=n=>String(n||"").toLowerCase().split(/[-_]/)[0].replace(/^iw$/,"he");function Tt(n,e){let t=Pe(n||(typeof navigator!="undefined"?navigator.language:"")||"");if(N[t])return t;let u=Pe(e);return N[u]?u:"en"}var It=/^[^\s@]+@[^\s@]+\.[^\s@]+$/,P=class{constructor(e){this.release=null;this.offline=!1;this.ui=null;this.state=null;this.session=null;this.t=O("en");this.locale="en";this.isOpen=!1;this.prevFocus=null;this.reportTimer=null;this.dirty=!1;this.destroyed=!1;this.waiting=!1;this.onPageHide=()=>this.flush(!0);this.onVisibility=()=>{document.visibilityState==="hidden"&&this.flush(!0)};if(!e||!e.publicKey)throw new Error("Kavchat: publicKey is required");this.opts={...e},this.wantOpen=!!e.open,this.api={apiBase:e.apiBase||wt,publicKey:e.publicKey,getToken:()=>this.opts.userToken||void 0},window.addEventListener("pagehide",this.onPageHide),document.addEventListener("visibilitychange",this.onVisibility),this.ready=this.load()}get shadowRoot(){return this.ui?this.ui.root:null}get opened(){return this.isOpen}open(){this.wantOpen=!0;let e=this.ui;!e||this.isOpen||(this.isOpen=!0,this.prevFocus=document.activeElement,this.state||this.startFresh(),e.panel.hidden=!1,e.launcher.setAttribute("aria-expanded","true"),e.launcher.setAttribute("aria-label",this.t["widget.close"]),e.launcher.replaceChildren(F(S.close)),this.render(),this.focusInside(),this.emit({type:"open"}))}close(){this.wantOpen=!1;let e=this.ui;if(!e||!this.isOpen)return;this.isOpen=!1,e.panel.hidden=!0,e.launcher.setAttribute("aria-expanded","false"),e.launcher.setAttribute("aria-label",this.t["widget.open"]),e.launcher.replaceChildren(F(S.chat));let t=this.prevFocus;t&&t!==e.host&&t.isConnected&&typeof t.focus=="function"?t.focus():e.launcher.focus(),this.emit({type:"close"})}toggle(){this.isOpen?this.close():this.open()}setUser(e){return this.opts.userToken=(e==null?void 0:e.token)||void 0,this.opts.userName=(e==null?void 0:e.name)||void 0,this.ready=this.load(),this.ready}destroy(){this.destroyed||(this.flush(!0),this.destroyed=!0,this.reportTimer&&clearTimeout(this.reportTimer),window.removeEventListener("pagehide",this.onPageHide),document.removeEventListener("visibilitychange",this.onVisibility),this.unmount())}async load(){var r,a;let e=this.opts.publicKey,t=Me(e),u=await ve(this.api,(t==null?void 0:t.etag)||void 0);if(this.destroyed)return;let i=null,o=!1;u.kind==="ok"?(qe(e,{etag:u.etag,release:u.release}),i=u.release):u.kind==="not-modified"?i=(r=t==null?void 0:t.release)!=null?r:null:u.kind==="offline"&&(i=(a=t==null?void 0:t.release)!=null?a:null,o=!!i),this.apply(i,o),this.registerScreens()}async registerScreens(){let e=Ft(this.opts.screens);if(e.length===0)return;let t=this.opts.publicKey,u=St(e),i=Le(t);i&&i.fp===u&&Date.now()-i.at<At||await ye(this.api,e)&&Ne(t,{fp:u,at:Date.now()})}apply(e,t){if(!e||!e.enabled){this.release=null,this.state=null,this.isOpen=!1,this.unmount(),this.emit({type:"unavailable",reason:e&&"reason"in e?e.reason:void 0});return}this.offline=t;let u=!this.release||this.release.version!==e.version;this.release=e,this.locale=Tt(this.opts.locale,e.locale),this.t=O(this.locale),(u||!this.ui)&&(this.state=null,this.restoreSession(),this.mount()),this.ui&&(this.ui.banner.hidden=!this.offline),this.emit({type:"ready",version:e.version,offline:t}),this.wantOpen&&this.open()}restoreSession(){let e=this.release;if(!e)return;let t=Oe(this.opts.publicKey);if(t&&t.version===e.version&&t.state){let u={};for(let i of e.scripts)u[i.id]=i;this.session=t,this.state={...t.state,scripts:u}}else this.session=null}run(e){return j({texts:this.t}),e()}startFresh(){let e=this.release;e&&(j({texts:this.t}),this.session={id:Ke(),startedAt:new Date().toISOString(),version:e.version,state:null},this.setState(pe({scripts:e.scripts,openingScriptId:e.openingScriptId,locale:e.locale,userName:this.opts.userName})))}setState(e){if(this.state=e,this.session){let{scripts:t,...u}=e;this.session.state=u,ze(this.opts.publicKey,this.session)}this.render()}onChip(e){if(!this.state||this.waiting)return;let t=this.state,u=this.run(()=>he(t,e));this.afterAction(u)}async onSend(e){var a;if(!this.state||!e.trim()||this.waiting)return;let t=this.state,u=this.session,i=(a=this.release)!=null&&a.settings.aiAnswers&&u?fe(t,e):null;if(!i||!u)return this.afterAction(this.run(()=>V(t,e)));let o=me(t,e);this.state=o,this.waiting=!0,this.render();let r=await xe(this.api,u.id,i);if(this.waiting=!1,this.destroyed||this.state!==o)return this.render();this.afterAction(this.run(()=>ge(t,e,r&&r.answer,r&&r.scriptId)))}afterAction(e){this.setState(e.state),this.scheduleReport(),e.effect&&this.handleEffect(e.effect)}handleEffect(e){var t,u,i;if(this.emit({type:"effect",effect:e}),e.type==="contact")this.showForm(e);else if(e.type==="link")Mt(e.url,(t=this.ui)==null?void 0:t.root);else if(e.type==="screen")try{(i=(u=this.opts).onScreen)==null||i.call(u,e.screen,e.targetId)}catch(o){console.error("Kavchat: onScreen failed",o)}}scheduleReport(){this.dirty=!0,this.reportTimer&&clearTimeout(this.reportTimer),this.reportTimer=setTimeout(()=>this.flush(!1),kt)}flush(e){if(this.reportTimer&&(clearTimeout(this.reportTimer),this.reportTimer=null),!this.dirty||!this.state||!this.release||!this.session||this.destroyed)return;this.dirty=!1;let t=De(this.state,this.session.id,this.release.version,this.session.startedAt);we(this.api,t,e).then(u=>{u||(this.dirty=!0)})}emit(e){var t,u;try{(u=(t=this.opts).onEvent)==null||u.call(t,e)}catch(i){console.error("Kavchat: onEvent failed",i)}}unmount(){this.ui&&this.ui.host.remove(),this.ui=null,this.isOpen=!1}mount(){let e=this.release;if(!e)return;this.unmount();let t=this.t,u=e.settings,i=Ae(u.primaryColor),o=s("div",{"data-kavchat":""}),r=o.attachShadow({mode:"open"}),a=s("div",{class:"kc",dir:oe(this.locale)?"rtl":"ltr",lang:this.locale,"data-pos":u.position==="start"?"start":"end"});a.style.setProperty("--kc-primary",i.primary),a.style.setProperty("--kc-on-primary",i.onPrimary),a.style.setProperty("--kc-accent",i.accent);let c=s("button",{type:"button",class:"launcher","aria-label":t["widget.open"],"aria-expanded":"false","aria-controls":"kc-panel"},F(S.chat));c.addEventListener("click",()=>this.toggle());let b=u.avatarImageUrl?B(u.avatarImageUrl,["https:"]):null,l=s("div",{class:"avatar","aria-hidden":"true"},b?s("img",{src:b,alt:""}):(u.avatarText||u.botName||"").slice(0,2)),h=s("button",{type:"button",class:"close","aria-label":t["widget.close"]},F(S.close));h.addEventListener("click",()=>this.close());let w=s("div",{class:"header"},l,s("div",{class:"titles"},s("h2",{class:"name",id:"kc-title"},u.botName||""),u.subtitle?s("div",{class:"subtitle"},u.subtitle):null),h),f=s("div",{class:"banner",role:"status"},t["widget.offline"]);f.hidden=!this.offline;let x=s("div",{class:"messages"}),g=s("div",{class:"chips",role:"group"}),m=s("input",{type:"text",dir:"auto",maxlength:"500",autocomplete:"off",placeholder:t["widget.placeholder"],"aria-label":t["widget.placeholder"],enterkeyhint:"send"}),d=s("form",{class:"composer"},m,s("button",{type:"submit",class:"send","aria-label":t["widget.send"]},F(S.send)));d.addEventListener("submit",M=>{M.preventDefault();let je=m.value;m.value="",this.onSend(je)});let v=s("div",{class:"view"},x,g,d),p=s("div",{class:"view"});p.hidden=!0;let k=s("div",{class:"sr-only","aria-live":"polite","aria-atomic":"false"}),R=s("div",{class:"msg msg-bot typing","aria-hidden":"true"},"..."),T=s("div",{class:"panel",id:"kc-panel",role:"dialog","aria-labelledby":"kc-title",tabindex:"-1"},w,f,v,p,k);u.removeBranding||T.appendChild(s("a",{class:"badge",href:yt,target:"_blank",rel:"noopener noreferrer"},t["widget.poweredBy"])),T.hidden=!0,T.addEventListener("keydown",M=>{M.key==="Escape"&&(M.stopPropagation(),this.close())}),K(a,[c,T]),K(r,[s("style",null,_e),a]),(this.opts.container||document.body).appendChild(o),this.ui={host:o,root:r,wrap:a,launcher:c,panel:T,banner:f,chatView:v,messages:x,chips:g,input:m,formView:p,live:k,typing:R,rendered:0},this.state&&this.renderMessages(!1)}focusInside(){let e=this.ui;if(!e)return;let t=typeof window.matchMedia=="function"&&window.matchMedia("(pointer: coarse)").matches;e.formView.hidden?t?e.panel.focus():e.input.focus():(e.formView.querySelector("input,select,textarea")||e.panel).focus()}render(){!this.ui||!this.state||(this.renderMessages(!0),this.renderChips())}renderMessages(e){let t=this.ui,u=this.state;if(!t||!u)return;t.rendered>u.messages.length&&(t.messages.textContent="",t.rendered=0);let i=[];for(let o=t.rendered;o<u.messages.length;o++){let r=u.messages[o];t.messages.appendChild(Rt(r,this.t)),r.from==="bot"&&i.push(r.ai?`${this.t["chat.aiLabel"]}: ${r.text}`:r.text)}t.rendered=u.messages.length,this.waiting?t.messages.appendChild(t.typing):t.typing.remove(),e&&i.length&&(t.live.textContent=i.join(`
|
|
65
|
+
`)),t.messages.scrollTop=t.messages.scrollHeight}renderChips(){let e=this.ui,t=this.state;if(!(!e||!t)){e.chips.textContent="";for(let u of t.chips){let i=s("button",{type:"button",class:`chip chip-${u.kind}`},u.label);i.addEventListener("click",()=>this.onChip(u)),e.chips.appendChild(i)}e.chips.hidden=t.chips.length===0}}showChat(){let e=this.ui;e&&(e.formView.hidden=!0,e.formView.textContent="",e.chatView.hidden=!1,this.render(),this.isOpen&&this.focusInside())}showForm(e){let t=this.ui,u=this.release;if(!t||!u)return;let i=this.t,o=u.settings.contactTopics||[],r=(m,d,v)=>s("div",{class:"field"},s("label",{for:m},d),v),a=s("input",{id:"kc-name",type:"text",autocomplete:"name",maxlength:"100",dir:"auto"});a.value=this.opts.userName||"";let c=s("input",{id:"kc-email",type:"email",autocomplete:"email",dir:"auto"}),b=s("input",{id:"kc-phone",type:"tel",autocomplete:"tel",dir:"auto"}),l=null;if(o.length){l=s("select",{id:"kc-topic"},s("option",{value:""},""));for(let m of o)l.appendChild(s("option",{value:m.id},m.name));e.contactTopicId&&o.some(m=>m.id===e.contactTopicId)&&(l.value=e.contactTopicId)}let h=s("textarea",{id:"kc-message",required:!0,maxlength:"2000",dir:"auto"});h.value=e.prefill||"";let w=s("div",{class:"error",role:"alert"}),f=s("button",{type:"submit",class:"btn btn-primary"},i["form.submit"]),x=s("button",{type:"button",class:"btn"},i["form.cancel"]);x.addEventListener("click",()=>this.showChat());let g=s("form",{class:"form",novalidate:!0},s("h2",null,i["form.title"]),r("kc-name",i["form.name"],a),r("kc-email",i["form.email"],c),r("kc-phone",i["form.phone"],b),l?r("kc-topic",i["form.topic"],l):null,r("kc-message",i["form.message"],h),w,s("div",{class:"actions"},f,x));g.addEventListener("submit",async m=>{var k;m.preventDefault();let d={message:h.value.trim(),name:a.value.trim()||void 0,email:c.value.trim()||void 0,phone:b.value.trim()||void 0,topicId:(l==null?void 0:l.value)||void 0,scriptId:e.scriptId||void 0,sessionId:(k=this.session)==null?void 0:k.id,transcript:e.transcript.slice(-200).map(R=>({...R,text:R.text.slice(0,1e3)}))},v=d.message?!d.email&&!d.phone&&!this.opts.userToken?i["form.contactRequired"]:d.email&&!It.test(d.email)?i["form.emailInvalid"]:"":i["form.required"];if(v){w.textContent=v;return}w.textContent="",f.disabled=!0,f.textContent=i["form.sending"];let p=await ke(this.api,d);this.destroyed||(f.disabled=!1,f.textContent=i["form.submit"],p.ok?(this.flush(!1),this.emit({type:"ticket",id:p.id,number:p.number}),this.showSent(p.number)):w.textContent=Bt(p.code,p.message,i))}),t.formView.replaceChildren(g),t.chatView.hidden=!0,t.formView.hidden=!1,this.isOpen&&this.focusInside()}showSent(e){let t=this.ui;if(!t)return;let u=this.t,i=s("button",{type:"button",class:"btn btn-primary"},u["form.backToChat"]);i.addEventListener("click",()=>this.showChat());let o=s("p",{role:"status"},ae(u["form.sent"],{number:e}));t.formView.replaceChildren(s("div",{class:"sent"},o,i)),i.focus()}};function Bt(n,e,t){return n==="ticket_contact_required"?t["form.contactRequired"]:n==="email_invalid"?t["form.emailInvalid"]:n==="network"?t["form.error"]:e||t["form.error"]}function Rt(n,e){let t=s("div",{class:`msg msg-${n.from}`,dir:"auto"});n.ai&&t.appendChild(s("span",{class:"ai"},e["chat.aiLabel"]));let u=n.from==="bot"&&n.imageUrl?B(n.imageUrl,["https:"]):null;return u&&t.appendChild(s("img",{src:u,alt:"",loading:"lazy"})),t.appendChild(n.from==="bot"&&!n.ai?Se(n.text):document.createTextNode(n.text)),t}function Mt(n,e){let t=B(n,["https:","tel:"]);if(!t)return!1;let u=s("a",{href:t,target:"_blank",rel:"noopener noreferrer"});return u.style.display="none",(e||document.body).appendChild(u),u.click(),u.remove(),!0}var qt=["init","open","close","toggle","setUser","destroy"];function He(n=window){let e=n.Kavchat;if(e&&e.loaded)return{api:e,queue:[]};let t=Array.isArray(e==null?void 0:e.q)?e.q.filter(Array.isArray):[],u=null,i,o,r={loaded:!0,init(a){u&&u.destroy();let c={...a};return o!==void 0&&(c.open=o),i!==void 0&&(c.userToken=(i==null?void 0:i.token)||void 0,c.userName=(i==null?void 0:i.name)||void 0),o=i=void 0,u=new P(c),u.ready},open(){u?u.open():o=!0},close(){u?u.close():o=!1},toggle(){u?u.toggle():o=!o},setUser(a){u?u.setUser(a):i=a},destroy(){u&&u.destroy(),u=null},q:{push:(...a)=>(a.forEach(c=>te(r,c)),0)},get widget(){return u}};return n.Kavchat=r,{api:r,queue:t}}function te(n,e){let[t,...u]=e;if(!qt.includes(t)){console.warn(`Kavchat: unknown method ${t}`);return}try{let i=n[t](...u);i&&typeof i.catch=="function"&&i.catch(o=>console.error("Kavchat:",o))}catch(i){console.error("Kavchat:",i)}}function Lt(n){if(!n)return null;let e=i=>n.getAttribute(i)||void 0,t=e("data-key");if(!t)return null;let u={publicKey:t};return e("data-api")&&(u.apiBase=e("data-api")),e("data-locale")&&(u.locale=e("data-locale")),e("data-user-token")&&(u.userToken=e("data-user-token")),e("data-user-name")&&(u.userName=e("data-user-name")),e("data-open")==="true"&&(u.open=!0),u}function Nt(){return document.currentScript||document.querySelector('script[data-key][src*="kavchat"]')||document.querySelector("script[data-key]")}function Ue(n=Nt()){let{api:e,queue:t}=He(),u=Lt(n),i=t.findIndex(r=>r[0]==="init"),o=t.filter((r,a)=>a!==i);return i>=0?o.unshift(["init",{...u,...t[i][1]}]):u&&o.unshift(["init",u]),o.forEach(r=>te(e,r)),e}try{Ue()}catch(n){console.error("Kavchat:",n)}})();
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ClientRelease, TranscriptEntry } from '@kavchat/core';
|
|
2
|
+
/**
|
|
3
|
+
* קריאות ל-Client API
|
|
4
|
+
* כלל CORS: בלי כותרות מותאמות (Cache-Control, Pragma וכו'). רק Authorization, Content-Type
|
|
5
|
+
* ו-If-None-Match, שהשרת מתיר. מניעת מטמון נעשית עם cache: 'no-store'
|
|
6
|
+
*/
|
|
7
|
+
export interface ApiConfig {
|
|
8
|
+
apiBase: string;
|
|
9
|
+
publicKey: string;
|
|
10
|
+
getToken: () => string | undefined;
|
|
11
|
+
}
|
|
12
|
+
export type DisabledRelease = {
|
|
13
|
+
enabled: false;
|
|
14
|
+
reason?: string;
|
|
15
|
+
};
|
|
16
|
+
export type AnyRelease = ClientRelease | DisabledRelease;
|
|
17
|
+
export type ReleaseResult = {
|
|
18
|
+
kind: 'ok';
|
|
19
|
+
release: AnyRelease;
|
|
20
|
+
etag: string;
|
|
21
|
+
} | {
|
|
22
|
+
kind: 'not-modified';
|
|
23
|
+
}
|
|
24
|
+
/** מפתח שגוי או חסום: לא מציגים את הבוט */
|
|
25
|
+
| {
|
|
26
|
+
kind: 'denied';
|
|
27
|
+
status: number;
|
|
28
|
+
}
|
|
29
|
+
/** אין רשת או שהשרת נפל: משתמשים בעותק השמור */
|
|
30
|
+
| {
|
|
31
|
+
kind: 'offline';
|
|
32
|
+
};
|
|
33
|
+
export interface TicketInput {
|
|
34
|
+
message: string;
|
|
35
|
+
name?: string;
|
|
36
|
+
email?: string;
|
|
37
|
+
phone?: string;
|
|
38
|
+
topicId?: string;
|
|
39
|
+
scriptId?: string;
|
|
40
|
+
sessionId?: string;
|
|
41
|
+
transcript?: TranscriptEntry[];
|
|
42
|
+
}
|
|
43
|
+
export type TicketResult = {
|
|
44
|
+
ok: true;
|
|
45
|
+
id: string;
|
|
46
|
+
number: number;
|
|
47
|
+
} | {
|
|
48
|
+
ok: false;
|
|
49
|
+
code: string;
|
|
50
|
+
message: string;
|
|
51
|
+
};
|
|
52
|
+
export declare function fetchRelease(c: ApiConfig, etag?: string): Promise<ReleaseResult>;
|
|
53
|
+
export declare const AI_TIMEOUT_MS = 20000;
|
|
54
|
+
export type AiAnswer = {
|
|
55
|
+
answer: string;
|
|
56
|
+
scriptId: string | null;
|
|
57
|
+
};
|
|
58
|
+
/** תשובת AI לשאלה בלי תסריט. כל כישלון (רשת, 429, אין תשובה, 20 שניות) מחזיר null, והצ'אט מציג את "לא נמצא" הרגיל */
|
|
59
|
+
export declare function postAiAnswer(c: ApiConfig, sessionId: string, question: string): Promise<AiAnswer | null>;
|
|
60
|
+
/** keepalive מאפשר לבקשה להסתיים גם כשהדף נסגר. sendBeacon לא מתאים כי אין בו Authorization ו-JSON */
|
|
61
|
+
export declare function postEvents(c: ApiConfig, payload: unknown, keepalive?: boolean): Promise<boolean>;
|
|
62
|
+
/** רישום המסכים של האתר או האפליקציה. כישלון לא מפריע לאף אחד: מנסים שוב בטעינה הבאה */
|
|
63
|
+
export declare function postScreens(c: ApiConfig, screens: Array<{
|
|
64
|
+
id: string;
|
|
65
|
+
label: string;
|
|
66
|
+
needsTarget: boolean;
|
|
67
|
+
}>): Promise<boolean>;
|
|
68
|
+
export declare function postTicket(c: ApiConfig, input: TicketInput): Promise<TicketResult>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { KavchatOptions } from './widget';
|
|
2
|
+
/**
|
|
3
|
+
* הפעלה מתגית הסקריפט:
|
|
4
|
+
* <script src="https://cdn.kavchat.com/v1/kavchat.js" data-key="pk_live_..." async></script>
|
|
5
|
+
*/
|
|
6
|
+
export declare function optionsFromScript(script: Element | null): Partial<KavchatOptions> | null;
|
|
7
|
+
export declare function boot(script?: Element | null): import("./global").KavchatGlobal;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* צבעים מתוך primaryColor של הלקוח
|
|
3
|
+
* הטקסט על הצבע הראשי נבחר לפי יחס ניגודיות (WCAG), כדי שגם צבע בהיר יישאר קריא
|
|
4
|
+
*/
|
|
5
|
+
export declare const DEFAULT_PRIMARY = "#2563eb";
|
|
6
|
+
export declare function parseHex(color: string): [number, number, number] | null;
|
|
7
|
+
export declare function contrast(a: string, b: string): number;
|
|
8
|
+
export interface Theme {
|
|
9
|
+
primary: string;
|
|
10
|
+
/** טקסט על רקע הצבע הראשי */
|
|
11
|
+
onPrimary: string;
|
|
12
|
+
/** טקסט וקווי מתאר בצבע הראשי על רקע לבן. צבע בהיר מדי מוחלף בכהה */
|
|
13
|
+
accent: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function themeFor(primaryColor: string): Theme;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* בניית DOM בלי innerHTML: כל טקסט מהתסריט או מהמשתמש נכנס כ-textContent
|
|
3
|
+
*/
|
|
4
|
+
type Child = Node | string | null | undefined | false;
|
|
5
|
+
type Attrs = Record<string, string | boolean | undefined | null>;
|
|
6
|
+
export declare function h<K extends keyof HTMLElementTagNameMap>(tag: K, attrs?: Attrs | null, ...children: Child[]): HTMLElementTagNameMap[K];
|
|
7
|
+
export declare function append(el: Node, children: Child[]): void;
|
|
8
|
+
/** אייקון SVG קטן מנתיב אחד, מוסתר מקוראי מסך */
|
|
9
|
+
export declare function icon(d: string): SVGSVGElement;
|
|
10
|
+
export declare const ICONS: {
|
|
11
|
+
chat: string;
|
|
12
|
+
close: string;
|
|
13
|
+
send: string;
|
|
14
|
+
};
|
|
15
|
+
/** מחזיר את הכתובת המנורמלת רק אם הפרוטוקול מותר */
|
|
16
|
+
export declare function safeUrl(raw: string, schemes: string[]): string | null;
|
|
17
|
+
/**
|
|
18
|
+
* טקסט עם קישורים: רק https הופך לקישור, וכל השאר נשאר טקסט
|
|
19
|
+
* ירידות שורה נשמרות ב-CSS (white-space: pre-wrap)
|
|
20
|
+
*/
|
|
21
|
+
export declare function richText(text: string): DocumentFragment;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { KavchatOptions, KavchatWidget } from './widget';
|
|
2
|
+
/**
|
|
3
|
+
* window.Kavchat: ממשק אחד לכל הדף
|
|
4
|
+
* קריאות שנעשו לפני שהסקריפט נטען נשמרות בתור q ומורצות לפי הסדר, אחרי init
|
|
5
|
+
*/
|
|
6
|
+
export type QueuedCall = [string, ...unknown[]];
|
|
7
|
+
export interface KavchatGlobal {
|
|
8
|
+
readonly loaded: true;
|
|
9
|
+
init(options: KavchatOptions): Promise<void>;
|
|
10
|
+
open(): void;
|
|
11
|
+
close(): void;
|
|
12
|
+
toggle(): void;
|
|
13
|
+
setUser(user: {
|
|
14
|
+
token?: string | null;
|
|
15
|
+
name?: string | null;
|
|
16
|
+
} | null): void;
|
|
17
|
+
destroy(): void;
|
|
18
|
+
/** אחרי הטעינה q.push מריץ את הקריאה מיד, כך שהקוד של האתר לא צריך לדעת אם הסקריפט כבר נטען */
|
|
19
|
+
q: {
|
|
20
|
+
push: (...calls: QueuedCall[]) => number;
|
|
21
|
+
};
|
|
22
|
+
readonly widget: KavchatWidget | null;
|
|
23
|
+
}
|
|
24
|
+
declare global {
|
|
25
|
+
interface Window {
|
|
26
|
+
Kavchat?: KavchatGlobal | {
|
|
27
|
+
q?: QueuedCall[];
|
|
28
|
+
[k: string]: unknown;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export declare function installGlobal(win?: Window): {
|
|
33
|
+
api: KavchatGlobal;
|
|
34
|
+
queue: QueuedCall[];
|
|
35
|
+
};
|
|
36
|
+
export declare function run(api: KavchatGlobal, call: QueuedCall): void;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* כניסה ל-npm: import { createWidget } from '@kavchat/widget'
|
|
3
|
+
* לאתרים בלי bundler יש את dist/kavchat.js, שמתקין את window.Kavchat
|
|
4
|
+
*/
|
|
5
|
+
import { KavchatWidget, KavchatOptions } from './widget';
|
|
6
|
+
export { KavchatWidget, DEFAULT_API, BRANDING_URL, pickLocale } from './widget';
|
|
7
|
+
export type { KavchatOptions, KavchatEvent } from './widget';
|
|
8
|
+
export { themeFor, contrast } from './color';
|
|
9
|
+
export { installGlobal } from './global';
|
|
10
|
+
export type { KavchatGlobal } from './global';
|
|
11
|
+
export declare function createWidget(options: KavchatOptions): KavchatWidget;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { BotState } from '@kavchat/core';
|
|
2
|
+
import type { AnyRelease } from './api';
|
|
3
|
+
export interface CachedRelease {
|
|
4
|
+
etag: string;
|
|
5
|
+
release: AnyRelease;
|
|
6
|
+
}
|
|
7
|
+
export declare const readCachedRelease: (pk: string) => CachedRelease | null;
|
|
8
|
+
export declare const writeCachedRelease: (pk: string, value: CachedRelease) => void;
|
|
9
|
+
/** השיחה נשמרת לאורך הלשונית, כדי שמעבר בין דפים באתר לא יתחיל אותה מחדש ולא ידרוס את המסלול בשרת */
|
|
10
|
+
export interface StoredSession {
|
|
11
|
+
id: string;
|
|
12
|
+
startedAt: string;
|
|
13
|
+
version: number;
|
|
14
|
+
state: Omit<BotState, 'scripts'> | null;
|
|
15
|
+
}
|
|
16
|
+
/** טביעה של רשימת המסכים האחרונה שנשלחה, כדי לשלוח רק כשהיא השתנתה או פעם בשבוע */
|
|
17
|
+
export interface ScreensMark {
|
|
18
|
+
fp: string;
|
|
19
|
+
at: number;
|
|
20
|
+
}
|
|
21
|
+
export declare const readScreensMark: (pk: string) => ScreensMark | null;
|
|
22
|
+
export declare const writeScreensMark: (pk: string, value: ScreensMark) => void;
|
|
23
|
+
export declare const readSession: (pk: string) => StoredSession | null;
|
|
24
|
+
export declare const writeSession: (pk: string, value: StoredSession) => void;
|
|
25
|
+
/** מזהה שיחה אקראי של 22 תווים מתוך [A-Za-z0-9_-], כפי שהשרת מצפה */
|
|
26
|
+
export declare function randomId(length?: number): string;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* העיצוב כולו בתוך ה-Shadow DOM. all: initial על ה-host עוצר ירושה של גופן, צבע וכיוון מהאתר.
|
|
3
|
+
* רק תכונות לוגיות (inline-start/end), כדי ש-dir="rtl" יהפוך את הכל בלי עקיפות ידניות
|
|
4
|
+
*/
|
|
5
|
+
export declare const CSS = "\n:host{all:initial}\n.kc{font-family:system-ui,-apple-system,\"Segoe UI\",Roboto,\"Noto Sans Hebrew\",\"Noto Sans Arabic\",Arial,sans-serif;font-size:15px;line-height:1.45;color:#1f2328;-webkit-font-smoothing:antialiased}\n.kc *,.kc *::before,.kc *::after{box-sizing:border-box}\n.kc [hidden]{display:none!important}\n.kc button,.kc input,.kc select,.kc textarea{font:inherit;color:inherit;margin:0}\n.kc :focus-visible{outline:2px solid var(--kc-accent);outline-offset:2px}\n.launcher{position:fixed;z-index:2147483000;inset-block-end:20px;inset-inline-end:20px;width:56px;height:56px;border-radius:50%;border:0;padding:0;background:var(--kc-primary);color:var(--kc-on-primary);cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 6px 20px rgba(0,0,0,.22);transition:transform .15s ease}\n.launcher:hover{transform:scale(1.05)}\n.launcher svg{width:28px;height:28px}\n.kc[data-pos=start] .launcher,.kc[data-pos=start] .panel{inset-inline-end:auto;inset-inline-start:20px}\n.panel{position:fixed;z-index:2147483001;inset-block-end:88px;inset-inline-end:20px;width:380px;max-width:calc(100vw - 40px);height:620px;max-height:calc(100vh - 108px);display:flex;flex-direction:column;background:#fff;border-radius:16px;box-shadow:0 12px 40px rgba(0,0,0,.24);overflow:hidden;animation:kc-in .18s ease-out}\n@keyframes kc-in{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}\n.header{display:flex;align-items:center;gap:10px;padding:12px 14px;background:var(--kc-primary);color:var(--kc-on-primary);flex:none}\n.avatar{width:38px;height:38px;border-radius:50%;flex:none;display:flex;align-items:center;justify-content:center;overflow:hidden;background:rgba(255,255,255,.22);font-weight:600;font-size:15px}\n.avatar img{width:100%;height:100%;object-fit:cover}\n.titles{min-width:0;flex:1}\n.name{font-weight:600;font-size:16px;margin:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.subtitle{font-size:13px;opacity:.85;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.close{flex:none;width:36px;height:36px;border:0;border-radius:50%;background:transparent;color:inherit;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0}\n.close:hover{background:rgba(127,127,127,.2)}\n.close svg{width:22px;height:22px}\n.banner{flex:none;background:#fff4e5;color:#7a4100;font-size:13px;padding:6px 14px}\n.view{flex:1;min-height:0;display:flex;flex-direction:column;background:#f6f7f9}\n.messages{flex:1;min-height:0;overflow-y:auto;padding:16px 14px 8px;display:flex;flex-direction:column;gap:6px;overscroll-behavior:contain}\n.msg{max-width:85%;padding:9px 13px;border-radius:16px;white-space:pre-wrap;overflow-wrap:anywhere}\n.msg-bot{align-self:flex-start;background:#fff;border:1px solid #e3e6ea;border-end-start-radius:4px}\n.msg-user{align-self:flex-end;background:var(--kc-primary);color:var(--kc-on-primary);border-end-end-radius:4px}\n.msg-user+.msg-bot,.msg-bot+.msg-user{margin-block-start:6px}\n.msg a{color:inherit;text-decoration:underline}\n.ai{display:block;font-size:11px;font-weight:600;color:#6b7280}\n.typing{color:#9ca3af;font-weight:700;animation:kc-dot 1s infinite}\n@keyframes kc-dot{50%{opacity:.3}}\n.msg img{display:block;max-width:100%;height:auto;border-radius:10px;margin-block-end:6px}\n.chips{flex:none;display:flex;flex-wrap:wrap;gap:6px;padding:4px 14px 10px;max-height:40%;overflow-y:auto}\n.chip{border:1px solid var(--kc-accent);background:#fff;color:var(--kc-accent);border-radius:999px;padding:6px 13px;cursor:pointer;font-size:14px;line-height:1.3;text-align:start}\n.chip:hover{background:var(--kc-primary);color:var(--kc-on-primary);border-color:var(--kc-primary)}\n.chip-home,.chip-back{border-color:#cfd4da;color:#4b5563}\n.composer{flex:none;display:flex;align-items:center;gap:8px;padding:8px 10px;border-block-start:1px solid #e3e6ea;background:#fff}\n.composer input{flex:1;min-width:0;border:0;outline:0;background:transparent;padding:8px 6px;font-size:16px}\n.kc .composer input:focus-visible{outline:0}\n.composer:focus-within{box-shadow:inset 0 2px 0 var(--kc-accent)}\n.send{flex:none;width:40px;height:40px;border:0;border-radius:50%;background:var(--kc-primary);color:var(--kc-on-primary);cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0}\n.send svg{width:20px;height:20px}\n.kc[dir=rtl] .send svg{transform:scaleX(-1)}\n.form{flex:1;min-height:0;overflow-y:auto;padding:16px 16px 12px;background:#fff;display:flex;flex-direction:column;gap:10px}\n.form h2{margin:0 0 4px;font-size:17px;font-weight:600}\n.field label{display:block;font-size:13px;color:#4b5563;margin-block-end:3px}\n.field input,.field select,.field textarea{display:block;width:100%;border:1px solid #cfd4da;border-radius:8px;padding:8px 10px;background:#fff;font-size:16px}\n.field textarea{min-height:96px;resize:vertical}\n.field input:focus,.field select:focus,.field textarea:focus{border-color:var(--kc-accent);outline:0;box-shadow:0 0 0 2px color-mix(in srgb,var(--kc-accent) 25%,transparent)}\n.error{color:#b42318;font-size:14px}\n.actions{display:flex;gap:8px;flex-wrap:wrap;margin-block-start:4px}\n.btn{border-radius:8px;padding:9px 16px;cursor:pointer;border:1px solid #cfd4da;background:#fff}\n.btn-primary{background:var(--kc-primary);color:var(--kc-on-primary);border-color:var(--kc-primary)}\n.btn:disabled{opacity:.6;cursor:default}\n.sent{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;padding:24px;text-align:center;background:#fff}\n.badge{flex:none;display:block;text-align:center;font-size:12px;color:#6b7280;padding:6px;background:#fff;border-block-start:1px solid #eef0f2;text-decoration:none}\n.badge:hover{color:#1f2328}\n.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n@media (max-width:480px){.panel,.kc[data-pos=start] .panel{inset:0;width:100%;max-width:none;height:100%;max-height:none;border-radius:0}}\n@media (prefers-reduced-motion:reduce){.kc *{animation:none!important;transition:none!important}}\n";
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { BotEffect } from '@kavchat/core';
|
|
2
|
+
/**
|
|
3
|
+
* הווידג'ט: טוען את הגרסה שפורסמה, מציג כפתור צף וחלון שיחה בתוך Shadow DOM,
|
|
4
|
+
* ומריץ את השיחה דרך המנוע המשותף ב-@kavchat/core. אין כאן לוגיקת התאמה או מכונת מצבים
|
|
5
|
+
*/
|
|
6
|
+
export declare const DEFAULT_API = "https://api.kavchat.com";
|
|
7
|
+
export declare const BRANDING_URL = "https://kavchat.com";
|
|
8
|
+
export type KavchatEvent = {
|
|
9
|
+
type: 'ready';
|
|
10
|
+
version: number;
|
|
11
|
+
offline: boolean;
|
|
12
|
+
} | {
|
|
13
|
+
type: 'unavailable';
|
|
14
|
+
reason?: string;
|
|
15
|
+
} | {
|
|
16
|
+
type: 'open';
|
|
17
|
+
} | {
|
|
18
|
+
type: 'close';
|
|
19
|
+
} | {
|
|
20
|
+
type: 'effect';
|
|
21
|
+
effect: BotEffect;
|
|
22
|
+
} | {
|
|
23
|
+
type: 'ticket';
|
|
24
|
+
id: string;
|
|
25
|
+
number: number;
|
|
26
|
+
};
|
|
27
|
+
export interface ScreenDeclaration {
|
|
28
|
+
id: string;
|
|
29
|
+
label: string;
|
|
30
|
+
needsTarget?: boolean;
|
|
31
|
+
}
|
|
32
|
+
/** רשימה תקינה: מזהים חוקיים, בלי כפילויות, שם עד 60 תווים, עד 100 מסכים */
|
|
33
|
+
export declare function normalizeScreens(list: ScreenDeclaration[] | undefined): {
|
|
34
|
+
id: string;
|
|
35
|
+
label: string;
|
|
36
|
+
needsTarget: boolean;
|
|
37
|
+
}[];
|
|
38
|
+
/** טביעה יציבה (FNV-1a) של הרשימה ממוינת. מספיק כדי לדעת אם משהו השתנה */
|
|
39
|
+
export declare function screensFingerprint(list: Array<{
|
|
40
|
+
id: string;
|
|
41
|
+
label: string;
|
|
42
|
+
needsTarget: boolean;
|
|
43
|
+
}>): string;
|
|
44
|
+
export interface KavchatOptions {
|
|
45
|
+
/** המפתח הציבורי pk_live_... */
|
|
46
|
+
publicKey: string;
|
|
47
|
+
/** ברירת מחדל https://api.kavchat.com */
|
|
48
|
+
apiBase?: string;
|
|
49
|
+
/** שפת הממשק. ברירת מחדל: שפת הדפדפן, ואם אינה נתמכת שפת הבוט */
|
|
50
|
+
locale?: string;
|
|
51
|
+
/** JWT לזיהוי המשתמש, נחתם בשרת של הלקוח עם המפתח הסודי */
|
|
52
|
+
userToken?: string;
|
|
53
|
+
userName?: string;
|
|
54
|
+
/** פתיחת החלון מיד כשהבוט נטען */
|
|
55
|
+
open?: boolean;
|
|
56
|
+
/** כפתור מסוג screen בתסריט. בלי callback הכפתור לא עושה דבר */
|
|
57
|
+
onScreen?: (screen: string, targetId?: string) => void;
|
|
58
|
+
/**
|
|
59
|
+
* המסכים שכפתורי "פתיחת מסך" יכולים להוביל אליהם. נרשמים בשרת, ומסך הניהול מציג אותם ברשימה נפתחת.
|
|
60
|
+
* needsTarget = המסך צריך מזהה פריט (למשל מספר מוצר)
|
|
61
|
+
*/
|
|
62
|
+
screens?: ScreenDeclaration[];
|
|
63
|
+
onEvent?: (event: KavchatEvent) => void;
|
|
64
|
+
/** איפה לשים את הווידג'ט. ברירת מחדל document.body */
|
|
65
|
+
container?: HTMLElement;
|
|
66
|
+
}
|
|
67
|
+
/** שפת הממשק: המבוקשת או שפת הדפדפן אם יש לה תרגום, אחרת שפת הבוט */
|
|
68
|
+
export declare function pickLocale(requested: string | undefined, releaseLocale: string): string;
|
|
69
|
+
export declare class KavchatWidget {
|
|
70
|
+
/** מתממש כשהגרסה נטענה (או כשהתברר שהבוט כבוי) */
|
|
71
|
+
ready: Promise<void>;
|
|
72
|
+
private opts;
|
|
73
|
+
private api;
|
|
74
|
+
private release;
|
|
75
|
+
private offline;
|
|
76
|
+
private ui;
|
|
77
|
+
private state;
|
|
78
|
+
private session;
|
|
79
|
+
private t;
|
|
80
|
+
private locale;
|
|
81
|
+
private isOpen;
|
|
82
|
+
private wantOpen;
|
|
83
|
+
private prevFocus;
|
|
84
|
+
private reportTimer;
|
|
85
|
+
private dirty;
|
|
86
|
+
private destroyed;
|
|
87
|
+
/** מחכים לתשובת AI: מציגים "מקליד" ומתעלמים מקלט חדש */
|
|
88
|
+
private waiting;
|
|
89
|
+
constructor(options: KavchatOptions);
|
|
90
|
+
/** ה-ShadowRoot, לבדיקות ולאוטומציה באתר המארח */
|
|
91
|
+
get shadowRoot(): ShadowRoot | null;
|
|
92
|
+
get opened(): boolean;
|
|
93
|
+
open(): void;
|
|
94
|
+
close(): void;
|
|
95
|
+
toggle(): void;
|
|
96
|
+
/** החלפת משתמש (התחברות או התנתקות). הגרסה נטענת מחדש, כי הקהל testers תלוי בזהות */
|
|
97
|
+
setUser(user: {
|
|
98
|
+
token?: string | null;
|
|
99
|
+
name?: string | null;
|
|
100
|
+
} | null): Promise<void>;
|
|
101
|
+
destroy(): void;
|
|
102
|
+
private load;
|
|
103
|
+
/** שולח את רשימת המסכים רק כשהשתנתה או פעם בשבוע. שגיאה נבלעת: ננסה שוב בטעינה הבאה */
|
|
104
|
+
private registerScreens;
|
|
105
|
+
private apply;
|
|
106
|
+
private restoreSession;
|
|
107
|
+
/**
|
|
108
|
+
* התסריט נבדק בשפת הבוט (מילות עצירה וכו'), והטקסטים הקבועים של הצ'אט מוצגים בשפת הממשק.
|
|
109
|
+
* ההגדרה של המנוע גלובלית, ולכן קובעים אותה לפני כל קריאה
|
|
110
|
+
*/
|
|
111
|
+
private run;
|
|
112
|
+
private startFresh;
|
|
113
|
+
private setState;
|
|
114
|
+
private onChip;
|
|
115
|
+
private onSend;
|
|
116
|
+
private afterAction;
|
|
117
|
+
private handleEffect;
|
|
118
|
+
private scheduleReport;
|
|
119
|
+
private flush;
|
|
120
|
+
private onPageHide;
|
|
121
|
+
private onVisibility;
|
|
122
|
+
private emit;
|
|
123
|
+
private unmount;
|
|
124
|
+
private mount;
|
|
125
|
+
private focusInside;
|
|
126
|
+
private render;
|
|
127
|
+
private renderMessages;
|
|
128
|
+
private renderChips;
|
|
129
|
+
private showChat;
|
|
130
|
+
private showForm;
|
|
131
|
+
private showSent;
|
|
132
|
+
}
|
|
133
|
+
/** קישור מהתסריט: רק https ו-tel, בלשונית חדשה ובלי גישה חזרה לדף */
|
|
134
|
+
export declare function openLink(url: string, root?: ShadowRoot | null): boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,53 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kavchat/widget",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Kavchat web widget: the embeddable scripted support chat for websites.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.mjs",
|
|
8
|
+
"module": "dist/index.mjs",
|
|
9
|
+
"types": "dist/types/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/types/index.d.ts",
|
|
13
|
+
"import": "./dist/index.mjs"
|
|
14
|
+
},
|
|
15
|
+
"./kavchat.js": "./dist/kavchat.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "node scripts/build.mjs",
|
|
23
|
+
"test": "vitest run",
|
|
24
|
+
"typecheck": "tsc --noEmit",
|
|
25
|
+
"prepublishOnly": "yarn build"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@kavchat/core": "0.0.1"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"esbuild": "^0.28.2",
|
|
32
|
+
"jsdom": "^30.1.3",
|
|
33
|
+
"typescript": "^5.9.3",
|
|
34
|
+
"vitest": "^5.0.5"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"chatbot",
|
|
38
|
+
"customer-support",
|
|
39
|
+
"kavchat",
|
|
40
|
+
"rtl",
|
|
41
|
+
"scripted-bot",
|
|
42
|
+
"support-chat"
|
|
43
|
+
],
|
|
44
|
+
"homepage": "https://kavchat.com/docs",
|
|
45
|
+
"bugs": {
|
|
46
|
+
"email": "support@kavchat.com"
|
|
47
|
+
},
|
|
48
|
+
"author": "maxsave <support@kavchat.com>",
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public",
|
|
51
|
+
"registry": "https://registry.npmjs.org/"
|
|
52
|
+
}
|
|
53
|
+
}
|