import { App, Plugin, PluginSettingTab, Setting, Vault, Notice, } from "obsidian"; import { format } from "./format.js"; interface ScrapsPluginSettings { scrapsRootDir: string; scrapsPathFormat: string; scrapsFileName: string; } const DEFAULT_SETTINGS: ScrapsPluginSettings = { scrapsRootDir: "_Scraps", scrapsPathFormat: "{time YYYY} Week {time WW MMM}", scrapsFileName: "{time DDDD} {A} {N}", }; const ICON = { New: "file-plus", Convert: "shuffle", Move: "replace", Copy: "copy-plus", } as const; async function mkdirp(vault: Vault, folderPath: string): Promise { const pathParts = folderPath.split("/"); // Start building the path incrementally let currentPath = ""; for (const part of pathParts) { currentPath = currentPath ? `${currentPath}/${part}` : part; try { // Check if folder exists if (!vault.getAbstractFileByPath(currentPath)) { // Create the folder if it doesn't exist await vault.createFolder(currentPath); } } catch (error) { console.error(`Error creating folder ${currentPath}:`, error); throw error; } } } export default class ScrapsPlugin extends Plugin { settings: ScrapsPluginSettings; async ensureScrapDir(): Promise { const pathname = `${this.getScrapDir()}`; await mkdirp(this.app.vault, pathname); return pathname; } getScrapDir(): string { return `${this.settings.scrapsRootDir}/${format( this.settings.scrapsPathFormat )}`; } getScrapFileName(): string { return format(this.settings.scrapsFileName, { ext: "md" }); } getScrapFilePath(): string { return `${this.getScrapDir()}/${this.getScrapFileName()}`; } async createScrap() { await this.ensureScrapDir(); const newScrap = await this.app.vault.create( this.getScrapFilePath(), "" ); await this.app.workspace.getLeaf(false).openFile(newScrap); } async convertToScrap(rename = true) { const currentFile = this.app.workspace.getActiveFile(); if (currentFile === null) { new Notice("No file is currently open"); return; } await this.ensureScrapDir(); const filename = rename ? this.getScrapFileName() : currentFile.name; const renamePath = `${this.getScrapDir()}/${filename}`; await this.app.fileManager.renameFile(currentFile, renamePath); } async copyToScrap() { const currentFile = this.app.workspace.getActiveFile(); if (currentFile === null) { new Notice("No file is currently open"); return; } await this.ensureScrapDir(); const newScrap = await this.app.vault.copy( currentFile, this.getScrapFilePath() ); await this.app.workspace.getLeaf(false).openFile(newScrap); } async onload() { await this.loadSettings(); this.addSettingTab(new ScrapsSettingTab(this.app, this)); this.addCommand({ id: "scraps-new", name: "Scraps: Create new Scrap", icon: ICON.New, callback: async () => { this.createScrap(); }, }); this.addRibbonIcon(ICON.New, "Create new Scrap", async () => { this.createScrap(); }); this.addCommand({ id: "scraps-convert", name: "Scraps: Convert current file to Scrap", icon: ICON.Convert, editorCallback: async () => this.convertToScrap(), }); this.addRibbonIcon(ICON.Convert, "Convert file to Scrap", async () => { this.convertToScrap(); }); this.addCommand({ id: "scraps-move", name: "Scraps: Move current file to Scraps", icon: ICON.Move, editorCallback: async () => this.convertToScrap(false), }); this.addRibbonIcon(ICON.Move, "Move file to Scraps", async () => { this.convertToScrap(false); }); this.addCommand({ id: "scraps-copy", name: "Scraps: Copy current file to Scraps", icon: ICON.Copy, editorCallback: () => this.copyToScrap(), }); this.addRibbonIcon(ICON.Copy, "Copy file to Scraps", async () => { this.copyToScrap(); }); } onunload() {} async loadSettings() { this.settings = Object.assign( {}, DEFAULT_SETTINGS, await this.loadData() ); } async saveSettings() { await this.saveData(this.settings); } } class ScrapsSettingTab extends PluginSettingTab { plugin: ScrapsPlugin; constructor(app: App, plugin: ScrapsPlugin) { super(app, plugin); this.plugin = plugin; } display(): void { const { containerEl } = this; containerEl.empty(); new Setting(containerEl) .setName("Scraps Root Directory") .setDesc("The directory where all your scraps will be stored") .addText((text) => text .setPlaceholder("Enter directory") .setValue(this.plugin.settings.scrapsRootDir) .onChange(async (value) => { this.plugin.settings.scrapsRootDir = value; await this.plugin.saveSettings(); }) ); const pathFormatSetting = new Setting(containerEl) .setName("Scraps Path Format") .setDesc( `Preview: ${format(this.plugin.settings.scrapsPathFormat)}` ) .addText((text) => text .setPlaceholder("Enter format") .setValue(this.plugin.settings.scrapsPathFormat) .onChange(async (value) => { this.plugin.settings.scrapsPathFormat = value; pathFormatSetting.setDesc(`Preview: ${format(value)}`); await this.plugin.saveSettings(); }) ); const fileFormatSetting = new Setting(containerEl) .setName("Scraps File Name Format") .setDesc( `Preview: ${format(this.plugin.settings.scrapsFileName, { ext: "md", })}` ) .addText((text) => text .setPlaceholder("Enter format") .setValue(this.plugin.settings.scrapsFileName) .onChange(async (value) => { this.plugin.settings.scrapsFileName = value; fileFormatSetting.setDesc( `Preview: ${format(value, { ext: "md" })}` ); await this.plugin.saveSettings(); }) ); new Setting(containerEl) .setName("Formatting Help") .setDesc("Tokens: {time }, {A}, {N}, {MW}"); } }