Request for testers – TabFern tab-at-a-time support

Three tabs open; two tabs closed but still saved!

My TabFern tab-management Chrome extension is ready for its next great leap and I need your help!  For years, TabFern has been all-or-nothing: all the tabs in a window are open, or none of them are.  Prerelease version 0.3.0-pre.1 removes that limitation.  If the instructions below are your speed, or you already know how to load unpacked Chrome extensions, would you please install TabFern 0.3.0-pre.1 and let me know how it works for you?  Please report problems through the GitHub issues page.  Once this release has had some testing, I will roll it out to the over 1,000 users of TabFern!  Thanks very much for your help, and for using TabFern!

(Reminder: as always — and especially when testing prereleases — backup early and often!  TabFern comes with no warranty of any kind!)


  1. Download the zip from here.
  2. Unzip it into a directory of your choice.
  3. In Chrome, go to chrome://extensions.
  4. Turn on the toggle in the upper-right that says “Developer mode”.
  5. Near the top left, click “Load unpacked”.
  6. Navigate to the directory where you unzipped the extension and hit OK.  (There’s no specific file to select — just go into the unzip location and then hit OK.)
  7. That’s it!  The TabFern popup window and the help window will open up.

Keyboardio Model100 custom-firmware notes

I am typing this on my new Keyboardio Model100!  Setup was a bit rocky; hopefully these notes will help you!

What worked (try 3)

Setup prerequisites

  1. Add udev rules for the Model100 per .
    • chown root:root the new rules file or it won’t be used (ask me how I know! 😉 )
  2. Install the latest dfu-util from source
  3. Confirm that the keyboard is accessible:
    1. Hold prog while plugging in the keyboard
    2. Run ./src/dfu-util -lv and make sure Model 100 (bootloader) appears
  4. Install the latest firmware using Chrysalis per

Setup Chrysalis-Firmware-Bundle

  1. Clone and cd into the clone dir.
  2. Run ./tools/bootstrap.  This will create .arduino/ and .kaleidoscope/ and download lots of files into them.
  3. export KALEIDOSCOPE_DIR="$(realpath .kaleidoscope/)"
  4. export ARDUINO_DIRECTORIES_USER="$(realpath .kaleidoscope/.arduino/user)"
  5. make -C .kaleidoscope setup

Build the firmware

  1. make Keyboardio/Model100 VERBOSE=1.  This should leave output in output/Keyboardio/Model100/default.bin.
  2. Unplug the Model100, hold Prog, and plug the Model100 back in.  The prog key should light steady red.
  3. Run dfu-util -d 3496:0005 -D ./output/Keyboardio/Model100/default.bin -R — use your locally-built dfu-util.  You should see something like the below “Sample dfu-util output”.  The first column of keys will cycle green while this is happening.
  4. When the programming is done, the keyboard should reset.  If it doesn’t, unplug and replug it.

Sample dfu-util output

dfu-util 0.11-dev
dfu-util: Warning: Invalid DFU suffix signature
dfu-util: A valid DFU suffix will be required in a future dfu-util release
Opening DFU capable USB device...
Device ID 3496:0005
Copying data from PC to DFU device
Download [=========================] 100% 167104 bytes
Download done.
DFU state(7) = dfuMANIFEST, status(0) = No error condition is present
DFU state(2) = dfuIDLE, status(0) = No error condition is present
Resetting USB to switch back to Run-Time mode
dfu-util: error resetting after download: LIBUSB_ERROR_OTHER

Customizing the firmware

  1. Edit Keyboardio/Model100/Model100.inoExample.
  2. Run the “Build the firmware” steps from above.

Try 1 (didn’t work)

  1. Add udev rules for the Model100 per .
    • chown root:root the new rules file or it won’t be used (ask me how I know! 😉 )
  2. Install the latest dfu-util from source
  3. Confirm that the keyboard is accessible:
    1. Hold prog while plugging in the keyboard
    2. Run ./src/dfu-util -lv and make sure Model 100 (bootloader) appears
  4. Install the latest firmware using Chrysalis per
  5. Update the Arduino keyboard defs:
    1. Tools | Boards | Board manager
    2. Select Type: Updateable in the upper-left
    3. click on Keyboardio, then Update
    4. In the top-right box, search for Model 100 (with a space)
    5. Click on the result and select Install
  6. Fork .  Clone and create a dev branch.
  7. In the dev branch, `git checkout 50a7fbb`.  This is to prevent a compile error (reported here).
  8. In the Arduino IDE, open the Model100 firmware from your clone of Chrysalis-Firmware-Bundle/Keyboardio/Model100/Model100.ino
  9. Build:
    1. Tools | Board | Kaleidoscope Keyboards (2021+) | Keyboardio Model 100
    2. Sketch | Verify/Compile
  10. Upload: When I try to upload, I get an error because the bundled dfu-util needs glibc 2.34, which my dev machine does not currently have.  Therefore:
    1. Sketch | Upload
    2. Copy the dfu-util command line from the error window
    3. Unplug the keyboard, then plug it back in while holding prog
    4. Run the dfu-util command using my built-from-source dfu-util

Unfortunately, the result is a nonfunctional keyboard!  The led key’s LED breathes green briefly but the keyboard is otherwise inert (although does show as USB-connected).  I am able to use Chrysalis to get back to factory firmware, however.

Try 2 (didn’t work)

  1. Remove my old ~/.arduino15/
  2. Download Arduino 1.8.19 tarball from
  3. Untar it
  4. Run cd arduino-1.8.19 && ./
  5. Run the IDE
  6. File | Preferences, and add as an additional board manager URL (ref.)
  7. Tools | Board | Board manager
  8. Search for Keyboardio
  9. Install the Kaleidoscope Keyboards (GD32) bundle (takes a while)
  10. Fix a warning: cd ~/.arduino15/packages/keyboardio/hardware/gd32/1.99.7/libraries/Kaleidoscope-LEDControl && ln -s ../Kaleidoscope/src/Kaleidoscope-LEDControl.h .
  11. Add the built-from-src dfu-util: cp ~/src/dfu-util/src/dfu-{prefix,suffix,util} ~/.arduino15/packages/keyboardio/tools/dfu-util/0.11.0-arduino3/
  12. In the IDE, Tools | Board | Kaleidoscope Keyboards (2021+) | Keyboardio Model 100
  13. In the IDE, File | Examples | Kaleidoscope | Devices | Keyboardio | Model 100
  14. In the IDE, Sketch | Verify/Compile
  15. Unplug the Model100, then hold Prog and plug it back in.  Prog should light steady red.
  16. In the IDE, Sketch | Upload

Trying out Perl packages locally

I’ve really enjoyed Perl since I started using it in late 2016. I invite you to experiment with it yourself! For trying out packages from CPAN (Perl’s package archive) on your local system, I suggest:

  1. Install local::lib per
  2. Install cpanminus. If it’s not a system package on your distro, follow,-plenv-etc.) .
  3. say cpanm App::cpmcpm is faster than cpanminus 😀
  4. To install a module, say cpm -gv Whatever::Module

Yes, this is more tedious than I would prefer! I’m sure there are shorter ways that are distro-specific, but the above should work just about anywhere.

Some sites that can help you learn:

Dragging tabs off a Chromium window (solved!)

For several months, I have not been able to drag tabs off a Chromium window. When I let go of the mouse button to release the tab, Chromium didn’t respond. That is finally fixed!

First, thanks to jangxx for discovering that turning off Chromium’s “Use system titlebar and borders” option fixes the problem. Unfortunately, my Chromium doesn’t have that option! I had to do it by hand:

  1. In the browser, go to chrome:settings
  2. Under Appearance, make sure “Theme” is “GTK+”. If it’s not, hit “Use GTK+”.
  3. In the browser, go to chrome:version
  4. Make a note of the “profile path”.
  5. Exit Chromium entirely.
  6. In a shell, cd to the profile path
  7. cp Preferences Preferences.bak
  8. Edit Preferences and change "custom_chrome_frame":false to "custom_chrome_frame":true
  9. Restart Chromium. You should be able to drag tabs!

Let me know if this helps you, or if you need more information about these steps. Happy browsing!

autom4te and m4sh for the impatient

autom4te is the engine beneath autoconf, but you can use it to generate your own portable shell scripts for other purposes.  For example, create this test.m4sh:

# test.m4sh
m4_errprintn([before init])
m4_errprintn([before copyright])

# draws a box on stdout
AS_BOX([Test foo])

# Puts a box in the output as a comment
m4_text_box([testing foo])

AS_IF([test "x$foo" = 'x1'],[
echo Foo is 1
m4_errprintn([at end])

Run autom4te:

$ autom4te -l M4sh -Wall -o test.m4sh 
before init
before copyright
at end

(Note that the m4_errprintn calls print while autom4te is running.)

Now you have

$ ./ WARNING: warning
## -------- ##
## Test foo ##
## -------- ##
Foo is 1

Stunning, right? 😉

The number one gotcha

If you forget AS_INIT, you will get no output and no error message.  You have been warned.

Commands used in this example

  • AS_INIT: required.  At the top of the input file.
  • AS_COPYRIGHT: puts the text you give it into the output as a comment at the top of the generated script file.
  • AS_WARN: prints a warning when the generated script runs
  • AS_ECHO: prints to stdout when the generated script runs
  • m4_errprintn: prints to stderr when autom4te runs.  No effect on the generated script.
  • AS_BOX: prints the “Test foo” box in the example output, when the generated script runs
  • m4_text_box: puts into the generated script file a comment like this one:
## ----------- ##
## testing foo ##
## ----------- ##
  • AS_IF: a shell conditional, but more portable.
  • test: the clunky, old-school (but very portable) way of checking conditionals.  For maximum portability, don’t let any argument to test be the empty string (hence the xs in the test command above)

Happy hacking!

Music of the Day

The “Desert Princess” house mix by Wav-E, distributed by Cafe de Anatolia:

Good variety and nice beats!  I think I’m on my third consecutive play-through 🙂 .

This mix reminds me of some albums from Cyan Music, as did the last Cafe de Anatolia set I wrote about.  I haven’t checked Cyan’s catalog recently and it is clearly time to do so 🙂 .  Any recent favorites you’d recommend?

Music of the Day

Cafe De Anatolia – Ethno Deep Summer Mix (by Professor)

2 hr. of music that is quite unlike my usual fare, but that I am enjoying! I am up to 1:09:03. So far, mellow with a nice beat and mostly instrumental.

Update: the section starting at ~1:09:43 reminds me very much of a Cyan Music album, though I can’t recall which one. Something by Fortadelis, maybe? Let me know what you think in the comments!

Happy Memorial Day!

Music of the Day

From the same group as last time, but at a slower tempo. There’s plenty of interesting material in this set — don’t plan to listen just before bedtime 😉 .

Repeating the last ‘:’ (Ex) command in Vim

Sometimes I run a complicated search-and-replace (:s/../../) in Vim, and I then want to repeat it somewhere else. In the past, I have always hit :, up-arrow to retrieve the command from the history. It should be no surprise by now that there is an easier way!

@: in normal mode (at-sign, colon) will repeat the last : command. Once you have done @:, you can hit @@ to re-run the command. Quick and easy!

This works because the : register, ":, holds the last Ex command. @ runs a macro, and knows that using the : register means you want to repeat an Ex command. @@ runs the last macro, so works for @: just as it does for @a or any other normal-mode macro.

I found ": in :help registers when I was looking for a register that held the filename of the file open in another window, as opposed to the alternate filename in the current window. Details of ": are at :help ": (believe it or not! 😉 ).