feat(regionSelector): adopt satty as default annotation tool

Replace the swappy edit flow with satty (preferred), driven by a bundled
config (satty -c ~/.config/hypr-lens/satty.toml) plus per-mode CLI flags,
so the edit action saves to the hypr-lens dir, copies to the clipboard,
and discards on Escape natively - with no XDG_CONFIG_HOME override, so GTK
theming stays native. Falls back to a bare swappy invocation when satty is
disabled or not installed (regionSelector.annotation.useSatty, default
true). satty owns its own notifications. Mirrors end-4's ScreenshotAction
tool toggle with the default flipped to satty.

Note: installer wiring (satty optional dep + bundled-config install) lives
in hypr-lens-install.sh, left unstaged here to avoid bundling unrelated
in-progress edits to that file.
This commit is contained in:
DJ committed 2026-06-06 14:43:15 -04:00
1 parent ee4849393f
commit a0f3b14039
5 files changed
+58 -12

No files matched your search

+10
View File
@@ -135,6 +135,16 @@ Circle selection tool settings.
| `strokeWidth` | `6` | Circle outline thickness |
| `padding` | `10` | Extra space around selection |
### regionSelector.annotation
Annotation tool used by the **Edit** action.
| Setting | Default | Description |
|---------|---------|-------------|
| `useSatty` | `true` | `true` = annotate with [satty](https://github.com/gabm/Satty) (preferred); `false` = use swappy. Falls back to swappy automatically if satty isn't installed. |
With satty, **Enter** (or the copy/save toolbar buttons) keeps the edit — copying to the clipboard and, when `screenSnip.copyAlsoSaves` is `true`, saving into the hypr-lens save dir — while **Escape** discards it. satty is configured via the bundled `~/.config/hypr-lens/satty.toml` (edit it to taste; it never touches your own `~/.config/satty`). The swappy fallback is a plain hand-off — swappy uses its own Save/Copy buttons and its own save directory.
---
## Tips
+3
View File
@@ -41,6 +41,9 @@
"circle": {
"strokeWidth": 6,
"padding": 10
},
"annotation": {
"useSatty": true
}
}
}
+23
View File
@@ -0,0 +1,23 @@
# hypr-lens bundled satty configuration (annotation tool).
#
# Installed to ~/.config/hypr-lens/satty.toml and passed to satty via `satty -c`.
# This does NOT touch your personal ~/.config/satty/config.toml, and editing it
# only affects hypr-lens's "edit" action. Targets satty 0.20.x syntax.
#
# Save behaviour is controlled per-invocation by hypr-lens via CLI flags
# (`-o` / `--actions-on-enter`), so `output-filename` is intentionally omitted
# here (omitting it disables saving unless hypr-lens passes `-o`).
[general]
# Copy uses the Wayland clipboard.
copy-command = "wl-copy"
# Enter copies to the clipboard; hypr-lens adds save-to-file via CLI when copy-also-saves is on.
actions-on-enter = ["save-to-clipboard"]
# Escape throws the edit away (no save, no copy).
actions-on-escape = ["exit"]
# Close satty as soon as a copy/save action fires.
early-exit = true
# Start on the brush tool.
initial-tool = "brush"
# Rounded rectangle corners (px).
corner-roundness = 12
+4
View File
@@ -103,6 +103,10 @@ Singleton {
property int strokeWidth: 6 // Outline thickness
property int padding: 10 // Extra space around selection
}
// Annotation tool for the "edit" action
property JsonObject annotation: JsonObject {
property bool useSatty: true // true = satty (preferred), false = swappy. Falls back to swappy if satty isn't installed.
}
}
}
}
@@ -78,34 +78,40 @@ Singleton {
];
}
// Edit with swappy (respects copyAlsoSaves setting)
// Edit action: annotate with satty (preferred) or swappy.
// satty runs under a bundled config (~/.config/hypr-lens/satty.toml via -c) plus per-mode
// flags, so it saves to the hypr-lens dir, copies to the clipboard and discards on Escape -
// with NO XDG_CONFIG_HOME override, so GTK theming stays native. satty shows its own
// notifications. Falls back to a bare swappy invocation (end-4 style) when satty is disabled
// or not installed.
function buildEditCommand(screenshotPath: string, rx: int, ry: int, rw: int, rh: int, saveDir: string, alsoSave: bool): list<string> {
const cropBase = buildCropBase(screenshotPath, rx, ry, rw, rh);
const cropToStdout = `${cropBase} -`;
const cleanup = buildCleanup(screenshotPath);
// swappy -o - prints its final surface to stdout on exit (independent of swappy's
// Save button, which is hardwired to swappy's own save_dir). Capture that here so the
// result lands in hypr-lens's clipboard/save dir. Mirrors buildCopyCommand.
// Annotation tool disabled: bare swappy (end-4 style; swappy owns its own save/copy).
if (!Config.options.regionSelector.annotation.useSatty) {
return ["bash", "-c", `${cropToStdout} | swappy -f -; ${cleanup}`];
}
const sattyCfg = StringUtils.shellSingleQuoteEscape(Directories.shellConfig + "/satty.toml");
if (!alsoSave) {
const sattyCmd = `satty -c '${sattyCfg}' -f - --copy-command 'wl-copy' --actions-on-escape exit`;
return [
"bash", "-c",
`${cropToStdout} | swappy -f - -o - | wl-copy && \
{ ${buildNotify("Copied to clipboard", "")}; } && \
`${cropToStdout} | if command -v satty >/dev/null 2>&1; then ${sattyCmd}; else swappy -f -; fi; \
${cleanup}`
];
}
const expandedSaveDir = resolveSavePath(saveDir);
// Save mode: satty writes the annotated image to $savePath on its save action.
// The swappy fallback is bare and saves via its own mechanism (ignores $savePath).
const sattyCmd = `satty -c '${sattyCfg}' -f - -o "$savePath" --copy-command 'wl-copy' --actions-on-enter save-to-clipboard,save-to-file --actions-on-escape exit`;
return [
"bash", "-c",
`${buildSaveSetup(expandedSaveDir)} && \
${cropToStdout} | swappy -f - -o - | tee >(wl-copy) > "$savePath" && \
if [ -s "$savePath" ]; then \
${buildNotify("Copied & saved", "$savePath")}; \
else \
rm -f "$savePath"; ${buildNotify("Copy failed", "")}; \
fi && \
${cropToStdout} | if command -v satty >/dev/null 2>&1; then ${sattyCmd}; else swappy -f -; fi; \
${cleanup}`
];
}