-
Aug 24, 2018
Getting URL and Tab Title from Firefox with AppleScript
A long time ago, a coworker shared an AppleScript function that gets the title and URL from the current tab in Chrome and formats the result as an Emacs org mode link.
Org mode links have the format
[[url][title]]
, e.g.:[[https://matthewbilyeu.com/blog/][· Matt's programming blog]]
Invoked via Alfred, this script was helpful for getting links from Github pull requests, Trello cards, etc. into my org TODO list. When I began using Firefox Quantum, however, the Chrome-specific script was of no use.
Firefox does not have AppleScript support, which means that any automated interaction with the application must be clumsily achieved through simulating GUI events. I found out that such GUI-based automation is prone to failure. It took some trial and error and cobbling various online snippets together, but so far this script has been fairly robust:
use scripting additions use framework "Foundation" tell application "Firefox" to activate -- get the tab title from FF tell application "System Events" to tell process "firefox" set frontmost to true set the_title to name of windows's item 1 set the_title to (do shell script "echo " & quoted form of the_title & " | tr '[' ' '") set the_title to (do shell script "echo " & quoted form of the_title & " | tr ']' ' '") end tell set thePasteboard to current application's NSPasteboard's generalPasteboard() set theCount to thePasteboard's changeCount() -- send cmd+l and cmd+c keystrokes to FF to highlight and copy the URL tell application "System Events" keystroke "l" using {command down} delay 0.2 keystroke "c" using {command down} end tell -- wait for the clipboard content change to have been detected repeat 20 times if thePasteboard's changeCount() is not theCount then exit repeat delay 0.1 end repeat -- get the clipboard contents set the_url to the clipboard return "[[" & the_url & "][" & the_title & "]]" as text
References:
- https://apple.stackexchange.com/questions/271161/how-to-get-the-selected-text-into-an-applescript-without-copying-the-text-to-th
- https://www.snip2code.com/Snippet/69440/AppleScript-to-get-url-from-Safari--Fire
- https://www.alfredforum.com/topic/2013-how-to-get-frontmost-tab%E2%80%99s-url-and-title-of-various-browsers/
-
Aug 19, 2018
Tech debt
For a long time I’d considered technical debt to be just a bad thing. That it was a mess left behind by imprudent coders of the past. I hadn’t paused to reflect on the idea of it.
Only recently did I come to appreciate that tech debt can be a tool, just like monetary debt. Monetary debt is a (hopefully) temporary suboptimal state that’s intentionally entered with an eye toward a long-term goal. E.g., you take on debt to pay for a university degree that you think will result in you having a higher salary in the future. The debt is useful in this way. Tech debt, too, can be useful.
This became clear to me while working on a prototype to test market fit for a potential new feature at work. By making tradeoffs that sacrificed design robustness for speed of implementation, we intentionally introduced tech debt into the system. Designing and implementing a polished, scalable system before knowing whether the offering was valuable to our customers could have been an expensive mistake. Tech debt is allowing us to explore an idea and quickly react to evolving requirements.
Of course, just as with monetary debt, too much tech debt can be disastrous. Tech debt is also harder than monetary debt to measure. Still, it’s a useful tool in some scenarios, and I’m glad I have a more nuanced understanding of it.
-
Jun 20, 2018
Command Lining
I’ve slowly accumulated knowledge of a few tricks that help me to be productive at the terminal prompt. These are things that I somehow missed during years of computer science schooling and my early career. I often learn things, then immediately forget the time before I learned them, and then take that new information for granted. I’m trying to reflect on such learning and distill the best parts. And so hopefully someone who happens upon this list may find a new tip or two.
TLDR
TLDR - Simplified and community-driven man pages.
This isn’t a built-in bash command, but I’ve found it to be very handy. It’s one of the first programs I install on a new system. Type
tldr <command>
to see usage examples of the most common options of command line programs.Tree
Another not-built-in program. Imagine if
ls
recursively displayed a hierarchical directory listing. I like visual information, so I liketree
.tree -L 3 . └── uploads ├── 2013 │ ├── 11 │ └── 12 ├── 2014 │ ├── 03 │ ├── 06 │ ├── 07 ├── 2015 │ └── 01 ├── 2016 │ ├── 01 │ └── 04 ├── 2017 │ └── 07 └── 2018 ├── 01 └── 02
Return to previous directory
I happened to be reading an intro to bash commands once, and I was surprised that one of the first lessons showed a use of
cd
that I somehow wasn’t familiar with. This is how you can change the working directory back to the previous working directory. Great for hopping between a couple related projects.cd -
Curly brace expansion
This is a really cool feature of bash and one I wished I’d learned about earlier. Read more about it here.
for i in {0..4}; do echo $i; done 0 1 2 3 4
My favorite use of brace expansion is to expand two long path names. E.g.
cp ~/my/long/path/to/a/file.txt ~/my/long/path/to/a/file.txt.bak
can be rewritten as
cp ~/my/long/path/to/a/file.txt{,.bak}
Previous command
!!
will give you access to the previous command. Of course you can also press the up arrow, but this is useful for modifying the previous command. 99% of the time I use this it’s because I forgotsudo
.touch somefile.txt touch: somefile.txt: Permission denied sudo !! sudo touch somefile.txt
Search command history
Press ctrl + r to start searching backwards through the command history. This is another one I wish I’d learned sooner.
(reverse-i-search)`touc': touch somefile.txt
And the entire command history is available via
history
:history | tail -n3 13637 ls 13638 ll 13639 history | tail -n3
-
Jun 4, 2018
User Feedback
I spent some of my free time over the past three weeks rebuilding a small single page app I maintain: Tumblr Top (code here).
The original incarnation of this app was written circa 2015 in CoffeeScript and AngularJS 1.4. The new version is in React and Semantic UI. There are also a few simple charts to visualize blog post and tag popularity that were coded using the Victory charting library.
I rewrote the app as an excuse to use React on a personal project and to make the site easier to build and maintain going forward. But the original version was running smoothly with something like 2-3k users per month. I didn’t want to upset the regular users by fixing what wasn’t broken.
So I built out the updated site to what I thought was a reduced but perhaps satisfactory level of functionality. I hosted the new app at a different url, and added a callout to the old site asking users to try it out.
While the new app was in-progress, I invited the early adopters to leave comments and criticism. Google Forms made soliciting and collecting user feedback dead simple (alternatives: SurveyMonkey or Typeform). The feedback survey was to the point. It only had four questions:
- Do you like the redesign?
- Do you miss the [missing feature]?
- Were there any errors or bugs with the redesign? (If so, please provide your browser / operating system)
- Any other feedback about the redesign or the app in general?
Over the course of a couple weeks the survey received 19 responses. This was enough to help me massively improve the first cut. The answers to those four questions gave me a lot of insight:
- The performance improvements I’d added had come at the cost of my API access often being rate-limited
- There was a critical display bug in a browser I hadn’t tested
- Other small bugs were highlighted
- Users had become very accustomed to what I thought were unimportant design details of the original version
- A feature I thought was useless was meaningful to a large percentage of users
- Strangers on the internet are willing to fill out a survey
- You can’t please everybody
I’m thankful to the handful of power users who took time out of their lives to provide valuable feedback for the app. Without that survey I may have shipped a shoddy update and degraded my users’ experience.
-
Apr 12, 2018
Spreadsheet Jeopardy!
My girlfriend and I often watch Jeopardy! together, and we play along and keep score to make it more fun.
To avoid having to pause the show to wager, we have a house rule that “Daily Double”s are just worth twice their marked value. E.g., a D.D. on a $600 clue would be worth $1200. And we yell the answers (questions?) instead of buzzing in.
After tallying a number of games by hand, I decided to make a custom function in Google Sheets to keep score. Our scoring notation is:
- correct: first initial of first name
- incorrect: first initial of last name
- daily double: initial followed by asterisk
function score(results, level) { mScore = 0; kScore = 0; results[0].forEach( function(cell) { cell = cell.toLowerCase() if (cell.match(/.*m\*.*/)) { mScore += 2 * level; } else if (cell.match(/.*m.*/) !== null) { mScore += level; } else if (cell.match(/.*b\*.*/)) { mScore -= 2 * level; } else if (cell.match(/.*b.*/)) { mScore -= level; } if (cell.match(/.*k\*.*/)) { kScore += 2 * level; } else if (cell.match(/.*k.*/)) { kScore += level; } else if (cell.match(/.*c\*.*/)) { kScore -= 2 * level; } else if (cell.match(/.*c.*/)) { kScore -= level; } }) return [[mScore, kScore]]; }
(Yes, I’m aware this could be refactored)
It just occurred to me that I could probably modify this function to gift me a stray hundred dollars every now and then. She probably wouldn’t notice… 😈
Recent Posts
- MacWhisper
- Coding with agent helper scripts
- Vibecoding’s allure: the inventor and the fiend
- Philadelphia Data Explorer
- Plant identification
- Let’s Flip an Unfair Coin
- Flight Focus
- Generative AI Accelerates Our Exploration of the Search Space
- AI Assistant Use-Case: Performance Feedback
- Poor Man's Computer Use with Execute Arbitrary AppleScript MCP Server