mirror of
https://github.com/scotttomaszewski/obsidian-disciples-journal.git
synced 2026-07-22 05:42:13 +00:00
fix: Insert targets last active non-generated note, not the passage pane
The action bar / command 'Insert' used the active pane, which is the (generated) passage note the user just clicked in. Track the last active editable, non-generated markdown note via active-leaf-change and insert there instead (no focus steal; notifies which note). Right-click 'Insert here' is unchanged.
This commit is contained in:
parent
2ea9bc7f0f
commit
2bc00828a4
3 changed files with 65 additions and 14 deletions
|
|
@ -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 <note>` notice. The right-click "Insert here" path is unaffected — it
|
||||
always uses the clicked editor.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue