mothy-08 commited on
Commit
ef8a3e3
·
1 Parent(s): 72a2ceb

UI changes

Browse files
chrome-extension/manifest.json CHANGED
@@ -20,7 +20,7 @@
20
  "48": "icons/icon48.png",
21
  "128": "icons/icon128.png"
22
  },
23
- "permissions": ["activeTab", "scripting", "sidePanel"],
24
  "host_permissions": [
25
  "http://localhost:8000/*",
26
  "https://mothy-08-ragbot.hf.space/*",
 
20
  "48": "icons/icon48.png",
21
  "128": "icons/icon128.png"
22
  },
23
+ "permissions": ["activeTab", "scripting", "sidePanel", "storage"],
24
  "host_permissions": [
25
  "http://localhost:8000/*",
26
  "https://mothy-08-ragbot.hf.space/*",
chrome-extension/sidepanel.css CHANGED
@@ -1,4 +1,4 @@
1
- /* RESET & BASE */
2
  :root {
3
  --bg-color: #ffffff;
4
  --text-primary: #111111;
@@ -8,17 +8,19 @@
8
  --border-color: #e5e5e5;
9
  --msg-bot-bg: #f4f4f5;
10
  --msg-user-bg: #111111;
 
11
  }
12
 
13
  * {
14
  box-sizing: border-box;
 
 
 
15
  }
16
 
17
  body {
18
- margin: 0;
19
- padding: 0;
20
  width: 100%;
21
- height: 100vh; /* Critical for Side Panel: Fills full vertical height */
22
  font-family:
23
  -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
24
  Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
@@ -26,18 +28,35 @@ body {
26
  color: var(--text-primary);
27
  display: flex;
28
  flex-direction: column;
29
- overflow: hidden; /* Prevents double scrollbars */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  }
31
 
32
- /* HEADER */
33
  header {
34
- padding: 1rem;
 
35
  display: flex;
36
  align-items: center;
37
  justify-content: space-between;
38
  backdrop-filter: blur(10px);
39
  z-index: 10;
40
- flex-shrink: 0; /* Prevents header from shrinking when chat gets full */
 
41
  }
42
 
43
  h1 {
@@ -48,6 +67,12 @@ h1 {
48
  letter-spacing: 0.05em;
49
  }
50
 
 
 
 
 
 
 
51
  .badge {
52
  font-size: 0.7rem;
53
  padding: 4px 8px;
@@ -57,9 +82,57 @@ h1 {
57
  font-family: monospace;
58
  }
59
 
60
- /* MAIN CONTAINER */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  main {
62
- flex: 1; /* Takes remaining height */
63
  position: relative;
64
  display: flex;
65
  flex-direction: column;
@@ -73,37 +146,15 @@ main {
73
  flex-direction: column;
74
  align-items: center;
75
  justify-content: center;
76
- animation: fadeIn 0.2s ease-out;
77
  }
78
 
79
  .hidden {
80
  display: none !important;
81
  }
82
 
83
- /* STATES & ICONS */
84
- .icon-large {
85
- font-size: 3rem;
86
- margin-bottom: 1rem;
87
- opacity: 0.8;
88
- }
89
-
90
- h2 {
91
- font-size: 1.1rem;
92
- font-weight: 600;
93
- margin: 0 0 0.5rem 0;
94
- }
95
-
96
- p {
97
- font-size: 0.9rem;
98
- color: var(--text-secondary);
99
- text-align: center;
100
- line-height: 1.5;
101
- margin-bottom: 1.5rem;
102
- max-width: 280px;
103
- }
104
-
105
- /* BUTTONS */
106
- button {
107
  background: var(--accent-color);
108
  color: var(--accent-text);
109
  border: 1px solid transparent;
@@ -117,13 +168,13 @@ button {
117
  max-width: 200px;
118
  }
119
 
120
- button:hover {
121
  background: #333;
122
  transform: translateY(-1px);
123
  }
124
 
125
  button:active {
126
- transform: translateY(0);
127
  }
128
 
129
  button:disabled {
@@ -132,16 +183,16 @@ button:disabled {
132
  transform: none;
133
  }
134
 
135
- /* CHAT INTERFACE */
136
  #chat-view {
137
  padding: 0;
138
- justify-content: flex-end; /* Anchors content to bottom initially */
139
  }
140
 
141
  #messages {
142
- flex: 1; /* Grows to fill space */
143
  width: 100%;
144
- overflow-y: auto; /* Scrollable area */
145
  padding: 1rem;
146
  display: flex;
147
  flex-direction: column;
@@ -152,11 +203,14 @@ button:disabled {
152
  .msg {
153
  max-width: 90%;
154
  padding: 10px 14px;
155
- border-radius: 8px;
156
  font-size: 0.9rem;
157
  line-height: 1.5;
158
  position: relative;
159
  word-wrap: break-word;
 
 
 
160
  }
161
 
162
  .msg.user {
@@ -164,6 +218,7 @@ button:disabled {
164
  background: var(--msg-user-bg);
165
  color: white;
166
  border-bottom-right-radius: 2px;
 
167
  }
168
 
169
  .msg.bot {
@@ -171,52 +226,128 @@ button:disabled {
171
  background: var(--msg-bot-bg);
172
  color: var(--text-primary);
173
  border-bottom-left-radius: 2px;
 
174
  }
175
 
176
  .error {
177
- color: red;
 
 
 
 
 
178
  }
179
 
180
- /* INPUT AREA */
181
  .input-area {
182
  width: 100%;
183
- padding: 1rem;
184
  backdrop-filter: blur(10px);
 
185
  display: flex;
186
  gap: 8px;
187
- flex-shrink: 0; /* Prevents squashing */
188
  }
189
 
190
  input {
191
  flex: 1;
192
- padding: 10px 12px;
193
  border: 1px solid var(--border-color);
194
- border-radius: 14px;
195
  font-size: 0.9rem;
196
  outline: none;
197
  transition:
198
  border-color 0.2s,
199
- background 0.2s;
200
  }
201
 
202
  input:focus {
203
  border-color: var(--accent-color);
204
- background: #fff;
205
  }
206
 
207
  .send-btn {
208
  background: none;
 
209
  color: var(--text-primary);
210
  width: 40px;
211
  padding: 0;
212
  display: flex;
213
  align-items: center;
214
  justify-content: center;
215
- font-size: 0.75rem;
216
- border-radius: 6px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  }
218
 
219
- /* LOADER */
220
  .loader {
221
  border: 2px solid #f3f3f3;
222
  border-top: 2px solid var(--accent-color);
@@ -238,13 +369,24 @@ input:focus {
238
  }
239
  }
240
 
241
- @keyframes fadeIn {
242
- from {
243
- opacity: 0;
244
- transform: translateY(5px);
245
- }
246
- to {
247
- opacity: 1;
248
- transform: translateY(0);
249
- }
 
 
 
 
 
 
 
 
 
 
 
250
  }
 
1
+ /* --- VARIABLES & THEME --- */
2
  :root {
3
  --bg-color: #ffffff;
4
  --text-primary: #111111;
 
8
  --border-color: #e5e5e5;
9
  --msg-bot-bg: #f4f4f5;
10
  --msg-user-bg: #111111;
11
+ --transition-speed: 0.2s;
12
  }
13
 
14
  * {
15
  box-sizing: border-box;
16
+ margin: 0;
17
+ padding: 0;
18
+ font: inherit;
19
  }
20
 
21
  body {
 
 
22
  width: 100%;
23
+ height: 100vh;
24
  font-family:
25
  -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
26
  Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
 
28
  color: var(--text-primary);
29
  display: flex;
30
  flex-direction: column;
31
+ overflow: hidden;
32
+ }
33
+
34
+ /* --- SCROLLBARS --- */
35
+ #messages::-webkit-scrollbar {
36
+ width: 6px;
37
+ }
38
+ #messages::-webkit-scrollbar-track {
39
+ background: transparent;
40
+ }
41
+ #messages::-webkit-scrollbar-thumb {
42
+ background-color: rgba(0, 0, 0, 0.1);
43
+ border-radius: 10px;
44
+ }
45
+ #messages:hover::-webkit-scrollbar-thumb {
46
+ background-color: rgba(0, 0, 0, 0.2);
47
  }
48
 
49
+ /* --- HEADER --- */
50
  header {
51
+ min-height: 60px;
52
+ padding-inline: 1rem;
53
  display: flex;
54
  align-items: center;
55
  justify-content: space-between;
56
  backdrop-filter: blur(10px);
57
  z-index: 10;
58
+ flex-shrink: 0;
59
+ border-bottom: 1px solid transparent;
60
  }
61
 
62
  h1 {
 
67
  letter-spacing: 0.05em;
68
  }
69
 
70
+ .header-controls {
71
+ display: flex;
72
+ align-items: center;
73
+ gap: 8px;
74
+ }
75
+
76
  .badge {
77
  font-size: 0.7rem;
78
  padding: 4px 8px;
 
82
  font-family: monospace;
83
  }
84
 
85
+ /* --- ICON BUTTONS & TOOLTIP --- */
86
+ .icon-btn {
87
+ position: relative; /* For Tooltip positioning */
88
+ background: transparent;
89
+ color: var(--text-secondary);
90
+ width: auto;
91
+ max-width: none;
92
+ padding: 4px;
93
+ border: none;
94
+ border-radius: 50%;
95
+ display: flex;
96
+ align-items: center;
97
+ justify-content: center;
98
+ cursor: pointer;
99
+ transition:
100
+ color 0.2s ease,
101
+ background-color 0.2s ease;
102
+ }
103
+
104
+ .icon-btn:hover {
105
+ background-color: rgba(0, 0, 0, 0.05);
106
+ color: #d32f2f;
107
+ transform: none;
108
+ }
109
+
110
+ /* CUSTOM POPUP MODAL (Tooltip) */
111
+ .icon-btn:hover::after {
112
+ content: attr(data-tooltip);
113
+ position: absolute;
114
+ top: 100%;
115
+ right: 0;
116
+ margin-top: 10px;
117
+ background: var(--msg-bot-bg);
118
+ color: var(--text-primary);
119
+ padding: 6px 10px;
120
+ border-radius: 6px;
121
+ font-size: 0.75rem;
122
+ font-weight: 500;
123
+ font-family:
124
+ -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
125
+ Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
126
+ white-space: nowrap;
127
+ pointer-events: none !important;
128
+ z-index: 100;
129
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
130
+ animation: fadeIn 0.2s ease-out;
131
+ }
132
+
133
+ /* --- MAIN VIEWS --- */
134
  main {
135
+ flex: 1;
136
  position: relative;
137
  display: flex;
138
  flex-direction: column;
 
146
  flex-direction: column;
147
  align-items: center;
148
  justify-content: center;
149
+ animation: fadeIn 0.3s ease-out;
150
  }
151
 
152
  .hidden {
153
  display: none !important;
154
  }
155
 
156
+ /* --- BUTTONS --- */
157
+ button:not(.icon-btn):not(.send-btn) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  background: var(--accent-color);
159
  color: var(--accent-text);
160
  border: 1px solid transparent;
 
168
  max-width: 200px;
169
  }
170
 
171
+ button:not(.icon-btn):not(.send-btn):hover {
172
  background: #333;
173
  transform: translateY(-1px);
174
  }
175
 
176
  button:active {
177
+ transform: scale(0.96) !important;
178
  }
179
 
180
  button:disabled {
 
183
  transform: none;
184
  }
185
 
186
+ /* --- CHAT AREA --- */
187
  #chat-view {
188
  padding: 0;
189
+ justify-content: flex-end;
190
  }
191
 
192
  #messages {
193
+ flex: 1;
194
  width: 100%;
195
+ overflow-y: auto;
196
  padding: 1rem;
197
  display: flex;
198
  flex-direction: column;
 
203
  .msg {
204
  max-width: 90%;
205
  padding: 10px 14px;
206
+ border-radius: 12px;
207
  font-size: 0.9rem;
208
  line-height: 1.5;
209
  position: relative;
210
  word-wrap: break-word;
211
+ opacity: 0;
212
+ animation: popIn 0.3s cubic-bezier(0.25, 0.8, 0.25, 1) forwards;
213
+ will-change: transform, opacity;
214
  }
215
 
216
  .msg.user {
 
218
  background: var(--msg-user-bg);
219
  color: white;
220
  border-bottom-right-radius: 2px;
221
+ transform-origin: bottom right;
222
  }
223
 
224
  .msg.bot {
 
226
  background: var(--msg-bot-bg);
227
  color: var(--text-primary);
228
  border-bottom-left-radius: 2px;
229
+ transform-origin: bottom left;
230
  }
231
 
232
  .error {
233
+ color: #d32f2f;
234
+ background: #ffebee;
235
+ border: 1px solid #ffcdd2;
236
+ padding: 1rem;
237
+ border-radius: 8px;
238
+ text-align: center;
239
  }
240
 
241
+ /* --- INPUT AREA --- */
242
  .input-area {
243
  width: 100%;
244
+ padding: 12px;
245
  backdrop-filter: blur(10px);
246
+ background: rgba(255, 255, 255, 0.8);
247
  display: flex;
248
  gap: 8px;
249
+ flex-shrink: 0;
250
  }
251
 
252
  input {
253
  flex: 1;
254
+ padding: 10px 14px;
255
  border: 1px solid var(--border-color);
256
+ border-radius: 20px;
257
  font-size: 0.9rem;
258
  outline: none;
259
  transition:
260
  border-color 0.2s,
261
+ box-shadow 0.2s;
262
  }
263
 
264
  input:focus {
265
  border-color: var(--accent-color);
266
+ box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.05);
267
  }
268
 
269
  .send-btn {
270
  background: none;
271
+ border: none;
272
  color: var(--text-primary);
273
  width: 40px;
274
  padding: 0;
275
  display: flex;
276
  align-items: center;
277
  justify-content: center;
278
+ border-radius: 50%;
279
+ cursor: pointer;
280
+ transition:
281
+ transform 0.1s ease,
282
+ background-color 0.2s ease;
283
+ }
284
+
285
+ .send-btn:hover {
286
+ background-color: rgba(0, 0, 0, 0.05);
287
+ transform: none;
288
+ }
289
+
290
+ .send-btn:active {
291
+ transform: scale(0.9);
292
+ }
293
+
294
+ /* --- ANIMATIONS --- */
295
+ @keyframes fadeIn {
296
+ from {
297
+ opacity: 0;
298
+ transform: translateY(5px);
299
+ }
300
+ to {
301
+ opacity: 1;
302
+ transform: translateY(0);
303
+ }
304
+ }
305
+
306
+ @keyframes popIn {
307
+ 0% {
308
+ opacity: 0;
309
+ transform: translateY(10px) scale(0.98);
310
+ }
311
+ 100% {
312
+ opacity: 1;
313
+ transform: translateY(0) scale(1);
314
+ }
315
+ }
316
+
317
+ .msg.loading {
318
+ display: flex;
319
+ align-items: center;
320
+ gap: 4px;
321
+ width: fit-content;
322
+ padding: 12px 16px;
323
+ }
324
+
325
+ .dot {
326
+ width: 6px;
327
+ height: 6px;
328
+ background-color: #999;
329
+ border-radius: 50%;
330
+ animation: bounce 1.4s infinite ease-in-out both;
331
+ }
332
+
333
+ .dot:nth-child(1) {
334
+ animation-delay: -0.32s;
335
+ }
336
+ .dot:nth-child(2) {
337
+ animation-delay: -0.16s;
338
+ }
339
+
340
+ @keyframes bounce {
341
+ 0%,
342
+ 80%,
343
+ 100% {
344
+ transform: scale(0);
345
+ }
346
+ 40% {
347
+ transform: scale(1);
348
+ }
349
  }
350
 
 
351
  .loader {
352
  border: 2px solid #f3f3f3;
353
  border-top: 2px solid var(--accent-color);
 
369
  }
370
  }
371
 
372
+ .icon-large {
373
+ font-size: 3rem;
374
+ margin-bottom: 1rem;
375
+ opacity: 0.8;
376
+ animation: float 3s ease-in-out infinite;
377
+ }
378
+
379
+ h2 {
380
+ font-size: 1.1rem;
381
+ font-weight: 600;
382
+ margin: 0 0 0.5rem 0;
383
+ }
384
+
385
+ p {
386
+ font-size: 0.9rem;
387
+ color: var(--text-secondary);
388
+ text-align: center;
389
+ line-height: 1.5;
390
+ margin-bottom: 1.5rem;
391
+ max-width: 280px;
392
  }
chrome-extension/sidepanel.html CHANGED
@@ -7,14 +7,23 @@
7
  <link rel="stylesheet" href="sidepanel.css" />
8
  <link
9
  rel="stylesheet"
10
- href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&icon_names=send"
11
  />
12
  <script src="sidepanel.js" defer></script>
13
  </head>
14
  <body>
15
  <header>
16
  <h1>Ragbot</h1>
17
- <span id="domain-badge" class="badge">Checking...</span>
 
 
 
 
 
 
 
 
 
18
  </header>
19
 
20
  <main>
 
7
  <link rel="stylesheet" href="sidepanel.css" />
8
  <link
9
  rel="stylesheet"
10
+ href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&icon_names=delete,send"
11
  />
12
  <script src="sidepanel.js" defer></script>
13
  </head>
14
  <body>
15
  <header>
16
  <h1>Ragbot</h1>
17
+ <div class="header-controls">
18
+ <span id="domain-badge" class="badge">Checking...</span>
19
+ <button
20
+ id="delete-btn"
21
+ class="icon-btn material-symbols-outlined hidden"
22
+ data-tooltip="Clear Chat History"
23
+ >
24
+ delete
25
+ </button>
26
+ </div>
27
  </header>
28
 
29
  <main>
chrome-extension/sidepanel.js CHANGED
@@ -14,6 +14,7 @@ const els = {
14
  messages: document.getElementById("messages"),
15
  input: document.getElementById("user-input"),
16
  sendBtn: document.getElementById("send-btn"),
 
17
  };
18
 
19
  // State
@@ -21,17 +22,14 @@ let currentUrl = "";
21
 
22
  // --- Initialization ---
23
 
24
- // Side Panels stay open, so we need to listen for tab changes
25
  document.addEventListener("DOMContentLoaded", () => {
26
- updateContext(); // Run once on load
27
  });
28
 
29
- // Listen for when user switches tabs
30
  chrome.tabs.onActivated.addListener(() => {
31
  updateContext();
32
  });
33
 
34
- // Listen for when user navigates within a tab
35
  chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
36
  if (changeInfo.status === "complete" && tab.active) {
37
  updateContext();
@@ -44,28 +42,25 @@ async function updateContext() {
44
  try {
45
  const tab = await getCurrentTab();
46
 
47
- // 1. Safety Check: If no URL or not a web page, stop here
48
  if (!tab.url || !tab.url.startsWith("http")) {
49
  currentUrl = "";
50
  els.domainBadge.textContent = "Restricted";
51
- showView("loading"); // Or a specific "error" view
52
  document.querySelector("#loading-view p").textContent =
53
  "Please open a valid website.";
54
  return;
55
  }
56
 
57
- // 2. Only reload if URL actually changed
58
  if (tab.url === currentUrl) return;
59
 
60
  currentUrl = tab.url;
61
-
62
- // 3. Update Badge
63
  const urlObj = new URL(currentUrl);
64
  els.domainBadge.textContent = urlObj.hostname;
65
 
66
- // 4. Reset UI & Check Backend
67
- els.messages.innerHTML =
68
- '<div class="msg bot">Hello! Ask me anything about this page.</div>';
 
69
  await checkIndexStatus(currentUrl);
70
  } catch (err) {
71
  console.error(err);
@@ -74,9 +69,13 @@ async function updateContext() {
74
  }
75
 
76
  async function checkIndexStatus(url) {
77
- showView("loading");
78
- document.querySelector("#loading-view p").textContent =
79
- "Connecting to Brain...";
 
 
 
 
80
 
81
  try {
82
  const res = await fetch(`${API_BASE}/check`, {
@@ -93,7 +92,9 @@ async function checkIndexStatus(url) {
93
  showView("train");
94
  }
95
  } catch (err) {
96
- showError("Backend Offline. Is server.py running?");
 
 
97
  }
98
  }
99
 
@@ -110,14 +111,16 @@ async function startTraining() {
110
 
111
  await res.json();
112
 
113
- // Fake delay for UX
 
 
114
  setTimeout(() => {
115
  showView("chat");
116
  addMessage(
117
  "bot",
118
  "I've started reading this site! You can ask me questions now.",
119
  );
120
- }, 2000);
121
  } catch (err) {
122
  els.trainBtn.disabled = false;
123
  els.trainStatus.classList.add("hidden");
@@ -130,10 +133,13 @@ async function sendMessage() {
130
  if (!text) return;
131
 
132
  addMessage("user", text);
 
133
  els.input.value = "";
134
  els.input.disabled = true;
135
  els.sendBtn.disabled = true;
136
 
 
 
137
  try {
138
  const res = await fetch(`${API_BASE}/chat`, {
139
  method: "POST",
@@ -147,8 +153,11 @@ async function sendMessage() {
147
  if (!res.ok) throw new Error("API Error");
148
 
149
  const data = await res.json();
 
 
150
  addMessage("bot", data.answer);
151
  } catch (err) {
 
152
  addMessage("error", "Failed to get response. Try again.");
153
  } finally {
154
  els.input.disabled = false;
@@ -157,13 +166,74 @@ async function sendMessage() {
157
  }
158
  }
159
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  // --- Utilities ---
161
 
162
  function getCurrentTab() {
163
  return new Promise((resolve) => {
164
  chrome.tabs.query({ active: true, lastFocusedWindow: true }, (tabs) => {
165
  if (tabs && tabs[0]) resolve(tabs[0]);
166
- else resolve({}); // Return empty object instead of crashing
167
  });
168
  });
169
  }
@@ -171,6 +241,13 @@ function getCurrentTab() {
171
  function showView(name) {
172
  Object.values(views).forEach((el) => el.classList.add("hidden"));
173
  views[name].classList.remove("hidden");
 
 
 
 
 
 
 
174
  }
175
 
176
  function addMessage(type, text) {
@@ -178,19 +255,44 @@ function addMessage(type, text) {
178
  div.className = `msg ${type}`;
179
  div.innerText = text;
180
  els.messages.appendChild(div);
181
- els.messages.scrollTop = els.messages.scrollHeight;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
  }
183
 
184
  function showError(msg) {
185
- // Overwrite main content for critical errors
186
- document.querySelector("main").innerHTML =
187
- `<div class="error view">${msg}</div>`;
 
 
188
  }
189
 
190
  // --- Event Listeners ---
191
 
192
  els.trainBtn.addEventListener("click", startTraining);
193
  els.sendBtn.addEventListener("click", sendMessage);
 
194
  els.input.addEventListener("keypress", (e) => {
195
  if (e.key === "Enter") sendMessage();
196
  });
 
14
  messages: document.getElementById("messages"),
15
  input: document.getElementById("user-input"),
16
  sendBtn: document.getElementById("send-btn"),
17
+ deleteBtn: document.getElementById("delete-btn"),
18
  };
19
 
20
  // State
 
22
 
23
  // --- Initialization ---
24
 
 
25
  document.addEventListener("DOMContentLoaded", () => {
26
+ updateContext();
27
  });
28
 
 
29
  chrome.tabs.onActivated.addListener(() => {
30
  updateContext();
31
  });
32
 
 
33
  chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
34
  if (changeInfo.status === "complete" && tab.active) {
35
  updateContext();
 
42
  try {
43
  const tab = await getCurrentTab();
44
 
 
45
  if (!tab.url || !tab.url.startsWith("http")) {
46
  currentUrl = "";
47
  els.domainBadge.textContent = "Restricted";
48
+ showView("loading");
49
  document.querySelector("#loading-view p").textContent =
50
  "Please open a valid website.";
51
  return;
52
  }
53
 
 
54
  if (tab.url === currentUrl) return;
55
 
56
  currentUrl = tab.url;
 
 
57
  const urlObj = new URL(currentUrl);
58
  els.domainBadge.textContent = urlObj.hostname;
59
 
60
+ // Load history OR Default message
61
+ await loadChatHistory(currentUrl);
62
+
63
+ // Now check if the backend is ready
64
  await checkIndexStatus(currentUrl);
65
  } catch (err) {
66
  console.error(err);
 
69
  }
70
 
71
  async function checkIndexStatus(url) {
72
+ const hasHistory = els.messages.childElementCount > 1;
73
+
74
+ if (!hasHistory) {
75
+ showView("loading");
76
+ document.querySelector("#loading-view p").textContent =
77
+ "Connecting to Brain...";
78
+ }
79
 
80
  try {
81
  const res = await fetch(`${API_BASE}/check`, {
 
92
  showView("train");
93
  }
94
  } catch (err) {
95
+ if (!hasHistory) {
96
+ showError("Backend Offline. Is the Space running?");
97
+ }
98
  }
99
  }
100
 
 
111
 
112
  await res.json();
113
 
114
+ // UX: Clear the "Hello" message so we don't have double messages
115
+ await clearHistory(false); // false = do NOT reload default message
116
+
117
  setTimeout(() => {
118
  showView("chat");
119
  addMessage(
120
  "bot",
121
  "I've started reading this site! You can ask me questions now.",
122
  );
123
+ }, 1500);
124
  } catch (err) {
125
  els.trainBtn.disabled = false;
126
  els.trainStatus.classList.add("hidden");
 
133
  if (!text) return;
134
 
135
  addMessage("user", text);
136
+
137
  els.input.value = "";
138
  els.input.disabled = true;
139
  els.sendBtn.disabled = true;
140
 
141
+ const loadingBubble = addLoadingBubble();
142
+
143
  try {
144
  const res = await fetch(`${API_BASE}/chat`, {
145
  method: "POST",
 
153
  if (!res.ok) throw new Error("API Error");
154
 
155
  const data = await res.json();
156
+
157
+ loadingBubble.remove();
158
  addMessage("bot", data.answer);
159
  } catch (err) {
160
+ loadingBubble.remove();
161
  addMessage("error", "Failed to get response. Try again.");
162
  } finally {
163
  els.input.disabled = false;
 
166
  }
167
  }
168
 
169
+ // --- Storage & History Logic ---
170
+
171
+ async function loadChatHistory(url) {
172
+ els.messages.innerHTML = ""; // Clear current view
173
+
174
+ const key = `chat_${url}`;
175
+ const result = await chrome.storage.local.get(key);
176
+ const history = result[key] || [];
177
+
178
+ if (history.length === 0) {
179
+ const text = "Hello! Ask me anything about this page.";
180
+ // 1. Show it
181
+ const div = document.createElement("div");
182
+ div.className = "msg bot";
183
+ div.innerText = text;
184
+ els.messages.appendChild(div);
185
+
186
+ // 2. Save it
187
+ saveMessageToStorage(url, "bot", text);
188
+ } else {
189
+ // Render saved history
190
+ history.forEach((msg) => {
191
+ const div = document.createElement("div");
192
+ div.className = `msg ${msg.type}`;
193
+ div.innerText = msg.text;
194
+ els.messages.appendChild(div);
195
+ });
196
+ scrollToBottom();
197
+ }
198
+ }
199
+
200
+ async function saveMessageToStorage(url, type, text) {
201
+ if (type === "error") return;
202
+
203
+ const key = `chat_${url}`;
204
+ const result = await chrome.storage.local.get(key);
205
+ const history = result[key] || [];
206
+
207
+ history.push({ type, text, timestamp: Date.now() });
208
+
209
+ if (history.length > 50) history.shift();
210
+
211
+ await chrome.storage.local.set({ [key]: history });
212
+ }
213
+
214
+ // Added 'reloadDefault' param to control behavior
215
+ async function clearHistory(reloadDefault = true) {
216
+ if (!currentUrl) return;
217
+ const key = `chat_${currentUrl}`;
218
+
219
+ // Wipe from storage
220
+ await chrome.storage.local.remove(key);
221
+
222
+ // Wipe visual
223
+ els.messages.innerHTML = "";
224
+
225
+ if (reloadDefault) {
226
+ await loadChatHistory(currentUrl);
227
+ }
228
+ }
229
+
230
  // --- Utilities ---
231
 
232
  function getCurrentTab() {
233
  return new Promise((resolve) => {
234
  chrome.tabs.query({ active: true, lastFocusedWindow: true }, (tabs) => {
235
  if (tabs && tabs[0]) resolve(tabs[0]);
236
+ else resolve({});
237
  });
238
  });
239
  }
 
241
  function showView(name) {
242
  Object.values(views).forEach((el) => el.classList.add("hidden"));
243
  views[name].classList.remove("hidden");
244
+
245
+ // UX Fix: Only show Delete Button in Chat View
246
+ if (name === "chat") {
247
+ els.deleteBtn.classList.remove("hidden");
248
+ } else {
249
+ els.deleteBtn.classList.add("hidden");
250
+ }
251
  }
252
 
253
  function addMessage(type, text) {
 
255
  div.className = `msg ${type}`;
256
  div.innerText = text;
257
  els.messages.appendChild(div);
258
+ scrollToBottom();
259
+
260
+ saveMessageToStorage(currentUrl, type, text);
261
+ }
262
+
263
+ function addLoadingBubble() {
264
+ const div = document.createElement("div");
265
+ div.className = "msg bot loading";
266
+ div.innerHTML = `
267
+ <div class="dot"></div>
268
+ <div class="dot"></div>
269
+ <div class="dot"></div>
270
+ `;
271
+ els.messages.appendChild(div);
272
+ scrollToBottom();
273
+ return div;
274
+ }
275
+
276
+ function scrollToBottom() {
277
+ els.messages.scrollTo({
278
+ top: els.messages.scrollHeight,
279
+ behavior: "smooth",
280
+ });
281
  }
282
 
283
  function showError(msg) {
284
+ document.querySelector("main").innerHTML = `
285
+ <div class="view">
286
+ <div class="error" style="max-width: 80%">${msg}</div>
287
+ </div>
288
+ `;
289
  }
290
 
291
  // --- Event Listeners ---
292
 
293
  els.trainBtn.addEventListener("click", startTraining);
294
  els.sendBtn.addEventListener("click", sendMessage);
295
+ els.deleteBtn.addEventListener("click", () => clearHistory(true));
296
  els.input.addEventListener("keypress", (e) => {
297
  if (e.key === "Enter") sendMessage();
298
  });