├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github └── workflows │ └── release.yml ├── .gitignore ├── LICENSE ├── README.md ├── esbuild.config.mjs ├── main.ts ├── manifest.json ├── package.json ├── styles.css ├── tsconfig.json └── versions.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | insert_final_newline = true 7 | indent_style = tab 8 | indent_size = 4 9 | tab_width = 4 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | npm node_modules 2 | build -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": [ 5 | "@typescript-eslint" 6 | ], 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/eslint-recommended", 10 | "plugin:@typescript-eslint/recommended" 11 | ], 12 | "parserOptions": { 13 | "sourceType": "module" 14 | }, 15 | "rules": { 16 | "no-unused-vars": "off", 17 | "@typescript-eslint/no-unused-vars": ["error", { "args": "none" }], 18 | "@typescript-eslint/ban-ts-comment": "off", 19 | "no-prototype-builtins": "off", 20 | "@typescript-eslint/no-empty-function": "off" 21 | } 22 | } -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release Obsidian plugin 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | env: 9 | PLUGIN_NAME: obsidian-save-as-gist 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Use Node.js 18 | uses: actions/setup-node@v1 19 | with: 20 | node-version: "14.x" 21 | 22 | - name: Build 23 | id: build 24 | run: | 25 | npm install 26 | npm run build 27 | mkdir ${{ env.PLUGIN_NAME }} 28 | cp main.js manifest.json styles.css ${{ env.PLUGIN_NAME }} 29 | zip -r ${{ env.PLUGIN_NAME }}.zip ${{ env.PLUGIN_NAME }} 30 | ls 31 | echo "::set-output name=tag_name::$(git tag --sort version:refname | tail -n 1)" 32 | 33 | - name: Create Release 34 | id: create_release 35 | uses: actions/create-release@v1 36 | env: 37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 38 | VERSION: ${{ github.ref }} 39 | with: 40 | tag_name: ${{ github.ref }} 41 | release_name: ${{ github.ref }} 42 | draft: false 43 | prerelease: false 44 | 45 | - name: Upload zip file 46 | id: upload-zip 47 | uses: actions/upload-release-asset@v1 48 | env: 49 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 50 | with: 51 | upload_url: ${{ steps.create_release.outputs.upload_url }} 52 | asset_path: ./${{ env.PLUGIN_NAME }}.zip 53 | asset_name: ${{ env.PLUGIN_NAME }}-${{ steps.build.outputs.tag_name }}.zip 54 | asset_content_type: application/zip 55 | 56 | - name: Upload main.js 57 | id: upload-main 58 | uses: actions/upload-release-asset@v1 59 | env: 60 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 61 | with: 62 | upload_url: ${{ steps.create_release.outputs.upload_url }} 63 | asset_path: ./main.js 64 | asset_name: main.js 65 | asset_content_type: text/javascript 66 | 67 | - name: Upload manifest.json 68 | id: upload-manifest 69 | uses: actions/upload-release-asset@v1 70 | env: 71 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 72 | with: 73 | upload_url: ${{ steps.create_release.outputs.upload_url }} 74 | asset_path: ./manifest.json 75 | asset_name: manifest.json 76 | asset_content_type: application/json 77 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # vscode 2 | .vscode 3 | 4 | # Intellij 5 | *.iml 6 | .idea 7 | 8 | # npm 9 | node_modules 10 | package-lock.json 11 | 12 | # Don't include the compiled main.js file in the repo. 13 | # They should be uploaded to GitHub releases instead. 14 | main.js 15 | 16 | # Exclude sourcemaps 17 | *.map 18 | 19 | # obsidian 20 | data.json 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Mattia Gheda 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 4 | associated documentation files (the "Software"), to deal in the Software without restriction, 5 | including without limitation the rights to use, copy, modify, merge, publish, distribute, 6 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 7 | furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial 10 | portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 13 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 14 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES 15 | OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Obsidian Save as Gist 2 | 3 | This plugin provides two commands 4 | 5 | - Save current file in a private Gist 6 | - Save current selection in a private Gist 7 | 8 | In order to use it you will have to generate a [GitHub API Token](https://github.com/settings/tokens/new) with "Create Gist" permission and set it using the plugin's settings. 9 | 10 | Once the gist is created **the Gist URL will be copied to your Clipboard**. 11 | 12 | ### Notes 13 | 14 | - it's currently not possible to generate a public gist 15 | - it's currently not possible to update a gist 16 | 17 | 18 | -------------------------------------------------------------------------------- /esbuild.config.mjs: -------------------------------------------------------------------------------- 1 | import esbuild from "esbuild"; 2 | import process from "process"; 3 | import builtins from 'builtin-modules' 4 | 5 | const banner = 6 | `/* 7 | THIS IS A GENERATED/BUNDLED FILE BY ESBUILD 8 | if you want to view the source, please visit the github repository of this plugin 9 | */ 10 | `; 11 | 12 | const prod = (process.argv[2] === 'production'); 13 | 14 | esbuild.build({ 15 | banner: { 16 | js: banner, 17 | }, 18 | entryPoints: ['main.ts'], 19 | bundle: true, 20 | external: [ 21 | 'obsidian', 22 | 'electron', 23 | '@codemirror/autocomplete', 24 | '@codemirror/closebrackets', 25 | '@codemirror/collab', 26 | '@codemirror/commands', 27 | '@codemirror/comment', 28 | '@codemirror/fold', 29 | '@codemirror/gutter', 30 | '@codemirror/highlight', 31 | '@codemirror/history', 32 | '@codemirror/language', 33 | '@codemirror/lint', 34 | '@codemirror/matchbrackets', 35 | '@codemirror/panel', 36 | '@codemirror/rangeset', 37 | '@codemirror/rectangular-selection', 38 | '@codemirror/search', 39 | '@codemirror/state', 40 | '@codemirror/stream-parser', 41 | '@codemirror/text', 42 | '@codemirror/tooltip', 43 | '@codemirror/view', 44 | ...builtins], 45 | format: 'cjs', 46 | watch: !prod, 47 | target: 'es2016', 48 | logLevel: "info", 49 | sourcemap: prod ? false : 'inline', 50 | treeShaking: true, 51 | outfile: 'main.js', 52 | }).catch(() => process.exit(1)); 53 | -------------------------------------------------------------------------------- /main.ts: -------------------------------------------------------------------------------- 1 | import {App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting} from 'obsidian'; 2 | import {Octokit} from '@octokit/rest'; 3 | 4 | // Remember to rename these classes and interfaces! 5 | 6 | interface SaveAsGistSettings { 7 | githubApiToken?: string; 8 | } 9 | 10 | const DEFAULT_SETTINGS: SaveAsGistSettings = { 11 | githubApiToken: null 12 | } 13 | 14 | export default class SaveAsGist extends Plugin { 15 | settings: SaveAsGistSettings; 16 | 17 | async onload() { 18 | await this.loadSettings(); 19 | 20 | this.addCommand({ 21 | id: 'save-as-new-gist', 22 | name: 'Save current file as a new private Gist', 23 | editorCallback: async (editor: Editor, view: MarkdownView) => { 24 | const noteFile = view.file; // Currently Open Note 25 | const fileName = noteFile.name; 26 | if (!fileName) { 27 | return; // Nothing Open 28 | } 29 | 30 | // Read the currently open note file. 31 | const body = editor.getValue(); 32 | 33 | await this._saveAsGist(fileName, body); 34 | } 35 | }); 36 | 37 | this.addCommand({ 38 | id: 'save-as-new-gist-selection', 39 | name: 'Save current selection as a new private Gist', 40 | editorCallback: async (editor: Editor, view: MarkdownView) => { 41 | const noteFile = view.file; // Currently Open Note 42 | const fileName = noteFile.name; 43 | if (!fileName) { 44 | return; // Nothing Open 45 | } 46 | const body = editor.getSelection(); 47 | 48 | await this._saveAsGist(fileName, body); 49 | } 50 | }); 51 | 52 | // This adds a settings tab so the user can configure various aspects of the plugin 53 | this.addSettingTab(new SaveAsGistSettingTab(this.app, this)); 54 | } 55 | 56 | onunload() { 57 | 58 | } 59 | 60 | async loadSettings() { 61 | this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); 62 | } 63 | 64 | async saveSettings() { 65 | await this.saveData(this.settings); 66 | } 67 | 68 | async _saveAsGist(fileName: string, body: string) { 69 | const token = this.settings.githubApiToken; 70 | 71 | if (!token) { 72 | new Notice('GitHub token not found, check your settings'); 73 | return; 74 | } 75 | try { 76 | const octokit = new Octokit({ 77 | auth: token 78 | }); 79 | 80 | const result = await octokit.rest.gists.create({ 81 | files: { 82 | [fileName]: { 83 | content: body 84 | } 85 | } 86 | }) 87 | 88 | const url = result.data.html_url; 89 | await navigator.clipboard.writeText(url); 90 | 91 | new Notice(`Gist created ${url} - URL copied to your clipboard`); 92 | } catch (err) { 93 | new Notice('There was an error creating your gist, check your token and connection'); 94 | throw err; 95 | } 96 | 97 | } 98 | } 99 | 100 | 101 | class SaveAsGistSettingTab extends PluginSettingTab { 102 | plugin: SaveAsGist; 103 | 104 | constructor(app: App, plugin: SaveAsGist) { 105 | super(app, plugin); 106 | this.plugin = plugin; 107 | } 108 | 109 | display(): void { 110 | const {containerEl} = this; 111 | 112 | containerEl.empty(); 113 | 114 | containerEl.createEl('h2', {text: 'Settings for Save As Gist'}); 115 | 116 | new Setting(containerEl) 117 | .setName('Github API token') 118 | .setDesc('create a token here https://github.com/settings/tokens/new (only gist permission required)') 119 | .addText(text => text 120 | .setPlaceholder('Enter your Github API token') 121 | .setValue(this.plugin.settings.githubApiToken) 122 | .onChange(async (value) => { 123 | this.plugin.settings.githubApiToken = value; 124 | await this.plugin.saveSettings(); 125 | })); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "obsidian-save-as-gist", 3 | "name": "Save as Gist", 4 | "version": "0.0.2", 5 | "minAppVersion": "0.12.0", 6 | "description": "Saving your current note as Gist on github", 7 | "author": "ghedamat", 8 | "authorUrl": "https://ghedam.at", 9 | "isDesktopOnly": false 10 | } 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "obsidian-save-as-gist", 3 | "version": "0.0.3", 4 | "description": "saving current note as a gist", 5 | "main": "main.js", 6 | "scripts": { 7 | "dev": "node esbuild.config.mjs", 8 | "build": "node esbuild.config.mjs production" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "MIT", 13 | "devDependencies": { 14 | "@octokit/rest": "^18.12.0", 15 | "@typescript-eslint/eslint-plugin": "^5.2.0", 16 | "@typescript-eslint/parser": "^5.2.0", 17 | "@types/node": "^16.11.6", 18 | "builtin-modules": "^3.2.0", 19 | "esbuild": "0.13.12", 20 | "obsidian": "^0.12.17", 21 | "tslib": "2.3.1", 22 | "typescript": "4.4.4" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghedamat/obsidian-save-as-gist/4355c1f4e8dbd30e48ce10276f713229e3c15a0f/styles.css -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "inlineSourceMap": true, 5 | "inlineSources": true, 6 | "module": "ESNext", 7 | "target": "ES6", 8 | "allowJs": true, 9 | "noImplicitAny": true, 10 | "moduleResolution": "node", 11 | "importHelpers": true, 12 | "lib": [ 13 | "DOM", 14 | "ES5", 15 | "ES6", 16 | "ES7" 17 | ] 18 | }, 19 | "include": [ 20 | "**/*.ts" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "1.0.1": "0.9.12", 3 | "1.0.0": "0.9.7" 4 | } 5 | --------------------------------------------------------------------------------