Hal Overlay¶
A keyboard-only "notch" that hangs from the top of the screen and drives opencode from one
line of text: type, Enter, watch the answer stream in. Built 2026-10-24 in
~/Projects/hal-overlay/, inspired by Void minus voice.
Overview¶
┌──────────────────────────────────────────────┐
│ ◉ HAL esc │
│ idle │
│ ❯ fix the failing tests │
│ ⚙ bash │
│ Done — the assertion in test_x was stale… │
│ ❯ ask hal… enter │
└──────────────────────────────────────────────┘
| piece | implementation |
|---|---|
| window | GTK4 + gtk4-layer-shell, layer OVERLAY, anchored top, keyboard grabbed only while visible |
| face | WebKit view of face.html (Hal eye as inline SVG, CSS states: idle/thinking/working/error) |
| agent | opencode run --format json --auto [--session sid] <prompt>, NDJSON streamed |
| session | sessionID from the first event reused for follow-ups |
| height | ResizeObserver in the page reports content height; the window resizes to fit |
Details¶
Usage¶
~/Projects/hal-overlay/hal # toggle
~/Projects/hal-overlay/hal --start-hidden # bind socket, no surface (autostart)
~/Projects/hal-overlay/hal --quit
~/Projects/hal-overlay/hal --dir ~/Projects/foo
Single instance via $XDG_RUNTIME_DIR/hal-overlay.sock, so a compositor keybind is instant.
It appears on whichever monitor is focused.
| env | default | meaning |
|---|---|---|
HAL_DIR |
~ |
working directory for opencode run |
HAL_MODEL |
opencode default | passed as --model provider/model |
HAL_TOP_MARGIN |
-48 |
Hyprland offsets top-anchored layer surfaces 48 px; -48 = flush to y=0, 26 = hang under the 74 px bar |
Wayland gotchas (the expensive part)¶
LD_PRELOAD=/usr/lib/libgtk4-layer-shell.sois mandatory. Otherwise the surface silently degrades to a normal toplevel withFailed to initialize layer surface, GTK4 Layer Shell may have been linked after libwayland. Thehalwrapper sets it and strips it again before spawningopencode.- webkit2gtk-4.1 is GTK3 — with GTK4 you must use
WebKit 6.0(webkitgtk-6.0). - GTK4 has no
Gtk.main()— useGLib.MainLoop(). - Hyprland puts top-anchored layer surfaces 48 px down; a negative margin is the workaround.
Design decisions¶
- No voice. Dropped faster-whisper/Kokoro/PTT entirely — keyboard only.
--autois hardcoded, so tool calls are never prompted for. An approval bubble is the obvious next step if it starts being pointed at unfamiliar repos.- The face is HTML because Void's is; that makes the eye trivially animatable and swappable
(see the portrait in
~/Pictures/hal-portrait.svg).
Status / next ideas¶
Done: overlay, streaming replies, session continuity, auto-height, --start-hidden, --quit.
Open: keybind in keybinds.lua, approval bubble, prompt history, /model + /agent switches,
tool-call detail (command + diffstat), theme sync with Noctalia's palette, selection → prompt.
Related¶
- [[Index]]
- [[Kitty Custom Shaders]]
- [[Hyprland Tweaks]]
- [[OpenCode Voice Input]] — the voice-based idea this deliberately skips