diff --git a/main.js b/main.js index 8730c34..20c45f7 100644 --- a/main.js +++ b/main.js @@ -632,7 +632,6 @@ var ProcessManager = class { var OpenCodeClient = class { constructor(apiBaseUrl, uiBaseUrl, projectDirectory) { this.trackedSessionId = null; - this.lastMessageId = null; this.lastPart = null; this.apiBaseUrl = this.normalizeBaseUrl(apiBaseUrl); this.uiBaseUrl = this.normalizeBaseUrl(uiBaseUrl); @@ -650,7 +649,6 @@ var OpenCodeClient = class { } resetTracking() { this.trackedSessionId = null; - this.lastMessageId = null; this.lastPart = null; } getSessionUrl(sessionId) { @@ -689,7 +687,6 @@ var OpenCodeClient = class { } const message = await this.sendPrompt(sessionId, contextText); if ((_a = message == null ? void 0 : message.info) == null ? void 0 : _a.id) { - this.lastMessageId = message.info.id; this.lastPart = (_c = (_b = message.parts) == null ? void 0 : _b[0]) != null ? _c : null; } } @@ -734,7 +731,6 @@ var OpenCodeClient = class { if (!ignored) { return false; } - this.lastMessageId = null; this.lastPart = null; return true; } @@ -791,84 +787,67 @@ var WorkspaceContext = class { this.lastMarkdownView = null; this.app = app; } - getOpenNotePaths(maxNotes) { - var _a; + gatherContext(maxNotes, maxSelectionLength) { + var _a, _b, _c, _d, _e; const leaves = this.app.workspace.getLeavesOfType("markdown"); const paths = /* @__PURE__ */ new Set(); for (const leaf of leaves) { - const view = leaf.view; - const path = (_a = view.file) == null ? void 0 : _a.path; + const view2 = leaf.view; + const path = (_a = view2.file) == null ? void 0 : _a.path; if (path) { paths.add(path); } } - return Array.from(paths).slice(0, Math.max(0, maxNotes)); - } - updateSelectionFromView(view) { - var _a, _b, _c; + const openNotePaths = Array.from(paths).slice(0, Math.max(0, maxNotes)); + const view = (_b = this.app.workspace.getActiveViewOfType(import_obsidian4.MarkdownView)) != null ? _b : this.lastMarkdownView; if (view) { this.lastMarkdownView = view; } - const sourcePath = (_a = view == null ? void 0 : view.file) == null ? void 0 : _a.path; - const selection = (_c = (_b = view == null ? void 0 : view.editor) == null ? void 0 : _b.getSelection()) != null ? _c : ""; - if (!sourcePath || !selection.trim()) { - this.lastSelection = null; - return; - } - this.lastSelection = { - text: selection, - sourcePath - }; - } - getSelectedText(maxSelectionLength) { - var _a, _b, _c, _d; - const view = (_a = this.app.workspace.getActiveViewOfType(import_obsidian4.MarkdownView)) != null ? _a : this.lastMarkdownView; - const sourcePath = (_b = view == null ? void 0 : view.file) == null ? void 0 : _b.path; - const selection = (_d = (_c = view == null ? void 0 : view.editor) == null ? void 0 : _c.getSelection()) != null ? _d : ""; - let text = ""; - let path = ""; + const sourcePath = (_c = view == null ? void 0 : view.file) == null ? void 0 : _c.path; + const selection = (_e = (_d = view == null ? void 0 : view.editor) == null ? void 0 : _d.getSelection()) != null ? _e : ""; + let selectionContext = null; if (sourcePath && selection.trim()) { - text = selection; - path = sourcePath; - this.lastSelection = { text, sourcePath: path }; - } else if (this.lastSelection) { - text = this.lastSelection.text; - path = this.lastSelection.sourcePath; + selectionContext = { + text: selection, + sourcePath + }; + this.lastSelection = selectionContext; + } else if (!sourcePath) { + selectionContext = this.lastSelection; } else { - return null; + this.lastSelection = null; + } + if (selectionContext && selectionContext.text.length > maxSelectionLength) { + selectionContext = { + ...selectionContext, + text: selectionContext.text.slice(0, maxSelectionLength) + "... [truncated]" + }; + } + let contextText = null; + if (openNotePaths.length > 0 || selectionContext) { + const lines = [""]; + if (openNotePaths.length > 0) { + lines.push("Currently open notes in Obsidian:"); + for (const path of openNotePaths) { + lines.push(`- ${path}`); + } + } + if (selectionContext) { + lines.push(""); + lines.push(`Selected text (from ${selectionContext.sourcePath}):`); + lines.push('"""'); + lines.push(selectionContext.text); + lines.push('"""'); + } + lines.push(""); + contextText = lines.join("\n"); } - const truncated = text.length > maxSelectionLength; - const trimmed = truncated ? text.slice(0, maxSelectionLength) : text; return { - text: trimmed, - sourcePath: path, - truncated + openNotePaths, + selection: selectionContext, + contextText }; } - formatContext(openPaths, selection) { - if (openPaths.length === 0 && !selection) { - return null; - } - const lines = [""]; - if (openPaths.length > 0) { - lines.push("Currently open notes in Obsidian:"); - for (const path of openPaths) { - lines.push(`- ${path}`); - } - } - if (selection) { - lines.push(""); - lines.push(`Selected text (from ${selection.sourcePath}):`); - lines.push('"""'); - lines.push(selection.text); - if (selection.truncated) { - lines.push("[truncated]"); - } - lines.push('"""'); - } - lines.push(""); - return lines.join("\n"); - } }; // src/main.ts @@ -879,9 +858,8 @@ var OpenCodePlugin = class extends import_obsidian5.Plugin { this.stateChangeCallbacks = []; this.cachedIframeUrl = null; this.lastBaseUrl = null; - this.focusEventRef = null; - this.sidebarEventRefs = []; - this.sidebarRefreshTimer = null; + this.contextEventRefs = []; + this.contextRefreshTimer = null; } async onload() { console.log("Loading OpenCode plugin"); @@ -934,8 +912,7 @@ var OpenCodePlugin = class extends import_obsidian5.Plugin { await this.startServer(); }); } - this.updateFocusListener(); - this.updateSidebarListeners(); + this.updateContextListeners(); this.onProcessStateChange((state) => { if (state === "running") { void this.handleServerRunning(); @@ -954,8 +931,7 @@ var OpenCodePlugin = class extends import_obsidian5.Plugin { await this.saveData(this.settings); this.processManager.updateSettings(this.settings); this.refreshClientState(); - this.updateFocusListener(); - this.updateSidebarListeners(); + this.updateContextListeners(); } // Update project directory and restart server if running async updateProjectDirectory(directory) { @@ -1092,69 +1068,58 @@ var OpenCodePlugin = class extends import_obsidian5.Plugin { } this.lastBaseUrl = nextUiBaseUrl; } - updateFocusListener() { + updateContextListeners() { if (!this.settings.injectWorkspaceContext) { - if (this.focusEventRef) { - this.app.workspace.offref(this.focusEventRef); - this.focusEventRef = null; - } + this.clearContextListeners(); return; } - if (this.focusEventRef) { + if (this.contextEventRefs.length > 0) { return; } - const eventRef = this.app.workspace.on("active-leaf-change", (leaf) => { - if ((leaf == null ? void 0 : leaf.view) instanceof import_obsidian5.MarkdownView) { - this.workspaceContext.updateSelectionFromView(leaf.view); - } - if ((leaf == null ? void 0 : leaf.view.getViewType()) === OPENCODE_VIEW_TYPE) { - void this.updateOpenCodeContext(leaf); - } + const activeLeafRef = this.app.workspace.on("active-leaf-change", () => { + this.scheduleContextRefresh(0); }); - this.focusEventRef = eventRef; - this.registerEvent(eventRef); - } - updateSidebarListeners() { - if (!this.settings.injectWorkspaceContext) { - this.clearSidebarListeners(); - return; - } - if (this.sidebarEventRefs.length > 0) { - return; - } const fileOpenRef = this.app.workspace.on("file-open", () => { - this.scheduleSidebarContextRefresh(); + this.scheduleContextRefresh(); }); - const editorChangeRef = this.app.workspace.on("editor-change", (_editor, view) => { - const markdownView = view instanceof import_obsidian5.MarkdownView ? view : this.app.workspace.getActiveViewOfType(import_obsidian5.MarkdownView); - this.workspaceContext.updateSelectionFromView(markdownView); - this.scheduleSidebarContextRefresh(); + const fileCloseRef = this.app.workspace.on("file-close", () => { + this.scheduleContextRefresh(); }); - this.sidebarEventRefs = [fileOpenRef, editorChangeRef]; - this.sidebarEventRefs.forEach((ref) => this.registerEvent(ref)); + const editorChangeRef = this.app.workspace.on("editor-change", () => { + this.scheduleContextRefresh(500); + }); + this.contextEventRefs = [activeLeafRef, fileOpenRef, fileCloseRef, editorChangeRef]; + this.contextEventRefs.forEach((ref) => this.registerEvent(ref)); } - clearSidebarListeners() { - for (const ref of this.sidebarEventRefs) { + clearContextListeners() { + for (const ref of this.contextEventRefs) { this.app.workspace.offref(ref); } - this.sidebarEventRefs = []; - if (this.sidebarRefreshTimer !== null) { - window.clearTimeout(this.sidebarRefreshTimer); - this.sidebarRefreshTimer = null; + this.contextEventRefs = []; + if (this.contextRefreshTimer !== null) { + window.clearTimeout(this.contextRefreshTimer); + this.contextRefreshTimer = null; } } - scheduleSidebarContextRefresh() { - const leaf = this.getVisibleSidebarOpenCodeLeaf(); + scheduleContextRefresh(delayMs = 300) { + const leaf = this.getOpenCodeLeafForRefresh(); if (!leaf) { return; } - if (this.sidebarRefreshTimer !== null) { - window.clearTimeout(this.sidebarRefreshTimer); + if (this.contextRefreshTimer !== null) { + window.clearTimeout(this.contextRefreshTimer); } - this.sidebarRefreshTimer = window.setTimeout(() => { - this.sidebarRefreshTimer = null; + this.contextRefreshTimer = window.setTimeout(() => { + this.contextRefreshTimer = null; void this.updateOpenCodeContext(leaf); - }, 1e3); + }, delayMs); + } + getOpenCodeLeafForRefresh() { + const activeLeaf = this.app.workspace.activeLeaf; + if ((activeLeaf == null ? void 0 : activeLeaf.view.getViewType()) === OPENCODE_VIEW_TYPE) { + return activeLeaf; + } + return this.getVisibleSidebarOpenCodeLeaf(); } getVisibleSidebarOpenCodeLeaf() { const leaves = this.app.workspace.getLeavesOfType(OPENCODE_VIEW_TYPE); @@ -1192,9 +1157,10 @@ var OpenCodePlugin = class extends import_obsidian5.Plugin { return; } this.cachedIframeUrl = iframeUrl; - const openPaths = this.workspaceContext.getOpenNotePaths(this.settings.maxNotesInContext); - const selection = this.workspaceContext.getSelectedText(this.settings.maxSelectionLength); - const contextText = this.workspaceContext.formatContext(openPaths, selection); + const { contextText } = this.workspaceContext.gatherContext( + this.settings.maxNotesInContext, + this.settings.maxSelectionLength + ); await this.openCodeClient.updateContext({ sessionId, contextText diff --git a/src/OpenCodeClient.ts b/src/OpenCodeClient.ts index 8008682..e277277 100644 --- a/src/OpenCodeClient.ts +++ b/src/OpenCodeClient.ts @@ -34,7 +34,6 @@ export class OpenCodeClient { private uiBaseUrl: string; private projectDirectory: string; private trackedSessionId: string | null = null; - private lastMessageId: string | null = null; private lastPart: OpenCodePart | null = null; constructor(apiBaseUrl: string, uiBaseUrl: string, projectDirectory: string) { @@ -60,7 +59,6 @@ export class OpenCodeClient { resetTracking(): void { this.trackedSessionId = null; - this.lastMessageId = null; this.lastPart = null; } @@ -107,7 +105,6 @@ export class OpenCodeClient { const message = await this.sendPrompt(sessionId, contextText); if (message?.info?.id) { - this.lastMessageId = message.info.id; this.lastPart = message.parts?.[0] ?? null; } } @@ -159,7 +156,6 @@ export class OpenCodeClient { return false; } - this.lastMessageId = null; this.lastPart = null; return true; } diff --git a/src/WorkspaceContext.ts b/src/WorkspaceContext.ts index 90ba560..32b7e9f 100644 --- a/src/WorkspaceContext.ts +++ b/src/WorkspaceContext.ts @@ -3,7 +3,12 @@ import { App, MarkdownView } from "obsidian"; type SelectedTextContext = { text: string; sourcePath: string; - truncated: boolean; +}; + +type WorkspaceContextSnapshot = { + openNotePaths: string[]; + selection: SelectedTextContext | null; + contextText: string | null; }; export class WorkspaceContext { @@ -15,7 +20,7 @@ export class WorkspaceContext { this.app = app; } - getOpenNotePaths(maxNotes: number): string[] { + gatherContext(maxNotes: number, maxSelectionLength: number): WorkspaceContextSnapshot { const leaves = this.app.workspace.getLeavesOfType("markdown"); const paths = new Set(); @@ -27,82 +32,63 @@ export class WorkspaceContext { } } - return Array.from(paths).slice(0, Math.max(0, maxNotes)); - } + const openNotePaths = Array.from(paths).slice(0, Math.max(0, maxNotes)); + const view = this.app.workspace.getActiveViewOfType(MarkdownView) ?? this.lastMarkdownView; - updateSelectionFromView(view: MarkdownView | null): void { if (view) { this.lastMarkdownView = view; } + const sourcePath = view?.file?.path; const selection = view?.editor?.getSelection() ?? ""; - - if (!sourcePath || !selection.trim()) { - this.lastSelection = null; - return; - } - - this.lastSelection = { - text: selection, - sourcePath, - }; - } - - getSelectedText(maxSelectionLength: number): SelectedTextContext | null { - const view = this.app.workspace.getActiveViewOfType(MarkdownView) ?? this.lastMarkdownView; - const sourcePath = view?.file?.path; - const selection = view?.editor?.getSelection() ?? ""; - - let text = ""; - let path = ""; + let selectionContext: SelectedTextContext | null = null; if (sourcePath && selection.trim()) { - text = selection; - path = sourcePath; - this.lastSelection = { text, sourcePath: path }; - } else if (this.lastSelection) { - text = this.lastSelection.text; - path = this.lastSelection.sourcePath; + selectionContext = { + text: selection, + sourcePath, + }; + this.lastSelection = selectionContext; + } else if (!sourcePath) { + selectionContext = this.lastSelection; } else { - return null; + this.lastSelection = null; } - const truncated = text.length > maxSelectionLength; - const trimmed = truncated ? text.slice(0, maxSelectionLength) : text; + if (selectionContext && selectionContext.text.length > maxSelectionLength) { + selectionContext = { + ...selectionContext, + text: selectionContext.text.slice(0, maxSelectionLength) + "... [truncated]", + }; + } + + let contextText: string | null = null; + if (openNotePaths.length > 0 || selectionContext) { + const lines: string[] = [""]; + + if (openNotePaths.length > 0) { + lines.push("Currently open notes in Obsidian:"); + for (const path of openNotePaths) { + lines.push(`- ${path}`); + } + } + + if (selectionContext) { + lines.push(""); + lines.push(`Selected text (from ${selectionContext.sourcePath}):`); + lines.push('"""'); + lines.push(selectionContext.text); + lines.push('"""'); + } + + lines.push(""); + contextText = lines.join("\n"); + } return { - text: trimmed, - sourcePath: path, - truncated, + openNotePaths, + selection: selectionContext, + contextText, }; } - - formatContext(openPaths: string[], selection: SelectedTextContext | null): string | null { - if (openPaths.length === 0 && !selection) { - return null; - } - - const lines: string[] = [""]; - - if (openPaths.length > 0) { - lines.push("Currently open notes in Obsidian:"); - for (const path of openPaths) { - lines.push(`- ${path}`); - } - } - - if (selection) { - lines.push(""); - lines.push(`Selected text (from ${selection.sourcePath}):`); - lines.push('"""'); - lines.push(selection.text); - if (selection.truncated) { - lines.push("[truncated]"); - } - lines.push('"""'); - } - - lines.push(""); - return lines.join("\n"); - } } diff --git a/src/main.ts b/src/main.ts index f528d48..25c0049 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,4 @@ -import { Plugin, WorkspaceLeaf, Notice, EventRef, MarkdownView } from "obsidian"; +import { Plugin, WorkspaceLeaf, Notice, EventRef } from "obsidian"; import { OpenCodeSettings, DEFAULT_SETTINGS, OPENCODE_VIEW_TYPE } from "./types"; import { OpenCodeView } from "./OpenCodeView"; import { OpenCodeSettingTab } from "./SettingsTab"; @@ -15,9 +15,8 @@ export default class OpenCodePlugin extends Plugin { private workspaceContext: WorkspaceContext; private cachedIframeUrl: string | null = null; private lastBaseUrl: string | null = null; - private focusEventRef: EventRef | null = null; - private sidebarEventRefs: EventRef[] = []; - private sidebarRefreshTimer: number | null = null; + private contextEventRefs: EventRef[] = []; + private contextRefreshTimer: number | null = null; async onload(): Promise { console.log("Loading OpenCode plugin"); @@ -83,8 +82,7 @@ export default class OpenCodePlugin extends Plugin { }); } - this.updateFocusListener(); - this.updateSidebarListeners(); + this.updateContextListeners(); this.onProcessStateChange((state) => { if (state === "running") { void this.handleServerRunning(); @@ -107,8 +105,7 @@ export default class OpenCodePlugin extends Plugin { await this.saveData(this.settings); this.processManager.updateSettings(this.settings); this.refreshClientState(); - this.updateFocusListener(); - this.updateSidebarListeners(); + this.updateContextListeners(); } // Update project directory and restart server if running @@ -278,80 +275,67 @@ export default class OpenCodePlugin extends Plugin { this.lastBaseUrl = nextUiBaseUrl; } - private updateFocusListener(): void { + private updateContextListeners(): void { if (!this.settings.injectWorkspaceContext) { - if (this.focusEventRef) { - this.app.workspace.offref(this.focusEventRef); - this.focusEventRef = null; - } + this.clearContextListeners(); return; } - if (this.focusEventRef) { + if (this.contextEventRefs.length > 0) { return; } - const eventRef = this.app.workspace.on("active-leaf-change", (leaf) => { - if (leaf?.view instanceof MarkdownView) { - this.workspaceContext.updateSelectionFromView(leaf.view); - } - if (leaf?.view.getViewType() === OPENCODE_VIEW_TYPE) { - void this.updateOpenCodeContext(leaf); - } + const activeLeafRef = this.app.workspace.on("active-leaf-change", () => { + this.scheduleContextRefresh(0); }); - - this.focusEventRef = eventRef; - this.registerEvent(eventRef); - } - - private updateSidebarListeners(): void { - if (!this.settings.injectWorkspaceContext) { - this.clearSidebarListeners(); - return; - } - - if (this.sidebarEventRefs.length > 0) { - return; - } - const fileOpenRef = this.app.workspace.on("file-open", () => { - this.scheduleSidebarContextRefresh(); + this.scheduleContextRefresh(); }); - const editorChangeRef = this.app.workspace.on("editor-change", (_editor, view) => { - const markdownView = view instanceof MarkdownView ? view : this.app.workspace.getActiveViewOfType(MarkdownView); - this.workspaceContext.updateSelectionFromView(markdownView); - this.scheduleSidebarContextRefresh(); + const fileCloseRef = (this.app.workspace as any).on("file-close", () => { + this.scheduleContextRefresh(); + }); + const editorChangeRef = this.app.workspace.on("editor-change", () => { + this.scheduleContextRefresh(500); }); - this.sidebarEventRefs = [fileOpenRef, editorChangeRef]; - this.sidebarEventRefs.forEach((ref) => this.registerEvent(ref)); + this.contextEventRefs = [activeLeafRef, fileOpenRef, fileCloseRef, editorChangeRef]; + this.contextEventRefs.forEach((ref) => this.registerEvent(ref)); } - private clearSidebarListeners(): void { - for (const ref of this.sidebarEventRefs) { + private clearContextListeners(): void { + for (const ref of this.contextEventRefs) { this.app.workspace.offref(ref); } - this.sidebarEventRefs = []; - if (this.sidebarRefreshTimer !== null) { - window.clearTimeout(this.sidebarRefreshTimer); - this.sidebarRefreshTimer = null; + this.contextEventRefs = []; + if (this.contextRefreshTimer !== null) { + window.clearTimeout(this.contextRefreshTimer); + this.contextRefreshTimer = null; } } - private scheduleSidebarContextRefresh(): void { - const leaf = this.getVisibleSidebarOpenCodeLeaf(); + private scheduleContextRefresh(delayMs: number = 300): void { + const leaf = this.getOpenCodeLeafForRefresh(); if (!leaf) { return; } - if (this.sidebarRefreshTimer !== null) { - window.clearTimeout(this.sidebarRefreshTimer); + if (this.contextRefreshTimer !== null) { + window.clearTimeout(this.contextRefreshTimer); } - this.sidebarRefreshTimer = window.setTimeout(() => { - this.sidebarRefreshTimer = null; + this.contextRefreshTimer = window.setTimeout(() => { + this.contextRefreshTimer = null; void this.updateOpenCodeContext(leaf); - }, 1000); + }, delayMs); + } + + private getOpenCodeLeafForRefresh(): WorkspaceLeaf | null { + const activeLeaf = this.app.workspace.activeLeaf; + if (activeLeaf?.view.getViewType() === OPENCODE_VIEW_TYPE) { + return activeLeaf; + } + + return this.getVisibleSidebarOpenCodeLeaf(); } private getVisibleSidebarOpenCodeLeaf(): WorkspaceLeaf | null { @@ -398,9 +382,10 @@ export default class OpenCodePlugin extends Plugin { this.cachedIframeUrl = iframeUrl; - const openPaths = this.workspaceContext.getOpenNotePaths(this.settings.maxNotesInContext); - const selection = this.workspaceContext.getSelectedText(this.settings.maxSelectionLength); - const contextText = this.workspaceContext.formatContext(openPaths, selection); + const { contextText } = this.workspaceContext.gatherContext( + this.settings.maxNotesInContext, + this.settings.maxSelectionLength + ); await this.openCodeClient.updateContext({ sessionId,