Fix and Improve Chat UI in Web, Desktop apps (#655)
### Improvements to Chat UI on Web, Desktop apps - Improve styling of chat session side panel - Improve styling of chat message bubble in Desktop, Web app - Add frosted, minimal chat UI to background of Login screen - Improve PWA install experience of Khoj ### Fixes to Chat UI on Web, Desktop apps - Fix creating new chat sessions from the Desktop app - Only show 3 starter questions even when consecutive chat sessions created ### Other Improvements - Update Khoj cloud trial period to a fortnight instead of a week - Document using venv to handle dependency conflict on khoj pip install Resolves #276
@@ -15,7 +15,7 @@ import TabItem from '@theme/TabItem';
|
||||
```
|
||||
|
||||
## Setup
|
||||
These are the general setup instructions for Khoj.
|
||||
These are the general setup instructions for self-hosted Khoj.
|
||||
|
||||
- Make sure [python](https://realpython.com/installing-python/) and [pip](https://pip.pypa.io/en/stable/installation/) are installed on your machine
|
||||
- Check the [Khoj Emacs docs](/clients/emacs#setup) to setup Khoj with Emacs<br />
|
||||
@@ -23,7 +23,7 @@ These are the general setup instructions for Khoj.
|
||||
- Check the [Khoj Obsidian docs](/clients/obsidian#setup) to setup Khoj with Obsidian<br />
|
||||
Its simpler as it can skip the *configure* step below.
|
||||
|
||||
For Installation, you can either use Docker or install Khoj locally.
|
||||
For Installation, you can either use Docker or install the Khoj server locally.
|
||||
|
||||
### Installation Option 1 (Docker)
|
||||
|
||||
@@ -267,6 +267,18 @@ You can head to http://localhost:42110 to use the web interface. You can also us
|
||||
|
||||
## Troubleshoot
|
||||
|
||||
#### Dependency conflict when trying to install Khoj python package with pip
|
||||
- **Reason**: When conflicting dependency versions are required by Khoj vs other python packages installed on your system
|
||||
- **Fix**: Install Khoj in a python virtual environment using [venv](https://docs.python.org/3/library/venv.html) or [pipx](https://pypa.github.io/pipx) to avoid this dependency conflicts
|
||||
- **Process**:
|
||||
1. Install [pipx](https://pypa.github.io/pipx/#install-pipx)
|
||||
2. Use `pipx` to install Khoj to avoid dependency conflicts with other python packages.
|
||||
```shell
|
||||
pipx install khoj-assistant
|
||||
```
|
||||
3. Now start `khoj` using the standard steps described earlier
|
||||
|
||||
|
||||
#### Install fails while building Tokenizer dependency
|
||||
- **Details**: `pip install khoj-assistant` fails while building the `tokenizers` dependency. Complains about Rust.
|
||||
- **Fix**: Install Rust to build the tokenizers package. For example on Mac run:
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<title>Khoj - About</title>
|
||||
|
||||
<link rel="icon" type="image/png" sizes="128x128" href="./assets/icons/favicon-128x128.png">
|
||||
<link rel="manifest" href="/static/khoj_chat.webmanifest">
|
||||
<link rel="manifest" href="/static/khoj.webmanifest">
|
||||
<link rel="stylesheet" href="./assets/khoj.css">
|
||||
</head>
|
||||
<script type="text/javascript" src="./utils.js"></script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* Amber Light scheme (Default) */
|
||||
/* Can be forced with data-theme="light" */
|
||||
@import url('https://fonts.googleapis.com/css2?family=Tajawal&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Tajawal:wght@300;500;700&display=swap');
|
||||
|
||||
[data-theme="light"],
|
||||
:root:not([data-theme="dark"]) {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<title>Khoj - Chat</title>
|
||||
|
||||
<link rel="icon" type="image/png" sizes="128x128" href="./assets/icons/favicon-128x128.png">
|
||||
<link rel="manifest" href="/static/khoj_chat.webmanifest">
|
||||
<link rel="manifest" href="/static/khoj.webmanifest">
|
||||
<link rel="stylesheet" href="./assets/khoj.css">
|
||||
</head>
|
||||
<script type="text/javascript" src="./assets/markdown-it.min.js"></script>
|
||||
@@ -104,7 +104,7 @@
|
||||
linkElement.classList.add("inline-chat-link");
|
||||
linkElement.classList.add("reference-link");
|
||||
linkElement.setAttribute('title', title);
|
||||
linkElement.innerHTML = title;
|
||||
linkElement.textContent = title;
|
||||
|
||||
let referenceButton = document.createElement('button');
|
||||
referenceButton.innerHTML = linkElement.outerHTML;
|
||||
@@ -133,7 +133,6 @@
|
||||
let message_time = formatDate(dt ?? new Date());
|
||||
let by_name = by == "khoj" ? "🏮 Khoj" : "🤔 You";
|
||||
let formattedMessage = formatHTMLMessage(message, raw);
|
||||
let chatBody = document.getElementById("chat-body");
|
||||
|
||||
// Create a new div for the chat message
|
||||
let chatMessage = document.createElement('div');
|
||||
@@ -152,6 +151,7 @@
|
||||
}
|
||||
|
||||
// Append chat message div to chat body
|
||||
let chatBody = document.getElementById("chat-body");
|
||||
chatBody.appendChild(chatMessage);
|
||||
|
||||
// Scroll to bottom of chat-body element
|
||||
@@ -285,9 +285,12 @@
|
||||
|
||||
// Render markdown
|
||||
newHTML = raw ? newHTML : md.render(newHTML);
|
||||
// Get any elements with a class that starts with "language"
|
||||
// Set rendered markdown to HTML DOM element
|
||||
let element = document.createElement('div');
|
||||
element.innerHTML = newHTML;
|
||||
element.className = "chat-message-text-response";
|
||||
|
||||
// Get any elements with a class that starts with "language"
|
||||
let codeBlockElements = element.querySelectorAll('[class^="language-"]');
|
||||
// For each element, add a parent div with the class "programmatic-output"
|
||||
codeBlockElements.forEach((codeElement) => {
|
||||
@@ -341,22 +344,20 @@
|
||||
let chat_body = document.getElementById("chat-body");
|
||||
|
||||
let conversationID = chat_body.dataset.conversationId;
|
||||
|
||||
let hostURL = await window.hostURLAPI.getURL();
|
||||
const khojToken = await window.tokenAPI.getToken();
|
||||
const headers = { 'Authorization': `Bearer ${khojToken}` };
|
||||
|
||||
if (!conversationID) {
|
||||
let response = await fetch(`${hostURL}/api/chat/sessions`, { method: "POST" });
|
||||
let response = await fetch(`${hostURL}/api/chat/sessions`, { method: "POST", headers });
|
||||
let data = await response.json();
|
||||
conversationID = data.conversation_id;
|
||||
chat_body.dataset.conversationId = conversationID;
|
||||
await refreshChatSessionsPanel();
|
||||
}
|
||||
|
||||
|
||||
// Generate backend API URL to execute query
|
||||
let url = `${hostURL}/api/chat?q=${encodeURIComponent(query)}&n=${resultsCount}&client=web&stream=true&conversation_id=${conversationID}®ion=${region}&city=${city}&country=${countryName}`;
|
||||
const khojToken = await window.tokenAPI.getToken();
|
||||
const headers = { 'Authorization': `Bearer ${khojToken}` };
|
||||
let chatApi = `${hostURL}/api/chat?q=${encodeURIComponent(query)}&n=${resultsCount}&client=web&stream=true&conversation_id=${conversationID}®ion=${region}&city=${city}&country=${countryName}`;
|
||||
|
||||
let new_response = document.createElement("div");
|
||||
new_response.classList.add("chat-message", "khoj");
|
||||
@@ -379,8 +380,8 @@
|
||||
let chatInput = document.getElementById("chat-input");
|
||||
chatInput.classList.remove("option-enabled");
|
||||
|
||||
// Call specified Khoj API
|
||||
let response = await fetch(url, { headers });
|
||||
// Call Khoj chat API
|
||||
let response = await fetch(chatApi, { headers });
|
||||
let rawResponse = "";
|
||||
const contentType = response.headers.get("content-type");
|
||||
|
||||
@@ -540,6 +541,7 @@
|
||||
chatInput.value = chatInput.value.trimStart();
|
||||
|
||||
let questionStarterSuggestions = document.getElementById("question-starters");
|
||||
questionStarterSuggestions.innerHTML = "";
|
||||
questionStarterSuggestions.style.display = "none";
|
||||
|
||||
if (chatInput.value.startsWith("/") && chatInput.value.split(" ").length === 1) {
|
||||
@@ -591,6 +593,7 @@
|
||||
const headers = { 'Authorization': `Bearer ${khojToken}` };
|
||||
|
||||
let chatBody = document.getElementById("chat-body");
|
||||
chatBody.innerHTML = "";
|
||||
let conversationId = chatBody.dataset.conversationId;
|
||||
let chatHistoryUrl = `/api/chat/history?client=desktop`;
|
||||
if (conversationId) {
|
||||
@@ -671,11 +674,11 @@
|
||||
fetch(`${hostURL}/api/chat/starters?client=desktop`, { headers })
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// Render chat options, if any
|
||||
// Render conversation starters, if any
|
||||
if (data.length > 0) {
|
||||
let questionStarterSuggestions = document.getElementById("question-starters");
|
||||
for (let index in data) {
|
||||
let questionStarter = data[index];
|
||||
questionStarterSuggestions.innerHTML = "";
|
||||
data.forEach((questionStarter) => {
|
||||
let questionStarterButton = document.createElement('button');
|
||||
questionStarterButton.innerHTML = questionStarter;
|
||||
questionStarterButton.classList.add("question-starter");
|
||||
@@ -685,7 +688,7 @@
|
||||
chat();
|
||||
});
|
||||
questionStarterSuggestions.appendChild(questionStarterButton);
|
||||
}
|
||||
});
|
||||
questionStarterSuggestions.style.display = "grid";
|
||||
}
|
||||
})
|
||||
@@ -785,7 +788,7 @@
|
||||
let conversationButton = document.createElement('div');
|
||||
let incomingConversationId = conversation["conversation_id"];
|
||||
const conversationTitle = conversation["slug"] || `New conversation 🌱`;
|
||||
conversationButton.innerHTML = conversationTitle;
|
||||
conversationButton.textContent = conversationTitle;
|
||||
conversationButton.classList.add("conversation-button");
|
||||
if (incomingConversationId == conversationId) {
|
||||
conversationButton.classList.add("selected-conversation");
|
||||
@@ -883,7 +886,7 @@
|
||||
fetch(`${hostURL}${editURL}` , { method: "PATCH" })
|
||||
.then(response => response.ok ? response.json() : Promise.reject(response))
|
||||
.then(data => {
|
||||
conversationButton.innerHTML = newTitle;
|
||||
conversationButton.textContent = newTitle;
|
||||
})
|
||||
.catch(err => {
|
||||
return;
|
||||
@@ -1014,6 +1017,7 @@
|
||||
document.getElementById('side-panel').classList.toggle('collapsed');
|
||||
document.getElementById('new-conversation').classList.toggle('collapsed');
|
||||
document.getElementById('existing-conversations').classList.toggle('collapsed');
|
||||
document.getElementById('side-panel-collapse').style.transform = document.getElementById('side-panel').classList.contains('collapsed') ? 'rotate(0deg)' : 'rotate(180deg)';
|
||||
|
||||
document.getElementById('chat-section-wrapper').classList.toggle('mobile-friendly');
|
||||
}
|
||||
@@ -1058,7 +1062,7 @@
|
||||
id="collapse-side-panel-button"
|
||||
onclick="handleCollapseSidePanel()"
|
||||
>
|
||||
<svg class="side-panel-collapse" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg id="side-panel-collapse" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<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"/>
|
||||
</svg>
|
||||
</button>
|
||||
@@ -1161,6 +1165,7 @@
|
||||
}
|
||||
|
||||
#side-panel {
|
||||
width: 250px;
|
||||
padding: 10px;
|
||||
background: var(--background-color);
|
||||
border-radius: 5px;
|
||||
@@ -1168,11 +1173,11 @@
|
||||
overflow-y: scroll;
|
||||
text-align: left;
|
||||
transition: width 0.3s ease-in-out;
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
div#side-panel.collapsed {
|
||||
width: 1px;
|
||||
width: 0;
|
||||
padding: 0;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -1227,6 +1232,7 @@
|
||||
display: inline-block;
|
||||
max-width: 80%;
|
||||
text-align: left;
|
||||
white-space: pre-line;
|
||||
}
|
||||
/* color chat bubble by khoj blue */
|
||||
.chat-message-text.khoj {
|
||||
@@ -1234,6 +1240,15 @@
|
||||
background: var(--primary);
|
||||
margin-left: auto;
|
||||
}
|
||||
.chat-message-text ol,
|
||||
.chat-message-text ul {
|
||||
white-space: normal;
|
||||
margin: 0;
|
||||
}
|
||||
.chat-message-text-response {
|
||||
margin-bottom: -16px;
|
||||
}
|
||||
|
||||
/* Spinner symbol when the chat message is loading */
|
||||
.spinner {
|
||||
border: 4px solid #f3f3f3;
|
||||
@@ -1350,7 +1365,7 @@
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
svg.side-panel-collapse {
|
||||
svg#side-panel-collapse {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
@@ -1604,7 +1619,7 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
svg.side-panel-collapse {
|
||||
svg#side-panel-collapse {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ def subscription_to_state(subscription: Subscription) -> str:
|
||||
return SubscriptionState.INVALID.value
|
||||
elif subscription.type == Subscription.Type.TRIAL:
|
||||
# Trial subscription is valid for 7 days
|
||||
if datetime.now(tz=timezone.utc) - subscription.created_at > timedelta(days=7):
|
||||
if datetime.now(tz=timezone.utc) - subscription.created_at > timedelta(days=14):
|
||||
return SubscriptionState.EXPIRED.value
|
||||
|
||||
return SubscriptionState.TRIAL.value
|
||||
@@ -573,7 +573,7 @@ class ConversationAdapters:
|
||||
return await SpeechToTextModelOptions.objects.filter().afirst()
|
||||
|
||||
@staticmethod
|
||||
async def aget_conversation_starters(user: KhojUser):
|
||||
async def aget_conversation_starters(user: KhojUser, max_results=3):
|
||||
all_questions = []
|
||||
if await ReflectiveQuestion.objects.filter(user=user).aexists():
|
||||
all_questions = await sync_to_async(ReflectiveQuestion.objects.filter(user=user).values_list)(
|
||||
@@ -584,7 +584,6 @@ class ConversationAdapters:
|
||||
"question", flat=True
|
||||
)
|
||||
|
||||
max_results = 3
|
||||
all_questions = await sync_to_async(list)(all_questions) # type: ignore
|
||||
if len(all_questions) < max_results:
|
||||
return all_questions
|
||||
|
||||
BIN
src/khoj/interface/web/assets/icons/favicon-256x256.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
@@ -1,6 +1,6 @@
|
||||
/* Amber Light scheme (Default) */
|
||||
/* Can be forced with data-theme="light" */
|
||||
@import url('https://fonts.googleapis.com/css2?family=Tajawal&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Tajawal:wght@300;500;700&display=swap');
|
||||
|
||||
[data-theme="light"],
|
||||
:root:not([data-theme="dark"]) {
|
||||
@@ -9,6 +9,7 @@
|
||||
--primary-focus: rgba(255, 179, 0, 0.125);
|
||||
--primary-inverse: rgba(0, 0, 0, 0.75);
|
||||
--background-color: #f5f4f3;
|
||||
--frosted-background-color: rgba(245, 244, 243, 0.75);
|
||||
--main-text-color: #475569;
|
||||
--summer-sun: #fcc50b;
|
||||
--water: #44b9da;
|
||||
@@ -26,6 +27,7 @@
|
||||
--primary-focus: rgba(255, 179, 0, 0.25);
|
||||
--primary-inverse: rgba(0, 0, 0, 0.75);
|
||||
--background-color: #f5f4f3;
|
||||
--frosted-background-color: rgba(245, 244, 243, 0.75);
|
||||
--main-text-color: #475569;
|
||||
--summer-sun: #fcc50b;
|
||||
--water: #44b9da;
|
||||
@@ -42,6 +44,7 @@
|
||||
--primary-focus: rgba(255, 179, 0, 0.25);
|
||||
--primary-inverse: rgba(0, 0, 0, 0.75);
|
||||
--background-color: #f5f4f3;
|
||||
--frosted-background-color: rgba(245, 244, 243, 0.75);
|
||||
--main-text-color: #475569;
|
||||
--summer-sun: #fcc50b;
|
||||
--water: #44b9da;
|
||||
|
||||
|
After Width: | Height: | Size: 577 KiB |
|
After Width: | Height: | Size: 193 KiB |
|
After Width: | Height: | Size: 262 KiB |
|
After Width: | Height: | Size: 397 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 231 KiB |
@@ -5,7 +5,7 @@
|
||||
<title>Khoj - Chat</title>
|
||||
|
||||
<link rel="icon" type="image/png" sizes="128x128" href="/static/assets/icons/favicon-128x128.png">
|
||||
<link rel="manifest" href="/static/khoj_chat.webmanifest">
|
||||
<link rel="manifest" href="/static/khoj.webmanifest">
|
||||
<link rel="stylesheet" href="/static/assets/khoj.css">
|
||||
</head>
|
||||
<script type="text/javascript" src="/static/assets/utils.js"></script>
|
||||
@@ -116,7 +116,7 @@ To get started, just start typing below. You can also type / to see a list of co
|
||||
linkElement.classList.add("inline-chat-link");
|
||||
linkElement.classList.add("reference-link");
|
||||
linkElement.setAttribute('title', title);
|
||||
linkElement.innerHTML = title;
|
||||
linkElement.textContent = title;
|
||||
|
||||
let referenceButton = document.createElement('button');
|
||||
referenceButton.innerHTML = linkElement.outerHTML;
|
||||
@@ -145,7 +145,6 @@ To get started, just start typing below. You can also type / to see a list of co
|
||||
let message_time = formatDate(dt ?? new Date());
|
||||
let by_name = by == "khoj" ? "🏮 Khoj" : "🤔 You";
|
||||
let formattedMessage = formatHTMLMessage(message, raw);
|
||||
let chatBody = document.getElementById("chat-body");
|
||||
|
||||
// Create a new div for the chat message
|
||||
let chatMessage = document.createElement('div');
|
||||
@@ -164,6 +163,7 @@ To get started, just start typing below. You can also type / to see a list of co
|
||||
}
|
||||
|
||||
// Append chat message div to chat body
|
||||
let chatBody = document.getElementById("chat-body");
|
||||
chatBody.appendChild(chatMessage);
|
||||
|
||||
// Scroll to bottom of chat-body element
|
||||
@@ -297,9 +297,12 @@ To get started, just start typing below. You can also type / to see a list of co
|
||||
|
||||
// Render markdown
|
||||
newHTML = raw ? newHTML : md.render(newHTML);
|
||||
// Get any elements with a class that starts with "language"
|
||||
// Set rendered markdown to HTML DOM element
|
||||
let element = document.createElement('div');
|
||||
element.innerHTML = newHTML;
|
||||
element.className = "chat-message-text-response";
|
||||
|
||||
// Get any elements with a class that starts with "language"
|
||||
let codeBlockElements = element.querySelectorAll('[class^="language-"]');
|
||||
// For each element, add a parent div with the class "programmatic-output"
|
||||
codeBlockElements.forEach((codeElement) => {
|
||||
@@ -520,6 +523,7 @@ To get started, just start typing below. You can also type / to see a list of co
|
||||
chatInput.value = chatInput.value.trimStart();
|
||||
|
||||
let questionStarterSuggestions = document.getElementById("question-starters");
|
||||
questionStarterSuggestions.innerHTML = "";
|
||||
questionStarterSuggestions.style.display = "none";
|
||||
|
||||
if (chatInput.value.startsWith("/") && chatInput.value.split(" ").length === 1) {
|
||||
@@ -565,6 +569,7 @@ To get started, just start typing below. You can also type / to see a list of co
|
||||
|
||||
function loadChat() {
|
||||
let chatBody = document.getElementById("chat-body");
|
||||
chatBody.innerHTML = "";
|
||||
let conversationId = chatBody.dataset.conversationId;
|
||||
let chatHistoryUrl = `/api/chat/history?client=web`;
|
||||
if (conversationId) {
|
||||
@@ -647,8 +652,8 @@ To get started, just start typing below. You can also type / to see a list of co
|
||||
// Render chat options, if any
|
||||
if (data.length > 0) {
|
||||
let questionStarterSuggestions = document.getElementById("question-starters");
|
||||
for (let index in data) {
|
||||
let questionStarter = data[index];
|
||||
questionStarterSuggestions.innerHTML = "";
|
||||
data.forEach((questionStarter) => {
|
||||
let questionStarterButton = document.createElement('button');
|
||||
questionStarterButton.innerHTML = questionStarter;
|
||||
questionStarterButton.classList.add("question-starter");
|
||||
@@ -658,7 +663,7 @@ To get started, just start typing below. You can also type / to see a list of co
|
||||
chat();
|
||||
});
|
||||
questionStarterSuggestions.appendChild(questionStarterButton);
|
||||
}
|
||||
});
|
||||
questionStarterSuggestions.style.display = "grid";
|
||||
}
|
||||
})
|
||||
@@ -713,7 +718,7 @@ To get started, just start typing below. You can also type / to see a list of co
|
||||
let conversationButton = document.createElement('div');
|
||||
let incomingConversationId = conversation["conversation_id"];
|
||||
const conversationTitle = conversation["slug"] || `New conversation 🌱`;
|
||||
conversationButton.innerHTML = conversationTitle;
|
||||
conversationButton.textContent = conversationTitle;
|
||||
conversationButton.classList.add("conversation-button");
|
||||
if (incomingConversationId == conversationId) {
|
||||
conversationButton.classList.add("selected-conversation");
|
||||
@@ -789,7 +794,7 @@ To get started, just start typing below. You can also type / to see a list of co
|
||||
fetch(editURL , { method: "PATCH" })
|
||||
.then(response => response.ok ? response.json() : Promise.reject(response))
|
||||
.then(data => {
|
||||
conversationButton.innerHTML = newTitle;
|
||||
conversationButton.textContent = newTitle;
|
||||
})
|
||||
.catch(err => {
|
||||
return;
|
||||
@@ -966,6 +971,7 @@ To get started, just start typing below. You can also type / to see a list of co
|
||||
document.getElementById('side-panel').classList.toggle('collapsed');
|
||||
document.getElementById('new-conversation').classList.toggle('collapsed');
|
||||
document.getElementById('existing-conversations').classList.toggle('collapsed');
|
||||
document.getElementById('side-panel-collapse').style.transform = document.getElementById('side-panel').classList.contains('collapsed') ? 'rotate(0deg)' : 'rotate(180deg)';
|
||||
|
||||
document.getElementById('chat-section-wrapper').classList.toggle('mobile-friendly');
|
||||
}
|
||||
@@ -1001,7 +1007,7 @@ To get started, just start typing below. You can also type / to see a list of co
|
||||
id="collapse-side-panel-button"
|
||||
onclick="handleCollapseSidePanel()"
|
||||
>
|
||||
<svg class="side-panel-collapse" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg id="side-panel-collapse" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<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"/>
|
||||
</svg>
|
||||
</button>
|
||||
@@ -1226,6 +1232,7 @@ To get started, just start typing below. You can also type / to see a list of co
|
||||
}
|
||||
|
||||
#side-panel {
|
||||
width: 250px;
|
||||
padding: 10px;
|
||||
background: var(--background-color);
|
||||
border-radius: 5px;
|
||||
@@ -1233,11 +1240,11 @@ To get started, just start typing below. You can also type / to see a list of co
|
||||
overflow-y: scroll;
|
||||
text-align: left;
|
||||
transition: width 0.3s ease-in-out;
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
div#side-panel.collapsed {
|
||||
width: 1px;
|
||||
width: 0;
|
||||
padding: 0;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -1291,6 +1298,7 @@ To get started, just start typing below. You can also type / to see a list of co
|
||||
display: inline-block;
|
||||
max-width: 80%;
|
||||
text-align: left;
|
||||
white-space: pre-line;
|
||||
}
|
||||
/* color chat bubble by khoj blue */
|
||||
.chat-message-text.khoj {
|
||||
@@ -1298,6 +1306,15 @@ To get started, just start typing below. You can also type / to see a list of co
|
||||
background: var(--primary);
|
||||
margin-left: auto;
|
||||
}
|
||||
.chat-message-text ol,
|
||||
.chat-message-text ul {
|
||||
white-space: normal;
|
||||
margin: 0;
|
||||
}
|
||||
.chat-message-text-response {
|
||||
margin-bottom: -16px;
|
||||
}
|
||||
|
||||
/* Spinner symbol when the chat message is loading */
|
||||
.spinner {
|
||||
border: 4px solid #f3f3f3;
|
||||
@@ -1412,7 +1429,7 @@ To get started, just start typing below. You can also type / to see a list of co
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
svg.side-panel-collapse {
|
||||
svg#side-panel-collapse {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
@@ -1551,7 +1568,7 @@ To get started, just start typing below. You can also type / to see a list of co
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
svg.side-panel-collapse {
|
||||
svg#side-panel-collapse {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,51 @@
|
||||
{
|
||||
"name": "Khoj",
|
||||
"short_name": "Khoj",
|
||||
"description": "An AI search assistant for your digital brain",
|
||||
"display": "standalone",
|
||||
"start_url": "/",
|
||||
"description": "The open, personal AI for your digital brain. You can ask Khoj to draft a message, paint your imagination, find information on the internet and even answer questions from your documents.",
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/static/assets/icons/favicon-128x128.png",
|
||||
"sizes": "128x128",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/static/assets/icons/favicon-256x256.png",
|
||||
"sizes": "256x256",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone",
|
||||
"start_url": "/"
|
||||
"screenshots": [
|
||||
{
|
||||
"src": "/static/assets/samples/phone-remember-plan-sample.png",
|
||||
"sizes": "419x900",
|
||||
"type": "image/png",
|
||||
"form_factor": "narrow",
|
||||
"label": "Remember and Plan"
|
||||
},
|
||||
{
|
||||
"src": "/static/assets/samples/phone-browse-draw-sample.png",
|
||||
"sizes": "419x900",
|
||||
"type": "image/png",
|
||||
"form_factor": "narrow",
|
||||
"label": "Browse and Draw"
|
||||
},
|
||||
{
|
||||
"src": "/static/assets/samples/desktop-remember-plan-sample.png",
|
||||
"sizes": "1260x742",
|
||||
"type": "image/png",
|
||||
"form_factor": "wide",
|
||||
"label": "Remember and Plan"
|
||||
},
|
||||
{
|
||||
"src": "/static/assets/samples/desktop-browse-draw-sample.png",
|
||||
"sizes": "1260x742",
|
||||
"type": "image/png",
|
||||
"form_factor": "wide",
|
||||
"label": "Browse and Draw"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"name": "Khoj Chat",
|
||||
"short_name": "Khoj Chat",
|
||||
"description": "An AI personal assistant for your digital brain",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/static/assets/icons/favicon-128x128.png",
|
||||
"sizes": "128x128",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone",
|
||||
"start_url": "/chat"
|
||||
}
|
||||
@@ -65,13 +65,24 @@
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
height: 100%;
|
||||
background: var(--background-color);
|
||||
background: url('/static/assets/samples/desktop-plain-chat-sample.png') no-repeat center center fixed;
|
||||
background-size: contain;
|
||||
color: var(--main-text-color);
|
||||
font-family: var(--font-family);
|
||||
font-size: 20px;
|
||||
font-weight: 300;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
body::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--frosted-background-color);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
body > * {
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
@@ -100,9 +111,9 @@
|
||||
grid-template-rows: 1fr auto auto 1fr;
|
||||
gap: 32px;
|
||||
min-height: 300px;
|
||||
background: var(--background-color);
|
||||
margin-left: 25%;
|
||||
margin-right: 25%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
div.g_id_signin {
|
||||
@@ -118,9 +129,14 @@
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 700px) {
|
||||
body{
|
||||
background: url('/static/assets/samples/phone-plain-chat-sample.png') no-repeat center center fixed;
|
||||
background-size: contain;
|
||||
}
|
||||
div#login-modal {
|
||||
margin-left: 10%;
|
||||
margin-right: 10%;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -122,6 +122,7 @@ def save_to_conversation_log(
|
||||
conversation_id=conversation_id,
|
||||
user_message=q,
|
||||
)
|
||||
logger.info(f'Saved Conversation Turn\nYou ({user.username}): "{q}"\n\nKhoj: "{chat_response}"')
|
||||
|
||||
|
||||
def generate_chatml_messages_with_context(
|
||||
|
||||
@@ -430,14 +430,14 @@ class ApiUserRateLimiter:
|
||||
if subscribed and count_requests >= self.subscribed_requests:
|
||||
raise HTTPException(status_code=429, detail="Slow down! Too Many Requests")
|
||||
if not subscribed and count_requests >= self.requests:
|
||||
if self.subscribed_requests == self.requests:
|
||||
if self.requests >= self.subscribed_requests:
|
||||
raise HTTPException(
|
||||
status_code=429,
|
||||
detail="Slow down! Too Many Requests",
|
||||
)
|
||||
raise HTTPException(
|
||||
status_code=429,
|
||||
detail="We're glad you're enjoying Khoj! You've exceeded your usage limit for today. Come back tomorrow or subscribe to increase your rate limit via [your settings](https://app.khoj.dev/config).",
|
||||
detail="We're glad you're enjoying Khoj! You've exceeded your usage limit for today. Come back tomorrow or subscribe to increase your usage limit via [your settings](https://app.khoj.dev/config).",
|
||||
)
|
||||
|
||||
# Add the current request to the cache
|
||||
@@ -476,7 +476,7 @@ class ConversationCommandRateLimiter:
|
||||
if not subscribed and count_requests >= self.trial_rate_limit:
|
||||
raise HTTPException(
|
||||
status_code=429,
|
||||
detail=f"We're glad you're enjoying Khoj! You've exceeded your `/{conversation_command.value}` command usage limit for today. You can increase your rate limit via [your settings](https://app.khoj.dev/config).",
|
||||
detail=f"We're glad you're enjoying Khoj! You've exceeded your `/{conversation_command.value}` command usage limit for today. Subscribe to increase your usage limit via [your settings](https://app.khoj.dev/config).",
|
||||
)
|
||||
await UserRequests.objects.acreate(user=user, slug=command_slug)
|
||||
return
|
||||
|
||||