From 9f65e8de98074ed289fda1d4ef648568172f3df2 Mon Sep 17 00:00:00 2001 From: Debanjum Singh Solanky Date: Mon, 6 May 2024 21:47:28 +0800 Subject: [PATCH] Open Khoj Chat as a Pane instead of a Modal - Allows having it open on the side as you traverse your Obsidian notes - Allow faster time to response, having responses visible for context - Enables ambient interactions --- .../src/{chat_modal.ts => chat_view.ts} | 32 +++++++++++++------ src/interface/obsidian/src/main.ts | 30 ++++++++++++++--- 2 files changed, 49 insertions(+), 13 deletions(-) rename src/interface/obsidian/src/{chat_modal.ts => chat_view.ts} (96%) diff --git a/src/interface/obsidian/src/chat_modal.ts b/src/interface/obsidian/src/chat_view.ts similarity index 96% rename from src/interface/obsidian/src/chat_modal.ts rename to src/interface/obsidian/src/chat_view.ts index 31b938a1..f9b67f12 100644 --- a/src/interface/obsidian/src/chat_modal.ts +++ b/src/interface/obsidian/src/chat_view.ts @@ -1,6 +1,8 @@ -import { App, MarkdownRenderer, Modal, request, requestUrl, setIcon } from 'obsidian'; +import { ItemView, MarkdownRenderer, WorkspaceLeaf, request, requestUrl, setIcon } from 'obsidian'; import { KhojSetting } from 'src/settings'; +export const KHOJ_CHAT_VIEW = "khoj-chat-view"; + export interface ChatJsonResult { image?: string; detail?: string; @@ -9,7 +11,7 @@ export interface ChatJsonResult { } -export class KhojChatModal extends Modal { +export class KhojChatView extends ItemView { result: string; setting: KhojSetting; region: string; @@ -17,13 +19,13 @@ export class KhojChatModal extends Modal { countryName: string; timezone: string; - constructor(app: App, setting: KhojSetting) { - super(app); + constructor(leaf: WorkspaceLeaf, setting: KhojSetting) { + super(leaf); + this.setting = setting; // Register Modal Keybindings to send user message - this.scope.register([], 'Enter', async () => { await this.chat() }); - + // this.scope.register([], 'Enter', async () => { await this.chat() }); fetch("https://ipapi.co/json") .then(response => response.json()) @@ -39,6 +41,18 @@ export class KhojChatModal extends Modal { }); } + getViewType(): string { + return KHOJ_CHAT_VIEW; + } + + getDisplayText(): string { + return "Khoj"; + } + + getIcon(): string { + return "message-circle"; + } + async chat() { // Get text in chat input element let input_el = this.contentEl.getElementsByClassName("khoj-chat-input")[0]; @@ -503,7 +517,7 @@ export class KhojChatModal extends Modal { event.preventDefault(); const transcribeButton = this.contentEl.getElementsByClassName("khoj-transcribe")[0]; const chatInput = this.contentEl.getElementsByClassName("khoj-chat-input")[0]; - const sendButton = this.modalEl.getElementsByClassName("khoj-chat-send")[0] + const sendButton = this.contentEl.getElementsByClassName("khoj-chat-send")[0] const generateRequestBody = async (audioBlob: Blob, boundary_string: string) => { const boundary = `------${boundary_string}`; @@ -606,7 +620,7 @@ export class KhojChatModal extends Modal { clearTimeout(this.sendMessageTimeout); // Revert to showing send-button and hide the stop-send-button - let sendButton = this.modalEl.getElementsByClassName("khoj-chat-send")[0]; + let sendButton = this.contentEl.getElementsByClassName("khoj-chat-send")[0]; setIcon(sendButton, "arrow-up-circle"); let sendImg = sendButton.getElementsByClassName("lucide-arrow-up-circle")[0] sendImg.addEventListener('click', async (_) => { await this.chat() }); @@ -637,7 +651,7 @@ export class KhojChatModal extends Modal { } scrollChatToBottom() { - let sendButton = this.modalEl.getElementsByClassName("khoj-chat-send")[0]; + let sendButton = this.contentEl.getElementsByClassName("khoj-chat-send")[0]; sendButton.scrollIntoView({ behavior: "auto", block: "center" }); } } diff --git a/src/interface/obsidian/src/main.ts b/src/interface/obsidian/src/main.ts index 7e152c49..9f3420e7 100644 --- a/src/interface/obsidian/src/main.ts +++ b/src/interface/obsidian/src/main.ts @@ -1,7 +1,7 @@ -import { Plugin } from 'obsidian'; +import { Plugin, WorkspaceLeaf } from 'obsidian'; import { KhojSetting, KhojSettingTab, DEFAULT_SETTINGS } from 'src/settings' import { KhojSearchModal } from 'src/search_modal' -import { KhojChatModal } from 'src/chat_modal' +import { KhojChatView, KHOJ_CHAT_VIEW } from 'src/chat_view' import { updateContentIndex, canConnectToBackend } from './utils'; @@ -30,12 +30,14 @@ export default class Khoj extends Plugin { this.addCommand({ id: 'chat', name: 'Chat', - callback: () => { new KhojChatModal(this.app, this.settings).open(); } + callback: () => { this.activateView(KHOJ_CHAT_VIEW); } }); + this.registerView(KHOJ_CHAT_VIEW, (leaf) => new KhojChatView(leaf, this.settings)); + // Create an icon in the left ribbon. this.addRibbonIcon('message-circle', 'Khoj', (_: MouseEvent) => { - new KhojChatModal(this.app, this.settings).open() + this.activateView(KHOJ_CHAT_VIEW); }); // Add a settings tab so the user can configure khoj @@ -69,4 +71,24 @@ export default class Khoj extends Plugin { this.unload(); } + + async activateView(viewType: string) { + const { workspace } = this.app; + + let leaf: WorkspaceLeaf | null = null; + const leaves = workspace.getLeavesOfType(viewType); + + if (leaves.length > 0) { + // A leaf with our view already exists, use that + leaf = leaves[0]; + } else { + // Our view could not be found in the workspace, create a new leaf + // in the right sidebar for it + leaf = workspace.getRightLeaf(false); + await leaf.setViewState({ type: viewType, active: true }); + } + + // "Reveal" the leaf in case it is in a collapsed sidebar + workspace.revealLeaf(leaf); + } }