A free-form shell-string --exec sitting next to the safe --exec-arg is a
standing invitation for the next caller to interpolate untrusted data and
reintroduce the RCE. Remove it: omarchy-notification-send --exec now errors and
points at --exec-arg, and the shell drops the omarchy-exec string hint and its
bash -lc execution path, leaving only the argv path.
Migrate the remaining string callers (the first-run invitation hooks, wifi and
welcome prompts) to --exec-arg, and update their notification mocks. Trim the
verbose security comments added along the way.
Quickshell.execDetached(argv) ran the click target with only the shell
process's stripped environment, so GUI actions like the screenshot editor
(tensaku-edit) — resolved on the login-shell PATH the old `bash -lc` string
exec provided — stopped launching on click.
Run the argv through `bash -lc 'exec "$@"'` instead: the script text is a
constant and the arguments are passed as positional parameters, which bash
expands without re-tokenizing or re-evaluating, so injection safety is intact
while PATH and session env match the old behavior exactly.
The click action of a notification was a free-form shell string run through
`bash -lc`, safe only when every sender shell-quoted every interpolated value
perfectly. One slip is RCE: a hostile yt-dlp video title forged an output
record and injected an mpv option into the click command (mehmetince.net RCE,
partially addressed by #7847).
Add a parameterized transport: omarchy-notification-send gains --exec-arg
(repeatable), encoding a JSON argv into the omarchy-exec-argv hint. The shell
runs it with Quickshell.execDetached(argv) and no shell, so data an attacker
controls is only ever one argument and can never be reparsed as a command. The
shell fails closed on a malformed argv hint.
The legacy free-form --exec string is retained but honored only from Omarchy's
own omarchy-action toasts, and deprecated. Migrate all in-repo callers
(screenshot, screen recording, taildrop receive, migrate-notify, crash-watch,
yt-dlp host) to --exec-arg. Update docs and tests.
* Show OSD and sync touchpad steps when scrolling bar widgets
Scrolling the volume or brightness icon in the bar changed the value
directly without ever calling omarchy-osd, so only the keyboard media
keys showed the popup. On top of that, a touchpad's stream of many
small wheel events per finger-drag wasn't matched to a mouse's single
±120 notch per click, so touchpad scrolling felt uncoordinated and
uneven next to the keyboard/mouse behavior.
- shell/plugins/panels/audio/Panel.qml: accumulate raw wheel delta and
only apply a step once it crosses a full mouse-notch's worth, so
touchpad and mouse move the volume in identical 5% increments, and
ping the OSD each time a step actually lands.
- shell/plugins/panels/monitor/Panel.qml: same accumulator for
brightness, with a 5% floor so scrolling down can never blank the
screen.
- shell/plugins/osd/Osd.qml: animate the progress bar's width instead
of snapping, so successive steps glide smoothly.
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Fix bar wheel OSD behavior
* Normalize scaled wheel events
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: David Heinemeier Hansson <david@hey.com>
Read ~/.config/omarchy/shell.toml as a user override merged over the active
theme's shell values, so a personal font/spacing/bar tweak survives theme
switches. The file is watched, so edits re-flow the shell live.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* emoji panel: handle CTRL+BACKSPACE to clear filter
* shell: handle CTRL+BACKSPACE to clear filter in all search overlays
* Indentation fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* menu filters: align keyboard editing with Qt standard shortcuts
* menu filters: extract shared filter-editing helpers
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* potential fix for pull request findings
* Only treat filter edit keys as edits when they change the text
Backspace/Ctrl+U on an empty filter no longer calls setFilter(""),
which was resetting the list selection back to the top. This also lets
the menu's empty-filter Backspace fall through to goBack() with any
modifier held, as it did before the Util extraction.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: David Heinemeier Hansson <david@hey.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
With initial workspace tracking disabled, windows naturally open on the active workspace. Remove the explicit Hyprland workspace dispatch and let shell actions, shell restarts, and presentation terminals launch directly.
The canonical colors.toml keys are now background/foreground, including
all permutations (dark_background, darker_background, lighter_background,
dark_foreground, light_foreground, bright_foreground). The old short
names are gone entirely, not kept as aliases; legacy alacritty-generated
themes still resolve through the ANSI color0/color7 fallback.
Theme files are also regrouped: accent/selection/muted first, then the
backgrounds, then the foregrounds, then the named colors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Hyprland gaps_out value (10px out of the box) reads well as a
window-to-window gap but felt cavernous when used as the screen-edge
margin for panels and notifications. Halve it at the source — panels
that anchor via PopupCard.margin, the launcher / clipboard / emojis /
polkit / menu / settings card-width clamps, and the notification popup
margins all read Style.gapsOut, so one tweak moves them in lockstep.
Revert the per-call /2 in the notifications service, since
Style.gapsOut now already encodes the smaller value.
TextField, NumberField, Dropdown, SearchableDropdown, and Toggle all
painted their background, border color, and border width with the same
three-line ternary ladder (`_focused ? focusFill : _hot ? hoverFill :
normalFill`). Five components × three properties × three branches is a
lot of room for one of them to drift from the others when a new state
ever gets added.
Add controlFill / controlBorder / controlBorderWidth on Style and rewrite
each call site as a single binding. No visual change.
Five plugins (menu, launcher, clipboard, emojis, polkit) each looked up
OMARCHY_MENU_FONT with the same env-or-monospace fallback. Hoist the
resolution into Style so plugins read one source of truth, and so the
fallback follows the real fontconfig family Style already tracks instead
of the literal string "monospace".
CustomCommandModule.update() in Bar.qml called root.parseModuleJson(raw)
but the function was removed in 3b970068 (the indicator split). Any
custom command module emitting waybar-style JSON would have crashed on
first poll.
Hoist the helper into Util so both call sites — the bar's custom command
module and BarIndicator.extractData — share one definition.
The three components were three takes on the same shape \u2014 a clickable
rectangle with text/icon, a hot/hover state, an optional persistent
border, and an optional 'selected' or 'active' highlight. CursorPill
was a 30-line PillButton wrapper that added a hovered() signal;
ChoiceButton was effectively PillButton with selected: bool painting
an accent fill+border.
Collapse them into a single qs.Ui.Button. State flags compose
independently:
hasCursor / hover hot fill
active persistent foreground-tint fill
selected accent fill + accent border
bordered: true persistent 1px idle border (form primaries)
focusable: true Tab focus paints the accent ring
pressed pressed fill
The hovered(bool) signal is now built-in, so CursorPill's wrapper is
unnecessary. ButtonGroup wraps a Row+Repeater for the form-style
'pick one of N' pattern; panel-cursor-driven cases still compose
Buttons directly in a Row with per-instance hasCursor wiring.
Theme tokens move into a new [style] section in shell.toml:
border-width = 1
focus-border-width = 3
idle-border-alpha = 0.4
hot-fill-alpha = 0.08
selected-fill-alpha = 0.18
pressed-fill-alpha = 0.22
focus-fill-alpha = 0.22
Style.qml parses these out of the same shell.toml [font] / [bar]
already reads, and exposes pre-computed Style.hotFill / selectedFill /
pressedFill / idleBorderColor / selectedAccentFill / borderWidth +
the existing focusBorder* tokens. Themes that don't ship a [style]
section get the previous defaults unchanged.
Dev gallery consolidates three sections (PillButton, CursorPill,
ChoiceButton) into Button + ButtonGroup, with the cursor model
sections renamed accordingly.
Themes now drive typography the same way they drive colors: one [font]
base-size in shell.toml is the rem root, and every Style.font.<token>
(caption, bodySmall, body, subtitle, title, heading, display,
displayLarge, iconSmall, icon, iconLarge) derives from it via a fixed
multiplier. Themes can also pin individual tokens for stylistic
emphasis. base-size is clamped 11..13 until row-height tokens exist.
Bar dimensions move to the same singleton: [bar] size-horizontal /
size-vertical replace the hardcoded 26/28 in Bar.qml, exposed as
Style.bar.sizeHorizontal / sizeVertical.
Style.qml also resolves the fontconfig 'monospace' alias via fc-match
and exposes Style.font.resolvedFamily so panels can display the
concrete family. Watches ~/.config/fontconfig/fonts.conf so it tracks
'omarchy font set <name>'.
The qs.Ui kit (PillButton, Dropdown, Toggle, TextField, etc.) and
every first-party plugin (bar widgets, settings, menu, clipboard,
emoji, polkit, notifications, osd, image-picker, dev-gallery) now
bind to Style.font.* instead of pixel literals. Only three deliberate
display-scale outliers remain: the notification empty-state glyph and
the weather flyout's hero temperature pair, all commented.
Background plugin's applyTheme IPC fast-path also pushes shell.toml to
Style so theme swaps update typography and bar size without waiting
for inotify debounce.
Dev gallery (omarchy dev ui-preview) now ships a Typography section
that renders the full scale and theme tokens live, and its summon
command is fixed (omarchy-shell-ipc -> omarchy-shell).