diff --git a/plugin/esbuild.config.mjs b/plugin/esbuild.config.mjs index f1fe201..c85d645 100644 --- a/plugin/esbuild.config.mjs +++ b/plugin/esbuild.config.mjs @@ -1,52 +1,54 @@ import esbuild from "esbuild"; import process from "process"; -import builtins from 'builtin-modules' +import builtins from "builtin-modules"; -const banner = -`/* +const banner = `/* THIS IS A GENERATED/BUNDLED FILE BY ESBUILD if you want to view the source, please visit the github repository of this plugin */ `; -const prod = (process.argv[2] === 'production'); +const prod = process.argv[2] === "production"; -esbuild.build({ - banner: { - js: banner, - }, - entryPoints: ['main.ts'], - bundle: true, - external: [ - 'obsidian', - 'electron', - '@codemirror/autocomplete', - '@codemirror/closebrackets', - '@codemirror/collab', - '@codemirror/commands', - '@codemirror/comment', - '@codemirror/fold', - '@codemirror/gutter', - '@codemirror/highlight', - '@codemirror/history', - '@codemirror/language', - '@codemirror/lint', - '@codemirror/matchbrackets', - '@codemirror/panel', - '@codemirror/rangeset', - '@codemirror/rectangular-selection', - '@codemirror/search', - '@codemirror/state', - '@codemirror/stream-parser', - '@codemirror/text', - '@codemirror/tooltip', - '@codemirror/view', - ...builtins], - format: 'cjs', - watch: !prod, - target: 'es2016', - logLevel: "info", - sourcemap: prod ? false : 'inline', - treeShaking: true, - outfile: 'main.js', -}).catch(() => process.exit(1)); +esbuild + .build({ + banner: { + js: banner, + }, + entryPoints: ["main.ts"], + bundle: true, + external: [ + "obsidian", + "electron", + "@codemirror/autocomplete", + "@codemirror/closebrackets", + "@codemirror/collab", + "@codemirror/commands", + "@codemirror/comment", + "@codemirror/fold", + "@codemirror/gutter", + "@codemirror/highlight", + "@codemirror/history", + "@codemirror/language", + "@codemirror/lint", + "@codemirror/matchbrackets", + "@codemirror/panel", + "@codemirror/rangeset", + "@codemirror/rectangular-selection", + "@codemirror/search", + "@codemirror/state", + "@codemirror/stream-parser", + "@codemirror/text", + "@codemirror/tooltip", + "@codemirror/view", + ...builtins, + ], + format: "cjs", + watch: !prod, + target: "es2016", + logLevel: "info", + sourcemap: prod ? false : "inline", + treeShaking: true, + outfile: "main.js", + }) + .catch(() => process.exit(1)); diff --git a/plugin/main.ts b/plugin/main.ts index 163a40c..627ec83 100644 --- a/plugin/main.ts +++ b/plugin/main.ts @@ -1,20 +1,21 @@ -import { App, MarkdownView, Plugin, PluginSettingTab, Setting } from "obsidian"; -import { encryptMarkdown } from "src/encryption"; +import { + EventRef, + MarkdownView, + Menu, + Plugin, + TAbstractFile, + TFile, +} from "obsidian"; import { NoteSharingService } from "src/NoteSharingService"; +import { DEFAULT_SETTINGS, PluginSettings } from "src/obsidian/PluginSettings"; +import SettingsTab from "src/obsidian/SettingsTab"; // Remember to rename these classes and interfaces! -interface MyPluginSettings { - serverUrl: string; -} - -const DEFAULT_SETTINGS: MyPluginSettings = { - serverUrl: "http://localhost:8080", -}; - -export default class MyPlugin extends Plugin { - settings: MyPluginSettings; - noteSharingService: NoteSharingService; +export default class NoteSharingPlugin extends Plugin { + private settings: PluginSettings; + private noteSharingService: NoteSharingService; + private eventRef: EventRef; async onload() { await this.loadSettings(); @@ -22,25 +23,28 @@ export default class MyPlugin extends Plugin { this.settings.serverUrl ); - // This adds a settings tab so the user can configure various aspects of the plugin + // Init settings tab this.addSettingTab(new SettingsTab(this.app, this)); - // This adds a complex command that can check whether the current state of the app allows execution of the command + // Add note sharing command this.addCommand({ id: "obsidian-note-sharing-share-note", name: "Create share link", checkCallback: (checking: boolean) => { - // Conditions to check - const markdownView = + // Only works on Markdown views + const activeView = this.app.workspace.getActiveViewOfType(MarkdownView); - if (markdownView) { - if (!checking) { - this.noteSharingService.shareNote(markdownView); - } - return true; - } + if (!activeView) return false; + if (checking) return true; + this.noteSharingService.shareNote(activeView.getViewData()); }, }); + + this.eventRef = this.app.workspace.on( + "file-menu", + (menu, file, source) => this.onMenuOpenCallback(menu, file, source) + ); + this.registerEvent(this.eventRef); } onunload() {} @@ -57,34 +61,19 @@ export default class MyPlugin extends Plugin { await this.saveData(this.settings); this.noteSharingService.serverUrl = this.settings.serverUrl; } -} -class SettingsTab extends PluginSettingTab { - plugin: MyPlugin; - - constructor(app: App, plugin: MyPlugin) { - super(app, plugin); - this.plugin = plugin; - } - - display(): void { - const { containerEl } = this; - - containerEl.empty(); - - containerEl.createEl("h2", { text: "Obsidian Note Sharing" }); - - new Setting(containerEl) - .setName("Server URL") - .setDesc("Server URL hosting the encrypted notes.") - .addText((text) => - text - .setPlaceholder("enter URL") - .setValue(this.plugin.settings.serverUrl) - .onChange(async (value) => { - this.plugin.settings.serverUrl = value; - await this.plugin.saveSettings(); - }) - ); + // https://github.dev/platers/obsidian-linter/blob/c30ceb17dcf2c003ca97862d94cbb0fd47b83d52/src/main.ts#L139-L149 + onMenuOpenCallback(menu: Menu, file: TAbstractFile, source: string) { + if (file instanceof TFile && file.extension === "md") { + menu.addItem((item) => { + item.setIcon("paper-plane-glyph"); + item.setTitle("Share note"); + item.onClick(async (evt) => { + this.noteSharingService.shareNote( + await this.app.vault.read(file) + ); + }); + }); + } } } diff --git a/plugin/src/NoteSharingService.ts b/plugin/src/NoteSharingService.ts index f8a993f..5a31f69 100644 --- a/plugin/src/NoteSharingService.ts +++ b/plugin/src/NoteSharingService.ts @@ -1,4 +1,4 @@ -import { MarkdownView, requestUrl } from "obsidian"; +import { requestUrl } from "obsidian"; import { encryptMarkdown } from "./encryption"; export class NoteSharingService { @@ -12,8 +12,8 @@ export class NoteSharingService { * @param mdView Markdown file to share. * @returns link to shared note with attached decryption key. */ - public async shareNote(mdView: MarkdownView): Promise { - const cryptData = encryptMarkdown(mdView); + public async shareNote(mdText: string): Promise { + const cryptData = encryptMarkdown(mdText); let url = await this.postNote(cryptData.ciphertext, cryptData.hmac); url += `#${cryptData.key}`; console.log(`Note shared: ${url}`); diff --git a/plugin/src/encryption.ts b/plugin/src/encryption.ts index f38b181..61f03c8 100644 --- a/plugin/src/encryption.ts +++ b/plugin/src/encryption.ts @@ -1,4 +1,4 @@ -import { MarkdownView, moment } from "obsidian"; +import { moment } from "obsidian"; import { generateKey, encryptString } from "./crypto"; export interface EncryptedMarkdown { @@ -7,8 +7,7 @@ export interface EncryptedMarkdown { key: string; } -export function encryptMarkdown(mdView: MarkdownView): EncryptedMarkdown { - const plaintext = mdView.getViewData(); +export function encryptMarkdown(plaintext: string): EncryptedMarkdown { const key = generateKey(moment.now() + plaintext); const { ciphertext, hmac } = encryptString(plaintext, key); return { ciphertext, hmac, key }; diff --git a/plugin/src/obsidian/PluginSettings.ts b/plugin/src/obsidian/PluginSettings.ts new file mode 100644 index 0000000..43cc087 --- /dev/null +++ b/plugin/src/obsidian/PluginSettings.ts @@ -0,0 +1,7 @@ +export interface PluginSettings { + serverUrl: string; +} + +export const DEFAULT_SETTINGS: PluginSettings = { + serverUrl: "http://localhost:8080", +}; diff --git a/plugin/src/obsidian/SettingsTab.ts b/plugin/src/obsidian/SettingsTab.ts new file mode 100644 index 0000000..0df1994 --- /dev/null +++ b/plugin/src/obsidian/SettingsTab.ts @@ -0,0 +1,32 @@ +import NoteSharingPlugin from "main"; +import { App, PluginSettingTab, Setting } from "obsidian"; + +export default class SettingsTab extends PluginSettingTab { + plugin: NoteSharingPlugin; + + constructor(app: App, plugin: NoteSharingPlugin) { + super(app, plugin); + this.plugin = plugin; + } + + display(): void { + const { containerEl } = this; + + containerEl.empty(); + + containerEl.createEl("h2", { text: "Obsidian Note Sharing" }); + + new Setting(containerEl) + .setName("Server URL") + .setDesc("Server URL hosting the encrypted notes.") + .addText((text) => + text + .setPlaceholder("enter URL") + .setValue(this.plugin.settings.serverUrl) + .onChange(async (value) => { + this.plugin.settings.serverUrl = value; + await this.plugin.saveSettings(); + }) + ); + } +} diff --git a/plugin/tsconfig.json b/plugin/tsconfig.json index 1383e2f..08c3248 100644 --- a/plugin/tsconfig.json +++ b/plugin/tsconfig.json @@ -1,23 +1,17 @@ { - "compilerOptions": { - "baseUrl": ".", - "inlineSourceMap": true, - "inlineSources": true, - "module": "ESNext", - "target": "ES6", - "allowJs": true, - "noImplicitAny": true, - "moduleResolution": "node", - "importHelpers": true, - "isolatedModules": true, - "lib": [ - "DOM", - "ES5", - "ES6", - "ES7" - ] - }, - "include": [ - "**/*.ts" - ] + "compilerOptions": { + "baseUrl": ".", + "inlineSourceMap": true, + "inlineSources": true, + "module": "ESNext", + "target": "ES6", + "allowJs": true, + "noImplicitAny": true, + "moduleResolution": "node", + "importHelpers": true, + "isolatedModules": true, + "outDir": "./build" /* Specify an output folder for all emitted files. */, + "lib": ["DOM", "ES5", "ES6", "ES7"] + }, + "include": ["**/*.ts"] } diff --git a/server/prisma/dev.db b/server/prisma/dev.db index eca1353..a8360e4 100644 Binary files a/server/prisma/dev.db and b/server/prisma/dev.db differ diff --git a/server/tsconfig.json b/server/tsconfig.json index a2b8d46..d859901 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -4,7 +4,6 @@ "lib": ["esnext"], "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, "module": "commonjs" /* Specify what module code is generated. */, - "outDir": "./build" /* Specify an output folder for all emitted files. */, "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, "strict": true /* Enable all strict type-checking options. */,