skylernorgaard_obsidian-bib.../createStudyNote.ts
2026-07-16 11:44:26 -04:00

141 lines
3.8 KiB
TypeScript

import { App, MarkdownView, Notice, TFile, normalizePath } from 'obsidian';
import type StudyBiblePlugin from './main';
import * as Utils from './utils';
import { openStudyNoteFile } from './openStudyNote';
type AppWithCommands = App & {
commands?: { executeCommandById: (id: string) => boolean };
};
async function resolveVersesBlockquote(
app: App,
plugin: StudyBiblePlugin,
book: string,
chapter: number,
verseRef: string
): Promise<string> {
const chapterPath = normalizePath(
`${plugin.settings.bibleBooksFolder}/${Utils.bookToDisplayName(book)} ${chapter}.md`
);
const chapterFile = app.vault.getAbstractFileByPath(chapterPath);
if (!(chapterFile instanceof TFile)) {
return '';
}
const content = await app.vault.read(chapterFile);
const verses = Utils.extractVersesFromChapterMarkdown(content, verseRef);
if (!verses) {
return '';
}
return `${verses} - ${Utils.BIBLE_TRANSLATION_LABEL}`;
}
export async function createStudyNote(
app: App,
plugin: StudyBiblePlugin,
chapterFile: TFile,
verseRef: string | null,
onCreated?: (file: TFile) => void,
title?: string,
referenceInput?: string
): Promise<TFile | null> {
const parsed = Utils.parseBookChapter(chapterFile.basename);
if (!parsed.book || parsed.chapter <= 0) {
new Notice('Could not read chapter from file');
return null;
}
let book = parsed.book;
let chapter = parsed.chapter;
let resolvedVerseRef = verseRef;
if (referenceInput?.trim()) {
const refParsed = Utils.parseStudyNoteReferenceInput(referenceInput.trim());
if (!refParsed) {
new Notice('Could not read verse reference');
return null;
}
book = refParsed.book;
chapter = refParsed.chapter;
resolvedVerseRef = refParsed.verseRef;
}
const notesFolder = plugin.settings.bibleNotesFolder;
const filename = Utils.buildQuickStudyNoteFilename(
book,
chapter,
resolvedVerseRef,
title
);
const path = normalizePath(`${notesFolder}/${filename}`);
if (await app.vault.adapter.exists(path)) {
new Notice('A note with this name already exists');
return null;
}
try {
const tags = [Utils.bookToTagName(book)];
const versesBlockquote =
resolvedVerseRef?.trim()
? await resolveVersesBlockquote(app, plugin, book, chapter, resolvedVerseRef)
: '';
const body = await Utils.buildStudyNoteBody(app, notesFolder, tags, {
versesBlockquote,
});
const file = await app.vault.create(path, body);
onCreated?.(file);
await openStudyNoteFile(app, file, {
notesFolder,
resolveReturnChapter: () =>
Utils.getLastBibleChapter() ?? plugin.getCurrentReaderChapter(),
});
if (title?.trim()) {
await focusJournalBlock(app);
} else {
await focusNoteTitle(app);
}
return file;
} catch (error) {
console.error('Failed to create study note', error);
new Notice('Could not create study note');
return null;
}
}
async function waitForEditorReady(): Promise<void> {
await new Promise<void>((resolve) => {
window.requestAnimationFrame(() => window.requestAnimationFrame(() => resolve()));
});
}
async function focusJournalBlock(app: App): Promise<void> {
await waitForEditorReady();
const view = app.workspace.getActiveViewOfType(MarkdownView);
if (!view) {
return;
}
const content = view.editor.getValue();
const journalMatch = content.match(/\*\*Thoughts:\s*[\d/]+(?::)?\*\*\s*\n>\s*/);
if (journalMatch?.index !== undefined) {
const cursor = journalMatch.index + journalMatch[0].length;
view.editor.setCursor(view.editor.offsetToPos(cursor));
view.editor.focus();
}
}
async function focusNoteTitle(app: App): Promise<void> {
await waitForEditorReady();
const internal = app as AppWithCommands;
for (const commandId of ['workspace:edit-file-title', 'file-explorer:move-file']) {
if (internal.commands?.executeCommandById(commandId)) {
return;
}
}
await focusJournalBlock(app);
}