-- Leo's gemini proxy

-- Connecting to log.aviary.biz:1965...

-- Connected

-- Sending request

-- Meta line: 20 text/gemini;

my own command launcher


i used to use a program called rofi to start programs on my computer. my i3 config looked like this:


bindsym	$mod+d exec /usr/local/bin/rofi -show combi -combi-modi 'window#run#ssh' -modi combi -terminal '/usr/local/bin/kitty  --single-instance'

rofi homepage


this complicated command line belies the many features available in rofi. they were neat, but when i tried to make the colors change along with the rest of my computer in dark mode, i was confronted by...css??? for example, this is the default theme, obtained with rofi -dump-theme.


/**
 * rofi -dump-theme output.
 * Rofi version: 1.7.0
 **/
* {
    red:                         rgba ( 220, 50, 47, 100 % );
    selected-active-foreground:  var(background);
    lightfg:                     rgba ( 88, 104, 117, 100 % );
    separatorcolor:              var(foreground);
    urgent-foreground:           var(red);
    alternate-urgent-background: var(lightbg);
    lightbg:                     rgba ( 238, 232, 213, 100 % );
    background-color:            rgba ( 0, 0, 0, 0 % );
    border-color:                var(foreground);
    normal-background:           var(background);
    selected-urgent-background:  var(red);
    alternate-active-background: var(lightbg);
    spacing:                     2;
    blue:                        rgba ( 38, 139, 210, 100 % );
    alternate-normal-foreground: var(foreground);
    urgent-background:           var(background);
    selected-normal-foreground:  var(lightbg);
    active-foreground:           var(blue);
    background:                  rgba ( 253, 246, 227, 100 % );
    selected-active-background:  var(blue);
    active-background:           var(background);
    selected-normal-background:  var(lightfg);
    alternate-normal-background: var(lightbg);
    foreground:                  rgba ( 0, 43, 54, 100 % );
    selected-urgent-foreground:  var(background);
    normal-foreground:           var(foreground);
    alternate-urgent-foreground: var(red);
    alternate-active-foreground: var(blue);
}
element {
    padding: 1px ;
    cursor:  pointer;
    spacing: 5px ;
    border:  0;
}
element normal.normal {
    background-color: var(normal-background);
    text-color:       var(normal-foreground);
}
element normal.urgent {
    background-color: var(urgent-background);
    text-color:       var(urgent-foreground);
}
element normal.active {
    background-color: var(active-background);
    text-color:       var(active-foreground);
}
element selected.normal {
    background-color: var(selected-normal-background);
    text-color:       var(selected-normal-foreground);
}
element selected.urgent {
    background-color: var(selected-urgent-background);
    text-color:       var(selected-urgent-foreground);
}
element selected.active {
    background-color: var(selected-active-background);
    text-color:       var(selected-active-foreground);
}
element alternate.normal {
    background-color: var(alternate-normal-background);
    text-color:       var(alternate-normal-foreground);
}
element alternate.urgent {
    background-color: var(alternate-urgent-background);
    text-color:       var(alternate-urgent-foreground);
}
element alternate.active {
    background-color: var(alternate-active-background);
    text-color:       var(alternate-active-foreground);
}
element-text {
    background-color: rgba ( 0, 0, 0, 0 % );
    cursor:           inherit;
    highlight:        inherit;
    text-color:       inherit;
}
element-icon {
    background-color: rgba ( 0, 0, 0, 0 % );
    size:             em ;
    cursor:           inherit;
    text-color:       inherit;
}
window {
    padding:          5;
    background-color: var(background);
    border:           1;
}
mainbox {
    padding: 0;
    border:  0;
}
message {
    padding:      1px ;
    border-color: var(separatorcolor);
    border:       2px dash 0px 0px ;
}
textbox {
    text-color: var(foreground);
}
listview {
    padding:      2px 0px 0px ;
    scrollbar:    true;
    border-color: var(separatorcolor);
    spacing:      2px ;
    fixed-height: 0;
    border:       2px dash 0px 0px ;
}
scrollbar {
    width:        4px ;
    padding:      0;
    handle-width: 8px ;
    border:       0;
    handle-color: var(normal-foreground);
}
sidebar {
    border-color: var(separatorcolor);
    border:       2px dash 0px 0px ;
}
button {
    cursor:     pointer;
    spacing:    0;
    text-color: var(normal-foreground);
}
button selected {
    background-color: var(selected-normal-background);
    text-color:       var(selected-normal-foreground);
}
num-filtered-rows {
    expand:     false;
    text-color: rgba ( 128, 128, 128, 100 % );
}
num-rows {
    expand:     false;
    text-color: rgba ( 128, 128, 128, 100 % );
}
textbox-num-sep {
    expand:     false;
    str:        "/";
    text-color: rgba ( 128, 128, 128, 100 % );
}
inputbar {
    padding:    1px ;
    spacing:    0px ;
    text-color: var(normal-foreground);
    children:   [ prompt,textbox-prompt-colon,entry,num-filtered-rows,textbox-num-sep,num-rows,case-indicator ];
}
case-indicator {
    spacing:    0;
    text-color: var(normal-foreground);
}
entry {
    text-color:        var(normal-foreground);
    cursor:            text;
    spacing:           0;
    placeholder-color: rgba ( 128, 128, 128, 100 % );
    placeholder:       "Type to filter";
}
prompt {
    spacing:    0;
    text-color: var(normal-foreground);
}
textbox-prompt-colon {
    margin:     0px em em em ;
    expand:     false;
    str:        ":";
    text-color: inherit;
}

if you are reading this via gemini, i hope you are sympathetic to my frustration :)


instead of trying to grok this, i figured i'd try to write my own launcher. i thought a little about what gui toolkit i would learn, how i would port the nice rofi features, etc.—and then i asked myself, "what is the absolute tiniest thing that would work?"


~/bin/launcher

#!/bin/ksh

set -e
set -u
set -o pipefail

logger -t launcher $PATH

gather () {
IFS=:
for path in $PATH
do
	logger -t launcher inspecting $path
	unset IFS
	find $path -maxdepth 1 \( -type f -or -type l \) -print \
	| while read
	do
    		# logger -t launcher inspecting $REPLY
		if test -x $REPLY
		then
			echo $REPLY
		else
    			logger -t launcher not executable: $REPLY
		fi
	done
done
unset IFS
}

prog=$(gather | fzf)
nohup $prog &
logger -t launcher executed $prog as $!

i3 config

bindsym $mod+d exec /usr/local/bin/kitty --single-instance --name launcher $HOME/bin/launcher
for_window [class="^kitty$" instance="^launcher"] floating enable sticky enable resize set 640 240

here's what it looks like.


light mode

dark mode


this is all i need! it's also very satisfying to let the launcher program only woory about stdin/stdout, and then let the window manager handle the placement and size of the window. i also like using kitty and fzf as my "gui toolkit." it turns out terminals are great at handling text!


references


fzf(1), "a command-line fuzzy finder"

kitty(1), a terminal emulator

openbsd manual pages on the www

-- Response ended

-- Page fetched on Sat May 11 13:40:54 2024