From 33aaec15e2f4e1da32f27503ffb91eb79a31d23d Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Thu, 19 Mar 2026 11:33:22 -0700 Subject: [PATCH] Add move to daily note support --- CLAUDE.md | 8 ++ src/modals/daily-note-select-modal.ts | 125 ++++++++++++++++++++------ 2 files changed, 107 insertions(+), 26 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 7a442ce..a929fa2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -22,6 +22,14 @@ npm run build npm run lint ``` +## Development Workflow + +After every code change, run **both**: +1. `npm run build` — type-checks and bundles +2. `npm run lint` — ESLint (Obsidian-specific rules + TypeScript strict checks) + +Fix all lint errors before considering a change complete. + ## Architecture ### Entry Points & Build Process diff --git a/src/modals/daily-note-select-modal.ts b/src/modals/daily-note-select-modal.ts index bf2b671..2b7aae1 100644 --- a/src/modals/daily-note-select-modal.ts +++ b/src/modals/daily-note-select-modal.ts @@ -1,29 +1,102 @@ -import { TodoItem } from 'core/types'; -import { App, Modal, Setting, TFile } from 'obsidian'; +import { App, Modal, Notice, Setting, TFile } from 'obsidian'; +import type { TodoItem } from '../core/types'; +import { HeadingSelectModal } from './heading-select-modal'; +import { + removeTodoBlock, + insertTodoAtBeginning, + collectTodoBlockLines, +} from '../core/todo-transformer'; export class DailyNoteSelectModal extends Modal { - constructor(app: App, todo: TodoItem, file: TFile) { - super(app); - this.setTitle('Select a daily note'); - - let name = ''; - new Setting(this.contentEl) - .setName('Date') - .addMomentFormat((component) => { - component.onChange((value) => { - name = value; - }); - }); - - new Setting(this.contentEl) - .addButton((btn) => - btn - .setButtonText('Submit') - .setCta() - .onClick(() => { - this.close(); - // do some stuff - console.debug(name); - })); - } + private todo: TodoItem; + private sourceFile: TFile; + private selectedDate = ''; + + constructor(app: App, todo: TodoItem, sourceFile: TFile) { + super(app); + this.todo = todo; + this.sourceFile = sourceFile; + this.setTitle('Select a daily note'); + + new Setting(this.contentEl) + .setName('Date') + .addText((text) => { + text.inputEl.type = 'date'; + text.onChange((value) => { + this.selectedDate = value; + }); + }); + + new Setting(this.contentEl) + .addButton((btn) => + btn + .setButtonText('Submit') + .setCta() + .onClick(() => { + this.onSubmit(); + }) + ); + } + + private onSubmit(): void { + if (!this.selectedDate) { + new Notice('Please select a date.'); + return; + } + + const targetFile = this.findDailyNote(this.selectedDate); + if (!targetFile) { + new Notice(`No daily note found for ${this.selectedDate}.`); + return; + } + + this.close(); + this.openTargetFile(targetFile); + } + + private findDailyNote(dateString: string): TFile | null { + interface DailyNotesOptions { format?: string; folder?: string; } + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access + const options = ((this.app as any).internalPlugins?.plugins?.['daily-notes']?.instance?.options ?? {}) as DailyNotesOptions; + const format = options.format ?? 'YYYY-MM-DD'; + const folder = options.folder ?? ''; + + const fileName = window.moment(dateString).format(format); + const filePath = folder ? `${folder}/${fileName}.md` : `${fileName}.md`; + + const file = this.app.vault.getAbstractFileByPath(filePath); + return file instanceof TFile ? file : null; + } + + private openTargetFile(targetFile: TFile): void { + const fileCache = this.app.metadataCache.getFileCache(targetFile); + const headings = fileCache?.headings ?? []; + + if (headings.length > 0) { + new HeadingSelectModal( + this.app, + this.todo, + this.sourceFile, + targetFile, + headings + ).open(); + } else { + void this.moveTodo(targetFile); + } + } + + private async moveTodo(targetFile: TFile): Promise { + let blockLines: string[] = []; + + await this.app.vault.process(this.sourceFile, (content) => { + const lines = content.split('\n'); + const blockLineNumbers = [this.todo.lineNumber, ...collectTodoBlockLines(content, this.todo.lineNumber)]; + blockLines = blockLineNumbers.map((ln) => lines[ln]!); + return removeTodoBlock(content, this.todo.lineNumber); + }); + + await this.app.vault.process(targetFile, (content) => { + return insertTodoAtBeginning(content, blockLines.join('\n')); + }); + } }