├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── keymaps └── markclip.cson ├── lib └── markclip.coffee ├── package-lock.json ├── package.json └── styles └── markclip.less /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.0.1 - First Release 2 | 3 | * Copy with `cmd + v` 4 | * `saveType` config with `base64`, `file` and `file in folder` 5 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Hans Chan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Markclip - Atom package 2 | 3 | An Atom package to insert image form clipboard into markdown file. 4 | 5 | ## Usage 6 | 7 | Copy some image into clipboard and key `cmd-v` (Mac) or `ctrl-v` (Windows) in markdown file. 8 | 9 | ## Config 10 | 11 | ### saveType 12 | 13 | #### **base64** 14 | 15 | Insert base64 string like 16 | 17 | ``` 18 | ![](data:image/png;base64,...) 19 | ``` 20 | 21 | #### **file** 22 | 23 | Create an image file in the same directory of your markdown file, then insert into markdown with a md5 file name. 24 | 25 | ``` 26 | path 27 | ├── markdown-file-name.md 28 | ├── image-md5-name.png 29 | └── ... 30 | ``` 31 | 32 | ``` 33 | ![](image-md5-name.png) 34 | ``` 35 | 36 | #### **file in folder** 37 | 38 | Create a directory with the same name of the current markdown file. Put the image in the directory, then insert into markdown with a md5 file name. 39 | 40 | ``` 41 | path 42 | ├── markdown-file-name.md 43 | ├── markdown-file-name 44 | │   ├── image-md5-name.png 45 | │   └── ... 46 | └── ... 47 | ``` 48 | 49 | ``` 50 | ![](markdown-file-name/image-md5-name.png) 51 | ``` 52 | 53 | #### **default folder** 54 | 55 | Create a directory with the name specified in the settings (defaults to 'img'). Put the image in the directory, then insert into markdown with an md5 file name. 56 | 57 | ``` 58 | path 59 | ├── markdown-file-name.md 60 | ├── img 61 | │   ├── image-md5-name.png 62 | │   └── ... 63 | └── ... 64 | ``` 65 | 66 | ``` 67 | ![](img/image-md5-name.png) 68 | ``` 69 | 70 | *Use Case*: having a common image directory for a collection of markdown files all in the same folder (e.g. wikis). Reduces the number of image directories compared to **file in folder**. 71 | 72 | #### **custom file** 73 | 74 | Ask to save each time. 75 | 76 | ### folderSpaceReplacer 77 | 78 | Replace spaces to special charset in image folder name while using setting `saveType: file in folder`. Default to `-`. 79 | -------------------------------------------------------------------------------- /keymaps/markclip.cson: -------------------------------------------------------------------------------- 1 | 'atom-text-editor[data-grammar=\'source md\'], atom-text-editor[data-grammar=\'source gfm\'], atom-text-editor[data-grammar=\'text html basic\'], atom-text-editor[data-grammar=\'text md\'], atom-text-editor[data-grammar=\'text plain\'], atom-text-editor[data-grammar=\'text plain null-grammar\']': 2 | "ctrl-v": "markclip:insert" 3 | "cmd-v": "markclip:insert" 4 | -------------------------------------------------------------------------------- /lib/markclip.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs' 2 | path = require 'path' 3 | mkdirp = require 'mkdirp' 4 | md5 = require 'md5' 5 | PKG = require '../package.json' 6 | {CompositeDisposable} = require 'atom' 7 | 8 | TAG_TEXT_EDITOR = 'ATOM-TEXT-EDITOR' 9 | SAVE_TYPE_BASE64 = 'base64' 10 | SAVE_TYPE_FILE = 'file' 11 | SAVE_TYPE_FILE_IN_FOLDER = 'file in folder' 12 | SAVE_TYPE_DEFAULT_FOLDER = 'default folder' 13 | SAVE_TYPE_CUSTOM_FILE = 'custom file' 14 | FILE_EXT = ['.md', '.markdown', '.mdown', '.mkd', '.mkdown'] 15 | SPACE_REPLACER = '-'; 16 | SPACE_REG = /\s+/g; 17 | 18 | module.exports = Markclip = 19 | config: 20 | saveType: 21 | type: 'string' 22 | description: 'Where to save the clipboard image file' 23 | default: SAVE_TYPE_BASE64 24 | enum: [SAVE_TYPE_BASE64, SAVE_TYPE_FILE, SAVE_TYPE_FILE_IN_FOLDER, SAVE_TYPE_DEFAULT_FOLDER, SAVE_TYPE_CUSTOM_FILE] 25 | order: 10 26 | folderSpaceReplacer: 27 | type: 'string' 28 | description: 'A charset to replace spaces in image folder name' 29 | default: SPACE_REPLACER 30 | order: 20 31 | defaultFolder: 32 | type: 'string' 33 | description: "A folder to save image, use with `saveType = #{SAVE_TYPE_DEFAULT_FOLDER}`" 34 | default: 'img' 35 | order: 30 36 | 37 | handleInsertEvent: (e) -> 38 | textEditor = atom.workspace.getActiveTextEditor() 39 | # do nothing if there is no ActiveTextEditor 40 | return if !textEditor 41 | 42 | # CHECK: do nothing if no image 43 | clipboard = require('clipboard') 44 | img = clipboard.readImage() 45 | if img.isEmpty() 46 | e.abortKeyBinding() 47 | return 48 | 49 | # CHECK: do nothing with unsaved file 50 | filePath = textEditor.getPath() 51 | if not filePath 52 | atom.notifications.addWarning(PKG.name + ': Markdown file NOT saved', { 53 | detail: 'save your file as ' + FILE_EXT.map((n) => '"' + n + '"').join(', ') 54 | }) 55 | return 56 | 57 | # CHECK: file type should in FILE_EXT 58 | filePathObj = path.parse(filePath) 59 | return if FILE_EXT.indexOf(filePathObj.ext) < 0 60 | 61 | saveType = atom.config.get('markclip.saveType') 62 | # atom 1.12 img.toDataURL / atom 1.11 img.toDataUrl 63 | imgDataURL = if img.toDataURL then img.toDataURL() else img.toDataUrl() 64 | # IF:saveType: SAVE AS A FILE 65 | if saveType == SAVE_TYPE_FILE_IN_FOLDER || saveType == SAVE_TYPE_FILE || saveType == SAVE_TYPE_DEFAULT_FOLDER 66 | imgFileDir = filePathObj.dir 67 | # IF:saveType: SAVE IN FOLDER or SAVE IN DEFAULT FOLDER, create it 68 | if saveType == SAVE_TYPE_FILE_IN_FOLDER || saveType == SAVE_TYPE_DEFAULT_FOLDER 69 | folderSpaceReplacer = atom.config.get('markclip.folderSpaceReplacer').replace(SPACE_REG, '') || SPACE_REPLACER; 70 | folderName = if saveType == SAVE_TYPE_FILE_IN_FOLDER then filePathObj.name else atom.config.get('markclip.defaultFolder'); 71 | imgFileDir = path.join(imgFileDir, folderName.replace(SPACE_REG, folderSpaceReplacer)) 72 | mkdirp.sync(imgFileDir) 73 | # create file with md5 name 74 | imgFilePath = path.join(imgFileDir, @getDefaultImageName(imgDataURL)) 75 | imgPNG = if img.toPNG then img.toPNG() else img.toPng() 76 | fs.writeFileSync(imgFilePath, imgPNG); 77 | @insertImgIntoEditor(textEditor, path.relative(filePathObj.dir, imgFilePath).replace(/\\/g, '/')) 78 | # IF:saveType: CUSTOM FILE 79 | else if saveType == SAVE_TYPE_CUSTOM_FILE 80 | newItemPath = atom.applicationDelegate.showSaveDialog({ 81 | defaultPath: path.join(filePathObj.dir, @getDefaultImageName(imgDataURL)) 82 | }) 83 | if newItemPath 84 | imgPNG = if img.toPNG then img.toPNG() else img.toPng() 85 | fs.writeFileSync(newItemPath, imgPNG); 86 | @insertImgIntoEditor(textEditor, path.relative(filePathObj.dir, newItemPath.replace(/\\/g, '/'))) 87 | # IF:saveType: SAVE AS BASE64 88 | else 89 | @insertImgIntoEditor(textEditor, imgDataURL) 90 | 91 | insertImgIntoEditor: (textEditor, src) -> 92 | textEditor.insertText("![](#{src})\n") 93 | getDefaultImageName: (imgDataURL) -> 94 | return md5(imgDataURL).replace('=', '') + '.png' 95 | 96 | subscriptions: null 97 | activate: (state) -> 98 | # Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable 99 | @subscriptions = new CompositeDisposable 100 | @subscriptions.add atom.commands.add 'atom-workspace', 101 | 'markclip:insert': (e) => @handleInsertEvent(e) 102 | @subscriptions.add atom.config.observe 'markclip.saveType', (val) -> 103 | saveType = val 104 | @subscriptions.add atom.config.observe 'markclip.folderSpaceReplacer', (val) -> 105 | folderSpaceReplacer = val 106 | 107 | # atom.contextMenu.add { 108 | # 'atom-text-editor': [{ 109 | # label: 'Bookmark-----', 110 | # command: 'my-package:toggle' 111 | # }] 112 | # } 113 | # console.log 'abc' 114 | 115 | deactivate: -> 116 | @subscriptions.dispose() 117 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "markclip", 3 | "version": "0.5.7", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "charenc": { 8 | "version": "0.0.2", 9 | "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", 10 | "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" 11 | }, 12 | "crypt": { 13 | "version": "0.0.2", 14 | "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", 15 | "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" 16 | }, 17 | "is-buffer": { 18 | "version": "1.1.6", 19 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", 20 | "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" 21 | }, 22 | "md5": { 23 | "version": "2.2.1", 24 | "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", 25 | "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=", 26 | "requires": { 27 | "charenc": "~0.0.1", 28 | "crypt": "~0.0.1", 29 | "is-buffer": "~1.1.1" 30 | } 31 | }, 32 | "minimist": { 33 | "version": "0.0.8", 34 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 35 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 36 | }, 37 | "mkdirp": { 38 | "version": "0.5.1", 39 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 40 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 41 | "requires": { 42 | "minimist": "0.0.8" 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "markclip", 3 | "main": "./lib/markclip", 4 | "version": "0.5.7", 5 | "description": "An Atom package to insert image form clipboard into markdown file", 6 | "keywords": [ 7 | "atom", 8 | "package", 9 | "markdown", 10 | "clipboard", 11 | "image" 12 | ], 13 | "license": "MIT", 14 | "engines": { 15 | "atom": ">=1.0.0 <2.0.0" 16 | }, 17 | "dependencies": { 18 | "md5": "^2.0.0", 19 | "mkdirp": "^0.5.1" 20 | }, 21 | "bugs": { 22 | "url": "https://github.com/csbun/atom-markclip/issues" 23 | }, 24 | "homepage": "https://github.com/csbun/atom-markclip#readme", 25 | "devDependencies": {}, 26 | "scripts": { 27 | "publish": "apm publish patch", 28 | "dev": "apm link" 29 | }, 30 | "author": "Hans Chan " 31 | } 32 | -------------------------------------------------------------------------------- /styles/markclip.less: -------------------------------------------------------------------------------- 1 | // The ui-variables file is provided by base themes provided by Atom. 2 | // 3 | // See https://github.com/atom/atom-dark-ui/blob/master/styles/ui-variables.less 4 | // for a full listing of what's available. 5 | @import "ui-variables"; 6 | 7 | .markclip { 8 | } 9 | --------------------------------------------------------------------------------