
Brett Terpstra
Brett is a writer and developer living in Minnesota, USA. You can follow him as ttscoff on Twitter, GitHub, and Mastodon. Sign up for the email newsletter, and keep up with this blog by adding it to your favorite news reader.
Bunch 1.4.8 was officially released this morning. It consists entirely of bug fixes that have been going through beta testing for a while now. Nothing groundbreaking, but some stuff that you probably didn’t know wasn’t working1 is now working.
Some variable handling stuff:
if var...
)Tagging stuff:
%\tag
was not ignoring the action on closeSome general fixes:
Grab the latest version from the download page (or just use Bunch->Check for Updates). Please keep the bug reports coming, and feel free to join the discussions if there are features you’d like to see.
Because they mostly relate to obscure features that, statistically, you likely don’t know exist. ↩
Thanks to TextExpander for sponsoring BrettTerpstra.com again this week!
Get your team communicating faster with TextExpander, and keep your team’s knowledge at their fingertips.
Put information in the hands of your team, outside of silos. Your team could be sending a unified message to your customers without reinventing the wheel.
Here’s how it works:
Store It: Keep your company’s most used emails, phrases, messaging, URLs and more right within TextExpander.
Share It: Get your whole team access to all the content they need to use every day.
Expand It: Deploy the content you need with just a few keystrokes on any device, across any apps you use.
It’s that easy!
TextExpander is available on Mac, Windows, Chrome, iPhone, iPad.
BrettTerpstra.com readers get 20% off their first year. Visit TextExpander.com for more info.
I’ve always relied on 2-bit coloring for terminal output, sticking to eight foreground and eight background colors. It’s nice because it conforms to any user’s terminal theme. But there’s a whole world of colors out there (in most modern terminals) and I figured it was about time I started expanding my horizons.
I write most of my command line tools and scripts in Ruby. Call me old fashioned, but it really is a beautiful, readable language with plenty of room for elegant solutions. So this little snippet is in Ruby, but the core of it is simple enough that I can’t imagine it would take much work to port.
Like all terminal color output, it relies on ANSI escape codes. 2-bit escape codes look like \033[0;30;41m
, which would be regular black text on a red background. But you can use RGB values in most modern terminals with an escape code like \033[38;2;RRR;GGG;BBBm
(for a foreground color). So all you have to do is get RGB values for a specific color and insert them into that code.
The following Ruby snippet (gist here) will take a CSS hex color, e.g. #FA380E
, and turn it into an ANSI escape sequence for you. It splits the hex value and uses the #hex
method in Ruby to convert a 2-digit hex into a value between 1 and 256. With it you can run something like print "text to color".color256('#FA380E')
and get some bright red text. The snippet itself is designed to be included in a script. If you run it directly you can test by using ./256color.rb "TEXT TO COLOR" "FG" "BG"
where FG and BG are 3 or 6-digit hex codes. The octothorp (#
) is always optional.
#!/usr/bin/env ruby
# frozen_string_literal: true
# 256-color hex interpretation for terminal colorization
#
# Usage
# "text".color256(foreground, background)
#
# print "Colorize this".color256('#f7921e', '#666')
# String category
module Color256
def fg(fgc)
"\x1b[38;2;#{fgc.rgb}m"
end
def bg(bgc)
"\x1b[48;2;#{bgc.rgb}m"
end
def reset
"\x1b[0m"
end
def rgb
hex_string = sub(/^#?/, '')
hex_string = hex_string.split(//).map { |a| a * 2 }.join('') if hex_string =~ /^#?[a-f0-9]{3}$/i
parts = hex_string.match(/#?(?<r>..)(?<g>..)(?<b>..)/)
t = []
%w[r g b].each do |e|
t << parts[e].hex
end
t.join(';')
end
def color256(fgc, bgc = nil)
out = ''
out += fg(fgc)
out += bg(bgc) if bgc
out += self
out + reset
end
end
class ::String
include Color256
end
def test
256.times do |r|
256.times do |g|
256.times do |b|
c = ''
[r, g, b].each { |comp| c += format('%02x', comp) }
print ' '.color256('000', c)
end
end
end
end
if __FILE__ == $0
print ARGV[0].color256(ARGV[1], ARGV[2])
end
Hope that’s of use to somebody!
By the way, this is incorporated into the latest version of Doing such that you can use %#RRGGBB
instead of a color name to set colors in a template. And use %b#RRGGBB
to set a background color. Customize away!
I haven’t had a lot of time for personal projects lately. Partly because I switched my ADHD meds to Vyvanse, which isn’t great for my attention but does keep my bipolar in check, meaning I’ve had moderate concentration abilities combined with a complete lack of all-night manic coding binges. Which, by and large, is better for my productivity overall, but definitely means less time spent on side projects. I’m at a point in my life where that’s the preferable outcome.
That said, I’ve pushed some updates to Doing recently, and have been working on a Bunch bug or two. Marked has gotten a couple of bugfixes pushed, and we’re so close to getting nvUltra out the door. Between Fletcher and I, time for it has been tight, but we’ve put a feature freeze on it and are just finalizing the MAS testing for the in-app purchase and subscription setup we’re going to use to allow us to offer a free trial and a sustainable pricing structure. More soon.
In the meantime, I’ve gotten some new hardware recently that I thought warranted a quick post.
First, I got my new Mac Studio. I didn’t go all out on it; I got the Max with 64G of RAM, but it leaves my M1 mini in the dust. Compiler tasks that took about 30 seconds on the mini now take 5 seconds on the Studio. And the test suite for Doing used to take about 72 seconds to run, and now takes 30 — which is impressive mostly because what slows those tests down is a ton of file read/writes that processor speed can’t improve anyway. So I’m very happy with the purchase, and if you’re looking for a Mac with top performance, the Studio is an excellent choice.
Second, I got an OWC Express 4M2 for work with a couple of NME SSD cards and I’m so blown away by the speed that I’ll soon be purchasing a second one for myself. 1345 MB/s read and 1436 MB/s writes. It’s crazy fast for an external drive, and you can put together terrabytes worth of storage for less than buying individual external SSD drives. I also got a G-RAID for 36TB of backup space, and over a Thunderbolt 4 connection that one gets 482/514 MB/s even with platter drives. Now I just need a faster Synology…
Well, that’s my update for now. I know my posting pace has slowed a bit, but I’m grateful to all of the BrettTerpstra.com readers who support my work with monthly donations. I’ll keep putting out (and supporting) new stuff, and I’ll be working to post a bit more often.
Thanks to TextExpander for sponsoring BrettTerpstra.com this week!
Get your team communicating faster so they can focus on what’s most important — with TextExpander your team’s knowledge is at their fingertips.
Get your whole team on the same page by getting information out of silos and into the hands of everyone that needs to use it. You can share your team’s knowledge across departments so your team is sending a unified message to your customers and isn’t spending time reinventing the wheel.
Here’s how it works:
Store It
Keep your company’s most used emails, phrases, messaging, URLs and more right within TextExpander.
Share It
Get your whole team access to all the content they need to use every day. Organize it by department and
Expand It
Deploy the content you need with just a few keystrokes on any device, across any apps you use.
It’s that easy!
TextExpander is available on Mac, Windows, Chrome, iPhone, iPad.
BrettTerpstra.com readers get 20% off their first year. Visit textexpander.com to learn more.
Thanks to TextExpander for sponsoring BrettTerpstra.com this week!
What would you do with a little more time in your week? Repetitive typing, little mistakes, searching for answers – they’re all taking precious minutes from you and your team. With TextExpander, you can take back your time so you can focus on what matters most in your business.
With TextExpander, you and your team can:
The way we work is changing rapidly. Make work happen wherever you are by saying more in less time and with less effort using TextExpander.
TextExpander is available on Mac, Windows, Chrome, iPhone, iPad.
BrettTerpstra.com readers get 20% off their first year.
Visit textexpander.com to learn more about TextExpander.
I put some time into Bunch this weekend. It’s been a little while since I’ve worked on it — I haven’t needed it to do anything new lately, and the current release hasn’t had any major bug reports. So it’s been happily humming along. But there was one feature request that I did keep receiving and decided to take another look at.
The request is for support for Monterey’s focus modes. Bunch does a decent job of enabling and disabling Do Not Disturb, but the process is a hack that involves nested settings in PLIST data and toggling system daemons. I don’t love that I have to do that, but Apple provides no API for such settings. And I haven’t found a way (yet) to extend that hack to work with Focus Modes. So I offer a workaround: you can turn Focus Modes on and off using Shortcuts, and you can easily run Shortcuts in a Bunch using the shortcuts
CLI.
Create two shortcuts for each focus mode you want to control from Bunch, one for turning on, and one for turning off.
Then, in a Bunch, you can use $ /usr/bin/shortcuts run "SHORTCUT_ON_NAME"
(replacing SHORTCUT_ON_NAME
with the name of the shortcut that turns the focus mode on), and !$ /usr/bin/shortcuts run "SHORTCUT_OFF_NAME"
. The combination of these two will turn the focus mode on when the Bunch opens, and off when it closes. You can, of course, reverse these as needed.
$ /usr/bin/shortcuts run "Work Mode"
!$ /usr/bin/shortcuts run "Work Mode Off"
There is no step 3.
Now when you open and close the Bunch, your desired Focus Mode will toggle on and off with it. It’s a couple extra steps beyond having a Bunch command to handle it, but I don’t foresee being able to incorporate them directly. Of course, I may just be missing something in the API that would make it possible without deep hacks, in which case I will definitely incorporate it.
Another request I got led to a couple of bug fixes. The following currently only works in the 2.4.8 beta (download here), but if testing goes well, it will be released to the stable version in short order.
What came up was the idea of having Single Bunch Mode apply to subsets of Bunches.
Single Bunch Mode can be enabled in preferences and simply means that when a Bunch opens, all other Bunches close, so you’re only running one at a time. Because Bunch is built for “context switching,” this makes switching your working context a single step. You can have any Bunch ignore Single Bunch Mode using frontmatter, so Bunches that should always be running can be easily excluded.
However, you might want to have multiple groups of Bunches where only one Bunch within the group is running at any given time. This can be accomplished with Bunch’s tagging features.
To tag a bunch, you just add frontmatter:
tags: work
Tag all of the Bunches in this subgroup with the same tag. Then, in each Bunch in the group, use the \tag
syntax to act on the tag. Use !
to indicate you want to close the tagged Bunches, and include a %
sign to ignore the directive when closing (so it doesn’t reverse it and reopen all of the tagged Bunches).
%!\work
This effectively gives you Single Bunch Mode for subsets of Bunches. If you ever want to do that, give it a shot.
Thanks to TextExpander for sponsoring BrettTerpstra.com this week! TextExpander will be one of the very first apps I install when I get my brand new Mac Studio!
In our fast-paced world, things change constantly — and errors in messaging often have significant consequences.
TextExpander lets you make new approved messaging available to every team member instantly with just a few keystrokes, ensuring your team remains consistent, current, and accurate.
Get your message right every time: expand content that corrects your spelling and keeps your language consistent with just a few keystrokes.
Your team members will always get the right message to the right person at the right time — it’s way faster than copy paste and way more reliable than your memory.
BrettTerpstra.com readers get 20% off their first year. Visit textexpander.com to learn more.
Web excursions brought to you in partnership with CleanMyMac X, all the tools to speed up your Mac, in one app.
Introducing 1Password for SSH & Git (Beta), the single source of truth for all your SSH keys.
After about a year of personal experiments, I present you the “Zettel Composer”, a script for combining Zettelkasten notes in a variety of situations: browsing annotations, publishing a book or a paper, assembling a handout for lectures or conferences etc.
ksdiff()
function to the debugger that you can call from the console or breakpoint actions. It can also diff the source and CSS of any page or element, which is amazing for web development and debugging. Nice work!I’ve added some stuff to Doing lately. Like, a lot of stuff. To be fair, I’ve also done a lot of jobby job stuff. And worked with Fletcher on nvUltra. And started a rewrite of Gather as a menu bar clipboard web Markdownifier with integration with multiple apps (that one’s going to be cool if I finish it). And some time on Bunch. But as I’ve said before, the one thing I use when working on all of these is Doing. And I always want it to do something new or better, so it gets some attention.
Oh, and looking at my doing recent
output reminds me that I also tried my hand at creating my first oh-my-fish plugin yesterday. It has some dependencies and I don’t think it’s right for the main packages repo, so you have to add the repository manually to try it out, but I think it’s fully functional. If you want intelligent directory navigation with bookmarks, fuzzy matching, and fasd/fzf integration in Fish, all in your cd
command, check it out.
Anyway, what were we talking about? Hang on:
Right. Doing updates. To get the latest version, just run gem install doing
, which as of this writing should get you at least version 2.1.34. (If you get an error, run the same command with sudo
.)