@kroonen-ai/librebot-widget 1.6.1 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/librebot.js CHANGED
@@ -1,2 +1,2 @@
1
- var LibreBot=function(e){"use strict";const n='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path></svg>',t='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>',o={en:{title:"Libre Bot",subtitle:"AI Documentation Assistant",placeholder:"Ask a question...",welcomeMessage:"Hi! I can help you find answers in the documentation. What would you like to know?",errorPrefix:"Sorry, I encountered an error:",connectionError:"Sorry, I had trouble connecting. Please try again.",streamingNotSupported:"Sorry, streaming is not supported.",poweredBy:"Powered by",bookingTitle:"Book an Appointment",selectDate:"Select a date",selectTime:"Select a time",bookAppointment:"Book Appointment",noSlotsAvailable:"No time slots available for this date",loadingSlots:"Loading available times...",bookingSuccess:"Your appointment has been confirmed!",bookingError:"Failed to book appointment. Please try again.",backToChat:"Back to chat",yourDetails:"Your Details",confirmBooking:"Confirm Booking",meetingWith:"Meeting",duration:"Duration",minutes:"minutes",joinMeeting:"Join Meeting"},fr:{title:"Libre Bot",subtitle:"Assistant IA de documentation",placeholder:"Posez une question...",welcomeMessage:"Bonjour ! Je peux vous aider à trouver des réponses dans la documentation. Que souhaitez-vous savoir ?",errorPrefix:"Désolé, j'ai rencontré une erreur :",connectionError:"Désolé, j'ai eu du mal à me connecter. Veuillez réessayer.",streamingNotSupported:"Désolé, le streaming n'est pas pris en charge.",poweredBy:"Propulsé par",bookingTitle:"Prendre rendez-vous",selectDate:"Choisir une date",selectTime:"Choisir une heure",bookAppointment:"Prendre rendez-vous",noSlotsAvailable:"Aucun créneau disponible pour cette date",loadingSlots:"Chargement des horaires...",bookingSuccess:"Votre rendez-vous a été confirmé !",bookingError:"Échec de la réservation. Veuillez réessayer.",backToChat:"Retour au chat",yourDetails:"Vos informations",confirmBooking:"Confirmer la réservation",meetingWith:"Réunion",duration:"Durée",minutes:"minutes",joinMeeting:"Rejoindre la réunion"},de:{title:"Libre Bot",subtitle:"KI-Dokumentationsassistent",placeholder:"Stellen Sie eine Frage...",welcomeMessage:"Hallo! Ich kann Ihnen helfen, Antworten in der Dokumentation zu finden. Was möchten Sie wissen?",errorPrefix:"Entschuldigung, ein Fehler ist aufgetreten:",connectionError:"Entschuldigung, ich hatte Verbindungsprobleme. Bitte versuchen Sie es erneut.",streamingNotSupported:"Entschuldigung, Streaming wird nicht unterstützt.",poweredBy:"Betrieben von",bookingTitle:"Termin buchen",selectDate:"Datum auswählen",selectTime:"Uhrzeit auswählen",bookAppointment:"Termin buchen",noSlotsAvailable:"Keine Termine für dieses Datum verfügbar",loadingSlots:"Verfügbare Zeiten werden geladen...",bookingSuccess:"Ihr Termin wurde bestätigt!",bookingError:"Terminbuchung fehlgeschlagen. Bitte versuchen Sie es erneut.",backToChat:"Zurück zum Chat",yourDetails:"Ihre Angaben",confirmBooking:"Buchung bestätigen",meetingWith:"Meeting",duration:"Dauer",minutes:"Minuten",joinMeeting:"An Meeting teilnehmen"},nl:{title:"Libre Bot",subtitle:"AI-documentatieassistent",placeholder:"Stel een vraag...",welcomeMessage:"Hallo! Ik kan je helpen antwoorden te vinden in de documentatie. Wat wil je weten?",errorPrefix:"Sorry, er is een fout opgetreden:",connectionError:"Sorry, ik had moeite met verbinden. Probeer het opnieuw.",streamingNotSupported:"Sorry, streaming wordt niet ondersteund.",poweredBy:"Mogelijk gemaakt door",bookingTitle:"Afspraak maken",selectDate:"Selecteer een datum",selectTime:"Selecteer een tijd",bookAppointment:"Afspraak maken",noSlotsAvailable:"Geen tijdsloten beschikbaar voor deze datum",loadingSlots:"Beschikbare tijden laden...",bookingSuccess:"Je afspraak is bevestigd!",bookingError:"Afspraak maken mislukt. Probeer het opnieuw.",backToChat:"Terug naar chat",yourDetails:"Je gegevens",confirmBooking:"Boeking bevestigen",meetingWith:"Vergadering",duration:"Duur",minutes:"minuten",joinMeeting:"Deelnemen aan vergadering"},es:{title:"Libre Bot",subtitle:"Asistente de documentación con IA",placeholder:"Haz una pregunta...",welcomeMessage:"¡Hola! Puedo ayudarte a encontrar respuestas en la documentación. ¿Qué te gustaría saber?",errorPrefix:"Lo siento, encontré un error:",connectionError:"Lo siento, tuve problemas para conectarme. Por favor, inténtalo de nuevo.",streamingNotSupported:"Lo siento, el streaming no está soportado.",poweredBy:"Desarrollado por",bookingTitle:"Reservar cita",selectDate:"Seleccionar fecha",selectTime:"Seleccionar hora",bookAppointment:"Reservar cita",noSlotsAvailable:"No hay horarios disponibles para esta fecha",loadingSlots:"Cargando horarios disponibles...",bookingSuccess:"¡Tu cita ha sido confirmada!",bookingError:"Error al reservar la cita. Por favor, inténtalo de nuevo.",backToChat:"Volver al chat",yourDetails:"Tus datos",confirmBooking:"Confirmar reserva",meetingWith:"Reunión",duration:"Duración",minutes:"minutos",joinMeeting:"Unirse a la reunión"},pt:{title:"Libre Bot",subtitle:"Assistente de documentação com IA",placeholder:"Faça uma pergunta...",welcomeMessage:"Olá! Posso ajudá-lo a encontrar respostas na documentação. O que você gostaria de saber?",errorPrefix:"Desculpe, encontrei um erro:",connectionError:"Desculpe, tive problemas para conectar. Por favor, tente novamente.",streamingNotSupported:"Desculpe, streaming não é suportado.",poweredBy:"Desenvolvido por",bookingTitle:"Agendar consulta",selectDate:"Selecionar data",selectTime:"Selecionar horário",bookAppointment:"Agendar consulta",noSlotsAvailable:"Nenhum horário disponível para esta data",loadingSlots:"Carregando horários disponíveis...",bookingSuccess:"Seu agendamento foi confirmado!",bookingError:"Falha ao agendar. Por favor, tente novamente.",backToChat:"Voltar ao chat",yourDetails:"Seus dados",confirmBooking:"Confirmar agendamento",meetingWith:"Reunião",duration:"Duração",minutes:"minutos",joinMeeting:"Entrar na reunião"},ko:{title:"Libre Bot",subtitle:"AI 문서 도우미",placeholder:"질문하세요...",welcomeMessage:"안녕하세요! 문서에서 답변을 찾는 데 도움을 드릴 수 있습니다. 무엇을 알고 싶으신가요?",errorPrefix:"죄송합니다. 오류가 발생했습니다:",connectionError:"죄송합니다. 연결에 문제가 있습니다. 다시 시도해 주세요.",streamingNotSupported:"죄송합니다. 스트리밍이 지원되지 않습니다.",poweredBy:"제공:",bookingTitle:"예약하기",selectDate:"날짜 선택",selectTime:"시간 선택",bookAppointment:"예약하기",noSlotsAvailable:"선택한 날짜에 가능한 시간이 없습니다",loadingSlots:"가능한 시간 로딩 중...",bookingSuccess:"예약이 확정되었습니다!",bookingError:"예약에 실패했습니다. 다시 시도해 주세요.",backToChat:"채팅으로 돌아가기",yourDetails:"정보 입력",confirmBooking:"예약 확정",meetingWith:"미팅",duration:"소요 시간",minutes:"분",joinMeeting:"미팅 참가"},ja:{title:"Libre Bot",subtitle:"AIドキュメントアシスタント",placeholder:"質問してください...",welcomeMessage:"こんにちは!ドキュメントから回答を見つけるお手伝いができます。何をお知りになりたいですか?",errorPrefix:"申し訳ありません。エラーが発生しました:",connectionError:"申し訳ありません。接続に問題がありました。もう一度お試しください。",streamingNotSupported:"申し訳ありません。ストリーミングはサポートされていません。",poweredBy:"提供:",bookingTitle:"予約する",selectDate:"日付を選択",selectTime:"時間を選択",bookAppointment:"予約する",noSlotsAvailable:"この日は空き時間がありません",loadingSlots:"空き時間を読み込み中...",bookingSuccess:"ご予約が確定しました!",bookingError:"予約に失敗しました。もう一度お試しください。",backToChat:"チャットに戻る",yourDetails:"お客様情報",confirmBooking:"予約を確定",meetingWith:"ミーティング",duration:"所要時間",minutes:"分",joinMeeting:"ミーティングに参加"},zh:{title:"Libre Bot",subtitle:"AI文档助手",placeholder:"请提问...",welcomeMessage:"您好!我可以帮助您在文档中查找答案。您想了解什么?",errorPrefix:"抱歉,遇到了错误:",connectionError:"抱歉,连接出现问题。请重试。",streamingNotSupported:"抱歉,不支持流式传输。",poweredBy:"由以下提供支持:",bookingTitle:"预约",selectDate:"选择日期",selectTime:"选择时间",bookAppointment:"预约",noSlotsAvailable:"该日期没有可用时间段",loadingSlots:"加载可用时间...",bookingSuccess:"您的预约已确认!",bookingError:"预约失败。请重试。",backToChat:"返回聊天",yourDetails:"您的信息",confirmBooking:"确认预约",meetingWith:"会议",duration:"时长",minutes:"分钟",joinMeeting:"加入会议"},hi:{title:"Libre Bot",subtitle:"AI दस्तावेज़ सहायक",placeholder:"कोई प्रश्न पूछें...",welcomeMessage:"नमस्ते! मैं आपको दस्तावेज़ों में उत्तर खोजने में मदद कर सकता हूं। आप क्या जानना चाहते हैं?",errorPrefix:"क्षमा करें, एक त्रुटि हुई:",connectionError:"क्षमा करें, कनेक्ट करने में समस्या हुई। कृपया पुनः प्रयास करें।",streamingNotSupported:"क्षमा करें, स्ट्रीमिंग समर्थित नहीं है।",poweredBy:"द्वारा संचालित",bookingTitle:"अपॉइंटमेंट बुक करें",selectDate:"तिथि चुनें",selectTime:"समय चुनें",bookAppointment:"अपॉइंटमेंट बुक करें",noSlotsAvailable:"इस तिथि के लिए कोई समय उपलब्ध नहीं है",loadingSlots:"उपलब्ध समय लोड हो रहा है...",bookingSuccess:"आपकी अपॉइंटमेंट की पुष्टि हो गई है!",bookingError:"बुकिंग विफल। कृपया पुनः प्रयास करें।",backToChat:"चैट पर वापस जाएं",yourDetails:"आपका विवरण",confirmBooking:"बुकिंग की पुष्टि करें",meetingWith:"मीटिंग",duration:"अवधि",minutes:"मिनट",joinMeeting:"मीटिंग में शामिल हों"},ar:{title:"Libre Bot",subtitle:"مساعد الوثائق بالذكاء الاصطناعي",placeholder:"اطرح سؤالاً...",welcomeMessage:"مرحباً! يمكنني مساعدتك في العثور على إجابات في الوثائق. ماذا تريد أن تعرف؟",errorPrefix:"عذراً، حدث خطأ:",connectionError:"عذراً، واجهت مشكلة في الاتصال. يرجى المحاولة مرة أخرى.",streamingNotSupported:"عذراً، البث غير مدعوم.",poweredBy:"مدعوم من",bookingTitle:"حجز موعد",selectDate:"اختر التاريخ",selectTime:"اختر الوقت",bookAppointment:"حجز موعد",noSlotsAvailable:"لا توجد أوقات متاحة لهذا التاريخ",loadingSlots:"جاري تحميل الأوقات المتاحة...",bookingSuccess:"تم تأكيد موعدك!",bookingError:"فشل الحجز. يرجى المحاولة مرة أخرى.",backToChat:"العودة إلى المحادثة",yourDetails:"بياناتك",confirmBooking:"تأكيد الحجز",meetingWith:"اجتماع",duration:"المدة",minutes:"دقيقة",joinMeeting:"انضم إلى الاجتماع"},bn:{title:"Libre Bot",subtitle:"AI ডকুমেন্টেশন সহায়ক",placeholder:"একটি প্রশ্ন জিজ্ঞাসা করুন...",welcomeMessage:"হ্যালো! আমি আপনাকে ডকুমেন্টেশনে উত্তর খুঁজে পেতে সাহায্য করতে পারি। আপনি কী জানতে চান?",errorPrefix:"দুঃখিত, একটি ত্রুটি ঘটেছে:",connectionError:"দুঃখিত, সংযোগে সমস্যা হয়েছে। অনুগ্রহ করে আবার চেষ্টা করুন।",streamingNotSupported:"দুঃখিত, স্ট্রিমিং সমর্থিত নয়।",poweredBy:"দ্বারা চালিত",bookingTitle:"অ্যাপয়েন্টমেন্ট বুক করুন",selectDate:"তারিখ নির্বাচন করুন",selectTime:"সময় নির্বাচন করুন",bookAppointment:"অ্যাপয়েন্টমেন্ট বুক করুন",noSlotsAvailable:"এই তারিখে কোনো সময় উপলব্ধ নেই",loadingSlots:"উপলব্ধ সময় লোড হচ্ছে...",bookingSuccess:"আপনার অ্যাপয়েন্টমেন্ট নিশ্চিত হয়েছে!",bookingError:"বুকিং ব্যর্থ হয়েছে। অনুগ্রহ করে আবার চেষ্টা করুন।",backToChat:"চ্যাটে ফিরে যান",yourDetails:"আপনার তথ্য",confirmBooking:"বুকিং নিশ্চিত করুন",meetingWith:"মিটিং",duration:"সময়কাল",minutes:"মিনিট",joinMeeting:"মিটিংয়ে যোগ দিন"},ru:{title:"Libre Bot",subtitle:"ИИ-ассистент по документации",placeholder:"Задайте вопрос...",welcomeMessage:"Привет! Я могу помочь вам найти ответы в документации. Что бы вы хотели узнать?",errorPrefix:"Извините, произошла ошибка:",connectionError:"Извините, возникла проблема с подключением. Пожалуйста, попробуйте снова.",streamingNotSupported:"Извините, потоковая передача не поддерживается.",poweredBy:"Работает на",bookingTitle:"Записаться на приём",selectDate:"Выберите дату",selectTime:"Выберите время",bookAppointment:"Записаться",noSlotsAvailable:"На эту дату нет свободного времени",loadingSlots:"Загрузка доступного времени...",bookingSuccess:"Ваша запись подтверждена!",bookingError:"Не удалось записаться. Пожалуйста, попробуйте снова.",backToChat:"Вернуться к чату",yourDetails:"Ваши данные",confirmBooking:"Подтвердить запись",meetingWith:"Встреча",duration:"Продолжительность",minutes:"минут",joinMeeting:"Присоединиться к встрече"},id:{title:"Libre Bot",subtitle:"Asisten Dokumentasi AI",placeholder:"Ajukan pertanyaan...",welcomeMessage:"Halo! Saya dapat membantu Anda menemukan jawaban di dokumentasi. Apa yang ingin Anda ketahui?",errorPrefix:"Maaf, terjadi kesalahan:",connectionError:"Maaf, ada masalah koneksi. Silakan coba lagi.",streamingNotSupported:"Maaf, streaming tidak didukung.",poweredBy:"Didukung oleh",bookingTitle:"Buat Janji",selectDate:"Pilih tanggal",selectTime:"Pilih waktu",bookAppointment:"Buat Janji",noSlotsAvailable:"Tidak ada waktu tersedia untuk tanggal ini",loadingSlots:"Memuat waktu tersedia...",bookingSuccess:"Janji Anda telah dikonfirmasi!",bookingError:"Gagal membuat janji. Silakan coba lagi.",backToChat:"Kembali ke chat",yourDetails:"Data Anda",confirmBooking:"Konfirmasi Pemesanan",meetingWith:"Pertemuan",duration:"Durasi",minutes:"menit",joinMeeting:"Gabung Pertemuan"},vi:{title:"Libre Bot",subtitle:"Trợ lý tài liệu AI",placeholder:"Đặt câu hỏi...",welcomeMessage:"Xin chào! Tôi có thể giúp bạn tìm câu trả lời trong tài liệu. Bạn muốn biết điều gì?",errorPrefix:"Xin lỗi, đã xảy ra lỗi:",connectionError:"Xin lỗi, có vấn đề kết nối. Vui lòng thử lại.",streamingNotSupported:"Xin lỗi, không hỗ trợ phát trực tuyến.",poweredBy:"Được cung cấp bởi",bookingTitle:"Đặt lịch hẹn",selectDate:"Chọn ngày",selectTime:"Chọn giờ",bookAppointment:"Đặt lịch hẹn",noSlotsAvailable:"Không có thời gian trống cho ngày này",loadingSlots:"Đang tải thời gian trống...",bookingSuccess:"Lịch hẹn của bạn đã được xác nhận!",bookingError:"Đặt lịch thất bại. Vui lòng thử lại.",backToChat:"Quay lại chat",yourDetails:"Thông tin của bạn",confirmBooking:"Xác nhận đặt lịch",meetingWith:"Cuộc họp",duration:"Thời lượng",minutes:"phút",joinMeeting:"Tham gia cuộc họp"},tr:{title:"Libre Bot",subtitle:"Yapay Zeka Dokümantasyon Asistanı",placeholder:"Bir soru sorun...",welcomeMessage:"Merhaba! Dokümantasyonda cevap bulmanıza yardımcı olabilirim. Ne öğrenmek istersiniz?",errorPrefix:"Üzgünüm, bir hata oluştu:",connectionError:"Üzgünüm, bağlantı sorunu yaşadım. Lütfen tekrar deneyin.",streamingNotSupported:"Üzgünüm, akış desteklenmiyor.",poweredBy:"Tarafından desteklenmektedir",bookingTitle:"Randevu Al",selectDate:"Tarih seçin",selectTime:"Saat seçin",bookAppointment:"Randevu Al",noSlotsAvailable:"Bu tarih için müsait saat yok",loadingSlots:"Müsait saatler yükleniyor...",bookingSuccess:"Randevunuz onaylandı!",bookingError:"Randevu alınamadı. Lütfen tekrar deneyin.",backToChat:"Sohbete dön",yourDetails:"Bilgileriniz",confirmBooking:"Randevuyu Onayla",meetingWith:"Toplantı",duration:"Süre",minutes:"dakika",joinMeeting:"Toplantıya Katıl"},it:{title:"Libre Bot",subtitle:"Assistente documentazione AI",placeholder:"Fai una domanda...",welcomeMessage:"Ciao! Posso aiutarti a trovare risposte nella documentazione. Cosa vorresti sapere?",errorPrefix:"Mi dispiace, si è verificato un errore:",connectionError:"Mi dispiace, ho avuto problemi di connessione. Per favore riprova.",streamingNotSupported:"Mi dispiace, lo streaming non è supportato.",poweredBy:"Offerto da",bookingTitle:"Prenota un Appuntamento",selectDate:"Seleziona una data",selectTime:"Seleziona un orario",bookAppointment:"Prenota Appuntamento",noSlotsAvailable:"Nessun orario disponibile per questa data",loadingSlots:"Caricamento orari disponibili...",bookingSuccess:"Il tuo appuntamento è stato confermato!",bookingError:"Prenotazione fallita. Per favore riprova.",backToChat:"Torna alla chat",yourDetails:"I tuoi dati",confirmBooking:"Conferma Prenotazione",meetingWith:"Riunione",duration:"Durata",minutes:"minuti",joinMeeting:"Partecipa alla Riunione"},th:{title:"Libre Bot",subtitle:"ผู้ช่วยเอกสาร AI",placeholder:"ถามคำถาม...",welcomeMessage:"สวัสดี! ฉันสามารถช่วยคุณค้นหาคำตอบในเอกสารได้ คุณต้องการทราบอะไร?",errorPrefix:"ขออภัย เกิดข้อผิดพลาด:",connectionError:"ขออภัย มีปัญหาในการเชื่อมต่อ กรุณาลองอีกครั้ง",streamingNotSupported:"ขออภัย ไม่รองรับการสตรีม",poweredBy:"ขับเคลื่อนโดย",bookingTitle:"จองนัดหมาย",selectDate:"เลือกวันที่",selectTime:"เลือกเวลา",bookAppointment:"จองนัดหมาย",noSlotsAvailable:"ไม่มีช่วงเวลาว่างสำหรับวันนี้",loadingSlots:"กำลังโหลดเวลาว่าง...",bookingSuccess:"การนัดหมายของคุณได้รับการยืนยันแล้ว!",bookingError:"การจองล้มเหลว กรุณาลองอีกครั้ง",backToChat:"กลับไปที่แชท",yourDetails:"ข้อมูลของคุณ",confirmBooking:"ยืนยันการจอง",meetingWith:"การประชุม",duration:"ระยะเวลา",minutes:"นาที",joinMeeting:"เข้าร่วมการประชุม"},pl:{title:"Libre Bot",subtitle:"Asystent dokumentacji AI",placeholder:"Zadaj pytanie...",welcomeMessage:"Cześć! Mogę pomóc Ci znaleźć odpowiedzi w dokumentacji. Co chciałbyś wiedzieć?",errorPrefix:"Przepraszam, wystąpił błąd:",connectionError:"Przepraszam, miałem problem z połączeniem. Spróbuj ponownie.",streamingNotSupported:"Przepraszam, streaming nie jest obsługiwany.",poweredBy:"Obsługiwane przez",bookingTitle:"Umów wizytę",selectDate:"Wybierz datę",selectTime:"Wybierz godzinę",bookAppointment:"Umów wizytę",noSlotsAvailable:"Brak dostępnych terminów na ten dzień",loadingSlots:"Ładowanie dostępnych terminów...",bookingSuccess:"Twoja wizyta została potwierdzona!",bookingError:"Nie udało się umówić wizyty. Spróbuj ponownie.",backToChat:"Wróć do czatu",yourDetails:"Twoje dane",confirmBooking:"Potwierdź rezerwację",meetingWith:"Spotkanie",duration:"Czas trwania",minutes:"minut",joinMeeting:"Dołącz do spotkania"},uk:{title:"Libre Bot",subtitle:"ШІ-асистент документації",placeholder:"Поставте запитання...",welcomeMessage:"Привіт! Я можу допомогти вам знайти відповіді в документації. Що б ви хотіли дізнатися?",errorPrefix:"Вибачте, сталася помилка:",connectionError:"Вибачте, виникла проблема з підключенням. Будь ласка, спробуйте ще раз.",streamingNotSupported:"Вибачте, потокова передача не підтримується.",poweredBy:"Працює на",bookingTitle:"Записатися на прийом",selectDate:"Оберіть дату",selectTime:"Оберіть час",bookAppointment:"Записатися",noSlotsAvailable:"На цю дату немає вільного часу",loadingSlots:"Завантаження доступного часу...",bookingSuccess:"Ваш запис підтверджено!",bookingError:"Не вдалося записатися. Будь ласка, спробуйте ще раз.",backToChat:"Повернутися до чату",yourDetails:"Ваші дані",confirmBooking:"Підтвердити запис",meetingWith:"Зустріч",duration:"Тривалість",minutes:"хвилин",joinMeeting:"Приєднатися до зустрічі"},ms:{title:"Libre Bot",subtitle:"Pembantu Dokumentasi AI",placeholder:"Tanya soalan...",welcomeMessage:"Hai! Saya boleh membantu anda mencari jawapan dalam dokumentasi. Apa yang anda ingin tahu?",errorPrefix:"Maaf, berlaku ralat:",connectionError:"Maaf, ada masalah sambungan. Sila cuba lagi.",streamingNotSupported:"Maaf, penstriman tidak disokong.",poweredBy:"Dikuasakan oleh",bookingTitle:"Buat Temujanji",selectDate:"Pilih tarikh",selectTime:"Pilih masa",bookAppointment:"Buat Temujanji",noSlotsAvailable:"Tiada masa tersedia untuk tarikh ini",loadingSlots:"Memuatkan masa tersedia...",bookingSuccess:"Temujanji anda telah disahkan!",bookingError:"Gagal membuat temujanji. Sila cuba lagi.",backToChat:"Kembali ke sembang",yourDetails:"Maklumat Anda",confirmBooking:"Sahkan Tempahan",meetingWith:"Mesyuarat",duration:"Tempoh",minutes:"minit",joinMeeting:"Sertai Mesyuarat"},cs:{title:"Libre Bot",subtitle:"AI asistent dokumentace",placeholder:"Položte otázku...",welcomeMessage:"Ahoj! Mohu vám pomoci najít odpovědi v dokumentaci. Co byste chtěli vědět?",errorPrefix:"Omlouvám se, došlo k chybě:",connectionError:"Omlouvám se, měl jsem problém s připojením. Zkuste to prosím znovu.",streamingNotSupported:"Omlouvám se, streamování není podporováno.",poweredBy:"Využívá technologii",bookingTitle:"Rezervovat schůzku",selectDate:"Vyberte datum",selectTime:"Vyberte čas",bookAppointment:"Rezervovat schůzku",noSlotsAvailable:"Na tento den nejsou k dispozici žádné termíny",loadingSlots:"Načítání dostupných termínů...",bookingSuccess:"Vaše schůzka byla potvrzena!",bookingError:"Rezervace se nezdařila. Zkuste to prosím znovu.",backToChat:"Zpět na chat",yourDetails:"Vaše údaje",confirmBooking:"Potvrdit rezervaci",meetingWith:"Schůzka",duration:"Délka",minutes:"minut",joinMeeting:"Připojit se ke schůzce"},sv:{title:"Libre Bot",subtitle:"AI-dokumentationsassistent",placeholder:"Ställ en fråga...",welcomeMessage:"Hej! Jag kan hjälpa dig hitta svar i dokumentationen. Vad vill du veta?",errorPrefix:"Tyvärr uppstod ett fel:",connectionError:"Tyvärr hade jag problem med anslutningen. Försök igen.",streamingNotSupported:"Tyvärr stöds inte streaming.",poweredBy:"Drivs av",bookingTitle:"Boka tid",selectDate:"Välj datum",selectTime:"Välj tid",bookAppointment:"Boka tid",noSlotsAvailable:"Inga lediga tider för detta datum",loadingSlots:"Laddar lediga tider...",bookingSuccess:"Din bokning har bekräftats!",bookingError:"Bokningen misslyckades. Försök igen.",backToChat:"Tillbaka till chatten",yourDetails:"Dina uppgifter",confirmBooking:"Bekräfta bokning",meetingWith:"Möte",duration:"Varaktighet",minutes:"minuter",joinMeeting:"Gå med i mötet"},da:{title:"Libre Bot",subtitle:"AI-dokumentationsassistent",placeholder:"Stil et spørgsmål...",welcomeMessage:"Hej! Jeg kan hjælpe dig med at finde svar i dokumentationen. Hvad vil du gerne vide?",errorPrefix:"Beklager, der opstod en fejl:",connectionError:"Beklager, jeg havde problemer med forbindelsen. Prøv venligst igen.",streamingNotSupported:"Beklager, streaming understøttes ikke.",poweredBy:"Drevet af",bookingTitle:"Book en tid",selectDate:"Vælg dato",selectTime:"Vælg tidspunkt",bookAppointment:"Book tid",noSlotsAvailable:"Ingen ledige tider på denne dato",loadingSlots:"Indlæser ledige tider...",bookingSuccess:"Din booking er bekræftet!",bookingError:"Booking mislykkedes. Prøv venligst igen.",backToChat:"Tilbage til chat",yourDetails:"Dine oplysninger",confirmBooking:"Bekræft booking",meetingWith:"Møde",duration:"Varighed",minutes:"minutter",joinMeeting:"Deltag i mødet"},is:{title:"Libre Bot",subtitle:"Gervigreind skjalahjálpari",placeholder:"Spurðu spurningu...",welcomeMessage:"Hæ! Ég get hjálpað þér að finna svör í skjölunum. Hvað viltu vita?",errorPrefix:"Því miður kom upp villa:",connectionError:"Því miður átti ég í vandræðum með tengingu. Vinsamlegast reyndu aftur.",streamingNotSupported:"Því miður er streymi ekki stutt.",poweredBy:"Knúið af",bookingTitle:"Bóka tíma",selectDate:"Veldu dagsetningu",selectTime:"Veldu tíma",bookAppointment:"Bóka tíma",noSlotsAvailable:"Engir tímar lausir á þessum degi",loadingSlots:"Hleð lausum tímum...",bookingSuccess:"Bókun þín hefur verið staðfest!",bookingError:"Bókun mistókst. Vinsamlegast reyndu aftur.",backToChat:"Til baka í spjall",yourDetails:"Þínar upplýsingar",confirmBooking:"Staðfesta bókun",meetingWith:"Fundur",duration:"Lengd",minutes:"mínútur",joinMeeting:"Taka þátt í fundi"}},i=["ar"];class r{constructor(e){if(this.container=null,this.modal=null,this.messagesContainer=null,this.input=null,this.isOpen=!1,this.messages=[],this.isLoading=!1,this.sessionId=null,this.translations=o.en,this.isRtl=!1,this.mode="chat",this.bookingConfig=null,this.selectedDate=null,this.selectedSlot=null,this.availableSlots=[],this.bookingContainer=null,this.chatContainer=null,this.inputArea=null,this.defaultConfig={apiUrl:"https://librebot.io/api",position:"bottom-right",theme:"dark",primaryColor:"#14b8a6",title:"",subtitle:"",placeholder:"",welcomeMessage:"",streaming:!0,whiteLabel:!1,autoLoadConfig:!0,lang:"en"},!e.apiKey)return void console.error("LibreBot: API key is required");const n=e.lang||function(){const e=document.documentElement.lang?.toLowerCase().split("-")[0];if(e&&e in o)return e;const n=navigator.language?.toLowerCase().split("-")[0];return n&&n in o?n:"en"}();this.translations=o[n]||o.en,this.isRtl=i.includes(n);const t={...e,lang:n,title:e.title||this.translations.title,subtitle:e.subtitle||this.translations.subtitle,placeholder:e.placeholder||this.translations.placeholder,welcomeMessage:e.welcomeMessage||this.translations.welcomeMessage};this.config={...this.defaultConfig,...t},this.config.autoLoadConfig?this.loadRemoteConfig().then(()=>this.init()):this.init()}async loadRemoteConfig(){try{const e=await fetch(`${this.config.apiUrl}/widget-config?key=${this.config.apiKey}`);if(e.ok){const n=await e.json();this.config={...this.defaultConfig,...n,...this.getProvidedConfig(),apiKey:this.config.apiKey,apiUrl:this.config.apiUrl,autoLoadConfig:this.config.autoLoadConfig}}}catch(e){console.warn("LibreBot: Could not load remote config, using defaults",e)}}getProvidedConfig(){const e={},n=this.config,t=this.defaultConfig;return n.position!==t.position&&(e.position=n.position),n.theme!==t.theme&&(e.theme=n.theme),n.primaryColor!==t.primaryColor&&(e.primaryColor=n.primaryColor),n.title!==t.title&&(e.title=n.title),n.subtitle!==t.subtitle&&(e.subtitle=n.subtitle),n.placeholder!==t.placeholder&&(e.placeholder=n.placeholder),n.welcomeMessage!==t.welcomeMessage&&(e.welcomeMessage=n.welcomeMessage),n.streaming!==t.streaming&&(e.streaming=n.streaming),n.whiteLabel!==t.whiteLabel&&(e.whiteLabel=n.whiteLabel),e}init(){this.loadSession(),this.injectStyles(),this.createWidget(),this.config.welcomeMessage&&this.addMessage("assistant",this.config.welcomeMessage)}getSessionKey(){return`librebot_session_${this.config.apiKey}`}loadSession(){try{const e=localStorage.getItem(this.getSessionKey());e&&(this.sessionId=e)}catch{}}saveSession(e){try{this.sessionId=e,localStorage.setItem(this.getSessionKey(),e)}catch{}}injectStyles(){const e="librebot-styles";if(document.getElementById(e))return;const n=document.createElement("style");n.id=e,n.textContent=((e="#14b8a6")=>`\n /* Reset all styles for the widget to prevent host page interference */\n .librebot-widget,\n .librebot-widget *,\n .librebot-widget *::before,\n .librebot-widget *::after,\n .librebot-fab,\n .librebot-fab *,\n .librebot-modal,\n .librebot-modal * {\n box-sizing: border-box !important;\n margin: 0;\n padding: 0;\n border: 0;\n outline: none;\n font: inherit;\n vertical-align: baseline;\n text-decoration: none;\n }\n\n .librebot-widget {\n --lb-primary: ${e};\n --lb-primary-hover: color-mix(in srgb, ${e} 85%, white);\n --lb-bg: #0f0f0f;\n --lb-bg-secondary: #1a1a1a;\n --lb-text: #ffffff;\n --lb-text-secondary: #9ca3af;\n --lb-border: #2a2a2a;\n font-family: 'Roboto Mono', -apple-system, BlinkMacSystemFont, 'Segoe UI', monospace !important;\n font-size: 14px !important;\n line-height: 1.5 !important;\n color: var(--lb-text);\n }\n\n .librebot-widget.light {\n --lb-bg: #ffffff;\n --lb-bg-secondary: #f5f5f5;\n --lb-text: #1a1a1a;\n --lb-text-secondary: #666666;\n --lb-border: #e0e0e0;\n }\n\n .librebot-fab {\n all: unset !important;\n position: fixed !important;\n bottom: 20px !important;\n right: 20px !important;\n width: 56px !important;\n height: 56px !important;\n border-radius: 50% !important;\n background: var(--lb-primary) !important;\n border: none !important;\n outline: none !important;\n cursor: pointer !important;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3) !important;\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n transition: transform 0.2s, box-shadow 0.2s !important;\n z-index: 999998 !important;\n -webkit-appearance: none !important;\n -moz-appearance: none !important;\n appearance: none !important;\n box-sizing: border-box !important;\n }\n\n .librebot-fab:hover {\n transform: scale(1.05) !important;\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4) !important;\n }\n\n .librebot-fab:focus,\n .librebot-fab:active {\n outline: none !important;\n border: none !important;\n }\n\n .librebot-fab svg {\n width: 24px !important;\n height: 24px !important;\n fill: white !important;\n border: none !important;\n outline: none !important;\n background: transparent !important;\n }\n\n .librebot-fab.left {\n right: auto !important;\n left: 20px !important;\n }\n\n .librebot-modal {\n position: fixed;\n bottom: 90px;\n right: 20px;\n width: 380px;\n max-width: calc(100vw - 40px);\n height: 520px;\n max-height: calc(100vh - 120px);\n background: var(--lb-bg);\n border: 1px solid var(--lb-border);\n border-radius: 16px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n z-index: 999999;\n opacity: 0;\n transform: translateY(20px) scale(0.95);\n transition: opacity 0.2s, transform 0.2s;\n pointer-events: none;\n }\n\n .librebot-modal.open {\n opacity: 1;\n transform: translateY(0) scale(1);\n pointer-events: auto;\n }\n\n .librebot-modal.left {\n right: auto;\n left: 20px;\n }\n\n .librebot-header {\n padding: 16px;\n border-bottom: 1px solid var(--lb-border);\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n .librebot-header-content {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .librebot-avatar {\n width: 40px;\n height: 40px;\n border-radius: 10px;\n background: var(--lb-primary);\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .librebot-avatar svg {\n width: 24px;\n height: 24px;\n fill: white;\n }\n\n .librebot-title {\n font-weight: 600;\n color: var(--lb-text);\n margin: 0;\n font-size: 16px;\n }\n\n .librebot-subtitle {\n font-size: 12px;\n color: var(--lb-text-secondary);\n margin: 0;\n }\n\n .librebot-close {\n width: 32px;\n height: 32px;\n border-radius: 8px;\n border: none;\n background: transparent;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--lb-text-secondary);\n transition: background 0.2s;\n }\n\n .librebot-close:hover {\n background: var(--lb-bg-secondary);\n }\n\n .librebot-chat-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n .librebot-messages {\n flex: 1;\n overflow-y: auto;\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .librebot-booking-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n .librebot-message {\n max-width: 85%;\n padding: 10px 14px;\n border-radius: 16px;\n word-wrap: break-word;\n }\n\n .librebot-message.user {\n align-self: flex-end;\n background: var(--lb-primary);\n color: white;\n border-bottom-right-radius: 4px;\n }\n\n .librebot-message.assistant {\n align-self: flex-start;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n border-bottom-left-radius: 4px;\n }\n\n /* Inline code */\n .librebot-inline-code {\n background: rgba(0, 0, 0, 0.3);\n padding: 2px 6px;\n border-radius: 4px;\n font-family: 'Roboto Mono', 'Fira Code', 'Consolas', monospace;\n font-size: 12px;\n color: #f0f0f0;\n }\n\n .librebot-widget.light .librebot-inline-code {\n background: rgba(0, 0, 0, 0.08);\n color: #1a1a1a;\n }\n\n /* Code blocks */\n .librebot-code-block {\n background: #0d1117;\n padding: 12px 14px;\n border-radius: 8px;\n overflow-x: auto;\n margin: 10px 0;\n border: 1px solid rgba(255, 255, 255, 0.1);\n position: relative;\n }\n\n .librebot-widget.light .librebot-code-block {\n background: #f6f8fa;\n border-color: rgba(0, 0, 0, 0.1);\n }\n\n .librebot-code-block code {\n font-family: 'Roboto Mono', 'Fira Code', 'Consolas', monospace;\n font-size: 12px;\n line-height: 1.5;\n color: #e6edf3;\n background: none;\n padding: 0;\n white-space: pre;\n display: block;\n }\n\n .librebot-widget.light .librebot-code-block code {\n color: #24292f;\n }\n\n .librebot-code-block[data-language]::before {\n content: attr(data-language);\n position: absolute;\n top: 6px;\n right: 10px;\n font-size: 10px;\n color: rgba(255, 255, 255, 0.4);\n text-transform: uppercase;\n font-family: 'Roboto Mono', monospace;\n }\n\n .librebot-widget.light .librebot-code-block[data-language]::before {\n color: rgba(0, 0, 0, 0.3);\n }\n\n /* Headers */\n .librebot-h2 {\n font-size: 16px;\n font-weight: 600;\n margin: 12px 0 8px 0;\n color: var(--lb-text);\n }\n\n .librebot-h3 {\n font-size: 14px;\n font-weight: 600;\n margin: 10px 0 6px 0;\n color: var(--lb-text);\n }\n\n .librebot-h4 {\n font-size: 13px;\n font-weight: 600;\n margin: 8px 0 4px 0;\n color: var(--lb-text);\n }\n\n /* Lists */\n .librebot-ul, .librebot-ol {\n margin: 8px 0;\n padding-left: 20px;\n }\n\n .librebot-ul {\n list-style-type: disc;\n }\n\n .librebot-ol {\n list-style-type: decimal;\n }\n\n .librebot-li, .librebot-li-ordered {\n margin: 4px 0;\n padding-left: 4px;\n line-height: 1.5;\n }\n\n /* Links */\n .librebot-link {\n color: var(--lb-primary);\n text-decoration: none;\n border-bottom: 1px solid transparent;\n transition: border-color 0.2s;\n }\n\n .librebot-link:hover {\n border-bottom-color: var(--lb-primary);\n }\n\n /* Blockquotes */\n .librebot-blockquote {\n border-left: 3px solid var(--lb-primary);\n margin: 8px 0;\n padding: 4px 12px;\n color: var(--lb-text-secondary);\n font-style: italic;\n background: rgba(0, 0, 0, 0.1);\n border-radius: 0 6px 6px 0;\n }\n\n .librebot-widget.light .librebot-blockquote {\n background: rgba(0, 0, 0, 0.04);\n }\n\n /* Horizontal rule */\n .librebot-hr {\n border: none;\n border-top: 1px solid var(--lb-border);\n margin: 12px 0;\n }\n\n /* Strong and emphasis */\n .librebot-message strong {\n font-weight: 600;\n }\n\n .librebot-message em {\n font-style: italic;\n }\n\n /* Legacy support for old code elements */\n .librebot-message code:not(.librebot-inline-code) {\n background: rgba(0, 0, 0, 0.2);\n padding: 2px 6px;\n border-radius: 4px;\n font-family: monospace;\n font-size: 13px;\n }\n\n .librebot-message pre:not(.librebot-code-block) {\n background: rgba(0, 0, 0, 0.3);\n padding: 10px;\n border-radius: 8px;\n overflow-x: auto;\n margin: 8px 0;\n }\n\n .librebot-message pre:not(.librebot-code-block) code {\n background: none;\n padding: 0;\n }\n\n .librebot-typing {\n display: flex;\n gap: 4px;\n padding: 12px 16px;\n align-self: flex-start;\n background: var(--lb-bg-secondary);\n border-radius: 16px;\n border-bottom-left-radius: 4px;\n }\n\n .librebot-typing span {\n width: 8px;\n height: 8px;\n background: var(--lb-text-secondary);\n border-radius: 50%;\n animation: librebot-bounce 1.4s infinite ease-in-out;\n }\n\n .librebot-typing span:nth-child(1) { animation-delay: 0s; }\n .librebot-typing span:nth-child(2) { animation-delay: 0.2s; }\n .librebot-typing span:nth-child(3) { animation-delay: 0.4s; }\n\n @keyframes librebot-bounce {\n 0%, 80%, 100% { transform: scale(0.8); opacity: 0.5; }\n 40% { transform: scale(1); opacity: 1; }\n }\n\n .librebot-input-area {\n padding: 12px 16px;\n border-top: 1px solid var(--lb-border);\n display: flex;\n gap: 8px;\n }\n\n .librebot-input {\n flex: 1;\n padding: 10px 14px;\n border: 1px solid var(--lb-border);\n border-radius: 24px;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n font-size: 14px;\n outline: none;\n transition: border-color 0.2s;\n }\n\n .librebot-input:focus {\n border-color: var(--lb-primary);\n }\n\n .librebot-input::placeholder {\n color: var(--lb-text-secondary);\n }\n\n .librebot-send {\n width: 40px;\n height: 40px;\n border-radius: 50%;\n border: none;\n background: var(--lb-primary);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.2s;\n }\n\n .librebot-send:hover {\n background: var(--lb-primary-hover);\n }\n\n .librebot-send:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .librebot-send svg {\n width: 18px;\n height: 18px;\n fill: white;\n }\n\n .librebot-welcome {\n text-align: center;\n padding: 20px;\n color: var(--lb-text-secondary);\n }\n\n .librebot-welcome-icon {\n width: 48px;\n height: 48px;\n margin: 0 auto 12px;\n background: var(--lb-primary);\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .librebot-welcome-icon svg {\n width: 28px;\n height: 28px;\n fill: white;\n }\n\n .librebot-powered {\n text-align: center;\n padding: 8px;\n font-size: 11px;\n color: var(--lb-text-secondary);\n border-top: 1px solid var(--lb-border);\n }\n\n .librebot-powered a {\n color: var(--lb-primary);\n text-decoration: none;\n }\n\n .librebot-powered a:hover {\n text-decoration: underline;\n }\n\n /* RTL Support */\n .librebot-widget.rtl {\n direction: rtl;\n text-align: right;\n }\n\n .librebot-widget.rtl .librebot-message.user {\n align-self: flex-start;\n border-bottom-right-radius: 16px;\n border-bottom-left-radius: 4px;\n }\n\n .librebot-widget.rtl .librebot-message.assistant {\n align-self: flex-end;\n border-bottom-left-radius: 16px;\n border-bottom-right-radius: 4px;\n }\n\n .librebot-widget.rtl .librebot-typing {\n align-self: flex-end;\n border-bottom-left-radius: 16px;\n border-bottom-right-radius: 4px;\n }\n\n .librebot-widget.rtl .librebot-header-content {\n flex-direction: row-reverse;\n }\n\n .librebot-widget.rtl .librebot-input {\n text-align: right;\n }\n\n .librebot-widget.rtl .librebot-input-area {\n flex-direction: row-reverse;\n }\n\n .librebot-widget.rtl .librebot-ul,\n .librebot-widget.rtl .librebot-ol {\n padding-left: 0;\n padding-right: 20px;\n }\n\n .librebot-widget.rtl .librebot-blockquote {\n border-left: none;\n border-right: 3px solid var(--lb-primary);\n border-radius: 6px 0 0 6px;\n }\n\n /* Booking Mode Styles */\n .librebot-booking {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n .librebot-booking-header {\n padding: 12px 16px;\n border-bottom: 1px solid var(--lb-border);\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .librebot-booking-back {\n width: 32px;\n height: 32px;\n border-radius: 8px;\n border: none;\n background: transparent;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--lb-text-secondary);\n transition: background 0.2s;\n }\n\n .librebot-booking-back:hover {\n background: var(--lb-bg-secondary);\n }\n\n .librebot-booking-back svg {\n width: 20px;\n height: 20px;\n }\n\n .librebot-booking-title {\n font-size: 16px;\n font-weight: 600;\n color: var(--lb-text);\n margin: 0;\n }\n\n .librebot-booking-content {\n flex: 1;\n overflow-y: auto;\n padding: 16px;\n }\n\n .librebot-booking-section {\n margin-bottom: 20px;\n }\n\n .librebot-booking-label {\n display: block;\n font-size: 13px;\n font-weight: 600;\n color: var(--lb-text);\n margin-bottom: 8px;\n }\n\n .librebot-date-input {\n width: 100%;\n padding: 10px 14px;\n border: 1px solid var(--lb-border);\n border-radius: 8px;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n font-size: 14px;\n outline: none;\n cursor: pointer;\n }\n\n .librebot-date-input:focus {\n border-color: var(--lb-primary);\n }\n\n .librebot-slots {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 8px;\n }\n\n .librebot-slot {\n padding: 10px 8px;\n border: 1px solid var(--lb-border);\n border-radius: 8px;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n font-size: 13px;\n cursor: pointer;\n transition: all 0.2s;\n text-align: center;\n }\n\n .librebot-slot:hover {\n border-color: var(--lb-primary);\n }\n\n .librebot-slot.selected {\n background: var(--lb-primary);\n border-color: var(--lb-primary);\n color: white;\n }\n\n .librebot-slot:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .librebot-slots-loading,\n .librebot-slots-empty {\n text-align: center;\n padding: 20px;\n color: var(--lb-text-secondary);\n font-size: 13px;\n }\n\n .librebot-form-field {\n margin-bottom: 16px;\n }\n\n .librebot-form-label {\n display: block;\n font-size: 13px;\n font-weight: 500;\n color: var(--lb-text);\n margin-bottom: 6px;\n }\n\n .librebot-form-label .required {\n color: #ef4444;\n margin-left: 2px;\n }\n\n .librebot-form-input,\n .librebot-form-textarea,\n .librebot-form-select {\n width: 100%;\n padding: 10px 14px;\n border: 1px solid var(--lb-border);\n border-radius: 8px;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n font-size: 14px;\n outline: none;\n font-family: inherit;\n }\n\n .librebot-form-input:focus,\n .librebot-form-textarea:focus,\n .librebot-form-select:focus {\n border-color: var(--lb-primary);\n }\n\n .librebot-form-textarea {\n min-height: 80px;\n resize: vertical;\n }\n\n .librebot-booking-submit {\n width: 100%;\n padding: 12px;\n border: none;\n border-radius: 8px;\n background: var(--lb-primary);\n color: white;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n transition: background 0.2s;\n margin-top: 12px;\n }\n\n .librebot-booking-submit:hover {\n background: var(--lb-primary-hover);\n }\n\n .librebot-booking-submit:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n /* Booking Success */\n .librebot-booking-success {\n text-align: center;\n padding: 24px 16px;\n }\n\n .librebot-booking-success-icon {\n width: 60px;\n height: 60px;\n margin: 0 auto 16px;\n background: var(--lb-primary);\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .librebot-booking-success-icon svg {\n width: 32px;\n height: 32px;\n fill: white;\n }\n\n .librebot-booking-success h3 {\n font-size: 18px;\n font-weight: 600;\n color: var(--lb-text);\n margin: 0 0 16px;\n }\n\n .librebot-booking-details {\n background: var(--lb-bg-secondary);\n border-radius: 8px;\n padding: 16px;\n margin-bottom: 16px;\n text-align: left;\n }\n\n .librebot-booking-detail {\n display: flex;\n justify-content: space-between;\n padding: 8px 0;\n border-bottom: 1px solid var(--lb-border);\n font-size: 13px;\n }\n\n .librebot-booking-detail:last-child {\n border-bottom: none;\n }\n\n .librebot-booking-detail-label {\n color: var(--lb-text-secondary);\n }\n\n .librebot-booking-detail-value {\n color: var(--lb-text);\n font-weight: 500;\n }\n\n .librebot-meeting-link {\n display: inline-block;\n padding: 12px 24px;\n background: var(--lb-primary);\n color: white;\n text-decoration: none;\n border-radius: 8px;\n font-weight: 600;\n margin-bottom: 16px;\n transition: background 0.2s;\n }\n\n .librebot-meeting-link:hover {\n background: var(--lb-primary-hover);\n }\n\n /* RTL Booking Styles */\n .librebot-widget.rtl .librebot-booking-header {\n flex-direction: row-reverse;\n }\n\n .librebot-widget.rtl .librebot-form-label .required {\n margin-left: 0;\n margin-right: 2px;\n }\n\n .librebot-widget.rtl .librebot-form-input,\n .librebot-widget.rtl .librebot-form-textarea {\n text-align: right;\n }\n\n .librebot-widget.rtl .librebot-booking-detail {\n flex-direction: row-reverse;\n }\n\n .librebot-widget.rtl .librebot-booking-details {\n text-align: right;\n }\n`)(this.config.primaryColor),document.head.appendChild(n)}createWidget(){this.container=document.createElement("div");let e="librebot-widget";"light"===this.config.theme&&(e+=" light"),this.isRtl&&(e+=" rtl"),this.container.className=e;const t=document.createElement("button");t.className="librebot-fab "+("bottom-left"===this.config.position?"left":""),t.innerHTML=n,t.onclick=()=>this.toggle(),this.modal=document.createElement("div"),this.modal.className="librebot-modal "+("bottom-left"===this.config.position?"left":""),this.modal.innerHTML=this.getModalHTML(),this.container.appendChild(t),this.container.appendChild(this.modal),document.body.appendChild(this.container),this.messagesContainer=this.modal.querySelector(".librebot-messages"),this.input=this.modal.querySelector(".librebot-input"),this.chatContainer=this.modal.querySelector(".librebot-chat-container"),this.bookingContainer=this.modal.querySelector(".librebot-booking-container"),this.inputArea=this.modal.querySelector(".librebot-input-area");const o=this.modal.querySelector(".librebot-close");o?.addEventListener("click",()=>this.close());const i=this.modal.querySelector(".librebot-send");if(i?.addEventListener("click",()=>this.sendMessage()),this.input?.addEventListener("keypress",e=>{"Enter"!==e.key||e.shiftKey||(e.preventDefault(),this.sendMessage())}),"auto"===this.config.theme){const e=window.matchMedia("(prefers-color-scheme: light)");this.updateTheme(e.matches),e.addEventListener("change",e=>this.updateTheme(e.matches))}}getModalHTML(){const e=this.config.whiteLabel?"":`<div class="librebot-powered">\n ${this.translations.poweredBy} <a href="https://librebot.io" target="_blank">Libre Bot</a>\n </div>`;return`\n <div class="librebot-header">\n <div class="librebot-header-content">\n <div class="librebot-avatar">${n}</div>\n <div>\n <h3 class="librebot-title">${this.config.title}</h3>\n <p class="librebot-subtitle">${this.config.subtitle}</p>\n </div>\n </div>\n <button class="librebot-close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg></button>\n </div>\n <div class="librebot-chat-container">\n <div class="librebot-messages"></div>\n </div>\n <div class="librebot-booking-container" style="display: none;"></div>\n <div class="librebot-input-area">\n <input type="text" class="librebot-input" placeholder="${this.config.placeholder}" />\n <button class="librebot-send"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg></button>\n </div>\n ${e}\n `}updateTheme(e){e?this.container?.classList.add("light"):this.container?.classList.remove("light")}toggle(){this.isOpen?this.close():this.open()}open(){this.isOpen=!0,this.modal?.classList.add("open"),this.input?.focus()}close(){this.isOpen=!1,this.modal?.classList.remove("open")}addMessage(e,n){const t={id:`msg-${Date.now()}`,role:e,content:n,timestamp:new Date};this.messages.push(t),this.renderMessage(t)}renderMessage(e){if(!this.messagesContainer)return;const n=document.createElement("div");n.className=`librebot-message ${e.role}`,n.innerHTML=this.formatMessage(e.content),this.messagesContainer.appendChild(n),this.scrollToBottom()}formatMessage(e){const n=e=>e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;"),t=[];let o=e.replace(/```\s*(\w*)\s*\n?([\s\S]*?)```/g,(e,o,i)=>{const r=n(i.trim()),a=o?` data-language="${n(o)}"`:"";return t.push(`<pre class="librebot-code-block"${a}><code>${r}</code></pre>`),`%%CODEBLOCK${t.length-1}%%`});const i=[];return o=o.replace(/`([^`]+)`/g,(e,t)=>(i.push(`<code class="librebot-inline-code">${n(t)}</code>`),`%%INLINECODE${i.length-1}%%`)),o=n(o),o=o.replace(/^### (.+)$/gm,'<h4 class="librebot-h4">$1</h4>'),o=o.replace(/^## (.+)$/gm,'<h3 class="librebot-h3">$1</h3>'),o=o.replace(/^# (.+)$/gm,'<h2 class="librebot-h2">$1</h2>'),o=o.replace(/\*\*\*([^*]+)\*\*\*/g,"<strong><em>$1</em></strong>"),o=o.replace(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>"),o=o.replace(/\*([^*]+)\*/g,"<em>$1</em>"),o=o.replace(/_([^_]+)_/g,"<em>$1</em>"),o=o.replace(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2" target="_blank" rel="noopener noreferrer" class="librebot-link">$1</a>'),o=o.replace(/^[\*\-] (.+)$/gm,'<li class="librebot-li">$1</li>'),o=o.replace(/(<li class="librebot-li">.*<\/li>\n?)+/g,e=>`<ul class="librebot-ul">${e}</ul>`),o=o.replace(/^\d+\. (.+)$/gm,'<li class="librebot-li-ordered">$1</li>'),o=o.replace(/(<li class="librebot-li-ordered">.*<\/li>\n?)+/g,e=>`<ol class="librebot-ol">${e}</ol>`),o=o.replace(/^---$/gm,'<hr class="librebot-hr">'),o=o.replace(/^&gt; (.+)$/gm,'<blockquote class="librebot-blockquote">$1</blockquote>'),o=o.replace(/\n/g,"<br>"),o=o.replace(/<\/(pre|ul|ol|blockquote|h[2-4])><br>/g,"</$1>"),o=o.replace(/<br><(pre|ul|ol|blockquote|h[2-4])/g,"<$1"),t.forEach((e,n)=>{o=o.replace(`%%CODEBLOCK${n}%%`,e)}),i.forEach((e,n)=>{o=o.replace(`%%INLINECODE${n}%%`,e)}),o}showTyping(){const e=document.createElement("div");return e.className="librebot-typing",e.innerHTML="<span></span><span></span><span></span>",this.messagesContainer?.appendChild(e),this.scrollToBottom(),e}scrollToBottom(){this.messagesContainer&&(this.messagesContainer.scrollTop=this.messagesContainer.scrollHeight)}async sendMessage(){if(!this.input||this.isLoading)return;const e=this.input.value.trim();if(!e)return;this.addMessage("user",e),this.input.value="",this.isLoading=!0;const n=this.showTyping();try{if(this.config.streaming)await this.callStreamingAPI(e,n);else{const t=await this.callAPI(e);n.remove(),t.error?this.addMessage("assistant",`${this.translations.errorPrefix} ${t.error}`):(this.addMessage("assistant",t.response),this.handleBookingIntent(t))}}catch(e){n.remove(),this.addMessage("assistant",this.translations.connectionError),console.error("LibreBot error:",e)}finally{this.isLoading=!1}}async callStreamingAPI(e,n){const t=await fetch(`${this.config.apiUrl}/chat`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({message:e,context:this.getConversationContext(),stream:!0,sessionId:this.sessionId})});if(!t.ok){n.remove();const e=await t.json().catch(()=>({error:"Request failed"}));return void this.addMessage("assistant",`${this.translations.errorPrefix} ${e.error||"Request failed"}`)}n.remove();const o=document.createElement("div");o.className="librebot-message assistant",this.messagesContainer?.appendChild(o);const i=t.body?.getReader();if(!i)return void this.addMessage("assistant",this.translations.streamingNotSupported);const r=new TextDecoder;let a="",s=null;try{for(;;){const{done:e,value:n}=await i.read();if(e)break;const t=r.decode(n,{stream:!0}).split("\n").filter(e=>""!==e.trim());for(const e of t)if(e.startsWith("data: ")){const n=e.slice(6);if("[DONE]"===n)continue;try{const e=JSON.parse(n);e.content&&(a+=e.content,o.innerHTML=this.formatMessage(a),this.scrollToBottom()),e.sessionId&&this.saveSession(e.sessionId),e.booking_intent&&e.booking_config&&(s=e)}catch{}}}}catch(e){console.error("Stream reading error:",e)}this.messages.push({id:`msg-${Date.now()}`,role:"assistant",content:a,timestamp:new Date}),s&&this.handleBookingIntent(s)}async callAPI(e){const n=await fetch(`${this.config.apiUrl}/chat`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({message:e,context:this.getConversationContext(),sessionId:this.sessionId})});if(!n.ok){return{response:"",error:(await n.json().catch(()=>({error:"Request failed"}))).error||"Request failed"}}const t=await n.json();return t.sessionId&&this.saveSession(t.sessionId),t}getConversationContext(){return this.messages.slice(-10).map(e=>({role:e.role,content:e.content}))}destroy(){this.container?.remove(),document.getElementById("librebot-styles")?.remove()}clearSession(){try{this.sessionId=null,this.messages=[],localStorage.removeItem(this.getSessionKey()),this.messagesContainer&&(this.messagesContainer.innerHTML=""),this.config.welcomeMessage&&this.addMessage("assistant",this.config.welcomeMessage)}catch{}}switchToBookingMode(e){this.bookingConfig=e,this.mode="booking",this.selectedDate=null,this.selectedSlot=null,this.availableSlots=[],this.chatContainer&&(this.chatContainer.style.display="none"),this.inputArea&&(this.inputArea.style.display="none"),this.bookingContainer&&(this.bookingContainer.style.display="flex",this.renderBookingUI())}switchToChatMode(){this.mode="chat",this.bookingConfig=null,this.selectedDate=null,this.selectedSlot=null,this.availableSlots=[],this.chatContainer&&(this.chatContainer.style.display="flex"),this.inputArea&&(this.inputArea.style.display="flex"),this.bookingContainer&&(this.bookingContainer.style.display="none",this.bookingContainer.innerHTML="")}renderBookingUI(){if(!this.bookingContainer||!this.bookingConfig)return;const e=new Date;e.setDate(e.getDate()+1);const n=new Date;n.setDate(n.getDate()+(this.bookingConfig.advance_booking_days||30));const o=this.bookingConfig.form_fields.map(e=>{const n=e.required?'<span class="required">*</span>':"",t=e.required?"required":"";if("textarea"===e.type)return`\n <div class="librebot-form-field">\n <label class="librebot-form-label">${e.label}${n}</label>\n <textarea\n class="librebot-form-textarea"\n name="${e.name}"\n placeholder="${e.placeholder||""}"\n ${t}\n ></textarea>\n </div>\n `;if("select"===e.type&&e.options){const o=e.options.map(e=>`<option value="${e}">${e}</option>`).join("");return`\n <div class="librebot-form-field">\n <label class="librebot-form-label">${e.label}${n}</label>\n <select\n class="librebot-form-select"\n name="${e.name}"\n ${t}\n >\n <option value="">${e.placeholder||"Select..."}</option>\n ${o}\n </select>\n </div>\n `}return`\n <div class="librebot-form-field">\n <label class="librebot-form-label">${e.label}${n}</label>\n <input\n type="${e.type}"\n class="librebot-form-input"\n name="${e.name}"\n placeholder="${e.placeholder||""}"\n ${t}\n />\n </div>\n `}).join("");this.bookingContainer.innerHTML=`\n <div class="librebot-booking">\n <div class="librebot-booking-header">\n <button class="librebot-booking-back">${t}</button>\n <h3 class="librebot-booking-title">${this.translations.bookingTitle}</h3>\n </div>\n <div class="librebot-booking-content">\n <div class="librebot-booking-section">\n <label class="librebot-booking-label">${this.translations.selectDate}</label>\n <input\n type="date"\n class="librebot-date-input"\n min="${e.toISOString().split("T")[0]}"\n max="${n.toISOString().split("T")[0]}"\n />\n </div>\n <div class="librebot-booking-section">\n <label class="librebot-booking-label">${this.translations.selectTime}</label>\n <div class="librebot-slots-container">\n <div class="librebot-slots-empty">${this.translations.selectDate}</div>\n </div>\n </div>\n <div class="librebot-booking-section librebot-form-section" style="display: none;">\n <label class="librebot-booking-label">${this.translations.yourDetails}</label>\n <form class="librebot-booking-form">\n ${o}\n <button type="submit" class="librebot-booking-submit" disabled>\n ${this.translations.confirmBooking}\n </button>\n </form>\n </div>\n </div>\n </div>\n `;const i=this.bookingContainer.querySelector(".librebot-booking-back");i?.addEventListener("click",()=>this.switchToChatMode());const r=this.bookingContainer.querySelector(".librebot-date-input");r?.addEventListener("change",e=>{const n=e.target;this.selectedDate=n.value,this.selectedSlot=null,this.loadAvailableSlots(n.value)});const a=this.bookingContainer.querySelector(".librebot-booking-form");a?.addEventListener("submit",e=>{e.preventDefault(),this.submitBooking(a)})}async loadAvailableSlots(e){const n=this.bookingContainer?.querySelector(".librebot-slots-container");if(n){n.innerHTML=`<div class="librebot-slots-loading">${this.translations.loadingSlots}</div>`;try{const t=await fetch(`${this.config.apiUrl}/bookings/slots?date=${e}&timezone=${Intl.DateTimeFormat().resolvedOptions().timeZone}`,{headers:{Authorization:`Bearer ${this.config.apiKey}`}});if(!t.ok)throw new Error("Failed to load slots");const o=await t.json();if(this.availableSlots=o.slots||[],0===this.availableSlots.length)return void(n.innerHTML=`<div class="librebot-slots-empty">${this.translations.noSlotsAvailable}</div>`);const i=this.availableSlots.map(e=>{const n=e.available?"":"disabled";return`\n <button\n type="button"\n class="librebot-slot"\n data-start="${e.start}"\n data-end="${e.end}"\n ${n}\n >${e.start}</button>\n `}).join("");n.innerHTML=`<div class="librebot-slots">${i}</div>`;n.querySelectorAll(".librebot-slot").forEach(e=>{e.addEventListener("click",()=>{const n=e.getAttribute("data-start")||"",t=e.getAttribute("data-end")||"";this.selectSlot({start:n,end:t,available:!0},e)})})}catch(e){console.error("Failed to load slots:",e),n.innerHTML=`<div class="librebot-slots-empty">${this.translations.bookingError}</div>`}}}selectSlot(e,n){this.selectedSlot=e;const t=this.bookingContainer?.querySelectorAll(".librebot-slot");t?.forEach(e=>e.classList.remove("selected")),n.classList.add("selected");const o=this.bookingContainer?.querySelector(".librebot-form-section");o&&(o.style.display="block");const i=this.bookingContainer?.querySelector(".librebot-booking-submit");i&&(i.disabled=!1),o?.scrollIntoView({behavior:"smooth",block:"start"})}async submitBooking(e){if(!this.selectedDate||!this.selectedSlot||!this.bookingConfig)return;const n=e.querySelector(".librebot-booking-submit");n&&(n.disabled=!0,n.textContent="...");const t=new FormData(e),o={};t.forEach((e,n)=>{o[n]=e.toString()});try{const e=await fetch(`${this.config.apiUrl}/bookings/submit`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({date:this.selectedDate,slot_start:this.selectedSlot.start,slot_end:this.selectedSlot.end,form_data:o,session_id:this.sessionId,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone})}),n=await e.json();if(!n.success||!n.booking)throw new Error(n.error||"Booking failed");this.renderBookingSuccess(n.booking)}catch(e){console.error("Booking submission failed:",e),n&&(n.disabled=!1,n.textContent=this.translations.confirmBooking),alert(this.translations.bookingError)}}renderBookingSuccess(e){if(!this.bookingContainer)return;const n=new Date(e.scheduled_start),o=n.toLocaleDateString(void 0,{weekday:"long",year:"numeric",month:"long",day:"numeric"}),i=n.toLocaleTimeString(void 0,{hour:"numeric",minute:"2-digit"}),r=e.meeting_link?`<a href="${e.meeting_link}" target="_blank" class="librebot-meeting-link">${this.translations.joinMeeting}</a>`:"";this.bookingContainer.innerHTML=`\n <div class="librebot-booking">\n <div class="librebot-booking-header">\n <button class="librebot-booking-back">${t}</button>\n <h3 class="librebot-booking-title">${this.translations.bookingTitle}</h3>\n </div>\n <div class="librebot-booking-content">\n <div class="librebot-booking-success">\n <div class="librebot-booking-success-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg></div>\n <h3>${this.translations.bookingSuccess}</h3>\n <div class="librebot-booking-details">\n <div class="librebot-booking-detail">\n <span class="librebot-booking-detail-label">${this.translations.selectDate}</span>\n <span class="librebot-booking-detail-value">${o}</span>\n </div>\n <div class="librebot-booking-detail">\n <span class="librebot-booking-detail-label">${this.translations.selectTime}</span>\n <span class="librebot-booking-detail-value">${i}</span>\n </div>\n <div class="librebot-booking-detail">\n <span class="librebot-booking-detail-label">${this.translations.duration}</span>\n <span class="librebot-booking-detail-value">${e.duration_minutes} ${this.translations.minutes}</span>\n </div>\n </div>\n ${r}\n <button class="librebot-booking-submit" type="button">${this.translations.backToChat}</button>\n </div>\n </div>\n </div>\n `;const a=this.bookingContainer.querySelector(".librebot-booking-back");a?.addEventListener("click",()=>this.switchToChatMode());const s=this.bookingContainer.querySelector(".librebot-booking-submit");s?.addEventListener("click",()=>this.switchToChatMode())}handleBookingIntent(e){e.booking_intent&&e.booking_config&&setTimeout(()=>{this.switchToBookingMode(e.booking_config)},1500)}}return async function(){const e=document.currentScript||document.querySelector('script[data-key][src*="librebot"]');if(!e)return void console.warn("LibreBot: Could not find script tag");const n=e.dataset.key||e.dataset.apiKey||"",t=e.dataset.apiUrl||"https://api.librebot.io";if(!n)return void console.error("LibreBot: Missing data-key attribute");let o={};try{const e=await fetch(`${t}/api/widget-config?key=${encodeURIComponent(n)}`);e.ok&&(o=await e.json())}catch(e){}const i={apiKey:n,apiUrl:t,position:e.dataset.position||o.position||"bottom-right",theme:e.dataset.theme||o.theme||"dark",primaryColor:e.dataset.color||o.primaryColor||"#14b8a6",title:e.dataset.title||o.title||"Libre Bot",subtitle:e.dataset.subtitle||o.subtitle||"AI Documentation Assistant",placeholder:e.dataset.placeholder||o.placeholder||void 0,welcomeMessage:e.dataset.welcome||o.welcomeMessage||void 0,streaming:"false"!==e.dataset.streaming,whiteLabel:o.whiteLabel||!1,lang:e.dataset.lang||o.lang},a=()=>{window.LibreBot=new r(i)};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",a):a()}(),e.LibreBotWidget=r,e}({});
1
+ var LibreBot=function(exports){'use strict';const chatIcon='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path></svg>',backIcon='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>',WIDGET_TRANSLATIONS={en:{title:'Libre Bot',subtitle:'AI Documentation Assistant',placeholder:'Ask a question...',welcomeMessage:'Hi! I can help you find answers in the documentation. What would you like to know?',errorPrefix:'Sorry, I encountered an error:',connectionError:'Sorry, I had trouble connecting. Please try again.',streamingNotSupported:'Sorry, streaming is not supported.',poweredBy:'Powered by',bookingTitle:'Book an Appointment',selectDate:'Select a date',selectTime:'Select a time',bookAppointment:'Book Appointment',noSlotsAvailable:'No time slots available for this date',loadingSlots:'Loading available times...',bookingSuccess:'Your appointment has been confirmed!',bookingError:'Failed to book appointment. Please try again.',backToChat:'Back to chat',yourDetails:'Your Details',confirmBooking:'Confirm Booking',meetingWith:'Meeting',duration:'Duration',minutes:'minutes',joinMeeting:'Join Meeting'},fr:{title:'Libre Bot',subtitle:'Assistant IA de documentation',placeholder:'Posez une question...',welcomeMessage:'Bonjour ! Je peux vous aider à trouver des réponses dans la documentation. Que souhaitez-vous savoir ?',errorPrefix:'Désolé, j\'ai rencontré une erreur :',connectionError:'Désolé, j\'ai eu du mal à me connecter. Veuillez réessayer.',streamingNotSupported:'Désolé, le streaming n\'est pas pris en charge.',poweredBy:'Propulsé par',bookingTitle:'Prendre rendez-vous',selectDate:'Choisir une date',selectTime:'Choisir une heure',bookAppointment:'Prendre rendez-vous',noSlotsAvailable:'Aucun créneau disponible pour cette date',loadingSlots:'Chargement des horaires...',bookingSuccess:'Votre rendez-vous a été confirmé !',bookingError:'Échec de la réservation. Veuillez réessayer.',backToChat:'Retour au chat',yourDetails:'Vos informations',confirmBooking:'Confirmer la réservation',meetingWith:'Réunion',duration:'Durée',minutes:'minutes',joinMeeting:'Rejoindre la réunion'},de:{title:'Libre Bot',subtitle:'KI-Dokumentationsassistent',placeholder:'Stellen Sie eine Frage...',welcomeMessage:'Hallo! Ich kann Ihnen helfen, Antworten in der Dokumentation zu finden. Was möchten Sie wissen?',errorPrefix:'Entschuldigung, ein Fehler ist aufgetreten:',connectionError:'Entschuldigung, ich hatte Verbindungsprobleme. Bitte versuchen Sie es erneut.',streamingNotSupported:'Entschuldigung, Streaming wird nicht unterstützt.',poweredBy:'Betrieben von',bookingTitle:'Termin buchen',selectDate:'Datum auswählen',selectTime:'Uhrzeit auswählen',bookAppointment:'Termin buchen',noSlotsAvailable:'Keine Termine für dieses Datum verfügbar',loadingSlots:'Verfügbare Zeiten werden geladen...',bookingSuccess:'Ihr Termin wurde bestätigt!',bookingError:'Terminbuchung fehlgeschlagen. Bitte versuchen Sie es erneut.',backToChat:'Zurück zum Chat',yourDetails:'Ihre Angaben',confirmBooking:'Buchung bestätigen',meetingWith:'Meeting',duration:'Dauer',minutes:'Minuten',joinMeeting:'An Meeting teilnehmen'},nl:{title:'Libre Bot',subtitle:'AI-documentatieassistent',placeholder:'Stel een vraag...',welcomeMessage:'Hallo! Ik kan je helpen antwoorden te vinden in de documentatie. Wat wil je weten?',errorPrefix:'Sorry, er is een fout opgetreden:',connectionError:'Sorry, ik had moeite met verbinden. Probeer het opnieuw.',streamingNotSupported:'Sorry, streaming wordt niet ondersteund.',poweredBy:'Mogelijk gemaakt door',bookingTitle:'Afspraak maken',selectDate:'Selecteer een datum',selectTime:'Selecteer een tijd',bookAppointment:'Afspraak maken',noSlotsAvailable:'Geen tijdsloten beschikbaar voor deze datum',loadingSlots:'Beschikbare tijden laden...',bookingSuccess:'Je afspraak is bevestigd!',bookingError:'Afspraak maken mislukt. Probeer het opnieuw.',backToChat:'Terug naar chat',yourDetails:'Je gegevens',confirmBooking:'Boeking bevestigen',meetingWith:'Vergadering',duration:'Duur',minutes:'minuten',joinMeeting:'Deelnemen aan vergadering'},es:{title:'Libre Bot',subtitle:'Asistente de documentación con IA',placeholder:'Haz una pregunta...',welcomeMessage:'¡Hola! Puedo ayudarte a encontrar respuestas en la documentación. ¿Qué te gustaría saber?',errorPrefix:'Lo siento, encontré un error:',connectionError:'Lo siento, tuve problemas para conectarme. Por favor, inténtalo de nuevo.',streamingNotSupported:'Lo siento, el streaming no está soportado.',poweredBy:'Desarrollado por',bookingTitle:'Reservar cita',selectDate:'Seleccionar fecha',selectTime:'Seleccionar hora',bookAppointment:'Reservar cita',noSlotsAvailable:'No hay horarios disponibles para esta fecha',loadingSlots:'Cargando horarios disponibles...',bookingSuccess:'¡Tu cita ha sido confirmada!',bookingError:'Error al reservar la cita. Por favor, inténtalo de nuevo.',backToChat:'Volver al chat',yourDetails:'Tus datos',confirmBooking:'Confirmar reserva',meetingWith:'Reunión',duration:'Duración',minutes:'minutos',joinMeeting:'Unirse a la reunión'},pt:{title:'Libre Bot',subtitle:'Assistente de documentação com IA',placeholder:'Faça uma pergunta...',welcomeMessage:'Olá! Posso ajudá-lo a encontrar respostas na documentação. O que você gostaria de saber?',errorPrefix:'Desculpe, encontrei um erro:',connectionError:'Desculpe, tive problemas para conectar. Por favor, tente novamente.',streamingNotSupported:'Desculpe, streaming não é suportado.',poweredBy:'Desenvolvido por',bookingTitle:'Agendar consulta',selectDate:'Selecionar data',selectTime:'Selecionar horário',bookAppointment:'Agendar consulta',noSlotsAvailable:'Nenhum horário disponível para esta data',loadingSlots:'Carregando horários disponíveis...',bookingSuccess:'Seu agendamento foi confirmado!',bookingError:'Falha ao agendar. Por favor, tente novamente.',backToChat:'Voltar ao chat',yourDetails:'Seus dados',confirmBooking:'Confirmar agendamento',meetingWith:'Reunião',duration:'Duração',minutes:'minutos',joinMeeting:'Entrar na reunião'},ko:{title:'Libre Bot',subtitle:'AI 문서 도우미',placeholder:'질문하세요...',welcomeMessage:'안녕하세요! 문서에서 답변을 찾는 데 도움을 드릴 수 있습니다. 무엇을 알고 싶으신가요?',errorPrefix:'죄송합니다. 오류가 발생했습니다:',connectionError:'죄송합니다. 연결에 문제가 있습니다. 다시 시도해 주세요.',streamingNotSupported:'죄송합니다. 스트리밍이 지원되지 않습니다.',poweredBy:'제공:',bookingTitle:'예약하기',selectDate:'날짜 선택',selectTime:'시간 선택',bookAppointment:'예약하기',noSlotsAvailable:'선택한 날짜에 가능한 시간이 없습니다',loadingSlots:'가능한 시간 로딩 중...',bookingSuccess:'예약이 확정되었습니다!',bookingError:'예약에 실패했습니다. 다시 시도해 주세요.',backToChat:'채팅으로 돌아가기',yourDetails:'정보 입력',confirmBooking:'예약 확정',meetingWith:'미팅',duration:'소요 시간',minutes:'분',joinMeeting:'미팅 참가'},ja:{title:'Libre Bot',subtitle:'AIドキュメントアシスタント',placeholder:'質問してください...',welcomeMessage:'こんにちは!ドキュメントから回答を見つけるお手伝いができます。何をお知りになりたいですか?',errorPrefix:'申し訳ありません。エラーが発生しました:',connectionError:'申し訳ありません。接続に問題がありました。もう一度お試しください。',streamingNotSupported:'申し訳ありません。ストリーミングはサポートされていません。',poweredBy:'提供:',bookingTitle:'予約する',selectDate:'日付を選択',selectTime:'時間を選択',bookAppointment:'予約する',noSlotsAvailable:'この日は空き時間がありません',loadingSlots:'空き時間を読み込み中...',bookingSuccess:'ご予約が確定しました!',bookingError:'予約に失敗しました。もう一度お試しください。',backToChat:'チャットに戻る',yourDetails:'お客様情報',confirmBooking:'予約を確定',meetingWith:'ミーティング',duration:'所要時間',minutes:'分',joinMeeting:'ミーティングに参加'},zh:{title:'Libre Bot',subtitle:'AI文档助手',placeholder:'请提问...',welcomeMessage:'您好!我可以帮助您在文档中查找答案。您想了解什么?',errorPrefix:'抱歉,遇到了错误:',connectionError:'抱歉,连接出现问题。请重试。',streamingNotSupported:'抱歉,不支持流式传输。',poweredBy:'由以下提供支持:',bookingTitle:'预约',selectDate:'选择日期',selectTime:'选择时间',bookAppointment:'预约',noSlotsAvailable:'该日期没有可用时间段',loadingSlots:'加载可用时间...',bookingSuccess:'您的预约已确认!',bookingError:'预约失败。请重试。',backToChat:'返回聊天',yourDetails:'您的信息',confirmBooking:'确认预约',meetingWith:'会议',duration:'时长',minutes:'分钟',joinMeeting:'加入会议'},hi:{title:'Libre Bot',subtitle:'AI दस्तावेज़ सहायक',placeholder:'कोई प्रश्न पूछें...',welcomeMessage:'नमस्ते! मैं आपको दस्तावेज़ों में उत्तर खोजने में मदद कर सकता हूं। आप क्या जानना चाहते हैं?',errorPrefix:'क्षमा करें, एक त्रुटि हुई:',connectionError:'क्षमा करें, कनेक्ट करने में समस्या हुई। कृपया पुनः प्रयास करें।',streamingNotSupported:'क्षमा करें, स्ट्रीमिंग समर्थित नहीं है।',poweredBy:'द्वारा संचालित',bookingTitle:'अपॉइंटमेंट बुक करें',selectDate:'तिथि चुनें',selectTime:'समय चुनें',bookAppointment:'अपॉइंटमेंट बुक करें',noSlotsAvailable:'इस तिथि के लिए कोई समय उपलब्ध नहीं है',loadingSlots:'उपलब्ध समय लोड हो रहा है...',bookingSuccess:'आपकी अपॉइंटमेंट की पुष्टि हो गई है!',bookingError:'बुकिंग विफल। कृपया पुनः प्रयास करें।',backToChat:'चैट पर वापस जाएं',yourDetails:'आपका विवरण',confirmBooking:'बुकिंग की पुष्टि करें',meetingWith:'मीटिंग',duration:'अवधि',minutes:'मिनट',joinMeeting:'मीटिंग में शामिल हों'},ar:{title:'Libre Bot',subtitle:'مساعد الوثائق بالذكاء الاصطناعي',placeholder:'اطرح سؤالاً...',welcomeMessage:'مرحباً! يمكنني مساعدتك في العثور على إجابات في الوثائق. ماذا تريد أن تعرف؟',errorPrefix:'عذراً، حدث خطأ:',connectionError:'عذراً، واجهت مشكلة في الاتصال. يرجى المحاولة مرة أخرى.',streamingNotSupported:'عذراً، البث غير مدعوم.',poweredBy:'مدعوم من',bookingTitle:'حجز موعد',selectDate:'اختر التاريخ',selectTime:'اختر الوقت',bookAppointment:'حجز موعد',noSlotsAvailable:'لا توجد أوقات متاحة لهذا التاريخ',loadingSlots:'جاري تحميل الأوقات المتاحة...',bookingSuccess:'تم تأكيد موعدك!',bookingError:'فشل الحجز. يرجى المحاولة مرة أخرى.',backToChat:'العودة إلى المحادثة',yourDetails:'بياناتك',confirmBooking:'تأكيد الحجز',meetingWith:'اجتماع',duration:'المدة',minutes:'دقيقة',joinMeeting:'انضم إلى الاجتماع'},bn:{title:'Libre Bot',subtitle:'AI ডকুমেন্টেশন সহায়ক',placeholder:'একটি প্রশ্ন জিজ্ঞাসা করুন...',welcomeMessage:'হ্যালো! আমি আপনাকে ডকুমেন্টেশনে উত্তর খুঁজে পেতে সাহায্য করতে পারি। আপনি কী জানতে চান?',errorPrefix:'দুঃখিত, একটি ত্রুটি ঘটেছে:',connectionError:'দুঃখিত, সংযোগে সমস্যা হয়েছে। অনুগ্রহ করে আবার চেষ্টা করুন।',streamingNotSupported:'দুঃখিত, স্ট্রিমিং সমর্থিত নয়।',poweredBy:'দ্বারা চালিত',bookingTitle:'অ্যাপয়েন্টমেন্ট বুক করুন',selectDate:'তারিখ নির্বাচন করুন',selectTime:'সময় নির্বাচন করুন',bookAppointment:'অ্যাপয়েন্টমেন্ট বুক করুন',noSlotsAvailable:'এই তারিখে কোনো সময় উপলব্ধ নেই',loadingSlots:'উপলব্ধ সময় লোড হচ্ছে...',bookingSuccess:'আপনার অ্যাপয়েন্টমেন্ট নিশ্চিত হয়েছে!',bookingError:'বুকিং ব্যর্থ হয়েছে। অনুগ্রহ করে আবার চেষ্টা করুন।',backToChat:'চ্যাটে ফিরে যান',yourDetails:'আপনার তথ্য',confirmBooking:'বুকিং নিশ্চিত করুন',meetingWith:'মিটিং',duration:'সময়কাল',minutes:'মিনিট',joinMeeting:'মিটিংয়ে যোগ দিন'},ru:{title:'Libre Bot',subtitle:'ИИ-ассистент по документации',placeholder:'Задайте вопрос...',welcomeMessage:'Привет! Я могу помочь вам найти ответы в документации. Что бы вы хотели узнать?',errorPrefix:'Извините, произошла ошибка:',connectionError:'Извините, возникла проблема с подключением. Пожалуйста, попробуйте снова.',streamingNotSupported:'Извините, потоковая передача не поддерживается.',poweredBy:'Работает на',bookingTitle:'Записаться на приём',selectDate:'Выберите дату',selectTime:'Выберите время',bookAppointment:'Записаться',noSlotsAvailable:'На эту дату нет свободного времени',loadingSlots:'Загрузка доступного времени...',bookingSuccess:'Ваша запись подтверждена!',bookingError:'Не удалось записаться. Пожалуйста, попробуйте снова.',backToChat:'Вернуться к чату',yourDetails:'Ваши данные',confirmBooking:'Подтвердить запись',meetingWith:'Встреча',duration:'Продолжительность',minutes:'минут',joinMeeting:'Присоединиться к встрече'},id:{title:'Libre Bot',subtitle:'Asisten Dokumentasi AI',placeholder:'Ajukan pertanyaan...',welcomeMessage:'Halo! Saya dapat membantu Anda menemukan jawaban di dokumentasi. Apa yang ingin Anda ketahui?',errorPrefix:'Maaf, terjadi kesalahan:',connectionError:'Maaf, ada masalah koneksi. Silakan coba lagi.',streamingNotSupported:'Maaf, streaming tidak didukung.',poweredBy:'Didukung oleh',bookingTitle:'Buat Janji',selectDate:'Pilih tanggal',selectTime:'Pilih waktu',bookAppointment:'Buat Janji',noSlotsAvailable:'Tidak ada waktu tersedia untuk tanggal ini',loadingSlots:'Memuat waktu tersedia...',bookingSuccess:'Janji Anda telah dikonfirmasi!',bookingError:'Gagal membuat janji. Silakan coba lagi.',backToChat:'Kembali ke chat',yourDetails:'Data Anda',confirmBooking:'Konfirmasi Pemesanan',meetingWith:'Pertemuan',duration:'Durasi',minutes:'menit',joinMeeting:'Gabung Pertemuan'},vi:{title:'Libre Bot',subtitle:'Trợ lý tài liệu AI',placeholder:'Đặt câu hỏi...',welcomeMessage:'Xin chào! Tôi có thể giúp bạn tìm câu trả lời trong tài liệu. Bạn muốn biết điều gì?',errorPrefix:'Xin lỗi, đã xảy ra lỗi:',connectionError:'Xin lỗi, có vấn đề kết nối. Vui lòng thử lại.',streamingNotSupported:'Xin lỗi, không hỗ trợ phát trực tuyến.',poweredBy:'Được cung cấp bởi',bookingTitle:'Đặt lịch hẹn',selectDate:'Chọn ngày',selectTime:'Chọn giờ',bookAppointment:'Đặt lịch hẹn',noSlotsAvailable:'Không có thời gian trống cho ngày này',loadingSlots:'Đang tải thời gian trống...',bookingSuccess:'Lịch hẹn của bạn đã được xác nhận!',bookingError:'Đặt lịch thất bại. Vui lòng thử lại.',backToChat:'Quay lại chat',yourDetails:'Thông tin của bạn',confirmBooking:'Xác nhận đặt lịch',meetingWith:'Cuộc họp',duration:'Thời lượng',minutes:'phút',joinMeeting:'Tham gia cuộc họp'},tr:{title:'Libre Bot',subtitle:'Yapay Zeka Dokümantasyon Asistanı',placeholder:'Bir soru sorun...',welcomeMessage:'Merhaba! Dokümantasyonda cevap bulmanıza yardımcı olabilirim. Ne öğrenmek istersiniz?',errorPrefix:'Üzgünüm, bir hata oluştu:',connectionError:'Üzgünüm, bağlantı sorunu yaşadım. Lütfen tekrar deneyin.',streamingNotSupported:'Üzgünüm, akış desteklenmiyor.',poweredBy:'Tarafından desteklenmektedir',bookingTitle:'Randevu Al',selectDate:'Tarih seçin',selectTime:'Saat seçin',bookAppointment:'Randevu Al',noSlotsAvailable:'Bu tarih için müsait saat yok',loadingSlots:'Müsait saatler yükleniyor...',bookingSuccess:'Randevunuz onaylandı!',bookingError:'Randevu alınamadı. Lütfen tekrar deneyin.',backToChat:'Sohbete dön',yourDetails:'Bilgileriniz',confirmBooking:'Randevuyu Onayla',meetingWith:'Toplantı',duration:'Süre',minutes:'dakika',joinMeeting:'Toplantıya Katıl'},it:{title:'Libre Bot',subtitle:'Assistente documentazione AI',placeholder:'Fai una domanda...',welcomeMessage:'Ciao! Posso aiutarti a trovare risposte nella documentazione. Cosa vorresti sapere?',errorPrefix:'Mi dispiace, si è verificato un errore:',connectionError:'Mi dispiace, ho avuto problemi di connessione. Per favore riprova.',streamingNotSupported:'Mi dispiace, lo streaming non è supportato.',poweredBy:'Offerto da',bookingTitle:'Prenota un Appuntamento',selectDate:'Seleziona una data',selectTime:'Seleziona un orario',bookAppointment:'Prenota Appuntamento',noSlotsAvailable:'Nessun orario disponibile per questa data',loadingSlots:'Caricamento orari disponibili...',bookingSuccess:'Il tuo appuntamento è stato confermato!',bookingError:'Prenotazione fallita. Per favore riprova.',backToChat:'Torna alla chat',yourDetails:'I tuoi dati',confirmBooking:'Conferma Prenotazione',meetingWith:'Riunione',duration:'Durata',minutes:'minuti',joinMeeting:'Partecipa alla Riunione'},th:{title:'Libre Bot',subtitle:'ผู้ช่วยเอกสาร AI',placeholder:'ถามคำถาม...',welcomeMessage:'สวัสดี! ฉันสามารถช่วยคุณค้นหาคำตอบในเอกสารได้ คุณต้องการทราบอะไร?',errorPrefix:'ขออภัย เกิดข้อผิดพลาด:',connectionError:'ขออภัย มีปัญหาในการเชื่อมต่อ กรุณาลองอีกครั้ง',streamingNotSupported:'ขออภัย ไม่รองรับการสตรีม',poweredBy:'ขับเคลื่อนโดย',bookingTitle:'จองนัดหมาย',selectDate:'เลือกวันที่',selectTime:'เลือกเวลา',bookAppointment:'จองนัดหมาย',noSlotsAvailable:'ไม่มีช่วงเวลาว่างสำหรับวันนี้',loadingSlots:'กำลังโหลดเวลาว่าง...',bookingSuccess:'การนัดหมายของคุณได้รับการยืนยันแล้ว!',bookingError:'การจองล้มเหลว กรุณาลองอีกครั้ง',backToChat:'กลับไปที่แชท',yourDetails:'ข้อมูลของคุณ',confirmBooking:'ยืนยันการจอง',meetingWith:'การประชุม',duration:'ระยะเวลา',minutes:'นาที',joinMeeting:'เข้าร่วมการประชุม'},pl:{title:'Libre Bot',subtitle:'Asystent dokumentacji AI',placeholder:'Zadaj pytanie...',welcomeMessage:'Cześć! Mogę pomóc Ci znaleźć odpowiedzi w dokumentacji. Co chciałbyś wiedzieć?',errorPrefix:'Przepraszam, wystąpił błąd:',connectionError:'Przepraszam, miałem problem z połączeniem. Spróbuj ponownie.',streamingNotSupported:'Przepraszam, streaming nie jest obsługiwany.',poweredBy:'Obsługiwane przez',bookingTitle:'Umów wizytę',selectDate:'Wybierz datę',selectTime:'Wybierz godzinę',bookAppointment:'Umów wizytę',noSlotsAvailable:'Brak dostępnych terminów na ten dzień',loadingSlots:'Ładowanie dostępnych terminów...',bookingSuccess:'Twoja wizyta została potwierdzona!',bookingError:'Nie udało się umówić wizyty. Spróbuj ponownie.',backToChat:'Wróć do czatu',yourDetails:'Twoje dane',confirmBooking:'Potwierdź rezerwację',meetingWith:'Spotkanie',duration:'Czas trwania',minutes:'minut',joinMeeting:'Dołącz do spotkania'},uk:{title:'Libre Bot',subtitle:'ШІ-асистент документації',placeholder:'Поставте запитання...',welcomeMessage:'Привіт! Я можу допомогти вам знайти відповіді в документації. Що б ви хотіли дізнатися?',errorPrefix:'Вибачте, сталася помилка:',connectionError:'Вибачте, виникла проблема з підключенням. Будь ласка, спробуйте ще раз.',streamingNotSupported:'Вибачте, потокова передача не підтримується.',poweredBy:'Працює на',bookingTitle:'Записатися на прийом',selectDate:'Оберіть дату',selectTime:'Оберіть час',bookAppointment:'Записатися',noSlotsAvailable:'На цю дату немає вільного часу',loadingSlots:'Завантаження доступного часу...',bookingSuccess:'Ваш запис підтверджено!',bookingError:'Не вдалося записатися. Будь ласка, спробуйте ще раз.',backToChat:'Повернутися до чату',yourDetails:'Ваші дані',confirmBooking:'Підтвердити запис',meetingWith:'Зустріч',duration:'Тривалість',minutes:'хвилин',joinMeeting:'Приєднатися до зустрічі'},ms:{title:'Libre Bot',subtitle:'Pembantu Dokumentasi AI',placeholder:'Tanya soalan...',welcomeMessage:'Hai! Saya boleh membantu anda mencari jawapan dalam dokumentasi. Apa yang anda ingin tahu?',errorPrefix:'Maaf, berlaku ralat:',connectionError:'Maaf, ada masalah sambungan. Sila cuba lagi.',streamingNotSupported:'Maaf, penstriman tidak disokong.',poweredBy:'Dikuasakan oleh',bookingTitle:'Buat Temujanji',selectDate:'Pilih tarikh',selectTime:'Pilih masa',bookAppointment:'Buat Temujanji',noSlotsAvailable:'Tiada masa tersedia untuk tarikh ini',loadingSlots:'Memuatkan masa tersedia...',bookingSuccess:'Temujanji anda telah disahkan!',bookingError:'Gagal membuat temujanji. Sila cuba lagi.',backToChat:'Kembali ke sembang',yourDetails:'Maklumat Anda',confirmBooking:'Sahkan Tempahan',meetingWith:'Mesyuarat',duration:'Tempoh',minutes:'minit',joinMeeting:'Sertai Mesyuarat'},cs:{title:'Libre Bot',subtitle:'AI asistent dokumentace',placeholder:'Položte otázku...',welcomeMessage:'Ahoj! Mohu vám pomoci najít odpovědi v dokumentaci. Co byste chtěli vědět?',errorPrefix:'Omlouvám se, došlo k chybě:',connectionError:'Omlouvám se, měl jsem problém s připojením. Zkuste to prosím znovu.',streamingNotSupported:'Omlouvám se, streamování není podporováno.',poweredBy:'Využívá technologii',bookingTitle:'Rezervovat schůzku',selectDate:'Vyberte datum',selectTime:'Vyberte čas',bookAppointment:'Rezervovat schůzku',noSlotsAvailable:'Na tento den nejsou k dispozici žádné termíny',loadingSlots:'Načítání dostupných termínů...',bookingSuccess:'Vaše schůzka byla potvrzena!',bookingError:'Rezervace se nezdařila. Zkuste to prosím znovu.',backToChat:'Zpět na chat',yourDetails:'Vaše údaje',confirmBooking:'Potvrdit rezervaci',meetingWith:'Schůzka',duration:'Délka',minutes:'minut',joinMeeting:'Připojit se ke schůzce'},sv:{title:'Libre Bot',subtitle:'AI-dokumentationsassistent',placeholder:'Ställ en fråga...',welcomeMessage:'Hej! Jag kan hjälpa dig hitta svar i dokumentationen. Vad vill du veta?',errorPrefix:'Tyvärr uppstod ett fel:',connectionError:'Tyvärr hade jag problem med anslutningen. Försök igen.',streamingNotSupported:'Tyvärr stöds inte streaming.',poweredBy:'Drivs av',bookingTitle:'Boka tid',selectDate:'Välj datum',selectTime:'Välj tid',bookAppointment:'Boka tid',noSlotsAvailable:'Inga lediga tider för detta datum',loadingSlots:'Laddar lediga tider...',bookingSuccess:'Din bokning har bekräftats!',bookingError:'Bokningen misslyckades. Försök igen.',backToChat:'Tillbaka till chatten',yourDetails:'Dina uppgifter',confirmBooking:'Bekräfta bokning',meetingWith:'Möte',duration:'Varaktighet',minutes:'minuter',joinMeeting:'Gå med i mötet'},da:{title:'Libre Bot',subtitle:'AI-dokumentationsassistent',placeholder:'Stil et spørgsmål...',welcomeMessage:'Hej! Jeg kan hjælpe dig med at finde svar i dokumentationen. Hvad vil du gerne vide?',errorPrefix:'Beklager, der opstod en fejl:',connectionError:'Beklager, jeg havde problemer med forbindelsen. Prøv venligst igen.',streamingNotSupported:'Beklager, streaming understøttes ikke.',poweredBy:'Drevet af',bookingTitle:'Book en tid',selectDate:'Vælg dato',selectTime:'Vælg tidspunkt',bookAppointment:'Book tid',noSlotsAvailable:'Ingen ledige tider på denne dato',loadingSlots:'Indlæser ledige tider...',bookingSuccess:'Din booking er bekræftet!',bookingError:'Booking mislykkedes. Prøv venligst igen.',backToChat:'Tilbage til chat',yourDetails:'Dine oplysninger',confirmBooking:'Bekræft booking',meetingWith:'Møde',duration:'Varighed',minutes:'minutter',joinMeeting:'Deltag i mødet'},is:{title:'Libre Bot',subtitle:'Gervigreind skjalahjálpari',placeholder:'Spurðu spurningu...',welcomeMessage:'Hæ! Ég get hjálpað þér að finna svör í skjölunum. Hvað viltu vita?',errorPrefix:'Því miður kom upp villa:',connectionError:'Því miður átti ég í vandræðum með tengingu. Vinsamlegast reyndu aftur.',streamingNotSupported:'Því miður er streymi ekki stutt.',poweredBy:'Knúið af',bookingTitle:'Bóka tíma',selectDate:'Veldu dagsetningu',selectTime:'Veldu tíma',bookAppointment:'Bóka tíma',noSlotsAvailable:'Engir tímar lausir á þessum degi',loadingSlots:'Hleð lausum tímum...',bookingSuccess:'Bókun þín hefur verið staðfest!',bookingError:'Bókun mistókst. Vinsamlegast reyndu aftur.',backToChat:'Til baka í spjall',yourDetails:'Þínar upplýsingar',confirmBooking:'Staðfesta bókun',meetingWith:'Fundur',duration:'Lengd',minutes:'mínútur',joinMeeting:'Taka þátt í fundi'}},RTL_LANGUAGES=['ar'];class LibreBotWidget{constructor(config){if(this.container=null,this.modal=null,this.messagesContainer=null,this.input=null,this.isOpen=!1,this.messages=[],this.isLoading=!1,this.sessionId=null,this.translations=WIDGET_TRANSLATIONS.en,this.isRtl=!1,this.mode='chat',this.bookingConfig=null,this.selectedDate=null,this.selectedSlot=null,this.availableSlots=[],this.bookingContainer=null,this.chatContainer=null,this.inputArea=null,this.defaultConfig={apiUrl:'https://librebot.io/api',position:'bottom-right',theme:'dark',primaryColor:'#14b8a6',title:'',subtitle:'',placeholder:'',welcomeMessage:'',streaming:!0,whiteLabel:!1,autoLoadConfig:!0,lang:'en'},!config.apiKey)return void console.error('LibreBot: API key is required');const detectedLang=config.lang||function(){const htmlLang=document.documentElement.lang?.toLowerCase().split('-')[0];if(htmlLang&&htmlLang in WIDGET_TRANSLATIONS)return htmlLang;const navLang=navigator.language?.toLowerCase().split('-')[0];return navLang&&navLang in WIDGET_TRANSLATIONS?navLang:'en'}();this.translations=WIDGET_TRANSLATIONS[detectedLang]||WIDGET_TRANSLATIONS.en,this.isRtl=RTL_LANGUAGES.includes(detectedLang);const configWithTranslations={...config,lang:detectedLang,title:config.title||this.translations.title,subtitle:config.subtitle||this.translations.subtitle,placeholder:config.placeholder||this.translations.placeholder,welcomeMessage:config.welcomeMessage||this.translations.welcomeMessage};this.config={...this.defaultConfig,...configWithTranslations},this.config.autoLoadConfig?this.loadRemoteConfig().then(()=>this.init()):this.init()}async loadRemoteConfig(){try{const response=await fetch(`${this.config.apiUrl}/widget-config?key=${this.config.apiKey}`);if(response.ok){const remoteConfig=await response.json();this.config={...this.defaultConfig,...remoteConfig,...this.getProvidedConfig(),apiKey:this.config.apiKey,apiUrl:this.config.apiUrl,autoLoadConfig:this.config.autoLoadConfig}}}catch(error){console.warn('LibreBot: Could not load remote config, using defaults',error)}}getProvidedConfig(){const provided={},userConfig=this.config,defaults=this.defaultConfig;return userConfig.position!==defaults.position&&(provided.position=userConfig.position),userConfig.theme!==defaults.theme&&(provided.theme=userConfig.theme),userConfig.primaryColor!==defaults.primaryColor&&(provided.primaryColor=userConfig.primaryColor),userConfig.title!==defaults.title&&(provided.title=userConfig.title),userConfig.subtitle!==defaults.subtitle&&(provided.subtitle=userConfig.subtitle),userConfig.placeholder!==defaults.placeholder&&(provided.placeholder=userConfig.placeholder),userConfig.welcomeMessage!==defaults.welcomeMessage&&(provided.welcomeMessage=userConfig.welcomeMessage),userConfig.streaming!==defaults.streaming&&(provided.streaming=userConfig.streaming),userConfig.whiteLabel!==defaults.whiteLabel&&(provided.whiteLabel=userConfig.whiteLabel),provided}init(){this.loadSession(),this.injectStyles(),this.createWidget(),this.config.welcomeMessage&&this.addMessage('assistant',this.config.welcomeMessage)}getSessionKey(){return`librebot_session_${this.config.apiKey}`}loadSession(){try{const stored=localStorage.getItem(this.getSessionKey());stored&&(this.sessionId=stored)}catch{}}saveSession(sessionId){try{this.sessionId=sessionId,localStorage.setItem(this.getSessionKey(),sessionId)}catch{}}injectStyles(){if(document.getElementById('librebot-styles'))return;const style=document.createElement('style');style.id='librebot-styles',style.textContent=((primaryColor='#14b8a6')=>`\n /* Reset all styles for the widget to prevent host page interference */\n .librebot-widget,\n .librebot-widget *,\n .librebot-widget *::before,\n .librebot-widget *::after,\n .librebot-fab,\n .librebot-fab *,\n .librebot-modal,\n .librebot-modal * {\n box-sizing: border-box !important;\n margin: 0;\n padding: 0;\n border: 0;\n outline: none;\n font: inherit;\n vertical-align: baseline;\n text-decoration: none;\n }\n\n .librebot-widget {\n --lb-primary: ${primaryColor};\n --lb-primary-hover: color-mix(in srgb, ${primaryColor} 85%, white);\n --lb-bg: #0f0f0f;\n --lb-bg-secondary: #1a1a1a;\n --lb-text: #ffffff;\n --lb-text-secondary: #9ca3af;\n --lb-border: #2a2a2a;\n font-family: 'Roboto Mono', -apple-system, BlinkMacSystemFont, 'Segoe UI', monospace !important;\n font-size: 14px !important;\n line-height: 1.5 !important;\n color: var(--lb-text);\n }\n\n .librebot-widget.light {\n --lb-bg: #ffffff;\n --lb-bg-secondary: #f5f5f5;\n --lb-text: #1a1a1a;\n --lb-text-secondary: #666666;\n --lb-border: #e0e0e0;\n }\n\n .librebot-fab {\n all: unset !important;\n position: fixed !important;\n bottom: 20px !important;\n right: 20px !important;\n width: 56px !important;\n height: 56px !important;\n border-radius: 50% !important;\n background: var(--lb-primary) !important;\n border: none !important;\n outline: none !important;\n cursor: pointer !important;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3) !important;\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n transition: transform 0.2s, box-shadow 0.2s !important;\n z-index: 999998 !important;\n -webkit-appearance: none !important;\n -moz-appearance: none !important;\n appearance: none !important;\n box-sizing: border-box !important;\n }\n\n .librebot-fab:hover {\n transform: scale(1.05) !important;\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4) !important;\n }\n\n .librebot-fab:focus,\n .librebot-fab:active {\n outline: none !important;\n border: none !important;\n }\n\n .librebot-fab svg {\n width: 24px !important;\n height: 24px !important;\n fill: white !important;\n border: none !important;\n outline: none !important;\n background: transparent !important;\n }\n\n .librebot-fab.left {\n right: auto !important;\n left: 20px !important;\n }\n\n .librebot-modal {\n position: fixed;\n bottom: 90px;\n right: 20px;\n width: 380px;\n max-width: calc(100vw - 40px);\n height: 520px;\n max-height: calc(100vh - 120px);\n background: var(--lb-bg);\n border: 1px solid var(--lb-border);\n border-radius: 16px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n z-index: 999999;\n opacity: 0;\n transform: translateY(20px) scale(0.95);\n transition: opacity 0.2s, transform 0.2s;\n pointer-events: none;\n }\n\n .librebot-modal.open {\n opacity: 1;\n transform: translateY(0) scale(1);\n pointer-events: auto;\n }\n\n .librebot-modal.left {\n right: auto;\n left: 20px;\n }\n\n .librebot-header {\n padding: 16px;\n border-bottom: 1px solid var(--lb-border);\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n .librebot-header-content {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .librebot-avatar {\n width: 40px;\n height: 40px;\n border-radius: 10px;\n background: var(--lb-primary);\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .librebot-avatar svg {\n width: 24px;\n height: 24px;\n fill: white;\n }\n\n .librebot-title {\n font-weight: 600;\n color: var(--lb-text);\n margin: 0;\n font-size: 16px;\n }\n\n .librebot-subtitle {\n font-size: 12px;\n color: var(--lb-text-secondary);\n margin: 0;\n }\n\n .librebot-close {\n width: 32px;\n height: 32px;\n border-radius: 8px;\n border: none;\n background: transparent;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--lb-text-secondary);\n transition: background 0.2s;\n }\n\n .librebot-close:hover {\n background: var(--lb-bg-secondary);\n }\n\n .librebot-chat-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n .librebot-messages {\n flex: 1;\n overflow-y: auto;\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .librebot-booking-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n .librebot-message {\n max-width: 85%;\n padding: 10px 14px;\n border-radius: 16px;\n word-wrap: break-word;\n }\n\n .librebot-message.user {\n align-self: flex-end;\n background: var(--lb-primary);\n color: white;\n border-bottom-right-radius: 4px;\n }\n\n .librebot-message.assistant {\n align-self: flex-start;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n border-bottom-left-radius: 4px;\n }\n\n /* Inline code */\n .librebot-inline-code {\n background: rgba(0, 0, 0, 0.3);\n padding: 2px 6px;\n border-radius: 4px;\n font-family: 'Roboto Mono', 'Fira Code', 'Consolas', monospace;\n font-size: 12px;\n color: #f0f0f0;\n }\n\n .librebot-widget.light .librebot-inline-code {\n background: rgba(0, 0, 0, 0.08);\n color: #1a1a1a;\n }\n\n /* Code blocks */\n .librebot-code-block {\n background: #0d1117;\n padding: 12px 14px;\n border-radius: 8px;\n overflow-x: auto;\n margin: 10px 0;\n border: 1px solid rgba(255, 255, 255, 0.1);\n position: relative;\n }\n\n .librebot-widget.light .librebot-code-block {\n background: #f6f8fa;\n border-color: rgba(0, 0, 0, 0.1);\n }\n\n .librebot-code-block code {\n font-family: 'Roboto Mono', 'Fira Code', 'Consolas', monospace;\n font-size: 12px;\n line-height: 1.5;\n color: #e6edf3;\n background: none;\n padding: 0;\n white-space: pre;\n display: block;\n }\n\n .librebot-widget.light .librebot-code-block code {\n color: #24292f;\n }\n\n .librebot-code-block[data-language]::before {\n content: attr(data-language);\n position: absolute;\n top: 6px;\n right: 10px;\n font-size: 10px;\n color: rgba(255, 255, 255, 0.4);\n text-transform: uppercase;\n font-family: 'Roboto Mono', monospace;\n }\n\n .librebot-widget.light .librebot-code-block[data-language]::before {\n color: rgba(0, 0, 0, 0.3);\n }\n\n /* Headers */\n .librebot-h2 {\n font-size: 16px;\n font-weight: 600;\n margin: 12px 0 8px 0;\n color: var(--lb-text);\n }\n\n .librebot-h3 {\n font-size: 14px;\n font-weight: 600;\n margin: 10px 0 6px 0;\n color: var(--lb-text);\n }\n\n .librebot-h4 {\n font-size: 13px;\n font-weight: 600;\n margin: 8px 0 4px 0;\n color: var(--lb-text);\n }\n\n /* Lists */\n .librebot-ul, .librebot-ol {\n margin: 8px 0;\n padding-left: 20px;\n }\n\n .librebot-ul {\n list-style-type: disc;\n }\n\n .librebot-ol {\n list-style-type: decimal;\n }\n\n .librebot-li, .librebot-li-ordered {\n margin: 4px 0;\n padding-left: 4px;\n line-height: 1.5;\n }\n\n /* Links */\n .librebot-link {\n color: var(--lb-primary);\n text-decoration: none;\n border-bottom: 1px solid transparent;\n transition: border-color 0.2s;\n }\n\n .librebot-link:hover {\n border-bottom-color: var(--lb-primary);\n }\n\n /* Blockquotes */\n .librebot-blockquote {\n border-left: 3px solid var(--lb-primary);\n margin: 8px 0;\n padding: 4px 12px;\n color: var(--lb-text-secondary);\n font-style: italic;\n background: rgba(0, 0, 0, 0.1);\n border-radius: 0 6px 6px 0;\n }\n\n .librebot-widget.light .librebot-blockquote {\n background: rgba(0, 0, 0, 0.04);\n }\n\n /* Horizontal rule */\n .librebot-hr {\n border: none;\n border-top: 1px solid var(--lb-border);\n margin: 12px 0;\n }\n\n /* Strong and emphasis */\n .librebot-message strong {\n font-weight: 600;\n }\n\n .librebot-message em {\n font-style: italic;\n }\n\n /* Legacy support for old code elements */\n .librebot-message code:not(.librebot-inline-code) {\n background: rgba(0, 0, 0, 0.2);\n padding: 2px 6px;\n border-radius: 4px;\n font-family: monospace;\n font-size: 13px;\n }\n\n .librebot-message pre:not(.librebot-code-block) {\n background: rgba(0, 0, 0, 0.3);\n padding: 10px;\n border-radius: 8px;\n overflow-x: auto;\n margin: 8px 0;\n }\n\n .librebot-message pre:not(.librebot-code-block) code {\n background: none;\n padding: 0;\n }\n\n .librebot-typing {\n display: flex;\n gap: 4px;\n padding: 12px 16px;\n align-self: flex-start;\n background: var(--lb-bg-secondary);\n border-radius: 16px;\n border-bottom-left-radius: 4px;\n }\n\n .librebot-typing span {\n width: 8px;\n height: 8px;\n background: var(--lb-text-secondary);\n border-radius: 50%;\n animation: librebot-bounce 1.4s infinite ease-in-out;\n }\n\n .librebot-typing span:nth-child(1) { animation-delay: 0s; }\n .librebot-typing span:nth-child(2) { animation-delay: 0.2s; }\n .librebot-typing span:nth-child(3) { animation-delay: 0.4s; }\n\n @keyframes librebot-bounce {\n 0%, 80%, 100% { transform: scale(0.8); opacity: 0.5; }\n 40% { transform: scale(1); opacity: 1; }\n }\n\n .librebot-input-area {\n padding: 12px 16px;\n border-top: 1px solid var(--lb-border);\n display: flex;\n gap: 8px;\n }\n\n .librebot-input {\n flex: 1;\n padding: 10px 14px;\n border: 1px solid var(--lb-border);\n border-radius: 24px;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n font-size: 14px;\n outline: none;\n transition: border-color 0.2s;\n }\n\n .librebot-input:focus {\n border-color: var(--lb-primary);\n }\n\n .librebot-input::placeholder {\n color: var(--lb-text-secondary);\n }\n\n .librebot-send {\n width: 40px;\n height: 40px;\n border-radius: 50%;\n border: none;\n background: var(--lb-primary);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.2s;\n }\n\n .librebot-send:hover {\n background: var(--lb-primary-hover);\n }\n\n .librebot-send:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .librebot-send svg {\n width: 18px;\n height: 18px;\n fill: white;\n }\n\n .librebot-welcome {\n text-align: center;\n padding: 20px;\n color: var(--lb-text-secondary);\n }\n\n .librebot-welcome-icon {\n width: 48px;\n height: 48px;\n margin: 0 auto 12px;\n background: var(--lb-primary);\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .librebot-welcome-icon svg {\n width: 28px;\n height: 28px;\n fill: white;\n }\n\n .librebot-powered {\n text-align: center;\n padding: 8px;\n font-size: 11px;\n color: var(--lb-text-secondary);\n border-top: 1px solid var(--lb-border);\n }\n\n .librebot-powered a {\n color: var(--lb-primary);\n text-decoration: none;\n }\n\n .librebot-powered a:hover {\n text-decoration: underline;\n }\n\n /* RTL Support */\n .librebot-widget.rtl {\n direction: rtl;\n text-align: right;\n }\n\n .librebot-widget.rtl .librebot-message.user {\n align-self: flex-start;\n border-bottom-right-radius: 16px;\n border-bottom-left-radius: 4px;\n }\n\n .librebot-widget.rtl .librebot-message.assistant {\n align-self: flex-end;\n border-bottom-left-radius: 16px;\n border-bottom-right-radius: 4px;\n }\n\n .librebot-widget.rtl .librebot-typing {\n align-self: flex-end;\n border-bottom-left-radius: 16px;\n border-bottom-right-radius: 4px;\n }\n\n .librebot-widget.rtl .librebot-header-content {\n flex-direction: row-reverse;\n }\n\n .librebot-widget.rtl .librebot-input {\n text-align: right;\n }\n\n .librebot-widget.rtl .librebot-input-area {\n flex-direction: row-reverse;\n }\n\n .librebot-widget.rtl .librebot-ul,\n .librebot-widget.rtl .librebot-ol {\n padding-left: 0;\n padding-right: 20px;\n }\n\n .librebot-widget.rtl .librebot-blockquote {\n border-left: none;\n border-right: 3px solid var(--lb-primary);\n border-radius: 6px 0 0 6px;\n }\n\n /* Booking Mode Styles */\n .librebot-booking {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n .librebot-booking-header {\n padding: 12px 16px;\n border-bottom: 1px solid var(--lb-border);\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .librebot-booking-back {\n width: 32px;\n height: 32px;\n border-radius: 8px;\n border: none;\n background: transparent;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--lb-text-secondary);\n transition: background 0.2s;\n }\n\n .librebot-booking-back:hover {\n background: var(--lb-bg-secondary);\n }\n\n .librebot-booking-back svg {\n width: 20px;\n height: 20px;\n }\n\n .librebot-booking-title {\n font-size: 16px;\n font-weight: 600;\n color: var(--lb-text);\n margin: 0;\n }\n\n .librebot-booking-content {\n flex: 1;\n overflow-y: auto;\n padding: 16px;\n }\n\n .librebot-booking-section {\n margin-bottom: 20px;\n }\n\n .librebot-booking-label {\n display: block;\n font-size: 13px;\n font-weight: 600;\n color: var(--lb-text);\n margin-bottom: 8px;\n }\n\n .librebot-date-input {\n width: 100%;\n padding: 10px 14px;\n border: 1px solid var(--lb-border);\n border-radius: 8px;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n font-size: 14px;\n outline: none;\n cursor: pointer;\n }\n\n .librebot-date-input:focus {\n border-color: var(--lb-primary);\n }\n\n .librebot-slots {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 8px;\n }\n\n .librebot-slot {\n padding: 10px 8px;\n border: 1px solid var(--lb-border);\n border-radius: 8px;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n font-size: 13px;\n cursor: pointer;\n transition: all 0.2s;\n text-align: center;\n }\n\n .librebot-slot:hover {\n border-color: var(--lb-primary);\n }\n\n .librebot-slot.selected {\n background: var(--lb-primary);\n border-color: var(--lb-primary);\n color: white;\n }\n\n .librebot-slot:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .librebot-slots-loading,\n .librebot-slots-empty {\n text-align: center;\n padding: 20px;\n color: var(--lb-text-secondary);\n font-size: 13px;\n }\n\n .librebot-form-field {\n margin-bottom: 16px;\n }\n\n .librebot-form-label {\n display: block;\n font-size: 13px;\n font-weight: 500;\n color: var(--lb-text);\n margin-bottom: 6px;\n }\n\n .librebot-form-label .required {\n color: #ef4444;\n margin-left: 2px;\n }\n\n .librebot-form-input,\n .librebot-form-textarea,\n .librebot-form-select {\n width: 100%;\n padding: 10px 14px;\n border: 1px solid var(--lb-border);\n border-radius: 8px;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n font-size: 14px;\n outline: none;\n font-family: inherit;\n }\n\n .librebot-form-input:focus,\n .librebot-form-textarea:focus,\n .librebot-form-select:focus {\n border-color: var(--lb-primary);\n }\n\n .librebot-form-textarea {\n min-height: 80px;\n resize: vertical;\n }\n\n .librebot-booking-submit {\n width: 100%;\n padding: 12px;\n border: none;\n border-radius: 8px;\n background: var(--lb-primary);\n color: white;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n transition: background 0.2s;\n margin-top: 12px;\n }\n\n .librebot-booking-submit:hover {\n background: var(--lb-primary-hover);\n }\n\n .librebot-booking-submit:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n /* Booking Success */\n .librebot-booking-success {\n text-align: center;\n padding: 24px 16px;\n }\n\n .librebot-booking-success-icon {\n width: 60px;\n height: 60px;\n margin: 0 auto 16px;\n background: var(--lb-primary);\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .librebot-booking-success-icon svg {\n width: 32px;\n height: 32px;\n fill: white;\n }\n\n .librebot-booking-success h3 {\n font-size: 18px;\n font-weight: 600;\n color: var(--lb-text);\n margin: 0 0 16px;\n }\n\n .librebot-booking-details {\n background: var(--lb-bg-secondary);\n border-radius: 8px;\n padding: 16px;\n margin-bottom: 16px;\n text-align: left;\n }\n\n .librebot-booking-detail {\n display: flex;\n justify-content: space-between;\n padding: 8px 0;\n border-bottom: 1px solid var(--lb-border);\n font-size: 13px;\n }\n\n .librebot-booking-detail:last-child {\n border-bottom: none;\n }\n\n .librebot-booking-detail-label {\n color: var(--lb-text-secondary);\n }\n\n .librebot-booking-detail-value {\n color: var(--lb-text);\n font-weight: 500;\n }\n\n .librebot-meeting-link {\n display: inline-block;\n padding: 12px 24px;\n background: var(--lb-primary);\n color: white;\n text-decoration: none;\n border-radius: 8px;\n font-weight: 600;\n margin-bottom: 16px;\n transition: background 0.2s;\n }\n\n .librebot-meeting-link:hover {\n background: var(--lb-primary-hover);\n }\n\n /* RTL Booking Styles */\n .librebot-widget.rtl .librebot-booking-header {\n flex-direction: row-reverse;\n }\n\n .librebot-widget.rtl .librebot-form-label .required {\n margin-left: 0;\n margin-right: 2px;\n }\n\n .librebot-widget.rtl .librebot-form-input,\n .librebot-widget.rtl .librebot-form-textarea {\n text-align: right;\n }\n\n .librebot-widget.rtl .librebot-booking-detail {\n flex-direction: row-reverse;\n }\n\n .librebot-widget.rtl .librebot-booking-details {\n text-align: right;\n }\n`)(this.config.primaryColor),document.head.appendChild(style)}createWidget(){this.container=document.createElement('div');let className='librebot-widget';'light'===this.config.theme&&(className+=' light'),this.isRtl&&(className+=' rtl'),this.container.className=className;const fab=document.createElement('button');fab.className='librebot-fab '+('bottom-left'===this.config.position?'left':''),fab.innerHTML=chatIcon,fab.onclick=()=>this.toggle(),this.modal=document.createElement('div'),this.modal.className='librebot-modal '+('bottom-left'===this.config.position?'left':''),this.modal.innerHTML=this.getModalHTML(),this.container.appendChild(fab),this.container.appendChild(this.modal),document.body.appendChild(this.container),this.messagesContainer=this.modal.querySelector('.librebot-messages'),this.input=this.modal.querySelector('.librebot-input'),this.chatContainer=this.modal.querySelector('.librebot-chat-container'),this.bookingContainer=this.modal.querySelector('.librebot-booking-container'),this.inputArea=this.modal.querySelector('.librebot-input-area');const closeBtn=this.modal.querySelector('.librebot-close');closeBtn?.addEventListener('click',()=>this.close());const sendBtn=this.modal.querySelector('.librebot-send');if(sendBtn?.addEventListener('click',()=>this.sendMessage()),this.input?.addEventListener('keypress',e=>{'Enter'!==e.key||e.shiftKey||(e.preventDefault(),this.sendMessage())}),'auto'===this.config.theme){const mediaQuery=window.matchMedia('(prefers-color-scheme: light)');this.updateTheme(mediaQuery.matches),mediaQuery.addEventListener('change',e=>this.updateTheme(e.matches))}}getModalHTML(){const poweredByHtml=this.config.whiteLabel?'':`<div class="librebot-powered">\n ${this.translations.poweredBy} <a href="https://librebot.io" target="_blank">Libre Bot</a>\n </div>`;return`\n <div class="librebot-header">\n <div class="librebot-header-content">\n <div class="librebot-avatar">${chatIcon}</div>\n <div>\n <h3 class="librebot-title">${this.config.title}</h3>\n <p class="librebot-subtitle">${this.config.subtitle}</p>\n </div>\n </div>\n <button class="librebot-close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg></button>\n </div>\n <div class="librebot-chat-container">\n <div class="librebot-messages"></div>\n </div>\n <div class="librebot-booking-container" style="display: none;"></div>\n <div class="librebot-input-area">\n <input type="text" class="librebot-input" placeholder="${this.config.placeholder}" />\n <button class="librebot-send"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg></button>\n </div>\n ${poweredByHtml}\n `}updateTheme(isLight){isLight?this.container?.classList.add('light'):this.container?.classList.remove('light')}toggle(){this.isOpen?this.close():this.open()}open(){this.isOpen=!0,this.modal?.classList.add('open'),this.input?.focus()}close(){this.isOpen=!1,this.modal?.classList.remove('open')}addMessage(role,content){const message={id:`msg-${Date.now()}`,role:role,content:content,timestamp:new Date};this.messages.push(message),this.renderMessage(message)}renderMessage(message){if(!this.messagesContainer)return;const el=document.createElement('div');el.className=`librebot-message ${message.role}`,el.innerHTML=this.formatMessage(message.content),this.messagesContainer.appendChild(el),this.scrollToBottom()}formatMessage(content){const escapeHtml=text=>text.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#039;'),codeBlocks=[];let processed=content.replace(/```\s*(\w*)\s*\n?([\s\S]*?)```/g,(_,lang,code)=>{const escaped=escapeHtml(code.trim()),langClass=lang?` data-language="${escapeHtml(lang)}"`:'';return codeBlocks.push(`<pre class="librebot-code-block"${langClass}><code>${escaped}</code></pre>`),`%%CODEBLOCK${codeBlocks.length-1}%%`});const inlineCodes=[];return processed=processed.replace(/`([^`]+)`/g,(_,code)=>(inlineCodes.push(`<code class="librebot-inline-code">${escapeHtml(code)}</code>`),`%%INLINECODE${inlineCodes.length-1}%%`)),processed=escapeHtml(processed),processed=processed.replace(/^### (.+)$/gm,'<h4 class="librebot-h4">$1</h4>'),processed=processed.replace(/^## (.+)$/gm,'<h3 class="librebot-h3">$1</h3>'),processed=processed.replace(/^# (.+)$/gm,'<h2 class="librebot-h2">$1</h2>'),processed=processed.replace(/\*\*\*([^*]+)\*\*\*/g,'<strong><em>$1</em></strong>'),processed=processed.replace(/\*\*([^*]+)\*\*/g,'<strong>$1</strong>'),processed=processed.replace(/\*([^*]+)\*/g,'<em>$1</em>'),processed=processed.replace(/_([^_]+)_/g,'<em>$1</em>'),processed=processed.replace(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2" target="_blank" rel="noopener noreferrer" class="librebot-link">$1</a>'),processed=processed.replace(/^[\*\-] (.+)$/gm,'<li class="librebot-li">$1</li>'),processed=processed.replace(/(<li class="librebot-li">.*<\/li>\n?)+/g,match=>`<ul class="librebot-ul">${match}</ul>`),processed=processed.replace(/^\d+\. (.+)$/gm,'<li class="librebot-li-ordered">$1</li>'),processed=processed.replace(/(<li class="librebot-li-ordered">.*<\/li>\n?)+/g,match=>`<ol class="librebot-ol">${match}</ol>`),processed=processed.replace(/^---$/gm,'<hr class="librebot-hr">'),processed=processed.replace(/^&gt; (.+)$/gm,'<blockquote class="librebot-blockquote">$1</blockquote>'),processed=processed.replace(/\n/g,'<br>'),processed=processed.replace(/<\/(pre|ul|ol|blockquote|h[2-4])><br>/g,'</$1>'),processed=processed.replace(/<br><(pre|ul|ol|blockquote|h[2-4])/g,'<$1'),codeBlocks.forEach((block,i)=>{processed=processed.replace(`%%CODEBLOCK${i}%%`,block)}),inlineCodes.forEach((code,i)=>{processed=processed.replace(`%%INLINECODE${i}%%`,code)}),processed}showTyping(){const typing=document.createElement('div');return typing.className='librebot-typing',typing.innerHTML='<span></span><span></span><span></span>',this.messagesContainer?.appendChild(typing),this.scrollToBottom(),typing}scrollToBottom(){this.messagesContainer&&(this.messagesContainer.scrollTop=this.messagesContainer.scrollHeight)}async sendMessage(){if(!this.input||this.isLoading)return;const content=this.input.value.trim();if(!content)return;this.addMessage('user',content),this.input.value='',this.isLoading=!0;const typing=this.showTyping();try{if(this.config.streaming)await this.callStreamingAPI(content,typing);else{const response=await this.callAPI(content);typing.remove(),response.error?this.addMessage('assistant',`${this.translations.errorPrefix} ${response.error}`):(this.addMessage('assistant',response.response),this.handleBookingIntent(response))}}catch(error){typing.remove(),this.addMessage('assistant',this.translations.connectionError),console.error('LibreBot error:',error)}finally{this.isLoading=!1}}async callStreamingAPI(message,typing){const response=await fetch(`${this.config.apiUrl}/chat`,{method:'POST',headers:{'Content-Type':'application/json',Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({message:message,context:this.getConversationContext(),stream:!0,sessionId:this.sessionId})});if(!response.ok){typing.remove();const error=await response.json().catch(()=>({error:'Request failed'}));return void this.addMessage('assistant',`${this.translations.errorPrefix} ${error.error||'Request failed'}`)}typing.remove();const messageEl=document.createElement('div');messageEl.className='librebot-message assistant',this.messagesContainer?.appendChild(messageEl);const reader=response.body?.getReader();if(!reader)return void this.addMessage('assistant',this.translations.streamingNotSupported);const decoder=new TextDecoder;let fullContent='',bookingIntentData=null;try{for(;;){const{done:done,value:value}=await reader.read();if(done)break;const lines=decoder.decode(value,{stream:!0}).split('\n').filter(line=>''!==line.trim());for(const line of lines)if(line.startsWith('data: ')){const data=line.slice(6);if('[DONE]'===data)continue;try{const parsed=JSON.parse(data);parsed.content&&(fullContent+=parsed.content,messageEl.innerHTML=this.formatMessage(fullContent),this.scrollToBottom()),parsed.sessionId&&this.saveSession(parsed.sessionId),parsed.booking_intent&&parsed.booking_config&&(bookingIntentData=parsed)}catch{}}}}catch(error){console.error('Stream reading error:',error)}this.messages.push({id:`msg-${Date.now()}`,role:'assistant',content:fullContent,timestamp:new Date}),bookingIntentData&&this.handleBookingIntent(bookingIntentData)}async callAPI(message){const response=await fetch(`${this.config.apiUrl}/chat`,{method:'POST',headers:{'Content-Type':'application/json',Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({message:message,context:this.getConversationContext(),sessionId:this.sessionId})});if(!response.ok){return{response:'',error:(await response.json().catch(()=>({error:'Request failed'}))).error||'Request failed'}}const data=await response.json();return data.sessionId&&this.saveSession(data.sessionId),data}getConversationContext(){return this.messages.slice(-10).map(m=>({role:m.role,content:m.content}))}destroy(){this.container?.remove(),document.getElementById('librebot-styles')?.remove()}clearSession(){try{this.sessionId=null,this.messages=[],localStorage.removeItem(this.getSessionKey()),this.messagesContainer&&(this.messagesContainer.innerHTML=''),this.config.welcomeMessage&&this.addMessage('assistant',this.config.welcomeMessage)}catch{}}switchToBookingMode(config){this.bookingConfig=config,this.mode='booking',this.selectedDate=null,this.selectedSlot=null,this.availableSlots=[],this.chatContainer&&(this.chatContainer.style.display='none'),this.inputArea&&(this.inputArea.style.display='none'),this.bookingContainer&&(this.bookingContainer.style.display='flex',this.renderBookingUI())}switchToChatMode(){this.mode='chat',this.bookingConfig=null,this.selectedDate=null,this.selectedSlot=null,this.availableSlots=[],this.chatContainer&&(this.chatContainer.style.display='flex'),this.inputArea&&(this.inputArea.style.display='flex'),this.bookingContainer&&(this.bookingContainer.style.display='none',this.bookingContainer.innerHTML='')}renderBookingUI(){if(!this.bookingContainer||!this.bookingConfig)return;const minDate=new Date;minDate.setDate(minDate.getDate()+1);const maxDate=new Date;maxDate.setDate(maxDate.getDate()+(this.bookingConfig.advance_booking_days||30));const formFieldsHtml=this.bookingConfig.form_fields.map(field=>{const requiredMark=field.required?'<span class="required">*</span>':'',requiredAttr=field.required?'required':'';if('textarea'===field.type)return`\n <div class="librebot-form-field">\n <label class="librebot-form-label">${field.label}${requiredMark}</label>\n <textarea\n class="librebot-form-textarea"\n name="${field.name}"\n placeholder="${field.placeholder||''}"\n ${requiredAttr}\n ></textarea>\n </div>\n `;if('select'===field.type&&field.options){const optionsHtml=field.options.map(opt=>`<option value="${opt}">${opt}</option>`).join('');return`\n <div class="librebot-form-field">\n <label class="librebot-form-label">${field.label}${requiredMark}</label>\n <select\n class="librebot-form-select"\n name="${field.name}"\n ${requiredAttr}\n >\n <option value="">${field.placeholder||'Select...'}</option>\n ${optionsHtml}\n </select>\n </div>\n `}return`\n <div class="librebot-form-field">\n <label class="librebot-form-label">${field.label}${requiredMark}</label>\n <input\n type="${field.type}"\n class="librebot-form-input"\n name="${field.name}"\n placeholder="${field.placeholder||''}"\n ${requiredAttr}\n />\n </div>\n `}).join('');this.bookingContainer.innerHTML=`\n <div class="librebot-booking">\n <div class="librebot-booking-header">\n <button class="librebot-booking-back">${backIcon}</button>\n <h3 class="librebot-booking-title">${this.translations.bookingTitle}</h3>\n </div>\n <div class="librebot-booking-content">\n <div class="librebot-booking-section">\n <label class="librebot-booking-label">${this.translations.selectDate}</label>\n <input\n type="date"\n class="librebot-date-input"\n min="${minDate.toISOString().split('T')[0]}"\n max="${maxDate.toISOString().split('T')[0]}"\n />\n </div>\n <div class="librebot-booking-section">\n <label class="librebot-booking-label">${this.translations.selectTime}</label>\n <div class="librebot-slots-container">\n <div class="librebot-slots-empty">${this.translations.selectDate}</div>\n </div>\n </div>\n <div class="librebot-booking-section librebot-form-section" style="display: none;">\n <label class="librebot-booking-label">${this.translations.yourDetails}</label>\n <form class="librebot-booking-form">\n ${formFieldsHtml}\n <button type="submit" class="librebot-booking-submit" disabled>\n ${this.translations.confirmBooking}\n </button>\n </form>\n </div>\n </div>\n </div>\n `;const backBtn=this.bookingContainer.querySelector('.librebot-booking-back');backBtn?.addEventListener('click',()=>this.switchToChatMode());const dateInput=this.bookingContainer.querySelector('.librebot-date-input');dateInput?.addEventListener('change',e=>{const target=e.target;this.selectedDate=target.value,this.selectedSlot=null,this.loadAvailableSlots(target.value)});const form=this.bookingContainer.querySelector('.librebot-booking-form');form?.addEventListener('submit',e=>{e.preventDefault(),this.submitBooking(form)})}async loadAvailableSlots(date){const slotsContainer=this.bookingContainer?.querySelector('.librebot-slots-container');if(slotsContainer){slotsContainer.innerHTML=`<div class="librebot-slots-loading">${this.translations.loadingSlots}</div>`;try{const response=await fetch(`${this.config.apiUrl}/bookings/slots?date=${date}&timezone=${Intl.DateTimeFormat().resolvedOptions().timeZone}`,{headers:{Authorization:`Bearer ${this.config.apiKey}`}});if(!response.ok)throw new Error('Failed to load slots');const data=await response.json();if(this.availableSlots=data.slots||[],0===this.availableSlots.length)return void(slotsContainer.innerHTML=`<div class="librebot-slots-empty">${this.translations.noSlotsAvailable}</div>`);const slotsHtml=this.availableSlots.map(slot=>{const disabled=slot.available?'':'disabled';return`\n <button\n type="button"\n class="librebot-slot"\n data-start="${slot.start}"\n data-end="${slot.end}"\n ${disabled}\n >${slot.start}</button>\n `}).join('');slotsContainer.innerHTML=`<div class="librebot-slots">${slotsHtml}</div>`;slotsContainer.querySelectorAll('.librebot-slot').forEach(btn=>{btn.addEventListener('click',()=>{const start=btn.getAttribute('data-start')||'',end=btn.getAttribute('data-end')||'';this.selectSlot({start:start,end:end,available:!0},btn)})})}catch(err){console.error('Failed to load slots:',err),slotsContainer.innerHTML=`<div class="librebot-slots-empty">${this.translations.bookingError}</div>`}}}selectSlot(slot,buttonEl){this.selectedSlot=slot;const allSlots=this.bookingContainer?.querySelectorAll('.librebot-slot');allSlots?.forEach(el=>el.classList.remove('selected')),buttonEl.classList.add('selected');const formSection=this.bookingContainer?.querySelector('.librebot-form-section');formSection&&(formSection.style.display='block');const submitBtn=this.bookingContainer?.querySelector('.librebot-booking-submit');submitBtn&&(submitBtn.disabled=!1),formSection?.scrollIntoView({behavior:'smooth',block:'start'})}async submitBooking(form){if(!this.selectedDate||!this.selectedSlot||!this.bookingConfig)return;const submitBtn=form.querySelector('.librebot-booking-submit');submitBtn&&(submitBtn.disabled=!0,submitBtn.textContent='...');const formData=new FormData(form),formDataObj={};formData.forEach((value,key)=>{formDataObj[key]=value.toString()});try{const response=await fetch(`${this.config.apiUrl}/bookings/submit`,{method:'POST',headers:{'Content-Type':'application/json',Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({date:this.selectedDate,slot_start:this.selectedSlot.start,slot_end:this.selectedSlot.end,form_data:formDataObj,session_id:this.sessionId,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone})}),result=await response.json();if(!result.success||!result.booking)throw new Error(result.error||'Booking failed');this.renderBookingSuccess(result.booking)}catch(err){console.error('Booking submission failed:',err),submitBtn&&(submitBtn.disabled=!1,submitBtn.textContent=this.translations.confirmBooking),alert(this.translations.bookingError)}}renderBookingSuccess(booking){if(!this.bookingContainer)return;const startDate=new Date(booking.scheduled_start),formattedDate=startDate.toLocaleDateString(void 0,{weekday:'long',year:'numeric',month:'long',day:'numeric'}),formattedTime=startDate.toLocaleTimeString(void 0,{hour:'numeric',minute:'2-digit'}),meetingLinkHtml=booking.meeting_link?`<a href="${booking.meeting_link}" target="_blank" class="librebot-meeting-link">${this.translations.joinMeeting}</a>`:'';this.bookingContainer.innerHTML=`\n <div class="librebot-booking">\n <div class="librebot-booking-header">\n <button class="librebot-booking-back">${backIcon}</button>\n <h3 class="librebot-booking-title">${this.translations.bookingTitle}</h3>\n </div>\n <div class="librebot-booking-content">\n <div class="librebot-booking-success">\n <div class="librebot-booking-success-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg></div>\n <h3>${this.translations.bookingSuccess}</h3>\n <div class="librebot-booking-details">\n <div class="librebot-booking-detail">\n <span class="librebot-booking-detail-label">${this.translations.selectDate}</span>\n <span class="librebot-booking-detail-value">${formattedDate}</span>\n </div>\n <div class="librebot-booking-detail">\n <span class="librebot-booking-detail-label">${this.translations.selectTime}</span>\n <span class="librebot-booking-detail-value">${formattedTime}</span>\n </div>\n <div class="librebot-booking-detail">\n <span class="librebot-booking-detail-label">${this.translations.duration}</span>\n <span class="librebot-booking-detail-value">${booking.duration_minutes} ${this.translations.minutes}</span>\n </div>\n </div>\n ${meetingLinkHtml}\n <button class="librebot-booking-submit" type="button">${this.translations.backToChat}</button>\n </div>\n </div>\n </div>\n `;const backBtn=this.bookingContainer.querySelector('.librebot-booking-back');backBtn?.addEventListener('click',()=>this.switchToChatMode());const chatBtn=this.bookingContainer.querySelector('.librebot-booking-submit');chatBtn?.addEventListener('click',()=>this.switchToChatMode())}handleBookingIntent(data){data.booking_intent&&data.booking_config&&setTimeout(()=>{this.switchToBookingMode(data.booking_config)},1500)}}return async function(){const script=document.currentScript||document.querySelector('script[data-key][src*="librebot"]');if(!script)return void console.warn('LibreBot: Could not find script tag');const apiKey=script.dataset.key||script.dataset.apiKey||'',apiUrl=script.dataset.apiUrl||'https://api.librebot.io';if(!apiKey)return void console.error('LibreBot: Missing data-key attribute');let remoteConfig={};try{const response=await fetch(`${apiUrl}/api/widget-config?key=${encodeURIComponent(apiKey)}`);response.ok&&(remoteConfig=await response.json())}catch(e){}const config={apiKey:apiKey,apiUrl:apiUrl,position:script.dataset.position||remoteConfig.position||'bottom-right',theme:script.dataset.theme||remoteConfig.theme||'dark',primaryColor:script.dataset.color||remoteConfig.primaryColor||'#14b8a6',title:script.dataset.title||remoteConfig.title||'Libre Bot',subtitle:script.dataset.subtitle||remoteConfig.subtitle||'AI Documentation Assistant',placeholder:script.dataset.placeholder||remoteConfig.placeholder||void 0,welcomeMessage:script.dataset.welcome||remoteConfig.welcomeMessage||void 0,streaming:'false'!==script.dataset.streaming,whiteLabel:remoteConfig.whiteLabel||!1,lang:script.dataset.lang||remoteConfig.lang},initWidget=()=>{window.LibreBot=new LibreBotWidget(config)};'loading'===document.readyState?document.addEventListener('DOMContentLoaded',initWidget):initWidget()}(),exports.LibreBotWidget=LibreBotWidget,exports}({});
2
2
  //# sourceMappingURL=librebot.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"librebot.js","sources":["../src/styles.ts","../src/icons.ts","../src/translations.ts","../src/widget.ts","../src/embed.ts"],"sourcesContent":["export const getStyles = (primaryColor: string = '#14b8a6') => `\n /* Reset all styles for the widget to prevent host page interference */\n .librebot-widget,\n .librebot-widget *,\n .librebot-widget *::before,\n .librebot-widget *::after,\n .librebot-fab,\n .librebot-fab *,\n .librebot-modal,\n .librebot-modal * {\n box-sizing: border-box !important;\n margin: 0;\n padding: 0;\n border: 0;\n outline: none;\n font: inherit;\n vertical-align: baseline;\n text-decoration: none;\n }\n\n .librebot-widget {\n --lb-primary: ${primaryColor};\n --lb-primary-hover: color-mix(in srgb, ${primaryColor} 85%, white);\n --lb-bg: #0f0f0f;\n --lb-bg-secondary: #1a1a1a;\n --lb-text: #ffffff;\n --lb-text-secondary: #9ca3af;\n --lb-border: #2a2a2a;\n font-family: 'Roboto Mono', -apple-system, BlinkMacSystemFont, 'Segoe UI', monospace !important;\n font-size: 14px !important;\n line-height: 1.5 !important;\n color: var(--lb-text);\n }\n\n .librebot-widget.light {\n --lb-bg: #ffffff;\n --lb-bg-secondary: #f5f5f5;\n --lb-text: #1a1a1a;\n --lb-text-secondary: #666666;\n --lb-border: #e0e0e0;\n }\n\n .librebot-fab {\n all: unset !important;\n position: fixed !important;\n bottom: 20px !important;\n right: 20px !important;\n width: 56px !important;\n height: 56px !important;\n border-radius: 50% !important;\n background: var(--lb-primary) !important;\n border: none !important;\n outline: none !important;\n cursor: pointer !important;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3) !important;\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n transition: transform 0.2s, box-shadow 0.2s !important;\n z-index: 999998 !important;\n -webkit-appearance: none !important;\n -moz-appearance: none !important;\n appearance: none !important;\n box-sizing: border-box !important;\n }\n\n .librebot-fab:hover {\n transform: scale(1.05) !important;\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4) !important;\n }\n\n .librebot-fab:focus,\n .librebot-fab:active {\n outline: none !important;\n border: none !important;\n }\n\n .librebot-fab svg {\n width: 24px !important;\n height: 24px !important;\n fill: white !important;\n border: none !important;\n outline: none !important;\n background: transparent !important;\n }\n\n .librebot-fab.left {\n right: auto !important;\n left: 20px !important;\n }\n\n .librebot-modal {\n position: fixed;\n bottom: 90px;\n right: 20px;\n width: 380px;\n max-width: calc(100vw - 40px);\n height: 520px;\n max-height: calc(100vh - 120px);\n background: var(--lb-bg);\n border: 1px solid var(--lb-border);\n border-radius: 16px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n z-index: 999999;\n opacity: 0;\n transform: translateY(20px) scale(0.95);\n transition: opacity 0.2s, transform 0.2s;\n pointer-events: none;\n }\n\n .librebot-modal.open {\n opacity: 1;\n transform: translateY(0) scale(1);\n pointer-events: auto;\n }\n\n .librebot-modal.left {\n right: auto;\n left: 20px;\n }\n\n .librebot-header {\n padding: 16px;\n border-bottom: 1px solid var(--lb-border);\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n .librebot-header-content {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .librebot-avatar {\n width: 40px;\n height: 40px;\n border-radius: 10px;\n background: var(--lb-primary);\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .librebot-avatar svg {\n width: 24px;\n height: 24px;\n fill: white;\n }\n\n .librebot-title {\n font-weight: 600;\n color: var(--lb-text);\n margin: 0;\n font-size: 16px;\n }\n\n .librebot-subtitle {\n font-size: 12px;\n color: var(--lb-text-secondary);\n margin: 0;\n }\n\n .librebot-close {\n width: 32px;\n height: 32px;\n border-radius: 8px;\n border: none;\n background: transparent;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--lb-text-secondary);\n transition: background 0.2s;\n }\n\n .librebot-close:hover {\n background: var(--lb-bg-secondary);\n }\n\n .librebot-chat-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n .librebot-messages {\n flex: 1;\n overflow-y: auto;\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .librebot-booking-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n .librebot-message {\n max-width: 85%;\n padding: 10px 14px;\n border-radius: 16px;\n word-wrap: break-word;\n }\n\n .librebot-message.user {\n align-self: flex-end;\n background: var(--lb-primary);\n color: white;\n border-bottom-right-radius: 4px;\n }\n\n .librebot-message.assistant {\n align-self: flex-start;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n border-bottom-left-radius: 4px;\n }\n\n /* Inline code */\n .librebot-inline-code {\n background: rgba(0, 0, 0, 0.3);\n padding: 2px 6px;\n border-radius: 4px;\n font-family: 'Roboto Mono', 'Fira Code', 'Consolas', monospace;\n font-size: 12px;\n color: #f0f0f0;\n }\n\n .librebot-widget.light .librebot-inline-code {\n background: rgba(0, 0, 0, 0.08);\n color: #1a1a1a;\n }\n\n /* Code blocks */\n .librebot-code-block {\n background: #0d1117;\n padding: 12px 14px;\n border-radius: 8px;\n overflow-x: auto;\n margin: 10px 0;\n border: 1px solid rgba(255, 255, 255, 0.1);\n position: relative;\n }\n\n .librebot-widget.light .librebot-code-block {\n background: #f6f8fa;\n border-color: rgba(0, 0, 0, 0.1);\n }\n\n .librebot-code-block code {\n font-family: 'Roboto Mono', 'Fira Code', 'Consolas', monospace;\n font-size: 12px;\n line-height: 1.5;\n color: #e6edf3;\n background: none;\n padding: 0;\n white-space: pre;\n display: block;\n }\n\n .librebot-widget.light .librebot-code-block code {\n color: #24292f;\n }\n\n .librebot-code-block[data-language]::before {\n content: attr(data-language);\n position: absolute;\n top: 6px;\n right: 10px;\n font-size: 10px;\n color: rgba(255, 255, 255, 0.4);\n text-transform: uppercase;\n font-family: 'Roboto Mono', monospace;\n }\n\n .librebot-widget.light .librebot-code-block[data-language]::before {\n color: rgba(0, 0, 0, 0.3);\n }\n\n /* Headers */\n .librebot-h2 {\n font-size: 16px;\n font-weight: 600;\n margin: 12px 0 8px 0;\n color: var(--lb-text);\n }\n\n .librebot-h3 {\n font-size: 14px;\n font-weight: 600;\n margin: 10px 0 6px 0;\n color: var(--lb-text);\n }\n\n .librebot-h4 {\n font-size: 13px;\n font-weight: 600;\n margin: 8px 0 4px 0;\n color: var(--lb-text);\n }\n\n /* Lists */\n .librebot-ul, .librebot-ol {\n margin: 8px 0;\n padding-left: 20px;\n }\n\n .librebot-ul {\n list-style-type: disc;\n }\n\n .librebot-ol {\n list-style-type: decimal;\n }\n\n .librebot-li, .librebot-li-ordered {\n margin: 4px 0;\n padding-left: 4px;\n line-height: 1.5;\n }\n\n /* Links */\n .librebot-link {\n color: var(--lb-primary);\n text-decoration: none;\n border-bottom: 1px solid transparent;\n transition: border-color 0.2s;\n }\n\n .librebot-link:hover {\n border-bottom-color: var(--lb-primary);\n }\n\n /* Blockquotes */\n .librebot-blockquote {\n border-left: 3px solid var(--lb-primary);\n margin: 8px 0;\n padding: 4px 12px;\n color: var(--lb-text-secondary);\n font-style: italic;\n background: rgba(0, 0, 0, 0.1);\n border-radius: 0 6px 6px 0;\n }\n\n .librebot-widget.light .librebot-blockquote {\n background: rgba(0, 0, 0, 0.04);\n }\n\n /* Horizontal rule */\n .librebot-hr {\n border: none;\n border-top: 1px solid var(--lb-border);\n margin: 12px 0;\n }\n\n /* Strong and emphasis */\n .librebot-message strong {\n font-weight: 600;\n }\n\n .librebot-message em {\n font-style: italic;\n }\n\n /* Legacy support for old code elements */\n .librebot-message code:not(.librebot-inline-code) {\n background: rgba(0, 0, 0, 0.2);\n padding: 2px 6px;\n border-radius: 4px;\n font-family: monospace;\n font-size: 13px;\n }\n\n .librebot-message pre:not(.librebot-code-block) {\n background: rgba(0, 0, 0, 0.3);\n padding: 10px;\n border-radius: 8px;\n overflow-x: auto;\n margin: 8px 0;\n }\n\n .librebot-message pre:not(.librebot-code-block) code {\n background: none;\n padding: 0;\n }\n\n .librebot-typing {\n display: flex;\n gap: 4px;\n padding: 12px 16px;\n align-self: flex-start;\n background: var(--lb-bg-secondary);\n border-radius: 16px;\n border-bottom-left-radius: 4px;\n }\n\n .librebot-typing span {\n width: 8px;\n height: 8px;\n background: var(--lb-text-secondary);\n border-radius: 50%;\n animation: librebot-bounce 1.4s infinite ease-in-out;\n }\n\n .librebot-typing span:nth-child(1) { animation-delay: 0s; }\n .librebot-typing span:nth-child(2) { animation-delay: 0.2s; }\n .librebot-typing span:nth-child(3) { animation-delay: 0.4s; }\n\n @keyframes librebot-bounce {\n 0%, 80%, 100% { transform: scale(0.8); opacity: 0.5; }\n 40% { transform: scale(1); opacity: 1; }\n }\n\n .librebot-input-area {\n padding: 12px 16px;\n border-top: 1px solid var(--lb-border);\n display: flex;\n gap: 8px;\n }\n\n .librebot-input {\n flex: 1;\n padding: 10px 14px;\n border: 1px solid var(--lb-border);\n border-radius: 24px;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n font-size: 14px;\n outline: none;\n transition: border-color 0.2s;\n }\n\n .librebot-input:focus {\n border-color: var(--lb-primary);\n }\n\n .librebot-input::placeholder {\n color: var(--lb-text-secondary);\n }\n\n .librebot-send {\n width: 40px;\n height: 40px;\n border-radius: 50%;\n border: none;\n background: var(--lb-primary);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.2s;\n }\n\n .librebot-send:hover {\n background: var(--lb-primary-hover);\n }\n\n .librebot-send:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .librebot-send svg {\n width: 18px;\n height: 18px;\n fill: white;\n }\n\n .librebot-welcome {\n text-align: center;\n padding: 20px;\n color: var(--lb-text-secondary);\n }\n\n .librebot-welcome-icon {\n width: 48px;\n height: 48px;\n margin: 0 auto 12px;\n background: var(--lb-primary);\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .librebot-welcome-icon svg {\n width: 28px;\n height: 28px;\n fill: white;\n }\n\n .librebot-powered {\n text-align: center;\n padding: 8px;\n font-size: 11px;\n color: var(--lb-text-secondary);\n border-top: 1px solid var(--lb-border);\n }\n\n .librebot-powered a {\n color: var(--lb-primary);\n text-decoration: none;\n }\n\n .librebot-powered a:hover {\n text-decoration: underline;\n }\n\n /* RTL Support */\n .librebot-widget.rtl {\n direction: rtl;\n text-align: right;\n }\n\n .librebot-widget.rtl .librebot-message.user {\n align-self: flex-start;\n border-bottom-right-radius: 16px;\n border-bottom-left-radius: 4px;\n }\n\n .librebot-widget.rtl .librebot-message.assistant {\n align-self: flex-end;\n border-bottom-left-radius: 16px;\n border-bottom-right-radius: 4px;\n }\n\n .librebot-widget.rtl .librebot-typing {\n align-self: flex-end;\n border-bottom-left-radius: 16px;\n border-bottom-right-radius: 4px;\n }\n\n .librebot-widget.rtl .librebot-header-content {\n flex-direction: row-reverse;\n }\n\n .librebot-widget.rtl .librebot-input {\n text-align: right;\n }\n\n .librebot-widget.rtl .librebot-input-area {\n flex-direction: row-reverse;\n }\n\n .librebot-widget.rtl .librebot-ul,\n .librebot-widget.rtl .librebot-ol {\n padding-left: 0;\n padding-right: 20px;\n }\n\n .librebot-widget.rtl .librebot-blockquote {\n border-left: none;\n border-right: 3px solid var(--lb-primary);\n border-radius: 6px 0 0 6px;\n }\n\n /* Booking Mode Styles */\n .librebot-booking {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n .librebot-booking-header {\n padding: 12px 16px;\n border-bottom: 1px solid var(--lb-border);\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .librebot-booking-back {\n width: 32px;\n height: 32px;\n border-radius: 8px;\n border: none;\n background: transparent;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--lb-text-secondary);\n transition: background 0.2s;\n }\n\n .librebot-booking-back:hover {\n background: var(--lb-bg-secondary);\n }\n\n .librebot-booking-back svg {\n width: 20px;\n height: 20px;\n }\n\n .librebot-booking-title {\n font-size: 16px;\n font-weight: 600;\n color: var(--lb-text);\n margin: 0;\n }\n\n .librebot-booking-content {\n flex: 1;\n overflow-y: auto;\n padding: 16px;\n }\n\n .librebot-booking-section {\n margin-bottom: 20px;\n }\n\n .librebot-booking-label {\n display: block;\n font-size: 13px;\n font-weight: 600;\n color: var(--lb-text);\n margin-bottom: 8px;\n }\n\n .librebot-date-input {\n width: 100%;\n padding: 10px 14px;\n border: 1px solid var(--lb-border);\n border-radius: 8px;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n font-size: 14px;\n outline: none;\n cursor: pointer;\n }\n\n .librebot-date-input:focus {\n border-color: var(--lb-primary);\n }\n\n .librebot-slots {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 8px;\n }\n\n .librebot-slot {\n padding: 10px 8px;\n border: 1px solid var(--lb-border);\n border-radius: 8px;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n font-size: 13px;\n cursor: pointer;\n transition: all 0.2s;\n text-align: center;\n }\n\n .librebot-slot:hover {\n border-color: var(--lb-primary);\n }\n\n .librebot-slot.selected {\n background: var(--lb-primary);\n border-color: var(--lb-primary);\n color: white;\n }\n\n .librebot-slot:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .librebot-slots-loading,\n .librebot-slots-empty {\n text-align: center;\n padding: 20px;\n color: var(--lb-text-secondary);\n font-size: 13px;\n }\n\n .librebot-form-field {\n margin-bottom: 16px;\n }\n\n .librebot-form-label {\n display: block;\n font-size: 13px;\n font-weight: 500;\n color: var(--lb-text);\n margin-bottom: 6px;\n }\n\n .librebot-form-label .required {\n color: #ef4444;\n margin-left: 2px;\n }\n\n .librebot-form-input,\n .librebot-form-textarea,\n .librebot-form-select {\n width: 100%;\n padding: 10px 14px;\n border: 1px solid var(--lb-border);\n border-radius: 8px;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n font-size: 14px;\n outline: none;\n font-family: inherit;\n }\n\n .librebot-form-input:focus,\n .librebot-form-textarea:focus,\n .librebot-form-select:focus {\n border-color: var(--lb-primary);\n }\n\n .librebot-form-textarea {\n min-height: 80px;\n resize: vertical;\n }\n\n .librebot-booking-submit {\n width: 100%;\n padding: 12px;\n border: none;\n border-radius: 8px;\n background: var(--lb-primary);\n color: white;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n transition: background 0.2s;\n margin-top: 12px;\n }\n\n .librebot-booking-submit:hover {\n background: var(--lb-primary-hover);\n }\n\n .librebot-booking-submit:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n /* Booking Success */\n .librebot-booking-success {\n text-align: center;\n padding: 24px 16px;\n }\n\n .librebot-booking-success-icon {\n width: 60px;\n height: 60px;\n margin: 0 auto 16px;\n background: var(--lb-primary);\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .librebot-booking-success-icon svg {\n width: 32px;\n height: 32px;\n fill: white;\n }\n\n .librebot-booking-success h3 {\n font-size: 18px;\n font-weight: 600;\n color: var(--lb-text);\n margin: 0 0 16px;\n }\n\n .librebot-booking-details {\n background: var(--lb-bg-secondary);\n border-radius: 8px;\n padding: 16px;\n margin-bottom: 16px;\n text-align: left;\n }\n\n .librebot-booking-detail {\n display: flex;\n justify-content: space-between;\n padding: 8px 0;\n border-bottom: 1px solid var(--lb-border);\n font-size: 13px;\n }\n\n .librebot-booking-detail:last-child {\n border-bottom: none;\n }\n\n .librebot-booking-detail-label {\n color: var(--lb-text-secondary);\n }\n\n .librebot-booking-detail-value {\n color: var(--lb-text);\n font-weight: 500;\n }\n\n .librebot-meeting-link {\n display: inline-block;\n padding: 12px 24px;\n background: var(--lb-primary);\n color: white;\n text-decoration: none;\n border-radius: 8px;\n font-weight: 600;\n margin-bottom: 16px;\n transition: background 0.2s;\n }\n\n .librebot-meeting-link:hover {\n background: var(--lb-primary-hover);\n }\n\n /* RTL Booking Styles */\n .librebot-widget.rtl .librebot-booking-header {\n flex-direction: row-reverse;\n }\n\n .librebot-widget.rtl .librebot-form-label .required {\n margin-left: 0;\n margin-right: 2px;\n }\n\n .librebot-widget.rtl .librebot-form-input,\n .librebot-widget.rtl .librebot-form-textarea {\n text-align: right;\n }\n\n .librebot-widget.rtl .librebot-booking-detail {\n flex-direction: row-reverse;\n }\n\n .librebot-widget.rtl .librebot-booking-details {\n text-align: right;\n }\n`;\n","export const chatIcon = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"></path></svg>`;\n\nexport const closeIcon = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line></svg>`;\n\nexport const sendIcon = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"></line><polygon points=\"22 2 15 22 11 13 2 9 22 2\"></polygon></svg>`;\n\nexport const botIcon = `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z\"/></svg>`;\n\nexport const backIcon = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"15 18 9 12 15 6\"></polyline></svg>`;\n\nexport const calendarIcon = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"></rect><line x1=\"16\" y1=\"2\" x2=\"16\" y2=\"6\"></line><line x1=\"8\" y1=\"2\" x2=\"8\" y2=\"6\"></line><line x1=\"3\" y1=\"10\" x2=\"21\" y2=\"10\"></line></svg>`;\n\nexport const checkIcon = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"></polyline></svg>`;\n","export type SupportedLang =\n | 'en' | 'fr' | 'de' | 'nl' | 'es' | 'pt' | 'ko' | 'ja' | 'zh' | 'hi' | 'ar'\n | 'bn' | 'ru' | 'id' | 'vi' | 'tr' | 'it' | 'th' | 'pl' | 'uk' | 'ms' | 'cs' | 'sv' | 'da' | 'is';\n\nexport interface WidgetTranslations {\n title: string;\n subtitle: string;\n placeholder: string;\n welcomeMessage: string;\n errorPrefix: string;\n connectionError: string;\n streamingNotSupported: string;\n poweredBy: string;\n // Booking translations\n bookingTitle: string;\n selectDate: string;\n selectTime: string;\n bookAppointment: string;\n noSlotsAvailable: string;\n loadingSlots: string;\n bookingSuccess: string;\n bookingError: string;\n backToChat: string;\n yourDetails: string;\n confirmBooking: string;\n meetingWith: string;\n duration: string;\n minutes: string;\n joinMeeting: string;\n}\n\nexport const WIDGET_TRANSLATIONS: Record<SupportedLang, WidgetTranslations> = {\n en: {\n title: 'Libre Bot',\n subtitle: 'AI Documentation Assistant',\n placeholder: 'Ask a question...',\n welcomeMessage: 'Hi! I can help you find answers in the documentation. What would you like to know?',\n errorPrefix: 'Sorry, I encountered an error:',\n connectionError: 'Sorry, I had trouble connecting. Please try again.',\n streamingNotSupported: 'Sorry, streaming is not supported.',\n poweredBy: 'Powered by',\n bookingTitle: 'Book an Appointment',\n selectDate: 'Select a date',\n selectTime: 'Select a time',\n bookAppointment: 'Book Appointment',\n noSlotsAvailable: 'No time slots available for this date',\n loadingSlots: 'Loading available times...',\n bookingSuccess: 'Your appointment has been confirmed!',\n bookingError: 'Failed to book appointment. Please try again.',\n backToChat: 'Back to chat',\n yourDetails: 'Your Details',\n confirmBooking: 'Confirm Booking',\n meetingWith: 'Meeting',\n duration: 'Duration',\n minutes: 'minutes',\n joinMeeting: 'Join Meeting',\n },\n fr: {\n title: 'Libre Bot',\n subtitle: 'Assistant IA de documentation',\n placeholder: 'Posez une question...',\n welcomeMessage: 'Bonjour ! Je peux vous aider à trouver des réponses dans la documentation. Que souhaitez-vous savoir ?',\n errorPrefix: 'Désolé, j\\'ai rencontré une erreur :',\n connectionError: 'Désolé, j\\'ai eu du mal à me connecter. Veuillez réessayer.',\n streamingNotSupported: 'Désolé, le streaming n\\'est pas pris en charge.',\n poweredBy: 'Propulsé par',\n bookingTitle: 'Prendre rendez-vous',\n selectDate: 'Choisir une date',\n selectTime: 'Choisir une heure',\n bookAppointment: 'Prendre rendez-vous',\n noSlotsAvailable: 'Aucun créneau disponible pour cette date',\n loadingSlots: 'Chargement des horaires...',\n bookingSuccess: 'Votre rendez-vous a été confirmé !',\n bookingError: 'Échec de la réservation. Veuillez réessayer.',\n backToChat: 'Retour au chat',\n yourDetails: 'Vos informations',\n confirmBooking: 'Confirmer la réservation',\n meetingWith: 'Réunion',\n duration: 'Durée',\n minutes: 'minutes',\n joinMeeting: 'Rejoindre la réunion',\n },\n de: {\n title: 'Libre Bot',\n subtitle: 'KI-Dokumentationsassistent',\n placeholder: 'Stellen Sie eine Frage...',\n welcomeMessage: 'Hallo! Ich kann Ihnen helfen, Antworten in der Dokumentation zu finden. Was möchten Sie wissen?',\n errorPrefix: 'Entschuldigung, ein Fehler ist aufgetreten:',\n connectionError: 'Entschuldigung, ich hatte Verbindungsprobleme. Bitte versuchen Sie es erneut.',\n streamingNotSupported: 'Entschuldigung, Streaming wird nicht unterstützt.',\n poweredBy: 'Betrieben von',\n bookingTitle: 'Termin buchen',\n selectDate: 'Datum auswählen',\n selectTime: 'Uhrzeit auswählen',\n bookAppointment: 'Termin buchen',\n noSlotsAvailable: 'Keine Termine für dieses Datum verfügbar',\n loadingSlots: 'Verfügbare Zeiten werden geladen...',\n bookingSuccess: 'Ihr Termin wurde bestätigt!',\n bookingError: 'Terminbuchung fehlgeschlagen. Bitte versuchen Sie es erneut.',\n backToChat: 'Zurück zum Chat',\n yourDetails: 'Ihre Angaben',\n confirmBooking: 'Buchung bestätigen',\n meetingWith: 'Meeting',\n duration: 'Dauer',\n minutes: 'Minuten',\n joinMeeting: 'An Meeting teilnehmen',\n },\n nl: {\n title: 'Libre Bot',\n subtitle: 'AI-documentatieassistent',\n placeholder: 'Stel een vraag...',\n welcomeMessage: 'Hallo! Ik kan je helpen antwoorden te vinden in de documentatie. Wat wil je weten?',\n errorPrefix: 'Sorry, er is een fout opgetreden:',\n connectionError: 'Sorry, ik had moeite met verbinden. Probeer het opnieuw.',\n streamingNotSupported: 'Sorry, streaming wordt niet ondersteund.',\n poweredBy: 'Mogelijk gemaakt door',\n bookingTitle: 'Afspraak maken',\n selectDate: 'Selecteer een datum',\n selectTime: 'Selecteer een tijd',\n bookAppointment: 'Afspraak maken',\n noSlotsAvailable: 'Geen tijdsloten beschikbaar voor deze datum',\n loadingSlots: 'Beschikbare tijden laden...',\n bookingSuccess: 'Je afspraak is bevestigd!',\n bookingError: 'Afspraak maken mislukt. Probeer het opnieuw.',\n backToChat: 'Terug naar chat',\n yourDetails: 'Je gegevens',\n confirmBooking: 'Boeking bevestigen',\n meetingWith: 'Vergadering',\n duration: 'Duur',\n minutes: 'minuten',\n joinMeeting: 'Deelnemen aan vergadering',\n },\n es: {\n title: 'Libre Bot',\n subtitle: 'Asistente de documentación con IA',\n placeholder: 'Haz una pregunta...',\n welcomeMessage: '¡Hola! Puedo ayudarte a encontrar respuestas en la documentación. ¿Qué te gustaría saber?',\n errorPrefix: 'Lo siento, encontré un error:',\n connectionError: 'Lo siento, tuve problemas para conectarme. Por favor, inténtalo de nuevo.',\n streamingNotSupported: 'Lo siento, el streaming no está soportado.',\n poweredBy: 'Desarrollado por',\n bookingTitle: 'Reservar cita',\n selectDate: 'Seleccionar fecha',\n selectTime: 'Seleccionar hora',\n bookAppointment: 'Reservar cita',\n noSlotsAvailable: 'No hay horarios disponibles para esta fecha',\n loadingSlots: 'Cargando horarios disponibles...',\n bookingSuccess: '¡Tu cita ha sido confirmada!',\n bookingError: 'Error al reservar la cita. Por favor, inténtalo de nuevo.',\n backToChat: 'Volver al chat',\n yourDetails: 'Tus datos',\n confirmBooking: 'Confirmar reserva',\n meetingWith: 'Reunión',\n duration: 'Duración',\n minutes: 'minutos',\n joinMeeting: 'Unirse a la reunión',\n },\n pt: {\n title: 'Libre Bot',\n subtitle: 'Assistente de documentação com IA',\n placeholder: 'Faça uma pergunta...',\n welcomeMessage: 'Olá! Posso ajudá-lo a encontrar respostas na documentação. O que você gostaria de saber?',\n errorPrefix: 'Desculpe, encontrei um erro:',\n connectionError: 'Desculpe, tive problemas para conectar. Por favor, tente novamente.',\n streamingNotSupported: 'Desculpe, streaming não é suportado.',\n poweredBy: 'Desenvolvido por',\n bookingTitle: 'Agendar consulta',\n selectDate: 'Selecionar data',\n selectTime: 'Selecionar horário',\n bookAppointment: 'Agendar consulta',\n noSlotsAvailable: 'Nenhum horário disponível para esta data',\n loadingSlots: 'Carregando horários disponíveis...',\n bookingSuccess: 'Seu agendamento foi confirmado!',\n bookingError: 'Falha ao agendar. Por favor, tente novamente.',\n backToChat: 'Voltar ao chat',\n yourDetails: 'Seus dados',\n confirmBooking: 'Confirmar agendamento',\n meetingWith: 'Reunião',\n duration: 'Duração',\n minutes: 'minutos',\n joinMeeting: 'Entrar na reunião',\n },\n ko: {\n title: 'Libre Bot',\n subtitle: 'AI 문서 도우미',\n placeholder: '질문하세요...',\n welcomeMessage: '안녕하세요! 문서에서 답변을 찾는 데 도움을 드릴 수 있습니다. 무엇을 알고 싶으신가요?',\n errorPrefix: '죄송합니다. 오류가 발생했습니다:',\n connectionError: '죄송합니다. 연결에 문제가 있습니다. 다시 시도해 주세요.',\n streamingNotSupported: '죄송합니다. 스트리밍이 지원되지 않습니다.',\n poweredBy: '제공:',\n bookingTitle: '예약하기',\n selectDate: '날짜 선택',\n selectTime: '시간 선택',\n bookAppointment: '예약하기',\n noSlotsAvailable: '선택한 날짜에 가능한 시간이 없습니다',\n loadingSlots: '가능한 시간 로딩 중...',\n bookingSuccess: '예약이 확정되었습니다!',\n bookingError: '예약에 실패했습니다. 다시 시도해 주세요.',\n backToChat: '채팅으로 돌아가기',\n yourDetails: '정보 입력',\n confirmBooking: '예약 확정',\n meetingWith: '미팅',\n duration: '소요 시간',\n minutes: '분',\n joinMeeting: '미팅 참가',\n },\n ja: {\n title: 'Libre Bot',\n subtitle: 'AIドキュメントアシスタント',\n placeholder: '質問してください...',\n welcomeMessage: 'こんにちは!ドキュメントから回答を見つけるお手伝いができます。何をお知りになりたいですか?',\n errorPrefix: '申し訳ありません。エラーが発生しました:',\n connectionError: '申し訳ありません。接続に問題がありました。もう一度お試しください。',\n streamingNotSupported: '申し訳ありません。ストリーミングはサポートされていません。',\n poweredBy: '提供:',\n bookingTitle: '予約する',\n selectDate: '日付を選択',\n selectTime: '時間を選択',\n bookAppointment: '予約する',\n noSlotsAvailable: 'この日は空き時間がありません',\n loadingSlots: '空き時間を読み込み中...',\n bookingSuccess: 'ご予約が確定しました!',\n bookingError: '予約に失敗しました。もう一度お試しください。',\n backToChat: 'チャットに戻る',\n yourDetails: 'お客様情報',\n confirmBooking: '予約を確定',\n meetingWith: 'ミーティング',\n duration: '所要時間',\n minutes: '分',\n joinMeeting: 'ミーティングに参加',\n },\n zh: {\n title: 'Libre Bot',\n subtitle: 'AI文档助手',\n placeholder: '请提问...',\n welcomeMessage: '您好!我可以帮助您在文档中查找答案。您想了解什么?',\n errorPrefix: '抱歉,遇到了错误:',\n connectionError: '抱歉,连接出现问题。请重试。',\n streamingNotSupported: '抱歉,不支持流式传输。',\n poweredBy: '由以下提供支持:',\n bookingTitle: '预约',\n selectDate: '选择日期',\n selectTime: '选择时间',\n bookAppointment: '预约',\n noSlotsAvailable: '该日期没有可用时间段',\n loadingSlots: '加载可用时间...',\n bookingSuccess: '您的预约已确认!',\n bookingError: '预约失败。请重试。',\n backToChat: '返回聊天',\n yourDetails: '您的信息',\n confirmBooking: '确认预约',\n meetingWith: '会议',\n duration: '时长',\n minutes: '分钟',\n joinMeeting: '加入会议',\n },\n hi: {\n title: 'Libre Bot',\n subtitle: 'AI दस्तावेज़ सहायक',\n placeholder: 'कोई प्रश्न पूछें...',\n welcomeMessage: 'नमस्ते! मैं आपको दस्तावेज़ों में उत्तर खोजने में मदद कर सकता हूं। आप क्या जानना चाहते हैं?',\n errorPrefix: 'क्षमा करें, एक त्रुटि हुई:',\n connectionError: 'क्षमा करें, कनेक्ट करने में समस्या हुई। कृपया पुनः प्रयास करें।',\n streamingNotSupported: 'क्षमा करें, स्ट्रीमिंग समर्थित नहीं है।',\n poweredBy: 'द्वारा संचालित',\n bookingTitle: 'अपॉइंटमेंट बुक करें',\n selectDate: 'तिथि चुनें',\n selectTime: 'समय चुनें',\n bookAppointment: 'अपॉइंटमेंट बुक करें',\n noSlotsAvailable: 'इस तिथि के लिए कोई समय उपलब्ध नहीं है',\n loadingSlots: 'उपलब्ध समय लोड हो रहा है...',\n bookingSuccess: 'आपकी अपॉइंटमेंट की पुष्टि हो गई है!',\n bookingError: 'बुकिंग विफल। कृपया पुनः प्रयास करें।',\n backToChat: 'चैट पर वापस जाएं',\n yourDetails: 'आपका विवरण',\n confirmBooking: 'बुकिंग की पुष्टि करें',\n meetingWith: 'मीटिंग',\n duration: 'अवधि',\n minutes: 'मिनट',\n joinMeeting: 'मीटिंग में शामिल हों',\n },\n ar: {\n title: 'Libre Bot',\n subtitle: 'مساعد الوثائق بالذكاء الاصطناعي',\n placeholder: 'اطرح سؤالاً...',\n welcomeMessage: 'مرحباً! يمكنني مساعدتك في العثور على إجابات في الوثائق. ماذا تريد أن تعرف؟',\n errorPrefix: 'عذراً، حدث خطأ:',\n connectionError: 'عذراً، واجهت مشكلة في الاتصال. يرجى المحاولة مرة أخرى.',\n streamingNotSupported: 'عذراً، البث غير مدعوم.',\n poweredBy: 'مدعوم من',\n bookingTitle: 'حجز موعد',\n selectDate: 'اختر التاريخ',\n selectTime: 'اختر الوقت',\n bookAppointment: 'حجز موعد',\n noSlotsAvailable: 'لا توجد أوقات متاحة لهذا التاريخ',\n loadingSlots: 'جاري تحميل الأوقات المتاحة...',\n bookingSuccess: 'تم تأكيد موعدك!',\n bookingError: 'فشل الحجز. يرجى المحاولة مرة أخرى.',\n backToChat: 'العودة إلى المحادثة',\n yourDetails: 'بياناتك',\n confirmBooking: 'تأكيد الحجز',\n meetingWith: 'اجتماع',\n duration: 'المدة',\n minutes: 'دقيقة',\n joinMeeting: 'انضم إلى الاجتماع',\n },\n // Bengali (বাংলা) - ~270M speakers\n bn: {\n title: 'Libre Bot',\n subtitle: 'AI ডকুমেন্টেশন সহায়ক',\n placeholder: 'একটি প্রশ্ন জিজ্ঞাসা করুন...',\n welcomeMessage: 'হ্যালো! আমি আপনাকে ডকুমেন্টেশনে উত্তর খুঁজে পেতে সাহায্য করতে পারি। আপনি কী জানতে চান?',\n errorPrefix: 'দুঃখিত, একটি ত্রুটি ঘটেছে:',\n connectionError: 'দুঃখিত, সংযোগে সমস্যা হয়েছে। অনুগ্রহ করে আবার চেষ্টা করুন।',\n streamingNotSupported: 'দুঃখিত, স্ট্রিমিং সমর্থিত নয়।',\n poweredBy: 'দ্বারা চালিত',\n bookingTitle: 'অ্যাপয়েন্টমেন্ট বুক করুন',\n selectDate: 'তারিখ নির্বাচন করুন',\n selectTime: 'সময় নির্বাচন করুন',\n bookAppointment: 'অ্যাপয়েন্টমেন্ট বুক করুন',\n noSlotsAvailable: 'এই তারিখে কোনো সময় উপলব্ধ নেই',\n loadingSlots: 'উপলব্ধ সময় লোড হচ্ছে...',\n bookingSuccess: 'আপনার অ্যাপয়েন্টমেন্ট নিশ্চিত হয়েছে!',\n bookingError: 'বুকিং ব্যর্থ হয়েছে। অনুগ্রহ করে আবার চেষ্টা করুন।',\n backToChat: 'চ্যাটে ফিরে যান',\n yourDetails: 'আপনার তথ্য',\n confirmBooking: 'বুকিং নিশ্চিত করুন',\n meetingWith: 'মিটিং',\n duration: 'সময়কাল',\n minutes: 'মিনিট',\n joinMeeting: 'মিটিংয়ে যোগ দিন',\n },\n // Russian (Русский) - ~255M speakers\n ru: {\n title: 'Libre Bot',\n subtitle: 'ИИ-ассистент по документации',\n placeholder: 'Задайте вопрос...',\n welcomeMessage: 'Привет! Я могу помочь вам найти ответы в документации. Что бы вы хотели узнать?',\n errorPrefix: 'Извините, произошла ошибка:',\n connectionError: 'Извините, возникла проблема с подключением. Пожалуйста, попробуйте снова.',\n streamingNotSupported: 'Извините, потоковая передача не поддерживается.',\n poweredBy: 'Работает на',\n bookingTitle: 'Записаться на приём',\n selectDate: 'Выберите дату',\n selectTime: 'Выберите время',\n bookAppointment: 'Записаться',\n noSlotsAvailable: 'На эту дату нет свободного времени',\n loadingSlots: 'Загрузка доступного времени...',\n bookingSuccess: 'Ваша запись подтверждена!',\n bookingError: 'Не удалось записаться. Пожалуйста, попробуйте снова.',\n backToChat: 'Вернуться к чату',\n yourDetails: 'Ваши данные',\n confirmBooking: 'Подтвердить запись',\n meetingWith: 'Встреча',\n duration: 'Продолжительность',\n minutes: 'минут',\n joinMeeting: 'Присоединиться к встрече',\n },\n // Indonesian (Bahasa Indonesia) - ~200M speakers\n id: {\n title: 'Libre Bot',\n subtitle: 'Asisten Dokumentasi AI',\n placeholder: 'Ajukan pertanyaan...',\n welcomeMessage: 'Halo! Saya dapat membantu Anda menemukan jawaban di dokumentasi. Apa yang ingin Anda ketahui?',\n errorPrefix: 'Maaf, terjadi kesalahan:',\n connectionError: 'Maaf, ada masalah koneksi. Silakan coba lagi.',\n streamingNotSupported: 'Maaf, streaming tidak didukung.',\n poweredBy: 'Didukung oleh',\n bookingTitle: 'Buat Janji',\n selectDate: 'Pilih tanggal',\n selectTime: 'Pilih waktu',\n bookAppointment: 'Buat Janji',\n noSlotsAvailable: 'Tidak ada waktu tersedia untuk tanggal ini',\n loadingSlots: 'Memuat waktu tersedia...',\n bookingSuccess: 'Janji Anda telah dikonfirmasi!',\n bookingError: 'Gagal membuat janji. Silakan coba lagi.',\n backToChat: 'Kembali ke chat',\n yourDetails: 'Data Anda',\n confirmBooking: 'Konfirmasi Pemesanan',\n meetingWith: 'Pertemuan',\n duration: 'Durasi',\n minutes: 'menit',\n joinMeeting: 'Gabung Pertemuan',\n },\n // Vietnamese (Tiếng Việt) - ~85M speakers\n vi: {\n title: 'Libre Bot',\n subtitle: 'Trợ lý tài liệu AI',\n placeholder: 'Đặt câu hỏi...',\n welcomeMessage: 'Xin chào! Tôi có thể giúp bạn tìm câu trả lời trong tài liệu. Bạn muốn biết điều gì?',\n errorPrefix: 'Xin lỗi, đã xảy ra lỗi:',\n connectionError: 'Xin lỗi, có vấn đề kết nối. Vui lòng thử lại.',\n streamingNotSupported: 'Xin lỗi, không hỗ trợ phát trực tuyến.',\n poweredBy: 'Được cung cấp bởi',\n bookingTitle: 'Đặt lịch hẹn',\n selectDate: 'Chọn ngày',\n selectTime: 'Chọn giờ',\n bookAppointment: 'Đặt lịch hẹn',\n noSlotsAvailable: 'Không có thời gian trống cho ngày này',\n loadingSlots: 'Đang tải thời gian trống...',\n bookingSuccess: 'Lịch hẹn của bạn đã được xác nhận!',\n bookingError: 'Đặt lịch thất bại. Vui lòng thử lại.',\n backToChat: 'Quay lại chat',\n yourDetails: 'Thông tin của bạn',\n confirmBooking: 'Xác nhận đặt lịch',\n meetingWith: 'Cuộc họp',\n duration: 'Thời lượng',\n minutes: 'phút',\n joinMeeting: 'Tham gia cuộc họp',\n },\n // Turkish (Türkçe) - ~80M speakers\n tr: {\n title: 'Libre Bot',\n subtitle: 'Yapay Zeka Dokümantasyon Asistanı',\n placeholder: 'Bir soru sorun...',\n welcomeMessage: 'Merhaba! Dokümantasyonda cevap bulmanıza yardımcı olabilirim. Ne öğrenmek istersiniz?',\n errorPrefix: 'Üzgünüm, bir hata oluştu:',\n connectionError: 'Üzgünüm, bağlantı sorunu yaşadım. Lütfen tekrar deneyin.',\n streamingNotSupported: 'Üzgünüm, akış desteklenmiyor.',\n poweredBy: 'Tarafından desteklenmektedir',\n bookingTitle: 'Randevu Al',\n selectDate: 'Tarih seçin',\n selectTime: 'Saat seçin',\n bookAppointment: 'Randevu Al',\n noSlotsAvailable: 'Bu tarih için müsait saat yok',\n loadingSlots: 'Müsait saatler yükleniyor...',\n bookingSuccess: 'Randevunuz onaylandı!',\n bookingError: 'Randevu alınamadı. Lütfen tekrar deneyin.',\n backToChat: 'Sohbete dön',\n yourDetails: 'Bilgileriniz',\n confirmBooking: 'Randevuyu Onayla',\n meetingWith: 'Toplantı',\n duration: 'Süre',\n minutes: 'dakika',\n joinMeeting: 'Toplantıya Katıl',\n },\n // Italian (Italiano) - ~65M speakers\n it: {\n title: 'Libre Bot',\n subtitle: 'Assistente documentazione AI',\n placeholder: 'Fai una domanda...',\n welcomeMessage: 'Ciao! Posso aiutarti a trovare risposte nella documentazione. Cosa vorresti sapere?',\n errorPrefix: 'Mi dispiace, si è verificato un errore:',\n connectionError: 'Mi dispiace, ho avuto problemi di connessione. Per favore riprova.',\n streamingNotSupported: 'Mi dispiace, lo streaming non è supportato.',\n poweredBy: 'Offerto da',\n bookingTitle: 'Prenota un Appuntamento',\n selectDate: 'Seleziona una data',\n selectTime: 'Seleziona un orario',\n bookAppointment: 'Prenota Appuntamento',\n noSlotsAvailable: 'Nessun orario disponibile per questa data',\n loadingSlots: 'Caricamento orari disponibili...',\n bookingSuccess: 'Il tuo appuntamento è stato confermato!',\n bookingError: 'Prenotazione fallita. Per favore riprova.',\n backToChat: 'Torna alla chat',\n yourDetails: 'I tuoi dati',\n confirmBooking: 'Conferma Prenotazione',\n meetingWith: 'Riunione',\n duration: 'Durata',\n minutes: 'minuti',\n joinMeeting: 'Partecipa alla Riunione',\n },\n // Thai (ไทย) - ~60M speakers\n th: {\n title: 'Libre Bot',\n subtitle: 'ผู้ช่วยเอกสาร AI',\n placeholder: 'ถามคำถาม...',\n welcomeMessage: 'สวัสดี! ฉันสามารถช่วยคุณค้นหาคำตอบในเอกสารได้ คุณต้องการทราบอะไร?',\n errorPrefix: 'ขออภัย เกิดข้อผิดพลาด:',\n connectionError: 'ขออภัย มีปัญหาในการเชื่อมต่อ กรุณาลองอีกครั้ง',\n streamingNotSupported: 'ขออภัย ไม่รองรับการสตรีม',\n poweredBy: 'ขับเคลื่อนโดย',\n bookingTitle: 'จองนัดหมาย',\n selectDate: 'เลือกวันที่',\n selectTime: 'เลือกเวลา',\n bookAppointment: 'จองนัดหมาย',\n noSlotsAvailable: 'ไม่มีช่วงเวลาว่างสำหรับวันนี้',\n loadingSlots: 'กำลังโหลดเวลาว่าง...',\n bookingSuccess: 'การนัดหมายของคุณได้รับการยืนยันแล้ว!',\n bookingError: 'การจองล้มเหลว กรุณาลองอีกครั้ง',\n backToChat: 'กลับไปที่แชท',\n yourDetails: 'ข้อมูลของคุณ',\n confirmBooking: 'ยืนยันการจอง',\n meetingWith: 'การประชุม',\n duration: 'ระยะเวลา',\n minutes: 'นาที',\n joinMeeting: 'เข้าร่วมการประชุม',\n },\n // Polish (Polski) - ~45M speakers\n pl: {\n title: 'Libre Bot',\n subtitle: 'Asystent dokumentacji AI',\n placeholder: 'Zadaj pytanie...',\n welcomeMessage: 'Cześć! Mogę pomóc Ci znaleźć odpowiedzi w dokumentacji. Co chciałbyś wiedzieć?',\n errorPrefix: 'Przepraszam, wystąpił błąd:',\n connectionError: 'Przepraszam, miałem problem z połączeniem. Spróbuj ponownie.',\n streamingNotSupported: 'Przepraszam, streaming nie jest obsługiwany.',\n poweredBy: 'Obsługiwane przez',\n bookingTitle: 'Umów wizytę',\n selectDate: 'Wybierz datę',\n selectTime: 'Wybierz godzinę',\n bookAppointment: 'Umów wizytę',\n noSlotsAvailable: 'Brak dostępnych terminów na ten dzień',\n loadingSlots: 'Ładowanie dostępnych terminów...',\n bookingSuccess: 'Twoja wizyta została potwierdzona!',\n bookingError: 'Nie udało się umówić wizyty. Spróbuj ponownie.',\n backToChat: 'Wróć do czatu',\n yourDetails: 'Twoje dane',\n confirmBooking: 'Potwierdź rezerwację',\n meetingWith: 'Spotkanie',\n duration: 'Czas trwania',\n minutes: 'minut',\n joinMeeting: 'Dołącz do spotkania',\n },\n // Ukrainian (Українська) - ~40M speakers\n uk: {\n title: 'Libre Bot',\n subtitle: 'ШІ-асистент документації',\n placeholder: 'Поставте запитання...',\n welcomeMessage: 'Привіт! Я можу допомогти вам знайти відповіді в документації. Що б ви хотіли дізнатися?',\n errorPrefix: 'Вибачте, сталася помилка:',\n connectionError: 'Вибачте, виникла проблема з підключенням. Будь ласка, спробуйте ще раз.',\n streamingNotSupported: 'Вибачте, потокова передача не підтримується.',\n poweredBy: 'Працює на',\n bookingTitle: 'Записатися на прийом',\n selectDate: 'Оберіть дату',\n selectTime: 'Оберіть час',\n bookAppointment: 'Записатися',\n noSlotsAvailable: 'На цю дату немає вільного часу',\n loadingSlots: 'Завантаження доступного часу...',\n bookingSuccess: 'Ваш запис підтверджено!',\n bookingError: 'Не вдалося записатися. Будь ласка, спробуйте ще раз.',\n backToChat: 'Повернутися до чату',\n yourDetails: 'Ваші дані',\n confirmBooking: 'Підтвердити запис',\n meetingWith: 'Зустріч',\n duration: 'Тривалість',\n minutes: 'хвилин',\n joinMeeting: 'Приєднатися до зустрічі',\n },\n // Malay (Bahasa Melayu) - ~30M speakers\n ms: {\n title: 'Libre Bot',\n subtitle: 'Pembantu Dokumentasi AI',\n placeholder: 'Tanya soalan...',\n welcomeMessage: 'Hai! Saya boleh membantu anda mencari jawapan dalam dokumentasi. Apa yang anda ingin tahu?',\n errorPrefix: 'Maaf, berlaku ralat:',\n connectionError: 'Maaf, ada masalah sambungan. Sila cuba lagi.',\n streamingNotSupported: 'Maaf, penstriman tidak disokong.',\n poweredBy: 'Dikuasakan oleh',\n bookingTitle: 'Buat Temujanji',\n selectDate: 'Pilih tarikh',\n selectTime: 'Pilih masa',\n bookAppointment: 'Buat Temujanji',\n noSlotsAvailable: 'Tiada masa tersedia untuk tarikh ini',\n loadingSlots: 'Memuatkan masa tersedia...',\n bookingSuccess: 'Temujanji anda telah disahkan!',\n bookingError: 'Gagal membuat temujanji. Sila cuba lagi.',\n backToChat: 'Kembali ke sembang',\n yourDetails: 'Maklumat Anda',\n confirmBooking: 'Sahkan Tempahan',\n meetingWith: 'Mesyuarat',\n duration: 'Tempoh',\n minutes: 'minit',\n joinMeeting: 'Sertai Mesyuarat',\n },\n // Czech (Čeština) - ~10M speakers\n cs: {\n title: 'Libre Bot',\n subtitle: 'AI asistent dokumentace',\n placeholder: 'Položte otázku...',\n welcomeMessage: 'Ahoj! Mohu vám pomoci najít odpovědi v dokumentaci. Co byste chtěli vědět?',\n errorPrefix: 'Omlouvám se, došlo k chybě:',\n connectionError: 'Omlouvám se, měl jsem problém s připojením. Zkuste to prosím znovu.',\n streamingNotSupported: 'Omlouvám se, streamování není podporováno.',\n poweredBy: 'Využívá technologii',\n bookingTitle: 'Rezervovat schůzku',\n selectDate: 'Vyberte datum',\n selectTime: 'Vyberte čas',\n bookAppointment: 'Rezervovat schůzku',\n noSlotsAvailable: 'Na tento den nejsou k dispozici žádné termíny',\n loadingSlots: 'Načítání dostupných termínů...',\n bookingSuccess: 'Vaše schůzka byla potvrzena!',\n bookingError: 'Rezervace se nezdařila. Zkuste to prosím znovu.',\n backToChat: 'Zpět na chat',\n yourDetails: 'Vaše údaje',\n confirmBooking: 'Potvrdit rezervaci',\n meetingWith: 'Schůzka',\n duration: 'Délka',\n minutes: 'minut',\n joinMeeting: 'Připojit se ke schůzce',\n },\n // Swedish (Svenska) - ~10M speakers\n sv: {\n title: 'Libre Bot',\n subtitle: 'AI-dokumentationsassistent',\n placeholder: 'Ställ en fråga...',\n welcomeMessage: 'Hej! Jag kan hjälpa dig hitta svar i dokumentationen. Vad vill du veta?',\n errorPrefix: 'Tyvärr uppstod ett fel:',\n connectionError: 'Tyvärr hade jag problem med anslutningen. Försök igen.',\n streamingNotSupported: 'Tyvärr stöds inte streaming.',\n poweredBy: 'Drivs av',\n bookingTitle: 'Boka tid',\n selectDate: 'Välj datum',\n selectTime: 'Välj tid',\n bookAppointment: 'Boka tid',\n noSlotsAvailable: 'Inga lediga tider för detta datum',\n loadingSlots: 'Laddar lediga tider...',\n bookingSuccess: 'Din bokning har bekräftats!',\n bookingError: 'Bokningen misslyckades. Försök igen.',\n backToChat: 'Tillbaka till chatten',\n yourDetails: 'Dina uppgifter',\n confirmBooking: 'Bekräfta bokning',\n meetingWith: 'Möte',\n duration: 'Varaktighet',\n minutes: 'minuter',\n joinMeeting: 'Gå med i mötet',\n },\n // Danish (Dansk) - ~6M speakers\n da: {\n title: 'Libre Bot',\n subtitle: 'AI-dokumentationsassistent',\n placeholder: 'Stil et spørgsmål...',\n welcomeMessage: 'Hej! Jeg kan hjælpe dig med at finde svar i dokumentationen. Hvad vil du gerne vide?',\n errorPrefix: 'Beklager, der opstod en fejl:',\n connectionError: 'Beklager, jeg havde problemer med forbindelsen. Prøv venligst igen.',\n streamingNotSupported: 'Beklager, streaming understøttes ikke.',\n poweredBy: 'Drevet af',\n bookingTitle: 'Book en tid',\n selectDate: 'Vælg dato',\n selectTime: 'Vælg tidspunkt',\n bookAppointment: 'Book tid',\n noSlotsAvailable: 'Ingen ledige tider på denne dato',\n loadingSlots: 'Indlæser ledige tider...',\n bookingSuccess: 'Din booking er bekræftet!',\n bookingError: 'Booking mislykkedes. Prøv venligst igen.',\n backToChat: 'Tilbage til chat',\n yourDetails: 'Dine oplysninger',\n confirmBooking: 'Bekræft booking',\n meetingWith: 'Møde',\n duration: 'Varighed',\n minutes: 'minutter',\n joinMeeting: 'Deltag i mødet',\n },\n // Icelandic (Íslenska) - ~350K speakers\n is: {\n title: 'Libre Bot',\n subtitle: 'Gervigreind skjalahjálpari',\n placeholder: 'Spurðu spurningu...',\n welcomeMessage: 'Hæ! Ég get hjálpað þér að finna svör í skjölunum. Hvað viltu vita?',\n errorPrefix: 'Því miður kom upp villa:',\n connectionError: 'Því miður átti ég í vandræðum með tengingu. Vinsamlegast reyndu aftur.',\n streamingNotSupported: 'Því miður er streymi ekki stutt.',\n poweredBy: 'Knúið af',\n bookingTitle: 'Bóka tíma',\n selectDate: 'Veldu dagsetningu',\n selectTime: 'Veldu tíma',\n bookAppointment: 'Bóka tíma',\n noSlotsAvailable: 'Engir tímar lausir á þessum degi',\n loadingSlots: 'Hleð lausum tímum...',\n bookingSuccess: 'Bókun þín hefur verið staðfest!',\n bookingError: 'Bókun mistókst. Vinsamlegast reyndu aftur.',\n backToChat: 'Til baka í spjall',\n yourDetails: 'Þínar upplýsingar',\n confirmBooking: 'Staðfesta bókun',\n meetingWith: 'Fundur',\n duration: 'Lengd',\n minutes: 'mínútur',\n joinMeeting: 'Taka þátt í fundi',\n },\n};\n\nexport const RTL_LANGUAGES: SupportedLang[] = ['ar'];\n\nexport function detectLanguage(): SupportedLang {\n // 1. Check document's lang attribute\n const htmlLang = document.documentElement.lang?.toLowerCase().split('-')[0];\n if (htmlLang && htmlLang in WIDGET_TRANSLATIONS) {\n return htmlLang as SupportedLang;\n }\n\n // 2. Check navigator.language\n const navLang = navigator.language?.toLowerCase().split('-')[0];\n if (navLang && navLang in WIDGET_TRANSLATIONS) {\n return navLang as SupportedLang;\n }\n\n // 3. Default to English\n return 'en';\n}\n","import { LibreBotConfig, Message, ChatResponse, BookingConfig, TimeSlot, BookingResult } from './types';\nimport { getStyles } from './styles';\nimport { chatIcon, closeIcon, sendIcon, backIcon, checkIcon } from './icons';\nimport {\n SupportedLang,\n WidgetTranslations,\n WIDGET_TRANSLATIONS,\n RTL_LANGUAGES,\n detectLanguage\n} from './translations';\n\ntype WidgetMode = 'chat' | 'booking';\n\nexport class LibreBotWidget {\n private config!: Required<LibreBotConfig>;\n private container: HTMLDivElement | null = null;\n private modal: HTMLDivElement | null = null;\n private messagesContainer: HTMLDivElement | null = null;\n private input: HTMLInputElement | null = null;\n private isOpen = false;\n private messages: Message[] = [];\n private isLoading = false;\n private sessionId: string | null = null;\n private translations: WidgetTranslations = WIDGET_TRANSLATIONS.en;\n private isRtl = false;\n\n // Booking mode state\n private mode: WidgetMode = 'chat';\n private bookingConfig: BookingConfig | null = null;\n private selectedDate: string | null = null;\n private selectedSlot: TimeSlot | null = null;\n private availableSlots: TimeSlot[] = [];\n private bookingContainer: HTMLDivElement | null = null;\n private chatContainer: HTMLDivElement | null = null;\n private inputArea: HTMLDivElement | null = null;\n\n private defaultConfig: Omit<Required<LibreBotConfig>, 'apiKey'> = {\n apiUrl: 'https://librebot.io/api',\n position: 'bottom-right',\n theme: 'dark',\n primaryColor: '#14b8a6',\n title: '', // Will be set from translations\n subtitle: '', // Will be set from translations\n placeholder: '', // Will be set from translations\n welcomeMessage: '', // Will be set from translations\n streaming: true,\n whiteLabel: false,\n autoLoadConfig: true,\n lang: 'en',\n };\n\n constructor(config: LibreBotConfig) {\n if (!config.apiKey) {\n console.error('LibreBot: API key is required');\n return;\n }\n\n // Detect language from config or host page\n const detectedLang = config.lang || detectLanguage();\n this.translations = WIDGET_TRANSLATIONS[detectedLang] || WIDGET_TRANSLATIONS.en;\n this.isRtl = RTL_LANGUAGES.includes(detectedLang);\n\n // Apply defaults with translations\n const configWithTranslations: Partial<LibreBotConfig> = {\n ...config,\n lang: detectedLang,\n title: config.title || this.translations.title,\n subtitle: config.subtitle || this.translations.subtitle,\n placeholder: config.placeholder || this.translations.placeholder,\n welcomeMessage: config.welcomeMessage || this.translations.welcomeMessage,\n };\n\n this.config = { ...this.defaultConfig, ...configWithTranslations } as Required<LibreBotConfig>;\n\n // Auto-load config from server if enabled and no custom settings provided\n if (this.config.autoLoadConfig) {\n this.loadRemoteConfig().then(() => this.init());\n } else {\n this.init();\n }\n }\n\n private async loadRemoteConfig(): Promise<void> {\n try {\n const response = await fetch(`${this.config.apiUrl}/widget-config?key=${this.config.apiKey}`);\n if (response.ok) {\n const remoteConfig = await response.json();\n // Merge remote config with local config (local overrides remote)\n this.config = {\n ...this.defaultConfig,\n ...remoteConfig,\n ...this.getProvidedConfig(),\n apiKey: this.config.apiKey,\n apiUrl: this.config.apiUrl,\n autoLoadConfig: this.config.autoLoadConfig,\n } as Required<LibreBotConfig>;\n }\n } catch (error) {\n console.warn('LibreBot: Could not load remote config, using defaults', error);\n }\n }\n\n private getProvidedConfig(): Partial<LibreBotConfig> {\n // Return only the config options that were explicitly provided (not default values)\n const provided: Partial<LibreBotConfig> = {};\n const userConfig = this.config;\n const defaults = this.defaultConfig;\n\n if (userConfig.position !== defaults.position) provided.position = userConfig.position;\n if (userConfig.theme !== defaults.theme) provided.theme = userConfig.theme;\n if (userConfig.primaryColor !== defaults.primaryColor) provided.primaryColor = userConfig.primaryColor;\n if (userConfig.title !== defaults.title) provided.title = userConfig.title;\n if (userConfig.subtitle !== defaults.subtitle) provided.subtitle = userConfig.subtitle;\n if (userConfig.placeholder !== defaults.placeholder) provided.placeholder = userConfig.placeholder;\n if (userConfig.welcomeMessage !== defaults.welcomeMessage) provided.welcomeMessage = userConfig.welcomeMessage;\n if (userConfig.streaming !== defaults.streaming) provided.streaming = userConfig.streaming;\n if (userConfig.whiteLabel !== defaults.whiteLabel) provided.whiteLabel = userConfig.whiteLabel;\n\n return provided;\n }\n\n private init(): void {\n // Load sessionId from localStorage for persistent memory\n this.loadSession();\n\n // Inject styles\n this.injectStyles();\n\n // Create widget container\n this.createWidget();\n\n // Add welcome message\n if (this.config.welcomeMessage) {\n this.addMessage('assistant', this.config.welcomeMessage);\n }\n }\n\n private getSessionKey(): string {\n return `librebot_session_${this.config.apiKey}`;\n }\n\n private loadSession(): void {\n try {\n const stored = localStorage.getItem(this.getSessionKey());\n if (stored) {\n this.sessionId = stored;\n }\n } catch {\n // localStorage may not be available\n }\n }\n\n private saveSession(sessionId: string): void {\n try {\n this.sessionId = sessionId;\n localStorage.setItem(this.getSessionKey(), sessionId);\n } catch {\n // localStorage may not be available\n }\n }\n\n private injectStyles(): void {\n const styleId = 'librebot-styles';\n if (document.getElementById(styleId)) return;\n\n const style = document.createElement('style');\n style.id = styleId;\n style.textContent = getStyles(this.config.primaryColor);\n document.head.appendChild(style);\n }\n\n private createWidget(): void {\n // Create container\n this.container = document.createElement('div');\n let className = 'librebot-widget';\n if (this.config.theme === 'light') className += ' light';\n if (this.isRtl) className += ' rtl';\n this.container.className = className;\n\n // Create FAB button\n const fab = document.createElement('button');\n fab.className = `librebot-fab ${this.config.position === 'bottom-left' ? 'left' : ''}`;\n fab.innerHTML = chatIcon;\n fab.onclick = () => this.toggle();\n\n // Create modal\n this.modal = document.createElement('div');\n this.modal.className = `librebot-modal ${this.config.position === 'bottom-left' ? 'left' : ''}`;\n this.modal.innerHTML = this.getModalHTML();\n\n // Add to container\n this.container.appendChild(fab);\n this.container.appendChild(this.modal);\n document.body.appendChild(this.container);\n\n // Get references\n this.messagesContainer = this.modal.querySelector('.librebot-messages');\n this.input = this.modal.querySelector('.librebot-input');\n this.chatContainer = this.modal.querySelector('.librebot-chat-container');\n this.bookingContainer = this.modal.querySelector('.librebot-booking-container');\n this.inputArea = this.modal.querySelector('.librebot-input-area');\n\n // Add event listeners\n const closeBtn = this.modal.querySelector('.librebot-close');\n closeBtn?.addEventListener('click', () => this.close());\n\n const sendBtn = this.modal.querySelector('.librebot-send');\n sendBtn?.addEventListener('click', () => this.sendMessage());\n\n this.input?.addEventListener('keypress', (e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n this.sendMessage();\n }\n });\n\n // Auto theme detection\n if (this.config.theme === 'auto') {\n const mediaQuery = window.matchMedia('(prefers-color-scheme: light)');\n this.updateTheme(mediaQuery.matches);\n mediaQuery.addEventListener('change', (e) => this.updateTheme(e.matches));\n }\n }\n\n private getModalHTML(): string {\n const poweredByHtml = this.config.whiteLabel\n ? ''\n : `<div class=\"librebot-powered\">\n ${this.translations.poweredBy} <a href=\"https://librebot.io\" target=\"_blank\">Libre Bot</a>\n </div>`;\n\n return `\n <div class=\"librebot-header\">\n <div class=\"librebot-header-content\">\n <div class=\"librebot-avatar\">${chatIcon}</div>\n <div>\n <h3 class=\"librebot-title\">${this.config.title}</h3>\n <p class=\"librebot-subtitle\">${this.config.subtitle}</p>\n </div>\n </div>\n <button class=\"librebot-close\">${closeIcon}</button>\n </div>\n <div class=\"librebot-chat-container\">\n <div class=\"librebot-messages\"></div>\n </div>\n <div class=\"librebot-booking-container\" style=\"display: none;\"></div>\n <div class=\"librebot-input-area\">\n <input type=\"text\" class=\"librebot-input\" placeholder=\"${this.config.placeholder}\" />\n <button class=\"librebot-send\">${sendIcon}</button>\n </div>\n ${poweredByHtml}\n `;\n }\n\n private updateTheme(isLight: boolean): void {\n if (isLight) {\n this.container?.classList.add('light');\n } else {\n this.container?.classList.remove('light');\n }\n }\n\n public toggle(): void {\n this.isOpen ? this.close() : this.open();\n }\n\n public open(): void {\n this.isOpen = true;\n this.modal?.classList.add('open');\n this.input?.focus();\n }\n\n public close(): void {\n this.isOpen = false;\n this.modal?.classList.remove('open');\n }\n\n private addMessage(role: 'user' | 'assistant', content: string): void {\n const message: Message = {\n id: `msg-${Date.now()}`,\n role,\n content,\n timestamp: new Date(),\n };\n\n this.messages.push(message);\n this.renderMessage(message);\n }\n\n private renderMessage(message: Message): void {\n if (!this.messagesContainer) return;\n\n const el = document.createElement('div');\n el.className = `librebot-message ${message.role}`;\n el.innerHTML = this.formatMessage(message.content);\n\n this.messagesContainer.appendChild(el);\n this.scrollToBottom();\n }\n\n private formatMessage(content: string): string {\n // Escape HTML to prevent XSS\n const escapeHtml = (text: string): string => {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#039;');\n };\n\n // First, extract code blocks to protect them from other formatting\n // Regex handles optional space/newline after ``` and optional language identifier\n const codeBlocks: string[] = [];\n let processed = content.replace(/```\\s*(\\w*)\\s*\\n?([\\s\\S]*?)```/g, (_, lang, code) => {\n const escaped = escapeHtml(code.trim());\n const langClass = lang ? ` data-language=\"${escapeHtml(lang)}\"` : '';\n codeBlocks.push(`<pre class=\"librebot-code-block\"${langClass}><code>${escaped}</code></pre>`);\n return `%%CODEBLOCK${codeBlocks.length - 1}%%`;\n });\n\n // Extract inline code\n const inlineCodes: string[] = [];\n processed = processed.replace(/`([^`]+)`/g, (_, code) => {\n inlineCodes.push(`<code class=\"librebot-inline-code\">${escapeHtml(code)}</code>`);\n return `%%INLINECODE${inlineCodes.length - 1}%%`;\n });\n\n // Now escape remaining HTML\n processed = escapeHtml(processed);\n\n // Headers (## and ###)\n processed = processed.replace(/^### (.+)$/gm, '<h4 class=\"librebot-h4\">$1</h4>');\n processed = processed.replace(/^## (.+)$/gm, '<h3 class=\"librebot-h3\">$1</h3>');\n processed = processed.replace(/^# (.+)$/gm, '<h2 class=\"librebot-h2\">$1</h2>');\n\n // Bold and italic\n processed = processed.replace(/\\*\\*\\*([^*]+)\\*\\*\\*/g, '<strong><em>$1</em></strong>');\n processed = processed.replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>');\n processed = processed.replace(/\\*([^*]+)\\*/g, '<em>$1</em>');\n processed = processed.replace(/_([^_]+)_/g, '<em>$1</em>');\n\n // Links [text](url)\n processed = processed.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, '<a href=\"$2\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"librebot-link\">$1</a>');\n\n // Unordered lists\n processed = processed.replace(/^[\\*\\-] (.+)$/gm, '<li class=\"librebot-li\">$1</li>');\n processed = processed.replace(/(<li class=\"librebot-li\">.*<\\/li>\\n?)+/g, (match) => {\n return `<ul class=\"librebot-ul\">${match}</ul>`;\n });\n\n // Ordered lists\n processed = processed.replace(/^\\d+\\. (.+)$/gm, '<li class=\"librebot-li-ordered\">$1</li>');\n processed = processed.replace(/(<li class=\"librebot-li-ordered\">.*<\\/li>\\n?)+/g, (match) => {\n return `<ol class=\"librebot-ol\">${match}</ol>`;\n });\n\n // Horizontal rule\n processed = processed.replace(/^---$/gm, '<hr class=\"librebot-hr\">');\n\n // Blockquotes\n processed = processed.replace(/^&gt; (.+)$/gm, '<blockquote class=\"librebot-blockquote\">$1</blockquote>');\n\n // Convert newlines to <br> (but not inside lists/blockquotes)\n processed = processed.replace(/\\n/g, '<br>');\n\n // Clean up extra <br> after block elements\n processed = processed.replace(/<\\/(pre|ul|ol|blockquote|h[2-4])><br>/g, '</$1>');\n processed = processed.replace(/<br><(pre|ul|ol|blockquote|h[2-4])/g, '<$1');\n\n // Restore code blocks\n codeBlocks.forEach((block, i) => {\n processed = processed.replace(`%%CODEBLOCK${i}%%`, block);\n });\n\n // Restore inline code\n inlineCodes.forEach((code, i) => {\n processed = processed.replace(`%%INLINECODE${i}%%`, code);\n });\n\n return processed;\n }\n\n private showTyping(): HTMLDivElement {\n const typing = document.createElement('div');\n typing.className = 'librebot-typing';\n typing.innerHTML = '<span></span><span></span><span></span>';\n this.messagesContainer?.appendChild(typing);\n this.scrollToBottom();\n return typing;\n }\n\n private scrollToBottom(): void {\n if (this.messagesContainer) {\n this.messagesContainer.scrollTop = this.messagesContainer.scrollHeight;\n }\n }\n\n private async sendMessage(): Promise<void> {\n if (!this.input || this.isLoading) return;\n\n const content = this.input.value.trim();\n if (!content) return;\n\n // Add user message\n this.addMessage('user', content);\n this.input.value = '';\n\n // Show typing indicator\n this.isLoading = true;\n const typing = this.showTyping();\n\n try {\n if (this.config.streaming) {\n await this.callStreamingAPI(content, typing);\n } else {\n const response = await this.callAPI(content);\n typing.remove();\n\n if (response.error) {\n this.addMessage('assistant', `${this.translations.errorPrefix} ${response.error}`);\n } else {\n this.addMessage('assistant', response.response);\n // Check for booking intent\n this.handleBookingIntent(response);\n }\n }\n } catch (error) {\n typing.remove();\n this.addMessage('assistant', this.translations.connectionError);\n console.error('LibreBot error:', error);\n } finally {\n this.isLoading = false;\n }\n }\n\n private async callStreamingAPI(message: string, typing: HTMLDivElement): Promise<void> {\n const response = await fetch(`${this.config.apiUrl}/chat`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config.apiKey}`,\n },\n body: JSON.stringify({\n message,\n context: this.getConversationContext(),\n stream: true,\n sessionId: this.sessionId,\n }),\n });\n\n if (!response.ok) {\n typing.remove();\n const error = await response.json().catch(() => ({ error: 'Request failed' }));\n this.addMessage('assistant', `${this.translations.errorPrefix} ${error.error || 'Request failed'}`);\n return;\n }\n\n // Remove typing indicator and create streaming message element\n typing.remove();\n const messageEl = document.createElement('div');\n messageEl.className = 'librebot-message assistant';\n this.messagesContainer?.appendChild(messageEl);\n\n const reader = response.body?.getReader();\n if (!reader) {\n this.addMessage('assistant', this.translations.streamingNotSupported);\n return;\n }\n\n const decoder = new TextDecoder();\n let fullContent = '';\n let bookingIntentData: ChatResponse | null = null;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n const chunk = decoder.decode(value, { stream: true });\n const lines = chunk.split('\\n').filter((line) => line.trim() !== '');\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n const data = line.slice(6);\n if (data === '[DONE]') continue;\n\n try {\n const parsed = JSON.parse(data);\n if (parsed.content) {\n fullContent += parsed.content;\n messageEl.innerHTML = this.formatMessage(fullContent);\n this.scrollToBottom();\n }\n // Save sessionId when received from server\n if (parsed.sessionId) {\n this.saveSession(parsed.sessionId);\n }\n // Check for booking intent in metadata\n if (parsed.booking_intent && parsed.booking_config) {\n bookingIntentData = parsed;\n }\n } catch {\n // Skip malformed JSON\n }\n }\n }\n }\n } catch (error) {\n console.error('Stream reading error:', error);\n }\n\n // Add to messages array\n this.messages.push({\n id: `msg-${Date.now()}`,\n role: 'assistant',\n content: fullContent,\n timestamp: new Date(),\n });\n\n // Handle booking intent if detected\n if (bookingIntentData) {\n this.handleBookingIntent(bookingIntentData);\n }\n }\n\n private async callAPI(message: string): Promise<ChatResponse> {\n const response = await fetch(`${this.config.apiUrl}/chat`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config.apiKey}`,\n },\n body: JSON.stringify({\n message,\n context: this.getConversationContext(),\n sessionId: this.sessionId,\n }),\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({ error: 'Request failed' }));\n return { response: '', error: error.error || 'Request failed' };\n }\n\n const data = await response.json();\n // Save sessionId when received from server\n if (data.sessionId) {\n this.saveSession(data.sessionId);\n }\n return data;\n }\n\n private getConversationContext(): Array<{ role: string; content: string }> {\n // Get last 10 messages for context\n return this.messages.slice(-10).map((m) => ({\n role: m.role,\n content: m.content,\n }));\n }\n\n public destroy(): void {\n this.container?.remove();\n document.getElementById('librebot-styles')?.remove();\n }\n\n public clearSession(): void {\n try {\n this.sessionId = null;\n this.messages = [];\n localStorage.removeItem(this.getSessionKey());\n if (this.messagesContainer) {\n this.messagesContainer.innerHTML = '';\n }\n // Re-add welcome message\n if (this.config.welcomeMessage) {\n this.addMessage('assistant', this.config.welcomeMessage);\n }\n } catch {\n // localStorage may not be available\n }\n }\n\n // Booking Mode Methods\n\n private switchToBookingMode(config: BookingConfig): void {\n this.bookingConfig = config;\n this.mode = 'booking';\n this.selectedDate = null;\n this.selectedSlot = null;\n this.availableSlots = [];\n\n // Hide chat, show booking\n if (this.chatContainer) this.chatContainer.style.display = 'none';\n if (this.inputArea) this.inputArea.style.display = 'none';\n if (this.bookingContainer) {\n this.bookingContainer.style.display = 'flex';\n this.renderBookingUI();\n }\n }\n\n private switchToChatMode(): void {\n this.mode = 'chat';\n this.bookingConfig = null;\n this.selectedDate = null;\n this.selectedSlot = null;\n this.availableSlots = [];\n\n // Show chat, hide booking\n if (this.chatContainer) this.chatContainer.style.display = 'flex';\n if (this.inputArea) this.inputArea.style.display = 'flex';\n if (this.bookingContainer) {\n this.bookingContainer.style.display = 'none';\n this.bookingContainer.innerHTML = '';\n }\n }\n\n private renderBookingUI(): void {\n if (!this.bookingContainer || !this.bookingConfig) return;\n\n const minDate = new Date();\n minDate.setDate(minDate.getDate() + 1);\n const maxDate = new Date();\n maxDate.setDate(maxDate.getDate() + (this.bookingConfig.advance_booking_days || 30));\n\n const formFieldsHtml = this.bookingConfig.form_fields.map(field => {\n const requiredMark = field.required ? '<span class=\"required\">*</span>' : '';\n const requiredAttr = field.required ? 'required' : '';\n\n if (field.type === 'textarea') {\n return `\n <div class=\"librebot-form-field\">\n <label class=\"librebot-form-label\">${field.label}${requiredMark}</label>\n <textarea\n class=\"librebot-form-textarea\"\n name=\"${field.name}\"\n placeholder=\"${field.placeholder || ''}\"\n ${requiredAttr}\n ></textarea>\n </div>\n `;\n }\n\n if (field.type === 'select' && field.options) {\n const optionsHtml = field.options.map(opt =>\n `<option value=\"${opt}\">${opt}</option>`\n ).join('');\n return `\n <div class=\"librebot-form-field\">\n <label class=\"librebot-form-label\">${field.label}${requiredMark}</label>\n <select\n class=\"librebot-form-select\"\n name=\"${field.name}\"\n ${requiredAttr}\n >\n <option value=\"\">${field.placeholder || 'Select...'}</option>\n ${optionsHtml}\n </select>\n </div>\n `;\n }\n\n return `\n <div class=\"librebot-form-field\">\n <label class=\"librebot-form-label\">${field.label}${requiredMark}</label>\n <input\n type=\"${field.type}\"\n class=\"librebot-form-input\"\n name=\"${field.name}\"\n placeholder=\"${field.placeholder || ''}\"\n ${requiredAttr}\n />\n </div>\n `;\n }).join('');\n\n this.bookingContainer.innerHTML = `\n <div class=\"librebot-booking\">\n <div class=\"librebot-booking-header\">\n <button class=\"librebot-booking-back\">${backIcon}</button>\n <h3 class=\"librebot-booking-title\">${this.translations.bookingTitle}</h3>\n </div>\n <div class=\"librebot-booking-content\">\n <div class=\"librebot-booking-section\">\n <label class=\"librebot-booking-label\">${this.translations.selectDate}</label>\n <input\n type=\"date\"\n class=\"librebot-date-input\"\n min=\"${minDate.toISOString().split('T')[0]}\"\n max=\"${maxDate.toISOString().split('T')[0]}\"\n />\n </div>\n <div class=\"librebot-booking-section\">\n <label class=\"librebot-booking-label\">${this.translations.selectTime}</label>\n <div class=\"librebot-slots-container\">\n <div class=\"librebot-slots-empty\">${this.translations.selectDate}</div>\n </div>\n </div>\n <div class=\"librebot-booking-section librebot-form-section\" style=\"display: none;\">\n <label class=\"librebot-booking-label\">${this.translations.yourDetails}</label>\n <form class=\"librebot-booking-form\">\n ${formFieldsHtml}\n <button type=\"submit\" class=\"librebot-booking-submit\" disabled>\n ${this.translations.confirmBooking}\n </button>\n </form>\n </div>\n </div>\n </div>\n `;\n\n // Add event listeners\n const backBtn = this.bookingContainer.querySelector('.librebot-booking-back');\n backBtn?.addEventListener('click', () => this.switchToChatMode());\n\n const dateInput = this.bookingContainer.querySelector('.librebot-date-input') as HTMLInputElement;\n dateInput?.addEventListener('change', (e) => {\n const target = e.target as HTMLInputElement;\n this.selectedDate = target.value;\n this.selectedSlot = null;\n this.loadAvailableSlots(target.value);\n });\n\n const form = this.bookingContainer.querySelector('.librebot-booking-form') as HTMLFormElement;\n form?.addEventListener('submit', (e) => {\n e.preventDefault();\n this.submitBooking(form);\n });\n }\n\n private async loadAvailableSlots(date: string): Promise<void> {\n const slotsContainer = this.bookingContainer?.querySelector('.librebot-slots-container');\n if (!slotsContainer) return;\n\n slotsContainer.innerHTML = `<div class=\"librebot-slots-loading\">${this.translations.loadingSlots}</div>`;\n\n try {\n const response = await fetch(\n `${this.config.apiUrl}/bookings/slots?date=${date}&timezone=${Intl.DateTimeFormat().resolvedOptions().timeZone}`,\n {\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n },\n }\n );\n\n if (!response.ok) {\n throw new Error('Failed to load slots');\n }\n\n const data = await response.json();\n this.availableSlots = data.slots || [];\n\n if (this.availableSlots.length === 0) {\n slotsContainer.innerHTML = `<div class=\"librebot-slots-empty\">${this.translations.noSlotsAvailable}</div>`;\n return;\n }\n\n const slotsHtml = this.availableSlots.map(slot => {\n const disabled = !slot.available ? 'disabled' : '';\n return `\n <button\n type=\"button\"\n class=\"librebot-slot\"\n data-start=\"${slot.start}\"\n data-end=\"${slot.end}\"\n ${disabled}\n >${slot.start}</button>\n `;\n }).join('');\n\n slotsContainer.innerHTML = `<div class=\"librebot-slots\">${slotsHtml}</div>`;\n\n // Add click handlers to slot buttons\n const slotButtons = slotsContainer.querySelectorAll('.librebot-slot');\n slotButtons.forEach(btn => {\n btn.addEventListener('click', () => {\n const start = btn.getAttribute('data-start') || '';\n const end = btn.getAttribute('data-end') || '';\n this.selectSlot({ start, end, available: true }, btn);\n });\n });\n } catch (err) {\n console.error('Failed to load slots:', err);\n slotsContainer.innerHTML = `<div class=\"librebot-slots-empty\">${this.translations.bookingError}</div>`;\n }\n }\n\n private selectSlot(slot: TimeSlot, buttonEl: Element): void {\n this.selectedSlot = slot;\n\n // Update button styles\n const allSlots = this.bookingContainer?.querySelectorAll('.librebot-slot');\n allSlots?.forEach(el => el.classList.remove('selected'));\n buttonEl.classList.add('selected');\n\n // Show form section\n const formSection = this.bookingContainer?.querySelector('.librebot-form-section') as HTMLElement;\n if (formSection) {\n formSection.style.display = 'block';\n }\n\n // Enable submit button\n const submitBtn = this.bookingContainer?.querySelector('.librebot-booking-submit') as HTMLButtonElement;\n if (submitBtn) {\n submitBtn.disabled = false;\n }\n\n // Scroll to form\n formSection?.scrollIntoView({ behavior: 'smooth', block: 'start' });\n }\n\n private async submitBooking(form: HTMLFormElement): Promise<void> {\n if (!this.selectedDate || !this.selectedSlot || !this.bookingConfig) return;\n\n const submitBtn = form.querySelector('.librebot-booking-submit') as HTMLButtonElement;\n if (submitBtn) {\n submitBtn.disabled = true;\n submitBtn.textContent = '...';\n }\n\n // Collect form data\n const formData = new FormData(form);\n const formDataObj: Record<string, string> = {};\n formData.forEach((value, key) => {\n formDataObj[key] = value.toString();\n });\n\n try {\n const response = await fetch(`${this.config.apiUrl}/bookings/submit`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config.apiKey}`,\n },\n body: JSON.stringify({\n date: this.selectedDate,\n slot_start: this.selectedSlot.start,\n slot_end: this.selectedSlot.end,\n form_data: formDataObj,\n session_id: this.sessionId,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n }),\n });\n\n const result: BookingResult = await response.json();\n\n if (result.success && result.booking) {\n this.renderBookingSuccess(result.booking);\n } else {\n throw new Error(result.error || 'Booking failed');\n }\n } catch (err) {\n console.error('Booking submission failed:', err);\n if (submitBtn) {\n submitBtn.disabled = false;\n submitBtn.textContent = this.translations.confirmBooking;\n }\n alert(this.translations.bookingError);\n }\n }\n\n private renderBookingSuccess(booking: NonNullable<BookingResult['booking']>): void {\n if (!this.bookingContainer) return;\n\n // Format the date and time\n const startDate = new Date(booking.scheduled_start);\n const formattedDate = startDate.toLocaleDateString(undefined, {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n });\n const formattedTime = startDate.toLocaleTimeString(undefined, {\n hour: 'numeric',\n minute: '2-digit',\n });\n\n const meetingLinkHtml = booking.meeting_link\n ? `<a href=\"${booking.meeting_link}\" target=\"_blank\" class=\"librebot-meeting-link\">${this.translations.joinMeeting}</a>`\n : '';\n\n this.bookingContainer.innerHTML = `\n <div class=\"librebot-booking\">\n <div class=\"librebot-booking-header\">\n <button class=\"librebot-booking-back\">${backIcon}</button>\n <h3 class=\"librebot-booking-title\">${this.translations.bookingTitle}</h3>\n </div>\n <div class=\"librebot-booking-content\">\n <div class=\"librebot-booking-success\">\n <div class=\"librebot-booking-success-icon\">${checkIcon}</div>\n <h3>${this.translations.bookingSuccess}</h3>\n <div class=\"librebot-booking-details\">\n <div class=\"librebot-booking-detail\">\n <span class=\"librebot-booking-detail-label\">${this.translations.selectDate}</span>\n <span class=\"librebot-booking-detail-value\">${formattedDate}</span>\n </div>\n <div class=\"librebot-booking-detail\">\n <span class=\"librebot-booking-detail-label\">${this.translations.selectTime}</span>\n <span class=\"librebot-booking-detail-value\">${formattedTime}</span>\n </div>\n <div class=\"librebot-booking-detail\">\n <span class=\"librebot-booking-detail-label\">${this.translations.duration}</span>\n <span class=\"librebot-booking-detail-value\">${booking.duration_minutes} ${this.translations.minutes}</span>\n </div>\n </div>\n ${meetingLinkHtml}\n <button class=\"librebot-booking-submit\" type=\"button\">${this.translations.backToChat}</button>\n </div>\n </div>\n </div>\n `;\n\n // Add event listeners\n const backBtn = this.bookingContainer.querySelector('.librebot-booking-back');\n backBtn?.addEventListener('click', () => this.switchToChatMode());\n\n const chatBtn = this.bookingContainer.querySelector('.librebot-booking-submit');\n chatBtn?.addEventListener('click', () => this.switchToChatMode());\n }\n\n private handleBookingIntent(data: ChatResponse): void {\n if (data.booking_intent && data.booking_config) {\n // Add a brief delay so user can see the AI response before switching\n setTimeout(() => {\n this.switchToBookingMode(data.booking_config!);\n }, 1500);\n }\n }\n}\n","import { LibreBotWidget } from './widget';\nimport { LibreBotConfig } from './types';\nimport { SupportedLang } from './translations';\n\n// Auto-initialize from script tag\n(async function () {\n // Find the script tag - document.currentScript may be null with defer\n const script = document.currentScript as HTMLScriptElement\n || document.querySelector('script[data-key][src*=\"librebot\"]') as HTMLScriptElement;\n\n if (!script) {\n console.warn('LibreBot: Could not find script tag');\n return;\n }\n\n const apiKey = script.dataset.key || script.dataset.apiKey || '';\n const apiUrl = script.dataset.apiUrl || 'https://api.librebot.io';\n\n if (!apiKey) {\n console.error('LibreBot: Missing data-key attribute');\n return;\n }\n\n // Fetch remote config from dashboard settings\n let remoteConfig: Partial<LibreBotConfig> = {};\n try {\n const response = await fetch(`${apiUrl}/api/widget-config?key=${encodeURIComponent(apiKey)}`);\n if (response.ok) {\n remoteConfig = await response.json();\n }\n } catch (e) {\n // Silently fail - use defaults/data attributes\n }\n\n // Build config: remote settings as base, data attributes as overrides\n const config: LibreBotConfig = {\n apiKey,\n apiUrl,\n // Remote config as defaults, data attributes override\n position: (script.dataset.position as 'bottom-right' | 'bottom-left') || remoteConfig.position || 'bottom-right',\n theme: (script.dataset.theme as 'light' | 'dark' | 'auto') || remoteConfig.theme || 'dark',\n primaryColor: script.dataset.color || remoteConfig.primaryColor || '#14b8a6',\n title: script.dataset.title || remoteConfig.title || 'Libre Bot',\n subtitle: script.dataset.subtitle || remoteConfig.subtitle || 'AI Documentation Assistant',\n placeholder: script.dataset.placeholder || remoteConfig.placeholder || undefined, // Let language detection handle default\n welcomeMessage: script.dataset.welcome || remoteConfig.welcomeMessage || undefined, // Let language detection handle default\n streaming: script.dataset.streaming !== 'false', // default true\n whiteLabel: remoteConfig.whiteLabel || false,\n lang: (script.dataset.lang as SupportedLang) || remoteConfig.lang, // Allow explicit language override\n };\n\n // Initialize when DOM is ready\n const initWidget = () => {\n (window as any).LibreBot = new LibreBotWidget(config);\n };\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', initWidget);\n } else {\n initWidget();\n }\n})();\n\n// Export for manual initialization\nexport { LibreBotWidget, LibreBotConfig };\n"],"names":["chatIcon","backIcon","WIDGET_TRANSLATIONS","en","title","subtitle","placeholder","welcomeMessage","errorPrefix","connectionError","streamingNotSupported","poweredBy","bookingTitle","selectDate","selectTime","bookAppointment","noSlotsAvailable","loadingSlots","bookingSuccess","bookingError","backToChat","yourDetails","confirmBooking","meetingWith","duration","minutes","joinMeeting","fr","de","nl","es","pt","ko","ja","zh","hi","ar","bn","ru","id","vi","tr","it","th","pl","uk","ms","cs","sv","da","is","RTL_LANGUAGES","LibreBotWidget","constructor","config","this","container","modal","messagesContainer","input","isOpen","messages","isLoading","sessionId","translations","isRtl","mode","bookingConfig","selectedDate","selectedSlot","availableSlots","bookingContainer","chatContainer","inputArea","defaultConfig","apiUrl","position","theme","primaryColor","streaming","whiteLabel","autoLoadConfig","lang","apiKey","console","error","detectedLang","htmlLang","document","documentElement","toLowerCase","split","navLang","navigator","language","detectLanguage","includes","configWithTranslations","loadRemoteConfig","then","init","response","fetch","ok","remoteConfig","json","getProvidedConfig","warn","provided","userConfig","defaults","loadSession","injectStyles","createWidget","addMessage","getSessionKey","stored","localStorage","getItem","saveSession","setItem","styleId","getElementById","style","createElement","textContent","getStyles","head","appendChild","className","fab","innerHTML","onclick","toggle","getModalHTML","body","querySelector","closeBtn","addEventListener","close","sendBtn","sendMessage","e","key","shiftKey","preventDefault","mediaQuery","window","matchMedia","updateTheme","matches","poweredByHtml","isLight","classList","add","remove","open","focus","role","content","message","Date","now","timestamp","push","renderMessage","el","formatMessage","scrollToBottom","escapeHtml","text","replace","codeBlocks","processed","_","code","escaped","trim","langClass","length","inlineCodes","match","forEach","block","i","showTyping","typing","scrollTop","scrollHeight","value","callStreamingAPI","callAPI","handleBookingIntent","method","headers","Authorization","JSON","stringify","context","getConversationContext","stream","catch","messageEl","reader","getReader","decoder","TextDecoder","fullContent","bookingIntentData","done","read","lines","decode","filter","line","startsWith","data","slice","parsed","parse","booking_intent","booking_config","map","m","destroy","clearSession","removeItem","switchToBookingMode","display","renderBookingUI","switchToChatMode","minDate","setDate","getDate","maxDate","advance_booking_days","formFieldsHtml","form_fields","field","requiredMark","required","requiredAttr","type","label","name","options","optionsHtml","opt","join","toISOString","backBtn","dateInput","target","loadAvailableSlots","form","submitBooking","date","slotsContainer","Intl","DateTimeFormat","resolvedOptions","timeZone","Error","slots","slotsHtml","slot","disabled","available","start","end","querySelectorAll","btn","getAttribute","selectSlot","err","buttonEl","allSlots","formSection","submitBtn","scrollIntoView","behavior","formData","FormData","formDataObj","toString","slot_start","slot_end","form_data","session_id","timezone","result","success","booking","renderBookingSuccess","alert","startDate","scheduled_start","formattedDate","toLocaleDateString","undefined","weekday","year","month","day","formattedTime","toLocaleTimeString","hour","minute","meetingLinkHtml","meeting_link","duration_minutes","chatBtn","setTimeout","script","currentScript","dataset","encodeURIComponent","color","welcome","initWidget","LibreBot","readyState"],"mappings":"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"}
1
+ {"version":3,"file":"librebot.js","sources":["../src/styles.ts","../src/icons.ts","../src/translations.ts","../src/widget.ts","../src/embed.ts"],"sourcesContent":["export const getStyles = (primaryColor: string = '#14b8a6') => `\n /* Reset all styles for the widget to prevent host page interference */\n .librebot-widget,\n .librebot-widget *,\n .librebot-widget *::before,\n .librebot-widget *::after,\n .librebot-fab,\n .librebot-fab *,\n .librebot-modal,\n .librebot-modal * {\n box-sizing: border-box !important;\n margin: 0;\n padding: 0;\n border: 0;\n outline: none;\n font: inherit;\n vertical-align: baseline;\n text-decoration: none;\n }\n\n .librebot-widget {\n --lb-primary: ${primaryColor};\n --lb-primary-hover: color-mix(in srgb, ${primaryColor} 85%, white);\n --lb-bg: #0f0f0f;\n --lb-bg-secondary: #1a1a1a;\n --lb-text: #ffffff;\n --lb-text-secondary: #9ca3af;\n --lb-border: #2a2a2a;\n font-family: 'Roboto Mono', -apple-system, BlinkMacSystemFont, 'Segoe UI', monospace !important;\n font-size: 14px !important;\n line-height: 1.5 !important;\n color: var(--lb-text);\n }\n\n .librebot-widget.light {\n --lb-bg: #ffffff;\n --lb-bg-secondary: #f5f5f5;\n --lb-text: #1a1a1a;\n --lb-text-secondary: #666666;\n --lb-border: #e0e0e0;\n }\n\n .librebot-fab {\n all: unset !important;\n position: fixed !important;\n bottom: 20px !important;\n right: 20px !important;\n width: 56px !important;\n height: 56px !important;\n border-radius: 50% !important;\n background: var(--lb-primary) !important;\n border: none !important;\n outline: none !important;\n cursor: pointer !important;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3) !important;\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n transition: transform 0.2s, box-shadow 0.2s !important;\n z-index: 999998 !important;\n -webkit-appearance: none !important;\n -moz-appearance: none !important;\n appearance: none !important;\n box-sizing: border-box !important;\n }\n\n .librebot-fab:hover {\n transform: scale(1.05) !important;\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4) !important;\n }\n\n .librebot-fab:focus,\n .librebot-fab:active {\n outline: none !important;\n border: none !important;\n }\n\n .librebot-fab svg {\n width: 24px !important;\n height: 24px !important;\n fill: white !important;\n border: none !important;\n outline: none !important;\n background: transparent !important;\n }\n\n .librebot-fab.left {\n right: auto !important;\n left: 20px !important;\n }\n\n .librebot-modal {\n position: fixed;\n bottom: 90px;\n right: 20px;\n width: 380px;\n max-width: calc(100vw - 40px);\n height: 520px;\n max-height: calc(100vh - 120px);\n background: var(--lb-bg);\n border: 1px solid var(--lb-border);\n border-radius: 16px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n z-index: 999999;\n opacity: 0;\n transform: translateY(20px) scale(0.95);\n transition: opacity 0.2s, transform 0.2s;\n pointer-events: none;\n }\n\n .librebot-modal.open {\n opacity: 1;\n transform: translateY(0) scale(1);\n pointer-events: auto;\n }\n\n .librebot-modal.left {\n right: auto;\n left: 20px;\n }\n\n .librebot-header {\n padding: 16px;\n border-bottom: 1px solid var(--lb-border);\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n .librebot-header-content {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .librebot-avatar {\n width: 40px;\n height: 40px;\n border-radius: 10px;\n background: var(--lb-primary);\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .librebot-avatar svg {\n width: 24px;\n height: 24px;\n fill: white;\n }\n\n .librebot-title {\n font-weight: 600;\n color: var(--lb-text);\n margin: 0;\n font-size: 16px;\n }\n\n .librebot-subtitle {\n font-size: 12px;\n color: var(--lb-text-secondary);\n margin: 0;\n }\n\n .librebot-close {\n width: 32px;\n height: 32px;\n border-radius: 8px;\n border: none;\n background: transparent;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--lb-text-secondary);\n transition: background 0.2s;\n }\n\n .librebot-close:hover {\n background: var(--lb-bg-secondary);\n }\n\n .librebot-chat-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n .librebot-messages {\n flex: 1;\n overflow-y: auto;\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .librebot-booking-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n .librebot-message {\n max-width: 85%;\n padding: 10px 14px;\n border-radius: 16px;\n word-wrap: break-word;\n }\n\n .librebot-message.user {\n align-self: flex-end;\n background: var(--lb-primary);\n color: white;\n border-bottom-right-radius: 4px;\n }\n\n .librebot-message.assistant {\n align-self: flex-start;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n border-bottom-left-radius: 4px;\n }\n\n /* Inline code */\n .librebot-inline-code {\n background: rgba(0, 0, 0, 0.3);\n padding: 2px 6px;\n border-radius: 4px;\n font-family: 'Roboto Mono', 'Fira Code', 'Consolas', monospace;\n font-size: 12px;\n color: #f0f0f0;\n }\n\n .librebot-widget.light .librebot-inline-code {\n background: rgba(0, 0, 0, 0.08);\n color: #1a1a1a;\n }\n\n /* Code blocks */\n .librebot-code-block {\n background: #0d1117;\n padding: 12px 14px;\n border-radius: 8px;\n overflow-x: auto;\n margin: 10px 0;\n border: 1px solid rgba(255, 255, 255, 0.1);\n position: relative;\n }\n\n .librebot-widget.light .librebot-code-block {\n background: #f6f8fa;\n border-color: rgba(0, 0, 0, 0.1);\n }\n\n .librebot-code-block code {\n font-family: 'Roboto Mono', 'Fira Code', 'Consolas', monospace;\n font-size: 12px;\n line-height: 1.5;\n color: #e6edf3;\n background: none;\n padding: 0;\n white-space: pre;\n display: block;\n }\n\n .librebot-widget.light .librebot-code-block code {\n color: #24292f;\n }\n\n .librebot-code-block[data-language]::before {\n content: attr(data-language);\n position: absolute;\n top: 6px;\n right: 10px;\n font-size: 10px;\n color: rgba(255, 255, 255, 0.4);\n text-transform: uppercase;\n font-family: 'Roboto Mono', monospace;\n }\n\n .librebot-widget.light .librebot-code-block[data-language]::before {\n color: rgba(0, 0, 0, 0.3);\n }\n\n /* Headers */\n .librebot-h2 {\n font-size: 16px;\n font-weight: 600;\n margin: 12px 0 8px 0;\n color: var(--lb-text);\n }\n\n .librebot-h3 {\n font-size: 14px;\n font-weight: 600;\n margin: 10px 0 6px 0;\n color: var(--lb-text);\n }\n\n .librebot-h4 {\n font-size: 13px;\n font-weight: 600;\n margin: 8px 0 4px 0;\n color: var(--lb-text);\n }\n\n /* Lists */\n .librebot-ul, .librebot-ol {\n margin: 8px 0;\n padding-left: 20px;\n }\n\n .librebot-ul {\n list-style-type: disc;\n }\n\n .librebot-ol {\n list-style-type: decimal;\n }\n\n .librebot-li, .librebot-li-ordered {\n margin: 4px 0;\n padding-left: 4px;\n line-height: 1.5;\n }\n\n /* Links */\n .librebot-link {\n color: var(--lb-primary);\n text-decoration: none;\n border-bottom: 1px solid transparent;\n transition: border-color 0.2s;\n }\n\n .librebot-link:hover {\n border-bottom-color: var(--lb-primary);\n }\n\n /* Blockquotes */\n .librebot-blockquote {\n border-left: 3px solid var(--lb-primary);\n margin: 8px 0;\n padding: 4px 12px;\n color: var(--lb-text-secondary);\n font-style: italic;\n background: rgba(0, 0, 0, 0.1);\n border-radius: 0 6px 6px 0;\n }\n\n .librebot-widget.light .librebot-blockquote {\n background: rgba(0, 0, 0, 0.04);\n }\n\n /* Horizontal rule */\n .librebot-hr {\n border: none;\n border-top: 1px solid var(--lb-border);\n margin: 12px 0;\n }\n\n /* Strong and emphasis */\n .librebot-message strong {\n font-weight: 600;\n }\n\n .librebot-message em {\n font-style: italic;\n }\n\n /* Legacy support for old code elements */\n .librebot-message code:not(.librebot-inline-code) {\n background: rgba(0, 0, 0, 0.2);\n padding: 2px 6px;\n border-radius: 4px;\n font-family: monospace;\n font-size: 13px;\n }\n\n .librebot-message pre:not(.librebot-code-block) {\n background: rgba(0, 0, 0, 0.3);\n padding: 10px;\n border-radius: 8px;\n overflow-x: auto;\n margin: 8px 0;\n }\n\n .librebot-message pre:not(.librebot-code-block) code {\n background: none;\n padding: 0;\n }\n\n .librebot-typing {\n display: flex;\n gap: 4px;\n padding: 12px 16px;\n align-self: flex-start;\n background: var(--lb-bg-secondary);\n border-radius: 16px;\n border-bottom-left-radius: 4px;\n }\n\n .librebot-typing span {\n width: 8px;\n height: 8px;\n background: var(--lb-text-secondary);\n border-radius: 50%;\n animation: librebot-bounce 1.4s infinite ease-in-out;\n }\n\n .librebot-typing span:nth-child(1) { animation-delay: 0s; }\n .librebot-typing span:nth-child(2) { animation-delay: 0.2s; }\n .librebot-typing span:nth-child(3) { animation-delay: 0.4s; }\n\n @keyframes librebot-bounce {\n 0%, 80%, 100% { transform: scale(0.8); opacity: 0.5; }\n 40% { transform: scale(1); opacity: 1; }\n }\n\n .librebot-input-area {\n padding: 12px 16px;\n border-top: 1px solid var(--lb-border);\n display: flex;\n gap: 8px;\n }\n\n .librebot-input {\n flex: 1;\n padding: 10px 14px;\n border: 1px solid var(--lb-border);\n border-radius: 24px;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n font-size: 14px;\n outline: none;\n transition: border-color 0.2s;\n }\n\n .librebot-input:focus {\n border-color: var(--lb-primary);\n }\n\n .librebot-input::placeholder {\n color: var(--lb-text-secondary);\n }\n\n .librebot-send {\n width: 40px;\n height: 40px;\n border-radius: 50%;\n border: none;\n background: var(--lb-primary);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.2s;\n }\n\n .librebot-send:hover {\n background: var(--lb-primary-hover);\n }\n\n .librebot-send:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .librebot-send svg {\n width: 18px;\n height: 18px;\n fill: white;\n }\n\n .librebot-welcome {\n text-align: center;\n padding: 20px;\n color: var(--lb-text-secondary);\n }\n\n .librebot-welcome-icon {\n width: 48px;\n height: 48px;\n margin: 0 auto 12px;\n background: var(--lb-primary);\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .librebot-welcome-icon svg {\n width: 28px;\n height: 28px;\n fill: white;\n }\n\n .librebot-powered {\n text-align: center;\n padding: 8px;\n font-size: 11px;\n color: var(--lb-text-secondary);\n border-top: 1px solid var(--lb-border);\n }\n\n .librebot-powered a {\n color: var(--lb-primary);\n text-decoration: none;\n }\n\n .librebot-powered a:hover {\n text-decoration: underline;\n }\n\n /* RTL Support */\n .librebot-widget.rtl {\n direction: rtl;\n text-align: right;\n }\n\n .librebot-widget.rtl .librebot-message.user {\n align-self: flex-start;\n border-bottom-right-radius: 16px;\n border-bottom-left-radius: 4px;\n }\n\n .librebot-widget.rtl .librebot-message.assistant {\n align-self: flex-end;\n border-bottom-left-radius: 16px;\n border-bottom-right-radius: 4px;\n }\n\n .librebot-widget.rtl .librebot-typing {\n align-self: flex-end;\n border-bottom-left-radius: 16px;\n border-bottom-right-radius: 4px;\n }\n\n .librebot-widget.rtl .librebot-header-content {\n flex-direction: row-reverse;\n }\n\n .librebot-widget.rtl .librebot-input {\n text-align: right;\n }\n\n .librebot-widget.rtl .librebot-input-area {\n flex-direction: row-reverse;\n }\n\n .librebot-widget.rtl .librebot-ul,\n .librebot-widget.rtl .librebot-ol {\n padding-left: 0;\n padding-right: 20px;\n }\n\n .librebot-widget.rtl .librebot-blockquote {\n border-left: none;\n border-right: 3px solid var(--lb-primary);\n border-radius: 6px 0 0 6px;\n }\n\n /* Booking Mode Styles */\n .librebot-booking {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n .librebot-booking-header {\n padding: 12px 16px;\n border-bottom: 1px solid var(--lb-border);\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .librebot-booking-back {\n width: 32px;\n height: 32px;\n border-radius: 8px;\n border: none;\n background: transparent;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--lb-text-secondary);\n transition: background 0.2s;\n }\n\n .librebot-booking-back:hover {\n background: var(--lb-bg-secondary);\n }\n\n .librebot-booking-back svg {\n width: 20px;\n height: 20px;\n }\n\n .librebot-booking-title {\n font-size: 16px;\n font-weight: 600;\n color: var(--lb-text);\n margin: 0;\n }\n\n .librebot-booking-content {\n flex: 1;\n overflow-y: auto;\n padding: 16px;\n }\n\n .librebot-booking-section {\n margin-bottom: 20px;\n }\n\n .librebot-booking-label {\n display: block;\n font-size: 13px;\n font-weight: 600;\n color: var(--lb-text);\n margin-bottom: 8px;\n }\n\n .librebot-date-input {\n width: 100%;\n padding: 10px 14px;\n border: 1px solid var(--lb-border);\n border-radius: 8px;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n font-size: 14px;\n outline: none;\n cursor: pointer;\n }\n\n .librebot-date-input:focus {\n border-color: var(--lb-primary);\n }\n\n .librebot-slots {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 8px;\n }\n\n .librebot-slot {\n padding: 10px 8px;\n border: 1px solid var(--lb-border);\n border-radius: 8px;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n font-size: 13px;\n cursor: pointer;\n transition: all 0.2s;\n text-align: center;\n }\n\n .librebot-slot:hover {\n border-color: var(--lb-primary);\n }\n\n .librebot-slot.selected {\n background: var(--lb-primary);\n border-color: var(--lb-primary);\n color: white;\n }\n\n .librebot-slot:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .librebot-slots-loading,\n .librebot-slots-empty {\n text-align: center;\n padding: 20px;\n color: var(--lb-text-secondary);\n font-size: 13px;\n }\n\n .librebot-form-field {\n margin-bottom: 16px;\n }\n\n .librebot-form-label {\n display: block;\n font-size: 13px;\n font-weight: 500;\n color: var(--lb-text);\n margin-bottom: 6px;\n }\n\n .librebot-form-label .required {\n color: #ef4444;\n margin-left: 2px;\n }\n\n .librebot-form-input,\n .librebot-form-textarea,\n .librebot-form-select {\n width: 100%;\n padding: 10px 14px;\n border: 1px solid var(--lb-border);\n border-radius: 8px;\n background: var(--lb-bg-secondary);\n color: var(--lb-text);\n font-size: 14px;\n outline: none;\n font-family: inherit;\n }\n\n .librebot-form-input:focus,\n .librebot-form-textarea:focus,\n .librebot-form-select:focus {\n border-color: var(--lb-primary);\n }\n\n .librebot-form-textarea {\n min-height: 80px;\n resize: vertical;\n }\n\n .librebot-booking-submit {\n width: 100%;\n padding: 12px;\n border: none;\n border-radius: 8px;\n background: var(--lb-primary);\n color: white;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n transition: background 0.2s;\n margin-top: 12px;\n }\n\n .librebot-booking-submit:hover {\n background: var(--lb-primary-hover);\n }\n\n .librebot-booking-submit:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n /* Booking Success */\n .librebot-booking-success {\n text-align: center;\n padding: 24px 16px;\n }\n\n .librebot-booking-success-icon {\n width: 60px;\n height: 60px;\n margin: 0 auto 16px;\n background: var(--lb-primary);\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .librebot-booking-success-icon svg {\n width: 32px;\n height: 32px;\n fill: white;\n }\n\n .librebot-booking-success h3 {\n font-size: 18px;\n font-weight: 600;\n color: var(--lb-text);\n margin: 0 0 16px;\n }\n\n .librebot-booking-details {\n background: var(--lb-bg-secondary);\n border-radius: 8px;\n padding: 16px;\n margin-bottom: 16px;\n text-align: left;\n }\n\n .librebot-booking-detail {\n display: flex;\n justify-content: space-between;\n padding: 8px 0;\n border-bottom: 1px solid var(--lb-border);\n font-size: 13px;\n }\n\n .librebot-booking-detail:last-child {\n border-bottom: none;\n }\n\n .librebot-booking-detail-label {\n color: var(--lb-text-secondary);\n }\n\n .librebot-booking-detail-value {\n color: var(--lb-text);\n font-weight: 500;\n }\n\n .librebot-meeting-link {\n display: inline-block;\n padding: 12px 24px;\n background: var(--lb-primary);\n color: white;\n text-decoration: none;\n border-radius: 8px;\n font-weight: 600;\n margin-bottom: 16px;\n transition: background 0.2s;\n }\n\n .librebot-meeting-link:hover {\n background: var(--lb-primary-hover);\n }\n\n /* RTL Booking Styles */\n .librebot-widget.rtl .librebot-booking-header {\n flex-direction: row-reverse;\n }\n\n .librebot-widget.rtl .librebot-form-label .required {\n margin-left: 0;\n margin-right: 2px;\n }\n\n .librebot-widget.rtl .librebot-form-input,\n .librebot-widget.rtl .librebot-form-textarea {\n text-align: right;\n }\n\n .librebot-widget.rtl .librebot-booking-detail {\n flex-direction: row-reverse;\n }\n\n .librebot-widget.rtl .librebot-booking-details {\n text-align: right;\n }\n`;\n","export const chatIcon = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"></path></svg>`;\n\nexport const closeIcon = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line></svg>`;\n\nexport const sendIcon = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"></line><polygon points=\"22 2 15 22 11 13 2 9 22 2\"></polygon></svg>`;\n\nexport const botIcon = `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z\"/></svg>`;\n\nexport const backIcon = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"15 18 9 12 15 6\"></polyline></svg>`;\n\nexport const calendarIcon = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"></rect><line x1=\"16\" y1=\"2\" x2=\"16\" y2=\"6\"></line><line x1=\"8\" y1=\"2\" x2=\"8\" y2=\"6\"></line><line x1=\"3\" y1=\"10\" x2=\"21\" y2=\"10\"></line></svg>`;\n\nexport const checkIcon = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"></polyline></svg>`;\n","export type SupportedLang =\n | 'en' | 'fr' | 'de' | 'nl' | 'es' | 'pt' | 'ko' | 'ja' | 'zh' | 'hi' | 'ar'\n | 'bn' | 'ru' | 'id' | 'vi' | 'tr' | 'it' | 'th' | 'pl' | 'uk' | 'ms' | 'cs' | 'sv' | 'da' | 'is';\n\nexport interface WidgetTranslations {\n title: string;\n subtitle: string;\n placeholder: string;\n welcomeMessage: string;\n errorPrefix: string;\n connectionError: string;\n streamingNotSupported: string;\n poweredBy: string;\n // Booking translations\n bookingTitle: string;\n selectDate: string;\n selectTime: string;\n bookAppointment: string;\n noSlotsAvailable: string;\n loadingSlots: string;\n bookingSuccess: string;\n bookingError: string;\n backToChat: string;\n yourDetails: string;\n confirmBooking: string;\n meetingWith: string;\n duration: string;\n minutes: string;\n joinMeeting: string;\n}\n\nexport const WIDGET_TRANSLATIONS: Record<SupportedLang, WidgetTranslations> = {\n en: {\n title: 'Libre Bot',\n subtitle: 'AI Documentation Assistant',\n placeholder: 'Ask a question...',\n welcomeMessage: 'Hi! I can help you find answers in the documentation. What would you like to know?',\n errorPrefix: 'Sorry, I encountered an error:',\n connectionError: 'Sorry, I had trouble connecting. Please try again.',\n streamingNotSupported: 'Sorry, streaming is not supported.',\n poweredBy: 'Powered by',\n bookingTitle: 'Book an Appointment',\n selectDate: 'Select a date',\n selectTime: 'Select a time',\n bookAppointment: 'Book Appointment',\n noSlotsAvailable: 'No time slots available for this date',\n loadingSlots: 'Loading available times...',\n bookingSuccess: 'Your appointment has been confirmed!',\n bookingError: 'Failed to book appointment. Please try again.',\n backToChat: 'Back to chat',\n yourDetails: 'Your Details',\n confirmBooking: 'Confirm Booking',\n meetingWith: 'Meeting',\n duration: 'Duration',\n minutes: 'minutes',\n joinMeeting: 'Join Meeting',\n },\n fr: {\n title: 'Libre Bot',\n subtitle: 'Assistant IA de documentation',\n placeholder: 'Posez une question...',\n welcomeMessage: 'Bonjour ! Je peux vous aider à trouver des réponses dans la documentation. Que souhaitez-vous savoir ?',\n errorPrefix: 'Désolé, j\\'ai rencontré une erreur :',\n connectionError: 'Désolé, j\\'ai eu du mal à me connecter. Veuillez réessayer.',\n streamingNotSupported: 'Désolé, le streaming n\\'est pas pris en charge.',\n poweredBy: 'Propulsé par',\n bookingTitle: 'Prendre rendez-vous',\n selectDate: 'Choisir une date',\n selectTime: 'Choisir une heure',\n bookAppointment: 'Prendre rendez-vous',\n noSlotsAvailable: 'Aucun créneau disponible pour cette date',\n loadingSlots: 'Chargement des horaires...',\n bookingSuccess: 'Votre rendez-vous a été confirmé !',\n bookingError: 'Échec de la réservation. Veuillez réessayer.',\n backToChat: 'Retour au chat',\n yourDetails: 'Vos informations',\n confirmBooking: 'Confirmer la réservation',\n meetingWith: 'Réunion',\n duration: 'Durée',\n minutes: 'minutes',\n joinMeeting: 'Rejoindre la réunion',\n },\n de: {\n title: 'Libre Bot',\n subtitle: 'KI-Dokumentationsassistent',\n placeholder: 'Stellen Sie eine Frage...',\n welcomeMessage: 'Hallo! Ich kann Ihnen helfen, Antworten in der Dokumentation zu finden. Was möchten Sie wissen?',\n errorPrefix: 'Entschuldigung, ein Fehler ist aufgetreten:',\n connectionError: 'Entschuldigung, ich hatte Verbindungsprobleme. Bitte versuchen Sie es erneut.',\n streamingNotSupported: 'Entschuldigung, Streaming wird nicht unterstützt.',\n poweredBy: 'Betrieben von',\n bookingTitle: 'Termin buchen',\n selectDate: 'Datum auswählen',\n selectTime: 'Uhrzeit auswählen',\n bookAppointment: 'Termin buchen',\n noSlotsAvailable: 'Keine Termine für dieses Datum verfügbar',\n loadingSlots: 'Verfügbare Zeiten werden geladen...',\n bookingSuccess: 'Ihr Termin wurde bestätigt!',\n bookingError: 'Terminbuchung fehlgeschlagen. Bitte versuchen Sie es erneut.',\n backToChat: 'Zurück zum Chat',\n yourDetails: 'Ihre Angaben',\n confirmBooking: 'Buchung bestätigen',\n meetingWith: 'Meeting',\n duration: 'Dauer',\n minutes: 'Minuten',\n joinMeeting: 'An Meeting teilnehmen',\n },\n nl: {\n title: 'Libre Bot',\n subtitle: 'AI-documentatieassistent',\n placeholder: 'Stel een vraag...',\n welcomeMessage: 'Hallo! Ik kan je helpen antwoorden te vinden in de documentatie. Wat wil je weten?',\n errorPrefix: 'Sorry, er is een fout opgetreden:',\n connectionError: 'Sorry, ik had moeite met verbinden. Probeer het opnieuw.',\n streamingNotSupported: 'Sorry, streaming wordt niet ondersteund.',\n poweredBy: 'Mogelijk gemaakt door',\n bookingTitle: 'Afspraak maken',\n selectDate: 'Selecteer een datum',\n selectTime: 'Selecteer een tijd',\n bookAppointment: 'Afspraak maken',\n noSlotsAvailable: 'Geen tijdsloten beschikbaar voor deze datum',\n loadingSlots: 'Beschikbare tijden laden...',\n bookingSuccess: 'Je afspraak is bevestigd!',\n bookingError: 'Afspraak maken mislukt. Probeer het opnieuw.',\n backToChat: 'Terug naar chat',\n yourDetails: 'Je gegevens',\n confirmBooking: 'Boeking bevestigen',\n meetingWith: 'Vergadering',\n duration: 'Duur',\n minutes: 'minuten',\n joinMeeting: 'Deelnemen aan vergadering',\n },\n es: {\n title: 'Libre Bot',\n subtitle: 'Asistente de documentación con IA',\n placeholder: 'Haz una pregunta...',\n welcomeMessage: '¡Hola! Puedo ayudarte a encontrar respuestas en la documentación. ¿Qué te gustaría saber?',\n errorPrefix: 'Lo siento, encontré un error:',\n connectionError: 'Lo siento, tuve problemas para conectarme. Por favor, inténtalo de nuevo.',\n streamingNotSupported: 'Lo siento, el streaming no está soportado.',\n poweredBy: 'Desarrollado por',\n bookingTitle: 'Reservar cita',\n selectDate: 'Seleccionar fecha',\n selectTime: 'Seleccionar hora',\n bookAppointment: 'Reservar cita',\n noSlotsAvailable: 'No hay horarios disponibles para esta fecha',\n loadingSlots: 'Cargando horarios disponibles...',\n bookingSuccess: '¡Tu cita ha sido confirmada!',\n bookingError: 'Error al reservar la cita. Por favor, inténtalo de nuevo.',\n backToChat: 'Volver al chat',\n yourDetails: 'Tus datos',\n confirmBooking: 'Confirmar reserva',\n meetingWith: 'Reunión',\n duration: 'Duración',\n minutes: 'minutos',\n joinMeeting: 'Unirse a la reunión',\n },\n pt: {\n title: 'Libre Bot',\n subtitle: 'Assistente de documentação com IA',\n placeholder: 'Faça uma pergunta...',\n welcomeMessage: 'Olá! Posso ajudá-lo a encontrar respostas na documentação. O que você gostaria de saber?',\n errorPrefix: 'Desculpe, encontrei um erro:',\n connectionError: 'Desculpe, tive problemas para conectar. Por favor, tente novamente.',\n streamingNotSupported: 'Desculpe, streaming não é suportado.',\n poweredBy: 'Desenvolvido por',\n bookingTitle: 'Agendar consulta',\n selectDate: 'Selecionar data',\n selectTime: 'Selecionar horário',\n bookAppointment: 'Agendar consulta',\n noSlotsAvailable: 'Nenhum horário disponível para esta data',\n loadingSlots: 'Carregando horários disponíveis...',\n bookingSuccess: 'Seu agendamento foi confirmado!',\n bookingError: 'Falha ao agendar. Por favor, tente novamente.',\n backToChat: 'Voltar ao chat',\n yourDetails: 'Seus dados',\n confirmBooking: 'Confirmar agendamento',\n meetingWith: 'Reunião',\n duration: 'Duração',\n minutes: 'minutos',\n joinMeeting: 'Entrar na reunião',\n },\n ko: {\n title: 'Libre Bot',\n subtitle: 'AI 문서 도우미',\n placeholder: '질문하세요...',\n welcomeMessage: '안녕하세요! 문서에서 답변을 찾는 데 도움을 드릴 수 있습니다. 무엇을 알고 싶으신가요?',\n errorPrefix: '죄송합니다. 오류가 발생했습니다:',\n connectionError: '죄송합니다. 연결에 문제가 있습니다. 다시 시도해 주세요.',\n streamingNotSupported: '죄송합니다. 스트리밍이 지원되지 않습니다.',\n poweredBy: '제공:',\n bookingTitle: '예약하기',\n selectDate: '날짜 선택',\n selectTime: '시간 선택',\n bookAppointment: '예약하기',\n noSlotsAvailable: '선택한 날짜에 가능한 시간이 없습니다',\n loadingSlots: '가능한 시간 로딩 중...',\n bookingSuccess: '예약이 확정되었습니다!',\n bookingError: '예약에 실패했습니다. 다시 시도해 주세요.',\n backToChat: '채팅으로 돌아가기',\n yourDetails: '정보 입력',\n confirmBooking: '예약 확정',\n meetingWith: '미팅',\n duration: '소요 시간',\n minutes: '분',\n joinMeeting: '미팅 참가',\n },\n ja: {\n title: 'Libre Bot',\n subtitle: 'AIドキュメントアシスタント',\n placeholder: '質問してください...',\n welcomeMessage: 'こんにちは!ドキュメントから回答を見つけるお手伝いができます。何をお知りになりたいですか?',\n errorPrefix: '申し訳ありません。エラーが発生しました:',\n connectionError: '申し訳ありません。接続に問題がありました。もう一度お試しください。',\n streamingNotSupported: '申し訳ありません。ストリーミングはサポートされていません。',\n poweredBy: '提供:',\n bookingTitle: '予約する',\n selectDate: '日付を選択',\n selectTime: '時間を選択',\n bookAppointment: '予約する',\n noSlotsAvailable: 'この日は空き時間がありません',\n loadingSlots: '空き時間を読み込み中...',\n bookingSuccess: 'ご予約が確定しました!',\n bookingError: '予約に失敗しました。もう一度お試しください。',\n backToChat: 'チャットに戻る',\n yourDetails: 'お客様情報',\n confirmBooking: '予約を確定',\n meetingWith: 'ミーティング',\n duration: '所要時間',\n minutes: '分',\n joinMeeting: 'ミーティングに参加',\n },\n zh: {\n title: 'Libre Bot',\n subtitle: 'AI文档助手',\n placeholder: '请提问...',\n welcomeMessage: '您好!我可以帮助您在文档中查找答案。您想了解什么?',\n errorPrefix: '抱歉,遇到了错误:',\n connectionError: '抱歉,连接出现问题。请重试。',\n streamingNotSupported: '抱歉,不支持流式传输。',\n poweredBy: '由以下提供支持:',\n bookingTitle: '预约',\n selectDate: '选择日期',\n selectTime: '选择时间',\n bookAppointment: '预约',\n noSlotsAvailable: '该日期没有可用时间段',\n loadingSlots: '加载可用时间...',\n bookingSuccess: '您的预约已确认!',\n bookingError: '预约失败。请重试。',\n backToChat: '返回聊天',\n yourDetails: '您的信息',\n confirmBooking: '确认预约',\n meetingWith: '会议',\n duration: '时长',\n minutes: '分钟',\n joinMeeting: '加入会议',\n },\n hi: {\n title: 'Libre Bot',\n subtitle: 'AI दस्तावेज़ सहायक',\n placeholder: 'कोई प्रश्न पूछें...',\n welcomeMessage: 'नमस्ते! मैं आपको दस्तावेज़ों में उत्तर खोजने में मदद कर सकता हूं। आप क्या जानना चाहते हैं?',\n errorPrefix: 'क्षमा करें, एक त्रुटि हुई:',\n connectionError: 'क्षमा करें, कनेक्ट करने में समस्या हुई। कृपया पुनः प्रयास करें।',\n streamingNotSupported: 'क्षमा करें, स्ट्रीमिंग समर्थित नहीं है।',\n poweredBy: 'द्वारा संचालित',\n bookingTitle: 'अपॉइंटमेंट बुक करें',\n selectDate: 'तिथि चुनें',\n selectTime: 'समय चुनें',\n bookAppointment: 'अपॉइंटमेंट बुक करें',\n noSlotsAvailable: 'इस तिथि के लिए कोई समय उपलब्ध नहीं है',\n loadingSlots: 'उपलब्ध समय लोड हो रहा है...',\n bookingSuccess: 'आपकी अपॉइंटमेंट की पुष्टि हो गई है!',\n bookingError: 'बुकिंग विफल। कृपया पुनः प्रयास करें।',\n backToChat: 'चैट पर वापस जाएं',\n yourDetails: 'आपका विवरण',\n confirmBooking: 'बुकिंग की पुष्टि करें',\n meetingWith: 'मीटिंग',\n duration: 'अवधि',\n minutes: 'मिनट',\n joinMeeting: 'मीटिंग में शामिल हों',\n },\n ar: {\n title: 'Libre Bot',\n subtitle: 'مساعد الوثائق بالذكاء الاصطناعي',\n placeholder: 'اطرح سؤالاً...',\n welcomeMessage: 'مرحباً! يمكنني مساعدتك في العثور على إجابات في الوثائق. ماذا تريد أن تعرف؟',\n errorPrefix: 'عذراً، حدث خطأ:',\n connectionError: 'عذراً، واجهت مشكلة في الاتصال. يرجى المحاولة مرة أخرى.',\n streamingNotSupported: 'عذراً، البث غير مدعوم.',\n poweredBy: 'مدعوم من',\n bookingTitle: 'حجز موعد',\n selectDate: 'اختر التاريخ',\n selectTime: 'اختر الوقت',\n bookAppointment: 'حجز موعد',\n noSlotsAvailable: 'لا توجد أوقات متاحة لهذا التاريخ',\n loadingSlots: 'جاري تحميل الأوقات المتاحة...',\n bookingSuccess: 'تم تأكيد موعدك!',\n bookingError: 'فشل الحجز. يرجى المحاولة مرة أخرى.',\n backToChat: 'العودة إلى المحادثة',\n yourDetails: 'بياناتك',\n confirmBooking: 'تأكيد الحجز',\n meetingWith: 'اجتماع',\n duration: 'المدة',\n minutes: 'دقيقة',\n joinMeeting: 'انضم إلى الاجتماع',\n },\n // Bengali (বাংলা) - ~270M speakers\n bn: {\n title: 'Libre Bot',\n subtitle: 'AI ডকুমেন্টেশন সহায়ক',\n placeholder: 'একটি প্রশ্ন জিজ্ঞাসা করুন...',\n welcomeMessage: 'হ্যালো! আমি আপনাকে ডকুমেন্টেশনে উত্তর খুঁজে পেতে সাহায্য করতে পারি। আপনি কী জানতে চান?',\n errorPrefix: 'দুঃখিত, একটি ত্রুটি ঘটেছে:',\n connectionError: 'দুঃখিত, সংযোগে সমস্যা হয়েছে। অনুগ্রহ করে আবার চেষ্টা করুন।',\n streamingNotSupported: 'দুঃখিত, স্ট্রিমিং সমর্থিত নয়।',\n poweredBy: 'দ্বারা চালিত',\n bookingTitle: 'অ্যাপয়েন্টমেন্ট বুক করুন',\n selectDate: 'তারিখ নির্বাচন করুন',\n selectTime: 'সময় নির্বাচন করুন',\n bookAppointment: 'অ্যাপয়েন্টমেন্ট বুক করুন',\n noSlotsAvailable: 'এই তারিখে কোনো সময় উপলব্ধ নেই',\n loadingSlots: 'উপলব্ধ সময় লোড হচ্ছে...',\n bookingSuccess: 'আপনার অ্যাপয়েন্টমেন্ট নিশ্চিত হয়েছে!',\n bookingError: 'বুকিং ব্যর্থ হয়েছে। অনুগ্রহ করে আবার চেষ্টা করুন।',\n backToChat: 'চ্যাটে ফিরে যান',\n yourDetails: 'আপনার তথ্য',\n confirmBooking: 'বুকিং নিশ্চিত করুন',\n meetingWith: 'মিটিং',\n duration: 'সময়কাল',\n minutes: 'মিনিট',\n joinMeeting: 'মিটিংয়ে যোগ দিন',\n },\n // Russian (Русский) - ~255M speakers\n ru: {\n title: 'Libre Bot',\n subtitle: 'ИИ-ассистент по документации',\n placeholder: 'Задайте вопрос...',\n welcomeMessage: 'Привет! Я могу помочь вам найти ответы в документации. Что бы вы хотели узнать?',\n errorPrefix: 'Извините, произошла ошибка:',\n connectionError: 'Извините, возникла проблема с подключением. Пожалуйста, попробуйте снова.',\n streamingNotSupported: 'Извините, потоковая передача не поддерживается.',\n poweredBy: 'Работает на',\n bookingTitle: 'Записаться на приём',\n selectDate: 'Выберите дату',\n selectTime: 'Выберите время',\n bookAppointment: 'Записаться',\n noSlotsAvailable: 'На эту дату нет свободного времени',\n loadingSlots: 'Загрузка доступного времени...',\n bookingSuccess: 'Ваша запись подтверждена!',\n bookingError: 'Не удалось записаться. Пожалуйста, попробуйте снова.',\n backToChat: 'Вернуться к чату',\n yourDetails: 'Ваши данные',\n confirmBooking: 'Подтвердить запись',\n meetingWith: 'Встреча',\n duration: 'Продолжительность',\n minutes: 'минут',\n joinMeeting: 'Присоединиться к встрече',\n },\n // Indonesian (Bahasa Indonesia) - ~200M speakers\n id: {\n title: 'Libre Bot',\n subtitle: 'Asisten Dokumentasi AI',\n placeholder: 'Ajukan pertanyaan...',\n welcomeMessage: 'Halo! Saya dapat membantu Anda menemukan jawaban di dokumentasi. Apa yang ingin Anda ketahui?',\n errorPrefix: 'Maaf, terjadi kesalahan:',\n connectionError: 'Maaf, ada masalah koneksi. Silakan coba lagi.',\n streamingNotSupported: 'Maaf, streaming tidak didukung.',\n poweredBy: 'Didukung oleh',\n bookingTitle: 'Buat Janji',\n selectDate: 'Pilih tanggal',\n selectTime: 'Pilih waktu',\n bookAppointment: 'Buat Janji',\n noSlotsAvailable: 'Tidak ada waktu tersedia untuk tanggal ini',\n loadingSlots: 'Memuat waktu tersedia...',\n bookingSuccess: 'Janji Anda telah dikonfirmasi!',\n bookingError: 'Gagal membuat janji. Silakan coba lagi.',\n backToChat: 'Kembali ke chat',\n yourDetails: 'Data Anda',\n confirmBooking: 'Konfirmasi Pemesanan',\n meetingWith: 'Pertemuan',\n duration: 'Durasi',\n minutes: 'menit',\n joinMeeting: 'Gabung Pertemuan',\n },\n // Vietnamese (Tiếng Việt) - ~85M speakers\n vi: {\n title: 'Libre Bot',\n subtitle: 'Trợ lý tài liệu AI',\n placeholder: 'Đặt câu hỏi...',\n welcomeMessage: 'Xin chào! Tôi có thể giúp bạn tìm câu trả lời trong tài liệu. Bạn muốn biết điều gì?',\n errorPrefix: 'Xin lỗi, đã xảy ra lỗi:',\n connectionError: 'Xin lỗi, có vấn đề kết nối. Vui lòng thử lại.',\n streamingNotSupported: 'Xin lỗi, không hỗ trợ phát trực tuyến.',\n poweredBy: 'Được cung cấp bởi',\n bookingTitle: 'Đặt lịch hẹn',\n selectDate: 'Chọn ngày',\n selectTime: 'Chọn giờ',\n bookAppointment: 'Đặt lịch hẹn',\n noSlotsAvailable: 'Không có thời gian trống cho ngày này',\n loadingSlots: 'Đang tải thời gian trống...',\n bookingSuccess: 'Lịch hẹn của bạn đã được xác nhận!',\n bookingError: 'Đặt lịch thất bại. Vui lòng thử lại.',\n backToChat: 'Quay lại chat',\n yourDetails: 'Thông tin của bạn',\n confirmBooking: 'Xác nhận đặt lịch',\n meetingWith: 'Cuộc họp',\n duration: 'Thời lượng',\n minutes: 'phút',\n joinMeeting: 'Tham gia cuộc họp',\n },\n // Turkish (Türkçe) - ~80M speakers\n tr: {\n title: 'Libre Bot',\n subtitle: 'Yapay Zeka Dokümantasyon Asistanı',\n placeholder: 'Bir soru sorun...',\n welcomeMessage: 'Merhaba! Dokümantasyonda cevap bulmanıza yardımcı olabilirim. Ne öğrenmek istersiniz?',\n errorPrefix: 'Üzgünüm, bir hata oluştu:',\n connectionError: 'Üzgünüm, bağlantı sorunu yaşadım. Lütfen tekrar deneyin.',\n streamingNotSupported: 'Üzgünüm, akış desteklenmiyor.',\n poweredBy: 'Tarafından desteklenmektedir',\n bookingTitle: 'Randevu Al',\n selectDate: 'Tarih seçin',\n selectTime: 'Saat seçin',\n bookAppointment: 'Randevu Al',\n noSlotsAvailable: 'Bu tarih için müsait saat yok',\n loadingSlots: 'Müsait saatler yükleniyor...',\n bookingSuccess: 'Randevunuz onaylandı!',\n bookingError: 'Randevu alınamadı. Lütfen tekrar deneyin.',\n backToChat: 'Sohbete dön',\n yourDetails: 'Bilgileriniz',\n confirmBooking: 'Randevuyu Onayla',\n meetingWith: 'Toplantı',\n duration: 'Süre',\n minutes: 'dakika',\n joinMeeting: 'Toplantıya Katıl',\n },\n // Italian (Italiano) - ~65M speakers\n it: {\n title: 'Libre Bot',\n subtitle: 'Assistente documentazione AI',\n placeholder: 'Fai una domanda...',\n welcomeMessage: 'Ciao! Posso aiutarti a trovare risposte nella documentazione. Cosa vorresti sapere?',\n errorPrefix: 'Mi dispiace, si è verificato un errore:',\n connectionError: 'Mi dispiace, ho avuto problemi di connessione. Per favore riprova.',\n streamingNotSupported: 'Mi dispiace, lo streaming non è supportato.',\n poweredBy: 'Offerto da',\n bookingTitle: 'Prenota un Appuntamento',\n selectDate: 'Seleziona una data',\n selectTime: 'Seleziona un orario',\n bookAppointment: 'Prenota Appuntamento',\n noSlotsAvailable: 'Nessun orario disponibile per questa data',\n loadingSlots: 'Caricamento orari disponibili...',\n bookingSuccess: 'Il tuo appuntamento è stato confermato!',\n bookingError: 'Prenotazione fallita. Per favore riprova.',\n backToChat: 'Torna alla chat',\n yourDetails: 'I tuoi dati',\n confirmBooking: 'Conferma Prenotazione',\n meetingWith: 'Riunione',\n duration: 'Durata',\n minutes: 'minuti',\n joinMeeting: 'Partecipa alla Riunione',\n },\n // Thai (ไทย) - ~60M speakers\n th: {\n title: 'Libre Bot',\n subtitle: 'ผู้ช่วยเอกสาร AI',\n placeholder: 'ถามคำถาม...',\n welcomeMessage: 'สวัสดี! ฉันสามารถช่วยคุณค้นหาคำตอบในเอกสารได้ คุณต้องการทราบอะไร?',\n errorPrefix: 'ขออภัย เกิดข้อผิดพลาด:',\n connectionError: 'ขออภัย มีปัญหาในการเชื่อมต่อ กรุณาลองอีกครั้ง',\n streamingNotSupported: 'ขออภัย ไม่รองรับการสตรีม',\n poweredBy: 'ขับเคลื่อนโดย',\n bookingTitle: 'จองนัดหมาย',\n selectDate: 'เลือกวันที่',\n selectTime: 'เลือกเวลา',\n bookAppointment: 'จองนัดหมาย',\n noSlotsAvailable: 'ไม่มีช่วงเวลาว่างสำหรับวันนี้',\n loadingSlots: 'กำลังโหลดเวลาว่าง...',\n bookingSuccess: 'การนัดหมายของคุณได้รับการยืนยันแล้ว!',\n bookingError: 'การจองล้มเหลว กรุณาลองอีกครั้ง',\n backToChat: 'กลับไปที่แชท',\n yourDetails: 'ข้อมูลของคุณ',\n confirmBooking: 'ยืนยันการจอง',\n meetingWith: 'การประชุม',\n duration: 'ระยะเวลา',\n minutes: 'นาที',\n joinMeeting: 'เข้าร่วมการประชุม',\n },\n // Polish (Polski) - ~45M speakers\n pl: {\n title: 'Libre Bot',\n subtitle: 'Asystent dokumentacji AI',\n placeholder: 'Zadaj pytanie...',\n welcomeMessage: 'Cześć! Mogę pomóc Ci znaleźć odpowiedzi w dokumentacji. Co chciałbyś wiedzieć?',\n errorPrefix: 'Przepraszam, wystąpił błąd:',\n connectionError: 'Przepraszam, miałem problem z połączeniem. Spróbuj ponownie.',\n streamingNotSupported: 'Przepraszam, streaming nie jest obsługiwany.',\n poweredBy: 'Obsługiwane przez',\n bookingTitle: 'Umów wizytę',\n selectDate: 'Wybierz datę',\n selectTime: 'Wybierz godzinę',\n bookAppointment: 'Umów wizytę',\n noSlotsAvailable: 'Brak dostępnych terminów na ten dzień',\n loadingSlots: 'Ładowanie dostępnych terminów...',\n bookingSuccess: 'Twoja wizyta została potwierdzona!',\n bookingError: 'Nie udało się umówić wizyty. Spróbuj ponownie.',\n backToChat: 'Wróć do czatu',\n yourDetails: 'Twoje dane',\n confirmBooking: 'Potwierdź rezerwację',\n meetingWith: 'Spotkanie',\n duration: 'Czas trwania',\n minutes: 'minut',\n joinMeeting: 'Dołącz do spotkania',\n },\n // Ukrainian (Українська) - ~40M speakers\n uk: {\n title: 'Libre Bot',\n subtitle: 'ШІ-асистент документації',\n placeholder: 'Поставте запитання...',\n welcomeMessage: 'Привіт! Я можу допомогти вам знайти відповіді в документації. Що б ви хотіли дізнатися?',\n errorPrefix: 'Вибачте, сталася помилка:',\n connectionError: 'Вибачте, виникла проблема з підключенням. Будь ласка, спробуйте ще раз.',\n streamingNotSupported: 'Вибачте, потокова передача не підтримується.',\n poweredBy: 'Працює на',\n bookingTitle: 'Записатися на прийом',\n selectDate: 'Оберіть дату',\n selectTime: 'Оберіть час',\n bookAppointment: 'Записатися',\n noSlotsAvailable: 'На цю дату немає вільного часу',\n loadingSlots: 'Завантаження доступного часу...',\n bookingSuccess: 'Ваш запис підтверджено!',\n bookingError: 'Не вдалося записатися. Будь ласка, спробуйте ще раз.',\n backToChat: 'Повернутися до чату',\n yourDetails: 'Ваші дані',\n confirmBooking: 'Підтвердити запис',\n meetingWith: 'Зустріч',\n duration: 'Тривалість',\n minutes: 'хвилин',\n joinMeeting: 'Приєднатися до зустрічі',\n },\n // Malay (Bahasa Melayu) - ~30M speakers\n ms: {\n title: 'Libre Bot',\n subtitle: 'Pembantu Dokumentasi AI',\n placeholder: 'Tanya soalan...',\n welcomeMessage: 'Hai! Saya boleh membantu anda mencari jawapan dalam dokumentasi. Apa yang anda ingin tahu?',\n errorPrefix: 'Maaf, berlaku ralat:',\n connectionError: 'Maaf, ada masalah sambungan. Sila cuba lagi.',\n streamingNotSupported: 'Maaf, penstriman tidak disokong.',\n poweredBy: 'Dikuasakan oleh',\n bookingTitle: 'Buat Temujanji',\n selectDate: 'Pilih tarikh',\n selectTime: 'Pilih masa',\n bookAppointment: 'Buat Temujanji',\n noSlotsAvailable: 'Tiada masa tersedia untuk tarikh ini',\n loadingSlots: 'Memuatkan masa tersedia...',\n bookingSuccess: 'Temujanji anda telah disahkan!',\n bookingError: 'Gagal membuat temujanji. Sila cuba lagi.',\n backToChat: 'Kembali ke sembang',\n yourDetails: 'Maklumat Anda',\n confirmBooking: 'Sahkan Tempahan',\n meetingWith: 'Mesyuarat',\n duration: 'Tempoh',\n minutes: 'minit',\n joinMeeting: 'Sertai Mesyuarat',\n },\n // Czech (Čeština) - ~10M speakers\n cs: {\n title: 'Libre Bot',\n subtitle: 'AI asistent dokumentace',\n placeholder: 'Položte otázku...',\n welcomeMessage: 'Ahoj! Mohu vám pomoci najít odpovědi v dokumentaci. Co byste chtěli vědět?',\n errorPrefix: 'Omlouvám se, došlo k chybě:',\n connectionError: 'Omlouvám se, měl jsem problém s připojením. Zkuste to prosím znovu.',\n streamingNotSupported: 'Omlouvám se, streamování není podporováno.',\n poweredBy: 'Využívá technologii',\n bookingTitle: 'Rezervovat schůzku',\n selectDate: 'Vyberte datum',\n selectTime: 'Vyberte čas',\n bookAppointment: 'Rezervovat schůzku',\n noSlotsAvailable: 'Na tento den nejsou k dispozici žádné termíny',\n loadingSlots: 'Načítání dostupných termínů...',\n bookingSuccess: 'Vaše schůzka byla potvrzena!',\n bookingError: 'Rezervace se nezdařila. Zkuste to prosím znovu.',\n backToChat: 'Zpět na chat',\n yourDetails: 'Vaše údaje',\n confirmBooking: 'Potvrdit rezervaci',\n meetingWith: 'Schůzka',\n duration: 'Délka',\n minutes: 'minut',\n joinMeeting: 'Připojit se ke schůzce',\n },\n // Swedish (Svenska) - ~10M speakers\n sv: {\n title: 'Libre Bot',\n subtitle: 'AI-dokumentationsassistent',\n placeholder: 'Ställ en fråga...',\n welcomeMessage: 'Hej! Jag kan hjälpa dig hitta svar i dokumentationen. Vad vill du veta?',\n errorPrefix: 'Tyvärr uppstod ett fel:',\n connectionError: 'Tyvärr hade jag problem med anslutningen. Försök igen.',\n streamingNotSupported: 'Tyvärr stöds inte streaming.',\n poweredBy: 'Drivs av',\n bookingTitle: 'Boka tid',\n selectDate: 'Välj datum',\n selectTime: 'Välj tid',\n bookAppointment: 'Boka tid',\n noSlotsAvailable: 'Inga lediga tider för detta datum',\n loadingSlots: 'Laddar lediga tider...',\n bookingSuccess: 'Din bokning har bekräftats!',\n bookingError: 'Bokningen misslyckades. Försök igen.',\n backToChat: 'Tillbaka till chatten',\n yourDetails: 'Dina uppgifter',\n confirmBooking: 'Bekräfta bokning',\n meetingWith: 'Möte',\n duration: 'Varaktighet',\n minutes: 'minuter',\n joinMeeting: 'Gå med i mötet',\n },\n // Danish (Dansk) - ~6M speakers\n da: {\n title: 'Libre Bot',\n subtitle: 'AI-dokumentationsassistent',\n placeholder: 'Stil et spørgsmål...',\n welcomeMessage: 'Hej! Jeg kan hjælpe dig med at finde svar i dokumentationen. Hvad vil du gerne vide?',\n errorPrefix: 'Beklager, der opstod en fejl:',\n connectionError: 'Beklager, jeg havde problemer med forbindelsen. Prøv venligst igen.',\n streamingNotSupported: 'Beklager, streaming understøttes ikke.',\n poweredBy: 'Drevet af',\n bookingTitle: 'Book en tid',\n selectDate: 'Vælg dato',\n selectTime: 'Vælg tidspunkt',\n bookAppointment: 'Book tid',\n noSlotsAvailable: 'Ingen ledige tider på denne dato',\n loadingSlots: 'Indlæser ledige tider...',\n bookingSuccess: 'Din booking er bekræftet!',\n bookingError: 'Booking mislykkedes. Prøv venligst igen.',\n backToChat: 'Tilbage til chat',\n yourDetails: 'Dine oplysninger',\n confirmBooking: 'Bekræft booking',\n meetingWith: 'Møde',\n duration: 'Varighed',\n minutes: 'minutter',\n joinMeeting: 'Deltag i mødet',\n },\n // Icelandic (Íslenska) - ~350K speakers\n is: {\n title: 'Libre Bot',\n subtitle: 'Gervigreind skjalahjálpari',\n placeholder: 'Spurðu spurningu...',\n welcomeMessage: 'Hæ! Ég get hjálpað þér að finna svör í skjölunum. Hvað viltu vita?',\n errorPrefix: 'Því miður kom upp villa:',\n connectionError: 'Því miður átti ég í vandræðum með tengingu. Vinsamlegast reyndu aftur.',\n streamingNotSupported: 'Því miður er streymi ekki stutt.',\n poweredBy: 'Knúið af',\n bookingTitle: 'Bóka tíma',\n selectDate: 'Veldu dagsetningu',\n selectTime: 'Veldu tíma',\n bookAppointment: 'Bóka tíma',\n noSlotsAvailable: 'Engir tímar lausir á þessum degi',\n loadingSlots: 'Hleð lausum tímum...',\n bookingSuccess: 'Bókun þín hefur verið staðfest!',\n bookingError: 'Bókun mistókst. Vinsamlegast reyndu aftur.',\n backToChat: 'Til baka í spjall',\n yourDetails: 'Þínar upplýsingar',\n confirmBooking: 'Staðfesta bókun',\n meetingWith: 'Fundur',\n duration: 'Lengd',\n minutes: 'mínútur',\n joinMeeting: 'Taka þátt í fundi',\n },\n};\n\nexport const RTL_LANGUAGES: SupportedLang[] = ['ar'];\n\nexport function detectLanguage(): SupportedLang {\n // 1. Check document's lang attribute\n const htmlLang = document.documentElement.lang?.toLowerCase().split('-')[0];\n if (htmlLang && htmlLang in WIDGET_TRANSLATIONS) {\n return htmlLang as SupportedLang;\n }\n\n // 2. Check navigator.language\n const navLang = navigator.language?.toLowerCase().split('-')[0];\n if (navLang && navLang in WIDGET_TRANSLATIONS) {\n return navLang as SupportedLang;\n }\n\n // 3. Default to English\n return 'en';\n}\n","import { LibreBotConfig, Message, ChatResponse, BookingConfig, TimeSlot, BookingResult } from './types';\nimport { getStyles } from './styles';\nimport { chatIcon, closeIcon, sendIcon, backIcon, checkIcon } from './icons';\nimport {\n SupportedLang,\n WidgetTranslations,\n WIDGET_TRANSLATIONS,\n RTL_LANGUAGES,\n detectLanguage\n} from './translations';\n\ntype WidgetMode = 'chat' | 'booking';\n\nexport class LibreBotWidget {\n private config!: Required<LibreBotConfig>;\n private container: HTMLDivElement | null = null;\n private modal: HTMLDivElement | null = null;\n private messagesContainer: HTMLDivElement | null = null;\n private input: HTMLInputElement | null = null;\n private isOpen = false;\n private messages: Message[] = [];\n private isLoading = false;\n private sessionId: string | null = null;\n private translations: WidgetTranslations = WIDGET_TRANSLATIONS.en;\n private isRtl = false;\n\n // Booking mode state\n private mode: WidgetMode = 'chat';\n private bookingConfig: BookingConfig | null = null;\n private selectedDate: string | null = null;\n private selectedSlot: TimeSlot | null = null;\n private availableSlots: TimeSlot[] = [];\n private bookingContainer: HTMLDivElement | null = null;\n private chatContainer: HTMLDivElement | null = null;\n private inputArea: HTMLDivElement | null = null;\n\n private defaultConfig: Omit<Required<LibreBotConfig>, 'apiKey'> = {\n apiUrl: 'https://librebot.io/api',\n position: 'bottom-right',\n theme: 'dark',\n primaryColor: '#14b8a6',\n title: '', // Will be set from translations\n subtitle: '', // Will be set from translations\n placeholder: '', // Will be set from translations\n welcomeMessage: '', // Will be set from translations\n streaming: true,\n whiteLabel: false,\n autoLoadConfig: true,\n lang: 'en',\n };\n\n constructor(config: LibreBotConfig) {\n if (!config.apiKey) {\n console.error('LibreBot: API key is required');\n return;\n }\n\n // Detect language from config or host page\n const detectedLang = config.lang || detectLanguage();\n this.translations = WIDGET_TRANSLATIONS[detectedLang] || WIDGET_TRANSLATIONS.en;\n this.isRtl = RTL_LANGUAGES.includes(detectedLang);\n\n // Apply defaults with translations\n const configWithTranslations: Partial<LibreBotConfig> = {\n ...config,\n lang: detectedLang,\n title: config.title || this.translations.title,\n subtitle: config.subtitle || this.translations.subtitle,\n placeholder: config.placeholder || this.translations.placeholder,\n welcomeMessage: config.welcomeMessage || this.translations.welcomeMessage,\n };\n\n this.config = { ...this.defaultConfig, ...configWithTranslations } as Required<LibreBotConfig>;\n\n // Auto-load config from server if enabled and no custom settings provided\n if (this.config.autoLoadConfig) {\n this.loadRemoteConfig().then(() => this.init());\n } else {\n this.init();\n }\n }\n\n private async loadRemoteConfig(): Promise<void> {\n try {\n const response = await fetch(`${this.config.apiUrl}/widget-config?key=${this.config.apiKey}`);\n if (response.ok) {\n const remoteConfig = await response.json();\n // Merge remote config with local config (local overrides remote)\n this.config = {\n ...this.defaultConfig,\n ...remoteConfig,\n ...this.getProvidedConfig(),\n apiKey: this.config.apiKey,\n apiUrl: this.config.apiUrl,\n autoLoadConfig: this.config.autoLoadConfig,\n } as Required<LibreBotConfig>;\n }\n } catch (error) {\n console.warn('LibreBot: Could not load remote config, using defaults', error);\n }\n }\n\n private getProvidedConfig(): Partial<LibreBotConfig> {\n // Return only the config options that were explicitly provided (not default values)\n const provided: Partial<LibreBotConfig> = {};\n const userConfig = this.config;\n const defaults = this.defaultConfig;\n\n if (userConfig.position !== defaults.position) provided.position = userConfig.position;\n if (userConfig.theme !== defaults.theme) provided.theme = userConfig.theme;\n if (userConfig.primaryColor !== defaults.primaryColor) provided.primaryColor = userConfig.primaryColor;\n if (userConfig.title !== defaults.title) provided.title = userConfig.title;\n if (userConfig.subtitle !== defaults.subtitle) provided.subtitle = userConfig.subtitle;\n if (userConfig.placeholder !== defaults.placeholder) provided.placeholder = userConfig.placeholder;\n if (userConfig.welcomeMessage !== defaults.welcomeMessage) provided.welcomeMessage = userConfig.welcomeMessage;\n if (userConfig.streaming !== defaults.streaming) provided.streaming = userConfig.streaming;\n if (userConfig.whiteLabel !== defaults.whiteLabel) provided.whiteLabel = userConfig.whiteLabel;\n\n return provided;\n }\n\n private init(): void {\n // Load sessionId from localStorage for persistent memory\n this.loadSession();\n\n // Inject styles\n this.injectStyles();\n\n // Create widget container\n this.createWidget();\n\n // Add welcome message\n if (this.config.welcomeMessage) {\n this.addMessage('assistant', this.config.welcomeMessage);\n }\n }\n\n private getSessionKey(): string {\n return `librebot_session_${this.config.apiKey}`;\n }\n\n private loadSession(): void {\n try {\n const stored = localStorage.getItem(this.getSessionKey());\n if (stored) {\n this.sessionId = stored;\n }\n } catch {\n // localStorage may not be available\n }\n }\n\n private saveSession(sessionId: string): void {\n try {\n this.sessionId = sessionId;\n localStorage.setItem(this.getSessionKey(), sessionId);\n } catch {\n // localStorage may not be available\n }\n }\n\n private injectStyles(): void {\n const styleId = 'librebot-styles';\n if (document.getElementById(styleId)) return;\n\n const style = document.createElement('style');\n style.id = styleId;\n style.textContent = getStyles(this.config.primaryColor);\n document.head.appendChild(style);\n }\n\n private createWidget(): void {\n // Create container\n this.container = document.createElement('div');\n let className = 'librebot-widget';\n if (this.config.theme === 'light') className += ' light';\n if (this.isRtl) className += ' rtl';\n this.container.className = className;\n\n // Create FAB button\n const fab = document.createElement('button');\n fab.className = `librebot-fab ${this.config.position === 'bottom-left' ? 'left' : ''}`;\n fab.innerHTML = chatIcon;\n fab.onclick = () => this.toggle();\n\n // Create modal\n this.modal = document.createElement('div');\n this.modal.className = `librebot-modal ${this.config.position === 'bottom-left' ? 'left' : ''}`;\n this.modal.innerHTML = this.getModalHTML();\n\n // Add to container\n this.container.appendChild(fab);\n this.container.appendChild(this.modal);\n document.body.appendChild(this.container);\n\n // Get references\n this.messagesContainer = this.modal.querySelector('.librebot-messages');\n this.input = this.modal.querySelector('.librebot-input');\n this.chatContainer = this.modal.querySelector('.librebot-chat-container');\n this.bookingContainer = this.modal.querySelector('.librebot-booking-container');\n this.inputArea = this.modal.querySelector('.librebot-input-area');\n\n // Add event listeners\n const closeBtn = this.modal.querySelector('.librebot-close');\n closeBtn?.addEventListener('click', () => this.close());\n\n const sendBtn = this.modal.querySelector('.librebot-send');\n sendBtn?.addEventListener('click', () => this.sendMessage());\n\n this.input?.addEventListener('keypress', (e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n this.sendMessage();\n }\n });\n\n // Auto theme detection\n if (this.config.theme === 'auto') {\n const mediaQuery = window.matchMedia('(prefers-color-scheme: light)');\n this.updateTheme(mediaQuery.matches);\n mediaQuery.addEventListener('change', (e) => this.updateTheme(e.matches));\n }\n }\n\n private getModalHTML(): string {\n const poweredByHtml = this.config.whiteLabel\n ? ''\n : `<div class=\"librebot-powered\">\n ${this.translations.poweredBy} <a href=\"https://librebot.io\" target=\"_blank\">Libre Bot</a>\n </div>`;\n\n return `\n <div class=\"librebot-header\">\n <div class=\"librebot-header-content\">\n <div class=\"librebot-avatar\">${chatIcon}</div>\n <div>\n <h3 class=\"librebot-title\">${this.config.title}</h3>\n <p class=\"librebot-subtitle\">${this.config.subtitle}</p>\n </div>\n </div>\n <button class=\"librebot-close\">${closeIcon}</button>\n </div>\n <div class=\"librebot-chat-container\">\n <div class=\"librebot-messages\"></div>\n </div>\n <div class=\"librebot-booking-container\" style=\"display: none;\"></div>\n <div class=\"librebot-input-area\">\n <input type=\"text\" class=\"librebot-input\" placeholder=\"${this.config.placeholder}\" />\n <button class=\"librebot-send\">${sendIcon}</button>\n </div>\n ${poweredByHtml}\n `;\n }\n\n private updateTheme(isLight: boolean): void {\n if (isLight) {\n this.container?.classList.add('light');\n } else {\n this.container?.classList.remove('light');\n }\n }\n\n public toggle(): void {\n this.isOpen ? this.close() : this.open();\n }\n\n public open(): void {\n this.isOpen = true;\n this.modal?.classList.add('open');\n this.input?.focus();\n }\n\n public close(): void {\n this.isOpen = false;\n this.modal?.classList.remove('open');\n }\n\n private addMessage(role: 'user' | 'assistant', content: string): void {\n const message: Message = {\n id: `msg-${Date.now()}`,\n role,\n content,\n timestamp: new Date(),\n };\n\n this.messages.push(message);\n this.renderMessage(message);\n }\n\n private renderMessage(message: Message): void {\n if (!this.messagesContainer) return;\n\n const el = document.createElement('div');\n el.className = `librebot-message ${message.role}`;\n el.innerHTML = this.formatMessage(message.content);\n\n this.messagesContainer.appendChild(el);\n this.scrollToBottom();\n }\n\n private formatMessage(content: string): string {\n // Escape HTML to prevent XSS\n const escapeHtml = (text: string): string => {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#039;');\n };\n\n // First, extract code blocks to protect them from other formatting\n // Regex handles optional space/newline after ``` and optional language identifier\n const codeBlocks: string[] = [];\n let processed = content.replace(/```\\s*(\\w*)\\s*\\n?([\\s\\S]*?)```/g, (_, lang, code) => {\n const escaped = escapeHtml(code.trim());\n const langClass = lang ? ` data-language=\"${escapeHtml(lang)}\"` : '';\n codeBlocks.push(`<pre class=\"librebot-code-block\"${langClass}><code>${escaped}</code></pre>`);\n return `%%CODEBLOCK${codeBlocks.length - 1}%%`;\n });\n\n // Extract inline code\n const inlineCodes: string[] = [];\n processed = processed.replace(/`([^`]+)`/g, (_, code) => {\n inlineCodes.push(`<code class=\"librebot-inline-code\">${escapeHtml(code)}</code>`);\n return `%%INLINECODE${inlineCodes.length - 1}%%`;\n });\n\n // Now escape remaining HTML\n processed = escapeHtml(processed);\n\n // Headers (## and ###)\n processed = processed.replace(/^### (.+)$/gm, '<h4 class=\"librebot-h4\">$1</h4>');\n processed = processed.replace(/^## (.+)$/gm, '<h3 class=\"librebot-h3\">$1</h3>');\n processed = processed.replace(/^# (.+)$/gm, '<h2 class=\"librebot-h2\">$1</h2>');\n\n // Bold and italic\n processed = processed.replace(/\\*\\*\\*([^*]+)\\*\\*\\*/g, '<strong><em>$1</em></strong>');\n processed = processed.replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>');\n processed = processed.replace(/\\*([^*]+)\\*/g, '<em>$1</em>');\n processed = processed.replace(/_([^_]+)_/g, '<em>$1</em>');\n\n // Links [text](url)\n processed = processed.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, '<a href=\"$2\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"librebot-link\">$1</a>');\n\n // Unordered lists\n processed = processed.replace(/^[\\*\\-] (.+)$/gm, '<li class=\"librebot-li\">$1</li>');\n processed = processed.replace(/(<li class=\"librebot-li\">.*<\\/li>\\n?)+/g, (match) => {\n return `<ul class=\"librebot-ul\">${match}</ul>`;\n });\n\n // Ordered lists\n processed = processed.replace(/^\\d+\\. (.+)$/gm, '<li class=\"librebot-li-ordered\">$1</li>');\n processed = processed.replace(/(<li class=\"librebot-li-ordered\">.*<\\/li>\\n?)+/g, (match) => {\n return `<ol class=\"librebot-ol\">${match}</ol>`;\n });\n\n // Horizontal rule\n processed = processed.replace(/^---$/gm, '<hr class=\"librebot-hr\">');\n\n // Blockquotes\n processed = processed.replace(/^&gt; (.+)$/gm, '<blockquote class=\"librebot-blockquote\">$1</blockquote>');\n\n // Convert newlines to <br> (but not inside lists/blockquotes)\n processed = processed.replace(/\\n/g, '<br>');\n\n // Clean up extra <br> after block elements\n processed = processed.replace(/<\\/(pre|ul|ol|blockquote|h[2-4])><br>/g, '</$1>');\n processed = processed.replace(/<br><(pre|ul|ol|blockquote|h[2-4])/g, '<$1');\n\n // Restore code blocks\n codeBlocks.forEach((block, i) => {\n processed = processed.replace(`%%CODEBLOCK${i}%%`, block);\n });\n\n // Restore inline code\n inlineCodes.forEach((code, i) => {\n processed = processed.replace(`%%INLINECODE${i}%%`, code);\n });\n\n return processed;\n }\n\n private showTyping(): HTMLDivElement {\n const typing = document.createElement('div');\n typing.className = 'librebot-typing';\n typing.innerHTML = '<span></span><span></span><span></span>';\n this.messagesContainer?.appendChild(typing);\n this.scrollToBottom();\n return typing;\n }\n\n private scrollToBottom(): void {\n if (this.messagesContainer) {\n this.messagesContainer.scrollTop = this.messagesContainer.scrollHeight;\n }\n }\n\n private async sendMessage(): Promise<void> {\n if (!this.input || this.isLoading) return;\n\n const content = this.input.value.trim();\n if (!content) return;\n\n // Add user message\n this.addMessage('user', content);\n this.input.value = '';\n\n // Show typing indicator\n this.isLoading = true;\n const typing = this.showTyping();\n\n try {\n if (this.config.streaming) {\n await this.callStreamingAPI(content, typing);\n } else {\n const response = await this.callAPI(content);\n typing.remove();\n\n if (response.error) {\n this.addMessage('assistant', `${this.translations.errorPrefix} ${response.error}`);\n } else {\n this.addMessage('assistant', response.response);\n // Check for booking intent\n this.handleBookingIntent(response);\n }\n }\n } catch (error) {\n typing.remove();\n this.addMessage('assistant', this.translations.connectionError);\n console.error('LibreBot error:', error);\n } finally {\n this.isLoading = false;\n }\n }\n\n private async callStreamingAPI(message: string, typing: HTMLDivElement): Promise<void> {\n const response = await fetch(`${this.config.apiUrl}/chat`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config.apiKey}`,\n },\n body: JSON.stringify({\n message,\n context: this.getConversationContext(),\n stream: true,\n sessionId: this.sessionId,\n }),\n });\n\n if (!response.ok) {\n typing.remove();\n const error = await response.json().catch(() => ({ error: 'Request failed' }));\n this.addMessage('assistant', `${this.translations.errorPrefix} ${error.error || 'Request failed'}`);\n return;\n }\n\n // Remove typing indicator and create streaming message element\n typing.remove();\n const messageEl = document.createElement('div');\n messageEl.className = 'librebot-message assistant';\n this.messagesContainer?.appendChild(messageEl);\n\n const reader = response.body?.getReader();\n if (!reader) {\n this.addMessage('assistant', this.translations.streamingNotSupported);\n return;\n }\n\n const decoder = new TextDecoder();\n let fullContent = '';\n let bookingIntentData: ChatResponse | null = null;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n const chunk = decoder.decode(value, { stream: true });\n const lines = chunk.split('\\n').filter((line) => line.trim() !== '');\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n const data = line.slice(6);\n if (data === '[DONE]') continue;\n\n try {\n const parsed = JSON.parse(data);\n if (parsed.content) {\n fullContent += parsed.content;\n messageEl.innerHTML = this.formatMessage(fullContent);\n this.scrollToBottom();\n }\n // Save sessionId when received from server\n if (parsed.sessionId) {\n this.saveSession(parsed.sessionId);\n }\n // Check for booking intent in metadata\n if (parsed.booking_intent && parsed.booking_config) {\n bookingIntentData = parsed;\n }\n } catch {\n // Skip malformed JSON\n }\n }\n }\n }\n } catch (error) {\n console.error('Stream reading error:', error);\n }\n\n // Add to messages array\n this.messages.push({\n id: `msg-${Date.now()}`,\n role: 'assistant',\n content: fullContent,\n timestamp: new Date(),\n });\n\n // Handle booking intent if detected\n if (bookingIntentData) {\n this.handleBookingIntent(bookingIntentData);\n }\n }\n\n private async callAPI(message: string): Promise<ChatResponse> {\n const response = await fetch(`${this.config.apiUrl}/chat`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config.apiKey}`,\n },\n body: JSON.stringify({\n message,\n context: this.getConversationContext(),\n sessionId: this.sessionId,\n }),\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({ error: 'Request failed' }));\n return { response: '', error: error.error || 'Request failed' };\n }\n\n const data = await response.json();\n // Save sessionId when received from server\n if (data.sessionId) {\n this.saveSession(data.sessionId);\n }\n return data;\n }\n\n private getConversationContext(): Array<{ role: string; content: string }> {\n // Get last 10 messages for context\n return this.messages.slice(-10).map((m) => ({\n role: m.role,\n content: m.content,\n }));\n }\n\n public destroy(): void {\n this.container?.remove();\n document.getElementById('librebot-styles')?.remove();\n }\n\n public clearSession(): void {\n try {\n this.sessionId = null;\n this.messages = [];\n localStorage.removeItem(this.getSessionKey());\n if (this.messagesContainer) {\n this.messagesContainer.innerHTML = '';\n }\n // Re-add welcome message\n if (this.config.welcomeMessage) {\n this.addMessage('assistant', this.config.welcomeMessage);\n }\n } catch {\n // localStorage may not be available\n }\n }\n\n // Booking Mode Methods\n\n private switchToBookingMode(config: BookingConfig): void {\n this.bookingConfig = config;\n this.mode = 'booking';\n this.selectedDate = null;\n this.selectedSlot = null;\n this.availableSlots = [];\n\n // Hide chat, show booking\n if (this.chatContainer) this.chatContainer.style.display = 'none';\n if (this.inputArea) this.inputArea.style.display = 'none';\n if (this.bookingContainer) {\n this.bookingContainer.style.display = 'flex';\n this.renderBookingUI();\n }\n }\n\n private switchToChatMode(): void {\n this.mode = 'chat';\n this.bookingConfig = null;\n this.selectedDate = null;\n this.selectedSlot = null;\n this.availableSlots = [];\n\n // Show chat, hide booking\n if (this.chatContainer) this.chatContainer.style.display = 'flex';\n if (this.inputArea) this.inputArea.style.display = 'flex';\n if (this.bookingContainer) {\n this.bookingContainer.style.display = 'none';\n this.bookingContainer.innerHTML = '';\n }\n }\n\n private renderBookingUI(): void {\n if (!this.bookingContainer || !this.bookingConfig) return;\n\n const minDate = new Date();\n minDate.setDate(minDate.getDate() + 1);\n const maxDate = new Date();\n maxDate.setDate(maxDate.getDate() + (this.bookingConfig.advance_booking_days || 30));\n\n const formFieldsHtml = this.bookingConfig.form_fields.map(field => {\n const requiredMark = field.required ? '<span class=\"required\">*</span>' : '';\n const requiredAttr = field.required ? 'required' : '';\n\n if (field.type === 'textarea') {\n return `\n <div class=\"librebot-form-field\">\n <label class=\"librebot-form-label\">${field.label}${requiredMark}</label>\n <textarea\n class=\"librebot-form-textarea\"\n name=\"${field.name}\"\n placeholder=\"${field.placeholder || ''}\"\n ${requiredAttr}\n ></textarea>\n </div>\n `;\n }\n\n if (field.type === 'select' && field.options) {\n const optionsHtml = field.options.map(opt =>\n `<option value=\"${opt}\">${opt}</option>`\n ).join('');\n return `\n <div class=\"librebot-form-field\">\n <label class=\"librebot-form-label\">${field.label}${requiredMark}</label>\n <select\n class=\"librebot-form-select\"\n name=\"${field.name}\"\n ${requiredAttr}\n >\n <option value=\"\">${field.placeholder || 'Select...'}</option>\n ${optionsHtml}\n </select>\n </div>\n `;\n }\n\n return `\n <div class=\"librebot-form-field\">\n <label class=\"librebot-form-label\">${field.label}${requiredMark}</label>\n <input\n type=\"${field.type}\"\n class=\"librebot-form-input\"\n name=\"${field.name}\"\n placeholder=\"${field.placeholder || ''}\"\n ${requiredAttr}\n />\n </div>\n `;\n }).join('');\n\n this.bookingContainer.innerHTML = `\n <div class=\"librebot-booking\">\n <div class=\"librebot-booking-header\">\n <button class=\"librebot-booking-back\">${backIcon}</button>\n <h3 class=\"librebot-booking-title\">${this.translations.bookingTitle}</h3>\n </div>\n <div class=\"librebot-booking-content\">\n <div class=\"librebot-booking-section\">\n <label class=\"librebot-booking-label\">${this.translations.selectDate}</label>\n <input\n type=\"date\"\n class=\"librebot-date-input\"\n min=\"${minDate.toISOString().split('T')[0]}\"\n max=\"${maxDate.toISOString().split('T')[0]}\"\n />\n </div>\n <div class=\"librebot-booking-section\">\n <label class=\"librebot-booking-label\">${this.translations.selectTime}</label>\n <div class=\"librebot-slots-container\">\n <div class=\"librebot-slots-empty\">${this.translations.selectDate}</div>\n </div>\n </div>\n <div class=\"librebot-booking-section librebot-form-section\" style=\"display: none;\">\n <label class=\"librebot-booking-label\">${this.translations.yourDetails}</label>\n <form class=\"librebot-booking-form\">\n ${formFieldsHtml}\n <button type=\"submit\" class=\"librebot-booking-submit\" disabled>\n ${this.translations.confirmBooking}\n </button>\n </form>\n </div>\n </div>\n </div>\n `;\n\n // Add event listeners\n const backBtn = this.bookingContainer.querySelector('.librebot-booking-back');\n backBtn?.addEventListener('click', () => this.switchToChatMode());\n\n const dateInput = this.bookingContainer.querySelector('.librebot-date-input') as HTMLInputElement;\n dateInput?.addEventListener('change', (e) => {\n const target = e.target as HTMLInputElement;\n this.selectedDate = target.value;\n this.selectedSlot = null;\n this.loadAvailableSlots(target.value);\n });\n\n const form = this.bookingContainer.querySelector('.librebot-booking-form') as HTMLFormElement;\n form?.addEventListener('submit', (e) => {\n e.preventDefault();\n this.submitBooking(form);\n });\n }\n\n private async loadAvailableSlots(date: string): Promise<void> {\n const slotsContainer = this.bookingContainer?.querySelector('.librebot-slots-container');\n if (!slotsContainer) return;\n\n slotsContainer.innerHTML = `<div class=\"librebot-slots-loading\">${this.translations.loadingSlots}</div>`;\n\n try {\n const response = await fetch(\n `${this.config.apiUrl}/bookings/slots?date=${date}&timezone=${Intl.DateTimeFormat().resolvedOptions().timeZone}`,\n {\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n },\n }\n );\n\n if (!response.ok) {\n throw new Error('Failed to load slots');\n }\n\n const data = await response.json();\n this.availableSlots = data.slots || [];\n\n if (this.availableSlots.length === 0) {\n slotsContainer.innerHTML = `<div class=\"librebot-slots-empty\">${this.translations.noSlotsAvailable}</div>`;\n return;\n }\n\n const slotsHtml = this.availableSlots.map(slot => {\n const disabled = !slot.available ? 'disabled' : '';\n return `\n <button\n type=\"button\"\n class=\"librebot-slot\"\n data-start=\"${slot.start}\"\n data-end=\"${slot.end}\"\n ${disabled}\n >${slot.start}</button>\n `;\n }).join('');\n\n slotsContainer.innerHTML = `<div class=\"librebot-slots\">${slotsHtml}</div>`;\n\n // Add click handlers to slot buttons\n const slotButtons = slotsContainer.querySelectorAll('.librebot-slot');\n slotButtons.forEach(btn => {\n btn.addEventListener('click', () => {\n const start = btn.getAttribute('data-start') || '';\n const end = btn.getAttribute('data-end') || '';\n this.selectSlot({ start, end, available: true }, btn);\n });\n });\n } catch (err) {\n console.error('Failed to load slots:', err);\n slotsContainer.innerHTML = `<div class=\"librebot-slots-empty\">${this.translations.bookingError}</div>`;\n }\n }\n\n private selectSlot(slot: TimeSlot, buttonEl: Element): void {\n this.selectedSlot = slot;\n\n // Update button styles\n const allSlots = this.bookingContainer?.querySelectorAll('.librebot-slot');\n allSlots?.forEach(el => el.classList.remove('selected'));\n buttonEl.classList.add('selected');\n\n // Show form section\n const formSection = this.bookingContainer?.querySelector('.librebot-form-section') as HTMLElement;\n if (formSection) {\n formSection.style.display = 'block';\n }\n\n // Enable submit button\n const submitBtn = this.bookingContainer?.querySelector('.librebot-booking-submit') as HTMLButtonElement;\n if (submitBtn) {\n submitBtn.disabled = false;\n }\n\n // Scroll to form\n formSection?.scrollIntoView({ behavior: 'smooth', block: 'start' });\n }\n\n private async submitBooking(form: HTMLFormElement): Promise<void> {\n if (!this.selectedDate || !this.selectedSlot || !this.bookingConfig) return;\n\n const submitBtn = form.querySelector('.librebot-booking-submit') as HTMLButtonElement;\n if (submitBtn) {\n submitBtn.disabled = true;\n submitBtn.textContent = '...';\n }\n\n // Collect form data\n const formData = new FormData(form);\n const formDataObj: Record<string, string> = {};\n formData.forEach((value, key) => {\n formDataObj[key] = value.toString();\n });\n\n try {\n const response = await fetch(`${this.config.apiUrl}/bookings/submit`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config.apiKey}`,\n },\n body: JSON.stringify({\n date: this.selectedDate,\n slot_start: this.selectedSlot.start,\n slot_end: this.selectedSlot.end,\n form_data: formDataObj,\n session_id: this.sessionId,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n }),\n });\n\n const result: BookingResult = await response.json();\n\n if (result.success && result.booking) {\n this.renderBookingSuccess(result.booking);\n } else {\n throw new Error(result.error || 'Booking failed');\n }\n } catch (err) {\n console.error('Booking submission failed:', err);\n if (submitBtn) {\n submitBtn.disabled = false;\n submitBtn.textContent = this.translations.confirmBooking;\n }\n alert(this.translations.bookingError);\n }\n }\n\n private renderBookingSuccess(booking: NonNullable<BookingResult['booking']>): void {\n if (!this.bookingContainer) return;\n\n // Format the date and time\n const startDate = new Date(booking.scheduled_start);\n const formattedDate = startDate.toLocaleDateString(undefined, {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n });\n const formattedTime = startDate.toLocaleTimeString(undefined, {\n hour: 'numeric',\n minute: '2-digit',\n });\n\n const meetingLinkHtml = booking.meeting_link\n ? `<a href=\"${booking.meeting_link}\" target=\"_blank\" class=\"librebot-meeting-link\">${this.translations.joinMeeting}</a>`\n : '';\n\n this.bookingContainer.innerHTML = `\n <div class=\"librebot-booking\">\n <div class=\"librebot-booking-header\">\n <button class=\"librebot-booking-back\">${backIcon}</button>\n <h3 class=\"librebot-booking-title\">${this.translations.bookingTitle}</h3>\n </div>\n <div class=\"librebot-booking-content\">\n <div class=\"librebot-booking-success\">\n <div class=\"librebot-booking-success-icon\">${checkIcon}</div>\n <h3>${this.translations.bookingSuccess}</h3>\n <div class=\"librebot-booking-details\">\n <div class=\"librebot-booking-detail\">\n <span class=\"librebot-booking-detail-label\">${this.translations.selectDate}</span>\n <span class=\"librebot-booking-detail-value\">${formattedDate}</span>\n </div>\n <div class=\"librebot-booking-detail\">\n <span class=\"librebot-booking-detail-label\">${this.translations.selectTime}</span>\n <span class=\"librebot-booking-detail-value\">${formattedTime}</span>\n </div>\n <div class=\"librebot-booking-detail\">\n <span class=\"librebot-booking-detail-label\">${this.translations.duration}</span>\n <span class=\"librebot-booking-detail-value\">${booking.duration_minutes} ${this.translations.minutes}</span>\n </div>\n </div>\n ${meetingLinkHtml}\n <button class=\"librebot-booking-submit\" type=\"button\">${this.translations.backToChat}</button>\n </div>\n </div>\n </div>\n `;\n\n // Add event listeners\n const backBtn = this.bookingContainer.querySelector('.librebot-booking-back');\n backBtn?.addEventListener('click', () => this.switchToChatMode());\n\n const chatBtn = this.bookingContainer.querySelector('.librebot-booking-submit');\n chatBtn?.addEventListener('click', () => this.switchToChatMode());\n }\n\n private handleBookingIntent(data: ChatResponse): void {\n if (data.booking_intent && data.booking_config) {\n // Add a brief delay so user can see the AI response before switching\n setTimeout(() => {\n this.switchToBookingMode(data.booking_config!);\n }, 1500);\n }\n }\n}\n","import { LibreBotWidget } from './widget';\nimport { LibreBotConfig } from './types';\nimport { SupportedLang } from './translations';\n\n// Auto-initialize from script tag\n(async function () {\n // Find the script tag - document.currentScript may be null with defer\n const script = document.currentScript as HTMLScriptElement\n || document.querySelector('script[data-key][src*=\"librebot\"]') as HTMLScriptElement;\n\n if (!script) {\n console.warn('LibreBot: Could not find script tag');\n return;\n }\n\n const apiKey = script.dataset.key || script.dataset.apiKey || '';\n const apiUrl = script.dataset.apiUrl || 'https://api.librebot.io';\n\n if (!apiKey) {\n console.error('LibreBot: Missing data-key attribute');\n return;\n }\n\n // Fetch remote config from dashboard settings\n let remoteConfig: Partial<LibreBotConfig> = {};\n try {\n const response = await fetch(`${apiUrl}/api/widget-config?key=${encodeURIComponent(apiKey)}`);\n if (response.ok) {\n remoteConfig = await response.json();\n }\n } catch (e) {\n // Silently fail - use defaults/data attributes\n }\n\n // Build config: remote settings as base, data attributes as overrides\n const config: LibreBotConfig = {\n apiKey,\n apiUrl,\n // Remote config as defaults, data attributes override\n position: (script.dataset.position as 'bottom-right' | 'bottom-left') || remoteConfig.position || 'bottom-right',\n theme: (script.dataset.theme as 'light' | 'dark' | 'auto') || remoteConfig.theme || 'dark',\n primaryColor: script.dataset.color || remoteConfig.primaryColor || '#14b8a6',\n title: script.dataset.title || remoteConfig.title || 'Libre Bot',\n subtitle: script.dataset.subtitle || remoteConfig.subtitle || 'AI Documentation Assistant',\n placeholder: script.dataset.placeholder || remoteConfig.placeholder || undefined, // Let language detection handle default\n welcomeMessage: script.dataset.welcome || remoteConfig.welcomeMessage || undefined, // Let language detection handle default\n streaming: script.dataset.streaming !== 'false', // default true\n whiteLabel: remoteConfig.whiteLabel || false,\n lang: (script.dataset.lang as SupportedLang) || remoteConfig.lang, // Allow explicit language override\n };\n\n // Initialize when DOM is ready\n const initWidget = () => {\n (window as any).LibreBot = new LibreBotWidget(config);\n };\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', initWidget);\n } else {\n initWidget();\n }\n})();\n\n// Export for manual initialization\nexport { LibreBotWidget, LibreBotConfig };\n"],"names":["chatIcon","backIcon","WIDGET_TRANSLATIONS","en","title","subtitle","placeholder","welcomeMessage","errorPrefix","connectionError","streamingNotSupported","poweredBy","bookingTitle","selectDate","selectTime","bookAppointment","noSlotsAvailable","loadingSlots","bookingSuccess","bookingError","backToChat","yourDetails","confirmBooking","meetingWith","duration","minutes","joinMeeting","fr","de","nl","es","pt","ko","ja","zh","hi","ar","bn","ru","id","vi","tr","it","th","pl","uk","ms","cs","sv","da","is","RTL_LANGUAGES","LibreBotWidget","constructor","config","this","container","modal","messagesContainer","input","isOpen","messages","isLoading","sessionId","translations","isRtl","mode","bookingConfig","selectedDate","selectedSlot","availableSlots","bookingContainer","chatContainer","inputArea","defaultConfig","apiUrl","position","theme","primaryColor","streaming","whiteLabel","autoLoadConfig","lang","apiKey","console","error","detectedLang","htmlLang","document","documentElement","toLowerCase","split","navLang","navigator","language","detectLanguage","includes","configWithTranslations","loadRemoteConfig","then","init","response","fetch","ok","remoteConfig","json","getProvidedConfig","warn","provided","userConfig","defaults","loadSession","injectStyles","createWidget","addMessage","getSessionKey","stored","localStorage","getItem","saveSession","setItem","getElementById","style","createElement","textContent","getStyles","head","appendChild","className","fab","innerHTML","onclick","toggle","getModalHTML","body","querySelector","closeBtn","addEventListener","close","sendBtn","sendMessage","e","key","shiftKey","preventDefault","mediaQuery","window","matchMedia","updateTheme","matches","poweredByHtml","isLight","classList","add","remove","open","focus","role","content","message","Date","now","timestamp","push","renderMessage","el","formatMessage","scrollToBottom","escapeHtml","text","replace","codeBlocks","processed","_","code","escaped","trim","langClass","length","inlineCodes","match","forEach","block","i","showTyping","typing","scrollTop","scrollHeight","value","callStreamingAPI","callAPI","handleBookingIntent","method","headers","Authorization","JSON","stringify","context","getConversationContext","stream","catch","messageEl","reader","getReader","decoder","TextDecoder","fullContent","bookingIntentData","done","read","lines","decode","filter","line","startsWith","data","slice","parsed","parse","booking_intent","booking_config","map","m","destroy","clearSession","removeItem","switchToBookingMode","display","renderBookingUI","switchToChatMode","minDate","setDate","getDate","maxDate","advance_booking_days","formFieldsHtml","form_fields","field","requiredMark","required","requiredAttr","type","label","name","options","optionsHtml","opt","join","toISOString","backBtn","dateInput","target","loadAvailableSlots","form","submitBooking","date","slotsContainer","Intl","DateTimeFormat","resolvedOptions","timeZone","Error","slots","slotsHtml","slot","disabled","available","start","end","querySelectorAll","btn","getAttribute","selectSlot","err","buttonEl","allSlots","formSection","submitBtn","scrollIntoView","behavior","formData","FormData","formDataObj","toString","slot_start","slot_end","form_data","session_id","timezone","result","success","booking","renderBookingSuccess","alert","startDate","scheduled_start","formattedDate","toLocaleDateString","undefined","weekday","year","month","day","formattedTime","toLocaleTimeString","hour","minute","meetingLinkHtml","meeting_link","duration_minutes","chatBtn","setTimeout","script","currentScript","dataset","encodeURIComponent","color","welcome","initWidget","LibreBot","readyState"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kroonen-ai/librebot-widget",
3
- "version": "1.6.1",
3
+ "version": "1.6.2",
4
4
  "type": "module",
5
5
  "description": "Libre Bot - AI documentation assistant widget",
6
6
  "main": "dist/librebot.js",