Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use DOM Scripting, Add CSP to Web config pages. Disable CSP in Obsidian plugin #834

Merged
merged 6 commits into from
Jul 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ dependencies = [
"pyyaml ~= 6.0",
"rich >= 13.3.1",
"schedule == 1.1.0",
"sentence-transformers == 2.5.1",
"sentence-transformers == 3.0.1",
"einops == 0.8.0",
"transformers >= 4.28.0",
"torch == 2.2.2",
"uvicorn == 0.17.6",
Expand Down
8 changes: 6 additions & 2 deletions src/interface/obsidian/src/chat_view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,9 @@ export class KhojChatView extends KhojPaneView {
const objectSrc = `object-src 'none';`;
const csp = `${defaultSrc} ${scriptSrc} ${connectSrc} ${styleSrc} ${imgSrc} ${childSrc} ${objectSrc}`;

// Add CSP meta tag to the Khoj Chat modal
document.head.createEl("meta", { attr: { "http-equiv": "Content-Security-Policy", "content": `${csp}` } });
// WARNING: CSP DISABLED for now as it breaks other Obsidian plugins. Enable when can scope CSP to only Khoj plugin.
// CSP meta tag for the Khoj Chat modal
// document.head.createEl("meta", { attr: { "http-equiv": "Content-Security-Policy", "content": `${csp}` } });

// Create area for chat logs
let chatBodyEl = contentEl.createDiv({ attr: { id: "khoj-chat-body", class: "khoj-chat-body" } });
Expand Down Expand Up @@ -1014,6 +1015,7 @@ export class KhojChatView extends KhojPaneView {

// Start the countdown timer UI
stopSendButtonImg.getElementsByTagName("circle")[0].style.animation = "countdown 3s linear 1 forwards";
stopSendButtonImg.getElementsByTagName("circle")[0].style.color = "var(--icon-color-active)";

// Auto send message after 3 seconds
this.sendMessageTimeout = setTimeout(() => {
Expand Down Expand Up @@ -1043,6 +1045,7 @@ export class KhojChatView extends KhojPaneView {

this.mediaRecorder.start();
setIcon(transcribeButton, "mic-off");
transcribeButton.classList.add("loading-encircle")
};

// Toggle recording
Expand All @@ -1057,6 +1060,7 @@ export class KhojChatView extends KhojPaneView {
this.mediaRecorder.stop();
this.mediaRecorder.stream.getTracks().forEach(track => track.stop());
this.mediaRecorder = undefined;
transcribeButton.classList.remove("loading-encircle");
setIcon(transcribeButton, "mic");
}
}
Expand Down
24 changes: 24 additions & 0 deletions src/interface/obsidian/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,30 @@ img.copy-icon {
}
}

/* Loading Encircle */
.loading-encircle {
position: relative;
}

.loading-encircle::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 24px;
height: 24px;
margin-top: -16px;
margin-left: -16px;
border: 4px solid transparent;
border-top-color: var(--icon-color-active);
border-radius: 50%;
animation: spin 1s linear infinite;
}

@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@media only screen and (max-width: 600px) {
div.khoj-header {
display: grid;
Expand Down
6 changes: 3 additions & 3 deletions src/khoj/database/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,11 +215,11 @@ class ModelType(models.TextChoices):
# Bi-encoder model of sentence-transformer type to load from HuggingFace
bi_encoder = models.CharField(max_length=200, default="thenlper/gte-small")
# Config passed to the sentence-transformer model constructor. E.g. device="cuda:0", trust_remote_server=True etc.
bi_encoder_model_config = models.JSONField(default=dict)
bi_encoder_model_config = models.JSONField(default=dict, blank=True)
# Query encode configs like prompt, precision, normalize_embeddings, etc. for sentence-transformer models
bi_encoder_query_encode_config = models.JSONField(default=dict)
bi_encoder_query_encode_config = models.JSONField(default=dict, blank=True)
# Docs encode configs like prompt, precision, normalize_embeddings, etc. for sentence-transformer models
bi_encoder_docs_encode_config = models.JSONField(default=dict)
bi_encoder_docs_encode_config = models.JSONField(default=dict, blank=True)
# Cross-encoder model of sentence-transformer type to load from HuggingFace
cross_encoder = models.CharField(max_length=200, default="mixedbread-ai/mxbai-rerank-xsmall-v1")
# Inference server API endpoint to use for embeddings inference. Bi-encoder model should be hosted on this server
Expand Down
31 changes: 19 additions & 12 deletions src/khoj/interface/web/agents.html
Original file line number Diff line number Diff line change
Expand Up @@ -242,18 +242,25 @@ <h2>{{ agent.name }}</h2>
<script>
async function openChat(agentSlug) {
// Create a loading animation
let loading = document.createElement("div");
loading.innerHTML = '<div>Booting your agent...</div><span class="loader"></span>';
loading.style.position = "fixed";
loading.style.top = "0";
loading.style.right = "0";
loading.style.bottom = "0";
loading.style.left = "0";
loading.style.display = "flex";
loading.style.justifyContent = "center";
loading.style.alignItems = "center";
loading.style.backgroundColor = "rgba(0, 0, 0, 0.5)"; // Semi-transparent black
document.body.appendChild(loading);
let loadingTextEl = document.createElement("div");
loadingTextEl.textContent = 'Booting your agent...';

let loadingAnimationEl = document.createElement("span");
loadingAnimationEl.className = "loader";

let loadingEl = document.createElement("div");
loadingEl.style.position = "fixed";
loadingEl.style.top = "0";
loadingEl.style.right = "0";
loadingEl.style.bottom = "0";
loadingEl.style.left = "0";
loadingEl.style.display = "flex";
loadingEl.style.justifyContent = "center";
loadingEl.style.alignItems = "center";
loadingEl.style.backgroundColor = "rgba(0, 0, 0, 0.5)"; // Semi-transparent black

loadingEl.append(loadingTextEl, loadingAnimationEl);
document.body.appendChild(loadingEl);

let response = await fetch(`/api/chat/sessions?agent_slug=${agentSlug}`, { method: "POST" });
let data = await response.json();
Expand Down
13 changes: 11 additions & 2 deletions src/khoj/interface/web/base_config.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,22 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0 maximum-scale=1.0">
<link rel="icon" type="image/png" sizes="128x128" href="/static/assets/icons/favicon-128x128.png?v={{ khoj_version }}">
<title>Khoj</title>
<meta http-equiv="Content-Security-Policy"
content="default-src 'self' https://assets.khoj.dev;
script-src 'self' https://assets.khoj.dev 'unsafe-inline';
connect-src 'self' https://ipapi.co/json;
style-src 'self' https://assets.khoj.dev 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' data: https://*.khoj.dev https://*.googleusercontent.com;
font-src https://assets.khoj.dev https://fonts.gstatic.com;
child-src 'none';
object-src 'none';">
<link rel="stylesheet" href="/static/assets/pico.min.css?v={{ khoj_version }}">
<link rel="stylesheet" href="/static/assets/khoj.css?v={{ khoj_version }}">
<script
integrity="sha384-05IkdNHoAlkhrFVUCCN805WC/h4mcI98GUBssmShF2VJAXKyZTrO/TmJ+4eBo0Cy"
crossorigin="anonymous"
src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.13/js/intlTelInput.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.13/css/intlTelInput.css">
src="https://assets.khoj.dev/intl-tel-input/intlTelInput.min.js"></script>
<link rel="stylesheet" href="https://assets.khoj.dev/intl-tel-input/intlTelInput.css">
</head>
<script type="text/javascript" src="/static/assets/utils.js?v={{ khoj_version }}"></script>
<script type="text/javascript" src="/static/assets/purify.min.js?v={{ khoj_version }}"></script>
Expand Down
44 changes: 26 additions & 18 deletions src/khoj/interface/web/chat.html
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@
}

function generateOnlineReference(reference, index) {

// Generate HTML for Chat Reference
let title = reference.title || reference.link;
let link = reference.link;
Expand All @@ -170,7 +169,7 @@
linkElement.textContent = title;

let referenceButton = document.createElement('button');
referenceButton.innerHTML = linkElement.outerHTML;
referenceButton.appendChild(linkElement);
referenceButton.id = `ref-${index}`;
referenceButton.classList.add("reference-button");
referenceButton.classList.add("collapsed");
Expand All @@ -181,11 +180,12 @@
if (this.classList.contains("collapsed")) {
this.classList.remove("collapsed");
this.classList.add("expanded");
this.innerHTML = linkElement.outerHTML + `<br><br>${question + snippet}`;
this.innerHTML = `${linkElement.outerHTML}<br><br>${question}${snippet}`;
} else {
this.classList.add("collapsed");
this.classList.remove("expanded");
this.innerHTML = linkElement.outerHTML;
this.innerHTML = "";
this.appendChild(linkElement);
}
});

Expand Down Expand Up @@ -578,7 +578,7 @@

let referenceExpandButton = document.createElement('button');
referenceExpandButton.classList.add("reference-expand-button");
referenceExpandButton.innerHTML = numReferences == 1 ? "1 reference" : `${numReferences} references`;
referenceExpandButton.textContent = numReferences == 1 ? "1 reference" : `${numReferences} references`;

referenceExpandButton.addEventListener('click', function() {
if (referenceSection.classList.contains("collapsed")) {
Expand Down Expand Up @@ -888,7 +888,7 @@
if (overlayText == null) {
dropzone.classList.add('dragover');
var overlayText = document.createElement("div");
overlayText.innerHTML = "Select file(s) or drag + drop it here to share it with Khoj";
overlayText.textContent = "Select file(s) or drag + drop it here to share it with Khoj";
overlayText.className = "dropzone-overlay";
overlayText.id = "dropzone-overlay";
dropzone.appendChild(overlayText);
Expand Down Expand Up @@ -949,7 +949,7 @@
if (overlayText != null) {
// Display loading spinner
var loadingSpinner = document.createElement("div");
overlayText.innerHTML = "Uploading file(s) for indexing";
overlayText.textContent = "Uploading file(s) for indexing";
loadingSpinner.className = "spinner";
overlayText.appendChild(loadingSpinner);
}
Expand Down Expand Up @@ -1042,7 +1042,7 @@

if (overlayText == null) {
var overlayText = document.createElement("div");
overlayText.innerHTML = "Drop file to share it with Khoj";
overlayText.textContent = "Drop file to share it with Khoj";
overlayText.className = "dropzone-overlay";
overlayText.id = "dropzone-overlay";
this.appendChild(overlayText);
Expand Down Expand Up @@ -1179,11 +1179,15 @@
websocket.onclose = function(event) {
websocket = null;
console.log("WebSocket is closed now.");
let setupWebSocketButton = document.createElement("button");
setupWebSocketButton.textContent = "Reconnect to Server";
setupWebSocketButton.onclick = setupWebSocket;
let statusDotIcon = document.getElementById("connection-status-icon");
statusDotIcon.style.backgroundColor = "red";
let statusDotText = document.getElementById("connection-status-text");
statusDotText.innerHTML = "";
statusDotText.style.marginTop = "5px";
statusDotText.innerHTML = '<button onclick="setupWebSocket()">Reconnect to Server</button>';
statusDotText.appendChild(setupWebSocketButton);
}
websocket.onerror = function(event) {
console.log("WebSocket error observed:", event);
Expand Down Expand Up @@ -1434,7 +1438,7 @@
questionStarterSuggestions.innerHTML = "";
data.forEach((questionStarter) => {
let questionStarterButton = document.createElement('button');
questionStarterButton.innerHTML = questionStarter;
questionStarterButton.textContent = questionStarter;
questionStarterButton.classList.add("question-starter");
questionStarterButton.addEventListener('click', function() {
questionStarterSuggestions.style.display = "none";
Expand Down Expand Up @@ -1606,7 +1610,7 @@

let closeButton = document.createElement('button');
closeButton.id = "close-button";
closeButton.innerHTML = "Close";
closeButton.textContent = "Close";
closeButton.classList.add("close-button");
closeButton.addEventListener('click', function() {
modal.remove();
Expand Down Expand Up @@ -1660,7 +1664,7 @@
let threeDotMenu = document.createElement('div');
threeDotMenu.classList.add("three-dot-menu");
let threeDotMenuButton = document.createElement('button');
threeDotMenuButton.innerHTML = "⋮";
threeDotMenuButton.textContent = "⋮";
threeDotMenuButton.classList.add("three-dot-menu-button");
threeDotMenuButton.addEventListener('click', function(event) {
event.stopPropagation();
Expand All @@ -1679,7 +1683,7 @@
conversationMenu.classList.add("conversation-menu");

let editTitleButton = document.createElement('button');
editTitleButton.innerHTML = "Rename";
editTitleButton.textContent = "Rename";
editTitleButton.classList.add("edit-title-button");
editTitleButton.classList.add("three-dot-menu-button-item");
editTitleButton.addEventListener('click', function(event) {
Expand Down Expand Up @@ -1713,7 +1717,7 @@

conversationTitleInputBox.appendChild(conversationTitleInput);
let conversationTitleInputButton = document.createElement('button');
conversationTitleInputButton.innerHTML = "Save";
conversationTitleInputButton.textContent = "Save";
conversationTitleInputButton.classList.add("three-dot-menu-button-item");
conversationTitleInputButton.addEventListener('click', function(event) {
event.stopPropagation();
Expand All @@ -1737,7 +1741,7 @@
threeDotMenu.appendChild(conversationMenu);

let shareButton = document.createElement('button');
shareButton.innerHTML = "Share";
shareButton.textContent = "Share";
shareButton.type = "button";
shareButton.classList.add("share-conversation-button");
shareButton.classList.add("three-dot-menu-button-item");
Expand Down Expand Up @@ -1804,7 +1808,7 @@

let deleteButton = document.createElement('button');
deleteButton.type = "button";
deleteButton.innerHTML = "Delete";
deleteButton.textContent = "Delete";
deleteButton.classList.add("delete-conversation-button");
deleteButton.classList.add("three-dot-menu-button-item");
deleteButton.addEventListener('click', function(event) {
Expand Down Expand Up @@ -1968,12 +1972,16 @@
}
allFiles = data;
var nofilesmessage = document.getElementsByClassName("no-files-message")[0];
nofilesmessage.innerHTML = "";
if(allFiles.length === 0){
nofilesmessage.innerHTML = `<a class="inline-chat-link" href="https://docs.khoj.dev/category/clients/">How to upload files</a>`;
let inlineChatLinkEl = document.createElement('a');
inlineChatLinkEl.className = "inline-chat-link";
inlineChatLinkEl.href = "https://docs.khoj.dev/category/clients/";
inlineChatLinkEl.textContent = "How to upload files";
nofilesmessage.appendChild(inlineChatLinkEl);
document.getElementsByClassName("file-toggle-button")[0].style.display = "none";
}
else{
nofilesmessage.innerHTML = "";
document.getElementsByClassName("file-toggle-button")[0].style.display = "block";
}
})
Expand Down
Loading
Loading