├── .github └── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_suggestion.yml ├── LICENSE ├── README.md ├── Resources └── find-dicts.swift └── Workflow ├── find-dicts ├── icon.png ├── images └── about │ ├── asdicm.png │ └── newdict.png └── info.plist /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report 3 | body: 4 | - type: input 5 | attributes: 6 | label: Workflow version 7 | description: Open the Workflow in Alfred Preferences and find it at the top, near the description 8 | validations: 9 | required: true 10 | - type: input 11 | attributes: 12 | label: Alfred version 13 | description: In the top left corner of Alfred Preferences → General 14 | validations: 15 | required: true 16 | - type: input 17 | attributes: 18 | label: macOS version 19 | description: Click  on the menubar → About This Mac 20 | validations: 21 | required: true 22 | - type: textarea 23 | attributes: 24 | label: Debugger output 25 | description: Perform the failing action with [the debugger](https://www.alfredapp.com/help/workflows/advanced/debugger/) open 26 | render: alfred_debugger 27 | validations: 28 | required: true 29 | - type: textarea 30 | attributes: 31 | label: More details 32 | description: Explain what you did, what happened, and what you expected to happen 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_suggestion.yml: -------------------------------------------------------------------------------- 1 | name: Feature Suggestion 2 | description: Suggest a new feature 3 | body: 4 | - type: textarea 5 | attributes: 6 | label: Feature details 7 | description: Explain the feature idea 8 | validations: 9 | required: true 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, Running with Crayons Ltd 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # icon AppleScript Dictionaries Alfred Workflow 2 | 3 | Find and open Mac automation dictionaries 4 | 5 | [⤓ Install on the Alfred Gallery](https://alfred.app/workflows/alfredapp/applescript-dictionaries) 6 | 7 | ## Usage 8 | 9 | Search through installed AppleScript dictionaries via the `asdic` keyword. ↩︎ to open. 10 | 11 | ![Alfred results for asdic m](Workflow/images/about/asdicm.png) 12 | 13 | A minority of apps use an older dictionary format which can be converted with the [Universal Action](https://www.alfredapp.com/help/features/universal-actions/). 14 | 15 | ![Universal Action for generating dictionary](Workflow/images/about/newdict.png) 16 | -------------------------------------------------------------------------------- /Resources/find-dicts.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | // Helpers 4 | struct ScriptFilter: Codable { 5 | let cache: Cache 6 | let items: [Item] 7 | 8 | struct Cache: Codable { 9 | let seconds: Int 10 | let loosereload: Bool 11 | } 12 | 13 | struct Item: Codable { 14 | let uid: String 15 | let title: String 16 | let subtitle: String 17 | let type: String 18 | let icon: FileIcon 19 | let arg: String 20 | 21 | struct FileIcon: Codable { 22 | let path: String 23 | let type: String 24 | } 25 | } 26 | } 27 | 28 | func findDict(inFolder: URL) -> URL? { 29 | let resourcesFolder = inFolder.appendingPathComponent("Contents/Resources") 30 | 31 | return try? FileManager.default.contentsOfDirectory( 32 | at: resourcesFolder, includingPropertiesForKeys: nil, options: .skipsHiddenFiles 33 | ).first(where: { $0.pathExtension == "sdef" }) 34 | } 35 | 36 | // Find apps with Spotlight 37 | let spotQuery = "kMDItemContentTypeTree == 'com.apple.application-bundle'" 38 | let searchQuery = MDQueryCreate(kCFAllocatorDefault, spotQuery as CFString, nil, nil) 39 | 40 | MDQueryExecute(searchQuery, CFOptionFlags(kMDQuerySynchronous.rawValue)) 41 | let resultCount = MDQueryGetResultCount(searchQuery) 42 | 43 | let spotApps: [URL] = (0...fromOpaque(rawPointer!).takeUnretainedValue() 46 | 47 | guard let resultPath = MDItemCopyAttribute(resultItem, kMDItemPath) as? String else { return nil } 48 | return URL(fileURLWithPath: resultPath) 49 | } 50 | 51 | // Find apps in special locations 52 | let additions: [URL] = [ 53 | URL(fileURLWithPath: "/System/Library/ScriptingAdditions/StandardAdditions.osax") 54 | ] 55 | 56 | let customApps: [URL] = { 57 | guard let workflowData = ProcessInfo.processInfo.environment["alfred_workflow_data"] else { return [] } 58 | let dataFolder = URL(fileURLWithPath: workflowData) 59 | 60 | guard 61 | let dataContents = try? FileManager.default.contentsOfDirectory( 62 | at: dataFolder, includingPropertiesForKeys: nil, options: .skipsHiddenFiles) 63 | else { return [] } 64 | 65 | return dataContents.filter { $0.pathExtension == "app" } 66 | }() 67 | 68 | // Join all apps 69 | let allApps = spotApps + additions + customApps 70 | 71 | // Structure JSON 72 | let cachingItems = ScriptFilter.Cache(seconds: 300, loosereload: true) 73 | 74 | let sfItems: [ScriptFilter.Item] = allApps.compactMap { app in 75 | guard let dict = findDict(inFolder: app)?.path else { return nil } 76 | 77 | return ScriptFilter.Item( 78 | uid: dict, 79 | title: app.deletingPathExtension().lastPathComponent, 80 | subtitle: dict, 81 | type: "file", 82 | icon: ScriptFilter.Item.FileIcon(path: app.path, type: "fileicon"), 83 | arg: dict 84 | ) 85 | } 86 | 87 | // Output JSON 88 | let jsonData = try JSONEncoder().encode(ScriptFilter(cache: cachingItems, items: sfItems)) 89 | print(String(data: jsonData, encoding: .utf8)!) 90 | -------------------------------------------------------------------------------- /Workflow/find-dicts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfredapp/applescript-dictionaries-workflow/31b596714f87a67af5fd283ef91b22474a7ce56f/Workflow/find-dicts -------------------------------------------------------------------------------- /Workflow/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfredapp/applescript-dictionaries-workflow/31b596714f87a67af5fd283ef91b22474a7ce56f/Workflow/icon.png -------------------------------------------------------------------------------- /Workflow/images/about/asdicm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfredapp/applescript-dictionaries-workflow/31b596714f87a67af5fd283ef91b22474a7ce56f/Workflow/images/about/asdicm.png -------------------------------------------------------------------------------- /Workflow/images/about/newdict.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfredapp/applescript-dictionaries-workflow/31b596714f87a67af5fd283ef91b22474a7ce56f/Workflow/images/about/newdict.png -------------------------------------------------------------------------------- /Workflow/info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | bundleid 6 | com.alfredapp.vitor.applescriptdictionaries 7 | connections 8 | 9 | 0C34FB83-CBAA-41E0-B482-47270F6ABB19 10 | 11 | 12 | destinationuid 13 | 7307CF18-FA86-41AF-BDC7-AB66D75667E9 14 | modifiers 15 | 0 16 | modifiersubtext 17 | 18 | sourceoutputuid 19 | 2304F28F-1CCF-4DE4-B47C-14A637B2551D 20 | vitoclose 21 | 22 | 23 | 24 | destinationuid 25 | 8F720020-3637-4681-926C-B434F10BCF70 26 | modifiers 27 | 0 28 | modifiersubtext 29 | 30 | vitoclose 31 | 32 | 33 | 34 | 7307CF18-FA86-41AF-BDC7-AB66D75667E9 35 | 36 | 7A58F92F-50C1-456F-90EA-BE0F47F2C5A4 37 | 38 | 39 | destinationuid 40 | D912659A-805F-410F-A602-8BA6AC894BEF 41 | modifiers 42 | 0 43 | modifiersubtext 44 | 45 | vitoclose 46 | 47 | 48 | 49 | 7D4ED081-C6A3-4262-9578-229B307CE081 50 | 51 | 52 | destinationuid 53 | 99D694BC-7C8B-4735-A22C-21F9CAAE5A54 54 | modifiers 55 | 0 56 | modifiersubtext 57 | 58 | vitoclose 59 | 60 | 61 | 62 | 8F720020-3637-4681-926C-B434F10BCF70 63 | 64 | 65 | destinationuid 66 | 27798F6D-1A31-4878-B7F9-CCB14F11A67F 67 | modifiers 68 | 0 69 | modifiersubtext 70 | 71 | vitoclose 72 | 73 | 74 | 75 | 99D694BC-7C8B-4735-A22C-21F9CAAE5A54 76 | 77 | D912659A-805F-410F-A602-8BA6AC894BEF 78 | 79 | 80 | destinationuid 81 | 0C34FB83-CBAA-41E0-B482-47270F6ABB19 82 | modifiers 83 | 0 84 | modifiersubtext 85 | 86 | vitoclose 87 | 88 | 89 | 90 | 91 | createdby 92 | Vítor Galvão 93 | description 94 | Find and open Mac automation dictionaries 95 | disabled 96 | 97 | name 98 | AppleScript Dictionaries 99 | objects 100 | 101 | 102 | config 103 | 104 | concurrently 105 | 106 | escaping 107 | 102 108 | script 109 | open -a 'Script Editor' "${1}" 110 | scriptargtype 111 | 1 112 | scriptfile 113 | 114 | type 115 | 11 116 | 117 | type 118 | alfred.workflow.action.script 119 | uid 120 | 99D694BC-7C8B-4735-A22C-21F9CAAE5A54 121 | version 122 | 2 123 | 124 | 125 | config 126 | 127 | alfredfiltersresults 128 | 129 | alfredfiltersresultsmatchmode 130 | 0 131 | argumenttreatemptyqueryasnil 132 | 133 | argumenttrimmode 134 | 0 135 | argumenttype 136 | 1 137 | escaping 138 | 102 139 | keyword 140 | {var:search_keyword} 141 | queuedelaycustom 142 | 3 143 | queuedelayimmediatelyinitially 144 | 145 | queuedelaymode 146 | 0 147 | queuemode 148 | 1 149 | runningsubtext 150 | Searching for AppleScript dictionaries… 151 | script 152 | 153 | scriptargtype 154 | 1 155 | scriptfile 156 | find-dicts 157 | skipuniversalaction 158 | 159 | subtext 160 | 161 | title 162 | AppleScript Dictionaries 163 | type 164 | 8 165 | withspace 166 | 167 | 168 | type 169 | alfred.workflow.input.scriptfilter 170 | uid 171 | 7D4ED081-C6A3-4262-9578-229B307CE081 172 | version 173 | 3 174 | 175 | 176 | config 177 | 178 | concurrently 179 | 180 | escaping 181 | 68 182 | script 183 | tell application id "com.runningwithcrayons.Alfred" to reload workflow (system attribute "alfred_workflow_bundleid") 184 | scriptargtype 185 | 1 186 | scriptfile 187 | 188 | type 189 | 6 190 | 191 | type 192 | alfred.workflow.action.script 193 | uid 194 | 7307CF18-FA86-41AF-BDC7-AB66D75667E9 195 | version 196 | 2 197 | 198 | 199 | config 200 | 201 | lastpathcomponent 202 | 203 | onlyshowifquerypopulated 204 | 205 | removeextension 206 | 207 | text 208 | Please ensure the app has an AppleScript dictionary 209 | title 210 | Failed to Generate Dictionary 211 | 212 | type 213 | alfred.workflow.output.notification 214 | uid 215 | 8F720020-3637-4681-926C-B434F10BCF70 216 | version 217 | 1 218 | 219 | 220 | config 221 | 222 | soundname 223 | Sosumi 224 | systemsound 225 | 226 | 227 | type 228 | alfred.workflow.output.playsound 229 | uid 230 | 27798F6D-1A31-4878-B7F9-CCB14F11A67F 231 | version 232 | 1 233 | 234 | 235 | config 236 | 237 | acceptsmulti 238 | 0 239 | filetypes 240 | 241 | com.apple.application-bundle 242 | 243 | name 244 | Generate AppleScript Dictionary 245 | 246 | type 247 | alfred.workflow.trigger.action 248 | uid 249 | 7A58F92F-50C1-456F-90EA-BE0F47F2C5A4 250 | version 251 | 1 252 | 253 | 254 | config 255 | 256 | concurrently 257 | 258 | escaping 259 | 102 260 | script 261 | ObjC.import("AppKit") 262 | 263 | // Helpers 264 | function envVar(varName) { 265 | return $.NSProcessInfo 266 | .processInfo 267 | .environment 268 | .objectForKey(varName).js 269 | } 270 | 271 | function writeFile(path, text) { 272 | $(text).writeToFileAtomicallyEncodingError(path, true, $.NSUTF8StringEncoding, undefined) 273 | } 274 | 275 | function mkdir(path) { 276 | $.NSFileManager 277 | .defaultManager 278 | .createDirectoryAtPathWithIntermediateDirectoriesAttributesError(path, true, undefined, undefined) 279 | } 280 | 281 | function runCommand(...arguments) { 282 | const task = $.NSTask.alloc.init 283 | const stdout = $.NSPipe.pipe 284 | 285 | task.executableURL = $.NSURL.fileURLWithPath("/usr/bin/env") 286 | task.arguments = arguments 287 | task.standardOutput = stdout 288 | task.launchAndReturnError(false) 289 | 290 | const dataOut = stdout.fileHandleForReading.readDataToEndOfFileAndReturnError(false) 291 | const stringOut = $.NSString.alloc.initWithDataEncoding(dataOut, $.NSUTF8StringEncoding).js 292 | 293 | return stringOut 294 | } 295 | 296 | // Main 297 | function run(argv) { 298 | // Get info 299 | const appPath = argv[0] 300 | const appName = Application(appPath).name() 301 | const appDict = runCommand("/usr/bin/sdef", appPath) 302 | 303 | // Exit if failed to generate dictionary 304 | if (appDict.length === 0) return false 305 | 306 | // Store dictionary in custom directory tree 307 | const shimDir = `${envVar("alfred_workflow_data")}/${appName}` 308 | const dictDir = `${shimDir}/Contents/Resources` 309 | mkdir(dictDir) 310 | writeFile(`${dictDir}/${appName}.sdef`, appDict) 311 | 312 | // Change folder icon to match app 313 | const workspace = $.NSWorkspace.sharedWorkspace 314 | const icon = workspace.iconForFile(appPath) 315 | workspace.setIconForFileOptions(icon, shimDir, 0) 316 | 317 | // Exit with success 318 | return true 319 | } 320 | scriptargtype 321 | 1 322 | scriptfile 323 | 324 | type 325 | 7 326 | 327 | type 328 | alfred.workflow.action.script 329 | uid 330 | D912659A-805F-410F-A602-8BA6AC894BEF 331 | version 332 | 2 333 | 334 | 335 | config 336 | 337 | conditions 338 | 339 | 340 | inputstring 341 | 342 | matchcasesensitive 343 | 344 | matchmode 345 | 5 346 | matchstring 347 | 348 | outputlabel 349 | Success 350 | uid 351 | 2304F28F-1CCF-4DE4-B47C-14A637B2551D 352 | 353 | 354 | elselabel 355 | Failure 356 | hideelse 357 | 358 | 359 | type 360 | alfred.workflow.utility.conditional 361 | uid 362 | 0C34FB83-CBAA-41E0-B482-47270F6ABB19 363 | version 364 | 1 365 | 366 | 367 | readme 368 | ## Usage 369 | 370 | Search through installed AppleScript dictionaries via the `asdic` keyword. <kbd>↩</kbd> to open. 371 | 372 | ![Alfred results for asdic m](images/about/asdicm.png) 373 | 374 | A minority of apps use an older dictionary format which can be converted with the [Universal Action](https://www.alfredapp.com/help/features/universal-actions/). 375 | 376 | ![Universal Action for generating dictionary](images/about/newdict.png) 377 | uidata 378 | 379 | 0C34FB83-CBAA-41E0-B482-47270F6ABB19 380 | 381 | xpos 382 | 455 383 | ypos 384 | 315 385 | 386 | 27798F6D-1A31-4878-B7F9-CCB14F11A67F 387 | 388 | xpos 389 | 750 390 | ypos 391 | 295 392 | 393 | 7307CF18-FA86-41AF-BDC7-AB66D75667E9 394 | 395 | note 396 | Flush cache 397 | xpos 398 | 575 399 | ypos 400 | 115 401 | 402 | 7A58F92F-50C1-456F-90EA-BE0F47F2C5A4 403 | 404 | note 405 | Generate sdef for app with older scriptSuite 406 | xpos 407 | 45 408 | ypos 409 | 295 410 | 411 | 7D4ED081-C6A3-4262-9578-229B307CE081 412 | 413 | note 414 | List AppleScript dictionaries 415 | xpos 416 | 45 417 | ypos 418 | 115 419 | 420 | 8F720020-3637-4681-926C-B434F10BCF70 421 | 422 | xpos 423 | 575 424 | ypos 425 | 295 426 | 427 | 99D694BC-7C8B-4735-A22C-21F9CAAE5A54 428 | 429 | note 430 | Open dictionary 431 | xpos 432 | 265 433 | ypos 434 | 115 435 | 436 | D912659A-805F-410F-A602-8BA6AC894BEF 437 | 438 | note 439 | Build sdef for app 440 | xpos 441 | 265 442 | ypos 443 | 295 444 | 445 | 446 | userconfigurationconfig 447 | 448 | 449 | config 450 | 451 | default 452 | asdic 453 | placeholder 454 | 455 | required 456 | 457 | trim 458 | 459 | 460 | description 461 | 462 | label 463 | Search Keyword 464 | type 465 | textfield 466 | variable 467 | search_keyword 468 | 469 | 470 | version 471 | 2024.1 472 | webaddress 473 | https://github.com/alfredapp/applescript-dictionaries-workflow/ 474 | 475 | 476 | --------------------------------------------------------------------------------