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,