Fix Khoj Obsidian plugin on Obsidian Mobile (#635)

- Removed node-fetch dependency to work on mobile. 
- Fix CORS issue for Khoj (streaming) chat on Obsidian mobile
- Verified Khoj plugin, search, chat work on Obsidian mobile.

## Details
### Major
- Allow calls to Khoj server from Obsidian mobile app to fix CORS issue
- Chat stream using default `fetch' not `node-fetch' in obsidian plugin

### Minor
- Load chat history after other elements in chat modal on Obsidian are rendered
- Scroll to bottom of chat modal on Obsidian across mobile & desktop
This commit is contained in:
Debanjum 2024-02-06 22:03:51 +05:30 committed by GitHub
commit fc1b8f6fb6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 94 additions and 159 deletions

View file

@ -25,9 +25,5 @@
"obsidian": "latest", "obsidian": "latest",
"tslib": "2.4.0", "tslib": "2.4.0",
"typescript": "4.7.4" "typescript": "4.7.4"
},
"dependencies": {
"@types/node-fetch": "^2.6.4",
"node-fetch": "^3.1.0"
} }
} }

View file

@ -1,6 +1,5 @@
import { App, MarkdownRenderer, Modal, request, requestUrl, setIcon } from 'obsidian'; import { App, MarkdownRenderer, Modal, request, requestUrl, setIcon } from 'obsidian';
import { KhojSetting } from 'src/settings'; import { KhojSetting } from 'src/settings';
import fetch from "node-fetch";
export interface ChatJsonResult { export interface ChatJsonResult {
image?: string; image?: string;
@ -43,10 +42,6 @@ export class KhojChatModal extends Modal {
// Create area for chat logs // Create area for chat logs
let chatBodyEl = contentEl.createDiv({ attr: { id: "khoj-chat-body", class: "khoj-chat-body" } }); let chatBodyEl = contentEl.createDiv({ attr: { id: "khoj-chat-body", class: "khoj-chat-body" } });
// Get chat history from Khoj backend
let getChatHistorySucessfully = await this.getChatHistory(chatBodyEl);
let placeholderText = getChatHistorySucessfully ? "Message" : "Configure Khoj to enable chat";
// Add chat input field // Add chat input field
let inputRow = contentEl.createDiv("khoj-input-row"); let inputRow = contentEl.createDiv("khoj-input-row");
let clearChat = inputRow.createEl("button", { let clearChat = inputRow.createEl("button", {
@ -62,9 +57,7 @@ export class KhojChatModal extends Modal {
attr: { attr: {
id: "khoj-chat-input", id: "khoj-chat-input",
autofocus: "autofocus", autofocus: "autofocus",
placeholder: placeholderText,
class: "khoj-chat-input option", class: "khoj-chat-input option",
disabled: !getChatHistorySucessfully ? "disabled" : null
}, },
}) })
chatInput.addEventListener('input', (_) => { this.onChatInput() }); chatInput.addEventListener('input', (_) => { this.onChatInput() });
@ -94,8 +87,14 @@ export class KhojChatModal extends Modal {
let sendImg = <SVGElement>send.getElementsByClassName("lucide-arrow-up-circle")[0] let sendImg = <SVGElement>send.getElementsByClassName("lucide-arrow-up-circle")[0]
sendImg.addEventListener('click', async (_) => { await this.chat() }); sendImg.addEventListener('click', async (_) => { await this.chat() });
// Get chat history from Khoj backend and set chat input state
let getChatHistorySucessfully = await this.getChatHistory(chatBodyEl);
let placeholderText = getChatHistorySucessfully ? "Message" : "Configure Khoj to enable chat";
chatInput.placeholder = placeholderText;
chatInput.disabled = !getChatHistorySucessfully;
// Scroll to bottom of modal, till the send message input box // Scroll to bottom of modal, till the send message input box
this.modalEl.scrollTop = this.modalEl.scrollHeight; this.scrollChatToBottom();
chatInput.focus(); chatInput.focus();
} }
@ -207,7 +206,7 @@ export class KhojChatModal extends Modal {
chatMessageEl.style.userSelect = "text"; chatMessageEl.style.userSelect = "text";
// Scroll to bottom after inserting chat messages // Scroll to bottom after inserting chat messages
this.modalEl.scrollTop = this.modalEl.scrollHeight; this.scrollChatToBottom();
return chatMessageEl return chatMessageEl
} }
@ -230,7 +229,7 @@ export class KhojChatModal extends Modal {
}) })
// Scroll to bottom after inserting chat messages // Scroll to bottom after inserting chat messages
this.modalEl.scrollTop = this.modalEl.scrollHeight; this.scrollChatToBottom();
return chat_message_el return chat_message_el
} }
@ -241,7 +240,7 @@ export class KhojChatModal extends Modal {
// @ts-ignore // @ts-ignore
await MarkdownRenderer.renderMarkdown(this.result, htmlElement, '', null); await MarkdownRenderer.renderMarkdown(this.result, htmlElement, '', null);
// Scroll to bottom of modal, till the send message input box // Scroll to bottom of modal, till the send message input box
this.modalEl.scrollTop = this.modalEl.scrollHeight; this.scrollChatToBottom();
} }
formatDate(date: Date): string { formatDate(date: Date): string {
@ -254,10 +253,13 @@ export class KhojChatModal extends Modal {
async getChatHistory(chatBodyEl: Element): Promise<boolean> { async getChatHistory(chatBodyEl: Element): Promise<boolean> {
// Get chat history from Khoj backend // Get chat history from Khoj backend
let chatUrl = `${this.setting.khojUrl}/api/chat/history?client=obsidian`; let chatUrl = `${this.setting.khojUrl}/api/chat/history?client=obsidian`;
let headers = { "Authorization": `Bearer ${this.setting.khojApiKey}` };
try { try {
let response = await fetch(chatUrl, { method: "GET", headers: headers }); let response = await fetch(chatUrl, {
method: "GET",
headers: { "Authorization": `Bearer ${this.setting.khojApiKey}` },
});
let responseJson: any = await response.json(); let responseJson: any = await response.json();
if (responseJson.detail) { if (responseJson.detail) {
@ -280,6 +282,68 @@ export class KhojChatModal extends Modal {
return true; return true;
} }
async readChatStream(response: Response, responseElement: HTMLDivElement): Promise<void> {
// Exit if response body is empty
if (response.body == null) return;
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { value, done } = await reader.read();
// Break if the stream is done
if (done) break;
let responseText = decoder.decode(value);
if (responseText.includes("### compiled references:")) {
// Render any references used to generate the response
const [additionalResponse, rawReference] = responseText.split("### compiled references:", 2);
await this.renderIncrementalMessage(responseElement, additionalResponse);
const rawReferenceAsJson = JSON.parse(rawReference);
let references = responseElement.createDiv();
references.classList.add("references");
let referenceExpandButton = references.createEl('button');
referenceExpandButton.classList.add("reference-expand-button");
let referenceSection = references.createDiv();
referenceSection.classList.add("reference-section");
referenceSection.classList.add("collapsed");
let numReferences = 0;
// If rawReferenceAsJson is a list, then count the length
if (Array.isArray(rawReferenceAsJson)) {
numReferences = rawReferenceAsJson.length;
rawReferenceAsJson.forEach((reference, index) => {
this.generateReference(referenceSection, reference, index);
});
}
references.appendChild(referenceExpandButton);
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");
}
});
let expandButtonText = numReferences == 1 ? "1 reference" : `${numReferences} references`;
referenceExpandButton.innerHTML = expandButtonText;
references.appendChild(referenceSection);
} else {
// Render incremental chat response
await this.renderIncrementalMessage(responseElement, responseText);
}
}
}
async getChatResponse(query: string | undefined | null): Promise<void> { async getChatResponse(query: string | undefined | null): Promise<void> {
// Exit if query is empty // Exit if query is empty
if (!query || query === "") return; if (!query || query === "") return;
@ -300,21 +364,22 @@ export class KhojChatModal extends Modal {
let response = await fetch(chatUrl, { let response = await fetch(chatUrl, {
method: "GET", method: "GET",
headers: { headers: {
"Access-Control-Allow-Origin": "*",
"Content-Type": "text/event-stream", "Content-Type": "text/event-stream",
"Authorization": `Bearer ${this.setting.khojApiKey}`, "Authorization": `Bearer ${this.setting.khojApiKey}`,
}, },
}) })
try { try {
if (response.body == null) { if (response.body === null) {
throw new Error("Response body is null"); throw new Error("Response body is null");
} }
// Clear thinking status message // Clear thinking status message
if (responseElement.innerHTML === "🤔") { if (responseElement.innerHTML === "🤔") {
responseElement.innerHTML = ""; responseElement.innerHTML = "";
} }
// Reset collated chat result to empty string
this.result = ""; this.result = "";
responseElement.innerHTML = ""; responseElement.innerHTML = "";
if (response.headers.get("content-type") == "application/json") { if (response.headers.get("content-type") == "application/json") {
@ -328,60 +393,17 @@ export class KhojChatModal extends Modal {
} }
} catch (error) { } catch (error) {
// If the chunk is not a JSON object, just display it as is // If the chunk is not a JSON object, just display it as is
responseText = response.body.read().toString() responseText = await response.text();
} finally { } finally {
await this.renderIncrementalMessage(responseElement, responseText); await this.renderIncrementalMessage(responseElement, responseText);
} }
} }
for await (const chunk of response.body) { // Stream and render chat response
let responseText = chunk.toString(); await this.readChatStream(response, responseElement);
if (responseText.includes("### compiled references:")) {
const [additionalResponse, rawReference] = responseText.split("### compiled references:", 2);
await this.renderIncrementalMessage(responseElement, additionalResponse);
const rawReferenceAsJson = JSON.parse(rawReference);
let references = responseElement.createDiv();
references.classList.add("references");
let referenceExpandButton = references.createEl('button');
referenceExpandButton.classList.add("reference-expand-button");
let referenceSection = references.createDiv();
referenceSection.classList.add("reference-section");
referenceSection.classList.add("collapsed");
let numReferences = 0;
// If rawReferenceAsJson is a list, then count the length
if (Array.isArray(rawReferenceAsJson)) {
numReferences = rawReferenceAsJson.length;
rawReferenceAsJson.forEach((reference, index) => {
this.generateReference(referenceSection, reference, index);
});
}
references.appendChild(referenceExpandButton);
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");
}
});
let expandButtonText = numReferences == 1 ? "1 reference" : `${numReferences} references`;
referenceExpandButton.innerHTML = expandButtonText;
references.appendChild(referenceSection);
} else {
await this.renderIncrementalMessage(responseElement, responseText);
}
}
} catch (err) { } catch (err) {
let errorMsg = "Sorry, unable to get response from Khoj backend ❤️‍🩹. Contact developer for help at team@khoj.dev or [in Discord](https://discord.gg/BDgyabRM6e)"; console.log(`Khoj chat response failed with\n${err}`);
let errorMsg = "Sorry, unable to get response from Khoj backend ❤️‍🩹. Retry or contact developers for help at <a href=mailto:'team@khoj.dev'>team@khoj.dev</a> or <a href='https://discord.gg/BDgyabRM6e'>on Discord</a>";
responseElement.innerHTML = errorMsg responseElement.innerHTML = errorMsg
} }
} }
@ -402,7 +424,7 @@ export class KhojChatModal extends Modal {
let chatBody = this.contentEl.getElementsByClassName("khoj-chat-body")[0]; let chatBody = this.contentEl.getElementsByClassName("khoj-chat-body")[0];
let response = await request({ let response = await request({
url: `${this.setting.khojUrl}/api/chat/history?client=web`, url: `${this.setting.khojUrl}/api/chat/history?client=obsidian`,
method: "DELETE", method: "DELETE",
headers: { "Authorization": `Bearer ${this.setting.khojApiKey}` }, headers: { "Authorization": `Bearer ${this.setting.khojApiKey}` },
}) })
@ -559,6 +581,11 @@ export class KhojChatModal extends Modal {
const scrollHeight = chatInput.scrollHeight + 8; // +8 accounts for padding const scrollHeight = chatInput.scrollHeight + 8; // +8 accounts for padding
chatInput.style.height = Math.min(scrollHeight, 200) + 'px'; chatInput.style.height = Math.min(scrollHeight, 200) + 'px';
chatInput.scrollTop = scrollTop; chatInput.scrollTop = scrollTop;
this.modalEl.scrollTop = this.modalEl.scrollHeight; this.scrollChatToBottom();
}
scrollChatToBottom() {
let sendButton = <HTMLButtonElement>this.modalEl.getElementsByClassName("khoj-chat-send")[0];
sendButton.scrollIntoView({ behavior: "auto", block: "center" });
} }
} }

View file

@ -40,19 +40,6 @@
resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz" resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz"
integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
"@types/node-fetch@^2.6.4":
version "2.6.4"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.4.tgz#1bc3a26de814f6bf466b25aeb1473fa1afe6a660"
integrity sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==
dependencies:
"@types/node" "*"
form-data "^3.0.0"
"@types/node@*":
version "20.3.3"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.3.tgz#329842940042d2b280897150e023e604d11657d6"
integrity sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==
"@types/node@^16.11.6": "@types/node@^16.11.6":
version "16.18.12" version "16.18.12"
resolved "https://registry.npmjs.org/@types/node/-/node-16.18.12.tgz" resolved "https://registry.npmjs.org/@types/node/-/node-16.18.12.tgz"
@ -150,11 +137,6 @@ array-union@^2.1.0:
resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz"
integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
braces@^3.0.2: braces@^3.0.2:
version "3.0.2" version "3.0.2"
resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz"
@ -167,18 +149,6 @@ builtin-modules@3.3.0:
resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz" resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz"
integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
data-uri-to-buffer@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e"
integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==
debug@^4.3.4: debug@^4.3.4:
version "4.3.4" version "4.3.4"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"
@ -186,11 +156,6 @@ debug@^4.3.4:
dependencies: dependencies:
ms "2.1.2" ms "2.1.2"
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
dir-glob@^3.0.1: dir-glob@^3.0.1:
version "3.0.1" version "3.0.1"
resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz"
@ -384,14 +349,6 @@ fastq@^1.6.0:
dependencies: dependencies:
reusify "^1.0.4" reusify "^1.0.4"
fetch-blob@^3.1.2, fetch-blob@^3.1.4:
version "3.2.0"
resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9"
integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==
dependencies:
node-domexception "^1.0.0"
web-streams-polyfill "^3.0.3"
fill-range@^7.0.1: fill-range@^7.0.1:
version "7.0.1" version "7.0.1"
resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz"
@ -399,22 +356,6 @@ fill-range@^7.0.1:
dependencies: dependencies:
to-regex-range "^5.0.1" to-regex-range "^5.0.1"
form-data@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f"
integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
formdata-polyfill@^4.0.10:
version "4.0.10"
resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423"
integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==
dependencies:
fetch-blob "^3.1.2"
functional-red-black-tree@^1.0.1: functional-red-black-tree@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz" resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz"
@ -481,18 +422,6 @@ micromatch@^4.0.4:
braces "^3.0.2" braces "^3.0.2"
picomatch "^2.3.1" picomatch "^2.3.1"
mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@^2.1.12:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
moment@2.29.4: moment@2.29.4:
version "2.29.4" version "2.29.4"
resolved "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz" resolved "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz"
@ -503,20 +432,6 @@ ms@2.1.2:
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
node-domexception@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5"
integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==
node-fetch@^3.1.0:
version "3.3.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.1.tgz#b3eea7b54b3a48020e46f4f88b9c5a7430d20b2e"
integrity sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==
dependencies:
data-uri-to-buffer "^4.0.0"
fetch-blob "^3.1.4"
formdata-polyfill "^4.0.10"
obsidian@latest: obsidian@latest:
version "1.1.1" version "1.1.1"
resolved "https://registry.npmjs.org/obsidian/-/obsidian-1.1.1.tgz" resolved "https://registry.npmjs.org/obsidian/-/obsidian-1.1.1.tgz"
@ -598,11 +513,6 @@ typescript@4.7.4:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235"
integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==
web-streams-polyfill@^3.0.3:
version "3.2.1"
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6"
integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==
yallist@^4.0.0: yallist@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz"

View file

@ -61,6 +61,8 @@ app.add_middleware(
CORSMiddleware, CORSMiddleware,
allow_origins=[ allow_origins=[
"app://obsidian.md", "app://obsidian.md",
"capacitor://localhost", # To allow access from Obsidian iOS app using Capacitor.JS
"http://localhost", # To allow access from Obsidian Android app
"http://localhost:*", "http://localhost:*",
"http://127.0.0.1:*", "http://127.0.0.1:*",
f"https://{KHOJ_DOMAIN}", f"https://{KHOJ_DOMAIN}",