<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Brett Terpstraos x page  - Brett Terpstra</title>
	<atom:link href="http://brettterpstra.com/tag/os-x/feed/" rel="self" type="application/rss+xml" />
	<link>http://brettterpstra.com</link>
	<description>Elegant solutions to complex problems.</description>
	<lastBuildDate>Tue, 22 May 2012 02:49:10 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=</generator>
		<item>
		<title>KeyBindings: new, improved “surround” commands</title>
		<link>http://brettterpstra.com/keybindings-new-improved-surround-commands/</link>
		<comments>http://brettterpstra.com/keybindings-new-improved-surround-commands/#comments</comments>
		<pubDate>Tue, 15 Nov 2011 14:30:10 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[keybindings]]></category>
		<category><![CDATA[keyboard]]></category>
		<category><![CDATA[os x]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=3186</guid>
		<description><![CDATA[<p>As the next part in the keybindings series I’m demonstrating some improvements I’ve made to the original “surround” commands since my first time around. This set of commands is designed to wrap selected text in a variety of paired characters. The keys are the same, but the commands now work inside of single-line text fields (like you often find in&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/keybindings-new-improved-surround-commands/">KeyBindings: new, improved “surround” commands</a></p>]]></description>
			<content:encoded><![CDATA[<p><img style=' display: block; margin-right: auto; margin-left: auto;'  src="http://cdn2.brettterpstra.com/wp-content/uploads/2011/11/Apple-Keyboard-Header-Image.jpg?9d7bd4" alt="Post header keyboard image" title="Apple-Keyboard-Header-Image" width="650" height="200" class="aligncenter size-full wp-image-3126" /></p>

<p>As the next part in the keybindings series I’m demonstrating some improvements I’ve made to the original “surround” commands since my <a href="http://brettterpstra.com/keybinding-madness/">first time around</a>. This set of commands is designed to wrap selected text in a variety of paired characters. The keys are the same, but the commands now work inside of single-line text fields (like you often find in Safari), prevent auto-pairing in apps like nvALT, MultiMarkdown Composer and Byword, and a few refinements to cursor positioning.</p>

<p><span id="more-3186"></span></p>

<p>Here’s the code to drop into ‘DefaultKeyBinding.dict’, located at <code>~/Library/DefaultKeyBinding.dict</code> (create the folder and/or the file if you don’t have one<sup id="fnref:2"><a href="#fn:2" rel="footnote">1</a></sup>):</p>

<pre><code>  "^@s" = {   // surround commands
    // wrap () with spaces
    "(" = (delete:, insertText:, "( ", yank:, insertText:, " ", moveLeft:, insertText:, " )", deleteForward:);
    // wrap () no spaces
    ")" = (delete:, insertText:, "( ", deleteBackward:, yank:, insertText:, " ", moveLeft:, insertText:, ")", deleteForward:);
    // wrap [] with spaces
    "[" = (delete:, insertText:, "[ ", yank:, insertText:, " ", moveLeft:, insertText:, " ]", deleteForward:);
    // wrap [] no spaces
    "]" = (delete:, insertText:, "[ ", deleteBackward:, yank:, insertText:, " ", moveLeft:, insertText:, "]", deleteForward:);
    // wrap {} with spaces
    "{" = (delete:, insertText:, "{ ", yank:, insertText:, " ", moveLeft:, insertText:, " }", deleteForward:);
    // wrap {} no spaces
    "}" = (delete:, insertText:, "{ ", deleteBackward:, yank:, insertText:, " ", moveLeft:, insertText:, "}", deleteForward:);
    // wrap &lt;&gt; with spaces
    "&lt;" = (delete:, insertText:, "&lt; ", yank:, insertText:, " ", moveLeft:, insertText:, " &gt;", deleteForward:);
    // wrap &lt;&gt; no spaces
    "&gt;" = (delete:, insertText:, "&lt; ", deleteBackward:, yank:, insertText:, " ", moveLeft:, insertText:, "&gt;", deleteForward:);
    // wrap single quotes
    "'" = (delete:, insertText:, "' ", deleteBackward:, yank:, insertText:, " ", moveLeft:, insertText:, "'", deleteForward:);
    // wrap backticks
    "`" = (delete:, insertText:, "` ", deleteBackward:, yank:, insertText:, " ", moveLeft:, insertText:, "`", deleteForward:);
    // wrap double quote
    "\"" = (delete:, insertText:, "\" ", deleteBackward:, yank:, insertText:, " ", moveLeft:, insertText:, "\"", deleteForward:);
  };
</code></pre>

<p><strong>Key:</strong> ^=control, @=command</p>

<p>The whole set is bound to Control-Command-S, so you use them by:
* selecting some text that needs quotes, parenthesis, brackets, etc. wrapped around it
* press Control-Command-S
* typing the key you want to wrap the text in</p>

<p>It’s set up to handle parenthesis, square brackets, angle brackets, curly brackets, single/double quotes and backticks. In the parenthesis and brackets, typing the left character of the pair adds the wrapping with spaces on the inside of the pair (e.g. <code>( text I wrapped )</code>) and the right character wraps with no spaces (e.g. <code>(text I wrapped</code>)).</p>

<p>If you just want one or two and don’t need them bundled in the Control-Command-S wrapper, you can always extract them from that section of the plist and move them into the root. Because we’re just wrapping and not autopairing, the commands make more sense (to me) with a hotkey to enable them.</p>

<p>If you don’t have a selection when you run these, it’s going to insert the last thing it “yanked” and you never know what you’ll get. The yank pasteboard is separate from the main pasteboard, so it has little to do with what you intentionally copied last. If you’re using these keybindings, it will likely contain the last selection you ran one of them on.</p>

<p>Just for edification, let’s dissect one, shall we? Here’s the binding for wrapping in double quotes, made a little prettier for readability:</p>

<pre><code>// wrap double quote
"\"" = (    delete:, 
            insertText:, "\" ", 
            deleteBackward:, 
            yank:, 
            insertText:, " ", 
            moveLeft:, 
            insertText:, "\"", 
            deleteForward:, 
    );
</code></pre>

<ul>
<li>In the first three line delete the selection. The text is now in our yank pasteboard.</li>
<li>Then we insert the text “\” “

<ul>
<li>The backslash escapes the double quote so it doesn’t break the surrounding quotes in the plist</li>
<li>The trailing space fools most auto-pairing setups into not inserting the right-side pairing automatically. We immediately delete backward to remove the extra space</li>
</ul></li>
<li>Next, we use the “yank:” method to put back the text we originally cut out and insert a space immediately after it. The space allows us to manipulate the end of the text without hitting the end of the line, as would happen in certain editors and always in single-line text fields.</li>
<li>We move left past the new space and insert the closing quote</li>
<li>Finally, we delete to the  right remove the trailing space</li>
</ul>

<p>We want the cursor to end up after the last character of the wrapped text, so we can just stop there.</p>

<p>Hopefully that gives you enough ammo to start coming up with more, and enough of a taste to help you realize that this is seriously the most powerful text tool in your arsenal. It’s application agnostic, completely customizable and totally free. Have fun.</p>

<p>For my full DefaultKeyBinding.dict file, check out the <a href="https://github.com/ttscoff/KeyBindings">GitHub repo</a> and feel free to steal/fork mine. I’ll continue posting and describing pieces of it, but if you’re impatient and know how to read it already, go for it.</p>

<div class="footnotes">
<hr />
<ol>

<li id="fn:2">
<p>If you’re creating a new DefaultKeyBinding.dict file, be sure to add a pair of curly brackets ({}) in the blank file and insert any new bindings between them. <a href="#fnref:2" rev="footnote">↩</a></p>
</li>

</ol>
</div>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/new-text-navigation-keybindings/' rel='bookmark' title='New text navigation KeyBindings'>New text navigation KeyBindings</a></li>
<li><a href='http://brettterpstra.com/keybinding-madness/' rel='bookmark' title='KeyBinding madness'>KeyBinding madness</a></li>
<li><a href='http://brettterpstra.com/the-keys-that-bind-keybinding-madness-part-2/' rel='bookmark' title='The keys that bind: KeyBinding Madness part 2'>The keys that bind: KeyBinding Madness part 2</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/keybindings-new-improved-surround-commands/">KeyBindings: new, improved “surround” commands</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/keybindings-new-improved-surround-commands/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>QuickQuestion 1.1 update</title>
		<link>http://brettterpstra.com/quickquestion-1-1-update/</link>
		<comments>http://brettterpstra.com/quickquestion-1-1-update/#comments</comments>
		<pubDate>Tue, 18 Oct 2011 19:00:28 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[notes]]></category>
		<category><![CDATA[os x]]></category>
		<category><![CDATA[productivity]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=2971</guid>
		<description><![CDATA[<p>I’ve made a small update to QuickQuestion. It adds the ability to specify part of the answer to be copied to the clipboard when one of the scripts (Alfred, Launchbar, CLI) runs. You format it with @copy(part to copy) and use it inline. The @copy syntax is stripped out when the answer is displayed, just like @(metadata). So if the&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/quickquestion-1-1-update/">QuickQuestion 1.1 update</a></p>]]></description>
			<content:encoded><![CDATA[<p>I’ve made a small update to <a href="http://brettterpstra.com/forget-about-it-or-not/">QuickQuestion</a>. It adds the ability to specify part of the answer to be copied to the clipboard when one of the scripts (Alfred, Launchbar, CLI) runs. You format it with <code>@copy(part to copy)</code> and use it inline. The @copy syntax is stripped out when the answer is displayed, just like @(metadata). So if the answer to your question is a command, url or anything else you’re probably going to copy and paste anyway, just add something like this:</p>

<pre><code>You can monitor all file access with @copy(sudo opensnoop)
</code></pre>

<p>The answer would be presented as “You can monitor all file access with sudo opensnoop,” and <code>sudo opensnoop</code> would be copied to your clipboard. If you don’t have an <code>@copy()</code> in the answer, nothing special happens.</p>

<p>If your answers are in Markdown files and you’re having any trouble querying them, <a href="http://brettterpstra.com/fixing-spotlight-indexing-of-markdown-content/">this will help</a>.</p>

<div class="download_desc"><p class="download-icon"><a href="http://brettterpstra.com/downloads/quickquestion1.1.zip?9d7bd4" title="Download QuickQuestion (324)"><img src="http://cdn2.brettterpstra.com/wp-content/uploads/downloads/thumbnails/2011/10/qqicon.png?9d7bd4" alt="download image for QuickQuestion" width="64" /></a><br /><a href="http://brettterpstra.com/downloads/quickquestion1.1.zip?9d7bd4" title="Download QuickQuestion (324)" class="download-button">Download</a></p><p class="desc"><a href="http://brettterpstra.com/downloads/quickquestion1.1.zip?9d7bd4" title="Download QuickQuestion (324)">QuickQuestion</a> — A set of scripts for building and querying a plain-text knowledgebase on OS X. Includes CLI, LaunchBar actions and Alfred extensions. Works great with Notational Velocity/nvALT, but does just fine without. <a href="https://github.com/ttscoff/QuickQuestion">More Info</a></p></div>

<p>Source and downloads also <a href="https://github.com/ttscoff/QuickQuestion">on GitHub</a>.</p>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/geeklet-top-cpu-processes/' rel='bookmark' title='Geeklet: Top CPU processes'>Geeklet: Top CPU processes</a></li>
<li><a href='http://brettterpstra.com/forget-about-it-or-not/' rel='bookmark' title='Forget about it. Or not.'>Forget about it. Or not.</a></li>
<li><a href='http://brettterpstra.com/quick-tip-running-nvalt-after-a-notational-velocity-update/' rel='bookmark' title='Quick tip: running nvALT after a Notational Velocity update'>Quick tip: running nvALT after a Notational Velocity update</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/quickquestion-1-1-update/">QuickQuestion 1.1 update</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/quickquestion-1-1-update/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Forget about it. Or not.</title>
		<link>http://brettterpstra.com/forget-about-it-or-not/</link>
		<comments>http://brettterpstra.com/forget-about-it-or-not/#comments</comments>
		<pubDate>Sun, 02 Oct 2011 23:07:31 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[notes]]></category>
		<category><![CDATA[os x]]></category>
		<category><![CDATA[productivity]]></category>
		<category><![CDATA[tagging]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=2888</guid>
		<description><![CDATA[<p>I forget a lot of things. I forget how I did something, or where I found an answer, or where I put my glasses. I use technology to help with that to some extent, and Notational Velocity/nvALT are excellent at helping me find previous notes, assuming I bothered to take them. I keep a log in VoodooPad, especially when I’m&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/forget-about-it-or-not/">Forget about it. Or not.</a></p>]]></description>
			<content:encoded><![CDATA[<p><img style=' display: block; margin-right: auto; margin-left: auto;'  src="http://cdn2.brettterpstra.com/wp-content/uploads/2011/10/Iforgetchalkboard.jpeg?9d7bd4" alt="Blank chalkboard image. Have I ever mentioned how much I dig the Acorn image editor from Flying Meat?" title="I forget" width="634" height="123" class="aligncenter size-full wp-image-2890" /></p>

<p>I forget a lot of things. I forget how I did something, or where I found an answer, or where I put my glasses. I use technology to help with that to some extent, and Notational Velocity/nvALT are excellent at helping me find previous notes, assuming I bothered to take them.</p>

<p>I keep a log in <a href="http://flyingmeat.com/voodoopad/">VoodooPad</a>, especially when I’m up late enough at my computer that I might not remember what I was working on in the morning. That’s handy, the search is excellent, and gathering tagged entries together on one page automatically is sweet. After a couple of years of doing it religiously, though, I’ve found that I rarely reference anything beyond the previous day or two. I rarely go back because the things I log aren’t “knowledge”, just activities. My logging system lacks the details I need to remember exactly <em>how</em> I did what I did. That’s my own fault, of course, but I needed a system that would build an environment-agnostic knowledgebase for remembering all the things I tend to forget.</p>

<p>Thus, I’ve been playing with an idea. I’m building a collection of plain-text notes (primarily using nvALT with notes saved to a Dropbox folder) where the title of the note is the question I think I would ask myself long after I’ve forgotten the answer. The content of the note is the answer in its briefest form, including commands, filenames, urls and whatever is necessary to get me back on the right track.</p>

<p><span id="more-2888"></span></p>

<p>I can query these notes quickly from the command line (<code>qq where glasses</code> or <code>qq best h.264 settings</code>), as well as from LaunchBar and Alfred. Adding new notes can be done just as easily from the same sources. The text files are stored among all of my nvALT notes (with a special prefix to easily separate them), so it automatically benefits from Notational Velocity-style searching and editing.</p>

<p>One of the reasons I prefer to store my nvALT notes as plain text files is that I have access to them from Spotlight and <code>mdfind</code> (<a href="http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man1/mdfind.1.html">man page</a>). Spotlight allows me to query a little more intricately, separating tags from titles and titles from content in a way that nvALT currently can’t. <code>mdfind</code> is the crux of this system. You can store all you want in plain text and feel great about it, but the ease of querying any information system is the measure of its worth.</p>

<p>I like <a href="http://brettterpstra.com/tag/tagging/">tagging</a>. I’ve found that tagging isn’t the solution for my forgetfulness, though. It’s been extremely helpful in tracking projects across multiple folders, but not so much in dealing with aged notes. I’m as likely to forget a tag I associated as I am anything else. Extra, more verbose text seems to be the trick. Instead of piling on a bunch of tags, I set my scripts up to search–but not output–anything inside of a @(extra text tag). Then I can add extra verbosity and keywords that I think might help the search later… without fouling up my tag database.</p>

<p>While there’s definite GTD-style comfort in knowing I have my discoveries and important notes in a portable, syncable system, the process of thinking through the syntax of the question/filename is actually helping me remember things better to begin with. It’s a conscious effort to connect neurons in a way that keeps the information more readily accessible to my feeble memory. I’ve only been doing it for a month, though, so I’m curious to see how the knowledgebase looks and functions in a year.</p>

<p>I’ve put the whole system and installation/usage/best practices <a href="http://ttscoff.github.com/QuickQuestion/">up on GitHub</a>. There’s a command line tool (bash), a pair of LaunchBar Actions and two standalone Alfred extensions for adding and querying the notes. As I use it more, I’ll probably come up with more ways to interface with it. Right now I’m thinking about an AppleScript/Mail Rule-based system for adding and querying notes via a GMail alias. More to come, pending determination that the new method is worth the fiddling time in the end. I do love having universal access to any bucket I use, though…</p>

<p>If you’ve ever forgotten where you put something important or solved a problem and later forgotten the solution, check out the <a href="http://ttscoff.github.com/QuickQuestion/">QuickQuestion GitHub page</a>. The Readme and up-to-date files are also available as a direct download below.</p>

<div class="download_desc"><p class="download-icon"><a href="http://brettterpstra.com/downloads/quickquestion1.1.zip?9d7bd4" title="Download QuickQuestion (324)"><img src="http://cdn2.brettterpstra.com/wp-content/uploads/downloads/thumbnails/2011/10/qqicon.png?9d7bd4" alt="download image for QuickQuestion" width="64" /></a><br /><a href="http://brettterpstra.com/downloads/quickquestion1.1.zip?9d7bd4" title="Download QuickQuestion (324)" class="download-button">Download</a></p><p class="desc"><a href="http://brettterpstra.com/downloads/quickquestion1.1.zip?9d7bd4" title="Download QuickQuestion (324)">QuickQuestion</a> — A set of scripts for building and querying a plain-text knowledgebase on OS X. Includes CLI, LaunchBar actions and Alfred extensions. Works great with Notational Velocity/nvALT, but does just fine without. <a href="https://github.com/ttscoff/QuickQuestion">More Info</a></p></div>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/quick-tip-running-nvalt-after-a-notational-velocity-update/' rel='bookmark' title='Quick tip: running nvALT after a Notational Velocity update'>Quick tip: running nvALT after a Notational Velocity update</a></li>
<li><a href='http://brettterpstra.com/taking-the-markdown-to-evernote-service-further/' rel='bookmark' title='Taking the Markdown to Evernote service further'>Taking the Markdown to Evernote service further</a></li>
<li><a href='http://brettterpstra.com/nvalt-2-2-public-beta/' rel='bookmark' title='nvALT 2.2 public beta'>nvALT 2.2 public beta</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/forget-about-it-or-not/">Forget about it. Or not.</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/forget-about-it-or-not/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>KeyBinding madness</title>
		<link>http://brettterpstra.com/keybinding-madness/</link>
		<comments>http://brettterpstra.com/keybinding-madness/#comments</comments>
		<pubDate>Sat, 13 Aug 2011 16:23:36 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[keybindings]]></category>
		<category><![CDATA[keyboard]]></category>
		<category><![CDATA[os x]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=2506</guid>
		<description><![CDATA[<p>There’s a somewhat mysterious individual who is consistently leaving brilliant little pieces of code and information in my comments. Dr. Drang has been visited by this apparition as well. We’re both duly impressed with what he (or she?) does. So, I’m digging through the code Lri’s posted and I find a DefaultKeyBinding.dict file. It’s the best one I’ve ever seen,&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/keybinding-madness/">KeyBinding madness</a></p>]]></description>
			<content:encoded><![CDATA[<p><img style=' float: right; padding: 4px; margin: 0 0 2px 7px;'  class="alignright size-full wp-image-2508" title="mackeys" src="http://cdn2.brettterpstra.com/wp-content/uploads/2011/08/mackeys.jpg?9d7bd4" alt="" width="350" height="333" />There’s a somewhat mysterious individual who is consistently leaving brilliant little pieces of code and information in my comments. <a href="http://www.leancrew.com/all-this/">Dr. Drang</a> has been visited by this apparition as well. We’re both duly impressed with what he (or she?) does.</p>

<p>So, I’m digging through the code Lri’s posted and I find a DefaultKeyBinding.dict file. It’s the best one I’ve ever seen, and it inspires me to do some “fiddling” of my own. I figured I’d post my result, but with due credit to Lri (hi, Lri!).</p>

<p>The DefaultKeyBinding.dict file tells your computer at a fairly root level how to handle keyboard shortcuts in input fields. It doesn’t work everywhere, but sometimes it’s surprising where it <em>does</em> work. In those cases, you can save yourself a ton of time by committing a few custom keystrokes to memory.</p>

<p>You may be familiar with the basic Emacs keybindings that are default (Ctrl-a beginning of line, Ctrl-e end of line, etc.), but this allows you to go much further. For more information, check out <a href="http://hints.macworld.com/article.php?story=20060317045211408">Macworld hints</a>, an older but very relevant <a href="http://www.hcs.harvard.edu/~jrus/site/cocoa-text.html">article from Harvard</a> and do <a href="http://duckduckgo.com/?q=cocoa+defaultkeybindings.dict">a search for more information</a>.</p>

<p>Basically, all you need to do is put the DefaultKeyBinding.dict file into <code>~/Library/KeyBindings</code> and restart applications you want it to work in. Edit away, and change shortcuts to things you’re comfortable with. Several of mine are sequences which allow groupings, which I like, but you might not. Be aware that these can be a little unpredictable as to when they’ll override a system/application default and when they’ll be overridden (I know there’s a logic to it, I just don’t want to dive into that).</p>

<p>I’ve posted my <a href="http://ttscoff.github.com/KeyBindings/">KeyBindings on GitHub</a> and included a cheatsheet in MultiMarkdown as well as a Readme with the tables already rendered. Direct download link below. If you’re looking for something to nerd out on this weekend, this should whet your appetite.</p>

<div class="download_desc"><p class="download-icon"><a href="http://brettterpstra.com/downloads/keybindings" title="Download Killer DefaultKeyBinding.dict (933)"><img src="http://cdn2.brettterpstra.com/wp-content/uploads/downloads/thumbnails/2011/08/Keyboard_Down.png?9d7bd4" alt="download image for Killer DefaultKeyBinding.dict" width="64" /></a><br /><a href="http://brettterpstra.com/downloads/keybindings" title="Download Killer DefaultKeyBinding.dict (933)" class="download-button">Download</a></p><p class="desc"><a href="http://brettterpstra.com/downloads/keybindings" title="Download Killer DefaultKeyBinding.dict (933)">Killer DefaultKeyBinding.dict</a> — A custom DefaultKeyBinding.dict file which adds shortcuts for document navigation, Markdown and much more. Inspired by <a href="http://www.cs.helsinki.fi/u/lranta/keybindings/">Lri</a>. <a href="http://brettterpstra.com/keybinding-madness/">More Info</a></p></div>

<p>Just for reference, here’s the table of shortcuts in the KeyBindings file:</p>

<p><span id="more-2506"></span></p>

<p><em>Grouped items begin with the groups shortcut, followed by the Keys specified. Items separated by commas are sequential, not concurrent.</em></p>

<table>
<caption id="allpurposekeycombos"> All purpose key combos </caption>
<colgroup>
<col style="text-align:center;"/>
<col style="text-align:center;"/>
<col style="text-align:left;"/>
</colgroup>

<thead>
<tr>
    <th style="text-align:center;">Group</th>
    <th style="text-align:center;">Keys</th>
    <th style="text-align:left;">Function</th>
</tr>
</thead>

<tbody>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-z</td>
    <td style="text-align:left;">copy character before cursor</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-r</td>
    <td style="text-align:left;">repeat character before cursor</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-t</td>
    <td style="text-align:left;">transpose characters</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-+</td>
    <td style="text-align:left;">uppercase word</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt–</td>
    <td style="text-align:left;">lowercase word</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-.</td>
    <td style="text-align:left;">capitalize word</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Ctrl-w</td>
    <td style="text-align:left;">delete word before cursor</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-w</td>
    <td style="text-align:left;">select word</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-f</td>
    <td style="text-align:left;">cut word (fold)</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-g</td>
    <td style="text-align:left;">copy word (grab)</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-h</td>
    <td style="text-align:left;">paste word (hit)</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-l</td>
    <td style="text-align:left;">select entire line/paragraph</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-s</td>
    <td style="text-align:left;">select from beginning of paragrah to last character</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-d</td>
    <td style="text-align:left;">delete line/paragraph</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-y</td>
    <td style="text-align:left;">copy paragraph</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-x</td>
    <td style="text-align:left;">cut paragraph</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-p</td>
    <td style="text-align:left;">paste paragraph below</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-Shift-P</td>
    <td style="text-align:left;">paste paragraph above</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Ctrl-Shift-A</td>
    <td style="text-align:left;">select to beginning of paragraph and copy</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Ctrl-Shift-E</td>
    <td style="text-align:left;">select to end of paragraph and copy</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-q</td>
    <td style="text-align:left;">cut to beginning of paragraph</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-k</td>
    <td style="text-align:left;">cut to end of paragraph</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-,</td>
    <td style="text-align:left;">move paragraph up a line, inserting blank space after</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-␠</td>
    <td style="text-align:left;">move paragraph to end of document</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-o</td>
    <td style="text-align:left;">blank line after current</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-Shift-O</td>
    <td style="text-align:left;">blank line before current</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Cmd-Opt-/</td>
    <td style="text-align:left;">comment with “//”</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Cmd-Opt-3</td>
    <td style="text-align:left;">comment with “#”</td>
</tr>
</tbody>
</table>

<table>
<caption id="markdown-specificshortcutsx2318w"> Markdown-specific shortcuts (Ctrl-Cmd-W) </caption>
<colgroup>
<col style="text-align:center;"/>
<col style="text-align:center;"/>
<col style="text-align:left;"/>
</colgroup>

<thead>
<tr>
    <th style="text-align:center;">Group</th>
    <th style="text-align:center;">Keys</th>
    <th style="text-align:left;">Function</th>
</tr>
</thead>

<tbody>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Cmd-Opt-b</td>
    <td style="text-align:left;">bold selection</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Cmd-Opt-i</td>
    <td style="text-align:left;">italicize selection</td>
</tr>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-w</td>
    <td style="text-align:center;">Return</td>
    <td style="text-align:left;">force Return</td>
</tr>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-w</td>
    <td style="text-align:center;">Tab</td>
    <td style="text-align:left;">force Tab</td>
</tr>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-w</td>
    <td style="text-align:center;">1–4</td>
    <td style="text-align:left;"># Headlines</td>
</tr>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-w</td>
    <td style="text-align:center;">l,t</td>
    <td style="text-align:left;">link text <code>"[selected text](|)"</code></td>
</tr>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-w</td>
    <td style="text-align:center;">l,c</td>
    <td style="text-align:left;">clipboard link <code>"[|selected text](clipboard contents)"</code></td>
</tr>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-w</td>
    <td style="text-align:center;">i,t</td>
    <td style="text-align:left;">image, selection as alt <code>"![selected text](|)"</code></td>
</tr>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-w</td>
    <td style="text-align:center;">i,c</td>
    <td style="text-align:left;">clipboard image <code>"![|selected text](clipboard contents)"</code></td>
</tr>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-w</td>
    <td style="text-align:center;">:,t</td>
    <td style="text-align:left;">create a reference from selected text</td>
</tr>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-w</td>
    <td style="text-align:center;">:,c</td>
    <td style="text-align:left;">create a reference from selected text, clipboard as url</td>
</tr>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-w</td>
    <td style="text-align:center;">[</td>
    <td style="text-align:left;">insert reference link</td>
</tr>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-w</td>
    <td style="text-align:center;">+|-|*</td>
    <td style="text-align:left;">lists</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-Shift-L</td>
    <td style="text-align:left;">new list item after current</td>
</tr>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-w</td>
    <td style="text-align:center;">e</td>
    <td style="text-align:left;">entity</td>
</tr>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-w</td>
    <td style="text-align:center;">/</td>
    <td style="text-align:left;">http://</td>
</tr>
</tbody>
</table>

<table>
<caption id="surroundx2318s"> Surround (Ctrl-Cmd-s) </caption>
<colgroup>
<col style="text-align:center;"/>
<col style="text-align:center;"/>
<col style="text-align:left;"/>
</colgroup>

<thead>
<tr>
    <th style="text-align:center;">Group</th>
    <th style="text-align:center;">Keys</th>
    <th style="text-align:left;">Function</th>
</tr>
</thead>

<tbody>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-s</td>
    <td style="text-align:center;">(</td>
    <td style="text-align:left;">wrap () with spaces</td>
</tr>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-s</td>
    <td style="text-align:center;">)</td>
    <td style="text-align:left;">wrap () no spaces</td>
</tr>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-s</td>
    <td style="text-align:center;">[</td>
    <td style="text-align:left;">wrap [] with spaces</td>
</tr>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-s</td>
    <td style="text-align:center;">]</td>
    <td style="text-align:left;">wrap [] no spaces</td>
</tr>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-s</td>
    <td style="text-align:center;">{</td>
    <td style="text-align:left;">wrap {} with spaces</td>
</tr>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-s</td>
    <td style="text-align:center;">}</td>
    <td style="text-align:left;">wrap {} no spaces</td>
</tr>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-s</td>
    <td style="text-align:center;">&lt;</td>
    <td style="text-align:left;">wrap &lt;&gt; with spaces</td>
</tr>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-s</td>
    <td style="text-align:center;">&gt;</td>
    <td style="text-align:left;">wrap &lt;&gt; no spaces</td>
</tr>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-s</td>
    <td style="text-align:center;">’</td>
    <td style="text-align:left;">wrap single quotes</td>
</tr>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-s</td>
    <td style="text-align:center;">‘</td>
    <td style="text-align:left;">wrap backticks</td>
</tr>
<tr>
    <td style="text-align:center;">Ctrl-Cmd-s</td>
    <td style="text-align:center;">”</td>
    <td style="text-align:left;">wrap double quote</td>
</tr>
</tbody>
</table>

<table>
<caption id="listparagraphmotion"> List/Paragraph motion </caption>
<colgroup>
<col style="text-align:center;"/>
<col style="text-align:center;"/>
<col style="text-align:left;"/>
</colgroup>

<thead>
<tr>
    <th style="text-align:center;">Group</th>
    <th style="text-align:center;">Keys</th>
    <th style="text-align:left;">Function</th>
</tr>
</thead>

<tbody>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Ctrl-Cmd-k</td>
    <td style="text-align:left;">move line up</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Ctrl-Cmd-j</td>
    <td style="text-align:left;">move line down</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Ctrl-Cmd-l</td>
    <td style="text-align:left;">indent line</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Ctrl-Cmd-h</td>
    <td style="text-align:left;">outdent line (all the way)</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Ctrl-Opt-k</td>
    <td style="text-align:left;">copy line and paste above</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Ctrl-Opt-j</td>
    <td style="text-align:left;">copy line and paste below</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Cmd-Opt-Up</td>
    <td style="text-align:left;">modify selection up</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Cmd-Opt-Down</td>
    <td style="text-align:left;">modify selection down</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Cmd-Del</td>
    <td style="text-align:left;">Forward delete to end of paragraph</td>
</tr>
</tbody>
</table>

<table>
<caption id="documentnavigation"> Document navigation </caption>
<colgroup>
<col style="text-align:center;"/>
<col style="text-align:center;"/>
<col style="text-align:left;"/>
</colgroup>

<thead>
<tr>
    <th style="text-align:center;">Group</th>
    <th style="text-align:center;">Keys</th>
    <th style="text-align:left;">Function</th>
</tr>
</thead>

<tbody>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">PgUp</td>
    <td style="text-align:left;">Page Up</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">PgDown</td>
    <td style="text-align:left;">Page Down</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-PgUp</td>
    <td style="text-align:left;">Scroll up</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-PgDown</td>
    <td style="text-align:left;">Scroll Down</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-1</td>
    <td style="text-align:left;">bookmark</td>
</tr>
<tr>
    <td style="text-align:center;"></td>
    <td style="text-align:center;">Opt-2</td>
    <td style="text-align:left;">jump to bookmark</td>
</tr>
</tbody>
</table>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/keybindings-new-improved-surround-commands/' rel='bookmark' title='KeyBindings: new, improved “surround” commands'>KeyBindings: new, improved “surround” commands</a></li>
<li><a href='http://brettterpstra.com/the-keys-that-bind-keybinding-madness-part-2/' rel='bookmark' title='The keys that bind: KeyBinding Madness part 2'>The keys that bind: KeyBinding Madness part 2</a></li>
<li><a href='http://brettterpstra.com/option-arrow-navigation-in-iterm2/' rel='bookmark' title='Option-arrow navigation in iTerm2'>Option-arrow navigation in iTerm2</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/keybinding-madness/">KeyBinding madness</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/keybinding-madness/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>Prefixr OS X Service</title>
		<link>http://brettterpstra.com/prefixr-service/</link>
		<comments>http://brettterpstra.com/prefixr-service/#comments</comments>
		<pubDate>Wed, 10 Aug 2011 03:21:27 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[css3]]></category>
		<category><![CDATA[os x]]></category>
		<category><![CDATA[service]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=2483</guid>
		<description><![CDATA[<p>Just for giggles, and because Jeffrey Way was kind enough to provide a simple API for Prefixr, here’s a System Service that will turn your standard CSS3 properties into cross-browser, vendor-prefixed versions. You can select an entire stylesheet and it will send back the text with the necessary changes, or just run it on a single rule to have it&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/prefixr-service/">Prefixr OS X Service</a></p>]]></description>
			<content:encoded><![CDATA[<p>Just for giggles, and because Jeffrey Way was kind enough to provide a simple API for <a href="http://prefixr.com/index.php">Prefixr</a>, here’s a System Service that will turn your standard CSS3 properties into cross-browser, vendor-prefixed versions.</p>

<p>You can select an entire stylesheet and it will send back the text with the necessary changes, or just run it on a single rule to have it vendor-prefixed for you. It turns this:</p>


<div class="wp_syntax"><div class="code"><pre class="css">p <span class="br0">&#123;</span>
  transition<span class="sy0">:</span> all .2s ease-in-out<span class="sy0">;</span>
  box-shadow<span class="sy0">:</span> <span class="re3">1px</span> <span class="re3">1px</span> <span class="re3">4px</span> rgba<span class="br0">&#40;</span><span class="nu0">0</span><span class="sy0">,</span><span class="nu0">0</span><span class="sy0">,</span><span class="nu0">0</span><span class="sy0">,</span>.3<span class="br0">&#41;</span> <span class="sy0">;</span>
  <span class="kw1">color</span><span class="sy0">:</span> <span class="re0">#333</span><span class="sy0">;</span>
<span class="br0">&#125;</span></pre></div></div>


<p>into:</p>


<div class="wp_syntax"><div class="code"><pre class="css">p <span class="br0">&#123;</span>
	-webkit-transition<span class="sy0">:</span> all .2s ease-in-out<span class="sy0">;</span>
	-moz-transition<span class="sy0">:</span> all .2s ease-in-out<span class="sy0">;</span>
	-o-transition<span class="sy0">:</span> all .2s ease-in-out<span class="sy0">;</span>
	-ms-transition<span class="sy0">:</span> all .2s ease-in-out<span class="sy0">;</span>
	transition<span class="sy0">:</span> all .2s ease-in-out<span class="sy0">;</span>
&nbsp;
	-webkit-box-shadow<span class="sy0">:</span> <span class="re3">1px</span> <span class="re3">1px</span> <span class="re3">4px</span> rgba<span class="br0">&#40;</span><span class="nu0">0</span><span class="sy0">,</span><span class="nu0">0</span><span class="sy0">,</span><span class="nu0">0</span><span class="sy0">,</span>.3<span class="br0">&#41;</span><span class="sy0">;</span>
	-moz-box-shadow<span class="sy0">:</span> <span class="re3">1px</span> <span class="re3">1px</span> <span class="re3">4px</span> rgba<span class="br0">&#40;</span><span class="nu0">0</span><span class="sy0">,</span><span class="nu0">0</span><span class="sy0">,</span><span class="nu0">0</span><span class="sy0">,</span>.3<span class="br0">&#41;</span><span class="sy0">;</span>
	box-shadow<span class="sy0">:</span> <span class="re3">1px</span> <span class="re3">1px</span> <span class="re3">4px</span> rgba<span class="br0">&#40;</span><span class="nu0">0</span><span class="sy0">,</span><span class="nu0">0</span><span class="sy0">,</span><span class="nu0">0</span><span class="sy0">,</span>.3<span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
	<span class="kw1">color</span><span class="sy0">:</span> <span class="re0">#333</span><span class="sy0">;</span>
<span class="br0">&#125;</span></pre></div></div>


<p>Pretty handy. <a href="http://sass-lang.com/">Sass</a> can do this for you, and there are already plugins for TextMate, Vim, Espresso, Coda and more on Prefixr’s <a href="http://www.prefixr.com/api/usage/">API page</a>. Just thought I’d add one more to the pile.</p>

<p>Unzip it and drop it into <code>~/Library/Services</code>. Select some text containing CSS3 properties and right click on the selection, then look under “Services” for the Prefixer service. Run it and (assuming you have an internet connection), it’ll do some of the tedious parts of CSS3 for you.</p>

<div class="download_desc"><p class="download-icon"><a href="http://brettterpstra.com/downloads/PrefixrService.zip?9d7bd4" title="Download Prefixr Service (332)"><img src="http://brettterpstra.com/wp-content/images/serviceicon.jpg?9d7bd4" alt="download image for Prefixr Service" width="64" /></a><br /><a href="http://brettterpstra.com/downloads/PrefixrService.zip?9d7bd4" title="Download Prefixr Service (332)" class="download-button">Download</a></p><p class="desc"><a href="http://brettterpstra.com/downloads/PrefixrService.zip?9d7bd4" title="Download Prefixr Service (332)">Prefixr Service</a> — Uses the Prefixr.com API to process your standard CSS3 properties into cross-browser, vendor-prefixed versions. <a href="">More Info</a></p></div>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/app-review-gradient/' rel='bookmark' title='App Review: Gradient'>App Review: Gradient</a></li>
<li><a href='http://brettterpstra.com/a-little-hootsuite-userstyling/' rel='bookmark' title='A little HootSuite userstyling'>A little HootSuite userstyling</a></li>
<li><a href='http://brettterpstra.com/fixing-gleebox/' rel='bookmark' title='Fixing Gleebox'>Fixing Gleebox</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/prefixr-service/">Prefixr OS X Service</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/prefixr-service/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Make CSSEdit use the latest Webkit nightly every time</title>
		<link>http://brettterpstra.com/make-cssedit-use-the-latest-webkit-nightly-every-time/</link>
		<comments>http://brettterpstra.com/make-cssedit-use-the-latest-webkit-nightly-every-time/#comments</comments>
		<pubDate>Mon, 28 Mar 2011 11:54:26 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[cssedit]]></category>
		<category><![CDATA[os x]]></category>
		<category><![CDATA[webdev]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=2119</guid>
		<description><![CDATA[<p>If you’re a web designer with a Mac, you probably use–or at least know of–CSSEdit. In it’s heyday, it was the way to edit CSS. It’s fallen a little out of repair; it doesn’t recognize new selectors and properties (which messes up color coding and completion). Plus, it doesn’t play well with nifty frameworks like LESS or SASS, if you’re&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/make-cssedit-use-the-latest-webkit-nightly-every-time/">Make CSSEdit use the latest Webkit nightly every time</a></p>]]></description>
			<content:encoded><![CDATA[<p><img style=' float: right; padding: 4px; margin: 0 0 2px 7px;'  src="http://cdn2.brettterpstra.com/wp-content/uploads/2011/03/CSSEditWebkitMashup.jpg?9d7bd4" alt="Post main image" title="CSSEditWebkitMashup" width="350" height="276" class="alignright size-full wp-image-2130" />If you’re a web designer with a Mac, you probably use–or at least know of–<a href="http://macrabbit.com/cssedit/">CSSEdit</a>. In it’s heyday, it was <em>the</em> way to edit CSS. It’s fallen a little out of repair; it doesn’t recognize new selectors and properties (which messes up color coding and completion). Plus, it doesn’t play well with nifty frameworks like LESS or SASS, if you’re in the habit of using those. My <a href="http://brettterpstra.com/watch-for-file-changes-and-refresh-your-browser-automatically/">“watcher” script</a> was my solution to the latter. There’s a cool trick, though, that allows you to force CSSEdit to use the bleeding-edge Webkit for rendering.</p>

<p>Quite a while back, developer Jan Van Boghout <a href="http://macrabbit.com/blog/tip-cssedit-webkit-nightly/">posted a tip</a> for unlinking the default Webkit framework and using the one inside of the latest nightly build you have installed. It was a simple Terminal command that you had to launch from the command line, and the effect only lasted for that session. The command eventually stopped working, until <a href="http://macrabbit.com/blog/tip-cssedit-webkit-nightly/#c7970">Ian Beck noted</a> that the location of the frameworks in Webkit.app had changed. It worked with that tweak, but it was still a pain sometimes, even with aliases.</p>

<p><span id="more-2119"></span></p>

<h3>The solution</h3>

<p>I don’t know why it took me this long to think of it, but there’s an easy way to make the command into a wrapper within the CSSEdit application bundle. This isn’t for the faint of heart, and it will have to be done again if CSSEdit ever updates in the future, but it’s a good hack for the time being.</p>

<p>I’m going to write these instructions to exclusively use Terminal, although many steps can be accomplished through Finder (use Show Package Contents from the right click menu).</p>

<ul>
<li><p>Go to the executable folder within the CSSEdit.app bundle:</p>

<p><code>$ cd /Applications/CSSEdit.app/Contents/MacOS/</code></p></li>
<li><p>Rename the main executable:</p>

<p><code>$ mv CSSEdit _CSSEdit</code></p></li>
<li><p>Create a new CSSEdit file (using whatever editor you like):</p>

<p><code>$ vim CSSEdit</code></p></li>
<li>Put the following code into it:</li>
</ul>


<div class="wp_syntax"><div class="code"><pre class="bash"><span class="co0">#!/bin/bash</span>
<span class="kw2">env</span> <span class="re2">DYLD_FRAMEWORK_PATH</span>=<span class="sy0">/</span>Applications<span class="sy0">/</span>WebKit.app<span class="sy0">/</span>Contents<span class="sy0">/</span>Frameworks<span class="sy0">/</span><span class="nu0">10.6</span><span class="sy0">/</span> <span class="re2">WEBKIT_UNSET_DYLD_FRAMEWORK_PATH</span>=YES <span class="sy0">/</span>Applications<span class="sy0">/</span>CSSEdit.app<span class="sy0">/</span>Contents<span class="sy0">/</span>MacOS<span class="sy0">/</span>_CSSEdit</pre></div></div>


<ul>
<li><p>Save the file, close it, and make it executable:</p>

<p><code>chmod a+x CSSEdit</code></p></li>
</ul>

<p>Now, when you launch the app, it will run the shell command to unlink and relink the Webkit framework. Note that if you do this on Leopard, you’ll need to change the DYLD_FRAMEWORK_PATH to point to 10.5 instead of 10.6. Enjoy!</p>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/quick-tip-extracting-mac-app-store-reviews-as-text/' rel='bookmark' title='Quick Tip: Extracting Mac App Store reviews as text'>Quick Tip: Extracting Mac App Store reviews as text</a></li>
<li><a href='http://brettterpstra.com/prefixr-service/' rel='bookmark' title='Prefixr OS X Service'>Prefixr OS X Service</a></li>
<li><a href='http://brettterpstra.com/duplicating-safari-browsing-sessions-between-macs/' rel='bookmark' title='Duplicating Safari browsing sessions between Macs'>Duplicating Safari browsing sessions between Macs</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/make-cssedit-use-the-latest-webkit-nightly-every-time/">Make CSSEdit use the latest Webkit nightly every time</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/make-cssedit-use-the-latest-webkit-nightly-every-time/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Watch for file changes and refresh your browser automatically</title>
		<link>http://brettterpstra.com/watch-for-file-changes-and-refresh-your-browser-automatically/</link>
		<comments>http://brettterpstra.com/watch-for-file-changes-and-refresh-your-browser-automatically/#comments</comments>
		<pubDate>Mon, 07 Mar 2011 08:33:34 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[automator]]></category>
		<category><![CDATA[os x]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[service]]></category>
		<category><![CDATA[webdev]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=2056</guid>
		<description><![CDATA[<p>After my epic three-part post on Saturday, I spent the rest of the weekend doing more “useful” things. Now it’s Sunday night (Monday morning, I think), and I’ve got some kind of minor food poisoning which is currently keeping me awake. Thus a “hey, cool trick” post. I actually already own an app which can do this to some extent,&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/watch-for-file-changes-and-refresh-your-browser-automatically/">Watch for file changes and refresh your browser automatically</a></p>]]></description>
			<content:encoded><![CDATA[<p><img style=' float: right; padding: 4px; margin: 0 0 2px 7px;'  src="http://cdn2.brettterpstra.com/wp-content/uploads/2011/03/magcodepic.jpg?9d7bd4" alt="Post image for Watcher Service" title="Code Watcher" width="288" height="263" class="alignright size-full wp-image-2062" />After my epic three-part post on Saturday, I spent the rest of the weekend doing more “useful” things. Now it’s Sunday night (Monday morning, I think), and I’ve got some kind of minor food poisoning which is currently keeping me awake. Thus a “hey, cool trick” post.</p>

<p>I actually already own an app which can do this to some extent, and I know there are more available. I like to do things the hard way once in a while. What I wanted was a basic script which could execute arbitrary code whenever a file of a certain type changed within a directory. The use case is web development: whenever I change a site-related file (html, php, css, less, rb, erb, etc.), I want Safari to refresh the related page.</p>

<p>Folder Actions don’t work well on my system (do they work for anyone?). Hazel would work, but I needed something more immediate. I had a version that used <code>launchd</code>, but it was difficult to consistently start and stop from a script. Here’s the final solution I came up with.</p>

<p><span id="more-2056"></span></p>

<p>At the top, let me say that the heavy lifting in my script was taken from a SASS file-watching script by <a href="https://github.com/carlo/haml-sass-file-watcher">Carlo Zottman</a>. It uses Ruby to poll a file collection for modification date variations, and keeps a pretty low profile. I wanted to avoid compiled code for this, as I eventually do want to go to bed tonight.</p>

<p>The basic goals:</p>

<ul>
<li>Watch only files of a specific type</li>
<li>Refresh my primary browser when a change occurs</li>
<li>Refresh across windows and tabs, but limit by user-specified keyword in URL</li>
</ul>

<p>I built both a command line script and a System Service to do this, and both work as standalone solutions. The Automator action makes it possible to right click a folder in Finder and choose “Watcher” to start watching it, and it asks you for the tab keyword in a nice popup dialog. Beyond that, it really just wraps the command line script. You can modify either with the following instructions.</p>

<p>Here’s the command line version:</p>


<div class="wp_syntax"><div class="code"><pre class="ruby"><span class="co1">#!/usr/bin/env ruby</span>
<span class="co1"># watch.rb by Brett Terpstra, 2011 &lt;http://brettterpstra.com&gt;</span>
<span class="co1"># with credit to Carlo Zottmann &lt;https://github.com/carlo/haml-sass-file-watcher&gt;</span>
&nbsp;
<span class="kw3">trap</span><span class="br0">&#40;</span><span class="st0">&quot;SIGINT&quot;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> <span class="kw3">exit</span> <span class="br0">&#125;</span>
&nbsp;
<span class="kw1">if</span> ARGV.<span class="me1">length</span> <span class="sy0">&lt;</span> <span class="nu0">2</span>
  <span class="kw3">puts</span> <span class="st0">&quot;Usage: #{$0} watch_folder keyword&quot;</span>
  <span class="kw3">puts</span> <span class="st0">&quot;Example: #{$0} . mywebproject&quot;</span>
  <span class="kw3">exit</span>
<span class="kw1">end</span>
&nbsp;
dev_extension = <span class="st0">'dev'</span>
filetypes = <span class="br0">&#91;</span><span class="st0">'css'</span>,<span class="st0">'html'</span>,<span class="st0">'htm'</span>,<span class="st0">'php'</span>,<span class="st0">'rb'</span>,<span class="st0">'erb'</span>,<span class="st0">'less'</span>,<span class="st0">'js'</span><span class="br0">&#93;</span>
watch_folder = ARGV<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span>
keyword = ARGV<span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span>
<span class="kw3">puts</span> <span class="st0">&quot;Watching #{watch_folder} and subfolders for changes in project files...&quot;</span>
&nbsp;
<span class="kw1">while</span> <span class="kw2">true</span> <span class="kw1">do</span>
  files = <span class="br0">&#91;</span><span class="br0">&#93;</span>
  filetypes.<span class="me1">each</span> <span class="br0">&#123;</span><span class="sy0">|</span>type<span class="sy0">|</span>
    files <span class="sy0">+</span>= <span class="kw4">Dir</span>.<span class="me1">glob</span><span class="br0">&#40;</span> <span class="kw4">File</span>.<span class="me1">join</span><span class="br0">&#40;</span> watch_folder, <span class="st0">&quot;**&quot;</span>, <span class="st0">&quot;*.#{type}&quot;</span> <span class="br0">&#41;</span> <span class="br0">&#41;</span>
  <span class="br0">&#125;</span>
  new_hash = files.<span class="me1">collect</span> <span class="br0">&#123;</span><span class="sy0">|</span>f<span class="sy0">|</span> <span class="br0">&#91;</span> f, <span class="kw4">File</span>.<span class="me1">stat</span><span class="br0">&#40;</span>f<span class="br0">&#41;</span>.<span class="me1">mtime</span>.<span class="me1">to_i</span> <span class="br0">&#93;</span> <span class="br0">&#125;</span>
  hash <span class="sy0">||</span>= new_hash
  diff_hash = new_hash <span class="sy0">-</span> hash
&nbsp;
  <span class="kw1">unless</span> diff_hash.<span class="me1">empty</span>?
    hash = new_hash
&nbsp;
    diff_hash.<span class="me1">each</span> <span class="kw1">do</span> <span class="sy0">|</span>df<span class="sy0">|</span>
      <span class="kw3">puts</span> <span class="st0">&quot;Detected change in #{df[0]}, refreshing&quot;</span>
      <span class="sy0">%</span>x<span class="br0">&#123;</span>osascript<span class="co4">&lt;&lt;ENDGAME
        	tell application &quot;Safari&quot;
          	set windowList to every window
          	repeat with aWindow in windowList
          		set tabList to every tab of aWindow
          		repeat with atab in tabList
          			if (URL of atab contains &quot;#{keyword}&quot;) then
          			  tell atab to do javascript &quot;window.location.reload()&quot;
          			end if
          		end repeat
          	end repeat
        	end tell
ENDGAME</span>
<span class="br0">&#125;</span>
    <span class="kw1">end</span>
  <span class="kw1">end</span>
&nbsp;
  <span class="kw3">sleep</span> <span class="nu0">1</span>
<span class="kw1">end</span></pre></div></div>


<h2>Installing</h2>

<p>If you want to use the above script from Terminal, just put it in a directory in your path and run <code>chmod a+x watch.rb</code> on it. Then you can call it with <code>watch.rb folder/to/watch keyword</code>. The keyword you pass will determine which tabs will refresh in your browser. For example, if I’m working on dev.heckyesmarkdown.com (my local development version), I would use “dev.heckyes” to limit the refresh to only associated tabs. Once the script is running, you can stop it any time by typing Control-C in that Terminal. If you’ve run it in the background, you’ll either need to foreground it or kill it manually.</p>

<p>To install the System Service, <a href="#download">download the workflow</a>, unzip it and place it in <code>~/Library/Services</code> (where ‘~’ is your home folder). It will now show up when you right click on one or more selected folders in the Finder. Choose “Watcher,” enter a URL-matching keyword and let it go. You should see the spinning gear icon in your menubar. When you want to stop watching the folder, click that icon and choose “Stop Watcher”.</p>

<h2>Customizing</h2>

<p>In the standalone Ruby script (above), you can easily modify the watched filetypes in line 14, and you can replace the AppleScript with your own starting on line 34. I’ll offer some examples for other browsers below.</p>

<p>The Service contains pretty much the same script, but modified to work with an Automator workflow. If you open it in Automator and skip to the last action, you’ll see the script and you can make your filetype and AppleScript modifications in there.</p>

<h3>Using other browsers</h3>

<p>Chrome has decent AppleScript support these days, so changing this script to work with it is trivial. Just replace the AppleScript portion (beginning with “tell application…”) with the following:</p>


<div class="wp_syntax"><div class="code"><pre class="applescript"><span class="kw3">tell</span> <span class="kw1">application</span> <span class="st0">&quot;Google Chrome&quot;</span>
	<span class="kw3">set</span> windowList <span class="kw3">to</span> <span class="kw2">every</span> <span class="kw1">window</span>
	<span class="kw3">repeat</span> <span class="kw3">with</span> aWindow <span class="kw3">in</span> windowList
		<span class="kw3">set</span> tabList <span class="kw3">to</span> <span class="kw2">every</span> <span class="kw1">tab</span> <span class="kw3">of</span> aWindow
		<span class="kw3">repeat</span> <span class="kw3">with</span> atab <span class="kw3">in</span> tabList
			<span class="kw3">if</span> <span class="br0">&#40;</span>URL <span class="kw3">of</span> atab <span class="kw2">contains</span> <span class="st0">&quot;#{keyword}&quot;</span><span class="br0">&#41;</span> <span class="kw3">then</span>
				<span class="kw3">tell</span> atab <span class="kw3">to</span> reload
			<span class="kw3">end</span> <span class="kw3">if</span>
		<span class="kw3">end</span> <span class="kw3">repeat</span>
	<span class="kw3">end</span> <span class="kw3">repeat</span>
<span class="kw3">end</span> <span class="kw3">tell</span></pre></div></div>


<p>Firefox is a little less elegant, as far as I know, and requires System Events scripting to refresh a page. If you know a better way, I’d love to hear it, but here’s a basic script for reloading the front page. You might as well delete the keyword portions of the script/workflow if you go this route, they won’t be applicable:</p>


<div class="wp_syntax"><div class="code"><pre class="applescript"> <span class="kw3">tell</span> app <span class="st0">&quot;Firefox&quot;</span> <span class="kw3">to</span> <span class="kw1">activate</span>
 <span class="kw3">tell</span> app <span class="st0">&quot;System Events&quot;</span>
   keystroke <span class="st0">&quot;r&quot;</span> using command down
 <span class="kw3">end</span> <span class="kw3">tell</span></pre></div></div>


<p>I won’t detail any other browsers; if you’re doing web development in something more exotic, I’ll assume you know how to script it.</p>

<h3>Executing arbitrary code</h3>

<p>You don’t have to refresh browsers with this. You don’t even have to use it for web development. Have it watch text files for changes and run an iCal script when one is modified. It’s basically a hyperactive Folder Action.</p>

<p>If you want to have the script do something new every time you use it, you might want to externalize the action code. Just modify the <code>diff_hash.each do |df|</code> block to run an outside script. If it’s shell code, just make it executable and call it with <code>%x{path/to/script}</code>, and if it’s AppleScript, call it with <code>%x{/usr/bin/osascript path/to/script}</code>. Then you can modify the action script each time without changing the watch.rb file.</p>

<p>If you want to get really crazy, you could pass the script to execute as a command line parameter, or request it in the Automator workflow and pass it in. I don’t have a need for that right now, but if you build it, let me know!</p>

<p>I hope folks find this useful. Now, excuse me while I go retch for a little bit.</p>

<h2 id="download">Download</h2>

<div class="download_desc"><p class="download-icon"><a href="http://brettterpstra.com/downloads/Watcher.zip?9d7bd4" title="Download Watcher Service (227)"><img src="http://brettterpstra.com/wp-content/images/serviceicon.jpg?9d7bd4" alt="download image for Watcher Service" width="64" /></a><br /><a href="http://brettterpstra.com/downloads/Watcher.zip?9d7bd4" title="Download Watcher Service (227)" class="download-button">Download</a></p><p class="desc"><a href="http://brettterpstra.com/downloads/Watcher.zip?9d7bd4" title="Download Watcher Service (227)">Watcher Service</a> — A Snow Leopard System Service that runs on folders in Finder. By default, it watches for changes to web dev files and refreshes Safari, but it can be customized extensively. See the “more info” link for customization instructions. <a href="http://brettterpstra.com/?p=2056">More Info</a></p></div>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/os-x-service-for-natural-language-dates/' rel='bookmark' title='OS X Service for natural language dates'>OS X Service for natural language dates</a></li>
<li><a href='http://brettterpstra.com/a-few-scripts-for-taskpaper-users/' rel='bookmark' title='A few scripts for TaskPaper users'>A few scripts for TaskPaper users</a></li>
<li><a href='http://brettterpstra.com/a-bash-function-for-markdown-bloggers/' rel='bookmark' title='A Bash function for Markdown bloggers'>A Bash function for Markdown bloggers</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/watch-for-file-changes-and-refresh-your-browser-automatically/">Watch for file changes and refresh your browser automatically</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/watch-for-file-changes-and-refresh-your-browser-automatically/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Grabbing a Mac app’s icon: Automator style</title>
		<link>http://brettterpstra.com/grabbing-a-mac-apps-icon-automator-style/</link>
		<comments>http://brettterpstra.com/grabbing-a-mac-apps-icon-automator-style/#comments</comments>
		<pubDate>Sun, 06 Mar 2011 00:40:38 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[automator]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[mac]]></category>
		<category><![CDATA[os x]]></category>
		<category><![CDATA[unix]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=2030</guid>
		<description><![CDATA[<p>We’ve covered a small truckload of Bash scripting ideas for Mac and OS X in the previous two posts. It’s time to put them to use and create an Automator app that we can use as a droplet in Finder. If you skipped straight here, it probably means you don’t really want to know about the messy details of the&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/grabbing-a-mac-apps-icon-automator-style/">Grabbing a Mac app’s icon: Automator style</a></p>]]></description>
			<content:encoded><![CDATA[<p>We’ve covered a small truckload of Bash scripting ideas for Mac and OS X in the previous <a href="http://brettterpstra.com/?p=2028">two</a> <a href="http://brettterpstra.com/?p=2029">posts</a>. It’s time to put them to use and create an Automator app that we can use as a droplet in Finder. If you skipped straight here, it probably means you don’t really want to know about the messy details of the scripts, so this post won’t go into a lot of Unix mumbo jumbo.</p>

<p>You can download ready-to-go versions of the workflows covered in this post here: <a href="/share/GrabIconWorkflows.zip?9d7bd4"><strong>GrabIconWorkflows.zip</strong></a>.</p>

<p>If you want to customize these workflows, you’ll need to know a little bit about the inner Bash workings, but nothing an up-and-coming nerd can’t figure out. To get started, open up Automator.app and create a new file with the “Application” preset.<span id="more-2030"></span><img src="http://cdn2.brettterpstra.com/wp-content/uploads/2011/03/automator_application_workflow.jpg?9d7bd4" alt="Automator Application Workflow" /></p>

<h3>A basic workflow</h3>

<p>We’ll start with a really simple Automator application that you can drop an application onto to extract its icon at 512px and save it as a JPEG to the Desktop. One step, no interaction.</p>

<p>In your new Automator workflow, find “Run Shell Script” in the Library on the left. You can use the filter at the top to quickly narrow down the choices. Drag the action into your workflow editor on the right, and paste the following code into it:</p>

<pre><code>APPDIR=$1
ICON=`defaults read "$APPDIR/Contents/Info" CFBundleIconFile|sed -e 's/\.icns$//'`
ICONFILE="$APPDIR/Contents/Resources/$ICON.icns"
APPNAME=`basename "$APPDIR" .app`
OUTFILE="$HOME/Desktop/${APPNAME}_icon.jpg"
/usr/bin/sips -s format jpeg --resampleHeightWidthMax 512 "$ICONFILE" --out "$OUTFILE"
</code></pre>

<p>Make sure the shell is set to “/bin/bash” and change “Pass input” to “as arguments”. Save the workflow. You’ll probably want to name it something creative and put it somewhere useful, such as <code>~/Applications/512Desktop.app</code>. I have the utmost faith that you’ll come up with something brilliant. Here’s what your workflow should look like:</p>

<p><img src="http://cdn2.brettterpstra.com/wp-content/uploads/2011/03/512desktopapp.jpg?9d7bd4" alt="512desktop.app" /></p>

<p>Now, you can just drop an application onto the icon for 512Desktop.app and the extracted icon will show up on your Desktop, named based on the name of the application you dropped on the workflow, with “_icon.jpg” appended to it. That was easy, right?</p>

<h3>A complete workflow with user interaction</h3>

<p>To make something more universally useful, we’ll create a workflow that asks the user where to save it. This time, we’ll output a PNG file and use the maximum available icon size. We covered how to do both of these in the last post, so I won’t go into all of the details in this one. Page back if you’re curious (or confused).</p>

<p>We’ll also need to store variables and pass multiple arguments to shell scripts in the workflow to pull this off. This is the stuff that makes Automator both awesome and frustrating, but once you get the hang of it, new possibilities open up. In this example it may seem like a lot of work just to ask a user where to save a file, but the concept allows you to get user input (without using AppleScript) in shell scripts. It’s worth knowing.</p>

<p>Create a new Automator application, just like above. Since this is going to be a droplet, the path of the Application will be passed to the first action when you run it (by dragging an app onto it). We need to store that as a variable for later, and then pass it on to the first shell script. To do so, drag the “Set Value of Variable” action from the library into your workflow. Click the dropdown for “Variable:” and choose “New variable”. Title the variable “appname”.</p>

<p>Next, drag the “Run Shell Script” action in after the variable action. Paste the following code into it:</p>

<pre><code>ICON=`defaults read "$1/Contents/Info" CFBundleIconFile|sed -e 's/\.icns$//'`
echo "$1/Contents/Resources/$ICON.icns"
</code></pre>

<p>This gets the icon file’s name and hands it back to Automator. We’ve got the application name and location, as well as the name and path of the icon file we need. We just need to know where the user wants to save it to. Add the “Ask for Finder Items” action in next, and set the prompt to “Save to:”. “Start at:” should be set to Desktop by default, but you can modify that to open the dialog to whatever directory makes sense for you. Change “Type:” to “Folders” and make sure that “Allow Multiple Section” is unchecked.</p>

<p>Now we need our <code>appname</code> variable back, so drag “Get Value of Variable” in as the next action. Set “Variable:” to “appname” (which will be available in the dropdown). The outputs of each command stack up, so now we have the icon file path, the user’s destination selection and the Application’s name (appname) all ready to pass to the final shell script as arguments ($1 $2 $3).</p>

<p>Drag in a “Run Shell Script” action as the last command. Again, set it to “/bin/bash” and pass input as arguments. Then, paste this code into it:</p>

<pre><code>APPNAME=`basename "$3" .app`
OUTFILE="$2/${APPNAME}_icon.png"
MAX=`sips -g pixelWidth "$1"|tail -1|awk '{print $2}'`
/usr/bin/sips -s format png --resampleHeightWidthMax $MAX "$1" --out "$OUTFILE"
</code></pre>

<p>Here’s what it should look like now:</p>

<p><img src="http://cdn2.brettterpstra.com/wp-content/uploads/2011/03/max_png_to_user_loc.jpg?9d7bd4" alt="Max PNG to User loc" /></p>

<p>Done. Save the workflow to a file such as <code>~/Applications/SaveMaxIcon.app</code>. Try dragging an application directly onto it. You’ll be asked to choose a folder. Click “OK” and the resulting icon image file will show up in the selected location. You can stick this app on your Desktop (or alias it there) for easy access, add it to your Finder sidebar or even drag it into the toolbar of a Finder window.</p>

<p>If you want to choose an app from a dialog instead, you can insert an “Ask for Finder Items” action at the beginning of the workflow. Set “Start at:” to “/Applications”, “Type:” to “Files” and uncheck “Allow Multiple Selections”. Running the workflow will pop up a file dialog asking you to select an application manually before requesting the destination and running the rest of the script. This might be faster than finding, dragging and dropping, in some cases.</p>

<p><em>Bonus tip:</em> you can incorporate Automator workflows into shell scripts (and AppleScripts using <code>do shell script</code>) with the <code>automator</code> command line utility. You can call it directly on a workflow (<code>automator nameof.workflow</code>) or pass input to it using the –i parameter. The most practical application I can think of in this case would be to create an AppleScript application bundle containing both <code>on run</code> and <code>on open</code> handlers and both versions of the above workflow embedded in it. Then you could create a single application that would act on dropped files <em>or</em> offer a dialog if it was run directly. There’s probably an easier way to do that, though.</p>

<p>Grab copies of the workflows to get started (<a href="/share/GrabIconWorkflows.zip?9d7bd4"><strong>GrabIconWorkflows.zip</strong></a>), and I’d love to hear if you come up with other uses for them. That’s a wrap, hope it gave you some ideas for your own nerdery.</p>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/grabbing-a-mac-apps-icon-building-blocks/' rel='bookmark' title='Grabbing a Mac app’s icon: building blocks'>Grabbing a Mac app’s icon: building blocks</a></li>
<li><a href='http://brettterpstra.com/grabbing-a-mac-apps-icon-advanced-bash-usage-2/' rel='bookmark' title='Grabbing a Mac app’s icon: advanced Bash usage'>Grabbing a Mac app’s icon: advanced Bash usage</a></li>
<li><a href='http://brettterpstra.com/dropbox-and-seamless-mutli-mac-computing/' rel='bookmark' title='Dropbox and seamless mutli-Mac computing?'>Dropbox and seamless mutli-Mac computing?</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/grabbing-a-mac-apps-icon-automator-style/">Grabbing a Mac app’s icon: Automator style</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/grabbing-a-mac-apps-icon-automator-style/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Grabbing a Mac app’s icon: advanced Bash usage</title>
		<link>http://brettterpstra.com/grabbing-a-mac-apps-icon-advanced-bash-usage-2/</link>
		<comments>http://brettterpstra.com/grabbing-a-mac-apps-icon-advanced-bash-usage-2/#comments</comments>
		<pubDate>Sun, 06 Mar 2011 00:35:29 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[mac]]></category>
		<category><![CDATA[os x]]></category>
		<category><![CDATA[unix]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=2029</guid>
		<description><![CDATA[<p>In the previous post in this series, we looked at the basic Terminal commands we’d use to grab a Mac application’s icon from the command line. In this post, we’ll flesh out the script a little and turn it into a Bash function with some added features: Logic to locate the app if it exists outside of /Applications A check&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/grabbing-a-mac-apps-icon-advanced-bash-usage-2/">Grabbing a Mac app’s icon: advanced Bash usage</a></p>]]></description>
			<content:encoded><![CDATA[<p>In the <a href="http://brettterpstra.com/?p=2028">previous post</a> in this series, we looked at the basic Terminal commands we’d use to grab a Mac application’s icon from the command line. In this post, we’ll flesh out the script a little and turn it into a Bash function with some added features:</p>

<ul>
<li>Logic to locate the app if it exists outside of /Applications</li>
<li>A check using <code>sips</code> to find the maximum available image size</li>
<li>Allow user input to set the final output size</li>
<li>Allow the user to decide whether to open the result in Preview.app</li>
</ul>

<p>We’ll also look at a very, very cool trick for adding tab-completion for application names to the <code>open -a</code> command, as well as our <code>geticon()</code> function. Yes, it’s that nerdy.</p>

<p>In the next post, we’ll use Automator to make this into a droplet we can drag apps onto from Finder. If you don’t give a flying fire truck about Terminal, you can skip straight there and create something useful without touching the command line. This one’s for the nerds (and wannabe nerds).<span id="more-2029"></span>### A for loop directory search ###</p>

<p>We’ll use a bash <code>for</code> loop to search some predefined locations. You could also use <code>mdfind</code><sup id="fnref:mdfind"><a href="#fn:mdfind" rel="footnote">1</a></sup> to let Spotlight locate the app, but we’ll keep it simple for now. I’ve set it up to look in some standard locations for the file:</p>


<div class="wp_syntax"><div class="code"><pre class="bash"><span class="re2">APPDIR</span>=<span class="st_h">''</span>
 <span class="kw1">for</span> <span class="kw2">dir</span> <span class="kw1">in</span> <span class="st0">&quot;/Applications/&quot;</span> <span class="st0">&quot;/Applications/Utilities/&quot;</span> <span class="st0">&quot;/Developer/Applications/&quot;</span> <span class="st0">&quot;/Developer/Applications/Utilties/&quot;</span>; <span class="kw1">do</span>
   <span class="kw1">if</span> <span class="br0">&#91;</span><span class="br0">&#91;</span> <span class="re5">-d</span> <span class="co1">${dir}</span><span class="re1">$APP</span>.app <span class="br0">&#93;</span><span class="br0">&#93;</span>; <span class="kw1">then</span> 
       <span class="re2">APPDIR</span>=<span class="re1">$dir</span>
       <span class="kw3">break</span>
   <span class="kw1">fi</span>
 <span class="kw1">done</span></pre></div></div>


<p>$APPDIR is now set to the location that $APP was found, or to ” (blank) if it wasn’t found in any of the specified folders. We can check for that in the next part and fail gracefully if we didn’t find the specified app.</p>

<h3>Getting the maximum image size</h3>

<p>Modern Mac icons generally have a pixel width of 512 or greater, but some legacy applications’ icons are 256px or smaller. Finding the maximum pixel width of the image allows us to avoid creating a distorted image as a result of sizing up beyond the largest image in the .icns file. The <code>sips</code> command can do this with a little help from <code>awk</code><sup id="fnref:awk"><a href="#fn:awk" rel="footnote">2</a></sup> to clean up the output. The following command gets the <code>pixelWidth</code> property from the icon file, grabs the last line of output and removes extraneous text to leave us with just a number:</p>

<pre><code>MAXAVAIL=`sips -g pixelWidth "${APPDIR}$APP.app/Contents/Resources/$ICON.icns"|tail -1|awk '{print $2}'`
</code></pre>

<p>We could use the number we found to output a file with the maximum dimensions, but since we’re making a general-purpose function, we’ll ask the user what they want.</p>

<h3>Getting user input</h3>

<p>There are a few ways to offer options in Bash. This is the simplest route I know. It doesn’t innately allow for a lot of error checking; the user could enter letters instead of numbers or an unattainable dimension, for example. We’ll add some basic checks, but we’ll assume that you can properly enter a number when asked. You’re smart like that.</p>


<div class="wp_syntax"><div class="code"><pre class="bash"><span class="kw3">echo</span> <span class="re5">-n</span> <span class="st0">&quot;Enter max pixel width (<span class="es2">$MAXAVAIL</span>): &quot;</span>
<span class="kw2">read</span> MAX
<span class="kw1">if</span> <span class="br0">&#91;</span><span class="br0">&#91;</span> <span class="re1">$MAX</span> == <span class="st_h">''</span> <span class="sy0">||</span> <span class="re1">$MAX</span> <span class="re5">-gt</span> <span class="re1">$MAXAVAIL</span> <span class="br0">&#93;</span><span class="br0">&#93;</span>; <span class="kw1">then</span>
  <span class="re2">MAX</span>=<span class="re1">$MAXAVAIL</span>
<span class="kw1">fi</span></pre></div></div>


<p>We echo the prompt (the –n keeps it from echoing a newline), and we use our previously determined $MAXAVAIL variable to set a cap. If the user’s answer is empty or larger than $MAXAVAIL, we default to $MAXAVAIL.</p>

<h3>The whole shebang</h3>

<p>Here’s a complete Bash function that you can paste at the end of your <code>~/.bash_profile</code>. It offers to open the resulting image in Preview.app, but you can easily modify that to whatever app makes sense in your workflow.</p>


<div class="wp_syntax"><div class="code"><pre class="bash"><span class="kw1">function</span> geticon<span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
  <span class="re2">APP</span>=<span class="sy0">`</span><span class="kw3">echo</span> <span class="re4">$1</span><span class="sy0">|</span><span class="kw2">sed</span> <span class="re5">-e</span> <span class="st_h">'s/\.app$//'</span><span class="sy0">`</span>
  <span class="re2">APPDIR</span>=<span class="st_h">''</span>
  <span class="kw1">for</span> <span class="kw2">dir</span> <span class="kw1">in</span> <span class="st0">&quot;/Applications/&quot;</span> <span class="st0">&quot;/Applications/Utilities/&quot;</span> <span class="st0">&quot;/Developer/Applications/&quot;</span> <span class="st0">&quot;/Developer/Applications/Utilties/&quot;</span>; <span class="kw1">do</span>
    <span class="kw1">if</span> <span class="br0">&#91;</span><span class="br0">&#91;</span> <span class="re5">-d</span> <span class="co1">${dir}</span><span class="re1">$APP</span>.app <span class="br0">&#93;</span><span class="br0">&#93;</span>; <span class="kw1">then</span> 
        <span class="re2">APPDIR</span>=<span class="re1">$dir</span>
        <span class="kw3">break</span>
    <span class="kw1">fi</span>
  <span class="kw1">done</span>
  <span class="kw1">if</span> <span class="br0">&#91;</span><span class="br0">&#91;</span> <span class="re1">$APPDIR</span> == <span class="st_h">''</span> <span class="br0">&#93;</span><span class="br0">&#93;</span>; <span class="kw1">then</span>
    <span class="kw3">echo</span> <span class="st0">&quot;App not found&quot;</span>
  <span class="kw1">else</span>
    <span class="re2">ICON</span>=<span class="sy0">`</span>defaults <span class="kw2">read</span> <span class="st0">&quot;<span class="es3">${APPDIR}</span><span class="es2">$APP</span>.app/Contents/Info&quot;</span> CFBundleIconFile<span class="sy0">|</span><span class="kw2">sed</span> <span class="re5">-e</span> <span class="st_h">'s/\.icns$//'</span><span class="sy0">`</span>
    <span class="re2">OUTFILE</span>=<span class="st0">&quot;<span class="es2">$HOME</span>/Desktop/<span class="es3">${APP}</span>_icon.jpg&quot;</span>
    <span class="re2">MAXAVAIL</span>=<span class="sy0">`</span>sips <span class="re5">-g</span> pixelWidth <span class="st0">&quot;<span class="es3">${APPDIR}</span><span class="es2">$APP</span>.app/Contents/Resources/<span class="es2">$ICON</span>.icns&quot;</span><span class="sy0">|</span><span class="kw2">tail</span> <span class="re5">-1</span><span class="sy0">|</span><span class="kw2">awk</span> <span class="st_h">'{print $2}'</span><span class="sy0">`</span>
    <span class="kw3">echo</span> <span class="re5">-n</span> <span class="st0">&quot;Enter max pixel width (<span class="es2">$MAXAVAIL</span>): &quot;</span>
  	<span class="kw2">read</span> MAX
  	<span class="kw1">if</span> <span class="br0">&#91;</span><span class="br0">&#91;</span> <span class="re1">$MAX</span> == <span class="st_h">''</span> <span class="sy0">||</span> <span class="re1">$MAX</span> <span class="re5">-gt</span> <span class="re1">$MAXAVAIL</span> <span class="br0">&#93;</span><span class="br0">&#93;</span>; <span class="kw1">then</span>
  	  <span class="re2">MAX</span>=<span class="re1">$MAXAVAIL</span>
  	<span class="kw1">fi</span>
    <span class="sy0">/</span>usr<span class="sy0">/</span>bin<span class="sy0">/</span>sips <span class="re5">-s</span> format jpeg <span class="re5">--resampleHeightWidthMax</span> <span class="re1">$MAX</span> <span class="st0">&quot;<span class="es3">${APPDIR}</span><span class="es2">$APP</span>.app/Contents/Resources/<span class="es2">$ICON</span>.icns&quot;</span> <span class="re5">--out</span> <span class="st0">&quot;<span class="es2">$OUTFILE</span>&quot;</span> <span class="sy0">&gt;</span> <span class="sy0">/</span>dev<span class="sy0">/</span>null <span class="nu0">2</span><span class="sy0">&gt;&amp;</span><span class="nu0">1</span>
    <span class="kw3">echo</span> <span class="st0">&quot;Wrote JPEG to <span class="es2">$OUTFILE</span>.&quot;</span>
  	<span class="kw3">echo</span> <span class="re5">-n</span> <span class="st_h">'Open in Preview? (y/N): '</span>
  	<span class="kw2">read</span> ANSWER
  	<span class="kw1">if</span> <span class="br0">&#91;</span><span class="br0">&#91;</span> <span class="re1">$ANSWER</span> == <span class="st_h">'y'</span> <span class="br0">&#93;</span><span class="br0">&#93;</span>; <span class="kw1">then</span>
  	  open <span class="re5">-a</span> <span class="st0">&quot;Preview.app&quot;</span> <span class="st0">&quot;<span class="es2">$OUTFILE</span>&quot;</span>
  	<span class="kw1">fi</span>
  <span class="kw1">fi</span>
<span class="br0">&#125;</span></pre></div></div>


<h3>Extra credit: tab completion for app names</h3>

<p>Before we get into making this into a GUI using Automator, wouldn’t it be cool if you could autocomplete an app name from the command line when you use this? This is where you find out that I can nerd anything to death, but let’s do it.</p>

<p>First, we need some functions to build a list of all of our available applications and provide them to Bash’s completion command. I’m modifying a script from <a href="http://www.holburn.net/">Kim Holburn</a> for this, updated to work on 10.6 and provide case-insensitive completion. Just put the file in your user’s home directory for now. When we’re done, it will also provide application name tab completion for <code>open -a</code> (and <code>o</code> if you alias o=“open –a” in your .bash_profile). Click the link below to open the script, then save it to your home folder as “.app_completions”.</p>

<p><a href="/share/app_completions"><strong>Download .app_completions</strong></a></p>

<p>Once you have that file located in <code>~/.app_completions</code>, we just need to reference it in <code>.bash_profile</code> and add a few settings. Put this near the top of <code>~/.bash_profile</code>:</p>

<pre><code>source ~/.app_completions
bind "set completion-ignore-case on"
set show-all-if-ambiguous on
alias o="open -a"
</code></pre>

<p>At the command line, run <code>source ~/.bash_profile</code>. Now, assuming you’ve also added the previous <code>geticon</code> function to <code>.bash_profile</code>, you should be able to type <code>geticon term</code> and hit tab right after the ‘m’ to have it complete to “Terminal.app” automatically. This makes it a lot easier to get the app’s name, spacing and capitalization exactly right. You’ve also aliased “o” to launch an app in the process. Type <code>o auto</code> to complete to “Automator.app” and launch it.</p>

<p>We’ll <a href="http://brettterpstra.com/grabbing-a-mac-apps-icon-automator-style/">dig into Automator next</a> and turn this whole thing into something pretty.</p>

<div class="footnotes">
<hr />
<ol>

<li id="fn:mdfind">
<p><a href="http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man1/mdfind.1.html">mdfind man page</a> <a href="#fnref:mdfind" rev="footnote">↩</a></p>
</li>

<li id="fn:awk">
<p><a href="http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man1/awk.1.html">awk man page</a> <a href="#fnref:awk" rev="footnote">↩</a></p>
</li>

</ol>
</div>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/grabbing-a-mac-apps-icon-automator-style/' rel='bookmark' title='Grabbing a Mac app’s icon: Automator style'>Grabbing a Mac app’s icon: Automator style</a></li>
<li><a href='http://brettterpstra.com/grabbing-a-mac-apps-icon-building-blocks/' rel='bookmark' title='Grabbing a Mac app’s icon: building blocks'>Grabbing a Mac app’s icon: building blocks</a></li>
<li><a href='http://brettterpstra.com/mac-app-giveaway-textexpander/' rel='bookmark' title='Mac App Giveaway: TextExpander'>Mac App Giveaway: TextExpander</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/grabbing-a-mac-apps-icon-advanced-bash-usage-2/">Grabbing a Mac app’s icon: advanced Bash usage</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/grabbing-a-mac-apps-icon-advanced-bash-usage-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Grabbing a Mac app’s icon: building blocks</title>
		<link>http://brettterpstra.com/grabbing-a-mac-apps-icon-building-blocks/</link>
		<comments>http://brettterpstra.com/grabbing-a-mac-apps-icon-building-blocks/#comments</comments>
		<pubDate>Sun, 06 Mar 2011 00:30:20 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[mac]]></category>
		<category><![CDATA[os x]]></category>
		<category><![CDATA[unix]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=2028</guid>
		<description><![CDATA[<p>I spent a few hours last night nerding out over an easy way to grab a Mac application’s icon. I sent the basic Bash script and an example Automator action off to the other writers at TUAW. Then there was dinner, a movie, drinks and dessert. I found myself back at it when I got home. I am Jack’s complete&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/grabbing-a-mac-apps-icon-building-blocks/">Grabbing a Mac app’s icon: building blocks</a></p>]]></description>
			<content:encoded><![CDATA[<p>I spent a few hours last night nerding out over an easy way to grab a Mac application’s icon. I sent the basic Bash script and an example Automator action off to the other writers at TUAW. Then there was dinner, a movie, drinks and dessert. I found myself back at it when I got home. I am Jack’s complete inability to leave well enough alone<sup id="fnref:fightclub"><a href="#fn:fightclub" rel="footnote">1</a></sup>.</p>

<p>My solution ended up having some tricks in it that I thought were worth sharing, so I’m going to write it up here. This is the first post in a three-part series; an epic tribute to obsessive nerdery. It will probably eventually be summarized into the <a href="http://brettterpstra.com/howtos/">how-to section</a>, but this is the ever-so-informative longhand version.</p>

<p><span id="more-2028"></span></p>

<h2>Introduction/excusatory verbage</h2>

<p>I use application icons quite a bit in my <a href="http://www.tuaw.com/bloggers/brett-terpstra/">writing for TUAW</a>, so having a quick way to do this is worthwhile. You may never need an application icon as a ready-to-post JPEG, but it’s worthwhile to know tricks such as how to grab, resize and convert images from the command line or how to build an Automator action which combines Finder dialogs, shell scripts and Automator variables.</p>

<p>I’ll start with the basic Terminal commands to lay a foundation.</p>

<h3>Finding an app’s icon with the defaults command</h3>

<p>On a Mac, every application is actually a folder, referred to as a “bundle”. Within that folder are libraries, executables and resources (like images and icons). There’s also an important file called Info.plist which stores information like the application’s name, identifiers, file types and–most relevant right now–the name of the file containing the application’s icon. The application icon is an an <code>.icns</code> file, stored in <code>AppName.app/Contents/Resources</code>. Its title does not have to be the same as the app’s, so–before we can extract and convert it–we need to get its actual filename from Info.plist.</p>

<p>Info.plist is stored as an XML file (as opposed to a binary plist). As such, we could grep for the filename and run a bunch of Unix commands to parse out the filename, but there’s an easier and more reliable way to do it using the <code>defaults</code><sup id="fnref:defaults"><a href="#fn:defaults" rel="footnote">2</a></sup> command built into OS X. <code>defaults</code> reads plist files and can output the value of specific keys. The key we want is <code>CFBundleIconFile</code>, which holds the icon’s filename. We’ll assume that we’ve already retrieved the name of the app and stored it in a variable called <code>$APP</code>. Here’s what you would run on the command line:</p>

<pre><code>defaults read "/Applications/$APP.app/Contents/Info" CFBundleIconFile
</code></pre>

<p>The response may or may not have a file extension (.icns). We want to trim the extension if it exists so that we have a clean foundation to build on. You can do this with several different Unix utilities, including basic bash substitution; we’ll stick with <code>sed</code><sup id="fnref:sed"><a href="#fn:sed" rel="footnote">3</a></sup> for now. We pipe (<code>|</code>) the output of the <code>defaults</code> command into <code>sed</code>, and remove “.icns” if it exists:</p>

<pre><code>defaults read "/Applications/$APP.app/Contents/Info" CFBundleIconFile|sed -e 's/\.icns$//'
</code></pre>

<h3>Converting the icon file to an image format</h3>

<p>The .icns format holds multiple versions of the icon, and doesn’t do us much good if we want an image file for the web. Fortunately, the built-in command <code>sips</code> can do some neat tricks with it, including converting it to various formats<sup id="fnref:macworld1"><a href="#fn:macworld1" rel="footnote">4</a></sup>. We know what the icon file is titled now, and we know where to look for it. We just build a <code>sips</code> command to read it, convert it and output the result.</p>

<p>The <code>-s format</code> parameter sets an output format in sips. This can be one of many formats, but we’ll use the JPEG format for this example. This will compress the result to a web jpeg with a solid, white background. Using the PNG format instead would preserve the transparency, so if you need to do other image manipulation, you can substitute “png” for “jpeg”.</p>

<p>Let’s use the $APP variable we’ve created to set a default output filename on the Desktop. For now, we’re assuming the app exists in the standard /Applications folder (I’ll show a more complex workflow for locating the app in a bit). We’re also assuming that we’ve stored the result of the above <code>defaults</code> command in <code>$ICON</code>.</p>

<pre><code>OUTFILE="$HOME/Desktop/${APP}_icon.jpg"
sips -s format jpeg "/Applications/$APP.app/Contents/Resources/$ICON.icns" --out $OUTFILE
</code></pre>

<p>Now we have the basic pieces we can use to build a script to automate this from the command line. Here’s an example which expects the application name as the only argument, properly cased:</p>


<div class="wp_syntax"><div class="code"><pre class="bash"><span class="co0">#!/bin/bash</span>
&nbsp;
<span class="re2">APP</span>=<span class="sy0">`</span><span class="kw3">echo</span> <span class="re4">$1</span><span class="sy0">|</span><span class="kw2">sed</span> <span class="re5">-e</span> <span class="st_h">'s/\.app$//'</span><span class="sy0">`</span>
<span class="co0"># Removes &quot;.app&quot; if it was passed</span>
&nbsp;
<span class="re2">ICON</span>=<span class="sy0">`</span>defaults <span class="kw2">read</span> <span class="sy0">/</span>Applications<span class="sy0">/</span><span class="re1">$APP</span>.app<span class="sy0">/</span>Contents<span class="sy0">/</span>Info CFBundleIconFile<span class="sy0">|</span><span class="kw2">sed</span> <span class="re5">-e</span> <span class="st_h">'s/\.icns$//'</span><span class="sy0">`</span>
<span class="co0"># Removes &quot;.icns&quot; if the Info.plist value includes it</span>
<span class="co0"># Harcoded for /Applications, needs to be rewritten for other locations or droplets</span>
&nbsp;
<span class="co0">## Save icon on Desktop in JPEG format, resized to 256 max width/height</span>
<span class="re2">OUTFILE</span>=<span class="st0">&quot;<span class="es2">$HOME</span>/Desktop/<span class="es3">${APP}</span>_icon.jpg&quot;</span>
<span class="sy0">/</span>usr<span class="sy0">/</span>bin<span class="sy0">/</span>sips <span class="re5">-s</span> format jpeg <span class="st0">&quot;/Applications/<span class="es2">$APP</span>.app/Contents/Resources/<span class="es2">$ICON</span>.icns&quot;</span> <span class="re5">--out</span> <span class="re1">$OUTFILE</span></pre></div></div>


<h2>Extra credit: image optimization</h2>

<p>Since we’re automating, we can add a final step to the script to handle whatever steps we would normally take next. For example, we could open it in <a href="http://skitch.com/">Skitch</a> for quick resizing, or in <a href="http://flyingmeat.com/acorn/">Acorn</a> for more complex manipulation. If we saved a JPEG to a size we already know is correct for our purposes, we could open it straight in an optimizer such as <a href="http://imageoptim.pornel.net/">ImageOptim</a> to quickly improve the compression. We could also make use of a command line utility like ImageMagick, jpegtran or pngcrush, all of which require some installation and extra work. If you have them, great, if not, that’s an article for another time.</p>

<p><em>For the record, Skitch saves horribly large JPEGs, whereas Acorn saves amazingly well-compressed images. If you resize with Skitch, you’ll want to compress it with something else as well.</em></p>

<p>Assuming we’re using an application and not a CLI utility, we can use the <code>open</code> command to send the resulting file to the app. We can also avoid some confusion by initially saving the converted icon file to a default temporary directory and then opening it from there. Here’s a revised script that does that, then opens the final image in Acorn for quick resizing and saving to any location. We’ll use the PNG format to preserve the original transparency, and you can substitute any graphics application for Acorn:</p>


<div class="wp_syntax"><div class="code"><pre class="bash"><span class="co0">#!/bin/bash</span>
&nbsp;
<span class="re2">APP</span>=<span class="sy0">`</span><span class="kw3">echo</span> <span class="re4">$1</span><span class="sy0">|</span><span class="kw2">sed</span> <span class="re5">-e</span> <span class="st_h">'s/\.app$//'</span><span class="sy0">`</span>
<span class="co0"># Removes &quot;.app&quot; if it was passed</span>
&nbsp;
<span class="re2">ICON</span>=<span class="sy0">`</span>defaults <span class="kw2">read</span> <span class="sy0">/</span>Applications<span class="sy0">/</span><span class="re1">$APP</span>.app<span class="sy0">/</span>Contents<span class="sy0">/</span>Info CFBundleIconFile<span class="sy0">|</span><span class="kw2">sed</span> <span class="re5">-e</span> <span class="st_h">'s/\.icns$//'</span><span class="sy0">`</span>
<span class="co0"># Removes &quot;.icns&quot; if the Info.plist has it</span>
<span class="co0"># Harcoded for /Applications, needs to be rewritten for other locations or droplets</span>
&nbsp;
<span class="co0">## Output file to a temp directory (TMPDIR) as a PNG and open in Acorn for resizing and conversion</span>
&nbsp;
<span class="re2">OUTFILE</span>=<span class="st0">&quot;<span class="es2">$TMPDIR</span><span class="es3">${APP}</span>_icon.png&quot;</span>
sips <span class="re5">-s</span> format png <span class="st0">&quot;/Applications/<span class="es2">$APP</span>.app/Contents/Resources/<span class="es2">$ICON</span>.icns&quot;</span> <span class="re5">--out</span> <span class="re1">$OUTFILE</span>
open <span class="re5">-a</span> <span class="st0">&quot;Acorn.app&quot;</span> <span class="re1">$OUTFILE</span></pre></div></div>


<p>I think you get the idea. You can take this script and easily adjust the output format and destination application. In the <a href="http://brettterpstra.com/grabbing-a-mac-apps-icon-advanced-bash-usage-2/">next post</a>, we’ll cover making this into a fully customized Bash function, with some extra goodies.</p>

<div class="footnotes">
<hr />
<ol>

<li id="fn:fightclub">
<p>Oh, you got the Fight Club reference, huh? You’re still not a snowflake. <a href="#fnref:fightclub" rev="footnote">↩</a></p>
</li>

<li id="fn:defaults">
<p><a href="http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man1/defaults.1.html">defaults man page</a> <a href="#fnref:defaults" rev="footnote">↩</a></p>
</li>

<li id="fn:sed">
<p><a href="http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man1/sed.1.html">sed man page</a> <a href="#fnref:sed" rev="footnote">↩</a></p>
</li>

<li id="fn:macworld1">
<p><a href="http://www.macworld.com/article/60156/2007/09/sipsicns.html">“Convert ICNS files with sips”</a> via Macworld. <a href="#fnref:macworld1" rev="footnote">↩</a></p>
</li>

</ol>
</div>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/grabbing-a-mac-apps-icon-automator-style/' rel='bookmark' title='Grabbing a Mac app’s icon: Automator style'>Grabbing a Mac app’s icon: Automator style</a></li>
<li><a href='http://brettterpstra.com/grabbing-a-mac-apps-icon-advanced-bash-usage-2/' rel='bookmark' title='Grabbing a Mac app’s icon: advanced Bash usage'>Grabbing a Mac app’s icon: advanced Bash usage</a></li>
<li><a href='http://brettterpstra.com/bored-with-your-macvim-icon-me-too/' rel='bookmark' title='Bored with your MacVim icon? Me too.'>Bored with your MacVim icon? Me too.</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/grabbing-a-mac-apps-icon-building-blocks/">Grabbing a Mac app’s icon: building blocks</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/grabbing-a-mac-apps-icon-building-blocks/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Markdown in ScreenSteps</title>
		<link>http://brettterpstra.com/markdown-in-screensteps/</link>
		<comments>http://brettterpstra.com/markdown-in-screensteps/#comments</comments>
		<pubDate>Sat, 19 Feb 2011 07:01:47 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[markdown]]></category>
		<category><![CDATA[os x]]></category>
		<category><![CDATA[screensteps]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=1933</guid>
		<description><![CDATA[<p>I love ScreenSteps, the app from Blue Mango which makes creating documentation for screen-based applications as fast and easy as making documentation can possibly be. It’s a little clunky sometimes, but the features outweigh the cons by far, at least for me. In recent versions they’ve added the ability to post directly to WordPress, which has been pretty cool. I&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/markdown-in-screensteps/">Markdown in ScreenSteps</a></p>]]></description>
			<content:encoded><![CDATA[<p><a href="http://cdn2.brettterpstra.com/wp-content/uploads/2011/02/ScreenStepsLessonScreenShot.jpg?9d7bd4"><img style=' float: right; padding: 4px; margin: 0 0 2px 7px;'  src="http://cdn2.brettterpstra.com/wp-content/uploads/2011/02/ScreenStepsLessonScreenShot-300x224.jpg?9d7bd4" alt="" title="ScreenStepsLessonScreenShot" width="300" height="224" class="alignright size-medium wp-image-1934" /></a>I love <a href="http://www.bluemangolearning.com/screensteps/">ScreenSteps</a>, the app from Blue Mango which makes creating documentation for screen-based applications as fast and easy as making documentation can possibly be. It’s a little clunky sometimes, but the features outweigh the cons by far, at least for me.</p>

<p>In recent versions they’ve added the ability to post directly to WordPress, which has been pretty cool. I always got a little frustrated with the amount of extra markup included in the templates, though. Today I asked them if they could add a Markdown export feature. Their response? It’s already there.</p>

<p><span id="more-1933"></span></p>

<p>It doesn’t process Markdown to HTML, but that’s great for me as I do that server-side. It uploads the images in your lesson, inserts the resulting urls in your page, and uploads the plain old Markdown straight to your blog as either a post or a page. I re-created a couple of my older how-to’s using this new template, and they came out great. Check them out in the new <a href="http://brettterpstra.com/howtos/">How-To section</a>.</p>

<p>Creating the template is quite simple, and everything you need to know is detailed in <a href="http://help.bluemangolearning.com/spaces/screensteps/manuals/customizing_templates/lessons/1332-The-Anatomy-of-the-Template-File#TheConfigurationSection">Blue Mango’s help system</a>. I’m not going to go into a lot of detail, but here’s a copy of the <a href="http://abyss.designheresy.com/ScreenSteps-BTMarkdown.zip">template I’m using</a>. Just unzip the file into <code>~/Library/Application Support/ScreenSteps/2.0/Templates/HTML/Blog</code> and edit the index.html file inside.</p>

<p>Here’s where I went a little crazy. I have a custom post type in WordPress for the How-To’s, and I have plans to expand that section over time. I can’t post directly to WordPress as a How-To, and it was a little annoying to post it as a page and copy it over just to get the images uploaded. So… here’s a service called <a href="http://abyss.designheresy.com/UploadImagesToBlog.zip">Upload Images to Blog</a>. You can export as HTML in ScreenSteps using the above template and save it to a local folder. Run the service on the html file created, and it will upload all of the images to your blog and create a new <code>filename-out.html</code> file with the new image urls. You can render that to HTML or paste the Markdown directly to your post page, if your blog supports that.</p>

<p>The service requires a configuration file to be located at <code>~/.imguploader</code>. The content of the file is just one line, like this:</p>

<pre><code>username:password@http://yourblogname.com/xmlrpc.php
</code></pre>

<p>It also requires that you’re using an image output format like the one found in the template above. It just scans the file for <code>![](filename.jpg "alt text")</code> lines for the uploadable images. I can’t guarantee the service will work for everybody. It’s a quick and dirty solution, but it works really well for me. It even has Growl notifications, if you have Growl installed.</p>

<p>That’s it. Back to work on more serious things.</p>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/fun-with-marsedit-part-i/' rel='bookmark' title='Fun with MarsEdit, part I'>Fun with MarsEdit, part I</a></li>
<li><a href='http://brettterpstra.com/minor-tablinks-update/' rel='bookmark' title='Minor TabLinks update'>Minor TabLinks update</a></li>
<li><a href='http://brettterpstra.com/autotag2-smarter-tagging-for-textmate-and-wordpress/' rel='bookmark' title='AutoTag2: smarter tagging for TextMate and WordPress'>AutoTag2: smarter tagging for TextMate and WordPress</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/markdown-in-screensteps/">Markdown in ScreenSteps</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/markdown-in-screensteps/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Notational Velocity ALT 1.0 release</title>
		<link>http://brettterpstra.com/notational-velocity-alt-1-0-release/</link>
		<comments>http://brettterpstra.com/notational-velocity-alt-1-0-release/#comments</comments>
		<pubDate>Fri, 10 Dec 2010 05:09:44 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[Featured]]></category>
		<category><![CDATA[experiments]]></category>
		<category><![CDATA[markdown]]></category>
		<category><![CDATA[multimarkdown]]></category>
		<category><![CDATA[notational velocity]]></category>
		<category><![CDATA[os x]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=1463</guid>
		<description><![CDATA[<p>After a few late nights of off-hours programming, I’m putting Notational Velocity ALT out as a version 1.0. Obviously built on the backs of others, I’m just giving it a versioning scheme of its own to make it easier to track my own updates. It does have the built-in updater pointed to my own servers now, so once you install&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/notational-velocity-alt-1-0-release/">Notational Velocity ALT 1.0 release</a></p>]]></description>
			<content:encoded><![CDATA[<p><a href="http://cdn2.brettterpstra.com/wp-content/uploads/2010/12/NVALT1.0.1screenshot.jpg?9d7bd4"><img style=' float: right; padding: 4px; margin: 0 0 2px 7px;'  src="http://cdn2.brettterpstra.com/wp-content/uploads/2010/12/NVALT1.0.1screenshot-300x138.jpg?9d7bd4" alt="" title="NVALT1.0.1screenshot" width="300" height="138" class="alignright size-medium wp-image-1449" /></a>After a few late nights of off-hours programming, I’m putting <a href="http://brettterpstra.com/code/notational-velocity-alt/">Notational Velocity ALT</a> out as a version 1.0. Obviously built on the backs of others, I’m just giving it a versioning scheme of its own to make it easier to track my own updates. It does have the built-in updater pointed to my own servers now, so once you install it, updates will be available from within the app.</p>

<p>For a rundown of new features and tips, see the project page I just posted: <a href="http://brettterpstra.com/code/notational-velocity-alt/">Notational Velocity ALT</a>.</p>


<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/notational-velocity-alt-expatiation-and-roadmap/' rel='bookmark' title='Notational Velocity ALT expatiation (and roadmap)'>Notational Velocity ALT expatiation (and roadmap)</a></li>
<li><a href='http://brettterpstra.com/notational-velocity-alt-5-3/' rel='bookmark' title='Notational Velocity ALT 5.3'>Notational Velocity ALT 5.3</a></li>
<li><a href='http://brettterpstra.com/lunch-break-bugfixes-for-notational-velocity-alt/' rel='bookmark' title='Lunch-break bugfixes for Notational Velocity ALT'>Lunch-break bugfixes for Notational Velocity ALT</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/notational-velocity-alt-1-0-release/">Notational Velocity ALT 1.0 release</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/notational-velocity-alt-1-0-release/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>Notational Velocity ALT 5.3</title>
		<link>http://brettterpstra.com/notational-velocity-alt-5-3/</link>
		<comments>http://brettterpstra.com/notational-velocity-alt-5-3/#comments</comments>
		<pubDate>Wed, 08 Dec 2010 01:27:50 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[experiments]]></category>
		<category><![CDATA[mac]]></category>
		<category><![CDATA[markdown]]></category>
		<category><![CDATA[multimarkdown]]></category>
		<category><![CDATA[notational velocity]]></category>
		<category><![CDATA[notes]]></category>
		<category><![CDATA[os x]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=1443</guid>
		<description><![CDATA[<p>As usual with projects I didn’t plan to take as far as I did, my version numbering scheme is, well, stupid. With the next release I’m going to implement my own feed for automatic updates, so I’ll probably reset the versioning to something more incremental and sane. Anyway… This version cleans up the preview panel a little, replacing the Aqua&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/notational-velocity-alt-5-3/">Notational Velocity ALT 5.3</a></p>]]></description>
			<content:encoded><![CDATA[<p>As usual with projects I didn’t plan to take as far as I did, my version numbering scheme is, well, stupid. With the next release I’m going to implement my own feed for automatic updates, so I’ll probably reset the versioning to something more incremental and sane. Anyway…</p>

<p><img style=' float: right; padding: 4px; margin: 0 0 2px 7px;'  src="http://cdn2.brettterpstra.com/wp-content/uploads/2010/12/ViewSourceButtonNVALT-1.jpg?9d7bd4" alt="ViewSourceButtonNVALT-1.jpg" border="0" width="187" height="93" class="alignright" />This version cleans up the preview panel a little, replacing the Aqua tabs with a nice HUD-style toggle button for Preview/Source. Other visual changes are quite minor, mostly related to spacing.</p>

<p>An interesting change is that NV ALT now reads its HTML and CSS for the preview from your user’s <code>Library/Application Support/Notational Velocity</code> folder, which it will create on launch if it doesn’t exist. There you’ll find template.html and custom.css. You can edit both to your heart’s content; if you mess up, just delete or rename them and they’ll be recreated.</p>

<p>I also fixed the Save HTML (which I’m pretty sure <em>I</em> broke) so that it saves full, valid XHTML documents. The Preview shows fragments, Save makes documents.</p>

<p>I’m experimenting with adding some JavaScript to the preview template to make sure that external links open in a browser, but internal (page anchor) links get some nice smooth scrolling action. Just for fun. The CSS has the basic definitions for you to play with, and you can see your changes by switching notes, which will regenerate the preview.</p>

<p>I managed to make all of the scrollbars look great, except for the Preview window. Web views are way too much trouble to re-skin, I found, so I scrapped all of those changes and went back to default toolbars. Elastic Threads has a <a href="http://elasticthreads.tumblr.com/nv">fork of Notational Velocity</a> that I really like which has great toolbars, but I’m betting he can’t get a web view to look good either! Too bad his source isn’t available… there’s a lot there to love.</p>

<p>Have fun with the customizations, if you try that out!</p>

<div class="download_desc"><p class="download-icon"><a href="http://brettterpstra.com/downloads/nvalt2.1.zip?9d7bd4" title="Download nvALT (53968)"><img src="http://cdn2.brettterpstra.com/wp-content/uploads/downloads/thumbnails/2010/12/nvalticonnew.png?9d7bd4" alt="download image for nvALT" width="64" /></a><br /><a href="http://brettterpstra.com/downloads/nvalt2.1.zip?9d7bd4" title="Download nvALT (53968)" class="download-button">Download</a></p><p class="desc"><a href="http://brettterpstra.com/downloads/nvalt2.1.zip?9d7bd4" title="Download nvALT (53968)">nvALT</a> — Notational Velocity fork with full-screen, collapsible notes panel, (Multi)Markdown/Textile rendering and preview, as well as rendered source preview and export. This is originally the work of Zachary Schneirov, forked by Brett Terpstra and ElasticThreads. <a href="http://brettterpstra.com/project/nvalt">More Info</a></p></div>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/notational-velocity-and-multimarkdown/' rel='bookmark' title='Notational Velocity and MultiMarkdown'>Notational Velocity and MultiMarkdown</a></li>
<li><a href='http://brettterpstra.com/lunch-break-bugfixes-for-notational-velocity-alt/' rel='bookmark' title='Lunch-break bugfixes for Notational Velocity ALT'>Lunch-break bugfixes for Notational Velocity ALT</a></li>
<li><a href='http://brettterpstra.com/notational-velocity-alternative-multimarkdown-version/' rel='bookmark' title='Notational Velocity alternative MultiMarkdown version'>Notational Velocity alternative MultiMarkdown version</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/notational-velocity-alt-5-3/">Notational Velocity ALT 5.3</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/notational-velocity-alt-5-3/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Clear sticky Growl notifications with a keyboard shortcut</title>
		<link>http://brettterpstra.com/clear-sticky-growl-notifications-with-a-keyboard-shortcut/</link>
		<comments>http://brettterpstra.com/clear-sticky-growl-notifications-with-a-keyboard-shortcut/#comments</comments>
		<pubDate>Sat, 25 Sep 2010 21:05:35 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[applescript]]></category>
		<category><![CDATA[growl]]></category>
		<category><![CDATA[keyboard]]></category>
		<category><![CDATA[os x]]></category>
		<category><![CDATA[scripting]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=1022</guid>
		<description><![CDATA[<p>I ran into a question yesterday that didn’t seem to have an obvious answer: how can I clear a stack of sticky Growl notifications using only the keyboard. It’s not been any secret that I’m a fan of keyboard shortcuts, as indicated by the extensive keyboard support I’ve added to Instapaper Beyond. So I decided to make an interim solution&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/clear-sticky-growl-notifications-with-a-keyboard-shortcut/">Clear sticky Growl notifications with a keyboard shortcut</a></p>]]></description>
			<content:encoded><![CDATA[<p><img style=' float: right; padding: 4px; margin: 0 0 2px 7px;'  src="http://cdn2.brettterpstra.com/wp-content/uploads/2010/09/GrowlHelperApp.jpg?9d7bd4" alt="GrowlHelperApp.jpg" border="0" width="312" height="88" class="alignright" />I ran into a question yesterday that didn’t seem to have an obvious answer: how can I clear a stack of sticky <a href="http://growl.info/">Growl</a> notifications using only the keyboard. It’s not been any secret that I’m a fan of keyboard shortcuts, as indicated by the extensive keyboard support I’ve added to <a href="http://brettterpstra.com/instapaperbeyond/">Instapaper Beyond</a>. So I decided to make an interim solution until a specific feature for this is added, either to the Growl preferences or via AppleScript support.</p>

<p>For the record, I’m fully aware of the Option-Click shortcut to make all current notifications disappear, but thanks to all of the Twitter responses to that effect nonetheless. I really wanted a pure keyboard solution to handle this (I’m <a href="http://code.google.com/p/growl/issues/detail?id=11">not alone</a>). Usually, at least with apps that adhere to the Growl suggestion regarding stickiness, you only get sticky notifications when something goes wrong, and then you usually get more than one. I just wanted to be able to “poof” them away quickly and tend to whatever was causing them. I’m weird, I know.</p>

<p>After a lot of toying around, I decided to just go with a brute force method: quit the helper application and start it back up. It does the trick, and if you use AppleScript to handle the process, you can keep the bridge alive and experience no interruption in your Growl service. If you call the <a href="http://brettterpstra.com/tag/applescript/">AppleScript</a> with <a href="http://www.red-sweater.com/fastscripts/">FastScripts</a>, a launcher like <a href="http://www.shadowlab.org/Software/spark.php">Spark</a> or even <a href="http://code.google.com/p/blacktree-alchemy/">QuickSilver</a> or <a href="http://www.obdev.at/products/launchbar/index.html">LaunchBar</a>, you can cause all sticky notifications to disappear with a keystroke of your choice.</p>

<p>Here’s the script:</p>

<div markdown=0>
<pre><code>
tell application "GrowlHelperApp" to quit
delay 1
tell application "GrowlHelperApp" to activate
</code></pre>
</div>

<p>I found the 1 second delay necessary to let the process close out before trying to launch it again. If you run into issues with Growl not responding after running this script, you might try increasing that delay by a second or two. I also played around with using kill (grep/awk for the PID, kill the process, etc.), but the whole system stays much more stable this way.</p>

<p>There, my contribution to the keyboard-obsessed minority of OS X users.</p>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/quick-tip-quickly-clear-stuck-growl-notifications/' rel='bookmark' title='Quick Tip: Quickly clear stuck Growl notifications'>Quick Tip: Quickly clear stuck Growl notifications</a></li>
<li><a href='http://brettterpstra.com/quick-tip-applescript-application-toggle/' rel='bookmark' title='Quick tip: AppleScript application toggle'>Quick tip: AppleScript application toggle</a></li>
<li><a href='http://brettterpstra.com/answer-skype-with-a-hotkey/' rel='bookmark' title='Answer Skype with a hotkey'>Answer Skype with a hotkey</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/clear-sticky-growl-notifications-with-a-keyboard-shortcut/">Clear sticky Growl notifications with a keyboard shortcut</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/clear-sticky-growl-notifications-with-a-keyboard-shortcut/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Fun with MarsEdit, part I</title>
		<link>http://brettterpstra.com/fun-with-marsedit-part-i/</link>
		<comments>http://brettterpstra.com/fun-with-marsedit-part-i/#comments</comments>
		<pubDate>Sun, 06 Jun 2010 21:02:26 +0000</pubDate>
		<dc:creator>Brett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[applescript]]></category>
		<category><![CDATA[blogging]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[marsedit]]></category>
		<category><![CDATA[os x]]></category>
		<category><![CDATA[software]]></category>

		<guid isPermaLink="false">http://brettterpstra.com/?p=533</guid>
		<description><![CDATA[<p>I’ll be the first to admit that I get a little obsessed with projects that aren’t really going to improve my life all that much. Those projects can be fun to blog about, though, so I present you my brief obsession for this Sunday afternoon. You may have noticed on this blog that some posts have header images, and some&#8230;</p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/fun-with-marsedit-part-i/">Fun with MarsEdit, part I</a></p>]]></description>
			<content:encoded><![CDATA[<p><img style=' display:none'  src="http://cdn2.brettterpstra.com/wp-content/uploads/2010/06/funwithmarseditheader.jpg?9d7bd4" alt="funwithmarseditheader.jpg" border="0" width="650" height="187" class="headerimg" /></p>

<p>I’ll be the first to admit that I get a little obsessed with projects that aren’t really going to improve my life all that much. Those projects can be fun to blog about, though, so I present you my brief obsession for this Sunday afternoon.</p>

<p><img style=' float: right; padding: 4px; margin: 0 0 2px 7px;'  src="http://cdn2.brettterpstra.com/wp-content/uploads/2010/06/funwithmarseditbeforeafter.jpg?9d7bd4" alt="Template header before and after" border="0" width="298" height="202" class="alignright" />You may have noticed on this blog that some posts have header images, and some don’t, and that they get styled differently based on whether there’s an image or not. It pulls the header images from one of two places: the post thumbnail (which I can’t edit from MarsEdit) or the first image in the post content with a class of “headerimg”. As long as I’m blogging in <a href="http://​www​.red​-sweater​.com/​m​a​r​s​e​d​it/">MarsEdit</a> (or TextMate for that matter), I’m stuck with the latter option. Because the whole header image deal is handled through PHP in my functions.php file, I can’t really preview how it’s going to look; rather, I get a header image stuck somewhere before, in or after my content. In TextMate, I have a little more “scriptability” at my disposal, and the previews I’ve created there are quite accurate. As far as I can tell, I can’t pre-process content before the template is generated, so I had to try something else…
<span id="more-533"></span>My solution, for now, is jQuery in the preview template. I load the jQuery library up top (inside the header) using Google’s Ajax API:</p>

<div markdown=0>
<pre><code>
&lt;script src=&quot;http://www.google.com/jsapi&quot;&gt;&lt;/script&gt;
&lt;script&gt;google.load(&quot;jquery&quot;, &quot;1.4&quot;);&lt;/script&gt;
</code></pre>
</div>

<p>Then, at the bottom, I just start polling for an image with the right class to show up. I know it’s brute force, but this isn’t exactly public-facing, and it doesn’t seem to cause any hiccups in my writing. I have this right before my closing body tag:</p>

<div markdown=0>
<pre><code>
&lt;script&gt;
function fixHeader() {
    if ($(&#x27;img.headerimg&#x27;).attr(&#x27;src&#x27;) != undefined) {
        headerImg = $(&#x27;img.headerimg&#x27;).removeClass(&#x27;headerimg&#x27;).remove();
        postThumb = $(&#x27;.postthumb:first&#x27;);
        headerImg.prependTo(postThumb);
        postThumb.addClass(&#x27;hasimage&#x27;).removeClass(&#x27;noimage&#x27;);
    }
}
setInterval(fixHeader,2000);
&lt;/script&gt;
</code></pre>
</div>

<p>I’m sharing this just to toss the idea out there, not because I think there’s anyone else with the exact same template setup as mine.</p>

<p>In addition to having Javascript at my disposal, MarsEdit is also AppleScript-able. I haven’t looked very far into that yet, but I did whip up a quick script for adding the “headerimg” class to my images (because I always forget which class I assigned for this):</p>

<div markdown=0>
<pre><code>
tell application &quot;MarsEdit&quot;
    set selectionContents to selected text of document 1
    set {astid, AppleScript&#x27;s text item delimiters} to {AppleScript&#x27;s text item delimiters, &quot;/&gt;&quot;}
    if selectionContents is not &quot;&quot; then
        set textToInsert to (text item 1 of selectionContents) &amp; &quot;class=\&quot;headerimg\&quot; /&gt;&quot;
    end if
    set AppleScript&#x27;s text item delimiters to astid
    set selected text of document 1 to textToInsert
end tell
</code></pre>
</div>

<p>That lets me just select an image (or pending upload tag) and run the script to add the necessary class.</p>
<p>Related posts:<ol>
<li><a href='http://brettterpstra.com/multimarkdown-in-marsedit/' rel='bookmark' title='MultiMarkdown in MarsEdit'>MultiMarkdown in MarsEdit</a></li>
<li><a href='http://brettterpstra.com/instapaper-beyond-for-safari/' rel='bookmark' title='Instapaper Beyond for Safari'>Instapaper Beyond for Safari</a></li>
<li><a href='http://brettterpstra.com/marked-scripts-nvalt-evernote-marsedit-scrivener/' rel='bookmark' title='Marked scripts: nvALT, Evernote, MarsEdit, Scrivener'>Marked scripts: nvALT, Evernote, MarsEdit, Scrivener</a></li>
</ol></p><p>Originally posted on <a href="http://brettterpstra.com" title="BrettTerpstra.com">BrettTerpstra.com</a> at <a href="http://brettterpstra.com/fun-with-marsedit-part-i/">Fun with MarsEdit, part I</a></p>]]></content:encoded>
			<wfw:commentRss>http://brettterpstra.com/fun-with-marsedit-part-i/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Minified using disk: basic (Feed is rejected)
Page Caching using disk: enhanced
Database Caching 35/185 queries in 0.304 seconds using xcache
Object Caching 4443/4632 objects using xcache
Content Delivery Network via cdn2.brettterpstra.com

Served from: brettterpstra.com @ 2012-05-23 03:50:07 -->
