khoj 1.17.1.dev222__py3-none-any.whl → 1.20.0__py3-none-any.whl

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.
Files changed (52) hide show
  1. khoj/routers/web_client.py +29 -130
  2. {khoj-1.17.1.dev222.dist-info → khoj-1.20.0.dist-info}/METADATA +1 -1
  3. {khoj-1.17.1.dev222.dist-info → khoj-1.20.0.dist-info}/RECORD +6 -52
  4. khoj/interface/web/404.html +0 -56
  5. khoj/interface/web/agent.html +0 -312
  6. khoj/interface/web/agents.html +0 -276
  7. khoj/interface/web/assets/icons/cancel.svg +0 -3
  8. khoj/interface/web/assets/icons/collapse.svg +0 -17
  9. khoj/interface/web/assets/icons/computer.png +0 -0
  10. khoj/interface/web/assets/icons/confirm-icon.svg +0 -1
  11. khoj/interface/web/assets/icons/copy-button-success.svg +0 -6
  12. khoj/interface/web/assets/icons/copy-button.svg +0 -5
  13. khoj/interface/web/assets/icons/credit-card.png +0 -0
  14. khoj/interface/web/assets/icons/delete.svg +0 -26
  15. khoj/interface/web/assets/icons/docx.svg +0 -7
  16. khoj/interface/web/assets/icons/edit.svg +0 -4
  17. khoj/interface/web/assets/icons/favicon.icns +0 -0
  18. khoj/interface/web/assets/icons/key.svg +0 -4
  19. khoj/interface/web/assets/icons/markdown.svg +0 -1
  20. khoj/interface/web/assets/icons/new.svg +0 -23
  21. khoj/interface/web/assets/icons/notion.svg +0 -4
  22. khoj/interface/web/assets/icons/openai-logomark.svg +0 -1
  23. khoj/interface/web/assets/icons/org.svg +0 -1
  24. khoj/interface/web/assets/icons/pdf.svg +0 -23
  25. khoj/interface/web/assets/icons/pencil-edit.svg +0 -5
  26. khoj/interface/web/assets/icons/plaintext.svg +0 -1
  27. khoj/interface/web/assets/icons/question-mark-icon.svg +0 -1
  28. khoj/interface/web/assets/icons/send.svg +0 -1
  29. khoj/interface/web/assets/icons/share.svg +0 -8
  30. khoj/interface/web/assets/icons/speaker.svg +0 -4
  31. khoj/interface/web/assets/icons/stop-solid.svg +0 -37
  32. khoj/interface/web/assets/icons/thumbs-down-svgrepo-com.svg +0 -6
  33. khoj/interface/web/assets/icons/thumbs-up-svgrepo-com.svg +0 -6
  34. khoj/interface/web/assets/icons/user-silhouette.svg +0 -4
  35. khoj/interface/web/assets/icons/voice.svg +0 -8
  36. khoj/interface/web/assets/icons/web.svg +0 -2
  37. khoj/interface/web/assets/icons/whatsapp.svg +0 -17
  38. khoj/interface/web/assets/markdown-it.min.js +0 -8476
  39. khoj/interface/web/assets/natural-cron.min.js +0 -1
  40. khoj/interface/web/assets/org.min.js +0 -1823
  41. khoj/interface/web/assets/pico.min.css +0 -5
  42. khoj/interface/web/assets/purify.min.js +0 -3
  43. khoj/interface/web/chat.html +0 -3436
  44. khoj/interface/web/config_automation.html +0 -1103
  45. khoj/interface/web/content_source_computer_input.html +0 -139
  46. khoj/interface/web/content_source_notion_input.html +0 -94
  47. khoj/interface/web/public_conversation.html +0 -2006
  48. khoj/interface/web/search.html +0 -470
  49. khoj/interface/web/settings.html +0 -1011
  50. {khoj-1.17.1.dev222.dist-info → khoj-1.20.0.dist-info}/WHEEL +0 -0
  51. {khoj-1.17.1.dev222.dist-info → khoj-1.20.0.dist-info}/entry_points.txt +0 -0
  52. {khoj-1.17.1.dev222.dist-info → khoj-1.20.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,2006 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0 maximum-scale=1.0">
6
- <title>Khoj: {{ public_conversation_slug | replace("-", " ")}}</title>
7
-
8
- <link rel="stylesheet" href="/static/assets/khoj.css?v={{ khoj_version }}">
9
- <link rel="icon" type="image/png" sizes="128x128" href="/static/assets/icons/favicon-128x128.png?v={{ khoj_version }}">
10
- <link rel="apple-touch-icon" href="/static/assets/icons/favicon-128x128.png?v={{ khoj_version }}">
11
- <link rel="manifest" href="/static/khoj.webmanifest?v={{ khoj_version }}">
12
- <link rel="stylesheet" href="https://assets.khoj.dev/katex/katex.min.css">
13
-
14
- <!-- The loading of KaTeX is deferred to speed up page rendering -->
15
- <script defer src="https://assets.khoj.dev/katex/katex.min.js"></script>
16
-
17
- <!-- To automatically render math in text elements, include the auto-render extension: -->
18
- <script defer src="https://assets.khoj.dev/katex/auto-render.min.js" onload="renderMathInElement(document.body);"></script>
19
-
20
- <link rel="stylesheet" href="https://assets.khoj.dev/higlightjs/solarized-dark.min.css">
21
- <script src="https://assets.khoj.dev/higlightjs/highlight.min.js"></script>
22
- </head>
23
- <script type="text/javascript" src="/static/assets/utils.js?v={{ khoj_version }}"></script>
24
- <script type="text/javascript" src="/static/assets/markdown-it.min.js?v={{ khoj_version }}"></script>
25
- <script>
26
- let welcome_message = `
27
- Hi, I am Khoj, your open, personal AI 👋🏽. I can:
28
- - 🧠 Answer general knowledge questions
29
- - 💡 Be a sounding board for your ideas
30
- - 📜 Chat with your notes & documents
31
- - 🌄 Generate images based on your messages
32
- - 🔎 Search the web for answers to your questions
33
- - 🎙️ Listen to your audio messages (use the mic by the input box to speak your message)
34
- - 📚 Understand files you drag & drop here
35
- - 👩🏾‍🚀 Be tuned to your conversation needs via [agents](./agents)
36
-
37
- Get the Khoj [Desktop](https://khoj.dev/downloads), [Obsidian](https://docs.khoj.dev/clients/obsidian#setup), [Emacs](https://docs.khoj.dev/clients/emacs#setup) apps to search, chat with your 🖥️ computer docs. You can manage all the files you've shared with me at any time by going to [your settings](/settings/content/computer/).
38
-
39
- To get started, just start typing below. You can also type / to see a list of commands.
40
- `.trim()
41
- let isLoggedIn = "{{ username }}" !== "None"
42
- let publicConversationSlug = "{{ public_conversation_slug }}";
43
-
44
- function createCopyParentText(message) {
45
- return function(event) {
46
- copyParentText(event, message);
47
- }
48
- }
49
- function copyParentText(event, message=null) {
50
- const button = event.currentTarget;
51
- const textContent = message ?? button.parentNode.textContent.trim();
52
- navigator.clipboard.writeText(textContent).then(() => {
53
- button.firstChild.src = "/static/assets/icons/copy-button-success.svg";
54
- setTimeout(() => {
55
- button.firstChild.src = "/static/assets/icons/copy-button.svg";
56
- }, 1000);
57
- }).catch((error) => {
58
- console.error("Error copying programmatic output to clipboard:", error);
59
- const originalButtonText = button.innerHTML;
60
- button.innerHTML = "⛔️";
61
- setTimeout(() => {
62
- button.innerHTML = originalButtonText;
63
- button.firstChild.src = "/static/assets/icons/copy-button.svg";
64
- }, 1000);
65
- });
66
- }
67
-
68
- function formatDate(date) {
69
- // Format date in HH:MM, DD MMM YYYY format
70
- let time_string = date.toLocaleTimeString('en-IN', { hour: '2-digit', minute: '2-digit', hour12: false });
71
- let date_string = date.toLocaleString('en-IN', { year: 'numeric', month: 'short', day: '2-digit'}).replaceAll('-', ' ');
72
- return `${time_string}, ${date_string}`;
73
- }
74
-
75
- function generateReference(referenceJson, index) {
76
- let reference = referenceJson.hasOwnProperty("compiled") ? referenceJson.compiled : referenceJson;
77
- let referenceFile = referenceJson.hasOwnProperty("file") ? referenceJson.file : null;
78
-
79
- // Escape reference for HTML rendering
80
- let escaped_ref = reference.replaceAll('"', '&quot;');
81
-
82
- // Generate HTML for Chat Reference
83
- let short_ref = escaped_ref.slice(0, 100);
84
- short_ref = short_ref.length < escaped_ref.length ? short_ref + "..." : short_ref;
85
- let referenceButton = document.createElement('button');
86
- referenceButton.textContent = short_ref;
87
- referenceButton.id = `ref-${index}`;
88
- referenceButton.classList.add("reference-button");
89
- referenceButton.classList.add("collapsed");
90
- referenceButton.tabIndex = 0;
91
-
92
- // Add event listener to toggle full reference on click
93
- referenceButton.addEventListener('click', function() {
94
- if (this.classList.contains("collapsed")) {
95
- this.classList.remove("collapsed");
96
- this.classList.add("expanded");
97
- this.textContent = escaped_ref;
98
- } else {
99
- this.classList.add("collapsed");
100
- this.classList.remove("expanded");
101
- this.textContent = short_ref;
102
- }
103
- });
104
-
105
- return referenceButton;
106
- }
107
-
108
- function generateOnlineReference(reference, index) {
109
-
110
- // Generate HTML for Chat Reference
111
- let title = reference.title || reference.link;
112
- let link = reference.link;
113
- let snippet = reference.snippet;
114
- let question = reference.question;
115
- if (question) {
116
- question = `<b>Question:</b> ${question}<br><br>`;
117
- } else {
118
- question = "";
119
- }
120
-
121
- let linkElement = document.createElement('a');
122
- linkElement.setAttribute('href', link);
123
- linkElement.setAttribute('target', '_blank');
124
- linkElement.setAttribute('rel', 'noopener noreferrer');
125
- linkElement.classList.add("reference-link");
126
- linkElement.setAttribute('title', title);
127
- linkElement.textContent = title;
128
-
129
- let referenceButton = document.createElement('button');
130
- referenceButton.appendChild(linkElement);
131
- referenceButton.id = `ref-${index}`;
132
- referenceButton.classList.add("reference-button");
133
- referenceButton.classList.add("collapsed");
134
- referenceButton.tabIndex = 0;
135
-
136
- // Add event listener to toggle full reference on click
137
- referenceButton.addEventListener('click', function() {
138
- if (this.classList.contains("collapsed")) {
139
- this.classList.remove("collapsed");
140
- this.classList.add("expanded");
141
- this.innerHTML = `${linkElement.outerHTML}<br><br>${question + snippet}`;
142
- } else {
143
- this.classList.add("collapsed");
144
- this.classList.remove("expanded");
145
- this.innerHTML = "";
146
- this.appendChild(linkElement);
147
- }
148
- });
149
-
150
- return referenceButton;
151
- }
152
-
153
- function renderMessage(message, by, dt=null, annotations=null, raw=false, renderType="append") {
154
- let message_time = formatDate(dt ?? new Date());
155
- let formattedMessage = formatHTMLMessage(message, raw);
156
-
157
- // Create a new div for the chat message
158
- let chatMessage = document.createElement('div');
159
- chatMessage.className = `chat-message ${by}`;
160
-
161
- // Create a new div for the chat message text and append it to the chat message
162
- let chatMessageText = document.createElement('div');
163
- chatMessageText.className = `chat-message-text ${by}`;
164
- chatMessageText.appendChild(formattedMessage);
165
- chatMessage.appendChild(chatMessageText);
166
-
167
- // Append annotations div to the chat message
168
- if (annotations) {
169
- chatMessageText.appendChild(annotations);
170
- }
171
-
172
- // Append chat message div to chat body
173
- let chatBody = document.getElementById("chat-body");
174
- if (renderType === "append") {
175
- chatBody.appendChild(chatMessage);
176
- // Scroll to bottom of chat-body element
177
- chatBody.scrollTop = chatBody.scrollHeight;
178
- } else if (renderType === "prepend"){
179
- let chatBody = document.getElementById("chat-body");
180
- chatBody.insertBefore(chatMessage, chatBody.firstChild);
181
- } else if (renderType === "return") {
182
- return chatMessage;
183
- }
184
-
185
- let chatBodyWrapper = document.getElementById("chat-body-wrapper");
186
- chatBodyWrapperHeight = chatBodyWrapper.clientHeight;
187
- }
188
-
189
- function processOnlineReferences(referenceSection, onlineContext) {
190
- let numOnlineReferences = 0;
191
- for (let subquery in onlineContext) {
192
- let onlineReference = onlineContext[subquery];
193
- if (onlineReference.organic && onlineReference.organic.length > 0) {
194
- numOnlineReferences += onlineReference.organic.length;
195
- for (let index in onlineReference.organic) {
196
- let reference = onlineReference.organic[index];
197
- let polishedReference = generateOnlineReference(reference, index);
198
- referenceSection.appendChild(polishedReference);
199
- }
200
- }
201
-
202
- if (onlineReference.knowledgeGraph && onlineReference.knowledgeGraph.length > 0) {
203
- numOnlineReferences += onlineReference.knowledgeGraph.length;
204
- for (let index in onlineReference.knowledgeGraph) {
205
- let reference = onlineReference.knowledgeGraph[index];
206
- let polishedReference = generateOnlineReference(reference, index);
207
- referenceSection.appendChild(polishedReference);
208
- }
209
- }
210
-
211
- if (onlineReference.peopleAlsoAsk && onlineReference.peopleAlsoAsk.length > 0) {
212
- numOnlineReferences += onlineReference.peopleAlsoAsk.length;
213
- for (let index in onlineReference.peopleAlsoAsk) {
214
- let reference = onlineReference.peopleAlsoAsk[index];
215
- let polishedReference = generateOnlineReference(reference, index);
216
- referenceSection.appendChild(polishedReference);
217
- }
218
- }
219
-
220
- if (onlineReference.webpages && onlineReference.webpages.length > 0) {
221
- numOnlineReferences += onlineReference.webpages.length;
222
- for (let index in onlineReference.webpages) {
223
- let reference = onlineReference.webpages[index];
224
- let polishedReference = generateOnlineReference(reference, index);
225
- referenceSection.appendChild(polishedReference);
226
- }
227
- }
228
- }
229
-
230
- return numOnlineReferences;
231
- }
232
-
233
- function renderMessageWithReference(message, by, context=null, dt=null, onlineContext=null, intentType=null, inferredQueries=null) {
234
- // If no document or online context is provided, render the message as is
235
- if ((context == null || context.length == 0) && (onlineContext == null || (onlineContext && Object.keys(onlineContext).length == 0))) {
236
- if (intentType?.includes("text-to-image")) {
237
- let imageMarkdown;
238
- if (intentType === "text-to-image") {
239
- imageMarkdown = `![](data:image/png;base64,${message})`;
240
- } else if (intentType === "text-to-image2") {
241
- imageMarkdown = `![](${message})`;
242
- } else if (intentType === "text-to-image-v3") {
243
- imageMarkdown = `![](data:image/webp;base64,${message})`;
244
- }
245
- const inferredQuery = inferredQueries?.[0];
246
- if (inferredQuery) {
247
- imageMarkdown += `\n\n**Inferred Query**:\n\n${inferredQuery}`;
248
- }
249
- return renderMessage(imageMarkdown, by, dt, null, false, "return");
250
- }
251
-
252
- return renderMessage(message, by, dt, null, false, "return");
253
- }
254
-
255
- if ((context && context.length == 0) && (onlineContext == null || (onlineContext && Object.keys(onlineContext).length == 0))) {
256
- return renderMessage(message, by, dt, null, false, "return");
257
- }
258
-
259
- // If document or online context is provided, render the message with its references
260
- let references = document.createElement('div');
261
-
262
- let referenceExpandButton = document.createElement('button');
263
- referenceExpandButton.classList.add("reference-expand-button");
264
- let numReferences = 0;
265
-
266
- if (context) {
267
- numReferences += context.length;
268
- }
269
-
270
- references.appendChild(referenceExpandButton);
271
-
272
- let referenceSection = document.createElement('div');
273
- referenceSection.classList.add("reference-section");
274
- referenceSection.classList.add("collapsed");
275
-
276
- referenceExpandButton.addEventListener('click', function() {
277
- if (referenceSection.classList.contains("collapsed")) {
278
- referenceSection.classList.remove("collapsed");
279
- referenceSection.classList.add("expanded");
280
- } else {
281
- referenceSection.classList.add("collapsed");
282
- referenceSection.classList.remove("expanded");
283
- }
284
- });
285
-
286
- references.classList.add("references");
287
- if (context) {
288
- for (let index in context) {
289
- let reference = context[index];
290
- let polishedReference = generateReference(reference, index);
291
- referenceSection.appendChild(polishedReference);
292
- }
293
- }
294
-
295
- if (onlineContext) {
296
- numReferences += processOnlineReferences(referenceSection, onlineContext);
297
- }
298
-
299
- let expandButtonText = numReferences == 1 ? "1 reference" : `${numReferences} references`;
300
- referenceExpandButton.textContent = expandButtonText;
301
-
302
- references.appendChild(referenceSection);
303
-
304
- if (intentType?.includes("text-to-image")) {
305
- let imageMarkdown;
306
- if (intentType === "text-to-image") {
307
- imageMarkdown = `![](data:image/png;base64,${message})`;
308
- } else if (intentType === "text-to-image2") {
309
- imageMarkdown = `![](${message})`;
310
- } else if (intentType === "text-to-image-v3") {
311
- imageMarkdown = `![](data:image/webp;base64,${message})`;
312
- }
313
- const inferredQuery = inferredQueries?.[0];
314
- if (inferredQuery) {
315
- imageMarkdown += `\n\n**Inferred Query**:\n\n${inferredQuery}`;
316
- }
317
- return renderMessage(imageMarkdown, by, dt, references, false, "return");
318
- }
319
-
320
- return renderMessage(message, by, dt, references, false, "return");
321
- }
322
-
323
- function formatHTMLMessage(message, raw=false, willReplace=true) {
324
- var md = window.markdownit();
325
- let newHTML = message;
326
-
327
- // Replace LaTeX delimiters with placeholders
328
- newHTML = newHTML.replace(/\\\(/g, 'LEFTPAREN').replace(/\\\)/g, 'RIGHTPAREN')
329
- .replace(/\\\[/g, 'LEFTBRACKET').replace(/\\\]/g, 'RIGHTBRACKET');
330
-
331
- // Remove any text between <s>[INST] and </s> tags. These are spurious instructions for the AI chat model.
332
- newHTML = newHTML.replace(/<s>\[INST\].+(<\/s>)?/g, '');
333
-
334
- // Customize the rendering of images
335
- md.renderer.rules.image = function(tokens, idx, options, env, self) {
336
- let token = tokens[idx];
337
-
338
- // Add class="text-to-image" to images
339
- token.attrPush(['class', 'text-to-image']);
340
-
341
- // Use the default renderer to render image markdown format
342
- return self.renderToken(tokens, idx, options);
343
- };
344
-
345
- // Render markdown
346
- newHTML = raw ? newHTML : md.render(newHTML);
347
-
348
- // Replace placeholders with LaTeX delimiters
349
- newHTML = newHTML.replace(/LEFTPAREN/g, '\\(').replace(/RIGHTPAREN/g, '\\)')
350
- .replace(/LEFTBRACKET/g, '\\[').replace(/RIGHTBRACKET/g, '\\]');
351
-
352
- // Set rendered markdown to HTML DOM element
353
- let element = document.createElement('div');
354
- element.innerHTML = newHTML;
355
- element.className = "chat-message-text-response";
356
-
357
- // Add a copy button to each chat message, if it doesn't already exist
358
- if (willReplace === true) {
359
- let copyButton = document.createElement('button');
360
- copyButton.classList.add("copy-button");
361
- copyButton.title = "Copy Message";
362
- let copyIcon = document.createElement("img");
363
- copyIcon.src = "/static/assets/icons/copy-button.svg";
364
- copyIcon.classList.add("copy-icon");
365
- copyButton.appendChild(copyIcon);
366
- copyButton.addEventListener('click', createCopyParentText(message));
367
- element.append(copyButton);
368
- }
369
-
370
- renderMathInElement(element, {
371
- // customised options
372
- // • auto-render specific keys, e.g.:
373
- delimiters: [
374
- {left: '$$', right: '$$', display: true},
375
- {left: '\\(', right: '\\)', display: false},
376
- {left: '\\[', right: '\\]', display: true}
377
- ],
378
- // • rendering keys, e.g.:
379
- throwOnError : false
380
- });
381
-
382
- // Get any elements with a class that starts with "language"
383
- let codeBlockElements = element.querySelectorAll('[class^="language-"]');
384
- // For each element, add a parent div with the class "programmatic-output"
385
- codeBlockElements.forEach((codeElement) => {
386
- // Create the parent div
387
- let parentDiv = document.createElement('div');
388
- parentDiv.classList.add("programmatic-output");
389
- // Add the parent div before the code element
390
- codeElement.parentNode.insertBefore(parentDiv, codeElement);
391
- // Move the code element into the parent div
392
- parentDiv.appendChild(codeElement);
393
-
394
- // Check if hijs has been loaded
395
- if (typeof hljs !== 'undefined') {
396
- // Highlight the code block
397
- hljs.highlightBlock(codeElement);
398
- }
399
-
400
- // Add a copy button to each code block, if it doesn't already exist
401
- if (willReplace === true) {
402
- let copyButton = document.createElement('button');
403
- copyButton.classList.add("copy-button");
404
- copyButton.title = "Copy Code";
405
- let copyIcon = document.createElement("img");
406
- copyIcon.src = "/static/assets/icons/copy-button.svg";
407
- copyIcon.classList.add("copy-icon");
408
- copyButton.appendChild(copyIcon);
409
- copyButton.addEventListener('click', copyParentText);
410
- codeElement.prepend(copyButton);
411
- }
412
- });
413
-
414
- // Get all code elements that have no class.
415
- let codeElements = element.querySelectorAll('code:not([class])');
416
- codeElements.forEach((codeElement) => {
417
- // Add the class "chat-response" to each element
418
- codeElement.classList.add("chat-response");
419
- });
420
-
421
- let anchorElements = element.querySelectorAll('a');
422
- anchorElements.forEach((anchorElement) => {
423
- // Add the class "inline-chat-link" to each element
424
- anchorElement.classList.add("inline-chat-link");
425
- });
426
-
427
- return element
428
- }
429
-
430
- function createReferenceSection(references) {
431
- let referenceSection = document.createElement('div');
432
- referenceSection.classList.add("reference-section");
433
- referenceSection.classList.add("collapsed");
434
-
435
- let numReferences = 0;
436
-
437
- if (references.hasOwnProperty("notes")) {
438
- numReferences += references["notes"].length;
439
-
440
- references["notes"].forEach((reference, index) => {
441
- let polishedReference = generateReference(reference, index);
442
- referenceSection.appendChild(polishedReference);
443
- });
444
- }
445
- if (references.hasOwnProperty("online")) {
446
- numReferences += processOnlineReferences(referenceSection, references["online"]);
447
- }
448
-
449
- let referenceExpandButton = document.createElement('button');
450
- referenceExpandButton.classList.add("reference-expand-button");
451
- referenceExpandButton.textContent = numReferences == 1 ? "1 reference" : `${numReferences} references`;
452
-
453
- referenceExpandButton.addEventListener('click', function() {
454
- if (referenceSection.classList.contains("collapsed")) {
455
- referenceSection.classList.remove("collapsed");
456
- referenceSection.classList.add("expanded");
457
- } else {
458
- referenceSection.classList.add("collapsed");
459
- referenceSection.classList.remove("expanded");
460
- }
461
- });
462
-
463
- let referencesDiv = document.createElement('div');
464
- referencesDiv.classList.add("references");
465
- referencesDiv.appendChild(referenceExpandButton);
466
- referencesDiv.appendChild(referenceSection);
467
-
468
- return referencesDiv;
469
- }
470
-
471
- function createLoadingEllipse() {
472
- // Temporary status message to indicate that Khoj is thinking
473
- let loadingEllipsis = document.createElement("div");
474
- loadingEllipsis.classList.add("lds-ellipsis");
475
-
476
- let firstEllipsis = document.createElement("div");
477
- firstEllipsis.classList.add("lds-ellipsis-item");
478
-
479
- let secondEllipsis = document.createElement("div");
480
- secondEllipsis.classList.add("lds-ellipsis-item");
481
-
482
- let thirdEllipsis = document.createElement("div");
483
- thirdEllipsis.classList.add("lds-ellipsis-item");
484
-
485
- let fourthEllipsis = document.createElement("div");
486
- fourthEllipsis.classList.add("lds-ellipsis-item");
487
-
488
- loadingEllipsis.appendChild(firstEllipsis);
489
- loadingEllipsis.appendChild(secondEllipsis);
490
- loadingEllipsis.appendChild(thirdEllipsis);
491
- loadingEllipsis.appendChild(fourthEllipsis);
492
-
493
- return loadingEllipsis;
494
- }
495
-
496
- function addMessageToChatBody(rawResponse, newResponseElement, references) {
497
- newResponseElement.innerHTML = "";
498
- newResponseElement.appendChild(formatHTMLMessage(rawResponse));
499
-
500
- finalizeChatBodyResponse(references, newResponseElement);
501
- }
502
-
503
- function finalizeChatBodyResponse(references, newResponseElement) {
504
- if (references != null && Object.keys(references).length > 0) {
505
- newResponseElement.appendChild(createReferenceSection(references));
506
- }
507
- document.getElementById("chat-body").scrollTop = document.getElementById("chat-body").scrollHeight;
508
- }
509
-
510
- function autoResize() {
511
- const scrollTop = textarea.scrollTop;
512
- textarea.style.height = '0';
513
- const scrollHeight = textarea.scrollHeight + 8; // +8 accounts for padding
514
- textarea.style.height = Math.min(scrollHeight, 200) + 'px';
515
- textarea.scrollTop = scrollTop;
516
- document.getElementById("chat-body").scrollTop = document.getElementById("chat-body").scrollHeight;
517
- }
518
-
519
- window.onload = loadChat;
520
-
521
-
522
- function loadChat() {
523
- let chatBody = document.getElementById("chat-body");
524
- chatBody.innerHTML = "";
525
- chatBody.classList.add("relative-position");
526
- let chatHistoryUrl = `/api/chat/share/history?client=web`;
527
- chatHistoryUrl += `&public_conversation_slug=${publicConversationSlug}`;
528
-
529
- if (window.screen.width < 700) {
530
- handleCollapseSidePanel();
531
- }
532
-
533
- // Create loading screen and add it to chat-body
534
- let loadingScreen = document.createElement('div');
535
- loadingScreen.classList.add("loading-spinner");
536
- let yellowOrb = document.createElement('div');
537
- loadingScreen.appendChild(yellowOrb);
538
- chatBody.appendChild(loadingScreen);
539
-
540
- // Get the most recent 10 chat messages from conversation history
541
- fetch(`${chatHistoryUrl}&n=10`, { method: "GET" })
542
- .then(response => response.json())
543
- .then(data => {
544
- if (data.detail) {
545
- // If the server returns a 500 error with detail, render a setup hint.
546
- let setupMsg = "Hi 👋🏾, to start chatting add available chat models options via <a class='inline-chat-link' href='/server/admin'>the Django Admin panel</a> on the Server";
547
- renderMessage(setupMsg, "khoj", null, null, true);
548
- } else if (data.status != "ok") {
549
- throw new Error(data.message);
550
- } else {
551
- // Set welcome message on load
552
- renderMessage(welcome_message, "khoj");
553
- }
554
- return data.response;
555
- })
556
- .then(response => {
557
- // Render conversation history, if any
558
- let chatBody = document.getElementById("chat-body");
559
- chatBody.dataset.conversationId = response.conversation_id;
560
- chatBody.dataset.conversationTitle = response.slug || `New conversation 🌱`;
561
-
562
- let agentMetadata = response.agent;
563
- if (agentMetadata) {
564
- chatBody.innerHTML = "";
565
- let agentName = agentMetadata.name;
566
- let agentAvatar = agentMetadata.avatar;
567
-
568
- let agentAvatarElement = document.getElementById("agent-avatar");
569
- let agentNameElement = document.getElementById("agent-name");
570
-
571
- let agentLinkElement = document.getElementById("agent-link");
572
-
573
- agentAvatarElement.src = agentAvatar;
574
- agentNameElement.textContent = agentName;
575
- agentLinkElement.setAttribute("href", `/agent/${agentMetadata.slug}`);
576
- renderMessage(`Hello! I'm [${agentName}](/agent/${agentMetadata.slug}). I can:
577
- - 🧠 Answer general knowledge questions
578
- - 🔎 Get real-time answers from the internet
579
- - 📜 Find relevant info in your notes & documents
580
- - 💡 Be a sounding board for your ideas
581
- - 🌄 Generate images based on your context
582
- - 🎙️ Hear you talk (use the mic by the input box to say your message out loud)
583
- - 📚 Understand files you drag & drop here
584
- - 👩🏾‍🚀 Be tuned to your conversation needs via [agents](./agents)
585
-
586
- Learn more [here](https://khoj.dev).
587
-
588
- **What's on your mind today?**
589
- `, "khoj")
590
-
591
-
592
- let agentMetadataElement = document.getElementById("agent-metadata");
593
- agentMetadataElement.style.display = "block";
594
- } else {
595
- let agentMetadataElement = document.getElementById("agent-metadata");
596
- agentMetadataElement.style.display = "none";
597
- }
598
-
599
- // Create a new IntersectionObserver
600
- let fetchRemainingMessagesObserver = new IntersectionObserver((entries, observer) => {
601
- entries.forEach(entry => {
602
- // If the element is in the viewport, fetch the remaining message and unobserve the element
603
- if (entry.isIntersecting) {
604
- fetchRemainingChatMessages(chatHistoryUrl);
605
- observer.unobserve(entry.target);
606
- }
607
- });
608
- }, {rootMargin: '0px 0px 0px 0px'});
609
-
610
- const fullChatLog = response.chat || [];
611
- fullChatLog.forEach((chat_log, index) => {
612
- // Render the last 10 messages immediately
613
- if (chat_log.message != null) {
614
- let messageElement = renderMessageWithReference(
615
- chat_log.message,
616
- chat_log.by,
617
- chat_log.context,
618
- new Date(chat_log.created),
619
- chat_log.onlineContext,
620
- chat_log.intent?.type,
621
- chat_log.intent?.["inferred-queries"]);
622
- chatBody.appendChild(messageElement);
623
-
624
- // When the 4th oldest message is within viewing distance (~60% scroll up)
625
- // Fetch the remaining chat messages
626
- if (index === 4) {
627
- fetchRemainingMessagesObserver.observe(messageElement);
628
- }
629
- }
630
- loadingScreen.style.height = chatBody.scrollHeight + 'px';
631
- });
632
-
633
- // Scroll to bottom of chat-body element
634
- chatBody.scrollTop = chatBody.scrollHeight;
635
-
636
- // Set height of chat-body element to the height of the chat-body-wrapper
637
- let chatBodyWrapper = document.getElementById("chat-body-wrapper");
638
- let chatBodyWrapperHeight = chatBodyWrapper.clientHeight;
639
- chatBody.style.height = chatBodyWrapperHeight;
640
-
641
- // Add fade out animation to loading screen and remove it after the animation ends
642
- setTimeout(() => {
643
- loadingScreen.remove();
644
- chatBody.classList.remove("relative-position");
645
- }, 500);
646
-
647
- })
648
- .catch(err => {
649
- console.log(err);
650
- return;
651
- });
652
-
653
- }
654
-
655
-
656
- function continueConversation(event) {
657
- event.preventDefault();
658
-
659
- if (!isLoggedIn) {
660
- document.getElementById("login-modal").style.display = "flex";
661
- return;
662
- }
663
-
664
- let forkConversationAPI = "/api/chat/share/fork" + "?public_conversation_slug=" + publicConversationSlug;
665
- fetch(forkConversationAPI, { method: "POST" })
666
- .then(response => response.json())
667
- .then(data => {
668
- if (data.status != "ok") {
669
- throw new Error(data.detail);
670
- }
671
- return data.next_url;
672
- })
673
- .then(next_url => {
674
- window.location.href = next_url;
675
- });
676
- }
677
-
678
- function fetchRemainingChatMessages(chatHistoryUrl) {
679
- // Create a new IntersectionObserver
680
- let observer = new IntersectionObserver((entries, observer) => {
681
- entries.forEach(entry => {
682
- // If the element is in the viewport, render the message and unobserve the element
683
- if (entry.isIntersecting) {
684
- let chat_log = entry.target.chat_log;
685
- let messageElement = renderMessageWithReference(
686
- chat_log.message,
687
- chat_log.by,
688
- chat_log.context,
689
- new Date(chat_log.created),
690
- chat_log.onlineContext,
691
- chat_log.intent?.type,
692
- chat_log.intent?.["inferred-queries"]
693
- );
694
- entry.target.replaceWith(messageElement);
695
-
696
- // Remove the observer after the element has been rendered
697
- observer.unobserve(entry.target);
698
- }
699
- });
700
- }, {rootMargin: '0px 0px 200px 0px'}); // Trigger when the element is within 200px of the viewport
701
-
702
- // Fetch remaining chat messages from conversation history
703
- fetch(`${chatHistoryUrl}&n=-10`, { method: "GET" })
704
- .then(response => response.json())
705
- .then(data => {
706
- if (data.status != "ok") {
707
- throw new Error(data.message);
708
- }
709
- return data.response;
710
- })
711
- .then(response => {
712
- const fullChatLog = response.chat || [];
713
- let chatBody = document.getElementById("chat-body");
714
- fullChatLog
715
- .reverse()
716
- .forEach(chat_log => {
717
- if (chat_log.message != null) {
718
- // Create a new element for each chat log
719
- let placeholder = document.createElement('div');
720
- placeholder.chat_log = chat_log;
721
-
722
- // Insert the message placeholder as the first child of chat body after the welcome message
723
- chatBody.insertBefore(placeholder, chatBody.firstChild.nextSibling);
724
-
725
- // Observe the element
726
- placeholder.style.height = "20px";
727
- observer.observe(placeholder);
728
- }
729
- });
730
- })
731
- .catch(err => {
732
- console.log(err);
733
- return;
734
- });
735
- }
736
-
737
- function createNewConversation() {
738
-
739
- if (!isLoggedIn) {
740
- document.getElementById("login-modal").style.display = "block";
741
- return;
742
- }
743
-
744
- // Create a modal that appears in the middle of the entire screen. It should have a form to create a new conversation.
745
- let modal = document.createElement('div');
746
- modal.classList.add("modal");
747
- modal.id = "new-conversation-modal";
748
- let modalContent = document.createElement('div');
749
- modalContent.classList.add("modal-content");
750
- let modalHeader = document.createElement('div');
751
- modalHeader.classList.add("modal-header");
752
- let modalTitle = document.createElement('h2');
753
- modalTitle.textContent = "New Conversation";
754
- let modalCloseButton = document.createElement('button');
755
- modalCloseButton.classList.add("modal-close-button");
756
- modalCloseButton.innerHTML = "&times;";
757
- modalCloseButton.addEventListener('click', function() {
758
- modal.remove();
759
- });
760
- modalHeader.appendChild(modalTitle);
761
- modalHeader.appendChild(modalCloseButton);
762
- modalContent.appendChild(modalHeader);
763
- let modalBody = document.createElement('div');
764
- modalBody.classList.add("modal-body");
765
-
766
- let agentDropDownPicker = document.createElement('select');
767
- agentDropDownPicker.setAttribute("id", "agent-dropdown-picker");
768
- agentDropDownPicker.setAttribute("name", "agent-dropdown-picker");
769
-
770
- let agentDropDownLabel = document.createElement('label');
771
- agentDropDownLabel.setAttribute("for", "agent-dropdown-picker");
772
- agentDropDownLabel.textContent = "Who do you want to talk to?";
773
-
774
- fetch('/api/agents')
775
- .then(response => response.json())
776
- .then(data => {
777
- if (data.length > 0) {
778
- data.forEach((agent) => {
779
- let agentOption = document.createElement('option');
780
- agentOption.setAttribute("value", agent.slug);
781
- agentOption.textContent = agent.name;
782
- agentDropDownPicker.appendChild(agentOption);
783
- });
784
- }
785
- })
786
- .catch(err => {
787
- return;
788
- });
789
-
790
- let seeAllAgentsLink = document.createElement('a');
791
- seeAllAgentsLink.setAttribute("href", "/agents");
792
- seeAllAgentsLink.setAttribute("target", "_blank");
793
- seeAllAgentsLink.textContent = "See all agents";
794
-
795
- let newConversationSubmitButton = document.createElement('button');
796
- newConversationSubmitButton.setAttribute("type", "submit");
797
- newConversationSubmitButton.textContent = "Go";
798
- newConversationSubmitButton.id = "new-conversation-submit-button";
799
-
800
- newConversationSubmitButton.addEventListener('click', function(event) {
801
- event.preventDefault();
802
- let agentSlug = agentDropDownPicker.value;
803
- let createURL = `/api/chat/sessions?client=web&agent_slug=${agentSlug}`;
804
- let chatBody = document.getElementById("chat-body");
805
- fetch(createURL, { method: "POST" })
806
- .then(response => response.json())
807
- .then(data => {
808
- chatBody.dataset.conversationId = data.conversation_id;
809
- modal.remove();
810
- loadChat();
811
- })
812
- .catch(err => {
813
- return;
814
- });
815
- });
816
-
817
- let closeButton = document.createElement('button');
818
- closeButton.id = "close-button";
819
- closeButton.textContent = "Close";
820
- closeButton.classList.add("close-button");
821
- closeButton.addEventListener('click', function() {
822
- modal.remove();
823
- });
824
-
825
- modalBody.appendChild(agentDropDownLabel);
826
- modalBody.appendChild(agentDropDownPicker);
827
- modalBody.appendChild(seeAllAgentsLink);
828
-
829
- let modalFooter = document.createElement('div');
830
- modalFooter.classList.add("modal-footer");
831
- modalFooter.appendChild(closeButton);
832
- modalFooter.appendChild(newConversationSubmitButton);
833
- modalBody.appendChild(modalFooter);
834
-
835
- modalContent.appendChild(modalBody);
836
- modal.appendChild(modalContent);
837
- document.body.appendChild(modal);
838
- }
839
-
840
-
841
- function handleCollapseSidePanel() {
842
- document.getElementById('side-panel').classList.toggle('collapsed');
843
- document.getElementById('new-conversation').classList.toggle('collapsed');
844
- document.getElementById('side-panel-collapse').style.transform = document.getElementById('side-panel').classList.contains('collapsed') ? 'rotate(0deg)' : 'rotate(180deg)';
845
- }
846
- </script>
847
- <body>
848
- <div id="khoj-empty-container" class="khoj-empty-container">
849
- </div>
850
-
851
- <!-- Login Modal -->
852
- <div id="login-modal" style="display: none;">
853
- <img class="khoj-logo" src="/static/assets/icons/favicon-128x128.png" alt="Khoj"></img>
854
- <div class="login-modal-title">Login to continue</div>
855
- <!-- Sign in with Magic Link -->
856
- <div class="khoj-magic-link">
857
- <input type="email" id="email" placeholder="Email" required>
858
- <button id="magic-link-button">Send Magic Link</button>
859
- </div>
860
- <!-- Divider -->
861
- <div style="text-align: center; font-size: 16px; font-weight: 500; border-top: 1px solid black;">OR</div>
862
- <!-- Sign Up/Login with Google OAuth -->
863
- <div
864
- class="g_id_signin"
865
- data-shape="circle"
866
- data-text="continue_with"
867
- data-logo_alignment="center"
868
- data-size="large"
869
- data-type="standard">
870
- </div>
871
- <div id="g_id_onload"
872
- data-client_id="{{ google_client_id }}"
873
- data-ux_mode="redirect"
874
- data-login_uri="{{ redirect_uri }}"
875
- data-auto-select="true">
876
- </div>
877
- </div>
878
-
879
- <!--Add Header Logo and Nav Pane-->
880
- {% import 'utils.html' as utils %}
881
- {{ utils.heading_pane(user_photo, username, is_active, has_documents) }}
882
- <div id="chat-section-wrapper">
883
- <div id="side-panel-wrapper">
884
- <div id="side-panel">
885
- <div id="new-conversation">
886
- <div id="conversation-list-header">Agents</div>
887
- <button class="side-panel-button" id="new-conversation-button" onclick="createNewConversation()">
888
- New Topic
889
- <svg class="new-convo-button" viewBox="0 0 40 40" fill="#000000" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg">
890
- <path d="M16 0c-8.836 0-16 7.163-16 16s7.163 16 16 16c8.837 0 16-7.163 16-16s-7.163-16-16-16zM16 30.032c-7.72 0-14-6.312-14-14.032s6.28-14 14-14 14 6.28 14 14-6.28 14.032-14 14.032zM23 15h-6v-6c0-0.552-0.448-1-1-1s-1 0.448-1 1v6h-6c-0.552 0-1 0.448-1 1s0.448 1 1 1h6v6c0 0.552 0.448 1 1 1s1-0.448 1-1v-6h6c0.552 0 1-0.448 1-1s-0.448-1-1-1z"></path>
891
- </svg>
892
- </button>
893
- </div>
894
- <div id="other-agents">
895
- <div id="agents-list">
896
- {% for agent in agents %}
897
- <a class="inline-chat-link agent-link" href="/agent/{{ agent.slug }}">
898
- <div class="agent-metadata-content">
899
- <div class="agent-avatar-wrapper">
900
- <img class="agent-avatar" src="{{ agent.avatar }}" alt="Agent Avatar" />
901
- </div>
902
- <div class="agent-name-wrapper">
903
- <div class="agent-name">
904
- {{ agent.name }}
905
- </div>
906
- </div>
907
- </div>
908
- </a>
909
- {% endfor %}
910
- </div>
911
- </div>
912
- <a id="agent-link" class="inline-chat-link" href="">
913
- <div id="agent-metadata" style="display: none;">
914
- Current Agent
915
- <div id="agent-metadata-content">
916
- <div id="agent-avatar-wrapper">
917
- <img id="agent-avatar" src="" alt="Agent Avatar" />
918
- </div>
919
- <div id="agent-name-wrapper">
920
- <div id="agent-name"></div>
921
- </div>
922
- </div>
923
- </div>
924
- </a>
925
- </div>
926
- <div id="collapse-side-panel">
927
- <button
928
- class="side-panel-button"
929
- id="collapse-side-panel-button"
930
- onclick="handleCollapseSidePanel()"
931
- >
932
- <svg id="side-panel-collapse" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
933
- <path d="M7.82054 20.7313C8.21107 21.1218 8.84423 21.1218 9.23476 20.7313L15.8792 14.0868C17.0505 12.9155 17.0508 11.0167 15.88 9.84497L9.3097 3.26958C8.91918 2.87905 8.28601 2.87905 7.89549 3.26958C7.50497 3.6601 7.50497 4.29327 7.89549 4.68379L14.4675 11.2558C14.8581 11.6464 14.8581 12.2795 14.4675 12.67L7.82054 19.317C7.43002 19.7076 7.43002 20.3407 7.82054 20.7313Z" fill="#0F0F0F"/>
934
- </svg>
935
- </button>
936
- </div>
937
- </div>
938
- <div id="chat-body-wrapper">
939
- <!-- Chat Body -->
940
- <div id="chat-body"></div>
941
-
942
- <!-- Chat Footer -->
943
- <div id="chat-footer">
944
- <div id="chat-tooltip" style="display: none;"></div>
945
- <div id="input-row">
946
- <button id="continue-conversation" class="main-call-to-action" onclick="continueConversation(event)">
947
- Continue this conversation
948
- </button>
949
- <button id="speak-button" class="input-row-button" onclick="continueConversation(event)">
950
- <svg id="speak-button-img" class="input-row-button-img" alt="Transcribe" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
951
- <path d="M3.5 6.5A.5.5 0 0 1 4 7v1a4 4 0 0 0 8 0V7a.5.5 0 0 1 1 0v1a5 5 0 0 1-4.5 4.975V15h3a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1h3v-2.025A5 5 0 0 1 3 8V7a.5.5 0 0 1 .5-.5z"/>
952
- <path d="M10 8a2 2 0 1 1-4 0V3a2 2 0 1 1 4 0v5zM8 0a3 3 0 0 0-3 3v5a3 3 0 0 0 6 0V3a3 3 0 0 0-3-3z"/>
953
- </svg>
954
- <svg id="stop-record-button-img" style="display: none" class="input-row-button-img" alt="Stop Transcribing" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
955
- <path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/>
956
- <path d="M5 6.5A1.5 1.5 0 0 1 6.5 5h3A1.5 1.5 0 0 1 11 6.5v3A1.5 1.5 0 0 1 9.5 11h-3A1.5 1.5 0 0 1 5 9.5v-3z"/>
957
- </svg>
958
- </button>
959
- <button id="send-button" class="input-row-button" alt="Send message">
960
- <svg id="send-button-img" onclick="continueConversation()" class="input-row-button-img" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
961
- <path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-7.5 3.5a.5.5 0 0 1-1 0V5.707L5.354 7.854a.5.5 0 1 1-.708-.708l3-3a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 5.707V11.5z"/>
962
- </svg>
963
- </button>
964
- </div>
965
- </div>
966
- </div>
967
- </div>
968
- </body>
969
- <script>
970
- // Set the active nav pane
971
- let chatNav = document.getElementById("chat-nav");
972
- if (chatNav) {
973
- chatNav.classList.add("khoj-nav-selected");
974
- }
975
-
976
- const magicLinkButton = document.getElementById('magic-link-button');
977
- const emailInput = document.getElementById('email');
978
-
979
- magicLinkButton.addEventListener('click', async () => {
980
- const email = emailInput.value;
981
- if (!email) {
982
- alert('Please enter a valid email address');
983
- return;
984
- }
985
-
986
- if (!email.includes('@')) {
987
- alert('Please enter a valid email address');
988
- return;
989
- }
990
-
991
- magicLinkButton.disabled = true;
992
- magicLinkButton.innerText = 'Check your email for a sign-in link!';
993
-
994
- const response = await fetch('/auth/magic', {
995
- method: 'POST',
996
- headers: {
997
- 'Content-Type': 'application/json',
998
- },
999
- body: JSON.stringify({ "email": email }),
1000
- })
1001
-
1002
- if (response.status === 200) {
1003
- console.log('Magic link sent to your email');
1004
- } else {
1005
- alert('Failed to send magic link');
1006
- }
1007
- });
1008
-
1009
- </script>
1010
- <style>
1011
- html, body {
1012
- height: 100%;
1013
- width: 100%;
1014
- padding: 0px;
1015
- margin: 0px;
1016
- }
1017
- body {
1018
- display: grid;
1019
- background: var(--background-color);
1020
- color: var(--main-text-color);
1021
- text-align: center;
1022
- font-family: var(--font-family);
1023
- font-size: medium;
1024
- font-weight: 300;
1025
- line-height: 1.5em;
1026
- height: 100vh;
1027
- margin: 0;
1028
- }
1029
- body > * {
1030
- padding: 10px;
1031
- margin: 10px;
1032
- }
1033
-
1034
- div.collapsed {
1035
- display: none;
1036
- }
1037
-
1038
- div.expanded {
1039
- display: block;
1040
- }
1041
-
1042
- div.references {
1043
- padding-top: 8px;
1044
- }
1045
- div.reference {
1046
- display: grid;
1047
- grid-template-rows: auto;
1048
- grid-auto-flow: row;
1049
- grid-column-gap: 10px;
1050
- grid-row-gap: 10px;
1051
- margin: 10px;
1052
- }
1053
-
1054
- div.expanded.reference-section {
1055
- display: grid;
1056
- grid-template-rows: auto;
1057
- grid-auto-flow: row;
1058
- grid-column-gap: 10px;
1059
- grid-row-gap: 10px;
1060
- margin: 10px;
1061
- }
1062
-
1063
- button.reference-button {
1064
- background: var(--background-color);
1065
- color: var(--main-text-color);
1066
- border: 1px solid var(--main-text-color);
1067
- border-radius: 5px;
1068
- padding: 5px;
1069
- font-size: 14px;
1070
- font-weight: 300;
1071
- line-height: 1.5em;
1072
- cursor: pointer;
1073
- transition: background 0.2s ease-in-out;
1074
- text-align: left;
1075
- max-height: 75px;
1076
- transition: max-height 0.3s ease-in-out;
1077
- overflow: hidden;
1078
- }
1079
- button.reference-button.expanded {
1080
- max-height: none;
1081
- white-space: pre-wrap;
1082
- }
1083
-
1084
- button.reference-button::before {
1085
- content: "▶";
1086
- margin-right: 5px;
1087
- display: inline-block;
1088
- transition: transform 0.3s ease-in-out;
1089
- }
1090
-
1091
- button.reference-button:active:before,
1092
- button.reference-button[aria-expanded="true"]::before {
1093
- transform: rotate(90deg);
1094
- }
1095
-
1096
- button.reference-expand-button {
1097
- background: var(--background-color);
1098
- color: var(--main-text-color);
1099
- border: 1px dotted var(--main-text-color);
1100
- border-radius: 5px;
1101
- padding: 5px;
1102
- font-size: 14px;
1103
- font-weight: 300;
1104
- line-height: 1.5em;
1105
- cursor: pointer;
1106
- transition: background 0.4s ease-in-out;
1107
- text-align: left;
1108
- }
1109
-
1110
- button.reference-expand-button:hover {
1111
- background: var(--primary-hover);
1112
- }
1113
-
1114
- code.chat-response {
1115
- background: var(--primary-hover);
1116
- color: var(--primary-inverse);
1117
- border-radius: 5px;
1118
- padding: 5px;
1119
- font-size: 14px;
1120
- font-weight: 300;
1121
- line-height: 1.5em;
1122
- }
1123
-
1124
- input.conversation-title-input {
1125
- font-family: var(--font-family);
1126
- font-size: 14px;
1127
- font-weight: 300;
1128
- line-height: 1.5em;
1129
- padding: 5px;
1130
- border: 1px solid var(--main-text-color);
1131
- border-radius: 5px;
1132
- margin: 4px;
1133
- }
1134
-
1135
- input.conversation-title-input:focus {
1136
- outline: none;
1137
- }
1138
-
1139
- #chat-section-wrapper {
1140
- display: grid;
1141
- grid-template-columns: auto 1fr;
1142
- grid-column-gap: 10px;
1143
- grid-row-gap: 10px;
1144
- padding: 10px;
1145
- margin: 10px;
1146
- overflow-y: scroll;
1147
- }
1148
-
1149
- #chat-body-wrapper {
1150
- display: flex;
1151
- flex-direction: column;
1152
- overflow: hidden;
1153
- }
1154
-
1155
- #side-panel {
1156
- width: 250px;
1157
- padding: 10px;
1158
- background: var(--background-color);
1159
- border-radius: 5px;
1160
- box-shadow: 0 0 11px #aaa;
1161
- text-align: left;
1162
- transition: width 0.3s ease-in-out;
1163
- max-height: 100%;
1164
- display: grid;
1165
- grid-template-rows: auto 1fr auto;
1166
- }
1167
-
1168
- div#side-panel.collapsed {
1169
- width: 0;
1170
- padding: 0;
1171
- display: block;
1172
- overflow: hidden;
1173
- }
1174
-
1175
- div#collapse-side-panel {
1176
- align-self: center;
1177
- padding: 8px;
1178
- }
1179
-
1180
- div#conversation-list-body {
1181
- display: grid;
1182
- grid-template-columns: 1fr;
1183
- grid-gap: 8px;
1184
- }
1185
-
1186
- div#conversation-list {
1187
- height: 1;
1188
- }
1189
-
1190
- div#side-panel-wrapper {
1191
- display: flex;
1192
- }
1193
-
1194
- #chat-body {
1195
- height: 100%;
1196
- font-size: medium;
1197
- margin: 0px;
1198
- line-height: 20px;
1199
- overflow-y: scroll;
1200
- overflow-x: hidden;
1201
- transition: background-color 0.2s;
1202
- transition: opacity 0.2s;
1203
- }
1204
-
1205
- #chat-body.dragover {
1206
- background-color: var(--primary-active);
1207
- }
1208
-
1209
- .relative-position {
1210
- position: relative;
1211
- }
1212
-
1213
- #chat-body.dragover {
1214
- opacity: 50%;
1215
- }
1216
-
1217
- div.loading-screen {
1218
- position: absolute;
1219
- width: 100%;
1220
- height: 100%;
1221
- display: flex;
1222
- align-items: center;
1223
- justify-content: center;
1224
- font-size: 2rem;
1225
- color: #333;
1226
- z-index: 9999; /* This is the important part */
1227
- }
1228
-
1229
- button.main-call-to-action {
1230
- font-size: 24px;
1231
- font-weight: bold;
1232
- padding: 10px;
1233
- border: none;
1234
- border-radius: 8px;
1235
- background-color: var(--summer-sun);
1236
- font: inherit;
1237
- color: var(--main-text-color);
1238
- cursor: pointer;
1239
- transition: background-color 0.3s;
1240
- }
1241
-
1242
- button.main-call-to-action:hover {
1243
- background-color: var(--primary-hover);
1244
- box-shadow: 0 0 10px var(--primary-hover);
1245
- }
1246
-
1247
- /* add chat metatdata to bottom of bubble */
1248
- /* move message by khoj to left */
1249
- .chat-message.khoj {
1250
- margin-left: auto;
1251
- text-align: left;
1252
- }
1253
- /* move message by you to right */
1254
- .chat-message.you {
1255
- margin-right: auto;
1256
- text-align: right;
1257
- }
1258
- /* basic style chat message text */
1259
- .chat-message-text {
1260
- margin: 10px;
1261
- border-radius: 10px;
1262
- padding: 10px;
1263
- position: relative;
1264
- display: inline-block;
1265
- max-width: 80%;
1266
- text-align: left;
1267
- white-space: pre-line;
1268
- }
1269
- /* color chat bubble by khoj blue */
1270
- .chat-message-text.khoj {
1271
- color: var(--primary-inverse);
1272
- background: var(--primary);
1273
- margin-left: auto;
1274
- }
1275
- .chat-message-text ol,
1276
- .chat-message-text ul {
1277
- white-space: normal;
1278
- margin: 0;
1279
- }
1280
- .chat-message-text-response {
1281
- margin-bottom: 0px;
1282
- }
1283
-
1284
- .side-panel-button {
1285
- background: none;
1286
- border: none;
1287
- box-shadow: none;
1288
- font-size: 14px;
1289
- font-weight: 300;
1290
- line-height: 1.5em;
1291
- cursor: pointer;
1292
- transition: background 0.3s ease-in-out;
1293
- border-radius: 5%;;
1294
- font-family: var(--font-family);
1295
- }
1296
-
1297
- .side-panel-button:hover,
1298
- .input-row-button:hover {
1299
- background: var(--primary-hover);
1300
- }
1301
- .side-panel-button:active,
1302
- .input-row-button:active {
1303
- background: var(--primary-active);
1304
- }
1305
-
1306
- /* Spinner symbol when the chat message is loading */
1307
- .spinner {
1308
- border: 4px solid #f3f3f3;
1309
- border-top: 4px solid var(--primary-inverse);
1310
- border-radius: 50%;
1311
- width: 12px;
1312
- height: 12px;
1313
- animation: spin 2s linear infinite;
1314
- margin: 0px 0px 0px 10px;
1315
- display: inline-block;
1316
- }
1317
- @keyframes spin {
1318
- 0% { transform: rotate(0deg); }
1319
- 100% { transform: rotate(360deg); }
1320
- }
1321
- /* color chat bubble by you dark grey */
1322
- .chat-message-text.you {
1323
- color: #f8fafc;
1324
- background: #475569;
1325
- margin-right: auto;
1326
- }
1327
- img.text-to-image {
1328
- max-width: 60%;
1329
- }
1330
-
1331
- #chat-footer {
1332
- padding: 0;
1333
- margin: 8px;
1334
- display: grid;
1335
- grid-template-columns: minmax(70px, 100%);
1336
- grid-column-gap: 10px;
1337
- grid-row-gap: 10px;
1338
- }
1339
- #input-row {
1340
- display: grid;
1341
- grid-template-columns: auto 40px 32px;
1342
- grid-column-gap: 10px;
1343
- grid-row-gap: 10px;
1344
- background: var(--background-color);
1345
- align-items: center;
1346
- }
1347
- .option:hover {
1348
- box-shadow: 0 0 11px #aaa;
1349
- }
1350
- .input-row-button {
1351
- background: var(--background-color);
1352
- border: none;
1353
- box-shadow: none;
1354
- border-radius: 50%;
1355
- font-size: 14px;
1356
- font-weight: 300;
1357
- line-height: 1.5em;
1358
- cursor: pointer;
1359
- transition: background 0.3s ease-in-out;
1360
- width: 40px;
1361
- height: 40px;
1362
- margin-top: -2px;
1363
- margin-left: -5px;
1364
- }
1365
-
1366
- svg#side-panel-collapse {
1367
- width: 30px;
1368
- height: 30px;
1369
- }
1370
-
1371
- .input-row-button-img {
1372
- width: 24px;
1373
- height: 24px;
1374
- }
1375
- #send-button {
1376
- padding: 0;
1377
- position: relative;
1378
- }
1379
- #send-button-img {
1380
- width: 28px;
1381
- height: 28px;
1382
- background: var(--primary-hover);
1383
- border-radius: 50%;
1384
- }
1385
-
1386
- #stop-send-button-img {
1387
- position: absolute;
1388
- top: 6px;
1389
- right: 6px;
1390
- width: 28px;
1391
- height: 28px;
1392
- transform: rotateY(-180deg) rotateZ(-90deg);
1393
- }
1394
-
1395
- .option-enabled {
1396
- box-shadow: 0 0 12px rgb(119, 156, 46);
1397
- }
1398
-
1399
- .option-enabled:focus {
1400
- outline: none !important;
1401
- border:1px solid #475569;
1402
- box-shadow: 0 0 16px var(--primary);
1403
- }
1404
-
1405
- a.inline-chat-link {
1406
- color: var(--main-text-color);
1407
- text-decoration: none;
1408
- border-bottom: 1px dotted var(--main-text-color);
1409
- }
1410
-
1411
- a.reference-link {
1412
- color: var(--main-text-color);
1413
- border-bottom: 1px dotted var(--main-text-color);
1414
- }
1415
-
1416
- button.copy-button {
1417
- border-radius: 4px;
1418
- background-color: var(--background-color);
1419
- border: 1px solid var(--main-text-color);
1420
- text-align: center;
1421
- font-size: 16px;
1422
- transition: all 0.5s;
1423
- cursor: pointer;
1424
- padding: 4px;
1425
- float: right;
1426
- }
1427
-
1428
- button.copy-button span {
1429
- cursor: pointer;
1430
- display: inline-block;
1431
- position: relative;
1432
- transition: 0.5s;
1433
- }
1434
-
1435
- img.copy-icon {
1436
- width: 16px;
1437
- height: 16px;
1438
- }
1439
-
1440
- button.copy-button:hover {
1441
- background-color: var(--primary-hover);
1442
- color: #f5f5f5;
1443
- }
1444
-
1445
- pre {
1446
- text-wrap: unset;
1447
- }
1448
-
1449
- div#login-modal {
1450
- min-height: 300px;
1451
- z-index: 1;
1452
- position: absolute;
1453
- top: 40%;
1454
- left: 50%;
1455
- transform: translate(-50%, -50%);
1456
- box-shadow: 0 4px 4px var(--main-text-color);
1457
- border-radius: 8px;
1458
- padding: 40px;
1459
- background: linear-gradient(145deg, var(--background-color), var(--primary-hover));
1460
- display: flex;
1461
- flex-direction: column;
1462
- align-content: center;
1463
- justify-content: center;
1464
- align-items: center;
1465
- gap: 24px;
1466
- }
1467
-
1468
- .khoj-magic-link {
1469
- display: grid;
1470
- grid-template-columns: 1fr;
1471
- gap: 16px;
1472
- text-align: center;
1473
- }
1474
-
1475
- #email {
1476
- padding: 10px;
1477
- font-size: 16px;
1478
- border: 1px solid var(--main-text-color);
1479
- border-radius: 5px;
1480
- }
1481
-
1482
- #email:focus {
1483
- box-shadow: 0 0 10px var(--main-text-color);
1484
- }
1485
-
1486
- #magic-link-button {
1487
- padding: 10px;
1488
- font-size: 16px;
1489
- border: 1px solid var(--main-text-color);
1490
- border-radius: 5px;
1491
- width: 100%;
1492
- background: var(--main-text-color);
1493
- color: var(--frosted-background-color);
1494
- cursor: pointer;
1495
- }
1496
-
1497
- #magic-link-button:hover {
1498
- box-shadow: 0 0 10px var(--main-text-color);
1499
- }
1500
-
1501
- div.g_id_signin {
1502
- margin: 0 auto;
1503
- display: block;
1504
- }
1505
-
1506
- div.login-modal-title {
1507
- text-align: center;
1508
- line-height: 28px;
1509
- font-size: 24px;
1510
- font-weight: 500;
1511
- }
1512
-
1513
- @media (pointer: coarse), (hover: none) {
1514
- abbr[title] {
1515
- position: relative;
1516
- padding-left: 4px; /* space references out to ease tapping */
1517
- }
1518
- abbr[title]:focus:after {
1519
- content: attr(title);
1520
-
1521
- /* position tooltip */
1522
- position: absolute;
1523
- left: 16px; /* open tooltip to right of ref link, instead of on top of it */
1524
- width: auto;
1525
- z-index: 1; /* show tooltip above chat messages */
1526
-
1527
- /* style tooltip */
1528
- background-color: #aaa;
1529
- color: #f8fafc;
1530
- border-radius: 2px;
1531
- box-shadow: 1px 1px 4px 0 rgba(0, 0, 0, 0.4);
1532
- font-size: 14px;
1533
- padding: 2px 4px;
1534
- }
1535
- }
1536
- @media only screen and (max-width: 700px) {
1537
- body {
1538
- grid-template-columns: 1fr;
1539
- grid-template-rows: auto auto minmax(80px, 100%) auto;
1540
- }
1541
- body > * {
1542
- grid-column: 1;
1543
- }
1544
- #chat-footer {
1545
- padding: 0;
1546
- margin: 4px;
1547
- grid-template-columns: auto;
1548
- }
1549
- img.text-to-image {
1550
- max-width: 100%;
1551
- }
1552
- #clear-chat-button {
1553
- margin-left: 0;
1554
- }
1555
-
1556
- div#side-panel.collapsed {
1557
- width: 0px;
1558
- display: block;
1559
- overflow: hidden;
1560
- padding: 0;
1561
- }
1562
-
1563
- svg#side-panel-collapse {
1564
- width: 24px;
1565
- height: 24px;
1566
- }
1567
-
1568
- #chat-body-wrapper {
1569
- min-width: 0;
1570
- }
1571
-
1572
- div#chat-section-wrapper {
1573
- padding: 4px;
1574
- margin: 4px;
1575
- grid-column-gap: 4px;
1576
- }
1577
- div#collapse-side-panel {
1578
- align-self: center;
1579
- padding: 0px;
1580
- }
1581
- }
1582
- @media only screen and (min-width: 700px) {
1583
- body {
1584
- grid-template-columns: auto min(90vw, 100%) auto;
1585
- grid-template-rows: auto auto minmax(80px, 100%) auto;
1586
- }
1587
- body > * {
1588
- grid-column: 2;
1589
- }
1590
-
1591
- div#login-modal {
1592
- z-index: 1;
1593
- }
1594
- }
1595
-
1596
- div#chat-tooltip {
1597
- text-align: left;
1598
- font-size: medium;
1599
- }
1600
-
1601
- svg.new-convo-button {
1602
- width: 20px;
1603
- margin-left: 5px;
1604
- }
1605
-
1606
- div#new-conversation {
1607
- display: grid;
1608
- grid-auto-flow: column;
1609
- font-size: large;
1610
- text-align: left;
1611
- border-bottom: 1px solid var(--main-text-color);
1612
- margin-top: 8px;
1613
- margin-bottom: 8px;
1614
- }
1615
-
1616
- button#new-conversation-button {
1617
- display: inline-flex;
1618
- align-items: center;
1619
- justify-self: end;
1620
- }
1621
-
1622
- div.conversation-button {
1623
- background: var(--background-color);
1624
- color: var(--main-text-color);
1625
- border: 1px solid var(--main-text-color);
1626
- border-radius: 5px;
1627
- padding: 5px;
1628
- font-size: 14px;
1629
- font-weight: 300;
1630
- line-height: 1.5em;
1631
- cursor: pointer;
1632
- transition: background 0.2s ease-in-out;
1633
- text-align: left;
1634
- display: flex;
1635
- position: relative;
1636
- margin: 0 8px;
1637
- }
1638
-
1639
- .three-dot-menu {
1640
- display: none;
1641
- border-radius: 5px;
1642
- position: absolute;
1643
- right: 4px;
1644
- top: 4px;
1645
- }
1646
-
1647
- button.three-dot-menu-button-item {
1648
- background: var(--background-color);
1649
- color: var(--main-text-color);
1650
- border: none;
1651
- box-shadow: none;
1652
- font-size: 14px;
1653
- font-weight: 300;
1654
- line-height: 1.5em;
1655
- cursor: pointer;
1656
- transition: background 0.3s ease-in-out;
1657
- font-family: var(--font-family);
1658
- border-radius: 4px;
1659
- right: 0;
1660
- }
1661
-
1662
- button.three-dot-menu-button-item:hover {
1663
- background: var(--primary-hover);
1664
- color: var(--primary-inverse);
1665
- }
1666
-
1667
- .three-dot-menu-button {
1668
- background: var(--background-color);
1669
- border: none;
1670
- box-shadow: none;
1671
- font-size: 14px;
1672
- font-weight: 300;
1673
- line-height: 1.5em;
1674
- cursor: pointer;
1675
- transition: background 0.3s ease-in-out;
1676
- font-family: var(--font-family);
1677
- border-radius: 4px;
1678
- right: 0;
1679
- }
1680
-
1681
- .conversation-button:hover .three-dot-menu {
1682
- display: block;
1683
- }
1684
-
1685
- div.conversation-menu {
1686
- position: absolute;
1687
- z-index: 1;
1688
- top: 100%;
1689
- right: 0;
1690
- text-align: right;
1691
- background-color: var(--background-color);
1692
- border: 1px solid var(--main-text-color);
1693
- border-radius: 5px;
1694
- padding: 5px;
1695
- box-shadow: 0 0 11px #aaa;
1696
- }
1697
-
1698
- div.conversation-button:hover {
1699
- background: var(--primary-hover);
1700
- color: var(--primary-inverse);
1701
- }
1702
-
1703
- div.selected-conversation {
1704
- background: var(--primary-hover) !important;
1705
- color: var(--primary-inverse) !important;
1706
- }
1707
-
1708
- @keyframes gradient {
1709
- 0% {
1710
- background-position: 0% 50%;
1711
- }
1712
- 50% {
1713
- background-position: 100% 50%;
1714
- }
1715
- 100% {
1716
- background-position: 0% 50%;
1717
- }
1718
- }
1719
-
1720
- a.khoj-logo {
1721
- text-align: center;
1722
- }
1723
-
1724
- div.khoj-empty-container {
1725
- margin: 0px;
1726
- padding: 0px;
1727
- }
1728
-
1729
- p {
1730
- margin: 0;
1731
- }
1732
-
1733
-
1734
- div#other-agents {
1735
- max-height: 95%;
1736
- overflow-y: scroll;
1737
- }
1738
-
1739
- div#agents-list {
1740
- height: 1px;
1741
- }
1742
-
1743
- div.programmatic-output {
1744
- background-color: #f5f5f5;
1745
- border: 1px solid #ddd;
1746
- border-radius: 3px;
1747
- box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
1748
- color: #333;
1749
- font-family: monospace;
1750
- font-size: 14px;
1751
- line-height: 1.5;
1752
- margin: 10px 0;
1753
- overflow-x: auto;
1754
- padding: 10px;
1755
- white-space: pre-wrap;
1756
- }
1757
-
1758
- .loading-spinner {
1759
- display: inline-block;
1760
- position: relative;
1761
- width: 80px;
1762
- height: 80px;
1763
- }
1764
- .loading-spinner div {
1765
- position: absolute;
1766
- border: 4px solid var(--primary-hover);
1767
- opacity: 1;
1768
- border-radius: 50%;
1769
- animation: lds-ripple 0.5s cubic-bezier(0, 0.2, 0.8, 1) infinite;
1770
- }
1771
- .loading-spinner div:nth-child(2) {
1772
- animation-delay: -0.5s;
1773
- }
1774
- @keyframes lds-ripple {
1775
- 0% {
1776
- top: 36px;
1777
- left: 36px;
1778
- width: 0;
1779
- height: 0;
1780
- opacity: 1;
1781
- border-color: var(--primary-hover);
1782
- }
1783
- 50% {
1784
- border-color: var(--flower);
1785
- }
1786
- 100% {
1787
- top: 0px;
1788
- left: 0px;
1789
- width: 72px;
1790
- height: 72px;
1791
- opacity: 0;
1792
- border-color: var(--water);
1793
- }
1794
- }
1795
-
1796
- #agent-metadata-content, .agent-metadata-content {
1797
- display: grid;
1798
- grid-template-columns: auto 1fr;
1799
- padding: 10px;
1800
- background-color: var(--primary);
1801
- border-radius: 5px;
1802
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
1803
- margin-bottom: 20px;
1804
- }
1805
-
1806
- #agent-metadata, .agent-metadata {
1807
- border-top: 1px solid black;
1808
- padding-top: 10px;
1809
- }
1810
-
1811
- #agent-avatar-wrapper, .agent-avatar-wrapper {
1812
- margin-right: 10px;
1813
- }
1814
-
1815
- #agent-avatar, .agent-avatar {
1816
- width: 50px;
1817
- height: 50px;
1818
- border-radius: 50%;
1819
- object-fit: cover;
1820
- }
1821
-
1822
- #agent-name-wrapper, .agent-name-wrapper {
1823
- display: grid;
1824
- align-items: center;
1825
- }
1826
-
1827
- #agent-name, .agent-name {
1828
- font-size: 18px;
1829
- font-weight: bold;
1830
- color: #333;
1831
- }
1832
-
1833
- .modal {
1834
- position: fixed; /* Stay in place */
1835
- z-index: 1; /* Sit on top */
1836
- left: 0;
1837
- top: 0;
1838
- width: 100%; /* Full width */
1839
- height: 100%; /* Full height */
1840
- background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
1841
- margin: 0px;
1842
- }
1843
-
1844
- .modal-content {
1845
- margin: 15% auto; /* 15% from the top and centered */
1846
- padding: 20px;
1847
- border: 1px solid #888;
1848
- width: 250px;
1849
- text-align: left;
1850
- background: var(--background-color);
1851
- border-radius: 5px;
1852
- box-shadow: 0 0 11px #aaa;
1853
- text-align: left;
1854
- }
1855
-
1856
- .modal-header {
1857
- display: grid;
1858
- grid-template-columns: 1fr auto;
1859
- color: var(--main-text-color);
1860
- align-items: baseline;
1861
- }
1862
-
1863
- .modal-header h2 {
1864
- margin: 0;
1865
- text-align: left;
1866
- }
1867
-
1868
- .modal-body {
1869
- display: grid;
1870
- grid-auto-flow: row;
1871
- gap: 8px;
1872
- }
1873
-
1874
- .modal-body a {
1875
- /* text-decoration: none; */
1876
- color: var(--summer-sun);
1877
- }
1878
-
1879
- .modal-close-button {
1880
- margin: 0;
1881
- font-size: 20px;
1882
- background: none;
1883
- border: none;
1884
- color: var(--summer-sun);
1885
- }
1886
-
1887
- .modal-close-button:hover,
1888
- .modal-close-button:focus {
1889
- color: #000;
1890
- text-decoration: none;
1891
- cursor: pointer;
1892
- }
1893
-
1894
- #new-conversation-form {
1895
- display: flex;
1896
- flex-direction: column;
1897
- }
1898
-
1899
- #new-conversation-form label,
1900
- #new-conversation-form input,
1901
- #new-conversation-form button {
1902
- margin-bottom: 10px;
1903
- }
1904
-
1905
- #new-conversation-form button {
1906
- cursor: pointer;
1907
- }
1908
-
1909
- .modal-footer {
1910
- display: grid;
1911
- grid-template-columns: 1fr 1fr;
1912
- grid-gap: 12px;
1913
- }
1914
-
1915
- .modal-body button {
1916
- cursor: pointer;
1917
- border-radius: 12px;
1918
- padding: 8px;
1919
- border: 1px solid var(--main-text-color);
1920
- }
1921
-
1922
- button#new-conversation-submit-button {
1923
- background: var(--summer-sun);
1924
- transition: background 0.2s ease-in-out;
1925
- }
1926
-
1927
- button#close-button {
1928
- background: var(--background-color);
1929
- transition: background 0.2s ease-in-out;
1930
- }
1931
-
1932
- button#new-conversation-submit-button:hover {
1933
- background: var(--primary);
1934
- }
1935
-
1936
- button#close-button:hover {
1937
- background: var(--primary-hover);
1938
- }
1939
-
1940
- .modal-body select {
1941
- padding: 8px;
1942
- border-radius: 12px;
1943
- border: 1px solid var(--main-text-color);
1944
- }
1945
-
1946
-
1947
- .lds-ellipsis {
1948
- display: inline-block;
1949
- position: relative;
1950
- width: 60px;
1951
- height: 32px;
1952
- }
1953
- .lds-ellipsis div {
1954
- position: absolute;
1955
- top: 12px;
1956
- width: 8px;
1957
- height: 8px;
1958
- border-radius: 50%;
1959
- background: var(--main-text-color);
1960
- animation-timing-function: cubic-bezier(0, 1, 1, 0);
1961
- }
1962
- .lds-ellipsis div:nth-child(1) {
1963
- left: 8px;
1964
- animation: lds-ellipsis1 0.6s infinite;
1965
- }
1966
- .lds-ellipsis div:nth-child(2) {
1967
- left: 8px;
1968
- animation: lds-ellipsis2 0.6s infinite;
1969
- }
1970
- .lds-ellipsis div:nth-child(3) {
1971
- left: 32px;
1972
- animation: lds-ellipsis2 0.6s infinite;
1973
- }
1974
- .lds-ellipsis div:nth-child(4) {
1975
- left: 56px;
1976
- animation: lds-ellipsis3 0.6s infinite;
1977
- }
1978
- @keyframes lds-ellipsis1 {
1979
- 0% {
1980
- transform: scale(0);
1981
- }
1982
- 100% {
1983
- transform: scale(1);
1984
- }
1985
- }
1986
- @keyframes lds-ellipsis3 {
1987
- 0% {
1988
- transform: scale(1);
1989
- }
1990
- 100% {
1991
- transform: scale(0);
1992
- }
1993
- }
1994
- @keyframes lds-ellipsis2 {
1995
- 0% {
1996
- transform: translate(0, 0);
1997
- }
1998
- 100% {
1999
- transform: translate(24px, 0);
2000
- }
2001
- }
2002
-
2003
-
2004
- </style>
2005
- <script src="https://accounts.google.com/gsi/client" async defer></script>
2006
- </html>