├── .github ├── dependabot.yml └── workflows │ └── auto-merge.yml ├── demo.gif ├── .gitignore ├── .gitattributes ├── src ├── packal.zsh └── list.coffee ├── lib └── new.zsh ├── LICENSE.md ├── Cakefile ├── README.md ├── package.json └── info.plist /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: / 5 | schedule: 6 | interval: daily 7 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:b4304a4cdd6bde3e90038725b303004db7eec8bd4281e94b18a450cc472c7b68 3 | size 4441661 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # macOS 2 | .DS_Store 3 | 4 | # npm 5 | node_modules/ 6 | npm* 7 | *.tgz 8 | pack*/ 9 | 10 | *.js 11 | 12 | icon.png 13 | *.alfredworkflow 14 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | [attr]lfs filter=lfs diff=lfs merge=lfs -text 2 | *.png lfs 3 | *.gif lfs 4 | 5 | *.alfredworkflow filter=zip diff=zip 6 | 7 | *.coffee linguist-language=JavaScript 8 | Cakefile linguist-language=JavaScript 9 | -------------------------------------------------------------------------------- /src/packal.zsh: -------------------------------------------------------------------------------- 1 | #! /bin/zsh -f 2 | set -- ${1:-test} info.plist 3 | 4 | npm pack && # unpack 5 | tar -xf *.tgz && 6 | 7 | npm run icon && mv icon.png package 8 | cd $_ 9 | 10 | copy-node-modules .. . 11 | 12 | cp -f {,.}$2 && cake -w plist < .$2 > $2 13 | rm package.json 14 | 15 | zip -qryX ../$1.alfredworkflow * 16 | -------------------------------------------------------------------------------- /.github/workflows/auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: Dependabot Auto Merge 2 | on: 3 | pull_request: 4 | 5 | jobs: 6 | auto-merge: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: ahmadnassri/action-dependabot-auto-merge@v2 10 | if: github.actor == 'dependabot[bot]' 11 | with: 12 | target: minor 13 | github-token: ${{ secrets.DEPENDABOT_TOKEN }} 14 | -------------------------------------------------------------------------------- /lib/new.zsh: -------------------------------------------------------------------------------- 1 | #! /bin/zsh -f +-no-match --aliases 2 | cd ${TEMPLATES:-templates} 3 | 4 | # Active Finder tab 5 | osascript -e 'tell app "Finder" to POSIX path of (insertion location as alias)'| 6 | read && 7 | 8 | # Select diff tool 9 | for tool (opendiff ksdiff) if (/usr/bin/which -s $tool) alias diff=$tool 10 | 11 | case $@ in 12 | open*|mv*) eval $@;; # Run command 13 | *) # else 14 | 15 | if [ -f $@ ] # Copy template file or diff with incumbent 16 | then cp -n $@ $REPLY || diff {$REPLY/,}$@ 17 | 18 | # Copy [last modified] template file to match .ext 19 | elif [ -f *.$@:e(om[1]) ] 20 | then cp *.$@:e(om[1]) $REPLY/$@ 21 | 22 | # Folder or symlink 23 | elif [ -d $REPLY/$@ ] 24 | then diff {$REPLY/,}$@ 25 | elif [ -d $@ ] 26 | then cp -RL $@ $REPLY 27 | 28 | # New folder 29 | elif [[ $@ != *.* ]] 30 | then mkdir -p $REPLY/$@ 31 | 32 | # New file 33 | else touch $REPLY/$@ 34 | fi 35 | # Reveal in Finder 36 | open -R $REPLY/*(om[1]);; 37 | esac 38 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | Copyright © 2016 [Daniel Bayley](https://github.com/danielbayley) 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 | -------------------------------------------------------------------------------- /src/list.coffee: -------------------------------------------------------------------------------- 1 | `#! /usr/bin/env node 2 | ` 3 | query = process.argv[2] 4 | TEMPLATES = process.env.TEMPLATES ? "#{process.cwd()}/templates" 5 | 6 | ignore = require 'parse-gitignore' 7 | ignored = ignore "#{TEMPLATES}/.gitignore" 8 | 9 | glob = require 'glob' 10 | options = 11 | cwd: TEMPLATES 12 | nocase: true 13 | noglobstar: true 14 | dot: true 15 | ignore: ignored.concat ['.DS_Store'] 16 | stat: true 17 | 18 | #------------------------------------------------------------------------------- 19 | 20 | key = (subtitle, command) -> 21 | if @template? 22 | subtitle: subtitle 23 | arg: command 24 | else subtitle: '' 25 | 26 | list = (item) -> 27 | @template = templates?.cache["#{TEMPLATES}/#{item}"] 28 | @file = @template?.match /FILE/ 29 | 30 | keys = if @file 31 | alt: key "Edit template", "open '#{item}'" 32 | cmd: key "Reveal in Finder", "open -R '#{item}'" 33 | ctrl: subtitle: '' 34 | else {} # Folder 35 | keys.alt ?= keys.cmd ?= key "Open in Finder", "open '#{item}'" 36 | 37 | keys.ctrl = key "Trash template", "mv -f '#{item}' ~/.Trash" 38 | 39 | title: item 40 | autocomplete: item 41 | type: 'file' 42 | arg: item 43 | mods: keys 44 | icon: 45 | type: 'fileicon' 46 | path: 47 | if @template? then "#{TEMPLATES}/#{item}" 48 | else unless @file ? ~item.indexOf '.' then '/bin' else null 49 | 50 | items = [list query] 51 | templates = glob "*#{query}*", options, -> 52 | templates?.found.map (template) -> 53 | if ///^#{template}$///i.test query 54 | items.pop list query 55 | items.push list template 56 | 57 | console.log JSON.stringify {items} 58 | -------------------------------------------------------------------------------- /Cakefile: -------------------------------------------------------------------------------- 1 | {spawn, execSync} = require 'child_process' 2 | {readFileSync} = require 'fs' 3 | pkg = require './package'#.json 4 | {title} = require 'change-case' 5 | {parse, build} = require 'plist' 6 | process.stdin.setEncoding 'utf8' 7 | 8 | #------------------------------------------------------------------------------- 9 | 10 | fix = (obj) -> # FIXME https://github.com/TooTallNate/plist.js/issues/75 11 | for key in Object.keys obj 12 | val = obj[key] 13 | if val is null or val is undefined 14 | obj[key] = '' 15 | else if Array.isArray val 16 | obj[key] = val.map fix 17 | else if typeof val is 'object' 18 | obj[key] = fix val 19 | return obj 20 | #------------------------------------------------------------------------------- 21 | 22 | update = (options, info = {}) -> 23 | info.category = category if category = pkg.config?.category 24 | 25 | name = title pkg.name.replace /// (^alfred|#{info.category}) ///ig,'' 26 | info.name ?= pkg.config?.name ? name 27 | 28 | if options?.workflow # Packal 29 | info.version = pkg.version 30 | info.description = pkg.description 31 | info.webaddress = homepage if homepage = pkg.homepage 32 | info.createdby = author if author = pkg.author?.name 33 | 34 | username = pkg.author?.name.replace( /\s+/g,'').toLowerCase() 35 | info.bundleid ?= pkg.config?.bundleid ? "com.#{username}.#{pkg.name}" 36 | 37 | info.variables ?= pkg.config?.variables ? {} 38 | info.variables?.PATH = execSync("tr '\n' : < /etc/paths").toString()[0..-2] 39 | 40 | info.readme ?= pkg.config?.readme ? pkg.description 41 | if info.readme is true 42 | info.readme = readFileSync 'README.md','utf8' 43 | 44 | return info 45 | #------------------------------------------------------------------------------- 46 | 47 | option '-w','--workflow',"Also include npm postinstall modifications." 48 | 49 | task 'plist',"Update plist info from package.json", (options) -> 50 | process.stdin.on 'data', (info) -> 51 | console.log build update options, fix parse info 52 | 53 | task 'packal',"Package workflow for distribution on packal.org", -> 54 | {name} = update workflow: true 55 | spawn 'src/packal.zsh', [name] 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![badge][npm]][package] 2 | [![badge][downloads]][package] 3 | 4 | Finder New Item _[Alfred]_ [workflow] 5 | ===================================== 6 | Swiftly create new items in Finder with support for [templates](#templates). 7 | 8 | demo 9 | \* Demo using the _[mnml]_ Alfred [theme]. 10 | 11 | Templates 12 | --------- 13 | Templates are sourced from the workflow itself by default, but this can be overridden by a `TEMPLATES` [environment variable] specifying an alternative folder path. 14 | 15 | [Symbolic links] at the root level of the templates directory will be followed such that the original item is copied. 16 | 17 | If the selected template would replace an existing item, the two will first be compared using your favourite [diff tool]. 18 | 19 | The workflow will respect a `.gitignore` file in the templates directory, which can itself also be used as a template. 20 | 21 | select to edit template. 22 | select to reveal original template. 23 | select to remove template. 24 | 25 | Install 26 | ------- 27 | This workflow requires [Node].js (easily available with _[Homebrew]_) and [Alfred] 3 with the paid _[Powerpack]_ upgrade. 28 | 29 | ~~~ sh 30 | brew install node 31 | npm install -g alfred-finder-new-item 32 | ~~~ 33 | 34 | License 35 | ------- 36 | [MIT] © [Daniel Bayley] 37 | 38 | [MIT]: LICENSE.md 39 | [Daniel Bayley]: https://github.com/danielbayley 40 | 41 | [alfred]: http://alfredapp.com 42 | [mnml]: https://github.com/danielbayley/alfred-mnml-light 43 | [theme]: http://alfredapp.com/help/appearance 44 | [powerpack]: https://alfredapp.com/powerpack 45 | [workflow]: http://alfredapp.com/workflows 46 | [packal]: https://packal.org/workflow/finder-new-item 47 | [awm]: https://github.com/jonathanwiesel/awm 48 | 49 | [npm]: https://flat.badgen.net/npm/v/alfred-finder-new-item 50 | [downloads]: https://flat.badgen.net/npm/dt/alfred-finder-new-item 51 | [package]: https://npmjs.com/package/alfred-finder-new-item 52 | [node]: https://nodejs.org 53 | [homebrew]: http://brew.sh 54 | 55 | [environment variable]: http://alfredapp.com/help/workflows/script-environment-variables 56 | [symbolic links]: https://en.wikipedia.org/wiki/Symbolic_link 57 | [diff tool]: http://git-tower.com/blog/diff-tools-mac 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "alfred-finder-new-item", 3 | "version": "1.0.4", 4 | "description": "Swiftly create new items in Finder with support for templates.", 5 | "keywords": [ 6 | "alfred", 7 | "workflow", 8 | "packal", 9 | "template", 10 | "finder", 11 | "macos", 12 | "osx" 13 | ], 14 | "homepage": "https://github.com/danielbayley/alfred-finder-new-item#readme", 15 | "repository": "danielbayley/alfred-finder-new-item", 16 | "bugs": "https://github.com/danielbayley/alfred-finder-new-item/issues", 17 | "author": { 18 | "name": "Daniel Bayley", 19 | "email": "daniel.bayley@me.com", 20 | "url": "https://github.com/danielbayley" 21 | }, 22 | "license": "MIT", 23 | "os": [ 24 | "darwin" 25 | ], 26 | "engines": { 27 | "node": ">=4" 28 | }, 29 | "preferGlobal": true, 30 | "devDependencies": { 31 | "change-case": "latest", 32 | "coffee-script": "latest", 33 | "copy-node-modules": "latest", 34 | "plist": "^3.1.0" 35 | }, 36 | "dependencies": { 37 | "alfred-link": "^0.3.1", 38 | "glob": "^11.0.3", 39 | "parse-gitignore": "^2.0.0" 40 | }, 41 | "files": [ 42 | "info.plist", 43 | "lib/", 44 | "templates/" 45 | ], 46 | "config": { 47 | "icon": "/System/Library/CoreServices/Finder.app/Contents/Resources/Finder.icns", 48 | "category": "Finder" 49 | }, 50 | "scripts": { 51 | "icon": "sips -s format png $npm_package_config_icon -Z 512 --out icon.png", 52 | "compile": "coffee --no-header $w -cbo lib src && chmod +x lib/*", 53 | "plist": "cp -f {,.}$plist && cake plist < .$plist > $plist", 54 | "templates": "mkdir -p templates && cp {,$_/}README.md", 55 | "watch": "w=-w npm run compile", 56 | "test": "npm pack && npm i -g *.tgz", 57 | "posttest": "npm run clean", 58 | "prerelease": "git stash -u", 59 | "release": "npm version --", 60 | "postversion": "git push --follow-tags && npm publish && git stash pop --index", 61 | "prepare": "npm run compile && npm run templates", 62 | "prepublish": "npm run prepare && plist=info.plist npm run plist", 63 | "postpublish": "npm run clean", 64 | "packal": "cake packal && npm run clean", 65 | "postinstall": "npm run icon & alfred-link", 66 | "preuninstall": "alfred-unlink", 67 | "clean": "rm -rf *.tgz package templates icon.png lib/*.js & mv -f {.,}info.plist ||:", 68 | "zap": "npm run clean & rm -f *.alfredworkflow & npm rm -g $npm_package_name", 69 | "stop": "pkill Alfred", 70 | "start": "open -a 'Alfred 3'" 71 | }, 72 | "projectmanager": { 73 | "title": "finder-new-item", 74 | "template": "alfred-workflow" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | connections 6 | 7 | 7AAC1C97-F645-4953-AF66-5331F8E64004 8 | 9 | 10 | destinationuid 11 | FCA51658-3602-446A-8B60-3183C0EE0EE8 12 | modifiers 13 | 0 14 | modifiersubtext 15 | 16 | vitoclose 17 | 18 | 19 | 20 | 9BF5597C-F067-4757-82CD-757F577132FA 21 | 22 | 23 | destinationuid 24 | 7AAC1C97-F645-4953-AF66-5331F8E64004 25 | modifiers 26 | 0 27 | modifiersubtext 28 | 29 | vitoclose 30 | 31 | 32 | 33 | D249C1BD-B86B-4BF5-80DC-A54BEA367E31 34 | 35 | 36 | destinationuid 37 | 7AAC1C97-F645-4953-AF66-5331F8E64004 38 | modifiers 39 | 0 40 | modifiersubtext 41 | 42 | vitoclose 43 | 44 | 45 | 46 | 47 | objects 48 | 49 | 50 | config 51 | 52 | action 53 | 0 54 | argument 55 | 0 56 | hotkey 57 | 45 58 | hotmod 59 | 1048576 60 | hotstring 61 | N 62 | leftcursor 63 | 64 | modsmode 65 | 2 66 | relatedApps 67 | 68 | com.apple.finder 69 | 70 | relatedAppsMode 71 | 1 72 | 73 | type 74 | alfred.workflow.trigger.hotkey 75 | uid 76 | D249C1BD-B86B-4BF5-80DC-A54BEA367E31 77 | version 78 | 2 79 | 80 | 81 | config 82 | 83 | concurrently 84 | 85 | escaping 86 | 0 87 | script 88 | 89 | scriptargtype 90 | 1 91 | scriptfile 92 | lib/new.zsh 93 | type 94 | 8 95 | 96 | type 97 | alfred.workflow.action.script 98 | uid 99 | FCA51658-3602-446A-8B60-3183C0EE0EE8 100 | version 101 | 2 102 | 103 | 104 | config 105 | 106 | alfredfiltersresults 107 | 108 | argumenttype 109 | 2 110 | escaping 111 | 0 112 | queuedelaycustom 113 | 3 114 | queuedelayimmediatelyinitially 115 | 116 | queuedelaymode 117 | 0 118 | queuemode 119 | 2 120 | runningsubtext 121 | 122 | script 123 | node_modules/.bin/run-node lib/list.js "$@" 124 | scriptargtype 125 | 1 126 | scriptfile 127 | lib/list.js 128 | subtext 129 | 130 | title 131 | 132 | type 133 | 8 134 | withspace 135 | 136 | 137 | type 138 | alfred.workflow.input.scriptfilter 139 | uid 140 | 7AAC1C97-F645-4953-AF66-5331F8E64004 141 | version 142 | 2 143 | 144 | 145 | config 146 | 147 | argumenttype 148 | 2 149 | keyword 150 | new 151 | subtext 152 | 153 | text 154 | New item 155 | withspace 156 | 157 | 158 | type 159 | alfred.workflow.input.keyword 160 | uid 161 | 9BF5597C-F067-4757-82CD-757F577132FA 162 | version 163 | 1 164 | 165 | 166 | uidata 167 | 168 | 7AAC1C97-F645-4953-AF66-5331F8E64004 169 | 170 | xpos 171 | 200 172 | ypos 173 | 20 174 | 175 | 9BF5597C-F067-4757-82CD-757F577132FA 176 | 177 | xpos 178 | 20 179 | ypos 180 | 140 181 | 182 | D249C1BD-B86B-4BF5-80DC-A54BEA367E31 183 | 184 | xpos 185 | 20 186 | ypos 187 | 20 188 | 189 | FCA51658-3602-446A-8B60-3183C0EE0EE8 190 | 191 | xpos 192 | 380 193 | ypos 194 | 20 195 | 196 | 197 | name 198 | New item 199 | description 200 | 201 | 202 | 203 | --------------------------------------------------------------------------------