screenie
x 1.00 · purpose 0.20 · t 1 s · scope 0.85
from 2026-09
Rust · Hyprland · Wayland · OCR · CLI
Working in the terminal made me increasingly reluctant to reach for the mouse for small repetitive tasks. Screenie is a keyboard-driven screenshot tool for Hyprland: it freezes a frame, then lets me select, inspect, edit and extract from it using a consistent set of commands.
the selection
A scripted screenie session, 41 keyframes: the box starts on the active window, jumps between windows, widens and narrows its scope, is halved and refined corner by corner, then a loupe picks a pixel, a rectangle, a numbered callout and an arrow lock into one layer and a censor into a second, and Enter copies the 440 × 280 box to the clipboard.
data
| step | stage | keys | what happens | box (x, y, w × h) |
|---|---|---|---|---|
| 1 | invoke | the frame freezes; the box starts on the active window | 960, 10, 950 × 620 | |
| 2 | select | ctrl+h | the window to the left | 10, 10, 940 × 1060 |
| 3 | select | ctrl+l | back to the browser | 960, 10, 950 × 620 |
| 4 | select | + | widen the scope to the monitor | 0, 0, 1920 × 1080 |
| 5 | select | - | back to the window | 960, 10, 950 × 620 |
| 6 | refine | alt+k | keep the top half | 960, 10, 950 × 310 |
| 7 | refine | alt+h | keep the left half | 960, 10, 475 × 310 |
| 8 | refine | h ×3 | the cursor corner in 15px | 960, 10, 460 × 310 |
| 9 | refine | k ×2 | and up 10px | 960, 10, 460 × 300 |
| 10 | refine | o | the opposite corner becomes the cursor | 960, 10, 460 × 300 |
| 11 | refine | l ×4 | the left edge in 20px | 980, 10, 440 × 300 |
| 12 | refine | j ×4 | the top edge down 20px; the box is the card | 980, 30, 440 × 280 |
| 13 | inspect | z | the loupe, at the box's center | 980, 30, 440 × 280 |
| 14 | inspect | shift+j | a loupe width down | 980, 30, 440 × 280 |
| 15 | inspect | shift+j | again | 980, 30, 440 × 280 |
| 16 | inspect | shift+l | right, onto the peak | 980, 30, 440 × 280 |
| 17 | inspect | z | close the loupe; it remembers the pixel | 980, 30, 440 × 280 |
| 18 | annotate | a | annotate opens layer 1, clipped to the box | 980, 30, 440 × 280 |
| 19 | annotate | r | a rectangle, starting at the loupe's pixel | 980, 30, 440 × 280 |
| 20 | annotate | l ×10 | 50px wide | 980, 30, 440 × 280 |
| 21 | annotate | j ×6 | 30px tall | 980, 30, 440 × 280 |
| 22 | annotate | a | lock it, back to the pen | 980, 30, 440 × 280 |
| 23 | annotate | n | callout 1 at the pen point | 980, 30, 440 × 280 |
| 24 | annotate | u | undo removes the callout, nothing else | 980, 30, 440 × 280 |
| 25 | annotate | j ×8 | the pen point down 40px | 980, 30, 440 × 280 |
| 26 | annotate | h ×4 | left 20px | 980, 30, 440 × 280 |
| 27 | annotate | n | callout 1 again | 980, 30, 440 × 280 |
| 28 | annotate | v | an arrow from the loupe's pixel | 980, 30, 440 × 280 |
| 29 | annotate | h ×8 | the tip out 40px | 980, 30, 440 × 280 |
| 30 | annotate | j ×8 | and down 40px | 980, 30, 440 × 280 |
| 31 | annotate | o | swap ends; the head points at the peak | 980, 30, 440 × 280 |
| 32 | annotate | enter | lock the arrow, commit layer 1 | 980, 30, 440 × 280 |
| 33 | censor | x | a censor opens layer 2, a 20px square at the center | 980, 30, 440 × 280 |
| 34 | censor | alt+shift+l | double it rightward | 980, 30, 440 × 280 |
| 35 | censor | alt+shift+l | again, over the label | 980, 30, 440 × 280 |
| 36 | censor | enter | commit layer 2 (pixelate, 10px blocks) | 980, 30, 440 × 280 |
| 37 | layers | 1 | layer 1 on its own | 980, 30, 440 × 280 |
| 38 | layers | space | hide it | 980, 30, 440 × 280 |
| 39 | layers | u | undo the hide | 980, 30, 440 × 280 |
| 40 | layers | esc | back to select | 980, 30, 440 × 280 |
| 41 | export | enter | the box with both layers, to the clipboard | 980, 30, 440 × 280 |
It begins as a frozen display plane with a thin selection rectangle. Each keystroke changes a corner or snaps the boundary to something meaningful, and a crosshair, pixel loupe and faint guides make the operation legible. Mouse interaction remains possible, but the keyboard can perform the whole workflow.
nothing is burned in
24 keyframes of edits as layers over an untouched 440 × 280 capture. 2 layers end up committed (rectangle 50×30, callout 1, arrow; pixelate 80×20, 10px); one undo removes a single callout, and hiding a layer in the Layers view is undone the same way.
data
| step | keys | open layer | committed layers |
|---|---|---|---|
| 1 | a | (empty) | |
| 2 | r | (empty) | |
| 3 | l l l l l l l l l l | (empty) | |
| 4 | j j j j j j | (empty) | |
| 5 | a | rectangle 50×30 | |
| 6 | n | rectangle 50×30, callout 1 | |
| 7 | u | rectangle 50×30 | |
| 8 | j j j j j j j j | rectangle 50×30 | |
| 9 | h h h h | rectangle 50×30 | |
| 10 | n | rectangle 50×30, callout 1 | |
| 11 | v | rectangle 50×30, callout 1 | |
| 12 | h h h h h h h h | rectangle 50×30, callout 1 | |
| 13 | j j j j j j j j | rectangle 50×30, callout 1 | |
| 14 | o | rectangle 50×30, callout 1 | |
| 15 | Return | 1: rectangle 50×30, callout 1, arrow | |
| 16 | x | (empty) | 1: rectangle 50×30, callout 1, arrow |
| 17 | <A-S-l> | (empty) | 1: rectangle 50×30, callout 1, arrow |
| 18 | <A-S-l> | (empty) | 1: rectangle 50×30, callout 1, arrow |
| 19 | Return | 1: rectangle 50×30, callout 1, arrow; 2: pixelate 80×20, 10px | |
| 20 | 1 | 1: rectangle 50×30, callout 1, arrow; 2: pixelate 80×20, 10px | |
| 21 | space | 1: rectangle 50×30, callout 1, arrow (hidden); 2: pixelate 80×20, 10px | |
| 22 | u | 1: rectangle 50×30, callout 1, arrow; 2: pixelate 80×20, 10px | |
| 23 | Escape | 1: rectangle 50×30, callout 1, arrow; 2: pixelate 80×20, 10px | |
| 24 | Return | 1: rectangle 50×30, callout 1, arrow; 2: pixelate 80×20, 10px |
After selection, annotations and censoring become operations on a layer stack rather than permanent modifications to the source frame. The stack expands to reveal text, arrows, shapes and filters, and an undo is the removal of an operation, not a visual reset.
inspect and extract
Four results from the same box: pixel (1221,212), region (image 440×280, 2 layers), text (ocr text of 440×280), distance (440 × 280 px). Each goes to the clipboard.
data
| yield | keys | result |
|---|---|---|
| pixel | z shift+j shift+j shift+l z | pixel (1221,212) |
| region | enter | image 440×280, 2 layers |
| text | print . y | ocr text of 440×280 |
| distance | print . m r enter y | 440 × 280 px |
The picker also lets me inspect colors and pixels, extract text and make geometric measurements. It can be driven from scripts as well as through its interactive overlay.