Merge branch 'features/multi-user-support-khoj' of github.com:khoj-ai/khoj into fix-improve-config-page-on-desktop-and-web-app

This commit is contained in:
sabaimran 2023-11-07 12:46:49 -08:00
commit 08c86927cb
2 changed files with 388 additions and 41 deletions

View file

@ -34,32 +34,101 @@
let escaped_ref = reference.replaceAll('"', '"'); let escaped_ref = reference.replaceAll('"', '"');
// Generate HTML for Chat Reference // Generate HTML for Chat Reference
return `<sup><abbr title="${escaped_ref}" tabindex="0">${index}</abbr></sup>`; let short_ref = escaped_ref.slice(0, 100);
short_ref = short_ref.length < escaped_ref.length ? short_ref + "..." : short_ref;
let referenceButton = document.createElement('button');
referenceButton.innerHTML = short_ref;
referenceButton.id = `ref-${index}`;
referenceButton.classList.add("reference-button");
referenceButton.classList.add("collapsed");
referenceButton.tabIndex = 0;
// Add event listener to toggle full reference on click
referenceButton.addEventListener('click', function() {
console.log(`Toggling ref-${index}`)
if (this.classList.contains("collapsed")) {
this.classList.remove("collapsed");
this.classList.add("expanded");
this.innerHTML = escaped_ref;
} else {
this.classList.add("collapsed");
this.classList.remove("expanded");
this.innerHTML = short_ref;
}
});
return referenceButton;
} }
function renderMessage(message, by, dt=null) { function renderMessage(message, by, dt=null, annotations=null) {
let message_time = formatDate(dt ?? new Date()); let message_time = formatDate(dt ?? new Date());
let by_name = by == "khoj" ? "🏮 Khoj" : "🤔 You"; let by_name = by == "khoj" ? "🏮 Khoj" : "🤔 You";
let formattedMessage = formatHTMLMessage(message); let formattedMessage = formatHTMLMessage(message);
// Generate HTML for Chat Message and Append to Chat Body let chatBody = document.getElementById("chat-body");
document.getElementById("chat-body").innerHTML += `
<div data-meta="${by_name} at ${message_time}" class="chat-message ${by}"> // Create a new div for the chat message
<div class="chat-message-text ${by}">${formattedMessage}</div> let chatMessage = document.createElement('div');
</div> chatMessage.className = `chat-message ${by}`;
`; chatMessage.dataset.meta = `${by_name} at ${message_time}`;
// Create a new div for the chat message text and append it to the chat message
let chatMessageText = document.createElement('div');
chatMessageText.className = `chat-message-text ${by}`;
chatMessageText.innerHTML = formattedMessage;
chatMessage.appendChild(chatMessageText);
// Append annotations div to the chat message
if (annotations) {
chatMessageText.appendChild(annotations);
}
// Append chat message div to chat body
chatBody.appendChild(chatMessage);
// Scroll to bottom of chat-body element // Scroll to bottom of chat-body element
document.getElementById("chat-body").scrollTop = document.getElementById("chat-body").scrollHeight; chatBody.scrollTop = chatBody.scrollHeight;
} }
function renderMessageWithReference(message, by, context=null, dt=null) { function renderMessageWithReference(message, by, context=null, dt=null) {
let references = ''; if (context == null || context.length == 0) {
if (context) { renderMessage(message, by, dt);
references = context return;
.map((reference, index) => generateReference(reference, index))
.join("<sup>,</sup>");
} }
renderMessage(message+references, by, dt); let references = document.createElement('div');
let referenceExpandButton = document.createElement('button');
referenceExpandButton.classList.add("reference-expand-button");
let expandButtonText = context.length == 1 ? "1 reference" : `${context.length} references`;
referenceExpandButton.innerHTML = expandButtonText;
references.appendChild(referenceExpandButton);
let referenceSection = document.createElement('div');
referenceSection.classList.add("reference-section");
referenceSection.classList.add("collapsed");
referenceExpandButton.addEventListener('click', function() {
if (referenceSection.classList.contains("collapsed")) {
referenceSection.classList.remove("collapsed");
referenceSection.classList.add("expanded");
} else {
referenceSection.classList.add("collapsed");
referenceSection.classList.remove("expanded");
}
});
references.classList.add("references");
if (context) {
for (let index in context) {
let reference = context[index];
let polishedReference = generateReference(reference, index);
referenceSection.appendChild(polishedReference);
}
}
references.appendChild(referenceSection);
renderMessage(message, by, dt, references);
} }
function formatHTMLMessage(htmlMessage) { function formatHTMLMessage(htmlMessage) {
@ -120,17 +189,19 @@
// Call specified Khoj API which returns a streamed response of type text/plain // Call specified Khoj API which returns a streamed response of type text/plain
fetch(url, { headers }) fetch(url, { headers })
.then(response => { .then(response => {
const reader = response.body.getReader(); const reader = response.body.getReader();
const decoder = new TextDecoder(); const decoder = new TextDecoder();
function readStream() { function readStream() {
let references = null;
reader.read().then(({ done, value }) => { reader.read().then(({ done, value }) => {
if (done) { if (done) {
// Evaluate the contents of new_response_text.innerHTML after all the data has been streamed // Evaluate the contents of new_response_text.innerHTML after all the data has been streamed
const currentHTML = newResponseText.innerHTML; const currentHTML = newResponseText.innerHTML;
newResponseText.innerHTML = formatHTMLMessage(currentHTML); newResponseText.innerHTML = formatHTMLMessage(currentHTML);
newResponseText.appendChild(references);
document.getElementById("chat-body").scrollTop = document.getElementById("chat-body").scrollHeight;
return; return;
} }
@ -143,11 +214,36 @@
const rawReference = chunk.split("### compiled references:")[1]; const rawReference = chunk.split("### compiled references:")[1];
const rawReferenceAsJson = JSON.parse(rawReference); const rawReferenceAsJson = JSON.parse(rawReference);
let polishedReference = rawReferenceAsJson.map((reference, index) => generateReference(reference, index)) references = document.createElement('div');
.join("<sup>,</sup>"); references.classList.add("references");
newResponseText.innerHTML += polishedReference;
document.getElementById("chat-body").scrollTop = document.getElementById("chat-body").scrollHeight; let referenceExpandButton = document.createElement('button');
referenceExpandButton.classList.add("reference-expand-button");
let expandButtonText = rawReferenceAsJson.length == 1 ? "1 reference" : `${rawReferenceAsJson.length} references`;
referenceExpandButton.innerHTML = expandButtonText;
references.appendChild(referenceExpandButton);
let referenceSection = document.createElement('div');
referenceSection.classList.add("reference-section");
referenceSection.classList.add("collapsed");
referenceExpandButton.addEventListener('click', function() {
if (referenceSection.classList.contains("collapsed")) {
referenceSection.classList.remove("collapsed");
referenceSection.classList.add("expanded");
} else {
referenceSection.classList.add("collapsed");
referenceSection.classList.remove("expanded");
}
});
rawReferenceAsJson.forEach((reference, index) => {
let polishedReference = generateReference(reference, index);
referenceSection.appendChild(polishedReference);
});
references.appendChild(referenceSection);
readStream(); readStream();
} else { } else {
// Display response from Khoj // Display response from Khoj
@ -443,6 +539,83 @@
box-shadow: 0 0 12px rgb(119, 156, 46); box-shadow: 0 0 12px rgb(119, 156, 46);
} }
div.collapsed {
display: none;
}
div.expanded {
display: block;
}
div.reference {
display: grid;
grid-template-rows: auto;
grid-auto-flow: row;
grid-column-gap: 10px;
grid-row-gap: 10px;
margin: 10px;
}
div.expanded.reference-section {
display: grid;
grid-template-rows: auto;
grid-auto-flow: row;
grid-column-gap: 10px;
grid-row-gap: 10px;
margin: 10px;
}
button.reference-button {
background: var(--background-color);
color: var(--main-text-color);
border: 1px solid var(--main-text-color);
border-radius: 5px;
padding: 5px;
font-size: 14px;
font-weight: 300;
line-height: 1.5em;
cursor: pointer;
transition: background 0.2s ease-in-out;
text-align: left;
max-height: 50px;
transition: max-height 0.3s ease-in-out;
overflow: hidden;
}
button.reference-button.expanded {
max-height: 200px;
}
button.reference-button::before {
content: "▶";
margin-right: 5px;
display: inline-block;
transition: transform 0.3s ease-in-out;
}
button.reference-button:active:before,
button.reference-button[aria-expanded="true"]::before {
transform: rotate(90deg);
}
button.reference-expand-button {
background: var(--background-color);
color: var(--main-text-color);
border: 1px dotted var(--main-text-color);
border-radius: 5px;
padding: 5px;
font-size: 14px;
font-weight: 300;
line-height: 1.5em;
cursor: pointer;
transition: background 0.4s ease-in-out;
text-align: left;
}
button.reference-expand-button:hover {
background: var(--primary-hover);
}
.option-enabled:focus { .option-enabled:focus {
outline: none !important; outline: none !important;
border:1px solid #475569; border:1px solid #475569;

View file

@ -33,32 +33,101 @@
let escaped_ref = reference.replaceAll('"', '&quot;'); let escaped_ref = reference.replaceAll('"', '&quot;');
// Generate HTML for Chat Reference // Generate HTML for Chat Reference
return `<sup><abbr title="${escaped_ref}" tabindex="0">${index}</abbr></sup>`; let short_ref = escaped_ref.slice(0, 100);
short_ref = short_ref.length < escaped_ref.length ? short_ref + "..." : short_ref;
let referenceButton = document.createElement('button');
referenceButton.innerHTML = short_ref;
referenceButton.id = `ref-${index}`;
referenceButton.classList.add("reference-button");
referenceButton.classList.add("collapsed");
referenceButton.tabIndex = 0;
// Add event listener to toggle full reference on click
referenceButton.addEventListener('click', function() {
console.log(`Toggling ref-${index}`)
if (this.classList.contains("collapsed")) {
this.classList.remove("collapsed");
this.classList.add("expanded");
this.innerHTML = escaped_ref;
} else {
this.classList.add("collapsed");
this.classList.remove("expanded");
this.innerHTML = short_ref;
}
});
return referenceButton;
} }
function renderMessage(message, by, dt=null) { function renderMessage(message, by, dt=null, annotations=null) {
let message_time = formatDate(dt ?? new Date()); let message_time = formatDate(dt ?? new Date());
let by_name = by == "khoj" ? "🏮 Khoj" : "🤔 You"; let by_name = by == "khoj" ? "🏮 Khoj" : "🤔 You";
let formattedMessage = formatHTMLMessage(message); let formattedMessage = formatHTMLMessage(message);
// Generate HTML for Chat Message and Append to Chat Body let chatBody = document.getElementById("chat-body");
document.getElementById("chat-body").innerHTML += `
<div data-meta="${by_name} at ${message_time}" class="chat-message ${by}"> // Create a new div for the chat message
<div class="chat-message-text ${by}">${formattedMessage}</div> let chatMessage = document.createElement('div');
</div> chatMessage.className = `chat-message ${by}`;
`; chatMessage.dataset.meta = `${by_name} at ${message_time}`;
// Create a new div for the chat message text and append it to the chat message
let chatMessageText = document.createElement('div');
chatMessageText.className = `chat-message-text ${by}`;
chatMessageText.innerHTML = formattedMessage;
chatMessage.appendChild(chatMessageText);
// Append annotations div to the chat message
if (annotations) {
chatMessageText.appendChild(annotations);
}
// Append chat message div to chat body
chatBody.appendChild(chatMessage);
// Scroll to bottom of chat-body element // Scroll to bottom of chat-body element
document.getElementById("chat-body").scrollTop = document.getElementById("chat-body").scrollHeight; chatBody.scrollTop = chatBody.scrollHeight;
} }
function renderMessageWithReference(message, by, context=null, dt=null) { function renderMessageWithReference(message, by, context=null, dt=null) {
let references = ''; if (context == null || context.length == 0) {
if (context) { renderMessage(message, by, dt);
references = context return;
.map((reference, index) => generateReference(reference, index))
.join("<sup>,</sup>");
} }
renderMessage(message+references, by, dt); let references = document.createElement('div');
let referenceExpandButton = document.createElement('button');
referenceExpandButton.classList.add("reference-expand-button");
let expandButtonText = context.length == 1 ? "1 reference" : `${context.length} references`;
referenceExpandButton.innerHTML = expandButtonText;
references.appendChild(referenceExpandButton);
let referenceSection = document.createElement('div');
referenceSection.classList.add("reference-section");
referenceSection.classList.add("collapsed");
referenceExpandButton.addEventListener('click', function() {
if (referenceSection.classList.contains("collapsed")) {
referenceSection.classList.remove("collapsed");
referenceSection.classList.add("expanded");
} else {
referenceSection.classList.add("collapsed");
referenceSection.classList.remove("expanded");
}
});
references.classList.add("references");
if (context) {
for (let index in context) {
let reference = context[index];
let polishedReference = generateReference(reference, index);
referenceSection.appendChild(polishedReference);
}
}
references.appendChild(referenceSection);
renderMessage(message, by, dt, references);
} }
function formatHTMLMessage(htmlMessage) { function formatHTMLMessage(htmlMessage) {
@ -120,12 +189,14 @@
const decoder = new TextDecoder(); const decoder = new TextDecoder();
function readStream() { function readStream() {
let references = null;
reader.read().then(({ done, value }) => { reader.read().then(({ done, value }) => {
if (done) { if (done) {
// Evaluate the contents of new_response_text.innerHTML after all the data has been streamed // Evaluate the contents of new_response_text.innerHTML after all the data has been streamed
const currentHTML = newResponseText.innerHTML; const currentHTML = newResponseText.innerHTML;
newResponseText.innerHTML = formatHTMLMessage(currentHTML); newResponseText.innerHTML = formatHTMLMessage(currentHTML);
newResponseText.appendChild(references);
document.getElementById("chat-body").scrollTop = document.getElementById("chat-body").scrollHeight;
return; return;
} }
@ -138,11 +209,36 @@
const rawReference = chunk.split("### compiled references:")[1]; const rawReference = chunk.split("### compiled references:")[1];
const rawReferenceAsJson = JSON.parse(rawReference); const rawReferenceAsJson = JSON.parse(rawReference);
let polishedReference = rawReferenceAsJson.map((reference, index) => generateReference(reference, index)) references = document.createElement('div');
.join("<sup>,</sup>"); references.classList.add("references");
newResponseText.innerHTML += polishedReference;
document.getElementById("chat-body").scrollTop = document.getElementById("chat-body").scrollHeight; let referenceExpandButton = document.createElement('button');
referenceExpandButton.classList.add("reference-expand-button");
let expandButtonText = rawReferenceAsJson.length == 1 ? "1 reference" : `${rawReferenceAsJson.length} references`;
referenceExpandButton.innerHTML = expandButtonText;
references.appendChild(referenceExpandButton);
let referenceSection = document.createElement('div');
referenceSection.classList.add("reference-section");
referenceSection.classList.add("collapsed");
referenceExpandButton.addEventListener('click', function() {
if (referenceSection.classList.contains("collapsed")) {
referenceSection.classList.remove("collapsed");
referenceSection.classList.add("expanded");
} else {
referenceSection.classList.add("collapsed");
referenceSection.classList.remove("expanded");
}
});
rawReferenceAsJson.forEach((reference, index) => {
let polishedReference = generateReference(reference, index);
referenceSection.appendChild(polishedReference);
});
references.appendChild(referenceSection);
readStream(); readStream();
} else { } else {
// Display response from Khoj // Display response from Khoj
@ -237,6 +333,7 @@
}); });
}) })
.catch(err => { .catch(err => {
console.log(err);
return; return;
}); });
@ -299,6 +396,83 @@
padding: 10px; padding: 10px;
margin: 10px; margin: 10px;
} }
div.collapsed {
display: none;
}
div.expanded {
display: block;
}
div.reference {
display: grid;
grid-template-rows: auto;
grid-auto-flow: row;
grid-column-gap: 10px;
grid-row-gap: 10px;
margin: 10px;
}
div.expanded.reference-section {
display: grid;
grid-template-rows: auto;
grid-auto-flow: row;
grid-column-gap: 10px;
grid-row-gap: 10px;
margin: 10px;
}
button.reference-button {
background: var(--background-color);
color: var(--main-text-color);
border: 1px solid var(--main-text-color);
border-radius: 5px;
padding: 5px;
font-size: 14px;
font-weight: 300;
line-height: 1.5em;
cursor: pointer;
transition: background 0.2s ease-in-out;
text-align: left;
max-height: 50px;
transition: max-height 0.3s ease-in-out;
overflow: hidden;
}
button.reference-button.expanded {
max-height: 200px;
}
button.reference-button::before {
content: "▶";
margin-right: 5px;
display: inline-block;
transition: transform 0.3s ease-in-out;
}
button.reference-button:active:before,
button.reference-button[aria-expanded="true"]::before {
transform: rotate(90deg);
}
button.reference-expand-button {
background: var(--background-color);
color: var(--main-text-color);
border: 1px dotted var(--main-text-color);
border-radius: 5px;
padding: 5px;
font-size: 14px;
font-weight: 300;
line-height: 1.5em;
cursor: pointer;
transition: background 0.4s ease-in-out;
text-align: left;
}
button.reference-expand-button:hover {
background: var(--primary-hover);
}
#chat-body { #chat-body {
font-size: medium; font-size: medium;
margin: 0px; margin: 0px;