├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── .gitignore ├── LICENSE ├── README.md ├── package.json └── textexpander-to-alfred3.js /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: The tool does not work! You have to do something! 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Heads up: I programmed this in April 2016. I then converted all my snippets, so I haven't needed this tool since. In the last years, both TextExpander and Alfred have released several new versions, which may or may not work with this tool (Judging by the support requests I get, they don't!) 11 | 12 | If this website does not work, I can't help you. If you know Node.js, you may be able to fix it yourself and send a Pull Request to my respository. I will probably accept it! 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.textexpander 3 | *.alfredsnippets 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Daniel Diekmeier 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TextExpander to Alfred 3 2 | 3 | **⚠️ Heads up ⚠️**: I programmed this in April 2016. I then converted all my snippets, so I haven't needed this tool since. In the last years, both TextExpander and Alfred have released several new major versions, which may or may not work with this tool (Judging by the support requests I get, they don't!) 4 | 5 | If this tool does not work, I can't help you. If you know Node.js, you may be able to fix it yourself and send a Pull Request to my respository. I will probably accept it! 6 | 7 | ## Motivation 8 | 9 | With [TextExpander](https://smilesoftware.com/textexpander) changing its business model, the new [Alfred](https://www.alfredapp.com/) version supporting text replacement, and the general wish to use the smallest number of different apps, I wrote this script to convert the `.textexpander` files to `.alfredsnippets` files. 10 | 11 | TextExpander uses a `plist` with all the entries in an array, and Alfred uses a ZIP file containing a JSON file for each snippet. 12 | 13 | ## Usage 14 | 15 | First, you'll have to export your TextExpander snippets. To do that, right click on a snippet folder and select `Save a Copy of Group`. 16 | 17 | Then, you just have to run the program with the `.textexpander` file as the only argument. It will then put a `.alfredsnippets` file in your current working directory. Open that file with Alfred to load the snippets. 18 | 19 | You could install the tool with `npm install -g textexpander-to-alfred3`, but you can also use `npx` to skip the permanent installation:: 20 | 21 | ```sh 22 | npx textexpander-to-alfred3 Signatures.textexpander 23 | > Wrote file /Users/You/Signatures.alfredsnippets 24 | ``` 25 | 26 | __NOTE__: For some reason, TextExpander v3 does not export a snippet group's group-level prefix, so you will have to re-do that in Alfred once you import it. 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "textexpander-to-alfred3", 3 | "version": "0.2.0", 4 | "description": "Convert your TextExpander snippets to Alfred 3 snippets.", 5 | "main": "textexpander-to-alfred3.js", 6 | "scripts": {}, 7 | "author": { 8 | "name": "Daniel Diekmeier", 9 | "email": "npm@danieldiekmeier.de", 10 | "url": "https://danieldiekmeier.de" 11 | }, 12 | "license": "ISC", 13 | "bin": { 14 | "textexpander-to-alfred3": "textexpander-to-alfred3.js", 15 | "te2a3": "textexpander-to-alfred3.js" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/danieldiekmeier/textexpander-to-alfred3" 20 | }, 21 | "dependencies": { 22 | "archiver": "^1.0.0", 23 | "commander": "^2.9.0", 24 | "plist": "^1.2.0", 25 | "sanitize-filename": "^1.6.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /textexpander-to-alfred3.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict' 4 | 5 | const fs = require('fs') 6 | const path = require('path') 7 | const archiver = require('archiver') 8 | const sanitize = require('sanitize-filename') 9 | const plist = require('plist') 10 | const program = require('commander') 11 | 12 | program 13 | .version('0.1.2') 14 | .usage('') 15 | .option('', '.textexpander source file') 16 | .parse(process.argv) 17 | 18 | // show help by default 19 | if (!process.argv.slice(2).length) { 20 | program.outputHelp() 21 | } else { 22 | main() 23 | } 24 | 25 | function transformSnippet (snippet) { 26 | const name = !snippet.label || snippet.label === '' ? snippet.abbreviation.slice(0, 10) : snippet.label 27 | 28 | return { 29 | filename: sanitize(name) + ' [' + snippet.uuidString + '].json', 30 | usable: Boolean(snippet.plainText), 31 | content: JSON.stringify({ 32 | alfredsnippet: { 33 | snippet: snippet.plainText ? snippet.plainText.replace(/%clipboard/g, '{clipboard}').replace(/%\|/g, '{cursor}') : false, 34 | name: name, 35 | uid: snippet.uuidString, 36 | keyword: snippet.abbreviation 37 | } 38 | }, null, 2) 39 | } 40 | } 41 | 42 | function main () { 43 | let plistPath = program.args[0] 44 | if (!path.isAbsolute(plistPath)) { 45 | plistPath = path.join(process.cwd(), program.args[0]) 46 | } 47 | 48 | fs.readFile(plistPath, 'utf-8', function (err, plistString) { 49 | if (err) { throw err } 50 | 51 | const basename = path.basename(program.args[0], '.textexpander') 52 | const outFile = path.join(process.cwd(), (basename + '.alfredsnippets')) 53 | 54 | const output = fs.createWriteStream(outFile) 55 | const archive = textexpander2Alfred(plistString) 56 | 57 | archive.pipe(output) 58 | 59 | output.on('close', function () { 60 | console.log(`Wrote file ${outFile}`) 61 | }) 62 | }) 63 | } 64 | 65 | function textexpander2Alfred (plistString) { 66 | const parsedPlist = plist.parse(plistString) 67 | 68 | const archive = archiver('zip') 69 | 70 | // Use the TextExpander 3 snippet collection, if available. 71 | // The `snippetsTE2` key is included in the v3 snippet plist file 72 | // with a warning message in case someone using TextExpander v2 tries 73 | // to import a v3 snippet file. 74 | const snippets = parsedPlist.snippetsTE3 || parsedPlist.snippetsTE2 75 | 76 | snippets 77 | .map(transformSnippet) 78 | .filter((snippet) => { 79 | return snippet.usable 80 | }) 81 | .forEach(function (snippet) { 82 | archive.append(snippet.content, { 83 | name: snippet.filename 84 | }) 85 | }) 86 | 87 | archive.finalize() 88 | 89 | return archive 90 | } 91 | 92 | module.exports = textexpander2Alfred 93 | --------------------------------------------------------------------------------