clawe

Created: Aug 29, 2021Published: Nov 01, 2022Last modified: Apr 24, 2023
Word count: 1486Backlinks: 38

Welcome to the Clawe garden doc! This file is for thinking and organizing the work on the clawe repo.

  • Clawe Repo link

Clawe lets you use clojure and babashka to write wm-level keybindings and manage apps and workspaces.

Showcase

> From browsing a repo to opening it in a dedicated workspace

Completely via Rofi. Probably there's an alfred way to do this.

> 4 cases for toggle client

including per-workspace clients

configurable per client, of course

reach for: cidery, godot-y, log-y, deeper repo-type integrations

> (not implemented) Unified logging

Path/Dev Story

> I started russmatney/ralphie to replace bash hacks with babashaka hacks

> Ralphie deserved to stay 'pure' and impose less, so clawe was created to express stronger/larger WM opinions/features

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.

> I wrote away from a clojure-for-AwesomeWM implementation

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..

  • You have to restart awesomeWM to modify keybindings, which is annoying b/c Awesome's restore-after-restart is already left to user-land, so clients end up in whatever tag unless you handle that yourself. See AwesomeWM dev loop details.
  • I'd rather use web tech to build widgets, so, no need to try to deal with the awesome widget tools. Those apis aren't too bad in lua, but the syntax sugar lua benefits from does not translate into fennel. I have some examples of what this looks like. Ultimately, it's not as easy as using TailwindCSS, CLJS, and hiccup, so I moved on.

> I added SXHKD as a dependency (the keybinding tool that BSPWM uses),

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.

> Electron and then Tauri for a transparent shadow-cljs bar

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.

> major refactor: clawe/wm protocol, toggle, clawe.edn

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.

Goals

> Repl-based Development and Debugging

interactive feedback available via cider/emacs or <[]>/vscode

> Keybinding Management

writing and updating wm-level keybindings

easy to write and debug (full clojure repl!)

performant (in-lining commands)

> Client and Workspace Management

client and workspace definitions as data (config)

>> Repo-based workspace contexts

supporting contextually-aware commands

  • toggling:
  • editors
  • terminals
  • todos
  • godot
  • aseprite
  • [other apps]

>> Scratchpad/App workspaces

for quick access to journals, org, spotify, slack, todos, etc

> Speed

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

>> zero-overhead (in-lining) integrations

awesomeWM, sxhkd, i3

fnl written to awesome configs

shell commands written to sxhkd/i3 config

Integrations

  • Ralphie integrations
  • Emacs
  • Tmux
  • Git
  • Sxhkd - for keybindings
  • AwesomeWM - for some workspace/x-client interactions
  • Yabai
  • Lichess
  • Org - via orgcrud

Debugging help

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

> rewriting the bindings after pulling the latest

`clawe reload`

> starting up properly

`clawe reload`

Schemas and Naming

> Focused vs Selected

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.

Features

> Toggle Client

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.

>> TODO integrate charred to speed up json parsing in clawe

where do i have json parsing? ah, on osx.

> Create Client

:client/open supporting flexible client creation

  • via tmux.fire, browser.open, etc

> Workspace creation, swapping, focus

All the WM things

> Clawe M-x

M-x style selection of actions via Rofi or Choose (via ralphie.rofi/rofi )

> Clawe Rules

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.

> Repo Workspace Context

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

Bugs

> TODO fix default window size (tiny emacs windows?)

> TODO fix default directory (opening emacs, tmux workspaces)

> TODO clawe max width for widening clients

don't go beyond the screen's dimensions

> TODO clawe cmd-c should center and put the window back ontop/above

Wishlist

> smoother startup

  • emacs startup requires a manual id_rsa password, and `doom env` ? requires some keygen? probably can be replaced by `doom env` does that require some other zsh startup? (keygen?)

resources/clawe.edn

> TODO pull clawe.edn into dotfiles, or out of version control?

maybe a defs/{workspaces,clients}.edn makes sense or just a resources/defs.edn? resources/clawe.edn

> separate the automated .edn from the manually-maintained?

Workspace Verticals:

another workspace vertical: logs

  • workspace-logs
  • workspace-docs
  • workspace-apps (workspace-clients)
  • workspace-name
  • workspace-doctor
  • workspace-keybindings
  • workspace-layouts

these all as a list of things to start-up from rofi would be sick

Ideas

> TODO save on-the-fly window configurations

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

> workspaces with multiple repo support

i.e. apache-superset + amancevice-docker-superset treemacs gets it maybe there are primary and secondaries?

> clawe hydra interface so that hydras can do clawe-workspace-cy things

similar to not leaving vim mode, now we're not leaving the editor

  • fire godot commands
  • toggle dev browsers/consoles

maybe this also works with a fabb feature?

> start capturing workspace life-cycle

opened at, programs run, closed at

time open

show timelines and screenshots

auto-daily review

based on the telemetry

clawe db

> start tracking fast and slow functions/commands

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

in WM land, we don't need shared state, we just need smart cache-busting

e.g. bust workspaces cache when git cloning

resources

TODO display keybindings in a doctor defkbd helper

TODO generate a hydra from defkbds

bet heavy on emacs for navigating

emacs bindings can be similarly written out, as zero overhead, like fennel awm bindings

TODO add gitlab/etc support to ralphie's git namespace

and other clawe features

TODO icons for workspaces/apps pulled from wm/xprop/whatever api

TODO ideas/bucket note-focused review tool

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


Backlinks

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:

  • write/edit toggle-app-scratchpad clients
  • app-names for matching
  • init-defs for starting apps with specific inputs

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 Repo link

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.