diff --git a/docs/gotchas.md b/docs/gotchas.md index 4bfb7fb..c57382b 100644 --- a/docs/gotchas.md +++ b/docs/gotchas.md @@ -61,10 +61,21 @@ non-obvious things: - **One selection, globally.** `VerseSelectionService` (plugin-owned, `addChild`) holds a single active selection + its owning controller. Only the owner renders highlight and the action bar, so selecting in one passage clears another. -- **The action bar is per-`Document` and `fixed`-positioned.** It's appended to the - passage's own `doc.body` (popout-safe) and positioned from the passage's - `getBoundingClientRect`. Blockquote text is extracted from the owner's `sourceEl` (its - own document) for the same reason — never query the global `document`. +- **The action bar is per-`Document` and bottom-center docked.** It's appended to the + passage's own `doc.body` (popout-safe). It's docked at the bottom-center of the viewport + via CSS — **not** anchored to the passage — because a full chapter is taller than the + viewport, so anchoring to the passage's `getBoundingClientRect().bottom` rendered the bar + off-screen. Blockquote text is extracted from the owner's `sourceEl` (its own document) so + popouts stay correct — never query the global `document`. - **Selection clears when its passage unloads.** `controller.onunload` calls `service.clearIfOwner(this)`, so a note re-render or pane close drops a stale selection and its bar. +- **"Insert" doesn't target the active pane.** When you click a verse, the active pane + *is* the passage's pane — usually a generated note under the Bible content folder, which + is the last place you want the verse inserted. So the plugin tracks the last active + **editable, non-generated** markdown note (`DisciplesJournalPlugin.handleActiveLeafChange` + → `lastEditableLeaf`, filtered by `isBibleContentFile`) and `resolveInsertTarget()` + returns it. There's no public "leaves in MRU order" API — this `active-leaf-change` + tracking is the stand-in. Insert does **not** steal focus; it drops the text in and shows + an `Inserted … into ` notice. The right-click "Insert here" path is unaffected — it + always uses the clicked editor. diff --git a/src/components/VerseActions.ts b/src/components/VerseActions.ts index bc1a8a1..df1f238 100644 --- a/src/components/VerseActions.ts +++ b/src/components/VerseActions.ts @@ -1,4 +1,4 @@ -import { MarkdownView, Notice, TFile } from "obsidian"; +import { Notice, TFile } from "obsidian"; import { VerseSelection } from "../core/VerseSelection"; import { formatBlockquote, formatCodeBlock, formatInlineReference } from "../utils/VerseFormatter"; import { InsertTargetModal } from "./InsertTargetModal"; @@ -63,12 +63,13 @@ export async function runVerseAction( } if (kind === "insert") { - const editor = plugin.app.workspace.getActiveViewOfType(MarkdownView)?.editor; - if (!editor) { - new Notice("Open a note and place your cursor to insert."); + const view = plugin.resolveInsertTarget(); + if (!view) { + new Notice("No note to insert into — open a note (not a generated Bible note), or right-click where you want it."); return; } - editor.replaceSelection(payload); + view.editor.replaceSelection(payload); + new Notice(`Inserted ${selection.label()} into ${view.file?.basename ?? "note"}`); return; } diff --git a/src/core/DisciplesJournalPlugin.ts b/src/core/DisciplesJournalPlugin.ts index 9be0195..91b41e7 100644 --- a/src/core/DisciplesJournalPlugin.ts +++ b/src/core/DisciplesJournalPlugin.ts @@ -1,4 +1,4 @@ -import {Plugin, MarkdownView, Notice, normalizePath, Editor, Menu, MenuItem} from 'obsidian'; +import {Plugin, MarkdownView, Notice, normalizePath, Editor, Menu, MenuItem, TFile, WorkspaceLeaf} from 'obsidian'; import {ESVApiService} from '../services/ESVApiService'; import {BibleContentService} from '../services/BibleContentService'; import {BibleReferenceRenderer} from '../components/BibleReferenceRenderer'; @@ -37,6 +37,11 @@ export default class DisciplesJournalPlugin extends Plugin { private bibleMarkupProcessor: BibleMarkupProcessor; private verseSelectionService: VerseSelectionService; + // The most recently active editable, non-generated markdown note — the target for + // "Insert selected verses" from the action bar/command (the passage's own pane is + // usually a generated Bible note, which we never want to insert into). + private lastEditableLeaf: WorkspaceLeaf | null = null; + async onload() { // Initialize settings await this.loadSettings(); @@ -104,12 +109,10 @@ export default class DisciplesJournalPlugin extends Plugin { this.addCommand({ id: 'insert-selected-verses', name: 'Insert selected verses at cursor', - editorCallback: (editor: Editor) => { + callback: () => { const active = this.verseSelectionService.get(); if (!active) { new Notice('No verses selected.'); return; } - editor.replaceSelection( - buildPayload(this, active.selection, this.settings.defaultInsertFormat, active.owner.sourceEl) - ); + void runVerseAction(this, 'insert', active.selection, this.settings.defaultInsertFormat, active.owner.sourceEl); } }); @@ -172,6 +175,42 @@ export default class DisciplesJournalPlugin extends Plugin { handleActiveLeafChange() { // Refresh theme/styling when active leaf changes this.updateBibleStyles(); + + // Remember the last editable, non-generated note so "Insert" targets the note the + // user was working in — not the (generated) passage note they clicked to select. + const view = this.app.workspace.getActiveViewOfType(MarkdownView); + if (view?.file && !this.isBibleContentFile(view.file)) { + this.lastEditableLeaf = view.leaf; + } + } + + /** True when a file lives under the configured Bible content folder (a generated note). */ + public isBibleContentFile(file: TFile): boolean { + return file.path.startsWith(normalizePath(this.settings.bibleContentVaultPath) + '/'); + } + + /** + * Resolve where "Insert selected verses" should go: the most recently active editable, + * non-generated markdown note (if still open), else the active note when it isn't a + * generated Bible note. Returns null when there's no suitable target. + */ + public resolveInsertTarget(): MarkdownView | null { + let leaf = this.lastEditableLeaf; + if (leaf) { + let stillOpen = false; + this.app.workspace.iterateRootLeaves((l) => { if (l === leaf) stillOpen = true; }); + if (!stillOpen) leaf = null; + } + + if (leaf?.view instanceof MarkdownView) { + return leaf.view; + } + + const active = this.app.workspace.getActiveViewOfType(MarkdownView); + if (active?.file && !this.isBibleContentFile(active.file)) { + return active; + } + return null; } /**