├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── Rakefile ├── assets └── fitzpatrick-preview.gif ├── emoji.rb ├── icon.png └── info.plist /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "emoji-db"] 2 | path = emoji-db 3 | url = git@github.com:meyer/emoji-db.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | octocat, squirrel, shipit 2 | Copyright (c) 2012 GitHub Inc. All rights reserved. 3 | 4 | bowtie, neckbeard 5 | Copyright (c) 2012 37signals, LLC. All rights reserved. 6 | 7 | feelsgood, finnadie, goberserk, godmode, hurtrealbad, rage 1-4, suspect 8 | Copyright (c) 2012 id Software. All rights reserved. 9 | 10 | trollface 11 | Copyright (c) 2012 whynne@deviantart. All rights reserved. 12 | 13 | All other emoji images 14 | Copyright (c) 2012 Apple Inc. All rights reserved. 15 | 16 | All existing code 17 | Copyright 2013 Carlos Galdino 18 | 19 | Licensed under the Apache License, Version 2.0 (the "License"); 20 | you may not use this file except in compliance with the License. 21 | You may obtain a copy of the License at 22 | 23 | http://www.apache.org/licenses/LICENSE-2.0 24 | 25 | Unless required by applicable law or agreed to in writing, software 26 | distributed under the License is distributed on an "AS IS" BASIS, 27 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 28 | See the License for the specific language governing permissions and 29 | limitations under the License. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Alfred Emoji Picker 2 | 3 | 1. Install the latest version of this extension from the Releases page. 4 | 2. Type `emoji [thing you want to find]` 5 | 6 | ## Skin tone modifier support 7 | 8 | By default, emoji that support skin tone (fitzpatrick) modifiers will default to no modifier. To pick an emoji with fitzpatrick modifier, add `-t[1-5]` to the end of your query. For emoji that accept more than one fitzpatrick modifier, you can specify multiple digits: 9 | 10 | ![GIF example of Fitzpatrick modifier support](assets/fitzpatrick-preview.gif) 11 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | require 'shellwords' 3 | 4 | RootDir = Pathname.new(Rake.application.original_dir) 5 | DestDir = Pathname.new(Rake.application.original_dir + '-pristine') 6 | BuildDir = RootDir.join('build') 7 | 8 | desc 'Copy necessary files to new bundle' 9 | task :default do 10 | rm_rf DestDir 11 | mkdir_p DestDir 12 | rm_rf BuildDir 13 | mkdir_p BuildDir 14 | 15 | [ 16 | 'emoji.rb', 17 | 'icon.png', 18 | 'emoji-db/emoji-db.json', 19 | 'emoji-db/images/', 20 | ].each do |f| 21 | dir_name = File.dirname(f) 22 | mkdir_p(DestDir.join dir_name) if dir_name != '.' 23 | cp_r RootDir.join(f), DestDir.join(f) 24 | end 25 | 26 | plist_contents = File.read(RootDir.join('info.plist')) 27 | 28 | File.open(DestDir.join('info.plist'), 'w', 0644) do |f| 29 | f.write plist_contents.gsub( 30 | 'Find Dat Emoji DEV', 31 | 'Find Dat Emoji' 32 | ).gsub( 33 | 'testmoji', 34 | 'emoji' 35 | ).gsub( 36 | 'fm.meyer.FindDatEmojiDev', 37 | 'fm.meyer.FindDatEmoji' 38 | ) 39 | end 40 | 41 | chdir DestDir 42 | system( 43 | 'zip', 44 | '-r9', 45 | BuildDir.join('find-emoji.alfredworkflow').to_s, 46 | '.' 47 | ) 48 | end 49 | -------------------------------------------------------------------------------- /assets/fitzpatrick-preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meyer/alfred-emoji-workflow/33187d726a738baf45335c2b5bba7807fd623637/assets/fitzpatrick-preview.gif -------------------------------------------------------------------------------- /emoji.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | require 'tmpdir' 5 | require 'optparse' 6 | require 'json' 7 | require 'shellwords' 8 | require 'pathname' 9 | 10 | class Integer 11 | def to_unicode 12 | self.to_s(16).rjust(4, '0') 13 | end 14 | end 15 | 16 | class Array 17 | def to_codepoint_string 18 | self.map {|item| item.is_a?(Numeric) ? '0x' + item.to_unicode : item.to_s}.join(', ') 19 | end 20 | end 21 | 22 | PWD = Pathname.new File.expand_path(File.dirname(__FILE__)) 23 | EMOJI_DB_PATH = PWD.join('./emoji-db/') 24 | MARSHAL_TMP_FILE = File.expand_path('./alfred-emoji-workflow-cache', Dir.tmpdir) 25 | 26 | STDERR.puts '====' 27 | STDERR.puts "ARGV: `#{ARGV}`" 28 | STDERR.puts '====' 29 | 30 | option_array = ARGV.join(' ').split 31 | 32 | OptionParser.new do |opts| 33 | opts.program_name = File.basename(__FILE__) 34 | opts.summary_indent = " " 35 | opts.summary_width = 20 36 | 37 | opts.on("-t", "--tone [number]", /^\d+$/, "Include skin tone") do |t| 38 | if t.to_i % 11 == 0 39 | $skin_tone = (t.to_i / 11).to_s 40 | else 41 | $skin_tone = t 42 | end 43 | end 44 | 45 | opts.on("-d", "--debug", "Run in debug mode (no cache)") do |_o| 46 | STDERR.puts "Debug mode enabled" 47 | $debug_mode = true 48 | end 49 | end.parse!(option_array) 50 | 51 | def reset_marshal_cache 52 | File.open(MARSHAL_TMP_FILE, File::RDWR|File::CREAT, 0644) do |f| 53 | fc = { 54 | 'search_strings' => {}, 55 | 'db' => JSON.load(IO.read(EMOJI_DB_PATH.join('emoji-db.json'))) 56 | } 57 | 58 | fc['db'].each do |k, v| 59 | puts [k, v] 60 | 61 | fc['db'][k]['name'] = fc['db'][k]['name'] || '' 62 | fc['search_strings'][k] = [ 63 | '', 64 | (v['name'] || '').split, 65 | v['keywords'], 66 | v['codepoints'].map(&:to_unicode), 67 | fc['db'][k]['fitz'] ? 'fitz' : [], 68 | '', 69 | ].compact.join(' ').downcase 70 | 71 | if fc['db'][k]['image'] 72 | fc['db'][k]['image'] = EMOJI_DB_PATH.join(fc['db'][k]['image']) 73 | else 74 | STDERR.puts "Emoji #{k} is missing an image" 75 | end 76 | 77 | if fc['db'][k]['fitz'] 78 | fc['db'][k]['fitz'].each do |k, v| 79 | v.merge!({ 'image' => EMOJI_DB_PATH.join(v['image']) }) 80 | end 81 | end 82 | end 83 | f.rewind 84 | f.write(Marshal.dump(fc)) 85 | f.flush 86 | f.truncate(f.pos) 87 | fc 88 | end 89 | end 90 | 91 | if $debug_mode 92 | reset_marshal_cache 93 | STDERR.puts "Marshal cache reset!" 94 | puts JSON.pretty_generate({ 95 | :items => [ 96 | { 97 | :uid => '__debug__', 98 | :title => "Debug \u{1f41b}", 99 | :subtitle => "Emoji database has been reset", 100 | } 101 | ], 102 | }) 103 | exit 0 104 | end 105 | 106 | EMOJI_OBJ = begin 107 | File.open(MARSHAL_TMP_FILE, File::RDWR|File::CREAT, 0644) {|f| Marshal.load(f.read)} 108 | rescue ArgumentError 109 | STDERR.puts "Marshal cache could not be loaded. Resetting!" 110 | reset_marshal_cache 111 | end 112 | 113 | ### SEARCH SHIT 114 | 115 | exact_matches = [] 116 | matches = [] 117 | 118 | unless option_array.empty? 119 | query = option_array.join(' ').downcase.strip 120 | STDERR.puts "QUERY: `#{query}`" 121 | 122 | if query.strip == '' 123 | # show everything if no query is provided 124 | matches = EMOJI_OBJ['db'].keys 125 | else 126 | EMOJI_OBJ['search_strings'].each do |key, ss| 127 | if ss.include?(" #{query} ") 128 | exact_matches.push key 129 | STDERR.puts "`#{EMOJI_OBJ['db'][key]['name']}` is an exact match!" 130 | elsif ss.include?(query) 131 | matches.push key 132 | STDERR.puts "`#{EMOJI_OBJ['db'][key]['name']}` is a match!" 133 | end 134 | end 135 | end 136 | end 137 | 138 | STDERR.puts JSON.pretty_generate(ENV.to_h) 139 | 140 | items = (exact_matches + matches).map do |emoji_key| 141 | STDERR.puts "CODEPOINT: `#{emoji_key}`" 142 | emoji = EMOJI_OBJ['db'][emoji_key] 143 | 144 | if $skin_tone && emoji['fitz'] && emoji['fitz'][$skin_tone] 145 | emoji = emoji['fitz'][$skin_tone] 146 | end 147 | 148 | path = emoji['image'] 149 | codepoints = emoji['codepoints'] 150 | 151 | STDERR.puts "KEYWORDS: `#{EMOJI_OBJ['search_strings'][emoji_key]}`" 152 | STDERR.puts path 153 | 154 | 155 | emojilib_name = emoji['emojilib_name'] ? ":#{emoji['emojilib_name']}:" : '' 156 | 157 | unicode_txt = codepoints.pack('U*') 158 | codepoint_txt = codepoints.to_codepoint_string 159 | 160 | subtitle = "Copy #{unicode_txt} to clipboard" 161 | mods = { 162 | :ctrl => { 163 | :valid => true, 164 | :arg => emoji_key, 165 | :subtitle => "Copy #{emoji_key} to clipboard", 166 | :variables => { 167 | :active_key => 'ctrl' 168 | }, 169 | }, 170 | :shift => { 171 | :valid => !!emoji['emojilib_name'], 172 | :arg => emoji['emojilib_name'], 173 | :subtitle => emoji['emojilib_name'] ? "Copy #{emojilib_name} to clipboard" : "No emojilib name :..(", 174 | :variables => { 175 | :active_key => 'shift' 176 | }, 177 | }, 178 | :alt => { 179 | :valid => true, 180 | :arg => codepoint_txt, 181 | :subtitle => "Copy '#{codepoint_txt}' to clipboard", 182 | }, 183 | :cmd => { 184 | :valid => true, 185 | :arg => path, 186 | :subtitle => "Reveal image for #{unicode_txt} in Finder", 187 | :variables => { 188 | :active_key => 'cmd' 189 | }, 190 | }, 191 | } 192 | 193 | { 194 | :arg => unicode_txt, 195 | :uid => emoji_key, 196 | :variables => {}, 197 | :icon => { 198 | :path => path, 199 | }, 200 | # :type => 'file:skipcheck', 201 | :title => emoji['name'], 202 | :quicklookurl => path, 203 | :subtitle => subtitle, 204 | :mods => mods, 205 | } 206 | end 207 | 208 | puts JSON.pretty_generate({ 209 | :items => items, 210 | }) 211 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meyer/alfred-emoji-workflow/33187d726a738baf45335c2b5bba7807fd623637/icon.png -------------------------------------------------------------------------------- /info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | bundleid 6 | fm.meyer.FindDatEmojiDev 7 | category 8 | Internet 9 | connections 10 | 11 | 16B5910C-8088-46BF-AE4E-EFA8187C4407 12 | 13 | 14 | destinationuid 15 | 2404AE08-D4CF-4265-B7BB-44043DD356DD 16 | modifiers 17 | 0 18 | modifiersubtext 19 | 20 | vitoclose 21 | 22 | 23 | 24 | 18314CAD-E0C0-45FD-A21E-69F5D30C7DE5 25 | 26 | 27 | destinationuid 28 | 9006EDF2-1C48-490F-9E50-187D8E0384F6 29 | modifiers 30 | 1048576 31 | modifiersubtext 32 | 33 | vitoclose 34 | 35 | 36 | 37 | destinationuid 38 | 2404AE08-D4CF-4265-B7BB-44043DD356DD 39 | modifiers 40 | 0 41 | modifiersubtext 42 | 43 | vitoclose 44 | 45 | 46 | 47 | destinationuid 48 | 6684C3DA-026B-4B8D-ADCA-4514C8BEB053 49 | modifiers 50 | 131072 51 | modifiersubtext 52 | 53 | vitoclose 54 | 55 | 56 | 57 | destinationuid 58 | 16B5910C-8088-46BF-AE4E-EFA8187C4407 59 | modifiers 60 | 262144 61 | modifiersubtext 62 | 63 | vitoclose 64 | 65 | 66 | 67 | destinationuid 68 | CEBAC27D-23A5-4336-96C5-A9B7F422663B 69 | modifiers 70 | 524288 71 | modifiersubtext 72 | 73 | vitoclose 74 | 75 | 76 | 77 | 6684C3DA-026B-4B8D-ADCA-4514C8BEB053 78 | 79 | 80 | destinationuid 81 | 2404AE08-D4CF-4265-B7BB-44043DD356DD 82 | modifiers 83 | 0 84 | modifiersubtext 85 | 86 | vitoclose 87 | 88 | 89 | 90 | CEBAC27D-23A5-4336-96C5-A9B7F422663B 91 | 92 | 93 | destinationuid 94 | 2404AE08-D4CF-4265-B7BB-44043DD356DD 95 | modifiers 96 | 0 97 | modifiersubtext 98 | 99 | vitoclose 100 | 101 | 102 | 103 | 104 | createdby 105 | Carlos Galdino and Mike Meyer 106 | description 107 | A nicer alternative to the system emoji picker 108 | disabled 109 | 110 | name 111 | Find Dat Emoji DEV 112 | objects 113 | 114 | 115 | type 116 | alfred.workflow.action.revealfile 117 | uid 118 | 9006EDF2-1C48-490F-9E50-187D8E0384F6 119 | version 120 | 1 121 | 122 | 123 | config 124 | 125 | autopaste 126 | 127 | clipboardtext 128 | {query} 129 | transient 130 | 131 | 132 | type 133 | alfred.workflow.output.clipboard 134 | uid 135 | 2404AE08-D4CF-4265-B7BB-44043DD356DD 136 | version 137 | 2 138 | 139 | 140 | config 141 | 142 | alfredfiltersresults 143 | 144 | alfredfiltersresultsmatchmode 145 | 0 146 | argumenttrimmode 147 | 0 148 | argumenttype 149 | 1 150 | escaping 151 | 0 152 | keyword 153 | testmoji 154 | queuedelaycustom 155 | 3 156 | queuedelayimmediatelyinitially 157 | 158 | queuedelaymode 159 | 0 160 | queuemode 161 | 2 162 | runningsubtext 163 | One moment please… 164 | script 165 | 166 | scriptargtype 167 | 1 168 | scriptfile 169 | emoji.rb 170 | subtext 171 | 172 | title 173 | Search emoji… 174 | type 175 | 8 176 | withspace 177 | 178 | 179 | type 180 | alfred.workflow.input.scriptfilter 181 | uid 182 | 18314CAD-E0C0-45FD-A21E-69F5D30C7DE5 183 | version 184 | 2 185 | 186 | 187 | type 188 | alfred.workflow.utility.junction 189 | uid 190 | 6684C3DA-026B-4B8D-ADCA-4514C8BEB053 191 | version 192 | 1 193 | 194 | 195 | type 196 | alfred.workflow.utility.junction 197 | uid 198 | 16B5910C-8088-46BF-AE4E-EFA8187C4407 199 | version 200 | 1 201 | 202 | 203 | type 204 | alfred.workflow.utility.junction 205 | uid 206 | CEBAC27D-23A5-4336-96C5-A9B7F422663B 207 | version 208 | 1 209 | 210 | 211 | readme 212 | Emoji search for Alfred! 213 | - Adjust skin tone by adding -t1 through -t6 to your search query 214 | - Copy as Ruby by using -r 215 | uidata 216 | 217 | 16B5910C-8088-46BF-AE4E-EFA8187C4407 218 | 219 | xpos 220 | 310 221 | ypos 222 | 280 223 | 224 | 18314CAD-E0C0-45FD-A21E-69F5D30C7DE5 225 | 226 | xpos 227 | 30 228 | ypos 229 | 140 230 | 231 | 2404AE08-D4CF-4265-B7BB-44043DD356DD 232 | 233 | xpos 234 | 510 235 | ypos 236 | 140 237 | 238 | 6684C3DA-026B-4B8D-ADCA-4514C8BEB053 239 | 240 | xpos 241 | 310 242 | ypos 243 | 220 244 | 245 | 9006EDF2-1C48-490F-9E50-187D8E0384F6 246 | 247 | xpos 248 | 270 249 | ypos 250 | 60 251 | 252 | CEBAC27D-23A5-4336-96C5-A9B7F422663B 253 | 254 | xpos 255 | 310 256 | ypos 257 | 340 258 | 259 | 260 | webaddress 261 | https://github.com/meyer/alfred-emoji-workflow 262 | 263 | 264 | --------------------------------------------------------------------------------