fix(streaming): re-resolve the leaf per delta so deltas stay in the right file
This commit is contained in:
+25
-21
@@ -252,9 +252,14 @@ export default class BeaverPlugin extends Plugin {
|
|||||||
const filename = this.gatewayFilename(file);
|
const filename = this.gatewayFilename(file);
|
||||||
if (!filename) return;
|
if (!filename) return;
|
||||||
|
|
||||||
const { editor, view } = this.findEditorFor(file);
|
// Snapshot the buffer if the file is open, otherwise read disk. We
|
||||||
const content = editor
|
// don't keep the editor: an ``Editor`` belongs to a leaf, not a
|
||||||
? editor.getValue()
|
// file, so if the user opens another note in the same tab mid-
|
||||||
|
// stream the old handle now points at the wrong document. Every
|
||||||
|
// delta and the final write re-resolve the leaf by ``file.path``.
|
||||||
|
const initialEditor = this.findEditorFor(file);
|
||||||
|
const content = initialEditor
|
||||||
|
? initialEditor.getValue()
|
||||||
: await this.app.vault.read(file);
|
: await this.app.vault.read(file);
|
||||||
|
|
||||||
const notice = new Notice(`Beaver: sending to ${agent}…`, 0);
|
const notice = new Notice(`Beaver: sending to ${agent}…`, 0);
|
||||||
@@ -269,7 +274,10 @@ export default class BeaverPlugin extends Plugin {
|
|||||||
// write the partial to disk. The gateway also skips its own
|
// write the partial to disk. The gateway also skips its own
|
||||||
// intermediate file writes on this endpoint, so the local
|
// intermediate file writes on this endpoint, so the local
|
||||||
// file (and Obsidian Sync's copy) only sees the final state
|
// file (and Obsidian Sync's copy) only sees the final state
|
||||||
// once.
|
// once. Re-resolved per delta: if ``file`` is no longer
|
||||||
|
// open anywhere, the delta is dropped and the final
|
||||||
|
// ``vault.modify`` catches the buffer up.
|
||||||
|
const editor = this.findEditorFor(file);
|
||||||
if (editor) spliceIntoEditor(editor, newContent);
|
if (editor) spliceIntoEditor(editor, newContent);
|
||||||
},
|
},
|
||||||
onDone: (resp) => {
|
onDone: (resp) => {
|
||||||
@@ -305,7 +313,7 @@ export default class BeaverPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (typeof resp.new_content === "string") {
|
if (typeof resp.new_content === "string") {
|
||||||
await this.writeBack(file, editor, view, resp.new_content);
|
await this.writeBack(file, resp.new_content);
|
||||||
}
|
}
|
||||||
new Notice(`Beaver: ${agent} replied`);
|
new Notice(`Beaver: ${agent} replied`);
|
||||||
}
|
}
|
||||||
@@ -324,29 +332,22 @@ export default class BeaverPlugin extends Plugin {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private findEditorFor(file: TFile): {
|
private findEditorFor(file: TFile): Editor | null {
|
||||||
editor: Editor | null;
|
|
||||||
view: MarkdownView | null;
|
|
||||||
} {
|
|
||||||
// Walk open markdown views — we want the editor instance that owns
|
// Walk open markdown views — we want the editor instance that owns
|
||||||
// ``file`` so we can use setValue (keeps the buffer's edit history
|
// ``file`` *right now* so we can splice (keeps the buffer's edit
|
||||||
// intact) instead of falling back to vault.modify.
|
// history intact) instead of falling back to vault.modify. Never
|
||||||
|
// cached by callers: ``view.file`` changes when the user navigates
|
||||||
|
// within the same leaf, and is ``null`` between switches.
|
||||||
const leaves = this.app.workspace.getLeavesOfType("markdown");
|
const leaves = this.app.workspace.getLeavesOfType("markdown");
|
||||||
for (const leaf of leaves) {
|
for (const leaf of leaves) {
|
||||||
const view = leaf.view as MarkdownView;
|
const view = leaf.view as MarkdownView;
|
||||||
if (view?.file?.path === file.path) {
|
if (view?.file?.path === file.path) return view.editor;
|
||||||
return { editor: view.editor, view };
|
|
||||||
}
|
}
|
||||||
}
|
return null;
|
||||||
return { editor: null, view: null };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async writeBack(
|
private async writeBack(file: TFile, newContent: string): Promise<void> {
|
||||||
file: TFile,
|
const editor = this.findEditorFor(file);
|
||||||
editor: Editor | null,
|
|
||||||
_view: MarkdownView | null,
|
|
||||||
newContent: string,
|
|
||||||
): Promise<void> {
|
|
||||||
if (editor) {
|
if (editor) {
|
||||||
// Reuse the splice path so the final write (frontmatter refresh
|
// Reuse the splice path so the final write (frontmatter refresh
|
||||||
// at the top + USER_SCAFFOLD appended at the bottom) preserves
|
// at the top + USER_SCAFFOLD appended at the bottom) preserves
|
||||||
@@ -354,6 +355,9 @@ export default class BeaverPlugin extends Plugin {
|
|||||||
spliceIntoEditor(editor, newContent);
|
spliceIntoEditor(editor, newContent);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// File not open (anymore): write to disk. Obsidian refreshes any
|
||||||
|
// buffer that opens it later, and a leaf that shows it in another
|
||||||
|
// pane gets the update through the vault event.
|
||||||
await this.app.vault.modify(file, newContent);
|
await this.app.vault.modify(file, newContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user