File size: 4,580 Bytes
582520a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/**
 * ReplyFlow embeddable widget.
 * Usage: <script src="https://YOUR_HOST/widget.js" defer></script>
 */
(function () {
    if (window.__ReplyFlowWidgetLoaded) return;
    window.__ReplyFlowWidgetLoaded = true;

    const script =
        document.currentScript ||
        document.querySelector('script[src*="widget.js"]');
    const base =
        (script && script.src
            ? script.src.replace(/\/widget\.js(?:\?.*)?$/, "")
            : window.location.origin) || "";

    const style = document.createElement("style");
    style.textContent = `
    #rf-launcher{position:fixed;right:20px;bottom:20px;z-index:99999;
      width:58px;height:58px;border-radius:50%;border:0;cursor:pointer;
      background:linear-gradient(135deg,#d4a24c,#e8c078);color:#1a1408;
      font:700 14px/1 Manrope,system-ui,sans-serif;box-shadow:0 12px 30px rgba(0,0,0,.35)}
    #rf-panel{position:fixed;right:20px;bottom:90px;z-index:99999;width:min(360px,calc(100vw - 24px));
      height:min(520px,70vh);display:none;flex-direction:column;overflow:hidden;
      border-radius:18px;background:#16302b;color:#f3f0e8;border:1px solid rgba(243,240,232,.12);
      box-shadow:0 24px 60px rgba(0,0,0,.4);font:14px/1.4 Manrope,system-ui,sans-serif}
    #rf-panel.open{display:flex}
    #rf-head{padding:12px 14px;border-bottom:1px solid rgba(243,240,232,.12)}
    #rf-head strong{font-family:Syne,Manrope,sans-serif;letter-spacing:-.02em}
    #rf-msgs{flex:1;overflow:auto;padding:12px;display:flex;flex-direction:column;gap:8px}
    .rf-b{max-width:88%;padding:8px 10px;border-radius:12px;white-space:pre-wrap}
    .rf-u{align-self:flex-end;background:rgba(212,162,76,.22)}
    .rf-a{align-self:flex-start;background:rgba(255,255,255,.06);border:1px solid rgba(243,240,232,.1)}
    #rf-form{display:flex;gap:6px;padding:10px;border-top:1px solid rgba(243,240,232,.12)}
    #rf-form input{flex:1;border-radius:999px;border:1px solid rgba(243,240,232,.15);
      background:rgba(0,0,0,.25);color:#f3f0e8;padding:10px 12px;font:inherit}
    #rf-form button{border:0;border-radius:999px;padding:10px 12px;cursor:pointer;
      background:linear-gradient(135deg,#d4a24c,#e8c078);color:#1a1408;font-weight:700}
    `;
    document.head.appendChild(style);

    const launcher = document.createElement("button");
    launcher.id = "rf-launcher";
    launcher.type = "button";
    launcher.setAttribute("aria-label", "Open ReplyFlow chat");
    launcher.textContent = "RF";

    const panel = document.createElement("div");
    panel.id = "rf-panel";
    panel.innerHTML = `
      <div id="rf-head">
        <strong>ReplyFlow</strong>
      </div>
      <div id="rf-msgs"></div>
      <form id="rf-form">
        <input id="rf-input" placeholder="Message…" autocomplete="off" />
        <button type="submit">Send</button>
      </form>
    `;

    document.body.appendChild(panel);
    document.body.appendChild(launcher);

    const msgs = panel.querySelector("#rf-msgs");
    const form = panel.querySelector("#rf-form");
    const input = panel.querySelector("#rf-input");
    let conversationId = null;
    let started = false;

    function bubble(role, text) {
        const el = document.createElement("div");
        el.className = `rf-b ${role === "user" ? "rf-u" : "rf-a"}`;
        el.textContent = text;
        msgs.appendChild(el);
        msgs.scrollTop = msgs.scrollHeight;
    }

    async function start() {
        msgs.innerHTML = "";
        const res = await fetch(
            `${base}/api/chat/start?ui_language=en`,
            { method: "POST" }
        );
        const data = await res.json();
        conversationId = data.conversation_id;
        bubble("assistant", data.greeting);
        started = true;
    }

    launcher.addEventListener("click", async () => {
        panel.classList.toggle("open");
        if (panel.classList.contains("open") && !started) {
            await start();
        }
    });

    form.addEventListener("submit", async (e) => {
        e.preventDefault();
        const text = input.value.trim();
        if (!text || !conversationId) return;
        input.value = "";
        bubble("user", text);
        const res = await fetch(`${base}/api/chat/message`, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({
                conversation_id: conversationId,
                message: text,
                ui_language: "en",
            }),
        });
        const data = await res.json();
        bubble("assistant", data.reply || "…");
    });
})();