├── .gitignore ├── .travis.yml ├── LICENSE ├── css └── preview.css ├── package.json ├── readme.md ├── snip.svg ├── src ├── delete.js ├── deleteSnippet.js ├── lib │ └── snippets.js ├── read.js ├── write.js └── writeSnippet.js ├── test ├── cacheSnippets.js └── index.js └── zazu.json /.gitignore: -------------------------------------------------------------------------------- 1 | snippets 2 | node_modules 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - 6 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to -------------------------------------------------------------------------------- /css/preview.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif; 3 | overflow: hidden; 4 | margin: 0; 5 | padding: 0; 6 | } 7 | 8 | .meta { 9 | font-size: 11px; 10 | color: #767676; 11 | width: 100%; 12 | position: absolute; 13 | bottom: 0; 14 | text-align: center; 15 | margin-bottom: 5px; 16 | } 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zazu-snippets", 3 | "version": "0.1.0", 4 | "description": "Snippets for Zazu App.", 5 | "main": "zazu.js", 6 | "scripts": { 7 | "test": "node test" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/tinytacoteam/zazu-snippets.git" 12 | }, 13 | "author": "Tiny Taco Team", 14 | "license": "Unlicense", 15 | "bugs": { 16 | "url": "https://github.com/tinytacoteam/zazu-snippets/issues" 17 | }, 18 | "homepage": "https://github.com/tinytacoteam/zazu-snippets#readme", 19 | "devDependencies": { 20 | "tape": "^4.6.0" 21 | }, 22 | "dependencies": { 23 | "fuzzyfind": "^2.0.0", 24 | "js-htmlencode": "^0.2.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## Snippet manager for Zazu 2 | 3 | ## Configuration (default) 4 | 5 | By default the plugin will store your snippets in your home directory under `.zazu-snippets`. 6 | 7 | ## Configuration (specific folder name) 8 | 9 | Changing the folder name from `.zazu-snippets` to `snippets` in your home directory. 10 | 11 | ~~~ javascript 12 | { 13 | "name": "afaur/zazu-snippets", 14 | "variables": { 15 | "folder": "snippets" 16 | } 17 | } 18 | ~~~ 19 | 20 | ## Configuration (specific containing directory) 21 | 22 | Changing the folder name from `.zazu-snippets` to `snippets` and storing it in a specific directory. 23 | 24 | ~~~ javascript 25 | { 26 | "name": "afaur/zazu-snippets", 27 | "variables": { 28 | "directory": "/Users/username/Documents", 29 | "folder": "snippets" 30 | } 31 | } 32 | ~~~ 33 | 34 | ## Usage 35 | 36 | Finding a snippet: 37 | 38 | ~~~ 39 | snip emoji 40 | ~~~ 41 | 42 | Creating a snippet from your clipboard: 43 | 44 | ~~~ 45 | snipc emoji 46 | ~~~ 47 | 48 | Delete a snippet by name: 49 | 50 | ~~~ 51 | snipd emoji 52 | ~~~ 53 | 54 | ## Installing 55 | 56 | Add the package to your plugins array in `./zazurc.json`. 57 | 58 | ~~~ json 59 | { 60 | "plugins": [ 61 | "tinytacoteam/zazu-snippets" 62 | ] 63 | } 64 | ~~~ 65 | -------------------------------------------------------------------------------- /snip.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/delete.js: -------------------------------------------------------------------------------- 1 | module.exports = (pluginContext) => { 2 | return (key, env = {}) => { 3 | const { console } = pluginContext 4 | const snippets = require('./lib/snippets')(console, env) 5 | return new Promise((resolve, reject) => { 6 | const value = snippets.search(key) 7 | const deleteConfirm = [{ 8 | id: key, title: `Delete snippet called "${key}"`, value: key, 9 | }] 10 | resolve(value ? deleteConfirm : []) 11 | }) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/deleteSnippet.js: -------------------------------------------------------------------------------- 1 | module.exports = (pluginContext) => { 2 | return (name, env = {}) => { 3 | const { console } = pluginContext 4 | const snippets = require('./lib/snippets')(console, env) 5 | snippets.delete(name) 6 | return Promise.resolve() 7 | } 8 | } 9 | 10 | -------------------------------------------------------------------------------- /src/lib/snippets.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const os = require('os') 3 | const path = require('path') 4 | const fuzzy = require('fuzzyfind') 5 | const { htmlEncode } = require('js-htmlencode') 6 | 7 | class Snippets { 8 | constructor (console, env={}) { 9 | this.console = console 10 | this.env = env 11 | // Default or configured path to snippet directory 12 | this.snippetDir = path.join(this.getDirectory(), this.getFolder()) 13 | // Make directory if it does not already exist 14 | if (!fs.existsSync(this.snippetDir)) { fs.mkdirSync(this.snippetDir) } 15 | this.index = {} 16 | this.indexSnippets() 17 | } 18 | 19 | // Determines if a variable was set 20 | envHas(variable) { return this.env.hasOwnProperty(variable) } 21 | 22 | // If user defined a directory use it otherwise use home directory 23 | getDirectory() { 24 | return (this.envHas('directory') ? path.normalize(this.env.directory) : os.homedir()) 25 | } 26 | 27 | // If user defined a folder name use it otherwise use .zazu-snippets 28 | getFolder() { 29 | return (this.envHas('folder') ? this.env.folder : '.zazu-snippets') 30 | } 31 | 32 | indexSnippets () { 33 | fs.readdir(this.snippetDir, (err, data) => { 34 | if (err) return this.console.log('warn', err) 35 | data.map((file) => { 36 | if (file !== '.gitkeep' && file !== '.DS_Store') { 37 | const filePath = path.join(this.snippetDir, file) 38 | fs.readFile(filePath, (err, data) => { 39 | if (err) return this.console.log('warn', err) 40 | this.index[file] = data.toString() 41 | }) 42 | } 43 | }) 44 | }) 45 | } 46 | 47 | create (name, content) { 48 | this.index[name] = content 49 | const filePath = path.join(this.snippetDir, name) 50 | fs.writeFile(filePath, content, (err) => { 51 | if (err) return this.console.log('warn', err) 52 | }) 53 | } 54 | 55 | search (name) { 56 | const accessor = (obj) => obj.key 57 | const items = Object.keys(this.index).map((key) => { 58 | return { 59 | key, 60 | value: this.index[key], 61 | } 62 | }) 63 | return fuzzy(name, items, { accessor }).map((result) => { 64 | return { 65 | id: result.key, 66 | title: result.key, 67 | subtitle: 'Copy snippet to clipboard.', 68 | value: result.value, 69 | preview: ` 70 |
${htmlEncode(result.value)}
71 |
${result.value.length} characters
72 | ` 73 | } 74 | }) 75 | } 76 | 77 | delete (name) { 78 | delete this.index[name] 79 | const filePath = path.join(this.snippetDir, name) 80 | fs.unlink(filePath, function (err) { 81 | if (err) return this.console.log('warn', err) 82 | }) 83 | } 84 | } 85 | 86 | var singleton = null 87 | module.exports = (console, env={}) => { 88 | return singleton || (singleton = new Snippets(console, env)) 89 | } 90 | -------------------------------------------------------------------------------- /src/read.js: -------------------------------------------------------------------------------- 1 | module.exports = (pluginContext) => { 2 | return (query, env = {}) => { 3 | const { cwd, console } = pluginContext 4 | const snippets = require('./lib/snippets')(console, env) 5 | return new Promise((resolve, reject) => { 6 | const results = snippets.search(query) 7 | resolve(results ? results : []) 8 | }) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/write.js: -------------------------------------------------------------------------------- 1 | module.exports = (pluginContext) => { 2 | return (key, env = {}) => { 3 | return new Promise((resolve, reject) => { 4 | resolve([{ 5 | id: key, 6 | title: `Create snippet called "${key}"`, 7 | value: key, 8 | }]) 9 | }) 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /src/writeSnippet.js: -------------------------------------------------------------------------------- 1 | module.exports = (pluginContext) => { 2 | return (name, env = {}) => { 3 | const { console } = pluginContext 4 | const snippets = require('./lib/snippets')(console, env) 5 | const content = pluginContext.clipboard.readText() 6 | snippets.create(name, content) 7 | return Promise.resolve() 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /test/cacheSnippets.js: -------------------------------------------------------------------------------- 1 | const describe = require('tape') 2 | const Snippets = require('../src/lib/snippets') 3 | 4 | const snippets = Snippets(__dirname, { log: () => {} }) 5 | 6 | describe('search for major items', (assert) => { 7 | assert.plan(1) 8 | 9 | snippets.index = { 10 | 'migrations': 'foo', 11 | 'rails_migrations': 'bar', 12 | 'work_email': 'princess@tinytacoteam.github.io', 13 | } 14 | 15 | const results = snippets.search('migrations') 16 | assert.ok(results.length === 2) 17 | }) 18 | 19 | describe('search for minor items', (assert) => { 20 | assert.plan(1) 21 | 22 | snippets.index = { 23 | 'migrations': 'foo', 24 | 'rails_migrations': 'bar', 25 | 'work_email': 'princess@tinytacoteam.github.io', 26 | } 27 | 28 | const results = snippets.search('work') 29 | assert.ok(results.length === 1) 30 | }) 31 | 32 | describe('search for no items', (assert) => { 33 | assert.plan(1) 34 | 35 | snippets.index = { 36 | 'migrations': 'foo', 37 | 'rails_migrations': 'bar', 38 | 'work_email': 'princess@tinytacoteam.github.io', 39 | } 40 | 41 | const results = snippets.search('meow') 42 | assert.ok(results.length === 0) 43 | }) 44 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | require('./cacheSnippets') 2 | -------------------------------------------------------------------------------- /zazu.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Zazu Snippets", 3 | "icon": "snip.svg", 4 | "stylesheet": "css/preview.css", 5 | "blocks": { 6 | "input": [ 7 | { 8 | "id": "Read", 9 | "type": "PrefixScript", 10 | "prefix": "snip", 11 | "space": true, 12 | "args": "Required", 13 | "script": "src/read.js", 14 | "connections": [ 15 | "Copy" 16 | ] 17 | }, 18 | { 19 | "id": "Write", 20 | "type": "PrefixScript", 21 | "prefix": "snipc", 22 | "space": true, 23 | "args": "Required", 24 | "script": "src/write.js", 25 | "connections": [ 26 | "WriteSnippet" 27 | ] 28 | }, 29 | { 30 | "id": "Delete", 31 | "type": "PrefixScript", 32 | "prefix": "snipd", 33 | "space": true, 34 | "args": "Required", 35 | "script": "src/delete.js", 36 | "connections": [ 37 | "DeleteSnippet" 38 | ] 39 | } 40 | ], 41 | "output": [ 42 | { 43 | "id": "Copy", 44 | "type": "CopyToClipboard", 45 | "text": "{value}" 46 | }, 47 | { 48 | "id": "WriteSnippet", 49 | "type": "UserScript", 50 | "script": "src/writeSnippet.js", 51 | "value": "{value}" 52 | }, 53 | { 54 | "id": "DeleteSnippet", 55 | "type": "UserScript", 56 | "script": "src/deleteSnippet.js", 57 | "value": "{value}" 58 | } 59 | ], 60 | "external": [ 61 | { 62 | "id": "Direct", 63 | "type": "Hotkey", 64 | "hotkey": "CmdOrCtrl+Alt+Space", 65 | "connections": [ "Write" ] 66 | } 67 | ] 68 | } 69 | } 70 | --------------------------------------------------------------------------------