1import { d as he, a3 as W, m as re, N as lt, o as r, c as d, A as ie, b as o, e as x, _ as g, f as l, G as J, aj as Re, g as _, F as le, r as pe, t as S, i as m, R as xe, Z as ee, $ as te, a0 as ae, aI as Q, M as Le, U as Pe, J as z, ar as Ue, a as Z, ah as rt, w as it, q as Me, ai as ut, j as G, a8 as dt, a4 as ct, X as vt } from "./app-KW02oTuk.js"; 2import ft from "./AiChatBotMessage-Bwq2lmqs.js"; 3import "./purifier-DwKNMBJW.js"; 4import "./index-C44AX5GR.js"; 5const pt = ["aria-hidden"], mt = { class: "flex items-center gap-2 px-5 py-3 border-b border-gray-200 shrink-0" }, yt = { class: "px-5 py-2 border-b border-gray-200 shrink-0" }, gt = { class: "relative" }, ht = { key: 0, class: "text-center text-gray-400 italic text-[13px] py-8" }, xt = { key: 1, class: "text-center text-gray-400 italic text-[13px] py-8" }, bt = ["onClick"], wt = { class: "ai-history-icon" }, kt = { class: "flex-1 min-w-0 text-left" }, Ct = { class: "text-[13px] font-semibold text-gray-900 leading-tight truncate" }, St = { class: "text-[11px] text-gray-500 mt-0.5" }, _t = { key: 0, class: "flex items-center justify-center gap-2 text-[11px] text-gray-400 py-3" }, $t = { class: "px-4 py-2.5 border-t border-gray-200 shrink-0" }, Et = 250, At = he({ __name: "AiChatHistoryPanel", props: { open: { type: Boolean }, entries: {}, activeConversationId: {}, hasMore: { type: Boolean }, loadingMore: { type: Boolean }, searching: { type: Boolean } }, emits: ["close", "select", "new-conversation", "load-more", "search"], setup(e, { emit: a }) { 6 const u = e, h = a, C = m(""), v = W("sentinelEl"), y = W("scrollEl"); 7 let w = null, b = null; 8 const A = ($) => { 9 const s = new Date($); 10 return Number.isNaN(s.getTime()) ? $ : s.toLocaleString("fr-FR", { day: "2-digit", month: "short", hour: "2-digit", minute: "2-digit" }); 11 }, D = () => { 12 w !== null && clearTimeout(w), w = setTimeout(() => { 13 h("search", C.value.trim()); 14 }, Et); 15 }, M = () => { 16 b && (b.disconnect(), b = null); 17 }, B = () => { 18 M(), !(!v.value || !y.value) && (b = new IntersectionObserver(($) => { 19 for (const s of $) s.isIntersecting && u.hasMore && !u.loadingMore && h("load-more"); 20 }, { root: y.value, rootMargin: "120px 0px" }), b.observe(v.value)); 21 }; 22 return re(() => u.open, ($) => { 23 $ ? setTimeout(B, 0) : M(); 24 }, { immediate: true }), re(() => u.entries.length, () => { 25 u.open && setTimeout(B, 0); 26 }), lt(() => { 27 w !== null && clearTimeout(w), M(); 28 }), ($, s) => (r(), d("div", { class: ie(["ai-history", { "is-open": e.open }]), "aria-hidden": !e.open }, [o("div", mt, [o("button", { type: "button", class: "ai-icon-btn", "aria-label": "Retour", onClick: s[0] || (s[0] = (c) => h("close")) }, [x(g, { name: "fas fa-arrow-left" })]), s[3] || (s[3] = l()), s[4] || (s[4] = o("h3", { class: "m-0 text-sm font-bold flex-1 text-gray-900" }, ` 29 Historique 30 `, -1))]), s[13] || (s[13] = l()), o("div", yt, [o("div", gt, [x(g, { name: "fas fa-magnifying-glass", class: "absolute left-2.5 top-2.5 text-gray-400 text-xs" }), s[5] || (s[5] = l()), J(o("input", { "onUpdate:modelValue": s[1] || (s[1] = (c) => C.value = c), type: "text", placeholder: "Rechercher dans l'historique\u2026", class: "w-full pl-8 pr-2.5 py-1.5 border border-gray-300 rounded-md text-[13px] bg-gray-100 focus:bg-white focus:border-brand-500 focus:ring-2 focus:ring-brand-100 outline-hidden", onInput: D }, null, 544), [[Re, C.value]])])]), s[14] || (s[14] = l()), o("div", { ref_key: "scrollEl", ref: y, class: "flex-1 overflow-auto px-2 py-1" }, [e.searching && e.entries.length === 0 ? (r(), d("div", ht, ` 31 Recherche\u2026 32 `)) : e.entries.length === 0 ? (r(), d("div", xt, ` 33 Aucune conversation 34 `)) : _("", true), s[10] || (s[10] = l()), (r(true), d(le, null, pe(e.entries, (c) => (r(), d("button", { key: c.id, type: "button", class: ie(["ai-history-it", { "is-active": c.id === e.activeConversationId }]), onClick: (P) => h("select", c.id) }, [o("div", wt, [x(g, { name: "fas fa-message" })]), s[7] || (s[7] = l()), o("div", kt, [o("div", Ct, S(c.title), 1), s[6] || (s[6] = l()), o("div", St, S(A(c.updated_at)), 1)])], 10, bt))), 128)), s[11] || (s[11] = l()), o("div", { ref_key: "sentinelEl", ref: v, class: "ai-history-sentinel", "aria-hidden": "true" }, [e.loadingMore ? (r(), d("div", _t, [x(g, { name: "fas fa-spinner-third fa-spin" }), s[8] || (s[8] = l()), s[9] || (s[9] = o("span", null, "Chargement\u2026", -1))])) : _("", true)], 512)], 512), s[15] || (s[15] = l()), o("div", $t, [o("button", { type: "button", class: "w-full py-2.5 rounded-lg border border-dashed border-gray-300 bg-transparent text-gray-700 text-[13px] font-medium hover:bg-gray-100 hover:border-gray-400 hover:text-gray-900 inline-flex items-center justify-center gap-1.5", onClick: s[2] || (s[2] = (c) => h("new-conversation")) }, [x(g, { name: "fas fa-plus" }), s[12] || (s[12] = l(` 35 Nouvelle conversation 36 `, -1))])])], 10, pt)); 37} }), Mt = xe(At, [["__scopeId", "data-v-13906abf"]]), O = (e, a) => ({ url: O.url(e, a), method: "get" }); 38O.definition = { methods: ["get", "head"], url: "https://dev.dendreo.test/{of}/ai/chat/attachments/{message}/{index}" }; 39O.url = (e, a) => { 40 Array.isArray(e) && (e = { of: e[0], message: e[1], index: e[2] }), e = ee(e); 41 const u = { of: e.of, message: e.message, index: e.index }; 42 return O.definition.url.replace("{of}", u.of.toString()).replace("{message}", u.message.toString()).replace("{index}", u.index.toString()).replace(/\/+$/, "") + te(a); 43}; 44O.relative = (e, a) => ae(O.url(e, a)); 45O.get = (e, a) => ({ url: O.url(e, a), method: "get" }); 46O.head = (e, a) => ({ url: O.url(e, a), method: "head" }); 47const Oe = { show: Object.assign(O, O) }, T = (e, a) => ({ url: T.url(e, a), method: "get" }); 48T.definition = { methods: ["get", "head"], url: "https://dev.dendreo.test/{of}/ai/chat" }; 49T.url = (e, a) => { 50 (typeof e == "string" || typeof e == "number") && (e = { of: e }), Array.isArray(e) && (e = { of: e[0] }), e = ee(e); 51 const u = { of: e.of }; 52 return T.definition.url.replace("{of}", u.of.toString()).replace(/\/+$/, "") + te(a); 53}; 54T.relative = (e, a) => ae(T.url(e, a)); 55T.get = (e, a) => ({ url: T.url(e, a), method: "get" }); 56T.head = (e, a) => ({ url: T.url(e, a), method: "head" }); 57const U = (e, a) => ({ url: U.url(e, a), method: "post" }); 58U.definition = { methods: ["post"], url: "https://dev.dendreo.test/{of}/ai/chat" }; 59U.url = (e, a) => { 60 (typeof e == "string" || typeof e == "number") && (e = { of: e }), Array.isArray(e) && (e = { of: e[0] }), e = ee(e); 61 const u = { of: e.of }; 62 return U.definition.url.replace("{of}", u.of.toString()).replace(/\/+$/, "") + te(a); 63}; 64U.relative = (e, a) => ae(U.url(e, a)); 65U.post = (e, a) => ({ url: U.url(e, a), method: "post" }); 66const I = (e, a) => ({ url: I.url(e, a), method: "get" }); 67I.definition = { methods: ["get", "head"], url: "https://dev.dendreo.test/{of}/ai/chat/latest" }; 68I.url = (e, a) => { 69 (typeof e == "string" || typeof e == "number") && (e = { of: e }), Array.isArray(e) && (e = { of: e[0] }), e = ee(e); 70 const u = { of: e.of }; 71 return I.definition.url.replace("{of}", u.of.toString()).replace(/\/+$/, "") + te(a); 72}; 73I.relative = (e, a) => ae(I.url(e, a)); 74I.get = (e, a) => ({ url: I.url(e, a), method: "get" }); 75I.head = (e, a) => ({ url: I.url(e, a), method: "head" }); 76const N = (e, a) => ({ url: N.url(e, a), method: "get" }); 77N.definition = { methods: ["get", "head"], url: "https://dev.dendreo.test/{of}/ai/chat/{conversation}" }; 78N.url = (e, a) => { 79 Array.isArray(e) && (e = { of: e[0], conversation: e[1] }), e = ee(e); 80 const u = { of: e.of, conversation: typeof e.conversation == "object" ? e.conversation.id : e.conversation }; 81 return N.definition.url.replace("{of}", u.of.toString()).replace("{conversation}", u.conversation.toString()).replace(/\/+$/, "") + te(a); 82}; 83N.relative = (e, a) => ae(N.url(e, a)); 84N.get = (e, a) => ({ url: N.url(e, a), method: "get" }); 85N.head = (e, a) => ({ url: N.url(e, a), method: "head" }); 86const j = (e, a) => ({ url: j.url(e, a), method: "get" }); 87j.definition = { methods: ["get", "head"], url: "https://dev.dendreo.test/{of}/ai/chat/{conversation}/messages" }; 88j.url = (e, a) => { 89 Array.isArray(e) && (e = { of: e[0], conversation: e[1] }), e = ee(e); 90 const u = { of: e.of, conversation: typeof e.conversation == "object" ? e.conversation.id : e.conversation }; 91 return j.definition.url.replace("{of}", u.of.toString()).replace("{conversation}", u.conversation.toString()).replace(/\/+$/, "") + te(a); 92}; 93j.relative = (e, a) => ae(j.url(e, a)); 94j.get = (e, a) => ({ url: j.url(e, a), method: "get" }); 95j.head = (e, a) => ({ url: j.url(e, a), method: "head" }); 96const ue = { index: Object.assign(T, T), store: Object.assign(U, U), latest: Object.assign(I, I), attachments: Object.assign(Oe, Oe), show: Object.assign(N, N), messages: Object.assign(j, j) }, Ot = async (e, a, u = null, h = []) => { 97 const C = ue.store.relative({ of: window.of }); 98 if (h.length === 0) { 99 const { data: b, status: A } = await Q.post(C, { prompt: e, conversation_id: a, page_context: u }); 100 return { data: b.data, status: A }; 101 } 102 const v = new FormData(); 103 v.append("prompt", e), a !== null && v.append("conversation_id", a), u && (v.append("page_context[type]", u.type), v.append("page_context[type_label]", u.type_label), v.append("page_context[id]", String(u.id)), v.append("page_context[label]", u.label), v.append("page_context[url]", u.url)), h.forEach((b, A) => { 104 v.append(`files[${A}]`, b); 105 }); 106 const { data: y, status: w } = await Q.post(C, v); 107 return { data: y.data, status: w }; 108}, Tt = async () => { 109 const e = ue.latest.relative({ of: window.of }), a = await Q.get(e); 110 return { data: a.status === 204 ? null : a.data.data, status: a.status }; 111}, Te = async (e) => { 112 const a = ue.show.relative({ of: window.of, conversation: e }), { data: u, status: h } = await Q.get(a); 113 return { data: u.data, status: h }; 114}, Ie = async (e = null, a = null) => { 115 const u = ue.index.relative({ of: window.of }), h = {}; 116 e && (h.cursor = e), a && (h.q = a); 117 const { data: C, status: v } = await Q.get(u, { params: Object.keys(h).length > 0 ? h : void 0 }); 118 return { data: C, status: v }; 119}, It = async (e, a) => { 120 const u = ue.messages.relative({ of: window.of, conversation: e }), { data: h, status: C } = await Q.get(u, { params: { cursor: a } }); 121 return { data: h, status: C }; 122}, Nt = () => { 123 const e = document.querySelector('meta[name="ai-page-context"]'); 124 if (!e?.content) return null; 125 try { 126 return JSON.parse(e.content); 127 } catch { 128 return null; 129 } 130}, jt = /^<dendreo:page_context>[\s\S]*?<\/dendreo:page_context>\n+/, Ne = (e) => e ? e.replace(jt, "") : "", Dt = { class: "ai-hdr" }, Rt = { class: "ai-hdr-avatar" }, Lt = { class: "flex-1 min-w-0" }, Pt = { class: "flex items-center gap-1.5 text-[11px] text-gray-500 mt-0.5" }, Ut = { class: "flex gap-0.5" }, Bt = { key: 0, class: "ai-ctx" }, Ht = ["title"], Ft = { class: "text-gray-700 truncate" }, qt = { class: "text-gray-900" }, Kt = { key: 1, class: "flex items-center gap-2 min-w-0 flex-1" }, Vt = { class: "text-gray-700 truncate font-medium" }, zt = { key: 0, class: "flex items-center justify-center gap-2 text-[11px] text-gray-400 py-2" }, Jt = { key: 1, class: "text-center text-gray-400 italic text-[13px] py-12" }, Gt = { key: 2, class: "ai-empty" }, Wt = { class: "ai-empty-hero" }, Xt = { class: "m-0 text-base font-bold tracking-tight text-gray-900" }, Yt = { key: 0, class: "flex justify-end" }, Zt = { class: "ai-msg-user-wrap" }
130, Qt = { key: 0, class: "ai-msg-attachments" }, ea = { class: "truncate" }, ta = { key: 0, class: "ai-attachment-badge" }, aa = { key: 1, class: "ai-msg-user" }, na = { key: 1, class: "flex gap-2.5 items-start" }, sa = { class: "ai-bot-avatar" }, oa = { class: "flex-1 min-w-0" }, la = { key: 0, class: "text-[10px] text-gray-400 mt-1.5" }, ra = { key: 0 }, ia = { key: 1 }, ua = { key: 4, class: "ai-error" }, da = { class: "ai-foot" }, ca = { key: 0, class: "ai-pending-files" }, va = ["title"], fa = ["src"], pa = { class: "truncate text-[11px]" }, ma = ["onClick"], ya = { class: "ai-input-shell" }, ga = ["disabled"], ha = ["disabled"], xa = ["disabled"], fe = 5, ba = 10 * 1024 * 1024, wa = "PDF, PNG, JPG, WEBP", ge = "dendreo.ai-assistant.conversation-id", je = "new", ka = he({ __name: "AiChat", props: { userFirstName: { default: "" }, flat: { type: Boolean, default: false }, closable: { type: Boolean, default: false } }, emits: ["close"], setup(e, { expose: a, emit: u }) { 131 const h = ["application/pdf", "image/png", "image/jpeg", "image/webp"], C = e, v = u, y = m(null), w = m(null), b = m(Nt()), A = m([]), D = m(null), M = m(false), B = m(false), $ = m(false), s = m(""), c = m([]), P = m(null), F = m(false), X = m(false), Y = m(""), K = m(false), V = m(false), q = m(false), R = m(null), de = m(false), L = W("bodyEl"), H = W("textareaEl"), be = W("fileInputEl"), me = W("messagesSentinelEl"); 132 let ne = null; 133 const E = m([]), Be = G(() => (Y.value.trim() !== "" || E.value.length > 0) && !K.value), He = () => { 134 try { 135 return window.localStorage.getItem(ge); 136 } catch { 137 return null; 138 } 139 }, ce = (n) => { 140 try { 141 n === null ? window.localStorage.removeItem(ge) : window.localStorage.setItem(ge, n); 142 } catch { 143 } 144 }, Fe = G(() => C.userFirstName?.trim() || "bonjour"), we = G(() => c.value.length > 0), qe = G(() => c.value.filter((n) => n.role === "user").length), Ke = G(() => { 145 if (!we.value) return "Nouvelle conversation"; 146 const n = qe.value; 147 return `${n} question${n > 1 ? "s" : ""}`; 148 }), ke = (n) => n.map((t, k) => ({ id: t.id ?? `${t.role}-${k}-${Date.now()}`, role: t.role, content: t.content, blocks: t.blocks, usage: t.usage, attachments: t.attachments ?? null })), se = () => { 149 z(() => { 150 L.value && (L.value.scrollTop = L.value.scrollHeight); 151 }); 152 }, Ce = () => { 153 H.value && (H.value.style.height = "auto"); 154 }, Ve = () => { 155 z(() => { 156 H.value && (H.value.style.height = "auto", H.value.style.height = `${Math.min(H.value.scrollHeight, 110)}px`); 157 }); 158 }, oe = async () => { 159 try { 160 const { data: n } = await Ie(null, s.value || null); 161 A.value = n.data, D.value = n.meta.next_cursor, M.value = n.links.next !== null; 162 } catch { 163 } finally { 164 $.value = false; 165 } 166 }, ze = async () => { 167 if (!(B.value || !M.value || D.value === null)) { 168 B.value = true; 169 try { 170 const { data: n } = await Ie(D.value, s.value || null); 171 A.value = [...A.value, ...n.data], D.value = n.meta.next_cursor, M.value = n.links.next !== null; 172 } catch { 173 } finally { 174 B.value = false; 175 } 176 } 177 }, Je = async (n) => { 178 s.value = n, $.value = true, D.value = null, A.value = [], await oe(); 179 }, ve = (n) => { 180 y.value = n.id, w.value = n.title, c.value = ke(n.messages ?? []), P.value = n.messages_meta?.next_cursor ?? null, F.value = P.value !== null; 181 }, Se = async () => { 182 V.value = true, R.value = null; 183 try { 184 const [{ data: n }] = await Promise.all([Tt(), oe()]); 185 if (n === null) { 186 y.value = null, w.value = null, c.value = [], P.value = null, F.value = false; 187 return; 188 } 189 ve(n), se(); 190 } catch { 191 R.value = "Impossible de charger la conversation."; 192 } finally { 193 V.value = false, de.value = true; 194 } 195 }, Ge = async (n) => { 196 V.value = true, R.value = null; 197 try { 198 const [{ data: t }] = await Promise.all([Te(n), oe()]); 199 ve(t), se(); 200 } catch { 201 ce(null), await Se(); 202 return; 203 } finally { 204 V.value = false, de.value = true; 205 } 206 }, We = async () => { 207 if (X.value || !F.value || P.value === null || y.value === null) return; 208 const n = P.value, t = y.value; 209 X.value = true;
210 const k = L.value?.scrollHeight ?? 0, i = L.value?.scrollTop ?? 0; 211 try { 212 const { data: f } = await It(t, n); 213 if (y.value !== t) return; 214 const p = ke(f.data ?? []); 215 c.value = [...p, ...c.value], P.value = f.meta.next_cursor, F.value = f.links.next !== null, await z(), L.value && (L.value.scrollTop = i + (L.value.scrollHeight - k)); 216 } catch { 217 } finally { 218 X.value = false; 219 } 220 }, ye = () => { 221 ne && (ne.disconnect(), ne = null); 222 }, _e = () => { 223 ye(), !(!me.value || !L.value) && (ne = new IntersectionObserver((n) => { 224 for (const t of n) t.isIntersecting && F.value && !X.value && We(); 225 }, { root: L.value, rootMargin: "120px 0px" }), ne.observe(me.value)); 226 }, Xe = async () => { 227 const n = He(); 228 if (n === je) { 229 y.value = null, w.value = null, c.value = [], de.value = true, await oe(); 230 return; 231 } 232 if (n) { 233 await Ge(n); 234 return; 235 } 236 await Se(); 237 }, $e = () => { 238 y.value = null, w.value = null, c.value = [], P.value = null, F.value = false, Y.value = "", R.value = null, q.value = false, Ce(), ce(je); 239 }, Ye = async (n) => { 240 if (n === y.value) { 241 q.value = false; 242 return; 243 } 244 V.value = true, R.value = null, q.value = false; 245 try { 246 const { data: t } = await Te(n); 247 ve(t), ce(n), se(); 248 } catch { 249 R.value = "Impossible de charger la conversation."; 250 } finally { 251 V.value = false; 252 } 253 }, Ze = () => (D.value = null, M.value = false, oe()), Qe = (n) => h.includes(n.type) ? true : /\.(pdf|png|jpe?g|webp)$/i.test(n.name), et = () => { 254 be.value?.click(); 255 }, tt = (n) => { 256 const t = n.target, k = Array.from(t.files ?? []); 257 if (t.value = "", k.length === 0) return; 258 const i = [], f = []; 259 for (const p of k) { 260 if (E.value.length + i.length >= fe) { 261 f.push(`${p.name} (limite de ${fe} fichiers atteinte)`); 262 continue; 263 } 264 if (!Qe(p)) { 265 f.push(`${p.name} (format non support\xE9)`); 266 continue; 267 } 268 if (p.size > ba) { 269 f.push(`${p.name} (plus de 10 Mo)`); 270 continue; 271 } 272 i.push({ id: `f-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`, file: p, previewUrl: p.type.startsWith("image/") ? URL.createObjectURL(p) : null }); 273 } 274 E.value = [...E.value, ...i], f.length > 0 && (R.value = `Certains fichiers ont \xE9t\xE9 ignor\xE9s : ${f.join(", ")}.`); 275 }, at = (n) => { 276 const t = E.value.find((k) => k.id === n); 277 t?.previewUrl && URL.revokeObjectURL(t.previewUrl), E.value = E.value.filter((k) => k.id !== n); 278 }, nt = () => { 279 E.value.forEach((n) => { 280 n.previewUrl && URL.revokeObjectURL(n.previewUrl); 281 }), E.value = []; 282 }, Ee = async () => { 283 const n = Y.value.trim(), t = E.value.map((p) => p.file); 284 if (!n && t.length === 0 || K.value) return; 285 R.value = null, K.value = true; 286 const k = { id: `u-${Date.now()}`, role: "user", content: n, attachments: E.value.map((p) => ({ name: p.file.name, kind: p.file.type.startsWith("image/") ? "image" : "document", url: p.previewUrl ?? "" })) }, i = `b-${Date.now()}`, f = { id: i, role: "assistant", blocks: null, content: null, pending: true }; 287 c.value = [...c.value, k, f], Y.value = "", nt(), Ce(), se(); 288 try { 289 const { data: p } = await Ot(n, y.value, b.value, t); 290 ce(p.id), ve(p), Ze(), se(); 291 } catch { 292 c.value = c.value.filter((p) => p.id !== i), R.value = "Le message n'a pas pu \xEAtre envoy\xE9. R\xE9essayez."; 293 } finally { 294 K.value = false, z(() => H.value?.focus()); 295 } 296 }, st = (n) => { 297 n.key === "Enter" && !n.shiftKey && (n.preventDefault(), Ee()); 298 }, ot = () => { 299 Ve(); 300 }, Ae = (n) => { 301 n.key === "Escape" && q.value && (q.value = false); 302 }; 303 return Le(() => { 304 Xe(), window.addEventListener("keydown", Ae); 305 }), Pe(() => { 306 window.removeEventListener("keydown", Ae), ye(); 307 }), re(y, () => { 308 z(() => _e()); 309 }), re(F, (n) => { 310 n ? z(() => _e()) : ye(); 311 }), a({ focusComposer: () => { 312 z(() => H.value?.focus()); 313 } }), (n, t) => {
314 const k = Ue("dd-tooltip"); 315 return r(), d("div", { class: ie(["ai-shell", { "ai-shell--flat": e.flat }]) }, [o("header", Dt, [o("div", Rt, [x(g, { name: "fas fa-sparkles", class: "text-white" })]), t[9] || (t[9] = l()), o("div", Lt, [t[5] || (t[5] = o("h2", { class: "m-0 text-sm font-bold tracking-tight text-gray-900" }, ` 316 Assistant Dendreo 317 `, -1)), t[6] || (t[6] = l()), o("div", Pt, [t[4] || (t[4] = o("span", { class: "ai-status-dot" }, null, -1)), l(` 318 Pr\xEAt \xB7 ` + S(Ke.value), 1)])]), t[10] || (t[10] = l()), o("div", Ut, [J((r(), d("button", { type: "button", class: "ai-hdr-action", "aria-label": "Historique", onClick: t[0] || (t[0] = (i) => q.value = true) }, [x(g, { name: "fas fa-clock-rotate-left" })])), [[k, { value: "Historique", class: "ai-tooltip-over-drawer" }]]), t[7] || (t[7] = l()), J((r(), d("button", { type: "button", class: "ai-hdr-action", "aria-label": "Nouvelle conversation", onClick: $e }, [x(g, { name: "fas fa-pen-to-square" })])), [[k, { value: "Nouvelle conversation", class: "ai-tooltip-over-drawer" }]]), t[8] || (t[8] = l()), e.closable ? J((r(), d("button", { key: 0, type: "button", class: "ai-hdr-action", "aria-label": "R\xE9duire l'assistant", onClick: t[1] || (t[1] = (i) => v("close")) }, [x(g, { name: "fas fa-xmark" })])), [[k, { value: "R\xE9duire (\u2318J)", class: "ai-tooltip-over-drawer" }]]) : _("", true)])]), t[38] || (t[38] = l()), b.value || y.value ? (r(), d("div", Bt, [b.value ? (r(), d("div", { key: 0, class: "flex items-center gap-2 min-w-0 flex-1", title: `${b.value.type_label} ${b.value.label}` }, [x(g, { name: "fas fa-location-crosshairs", class: "text-brand-600 text-[11px]" }), t[11] || (t[11] = l()), t[12] || (t[12] = o("span", { class: "text-gray-500 font-medium" }, "Contexte :", -1)), t[13] || (t[13] = l()), o("span", Ft, [l(S(b.value.type_label) + " ", 1), o("strong", qt, S(b.value.label), 1)])], 8, Ht)) : (r(), d("div", Kt, [x(g, { name: "fas fa-message", class: "text-brand-600 text-[11px]" }), t[14] || (t[14] = l()), t[15] || (t[15] = o("span", { class: "text-gray-500 font-medium" }, "Conversation :", -1)), t[16] || (t[16] = l()), o("span", Vt, S(w.value || y.value), 1)]))])) : _("", true), t[39] || (t[39] = l()), o("div", { ref_key: "bodyEl", ref: L, class: "ai-body" }, [F.value || X.value ? (r(), d("div", { key: 0, ref_key: "messagesSentinelEl", ref: me, class: "ai-messages-sentinel", "aria-hidden": "true" }, [X.value ? (r(), d("div", zt, [x(g, { name: "fas fa-spinner-third fa-spin" }), t[17] || (t[17] = l()), t[18] || (t[18] = o("span", null, "Chargement\u2026", -1))])) : _("", true)], 512)) : _("", true), t[29] || (t[29] = l()), V.value ? (r(), d("div", Jt, ` 319 Chargement\u2026 320 `)) : !we.value && de.value ? (r(), d("div", Gt, [o("div", Wt, [x(g, { name: "fas fa-sparkles", class: "text-white text-[22px]" })]), t[19] || (t[19] = l()), o("h3", Xt, ` 321 Bonjour ` + S(Fe.value) + ` \u{1F44B} 322 `, 1), t[20] || (t[20] = l()), t[21] || (t[21] = o("p", { class: "m-0 text-xs text-gray-500 max-w-[280px] leading-relaxed" }, ` 323 Je peux r\xE9pondre \xE0 vos questions sur les apprenants, lancer des actions et vous aider \xE0 pr\xE9parer vos documents Qualiopi. 324 `, -1))])) : (r(true), d(le, { key: 3 }, pe(c.value, (i) => (r(), d(le, { key: i.id }, [i.role === "user" ? (r(), d("div", Yt, [o("div", Zt, [i.attachments && i.attachments.length > 0 ? (r(), d("div", Qt, [(r(true), d(le, null, pe(i.attachments, (f, p) => (r(), Z(rt(f.expired ? "span" : "a"), { key: `${i.id}-att-${p}`, href: f.expired ? void 0 : f.url, target: f.expired ? void 0 : "_blank", rel: f.expired ? void 0 : "noopener", class: ie(["ai-attachment-chip", { "ai-attachment-chip--expired": f.expired }]), title: f.expired ? `${f.name} (fichier expir\xE9)` : f.name }, { default: it(() => [x(g, { name: f.kind === "image" ? "fas fa-image" : "fas fa-file-pdf" }, null, 8, ["name"]), t[22] || (t[22] = l()), o("span", ea, S(f.name), 1), t[23] || (t[23] = l()), f.expired ? (r(), d("span", ta, "Expir\xE9")) : _("", true)]), _: 2 }, 1032, ["href", "target", "rel", "class", "title"]))), 128))])) : _("", true), t[24] || (t[24] = l()), Me(Ne)(i.content) ? (r(), d("div", aa, S(Me(Ne)(i.content)), 1)) : _("", true)])])) : (r(), d("div", na, [o("div", sa, [x(g, { name: "fas fa-sparkles" })]), t[27] || (t[27] = l()), o("div", oa, [x(ft, { blocks: i.blocks, content: i.content, streaming: i.pending }, null, 8, ["blocks", "content", "streaming"]), t[26] || (t[26] = l()), i.usage && !i.pending ? (r(), d("div", la, [l(` 325 in:` + S(i.usage.prompt_tokens ?? 0) + " \xB7 out:" + S(i.usage.completion_tokens ?? 0) + " ", 1), (i.usage.cache_read_input_tokens ?? 0) > 0 ? (r(), d("span", ra, " \xB7 cr:" + S(i.usage.cache_read_input_tokens), 1)) : _("", true), t[25] || (t[25] = l()), (i.usage.cache_write_input_tokens ?? 0) > 0 ? (r(), d("span", ia, " \xB7 cw:" + S(i.usage.cache_write_input_tokens), 1)) : _("", true)])) : _("", true)])]))], 64))), 128)), t[30] || (t[3
3250] = l()), R.value ? (r(), d("div", ua, [x(g, { name: "fas fa-triangle-exclamation", class: "text-red-500" }), t[28] || (t[28] = l()), o("span", null, S(R.value), 1)])) : _("", true)], 512), t[40] || (t[40] = l()), o("footer", da, [E.value.length > 0 ? (r(), d("div", ca, [(r(true), d(le, null, pe(E.value, (i) => (r(), d("div", { key: i.id, class: "ai-pending-file", title: i.file.name }, [i.previewUrl ? (r(), d("img", { key: 0, src: i.previewUrl, class: "ai-pending-thumb", alt: "" }, null, 8, fa)) : (r(), Z(g, { key: 1, name: "fas fa-file-pdf", class: "ai-pending-icon" })), t[31] || (t[31] = l()), o("span", pa, S(i.file.name), 1), t[32] || (t[32] = l()), o("button", { type: "button", class: "ai-pending-remove", "aria-label": "Retirer le fichier", onClick: (f) => at(i.id) }, [x(g, { name: "fas fa-xmark" })], 8, ma)], 8, va))), 128))])) : _("", true), t[36] || (t[36] = l()), o("div", ya, [o("input", { ref_key: "fileInputEl", ref: be, type: "file", multiple: "", accept: ".pdf,.png,.jpg,.jpeg,.webp,application/pdf,image/png,image/jpeg,image/webp", class: "hidden", onChange: tt }, null, 544), t[33] || (t[33] = l()), J((r(), d("button", { type: "button", class: "ai-tool-btn", "aria-label": "Joindre un fichier", disabled: K.value || E.value.length >= fe, onClick: et }, [x(g, { name: "fas fa-paperclip" })], 8, ga)), [[k, { value: `Joindre un fichier (${wa}, max 10 Mo, ${fe} max)`, class: "ai-tooltip-over-drawer" }]]), t[34] || (t[34] = l()), J(o("textarea", { ref_key: "textareaEl", ref: H, "onUpdate:modelValue": t[2] || (t[2] = (i) => Y.value = i), rows: "1", placeholder: "Posez une question, demandez une action\u2026", disabled: K.value, onKeydown: st, onInput: ot }, null, 40, ha), [[Re, Y.value]]), t[35] || (t[35] = l()), o("button", { type: "button", class: "ai-send", disabled: !Be.value, "aria-label": "Envoyer", onClick: Ee }, [K.value ? (r(), Z(g, { key: 1, name: "fas fa-spinner-third fa-spin" })) : (r(), Z(g, { key: 0, name: "fas fa-arrow-up" }))], 8, xa)]), t[37] || (t[37] = ut(' <div class="flex justify-between items-center text-[11px] text-gray-500 mt-1.5 px-1" data-v-f3f8fc7e><span class="inline-flex gap-1.5 items-center" data-v-f3f8fc7e><kbd class="ai-kbd" data-v-f3f8fc7e>\u21B5</kbd> <span data-v-f3f8fc7e>envoyer</span> <span class="text-gray-300" data-v-f3f8fc7e>\xB7</span> <kbd class="ai-kbd" data-v-f3f8fc7e>\u21E7 \u21B5</kbd> <span data-v-f3f8fc7e>nouvelle ligne</span></span></div>', 2))]), t[41] || (t[41] = l()), x(Mt, { open: q.value, entries: A.value, "active-conversation-id": y.value, "has-more": M.value, "loading-more": B.value, searching: $.value, onClose: t[3] || (t[3] = (i) => q.value = false), onSelect: Ye, onNewConversation: $e, onLoadMore: ze, onSearch: Je }, null, 8, ["open", "entries", "active-conversation-id", "has-more", "loading-more", "searching"])], 2); 326 }; 327} }), Ca = xe(ka, [["__scopeId", "data-v-f3f8fc7e"]]), Sa = { class: "ai-overlay" }, _a = { class: "ai-launch-tab-icon" }, $a = ["aria-hidden"], De = "dendreo.ai-assistant.open", Ea = he({ __name: "AiChatDrawer", props: { userFirstName: { default: "" }, width: { default: 420 }, initialOpen: { type: Boolean, default: false } }, setup(e) { 328 const a = e, u = () => { 329 try { 330 return window.localStorage.getItem(De) === "1"; 331 } catch { 332 return false; 333 } 334 }, h = (s) => { 335 try { 336 window.localStorage.setItem(De, s ? "1" : "0"); 337 } catch { 338 } 339 }, C = u() || a.initialOpen, v = m(C), y = m(C), w = W("chatRef"), b = G(() => Math.min(560, Math.max(360, a.width))), A = G(() => ({ width: `${b.value}px` })), D = () => { 340 v.value = true, y.value = true, w.value?.focusComposer(); 341 }, M = () => { 342 v.value = false; 343 }, B = () => { 344 v.value ? M() : D(); 345 }, $ = (s) => { 346 if ((s.metaKey || s.ctrlKey) && s.key.toLowerCase() === "j") { 347 s.preventDefault(), B(); 348 return; 349 } 350 s.key === "Escape" && v.value && M(); 351 }; 352 return re(v, (s) => { 353 h(s), s && w.value?.focusComposer(); 354 }), Le(() => { 355 window.addEventListener("keydown", $); 356 }), Pe(() => { 357 window.removeEventListener("keydown", $); 358 }), (s, c) => { 359 const P = Ue("dd-tooltip"); 360 return r(), Z(vt, { to: "body" }, [o("div", Sa, [J((r(), d("button", { type: "button", class: "ai-launch-tab", "aria-label": "Ouvrir l'Assistant Dendreo", onClick: D }, [o("span", _a, [x(g, { name: "fas fa-sparkles" })]), c[0] || (c[0] = l()), c[1] || (c[1] = o("span", { class: "ai-launch-tab-label" }, "Assistant", -1))])), [[dt, !v.value], [P, "Ouvrir l'Assistant Dendreo (\u2318J)", void 0, { left
360: true }]]), c[2] || (c[2] = l()), o("aside", { class: ie(["ai-drawer", { "is-open": v.value }]), style: ct(A.value), role: "dialog", "aria-label": "Assistant Dendreo", "aria-hidden": !v.value }, [y.value ? (r(), Z(Ca, { key: 0, ref_key: "chatRef", ref: w, flat: "", closable: "", "user-first-name": e.userFirstName, onClose: M }, null, 8, ["user-first-name"])) : _("", true)], 14, $a)])]); 361 }; 362} }), Ia = xe(Ea, [["__scopeId", "data-v-b2c90dab"]]); 363export { 364 Ia as default 365};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.