Welcome to the Clawe garden doc! This file is for thinking and organizing the work on the clawe repo.
Clawe lets you use clojure and babashka to write wm-level keybindings and manage apps and workspaces.
Completely via Rofi. Probably there's an alfred way to do this.
including per-workspace clients
configurable per client, of course
reach for: cidery, godot-y, log-y, deeper repo-type integrations
The goal was to make window-management use-cases clojure-y and REPL-friendly, so that problems are easy to solve and ideas easy to prototype and accomplish.
Ideally we won't sacrifice any speed or add too much complexity.
The initial implementation was a merger of a fennel-based awesomeWM config and a handful of babashka-based clojure namespaces.
Decoupling is useful - an agnostic implementation means more users can benefit and contribute back to clawe.
Awesome has some annoyances that I should write up in a more presentable way..
I'm hopeful that it could be used everywhere (awm, bsp, i3, perhaps?), but it already doesn't apply to OSX (using yabai 's shkd or whatever for now).
Notably, there are still some pure awesomewm bindings in-place, which can be more performant inside awesome. This is done via binding-inlining - the bindings are written from 'raw' clojure (fennel in clojure) into awesomewm config files.
The clawe 'topbar' was implemented via shadow-cljs, and electron was used to render it. I tried to abstract electron as an easy renderer in a tool called clover for a bit, but Tauri is a more recent option that I moved to, and it has been fine for my needs so far.
Refactored clawe to clean up the implementation. I had been relying on a few abstractions: defthing as an attempt at data-driven clojure, and a datalevin db backing clawe's workspaces and other details. I recently removed both of these from clawe completely - they were more complexity than the use-cases needed.
Defthing and defcom in particular were used all over to bring all the clawe/ralphie features into one binary - since babashka now supports calling arbitrary functions via `-x`, the defcom infra is redundant and has been removed.
The DB is a nice idea, but overkill - I'm still using a db for nearby use-cases, but not for things that are better off config-driven. Similarly, defworkspace was dropped completely.
Instead, we put client and workspace defs into
a
resources/clawe.edn
file, and parse that for doing wm-things.
A clawe.wm
namespace and protocol was introduced and
implemented from both AwesomeWM and Yabai, with integration
testing and malli schemas to improve stability.
clawe/toggle was rewritten to separate out the logic and execution, which helped testing and has cleaned up quite a bit.
interactive feedback available via cider/emacs or <[]>/vscode
writing and updating wm-level keybindings
easy to write and debug (full clojure repl!)
performant (in-lining commands)
client and workspace definitions as data (config)
supporting contextually-aware commands
for quick access to journals, org, spotify, slack, todos, etc
Clawe is enabled by Babashka, which gives it strong clojure-repl powers and makes
it possible for clojure programs to run fast (as in keybindings). In this way, a
bb-based clojure program runs at reasonable speed for most
awesomeWM, sxhkd, i3
fnl written to awesome configs
shell commands written to sxhkd/i3 config
Most things should be fixed after a successful cmd-r and cmd-d, which do
clawe-reload and clawe-cleanup
having/writing a proper 'clawe doctor' command would probably be useful
`clawe reload`
`clawe reload`
clawe workspaces are focused
awesome tags are selected
this is probably because multiple awesome tags can be selected at a time, which is not typical of window-managers.
'tag' is a funny kind of name for workspace, I might be abusing it.
Create, hide, show, or focus an arbitrary client.
Similar to i3's 'Scratchpad' feature
Clawe allows you to toggle workspaces as a 'scratchpad', bringing an app in that workspace front and center with a single binding.
Must be fast AF.
where do i have json parsing? ah, on osx.
:client/open supporting flexible client creation
All the WM things
Named after AwesomeWM's rules, these are a set of functions that attempt to correct the state of the apps and workspaces, moving apps to the correct workspaces if they are out of place.
when toggling emacs/terminal/other apps.
Repos (as in git-repos) are a common context for running misc commands. This can connect to bb.edn tasks and Fabb as UI Or git interactions and Magit as UI
don't go beyond the screen's dimensions
resources/clawe.edn
maybe a defs/{workspaces,clients}.edn makes sense
or just a
resources/defs.edn
?
resources/clawe.edn
another workspace vertical: logs
these all as a list of things to start-up from rofi would be sick
so that popping in with a floating-window-with-dimensions-burying scratchpad
doesn't destroy writing time
not far from the mod+c centering - take me to my last floating configuration
i.e. apache-superset + amancevice-docker-superset treemacs gets it maybe there are primary and secondaries?
similar to not leaving vim mode, now we're not leaving the editor
maybe this also works with a fabb feature?
opened at, programs run, closed at
time open
show timelines and screenshots
auto-daily review
based on the telemetry
clawe db
maybe it's just reading the logs, sourcing it that way
or maybe it's a post-processing write-to-db, something cheap
tracking gets expensive, donit
e.g. bust workspaces cache when git cloning
bet heavy on emacs for navigating
emacs bindings can be similarly written out, as zero overhead, like fennel awm bindings
and other clawe features
for processing these huge ideas buckets
automagic pomodoro powered. start the pomodoro whenever refiles/tags/etc are added we actually already have the events going into clawe, to push to the focus widget just need to start a pomodoro and serve the question: start new or continue last
definitely a clawe feature
babashka/cli
's
-x
feature plus arg coercion lets you call
arbitrary
functions in your scripts with well formed inputs. It's an excellent
combination, and spurred a clawe refactor (re-achitecture) that I hope to write
up soon. (Though I do have a brief bb-cli note here.)
For today, just want to share a piece of the new
structure:
clawe.debug/ls
and
clawe.debug/ls-print
.
When debugging clawe, especially clawe's toggle feature, I often need to get a look at the workspaces and clients that are currently running - specifically the names, window titles, class names, etc, which are used to match with client/workspace definitions. When you invoke toggle for "spotify", clawe determines the current need. Are we already focusing spotify? Does the client exist in a different workspace? When this goes wrong, we need to figure out why.
The debug namespace is a quick way to list the
clients or workspaces that clawe knows about -
(clawe.debug/ls {:type
:clients})
in a repl is
simple and quite useful.
But sometimes you're not in a clojure repl, or you want to know what a fresh invokation of clawe would see. Maybe the repl state has some advantage or difference from the actual toggle invokation that your keybinding is sending.
> This is especially true for clawe, as I like to develop it against the > fully-featured jvm cider repl (against doctor 's backend server) rather than a > basic bb-repl. This supports extremely useful features like cider-inspect, > and makes clojure development a real joy.
Fortunately, bb-cli's
-x
lets us invoke the exact command on the
command line:
> alias clawebb='bb --config ~/russmatney/clawe/bb.edn'
> clawebb -x clawe.debug/ls-print --type :clients
| :client/window-title | :client/app-name |
|--------------------------------+------------------|
| clawe | alacritty |
| clawe | emacs |
| dino | emacs |
| dotfiles | emacs |
| tauri-doctor-topbar | clove |
| journal | emacs |
| slack | clojurians | slack |
| spotify | spotify |
| babashka/cli — mozilla firefox | firefox |
There we are! We can see that the spotify client is found and properly labelled.
Note that here we're calling
ls-print
instead, which is a simple wrapper that
passes the ls
results to
clojure.pprint/print-table
.
---
A few weeks after this implementation, I started toying with nextjournal/clerk,
and it was a delight to find that
clawe.debug/ls
can feed directly into
clerk/table
, which led to
this tiny tmux status notebook:
(ns notebooks.tmux
{:nextjournal.clerk/visibility {:code :hide :result :show}}
(:require
[clawe.debug :as debug]
[nextjournal.clerk :as clerk]))
;; ### tmux sessions
^::clerk/no-cache
(clerk/table (debug/ls {:type :tmux}))
;; ### tmux panes
^::clerk/no-cache
(clerk/table (debug/ls {:type :tmux-panes}))
---
Another piece of the
clawe
refactor: Clawe now invokes its
keybindings by default with
-x
.
;; from clawe/defs/bindings.clj
(defkbd toggle-terminal
[[:mod] "Return"]
(sxhkd-exec "bb --config ~/russmatney/clawe/bb.edn -x clawe.toggle/toggle --key terminal"))
(defkbd toggle-emacs
[[:mod :shift] "Return"]
(sxhkd-exec "bb --config ~/russmatney/clawe/bb.edn -x clawe.toggle/toggle --key emacs"))
(defkbd toggle-workspace-journal
[[:mod] "u"]
(sxhkd-exec "bb --config ~/russmatney/clawe/bb.edn -x clawe.toggle/toggle --key journal"))
(defkbd toggle-workspace-web
[[:mod] "t"]
(sxhkd-exec "bb --config ~/russmatney/clawe/bb.edn -x clawe.toggle/toggle --key web"))
Prior to this, I was using a defthing macro to register commands, and invoking them by passing the name into clawe's main function.
This change simplified the architecture by completely removing that registry,
and has the added benefit of making it very simple to reproduce an invokation -
you can just invoke it yourself:
bb --config ~/russmatney/clawe/bb.edn -x clawe.toggle/toggle --key journal
This ends up being more performant as well -
there's no need to require every namespace in clawe to call
one command. When invoked via
-x
, only the
required namespaces are loaded, so we get to the good bits
right away.
---
For more on Clawe's features and design journey, check out the clawe workspace note.
I'm finally sponsoring some folks for their open source work with (small) monthly donations - just a way to say thanks for tools I use all day, every day.
Babashka has a 'preloads' feature for adding things to any babashka project/usage on your machine.
So you want to create a quick native dashboard. You know enough web dev to be dangerous, and now you're hoping to create something useful that isn't stuck in one of your browser tabs.
Here I'll touch on the path I took through Electron that eventually lead to Tauri, then include links to the way I'm currently using Tauri in Clawe.
Clawe, and your window manager, have a config file somewhere that lets you tell the program what your preferences are. Which clients should the window manager manage? What workspaces do you care for? What are you keybindings?
What's the most ergonomic way to manage that config? Plain text? Or fancy UI components?
A clojure repl is excellent, if you can eval things right then and there.
Lisp and clojure data structures are very nice for manipulating data: both minimal and expressive.
But we also want some automated generation of this data, some mixing of something sort of like a database.
Example: automatically adding a workspace for a new github repo from a browser url.
Ralphie is a library for useful clojure / babashka apis.
It provides namespaces and functions for integrating with whatever tools I use.
The gist: bash-like scripting and automation libraries via babashka and repl -driven development.
Originally targetted linux cli helpers, but expanded to some osx use-cases as well.
Example namespaces: emacs, tmux, browser, git, spotify, rofi.
A video overview of the components that make up the clawe monorepo.
Relevant April 2023.
I'll post a link here once it exists! For now, if you're reading this, I could stream this any day, so take a look at my schedule or ping me to see when it's going to happen.
I refactored most of clawe last week (early august 2022).
It'd be good to capture what happened and why, and to share that as a post.
some raw:
clawe clients need def backing (persisted) so that i can:
just a name gets you pretty far, b/c you can sync across apps for workspaces
(emacs, tmux) to get re-openable state - useful when moving between contexts to
re-open a workspace just as you left it
bb-cli unlocks some strong apis that i can bake inputs of into the config, and
also easily test on the command line. it's already been a treat for
clawe.toggle/toggle-app
Lisp-based desktop environment tools! For managing windows, keyboards, config files, whatever.
Welcome to the Clawe garden doc! This file is for thinking and organizing the work on the clawe repo.
Clawe lets you use clojure and babashka to write wm-level keybindings and manage apps and workspaces.
A fully-featured tiling window manager on OSX.
Targetted by clawe to bring clojure -y, repl-driven development to OSX.
I need a focus 'stack' - something to push a new focus onto and pop a focus off of when that one is complete.
I want to be able to org-clock into a few items in a row, and have them stack rather than overwrite.
This behavior seems to follow the real-world rabbit-hole problem - you start in on a task, realize something else has to change first, pop that focus on top. Then you solve that problem, so it's time to pop a stack frame back to the previous focus as it is no longer blocked.
Seems blockers are relevant here - perhaps the focus-stack could re-arrange itself based on blockers, automatically guiding you through whatever focuses you've opted into.
This was solved somewhat in yodo - I used a last-focused-at to determine what items had been focused in a given pomodoro - that was the focus list. As items were completed, they were removed from that list.
Maybe a similar approach could be made using org-clock and org-crud now?
Yodo is a todo app that suggests what to work on next.
'Yo, do X' or 'Yo, do Y'.
I've been through a few versions, but all feature an emacs org-mode integration and a clojure implementation.
Some of the biggest learnings are that a web app is
not enough. It needs to be
integrated
.
Fortunately, that's what ~ clawe
~ is all about.