diff --git a/src/file-preview.ts b/src/file-previews.ts similarity index 97% rename from src/file-preview.ts rename to src/file-previews.ts index b623c2e..631bafb 100644 --- a/src/file-preview.ts +++ b/src/file-previews.ts @@ -9,7 +9,7 @@ export const FILETYPE_MAP: {[key: string]: typeof ConvertableFileView} = { export function registerFilePreviews(plugin: Plugin) { for (const [filetype, view] of Object.entries(FILETYPE_MAP)) { // @ts-ignore - plugin.registerView(view.VIEW_TYPE, (leaf) => new view(leaf, this)); + plugin.registerView(view.VIEW_TYPE, (leaf) => new view(leaf, plugin)); // @ts-ignore plugin.registerExtensions([filetype], view.VIEW_TYPE); diff --git a/src/filetypes/convertable-file-view.ts b/src/filetypes/convertable-file-view.ts index e97863a..5eefcae 100644 --- a/src/filetypes/convertable-file-view.ts +++ b/src/filetypes/convertable-file-view.ts @@ -1,13 +1,16 @@ import { Notice, TFile, TextFileView, WorkspaceLeaf } from "obsidian"; import * as path from "path"; +import DocxerPlugin from "src/main"; export default abstract class ConvertableFileView extends TextFileView { + plugin: DocxerPlugin; fileContent: string; header: HTMLElement|null = null; content: HTMLElement|null = null; - constructor(leaf: WorkspaceLeaf) { + constructor(leaf: WorkspaceLeaf, plugin: DocxerPlugin) { super(leaf); + this.plugin = plugin; } getDisplayText(): string { @@ -70,7 +73,12 @@ export default abstract class ConvertableFileView extends TextFileView { if (!this.file) return; const targetFilepath = path.join(path.dirname(this.file.path), path.basename(this.file.path, path.extname(this.file.path)) + ".md"); - const attachmentsDirectory = path.dirname(targetFilepath); + const attachmentsDirectory = { + "vault": "", + "custom": this.plugin.settings.customAttachmentsFolder, + "same": path.dirname(targetFilepath), + "subfolder": path.join(path.dirname(targetFilepath), this.plugin.settings.customAttachmentsFolder) + }[this.plugin.settings.attachmentsFolder]; const markdown = await this.toMarkdown(attachmentsDirectory); if (!markdown) { @@ -78,6 +86,10 @@ export default abstract class ConvertableFileView extends TextFileView { return; } - this.app.vault.create(targetFilepath, markdown); + const convertedFile = await this.app.vault.create(targetFilepath, markdown); + this.leaf.openFile(convertedFile); + + if (this.plugin.settings.deleteFileAfterConversion) + this.app.vault.delete(this.file); } } \ No newline at end of file diff --git a/src/filetypes/docx.ts b/src/filetypes/docx.ts index 6d0be94..da851e7 100644 --- a/src/filetypes/docx.ts +++ b/src/filetypes/docx.ts @@ -3,7 +3,7 @@ import * as mammoth from "mammoth"; import { NodeHtmlMarkdown } from 'node-html-markdown' import { renderAsync } from 'docx-preview' import * as path from "path"; -import { toValidFilename } from "src/utils"; +import { createMissingFolders, toObsidianPath, toValidFilename } from "src/utils"; export default class DocxFileView extends ConvertableFileView { static readonly VIEW_TYPE = "docx-view"; @@ -30,8 +30,9 @@ export default class DocxFileView extends ConvertableFileView { convertImage: mammoth.images.imgElement((image: any) => { return image.read().then((imageBinary: any) => { const filename = toValidFilename(image.altText) + "." + image.contentType.split("/")[1]; - const filepath = path.join(attachmentsDirectory, filename); + const filepath = toObsidianPath(path.join(attachmentsDirectory, filename)); + createMissingFolders(this.app, filepath); this.app.vault.createBinary(filepath, imageBinary); return { alt: image.altText, src: filepath }; }); diff --git a/src/main.ts b/src/main.ts index e6d15d0..6324af2 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,6 +1,6 @@ import { Plugin } from 'obsidian'; import { DocxerPluginSettings, DocxerSettingTab, DEFAULT_SETTINGS } from './settings'; -import { registerFilePreviews } from './file-preview'; +import { registerFilePreviews } from './file-previews'; export default class DocxerPlugin extends Plugin { settings: DocxerPluginSettings; diff --git a/src/settings.ts b/src/settings.ts index 7c5b5f4..a599d84 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -2,11 +2,15 @@ import DocxerPlugin from "./main"; import { App, PluginSettingTab, Setting } from "obsidian"; export interface DocxerPluginSettings { - deleteDocxAfterConversion: boolean; + deleteFileAfterConversion: boolean; + attachmentsFolder: "vault" | "custom" | "same" | "subfolder"; + customAttachmentsFolder: string; } export const DEFAULT_SETTINGS: Partial = { - deleteDocxAfterConversion: false, + deleteFileAfterConversion: false, + attachmentsFolder: "subfolder", + customAttachmentsFolder: "Attachments" }; export class DocxerSettingTab extends PluginSettingTab { @@ -22,13 +26,44 @@ export class DocxerSettingTab extends PluginSettingTab { containerEl.empty(); new Setting(containerEl) - .setName("Delete .docx after conversion") - .setDesc("Delete .docx file after pressing the conversion button.") + .setName("Delete file after conversion") + .setDesc("Delete file after pressing the conversion button.") .addToggle((toggle) => toggle - .setValue(this.plugin.settings.deleteDocxAfterConversion) + .setValue(this.plugin.settings.deleteFileAfterConversion) .onChange(async (value) => { - this.plugin.settings.deleteDocxAfterConversion = value; + this.plugin.settings.deleteFileAfterConversion = value; + await this.plugin.saveSettings(); + }) + ); + + new Setting(containerEl) + .setName("Attachments Folder") + .setDesc("Specify the destination for attachments extracted during file conversion.") + .addDropdown((dropdown) => + dropdown + .addOptions({ + "vault": "Vault folder", + "custom": "In the folder specified below", + "same": "Same folder as current file", + "subfolder": "In subfolder under current folder" + }) + .setValue(this.plugin.settings.attachmentsFolder) + .onChange(async (value) => { + this.plugin.settings.attachmentsFolder = value as any; + await this.plugin.saveSettings(); + }) + ); + + new Setting(containerEl) + .setName("Custom Attachments Folder") + .setDesc("Specify the name of the folder where attachments will be extracted.") + .addText((text) => + text + .setPlaceholder("Attachments") + .setValue(this.plugin.settings.customAttachmentsFolder) + .onChange(async (value) => { + this.plugin.settings.customAttachmentsFolder = value; await this.plugin.saveSettings(); }) ); diff --git a/src/utils.ts b/src/utils.ts index f5fadac..035d7e4 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,24 @@ +import { App } from "obsidian"; +import * as path from "path"; + export function toValidFilename(filename: string): string { let validFilename = filename.replace(/[^a-zA-Z0-9öüäÖÜÄ.\-]/g, ""); return validFilename; +} + +export function toObsidianPath(path: string): string { + return path.replace(/\\/g, "/"); +} + +export function createMissingFolders(app: App, filepath: string) { + const folder = path.dirname(filepath); + const folders = folder.contains("\\") ? folder.split("\\") : folder.split("/"); + + let currentFolder = ""; + for (const folder of folders) { + currentFolder = path.join(currentFolder, folder); + + if (!app.vault.getAbstractFileByPath(currentFolder)) + app.vault.createFolder(currentFolder); + } } \ No newline at end of file diff --git a/styles.css b/styles.css index f3ac9c1..b28bef9 100644 --- a/styles.css +++ b/styles.css @@ -10,5 +10,6 @@ } .docx-wrapper { + padding: 0 !important; background-color: var(--background-primary) !important; } \ No newline at end of file