kvashist commited on
Commit
c613ef8
·
verified ·
1 Parent(s): 72e8ca8

Create app.js

Browse files
Files changed (1) hide show
  1. app.js +121 -0
app.js ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const CHAT_URL = "/api/chat";
2
+ const MODEL = "llama3:latest";
3
+
4
+ const BASE_SYSTEM = "You are helpful.";
5
+
6
+ const chatEl = document.getElementById("chat");
7
+ const formEl = document.getElementById("form");
8
+ const promptEl = document.getElementById("prompt");
9
+ const searchBtn = document.getElementById("searchBtn");
10
+ const sendBtn = document.getElementById("sendBtn");
11
+
12
+ const statusDot = document.getElementById("statusDot");
13
+ const statusText = document.getElementById("statusText");
14
+ const heroEl = document.getElementById("hero");
15
+
16
+ let searchMode = false;
17
+
18
+ function addMessage(role, text, { highlight = false } = {}) {
19
+ const msg = document.createElement("div");
20
+ msg.className = `msg ${role}${highlight ? " highlight" : ""}`;
21
+
22
+ const bubble = document.createElement("div");
23
+ bubble.className = "bubble";
24
+ bubble.textContent = text;
25
+
26
+ msg.appendChild(bubble);
27
+ chatEl.appendChild(msg);
28
+ chatEl.scrollTop = chatEl.scrollHeight;
29
+
30
+ if (heroEl) heroEl.style.display = "none";
31
+ return msg;
32
+ }
33
+
34
+ function setConnected(ok) {
35
+ statusDot.classList.toggle("on", ok);
36
+ statusText.textContent = ok ? "Connected" : "Disconnected";
37
+ }
38
+
39
+ async function pingProxy() {
40
+ try {
41
+ const r = await fetch("/health");
42
+ setConnected(r.ok);
43
+ } catch {
44
+ setConnected(false);
45
+ }
46
+ }
47
+
48
+ function autosizeTextarea(el) {
49
+ el.style.height = "auto";
50
+ el.style.height = Math.min(el.scrollHeight, 180) + "px";
51
+ }
52
+
53
+ promptEl.addEventListener("input", () => autosizeTextarea(promptEl));
54
+
55
+ promptEl.addEventListener("keydown", (e) => {
56
+ if (e.key === "Enter" && !e.shiftKey) {
57
+ e.preventDefault();
58
+ formEl.requestSubmit();
59
+ }
60
+ });
61
+
62
+ searchBtn.addEventListener("click", () => {
63
+ searchMode = !searchMode;
64
+ searchBtn.setAttribute("aria-pressed", String(searchMode));
65
+ });
66
+
67
+ formEl.addEventListener("submit", async (e) => {
68
+ e.preventDefault();
69
+
70
+ const userText = promptEl.value.trim();
71
+ if (!userText) return;
72
+
73
+ addMessage("user", userText);
74
+
75
+ promptEl.value = "";
76
+ autosizeTextarea(promptEl);
77
+
78
+ sendBtn.disabled = true;
79
+ const placeholder = addMessage("ai", "Thinking…");
80
+
81
+ try {
82
+ const body = {
83
+ model: MODEL,
84
+ messages: [
85
+ { role: "system", content: BASE_SYSTEM },
86
+ { role: "user", content: userText }
87
+ ],
88
+ stream: false
89
+ };
90
+
91
+ const resp = await fetch(CHAT_URL, {
92
+ method: "POST",
93
+ headers: { "Content-Type": "application/json" },
94
+ body: JSON.stringify(body)
95
+ });
96
+
97
+ if (!resp.ok) {
98
+ const err = await resp.text();
99
+ placeholder.querySelector(".bubble").textContent = `Error (${resp.status}): ${err}`;
100
+ placeholder.classList.add("highlight");
101
+ return;
102
+ }
103
+
104
+ const data = await resp.json();
105
+ const answer = data?.message?.content ?? "(No content)";
106
+ placeholder.querySelector(".bubble").textContent = answer;
107
+
108
+ placeholder.classList.add("highlight");
109
+ setTimeout(() => placeholder.classList.remove("highlight"), 1200);
110
+ } catch (err) {
111
+ placeholder.querySelector(".bubble").textContent = `Request failed: ${err?.message ?? String(err)}`;
112
+ placeholder.classList.add("highlight");
113
+ } finally {
114
+ sendBtn.disabled = false;
115
+ promptEl.focus();
116
+ await pingProxy();
117
+ }
118
+ });
119
+
120
+ pingProxy();
121
+ setInterval(pingProxy, 5000);