Compare commits

..
Author SHA1 Message Date
David Heinemeier Hansson 61d36a3ab5 AI prompt experiments 2026-05-08 14:18:39 +02:00
957 changed files with 8755 additions and 56005 deletions
-11
View File
@@ -1,11 +0,0 @@
{
"workspace": {
"library": [
"/usr/share/hypr/stubs"
],
"checkThirdParty": false
},
"diagnostics": {
"globals": ["hl"]
}
}
+16 -77
View File
@@ -31,7 +31,7 @@ Common prefixes include:
Other current prefixes include:
- `ac-`, `audio-`, `battery-`, `branch-`, `brightness-`, `channel-`, `config-`, `debug-`, `dev-`, `drive-`, `first-`, `font-`, `haptic-`, `hibernation-`, `hook-`, `hyprland-`, `menu-`, `migrate-`, `notification-`, `npm-`, `plymouth-`, `powerprofiles-`, `reinstall-`, `remove-`, `screensaver-`, `show-`, `snapshot-`, `state-`, `sudo-`, `system-`, `transcode-`, `tui-`, `tz-`, `upload-`, `version-`, `voxtype-`, `webapp-`, `wifi-`, `windows-`
- `ac-`, `audio-`, `battery-`, `branch-`, `brightness-`, `channel-`, `config-`, `debug-`, `dev-`, `drive-`, `first-`, `font-`, `haptic-`, `hibernation-`, `hook-`, `hyprland-`, `menu-`, `migrate-`, `notification-`, `npx-`, `plymouth-`, `powerprofiles-`, `reinstall-`, `remove-`, `screensaver-`, `show-`, `snapshot-`, `state-`, `sudo-`, `swayosd-`, `system-`, `transcode-`, `tui-`, `tz-`, `upload-`, `version-`, `voxtype-`, `webapp-`, `wifi-`, `windows-`
# Command Metadata
@@ -40,38 +40,26 @@ Commands in `bin/` can declare CLI metadata in comments near the top of the file
Supported metadata keys:
- `# omarchy:summary=...` - short help text
- `# omarchy:group=...` - command group when it differs from the filename-derived prefix
- `# omarchy:name=...` - command name within the group
- `# omarchy:args=...` - usage arguments
- `# omarchy:examples=...` - examples separated with ` | `
- `# omarchy:alias=...` / `# omarchy:aliases=...` - alternate routes
- `# omarchy:hidden=true` - hide from default command listings
- `# omarchy:requires-sudo=true` - mark commands that require sudo
Only use `omarchy:examples` where there are args that need explaining.
Prefer explicit metadata for user-facing commands. Keep routes consistent with the filename unless there is a deliberate alias or compatibility route.
Example:
```bash
# omarchy:summary=Take a screenshot
# omarchy:group=capture
# omarchy:args=[smart|region|windows|fullscreen] [slurp|copy]
# omarchy:examples=omarchy screenshot | omarchy capture screenshot region
# omarchy:aliases=omarchy screenshot
```
# Runtime Environment
- `$OMARCHY_PATH` is set at the top level by the uwsm session environment and is always available to Omarchy runtime code.
- Commands in `bin/` and Quickshell QML should rely on `$OMARCHY_PATH` / `Quickshell.env("OMARCHY_PATH")`; do not derive fallback paths from `HOME`, `Quickshell.shellDir`, or re-export/default `OMARCHY_PATH` manually.
# Privileged Commands
- Whenever you need to trigger a sudo command, use `pkexec` so it results in a user prompt they can approve.
# Git
- Commits should be atomic: include only one coherent change or fix, and do not mix unrelated work.
- Commit messages should be succinct and describe the change being made.
# Install Scripts
Install entry points (`install.sh`, `boot.sh`) use `#!/bin/bash`. Many scripts under `install/` are sourced via `run_logged` and intentionally do not have shebangs.
@@ -92,10 +80,8 @@ Raw `command -v`, `pacman`, and `pacman-key` are acceptable in bootstrap/preflig
Use these instead of raw shell commands:
- `omarchy-cmd-missing` / `omarchy-cmd-present` - check for commands
- `omarchy-pkg-missing` / `omarchy-pkg-present` - check for packages (don't use these if you can just use `omarchy-pkg-add`/`omarchy-pkg-drop`)
- `omarchy-pkg-missing` / `omarchy-pkg-present` - check for packages
- `omarchy-pkg-add` - install packages (handles both pacman and AUR)
- `omarchy-pkg-drop` - remove packages; use this instead of raw `pacman -R*`
- `omarchy-notification-send` - send desktop notifications; do not call `notify-send` directly
- `omarchy-hw-asus-rog` - detect ASUS ROG hardware (and similar `hw-*` commands)
Exceptions are allowed for bootstrap, preflight, migration, and package-helper scripts where the helper may not be available yet, where the helper itself is being implemented, or where direct package-manager behavior is required.
@@ -104,63 +90,7 @@ Exceptions are allowed for bootstrap, preflight, migration, and package-helper s
- `config/` - default configs copied to `~/.config/`
- `default/themed/*.tpl` - templates with `{{ variable }}` placeholders for theme colors
- `themes/*/colors.toml` - theme color definitions (accent, background, foreground, red/green/yellow/blue/magenta/cyan and bright_* variants)
# Tests
Run focused automated tests for the area you changed. Current test entry points:
- `./test/all` - aggregate runner for CLI and shell tests
- `./test/cli` - CLI routing, command metadata, theme helpers, and safe dispatch coverage
- `./test/shell` - all Omarchy shell tests under `test/shell.d/`
New Omarchy shell tests should live in `test/shell.d/*-test.sh` so `./test/shell` picks them up automatically. Source `test/shell.d/base-test.sh` for shared root-path discovery, assertions, and Node test helpers.
For visual changes, such as omarchy-shell styling, desktop appearance, screenshots, or screen recording flows, verify with the running UI in addition to automated tests. Take and analyze screenshots with `omarchy capture screenshot fullscreen save`. For animation, transitions, capture, or screen recording behavior, make a short recording with `omarchy screenrecord --fullscreen`, stop it with `omarchy screenrecord --stop-recording`, and review the output before finishing.
For interactive UI work, use `wtype` to simulate keyboard input when available. Example: start the UI in the background, wait briefly for focus, then run `wtype -k Right -k Return` to exercise keyboard selection and confirm the resulting command output or state change. Prefer this over manual-only verification when a UI returns a selected value or changes a symlink/config.
When testing layer-shell UI, capture the reference and candidate states as separate screenshots, then compare them visually before further edits. If a launched UI would otherwise remain open, keep track of its PID and stop it after the screenshot; avoid broad process kills unless checking with `ps` first.
# Omarchy shell
The Quickshell desktop runs as a single long-running process out of
`shell/`. Hyprland autostart launches it directly with `quickshell -p`; do
not start additional standalone `quickshell -p` instances for individual
components.
Run `omarchy-restart-shell` after making changes to QML files.
Plugin contract:
- Each plugin lives in its own directory under
`shell/plugins/<id>/` (first-party) or
`~/.config/omarchy/plugins/<id>/` (third-party).
- Every plugin ships a `manifest.json` declaring `id`, `kinds`,
`activation`, and `entryPoints`. The full schema is in
[`docs/omarchy-shell.md`](docs/omarchy-shell.md).
- Entry-point QML files are `Item`s (not `ShellRoot`), and accept the
shell-injected properties `omarchyPath`, `shell`, `manifest`, and
`pluginRegistry` / `barWidgetRegistry` as appropriate.
- Panel / overlay / menu plugins must expose `open(payloadJson)` and
`close()` lifecycle methods for `shell summon` and `shell hide`.
IPC:
- `bin/omarchy-shell` is the canonical IPC entry point. It forwards to
the running shell and does not start it. Prefer it over re-implementing
direct Quickshell socket calls in every CLI.
- The `shell` IPC target exposes `ping`, `summon`, `hide`, `toggle`,
`rescanPlugins`, `setPluginEnabled`, and `listPlugins`. Individual
plugins can register additional IPC targets (the bar registers `bar`,
the background switcher registers `image-selector`).
Widget files in `shell/plugins/bar/widgets/` contain Nerd Font glyphs as raw
unicode characters. The `Write` and `Edit` tools strip multi-byte
codepoints in some positions — do **not** rewrite widget files wholesale
through those tools. For glyph fixes, use the targeted `Edit` tool with
the surrounding context, or a Python script that inserts codepoints via
`chr(0xXXXXX)`.
- `themes/*/colors.toml` - theme color definitions (accent, background, foreground, color0-15)
# Refresh Pattern
@@ -186,3 +116,12 @@ New migration format:
Some older migrations predate these rules. Do not copy older migrations that start with shebangs, omit the leading `echo`, or hard-code `~/.local/share/omarchy`.
Migrations may use raw `pacman`, `command -v`, or direct config edits when needed for historical compatibility or one-off repair work.
Example:
```bash
echo "Disable fingerprint in hyprlock if fingerprint auth is not configured"
if omarchy-cmd-missing fprintd-list || ! fprintd-list "$USER" 2>/dev/null | grep -q "finger"; then
sed -i 's/fingerprint:enabled = .*/fingerprint:enabled = false/' ~/.config/hypr/hyprlock.conf
fi
```
-4
View File
@@ -4,10 +4,6 @@ Omarchy is a beautiful, modern & opinionated Linux distribution by DHH.
Read more at [omarchy.org](https://omarchy.org).
## Docs
- [omarchy-shell](docs/omarchy-shell.md) — shell host, plugin manifest, IPC, `shell.json`, custom bar modules
## License
Omarchy is released under the [MIT License](https://opensource.org/licenses/MIT).
-11
View File
@@ -1,11 +0,0 @@
[Desktop Entry]
Type=Application
Name=Battle.net
GenericName=Game Launcher
Comment=Blizzard game launcher (umu-launcher + GE-Proton)
Exec=omarchy-launch-battlenet
Icon=Battle.net
Terminal=false
Categories=Game;
StartupNotify=true
StartupWMClass=battle.net.exe
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
+2
View File
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
+2
View File
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
+2
View File
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
+2
View File
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
+2
View File
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
+2
View File
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
+2
View File
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
+2
View File
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
+2
View File
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
+2
View File
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
+2
View File
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
+2
View File
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
+2
View File
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
+2
View File
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
+2
View File
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
+2
View File
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
+2
View File
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
+2
View File
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
+2
View File
@@ -0,0 +1,2 @@
[Desktop Entry]
Hidden=true
Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

+4 -14
View File
@@ -27,24 +27,19 @@ declare -A BINARY_TO_KEY
declare -A GROUP_DESCRIPTIONS
GROUP_DESCRIPTIONS[ac]="AC power detection"
GROUP_DESCRIPTIONS[audio]="Audio input and output controls"
GROUP_DESCRIPTIONS[battery]="Battery status helpers"
GROUP_DESCRIPTIONS[bluetooth]="Bluetooth device controls"
GROUP_DESCRIPTIONS[branch]="Omarchy git branch management"
GROUP_DESCRIPTIONS[branding]="About and screensaver branding"
GROUP_DESCRIPTIONS[brightness]="Display and keyboard brightness"
GROUP_DESCRIPTIONS[capture]="Screenshots and screen recording"
GROUP_DESCRIPTIONS[channel]="Omarchy release channel management"
GROUP_DESCRIPTIONS[clipboard]="Clipboard helpers"
GROUP_DESCRIPTIONS[ai]="AI prompt helpers"
GROUP_DESCRIPTIONS[cmd]="Command and shortcut helpers"
GROUP_DESCRIPTIONS[config]="System configuration helpers"
GROUP_DESCRIPTIONS[debug]="Diagnostics and support logs"
GROUP_DESCRIPTIONS[default]="Default application selection"
GROUP_DESCRIPTIONS[dev]="Omarchy development tools"
GROUP_DESCRIPTIONS[dns]="DNS resolver configuration"
GROUP_DESCRIPTIONS[drive]="Drive selection and encryption"
GROUP_DESCRIPTIONS[font]="Font management"
GROUP_DESCRIPTIONS[games]="Game launchers and helpers"
GROUP_DESCRIPTIONS[hibernation]="Hibernation setup and removal"
GROUP_DESCRIPTIONS[hook]="User hook runner"
GROUP_DESCRIPTIONS[hw]="Hardware detection and controls"
@@ -53,13 +48,9 @@ GROUP_DESCRIPTIONS[install]="Optional software installers"
GROUP_DESCRIPTIONS[launch]="Application launchers"
GROUP_DESCRIPTIONS[menu]="Omarchy menu commands"
GROUP_DESCRIPTIONS[migrate]="Migration runner"
GROUP_DESCRIPTIONS[monitor]="Monitor status helpers"
GROUP_DESCRIPTIONS[network]="Network status helpers"
GROUP_DESCRIPTIONS[notification]="Notification helpers"
GROUP_DESCRIPTIONS[mise]="Mise tool wrappers"
GROUP_DESCRIPTIONS[osd]="On-screen display status helpers"
GROUP_DESCRIPTIONS[npx]="NPX package wrappers"
GROUP_DESCRIPTIONS[pkg]="Package management helpers"
GROUP_DESCRIPTIONS[plugin]="Omarchy shell plugin and bar widget management"
GROUP_DESCRIPTIONS[plymouth]="Plymouth boot theme management"
GROUP_DESCRIPTIONS[powerprofiles]="Power profile management"
GROUP_DESCRIPTIONS[refresh]="Reset config to defaults"
@@ -68,12 +59,11 @@ GROUP_DESCRIPTIONS[reminder]="Desktop notification reminders"
GROUP_DESCRIPTIONS[remove]="Removal workflows"
GROUP_DESCRIPTIONS[restart]="Restart Omarchy components"
GROUP_DESCRIPTIONS[setup]="Interactive setup wizards"
GROUP_DESCRIPTIONS[shell]="Omarchy shell IPC helpers"
GROUP_DESCRIPTIONS[screensaver]="Screensaver branding and animation"
GROUP_DESCRIPTIONS[snapshot]="System snapshots"
GROUP_DESCRIPTIONS[style]="Global UI style controls"
GROUP_DESCRIPTIONS[sudo]="Sudo configuration helpers"
GROUP_DESCRIPTIONS[system]="System status, reboot, shutdown, logout, and lock"
GROUP_DESCRIPTIONS[swayosd]="SwayOSD status display helpers"
GROUP_DESCRIPTIONS[system]="Reboot, shutdown, logout, and lock"
GROUP_DESCRIPTIONS[theme]="Theme management"
GROUP_DESCRIPTIONS[toggle]="Toggle Omarchy features"
GROUP_DESCRIPTIONS[transcode]="Image and video transcoding"
+70
View File
@@ -0,0 +1,70 @@
#!/bin/bash
# omarchy:summary=Ask Pi a question and show the answer as a notification
# omarchy:group=ai
# omarchy:name=prompt
# omarchy:args=[--speak] [prompt]
# omarchy:examples=omarchy ai prompt|omarchy-ai-prompt --speak "Summarize this"
set -euo pipefail
state_home=${XDG_STATE_HOME:-$HOME/.local/state}/omarchy
state_dir="$state_home/indicators"
session_file="$state_home/agent/prompt.jsonl"
state_file="$state_dir/ai"
mkdir -p "$state_dir" "$(dirname "$session_file")"
markdown_to_pango() {
sed -E \
-e 's/&/\&amp;/g' \
-e 's/</\&lt;/g' \
-e 's/>/\&gt;/g' \
-e 's/`([^`]+)`/<tt>\1<\/tt>/g' \
-e 's/\*\*([^*]+)\*\*/<b>\1<\/b>/g' \
-e 's/__([^_]+)__/<b>\1<\/b>/g' \
-e 's/\*([^*]+)\*/<i>\1<\/i>/g' \
-e 's/_([^_]+)_/<i>\1<\/i>/g' \
-e 's/~~([^~]+)~~/<s>\1<\/s>/g'
}
speak=false
if (($# > 0)) && [[ ${1:-} == "--speak" ]]; then
speak=true
shift
fi
if (($# > 0)); then
prompt="$*"
else
prompt=$(omarchy-menu-input "Ask AI" --width 800 || true)
fi
prompt=$(sed 's/^[[:space:]]*//; s/[[:space:]]*$//' <<<"$prompt")
[[ -n $prompt ]] || exit 0
cleanup() {
rm -f "$state_file"
pkill -RTMIN+11 waybar 2>/dev/null || true
}
trap cleanup EXIT
echo "thinking" >"$state_file"
pkill -RTMIN+11 waybar 2>/dev/null || true
if answer=$(pi -p \
--session "$session_file" \
--model gemini-2.5-flash-lite \
--skill "$HOME/.pi/agent/skills/omarchy" \
--skill "$HOME/.pi/agent/git/github.com/badlogic/pi-skills/brave-search" \
--append-system-prompt "For Omarchy desktop/system/config questions, load and follow the omarchy skill before answering. For current facts, historical facts, documentation, or questions that need web context, use the brave-search skill when BRAVE_API_KEY is available; otherwise answer from general knowledge and say when uncertain." \
"$prompt" 2>&1); then
if [[ $speak == true ]]; then
omarchy-ai-speak "$answer"
else
formatted_answer=$(markdown_to_pango <<<"$answer")
omarchy-notification-send "󱚣" "Oma" "$formatted_answer" -t 10000
fi
else
omarchy-notification-send "󱚡" "Oma failed" "$answer" -u critical -t 10000
exit 1
fi
+96
View File
@@ -0,0 +1,96 @@
#!/bin/bash
# omarchy:summary=Record speech, transcribe it with Voxtype, and ask Pi
# omarchy:group=ai
# omarchy:name=prompt-transcribe
# omarchy:hidden=true
set -euo pipefail
runtime_dir=${XDG_RUNTIME_DIR:-/tmp}/omarchy
state_dir=${XDG_STATE_HOME:-$HOME/.local/state}/omarchy/indicators
pid_file="$runtime_dir/prompt-transcribe.pid"
wav_file="$runtime_dir/prompt-transcribe.wav"
state_file="$state_dir/ai"
log_file="$runtime_dir/prompt-transcribe.log"
mkdir -p "$runtime_dir" "$state_dir"
log() {
printf '[%s] %s\n' "$(date '+%F %T')" "$*" >>"$log_file"
}
show_indicator() {
echo "listening" >"$state_file"
pkill -RTMIN+11 waybar 2>/dev/null || true
}
hide_indicator() {
rm -f "$state_file"
pkill -RTMIN+11 waybar 2>/dev/null || true
}
log "started: ${1:-toggle}"
case "${1:-toggle}" in
start)
if [[ -s $pid_file ]] && kill -0 "$(<"$pid_file")" 2>/dev/null; then
exit 0
fi
show_indicator
rm -f "$wav_file"
pw-record --target @DEFAULT_SOURCE@ --format s16 --rate 16000 --channels 1 "$wav_file" >>"$log_file" 2>&1 &
echo $! >"$pid_file"
log "recording started with pid $(<"$pid_file")"
;;
stop)
if [[ -s $pid_file ]]; then
pid=$(<"$pid_file")
log "stopping recorder pid $pid"
kill "$pid" 2>/dev/null || true
for _ in {1..20}; do
kill -0 "$pid" 2>/dev/null || break
sleep 0.05
done
rm -f "$pid_file"
else
log "no pid file found"
fi
if [[ ! -s $wav_file ]]; then
log "no recording found, exiting"
hide_indicator
exit 0
fi
log "transcribing $(stat -c%s "$wav_file") bytes"
if transcript=$(voxtype transcribe "$wav_file" 2>>"$log_file"); then
log "transcription length: ${#transcript}"
else
status=$?
rm -f "$wav_file"
log "transcription failed with status $status"
hide_indicator
omarchy-notification-send "󱚣" "AI transcription failed" "Voxtype could not transcribe the prompt." -u critical
exit 1
fi
rm -f "$wav_file"
transcript=$(sed 's/^[[:space:]]*//; s/[[:space:]]*$//' <<<"$transcript")
if [[ -z $transcript ]]; then
log "empty transcription, exiting"
hide_indicator
exit 0
fi
log "sending transcript to omarchy-ai-prompt"
omarchy-ai-prompt --speak "$transcript"
log "omarchy-ai-prompt finished"
;;
*)
echo "Usage: omarchy-ai-prompt-transcribe start|stop"
exit 1
;;
esac
+36
View File
@@ -0,0 +1,36 @@
#!/bin/bash
# omarchy:summary=Speak text using Piper TTS
# omarchy:group=ai
# omarchy:name=speak
# omarchy:args=[text]
# omarchy:examples=omarchy ai speak "Hello"
set -euo pipefail
voice_dir=${XDG_DATA_HOME:-$HOME/.local/share}/omarchy/piper
model="$voice_dir/en_US-amy-medium.onnx"
config="$voice_dir/en_US-amy-medium.onnx.json"
output=${XDG_RUNTIME_DIR:-/tmp}/omarchy-ai-speak.wav
mkdir -p "$voice_dir" "$(dirname "$output")"
if (($# > 0)); then
text="$*"
else
text=$(cat)
fi
text=$(sed 's/^[[:space:]]*//; s/[[:space:]]*$//' <<<"$text")
[[ -n $text ]] || exit 0
if [[ ! -f $model ]]; then
curl -L --fail -o "$model" "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/amy/medium/en_US-amy-medium.onnx"
fi
if [[ ! -f $config ]]; then
curl -L --fail -o "$config" "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/amy/medium/en_US-amy-medium.onnx.json"
fi
uvx --from piper-tts piper --model "$model" --config "$config" --output-file "$output" <<<"$text" >/dev/null
pw-play "$output"
+13 -5
View File
@@ -4,10 +4,18 @@
wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle >/dev/null
if wpctl get-volume @DEFAULT_AUDIO_SOURCE@ | grep -q MUTED; then
omarchy-brightness-keyboard-mute on
omarchy-osd -i microphone-muted -m "Microphone muted"
if pactl get-source-mute @DEFAULT_SOURCE@ | rg -q 'yes'; then
led=on
osd_message='Microphone muted'
osd_icon='microphone-sensitivity-muted-symbolic'
else
omarchy-brightness-keyboard-mute off
omarchy-osd -i microphone -m "Microphone on"
led=off
osd_message='Microphone on'
osd_icon='audio-input-microphone-symbolic'
fi
omarchy-brightness-keyboard-mute "$led"
omarchy-swayosd-client \
--custom-message "$osd_message" \
--custom-icon "$osd_icon"
-20
View File
@@ -1,20 +0,0 @@
#!/bin/bash
# omarchy:summary=Set the default audio input and move active streams
# omarchy:args=<node-id> <source-name>
# omarchy:examples=omarchy audio input set default 43 alsa_input.pci-0000_00_1f.3.analog-stereo
node_id=${1:-}
source_name=${2:-}
if [[ -z $node_id || -z $source_name ]]; then
echo "Usage: omarchy-audio-input-set-default <node-id> <source-name>" >&2
exit 1
fi
wpctl set-default "$node_id" 2>/dev/null || true
pactl set-default-source "$source_name" 2>/dev/null || true
pactl list short source-outputs 2>/dev/null | awk '{ print $1 }' | while read -r output; do
[[ -n $output ]] && pactl move-source-output "$output" "$source_name" 2>/dev/null || true
done
-20
View File
@@ -1,20 +0,0 @@
#!/bin/bash
# omarchy:summary=Set the default audio output and move active streams
# omarchy:args=<node-id> <sink-name>
# omarchy:examples=omarchy audio output set default 42 alsa_output.pci-0000_00_1f.3.analog-stereo
node_id=${1:-}
sink_name=${2:-}
if [[ -z $node_id || -z $sink_name ]]; then
echo "Usage: omarchy-audio-output-set-default <node-id> <sink-name>" >&2
exit 1
fi
timeout 2 wpctl set-default "$node_id" 2>/dev/null || true
timeout 2 pactl set-default-sink "$sink_name" 2>/dev/null || true
timeout 2 pactl list short sink-inputs 2>/dev/null | awk '{ print $1 }' | while read -r input; do
[[ -n $input ]] && timeout 2 pactl move-sink-input "$input" "$sink_name" 2>/dev/null || true
done
+34 -14
View File
@@ -1,17 +1,17 @@
#!/bin/bash
# omarchy:summary=Switch between audio outputs while preserving the mute status
# omarchy:summary=Switch between audio outputs while preserving the mute status. By default mapped to Super + Mute.
sinks=$(timeout 2 pactl -f json list sinks | jq '[.[] | select((.ports | length == 0) or ([.ports[]? | .availability != "not available"] | any))]')
sinks_count=$(jq 'length' <<<"$sinks")
sinks=$(pactl -f json list sinks | jq '[.[] | select((.ports | length == 0) or ([.ports[]? | .availability != "not available"] | any))]')
sinks_count=$(echo "$sinks" | jq '. | length')
if (( sinks_count == 0 )); then
omarchy-osd -m "No audio devices found"
omarchy-swayosd-client --custom-message "No audio devices found"
exit 1
fi
current_sink_name=$(timeout 2 pactl get-default-sink)
current_sink_index=$(jq -r --arg name "$current_sink_name" 'map(.name) | index($name)' <<<"$sinks")
current_sink_name=$(pactl get-default-sink)
current_sink_index=$(echo "$sinks" | jq -r --arg name "$current_sink_name" 'map(.name) | index($name)')
if [[ $current_sink_index != "null" ]]; then
next_sink_index=$(((current_sink_index + 1) % sinks_count))
@@ -19,13 +19,28 @@ else
next_sink_index=0
fi
next_sink=$(jq -c ".[$next_sink_index]" <<<"$sinks")
next_sink_name=$(jq -r '.name' <<<"$next_sink")
next_sink_description=$(jq -r '.description // .properties."device.description" // .name' <<<"$next_sink")
next_sink_volume=$(jq -r '.volume | to_entries[0].value.value_percent | sub("%"; "") | tonumber' <<<"$next_sink")
next_sink_is_muted=$(jq -r '.mute' <<<"$next_sink")
next_sink=$(echo "$sinks" | jq -r ".[$next_sink_index]")
next_sink_name=$(echo "$next_sink" | jq -r '.name')
if [[ $next_sink_is_muted == "true" ]] || (( next_sink_volume == 0 )); then
next_sink_description=$(echo "$next_sink" | jq -r '.description')
if [[ $next_sink_description == "(null)" ]] || [[ $next_sink_description == "null" ]] || [[ -z $next_sink_description ]]; then
# For Bluetooth devices, the friendly name is on the Device entry (device.id), not the Sink entry (object.id)
device_id=$(echo "$next_sink" | jq -r '.properties."device.id"')
if [[ $device_id != "null" ]] && [[ -n $device_id ]]; then
next_sink_description=$(wpctl status | grep -E "^\s*│?\s+${device_id}\." | sed -E 's/^.*[0-9]+\.\s+//' | sed -E 's/\s+\[.*$//')
fi
# Fall back to object.id lookup if device.id didn't yield a result
if [[ -z $next_sink_description ]]; then
sink_id=$(echo "$next_sink" | jq -r '.properties."object.id"')
next_sink_description=$(wpctl status | grep -E "\s+\*?\s+${sink_id}\." | sed -E 's/^.*[0-9]+\.\s+//' | sed -E 's/\s+\[.*$//')
fi
fi
next_sink_volume=$(echo "$next_sink" | jq -r \
'.volume | to_entries[0].value.value_percent | sub("%"; "")')
next_sink_is_muted=$(echo "$next_sink" | jq -r '.mute')
if [[ $next_sink_is_muted = "true" ]] || (( next_sink_volume == 0 )); then
icon_state="muted"
elif (( next_sink_volume <= 33 )); then
icon_state="low"
@@ -35,8 +50,13 @@ else
icon_state="high"
fi
next_sink_volume_icon="sink-volume-${icon_state}-symbolic"
if [[ $next_sink_name != $current_sink_name ]]; then
omarchy-audio-output-set-default "$(jq -r '.index' <<<"$next_sink")" "$next_sink_name"
next_sink_wpid=$(echo "$next_sink" | jq -r '.properties."object.id"')
wpctl set-default "$next_sink_wpid"
fi
omarchy-osd -i "volume-${icon_state}" -m "$next_sink_description"
omarchy-swayosd-client \
--custom-message "$next_sink_description" \
--custom-icon "$next_sink_volume_icon"
-67
View File
@@ -1,67 +0,0 @@
#!/bin/bash
# omarchy:summary=Adjust output volume and show the Omarchy OSD
# omarchy:args=<raise|lower|mute-toggle|+N|-N>
# omarchy:examples=omarchy audio output volume raise | omarchy audio output volume lower | omarchy audio output volume mute-toggle | omarchy audio output volume +1
action="${1:-}"
if [[ -z $action ]]; then
echo "Usage: omarchy-audio-output-volume <raise|lower|mute-toggle|+N|-N>"
exit 1
fi
volume_state() {
wpctl get-volume @DEFAULT_AUDIO_SINK@
}
volume_percent() {
volume_state | awk '{ for (i=1; i<=NF; i++) if ($i ~ /^[0-9.]+$/) print int($i * 100) }'
}
volume_muted() {
volume_state | grep -q MUTED
}
unmute_output() {
wpctl set-mute @DEFAULT_AUDIO_SINK@ 0 >/dev/null
}
case "$action" in
raise) action="+5" ;;
lower) action="-5" ;;
esac
if [[ $action == "mute-toggle" ]]; then
runtime_dir="${XDG_RUNTIME_DIR:-/tmp}"
debounce_file="$runtime_dir/omarchy-audio-output-volume-mute-toggle.last"
now=$(date +%s%3N)
last=0
[[ -r $debounce_file ]] && read -r last < "$debounce_file" || true
if (( now - last < 250 )); then
exit 0
fi
printf '%s\n' "$now" > "$debounce_file"
wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle >/dev/null
elif [[ $action == +* ]]; then
step="${action#+}"
unmute_output
wpctl set-volume -l 1.0 @DEFAULT_AUDIO_SINK@ "${step}%+"
elif [[ $action == -* ]]; then
step="${action#-}"
unmute_output
wpctl set-volume @DEFAULT_AUDIO_SINK@ "${step}%-"
else
echo "Unknown volume action: $action"
exit 1
fi
percent=$(volume_percent)
if volume_muted || (( percent == 0 )); then
icon="volume-muted"
else
icon="volume-high"
fi
omarchy-osd -i "$icon" -p "$percent"
-45
View File
@@ -1,45 +0,0 @@
#!/bin/bash
# omarchy:summary=Print PulseAudio sink availability for the shell
# omarchy:group=audio
pactl list sinks 2>/dev/null | awk '
function emit_sink() {
if (name == "") return
print name "\t" ((port_count == 0 || available) ? 1 : 0)
}
/^Sink #/ {
emit_sink()
name = ""
in_ports = 0
port_count = 0
available = 0
next
}
/^[[:space:]]*Name:/ {
name = $2
next
}
/^[[:space:]]*Ports:$/ {
in_ports = 1
next
}
in_ports && /^\tActive Port:/ {
in_ports = 0
next
}
in_ports && /^\t\t/ {
port_count++
if ($0 !~ /not available/) available = 1
next
}
END {
emit_sink()
}
'
-20
View File
@@ -1,20 +0,0 @@
#!/bin/bash
# omarchy:summary=Cycle to the next media source and transfer playback when the current source is playing
# omarchy:args=[next|previous]
# omarchy:examples=omarchy audio source switch | omarchy-audio-source-switch previous
direction="${1:-next}"
case "$direction" in
next)
omarchy-shell media sourceSwitch
;;
previous)
omarchy-shell media sourceSwitchPrevious
;;
*)
echo "Usage: omarchy-audio-source-switch [next|previous]" >&2
exit 1
;;
esac
-17
View File
@@ -1,17 +0,0 @@
#!/bin/bash
# omarchy:summary=Send the low battery warning notification and run battery-low hooks.
# omarchy:args=<percentage>
# omarchy:hidden=true
set -euo pipefail
if (($# != 1)); then
echo "Usage: omarchy-battery-low <percentage>" >&2
exit 1
fi
level=$1
omarchy-notification-send -g 󱐋 -u critical "Time to recharge!" "Battery is down to ${level}%" -i battery-caution -t 30000
omarchy-hook battery-low "$level"
+25
View File
@@ -0,0 +1,25 @@
#!/bin/bash
# omarchy:summary=Designed to be run by systemd timer every 30 seconds and alerts if battery is low
# omarchy:hidden=true
BATTERY_THRESHOLD=10
NOTIFICATION_FLAG="/run/user/$UID/omarchy_battery_notified"
BATTERY_LEVEL=$(omarchy-battery-remaining)
BATTERY_STATE=$(upower -i $(upower -e | grep 'BAT') | grep -E "state" | awk '{print $2}')
send_notification() {
notify-send -u critical "󱐋 Time to recharge!" "Battery is down to ${1}%" -i battery-caution -t 30000
omarchy-hook battery-low "$1"
}
if [[ -n $BATTERY_LEVEL && $BATTERY_LEVEL =~ ^[0-9]+$ ]]; then
if [[ $BATTERY_STATE == "discharging" ]] && (( BATTERY_LEVEL <= BATTERY_THRESHOLD )); then
if [[ ! -f $NOTIFICATION_FLAG ]]; then
send_notification $BATTERY_LEVEL
touch $NOTIFICATION_FLAG
fi
else
rm -f $NOTIFICATION_FLAG
fi
fi
+17 -98
View File
@@ -1,108 +1,27 @@
#!/bin/bash
# omarchy:summary=Returns a formatted battery status string with percentage and power draw/charge.
# omarchy:args=[--shell]
shell_output=false
battery_info=$(upower -i $(upower -e | grep BAT))
case "${1:-}" in
"")
;;
--shell)
shell_output=true
;;
*)
echo "Usage: omarchy-battery-status [--shell]" >&2
exit 2
;;
esac
percentage=$(echo "$battery_info" | awk '/percentage/ {
print int($2)
exit
}')
battery=$(upower -e 2>/dev/null | grep BAT | head -n 1)
[[ -z $battery ]] && exit 0
power_rate=$(echo "$battery_info" | awk '/energy-rate/ {
rounded = sprintf("%.1f", $2)
sub(/\.0$/, "", rounded)
print rounded
exit
}')
battery_info=$(upower -i "$battery")
state=$(echo "$battery_info" | awk '/state/ { print $2; exit }')
time_remaining=$(omarchy-battery-remaining-time)
capacity=$(omarchy-battery-capacity)
percentage=$(awk '/percentage/ { print int($2); exit }' <<<"$battery_info")
power_rate_raw=$(awk '/energy-rate/ { print $2; exit }' <<<"$battery_info")
power_rate=$(awk '/energy-rate/ {
rounded = sprintf("%.1f", $2)
sub(/\.0$/, "", rounded)
print rounded
exit
}' <<<"$battery_info")
state=$(awk '/state/ { print $2; exit }' <<<"$battery_info")
time_remaining=$(omarchy-battery-remaining-time 2>/dev/null)
capacity=$(omarchy-battery-capacity 2>/dev/null)
threshold_start=$(awk '/charge-start-threshold:/ { gsub(/%/, "", $2); print int($2); exit }' <<<"$battery_info")
threshold_end=$(awk '/charge-end-threshold:/ { gsub(/%/, "", $2); print int($2); exit }' <<<"$battery_info")
[[ -z $threshold_end ]] && threshold_end=$(cat /sys/class/power_supply/BAT*/charge_control_end_threshold 2>/dev/null | head -1)
[[ -z $threshold_start ]] && threshold_start=$(cat /sys/class/power_supply/BAT*/charge_control_start_threshold 2>/dev/null | head -1)
ac_online=false
for supply in /sys/class/power_supply/*; do
[[ -r $supply/type ]] || continue
[[ $(<"$supply/type") == "Mains" ]] || continue
[[ -r $supply/online ]] || continue
if [[ $(<"$supply/online") == "1" ]]; then
ac_online=true
break
fi
done
charge_idle=false
if awk -v rate="${power_rate_raw:-0}" 'BEGIN { exit !(rate <= 0.2) }'; then
charge_idle=true
fi
charge_holding=false
if [[ $ac_online == "true" && -n $threshold_end ]]; then
if [[ $state == "pending-charge" ]]; then
charge_holding=true
elif [[ $state == "fully-charged" ]] && (( percentage < 99 )); then
charge_holding=true
elif [[ $state == "charging" && $charge_idle == "true" ]] && (( threshold_end < 99 && percentage >= threshold_end )); then
charge_holding=true
fi
fi
if [[ $shell_output == "true" ]]; then
printf 'percentage\t%s\n' "${percentage}%"
if [[ $charge_holding == "true" ]]; then
printf 'state\tholding\n'
else
printf 'state\t%s\n' "$state"
fi
printf 'rate\t%s\n' "${power_rate}W"
printf 'size\t%s\n' "${capacity}Wh"
printf 'time\t%s\n' "$time_remaining"
cycles=$(cat /sys/class/power_supply/BAT*/cycle_count 2>/dev/null | head -1)
[[ -n $cycles ]] && printf 'cycles\t%s\n' "$cycles"
if [[ -n $threshold_end ]]; then
if [[ -n $threshold_start && $threshold_start != $threshold_end ]]; then
printf 'threshold\t%s-%s%%\n' "$threshold_start" "$threshold_end"
else
printf 'threshold\t%s%%\n' "$threshold_end"
fi
fi
exit 0
fi
if [[ $charge_holding == "true" ]]; then
if [[ -n $threshold_start && $threshold_start != $threshold_end ]]; then
threshold_label="${threshold_start}-${threshold_end}%"
else
threshold_label="${threshold_end}%"
fi
echo "Battery ${percentage}% · Holding at ${threshold_label} · ${power_rate}W / ${capacity}Wh"
elif [[ $state == "charging" ]]; then
echo "Battery ${percentage}% · ${time_remaining} to full ·  ${power_rate}W / ${capacity}Wh"
if [[ $state == "charging" ]]; then
echo "󰁹 Battery ${percentage}% · ${time_remaining} to full ·  ${power_rate}W / ${capacity}Wh"
else
echo "Battery ${percentage}% · ${time_remaining} left ·  ${power_rate}W / ${capacity}Wh"
echo "󰁹 Battery ${percentage}% · ${time_remaining} left ·  ${power_rate}W / ${capacity}Wh"
fi
-50
View File
@@ -1,50 +0,0 @@
#!/bin/bash
# omarchy:summary=Control a Bluetooth device
# omarchy:group=bluetooth
# omarchy:args=[pair|connect|disconnect|forget] <address>
# omarchy:examples=omarchy bluetooth device connect 00:11:22:33:44:55
set -e
usage() {
echo "Usage: omarchy-bluetooth-device [pair|connect|disconnect|forget] <address>" >&2
exit 1
}
action=${1:-}
address=${2:-}
[[ $action == "pair" || $action == "connect" || $action == "disconnect" || $action == "forget" ]] || usage
[[ $address =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]] || usage
power_on() {
bluetoothctl power on >/dev/null 2>&1 || true
sleep 0.5
}
trust_device() {
bluetoothctl trust "$address" >/dev/null 2>&1 || true
}
case "$action" in
pair)
power_on
timeout 20s bluetoothctl pair "$address" >/dev/null 2>&1 || true
trust_device
timeout 20s bluetoothctl connect "$address" >/dev/null 2>&1 || true
;;
connect)
power_on
trust_device
timeout 20s bluetoothctl connect "$address" >/dev/null 2>&1 || true
;;
disconnect)
timeout 10s bluetoothctl disconnect "$address" >/dev/null 2>&1 || true
;;
forget)
power_on
timeout 10s bluetoothctl disconnect "$address" >/dev/null 2>&1 || true
timeout 10s bluetoothctl remove "$address" >/dev/null 2>&1 || true
;;
esac
-30
View File
@@ -1,30 +0,0 @@
#!/bin/bash
# omarchy:summary=Edit, set, or reset About branding
# omarchy:group=branding
# omarchy:name=about
# omarchy:args=<image|text|reset>
# omarchy:examples=omarchy branding about image | omarchy branding about text | omarchy branding about reset
set -euo pipefail
SOURCE_DIR="${XDG_PICTURES_DIR:-$HOME/Pictures}"
case "${1:-}" in
image)
image=$(omarchy-menu-file "Pick png/svg from $SOURCE_DIR" "${SOURCE_DIR}" "svg png")
if [[ -n $image ]] && omarchy-transcode-ascii "$image" ~/.config/omarchy/branding/about.txt --width 54 --height 26; then
omarchy-launch-about >/dev/null 2>&1
fi
;;
text)
omarchy-launch-editor ~/.config/omarchy/branding/about.txt >/dev/null 2>&1 && omarchy-launch-about >/dev/null 2>&1
;;
reset)
cp "$OMARCHY_PATH/icon.txt" ~/.config/omarchy/branding/about.txt && omarchy-launch-about >/dev/null 2>&1
;;
*)
echo "Usage: omarchy-branding-about <image|text|reset>" >&2
exit 1
;;
esac
-30
View File
@@ -1,30 +0,0 @@
#!/bin/bash
# omarchy:summary=Edit, set, or reset screensaver branding
# omarchy:group=branding
# omarchy:name=screensaver
# omarchy:args=<image|text|reset>
# omarchy:examples=omarchy branding screensaver image | omarchy branding screensaver text | omarchy branding screensaver reset
set -euo pipefail
SOURCE_DIR="${XDG_PICTURES_DIR:-$HOME/Pictures}"
case "${1:-}" in
image)
image=$(omarchy-menu-file "Pick png/svg from $SOURCE_DIR" "${SOURCE_DIR}" "svg png")
if [[ -n $image ]] && omarchy-transcode-ascii "$image" ~/.config/omarchy/branding/screensaver.txt; then
omarchy-launch-screensaver force >/dev/null 2>&1
fi
;;
text)
omarchy-launch-editor ~/.config/omarchy/branding/screensaver.txt >/dev/null 2>&1 && omarchy-launch-screensaver force >/dev/null 2>&1
;;
reset)
cp "$OMARCHY_PATH/logo.txt" ~/.config/omarchy/branding/screensaver.txt && omarchy-launch-screensaver force >/dev/null 2>&1
;;
*)
echo "Usage: omarchy-branding-screensaver <image|text|reset>" >&2
exit 1
;;
esac
+17 -49
View File
@@ -1,67 +1,35 @@
#!/bin/bash
# omarchy:summary=Show or adjust brightness on the most likely display device.
# omarchy:args=[--no-osd] [+N%|N%-|N%|off|on]
# omarchy:examples=omarchy brightness display | omarchy brightness display +5% | omarchy brightness display --no-osd 50% | omarchy brightness display off | omarchy brightness display on
# omarchy:summary=Adjust brightness on the most likely display device.
# omarchy:args=<+N%|N%-|N%|off|on>
# omarchy:examples=omarchy brightness display +5% | omarchy brightness display 5%- | omarchy brightness display 50% | omarchy brightness display off | omarchy brightness display on
no_osd=0
args=()
step="${1:-+5%}"
for arg in "$@"; do
if [[ $arg == "--no-osd" ]]; then
no_osd=1
else
args+=("$arg")
# Start with the first possible output, then refine to the most likely given an order heuristic.
device="$(ls -1 /sys/class/backlight 2>/dev/null | head -n1)"
for candidate in amdgpu_bl* intel_backlight acpi_video*; do
if [[ -e /sys/class/backlight/$candidate ]]; then
device="$candidate"
break
fi
done
set -- "${args[@]}"
# Get the brightness of the passed display
display_brightness() {
brightnessctl -d "$1" -m 2>/dev/null | awk -F, '{ gsub("%", "", $4); print $4; found=1 } END{ exit !found }'
}
if (( $# == 0 )); then
if omarchy-hyprland-monitor-focused-apple; then
omarchy-brightness-display-apple
exit
fi
device="$(omarchy-hw-display)" || exit 1
display_brightness "$device"
exit
fi
step="$1"
if [[ $step == "off" ]]; then
hyprctl dispatch 'hl.dsp.dpms({ action = "disable" })' >/dev/null 2>&1
hyprctl dispatch dpms off >/dev/null 2>&1
exit 0
elif [[ $step == "on" ]]; then
hyprctl dispatch 'hl.dsp.dpms({ action = "enable" })' >/dev/null 2>&1
hyprctl dispatch dpms on >/dev/null 2>&1
exit 0
fi
# Drop overlapping brightness key events so concurrent invocations do not race.
# Hardware key repeat present on some devices can otherwise glitch OSD rendering.
exec {lock_fd}>"${XDG_RUNTIME_DIR:-/tmp}/omarchy-brightness-display.lock"
flock -n "$lock_fd" || exit 0
if omarchy-hyprland-monitor-focused-apple; then
if (( no_osd )); then
omarchy-brightness-display-apple --no-osd "$step"
else
omarchy-brightness-display-apple "$step"
fi
omarchy-brightness-display-apple "$step"
exit
fi
# Current device highlighted
device="$(omarchy-hw-display)" || exit 1
# Current brightness percentage
current=$(display_brightness "$device") || exit 1
current=$(brightnessctl -d "$device" -m | cut -d',' -f4 | tr -d '%')
# Apply non-uniform step size: 1% steps if at or below 5%, otherwise set an
# absolute target percentage to avoid raw backlight rounding causing uneven OSD steps.
@@ -85,8 +53,8 @@ elif [[ $step == "5%-" ]]; then
step="$target%"
fi
# Set brightness of the display device.
# Set the actual brightness of the display device.
brightnessctl -d "$device" set "$step" >/dev/null
# Show the new brightness in OSD
(( no_osd )) || omarchy-osd -i brightness -p "$(display_brightness "$device")"
# Use SwayOSD to display the new brightness setting.
omarchy-swayosd-brightness "$(brightnessctl -d "$device" -m | cut -d',' -f4 | tr -d '%')"
+18 -85
View File
@@ -1,101 +1,34 @@
#!/bin/bash
# omarchy:summary=Show or adjust Apple Studio Display and Apple XDR Display brightness using asdcontrol.
# omarchy:args=[--no-osd] [+N%|N%-|N%]
# omarchy:examples=omarchy brightness display apple | omarchy brightness display apple +5% | omarchy brightness display apple --no-osd 50%
# omarchy:summary=Adjust the brightness on Apple Studio Displays and Apple XDR Displays using asdcontrol.
# omarchy:args=<+N%|N%-|N%>
# omarchy:examples=omarchy brightness display apple +5% | omarchy brightness display apple 5%- | omarchy brightness display apple 50%
device_cache="${XDG_RUNTIME_DIR:-/tmp}/omarchy-brightness-display-apple.device"
no_osd=0
args=()
for arg in "$@"; do
if [[ $arg == "--no-osd" ]]; then
no_osd=1
else
args+=("$arg")
if (( $# == 0 )); then
echo "Adjust Apple Display brightness by passing +5%, 5%-, or 100%"
else
step="$1"
if [[ $step =~ ^([0-9]+)%-$ ]]; then
step="-${BASH_REMATCH[1]}%"
fi
done
set -- "${args[@]}"
detect_apple_display_device() {
local devices=()
local path=""
devices=()
for path in /dev/usb/hiddev* /dev/hiddev*; do
[[ -e $path ]] && devices+=("$path")
done
(( ${#devices[@]} > 0 )) || return 1
sudo asdcontrol --detect "${devices[@]}" 2>/dev/null | awk -F: '/^\/dev\/(usb\/)?hiddev/{ print $1; exit }'
}
find_apple_display_device() {
local cached=""
local device=""
if [[ -r $device_cache ]]; then
read -r cached <"$device_cache" || true
if [[ -n $cached && -e $cached ]]; then
printf '%s\n' "$cached"
return 0
fi
fi
device="$(detect_apple_display_device)" || return 1
[[ -n $device ]] || return 1
printf '%s\n' "$device" >"$device_cache"
printf '%s\n' "$device"
}
current_brightness() {
local device="$1"
sudo asdcontrol "$device" 2>/dev/null | awk -F= '
/BRIGHTNESS=/ {
print int($2 * 100 / 60000)
found = 1
}
END { exit !found }
'
}
retry_with_fresh_device() {
rm -f "$device_cache"
device="$(find_apple_display_device || true)"
if [[ -z $device ]]; then
echo "No Apple Display HID device found" >&2
if (( ${#devices[@]} == 0 )); then
echo "No Apple Display HID device found"
exit 1
fi
}
device="$(find_apple_display_device || true)"
if [[ -z $device ]]; then
echo "No Apple Display HID device found" >&2
exit 1
fi
if (( $# == 0 )); then
if current_brightness "$device"; then
exit
else
retry_with_fresh_device
current_brightness "$device"
exit
device="$(sudo asdcontrol --detect "${devices[@]}" | grep -E '^/dev/(usb/)?hiddev' | cut -d: -f1 | head -n1)"
if [[ -z $device ]]; then
echo "No Apple Display HID device found"
exit 1
fi
fi
step="$1"
if [[ $step =~ ^([0-9]+)%-$ ]]; then
step="-${BASH_REMATCH[1]}%"
fi
if ! sudo asdcontrol "$device" -- "$step" >/dev/null; then
retry_with_fresh_device
sudo asdcontrol "$device" -- "$step" >/dev/null
value="$(sudo asdcontrol "$device" | awk -F= '/BRIGHTNESS=/{print $2+0}')"
omarchy-swayosd-brightness "$(( value * 100 / 60000 ))"
fi
(( no_osd )) || omarchy-osd -i brightness -p "$(current_brightness "$device")"
+5 -15
View File
@@ -1,20 +1,7 @@
#!/bin/bash
# omarchy:summary=Adjust keyboard backlight brightness using available steps.
# omarchy:args=[--no-osd] <up|down|cycle|off|restore>
no_osd=0
args=()
for arg in "$@"; do
if [[ $arg == "--no-osd" ]]; then
no_osd=1
else
args+=("$arg")
fi
done
set -- "${args[@]}"
# omarchy:args=<up|down|cycle|off|restore>
direction="${1:-up}"
@@ -62,4 +49,7 @@ fi
# Set the new brightness.
brightnessctl -d "$device" set "$new_brightness" >/dev/null
(( no_osd )) || omarchy-osd -i keyboard -p "$(( new_brightness * 100 / max_brightness ))"
# Use SwayOSD to display the new brightness setting.
percent=$((new_brightness * 100 / max_brightness))
omarchy-swayosd-kbd-brightness "$percent"
+5 -12
View File
@@ -2,7 +2,7 @@
# omarchy:summary=Start or stop screen recording
# omarchy:group=capture
# omarchy:args=[--fullscreen] [--with-desktop-audio] [--with-microphone-audio] [--with-webcam] [--webcam-device=<device>] [--resolution=<size>] [--stop-recording]
# omarchy:args=[--with-desktop-audio] [--with-microphone-audio] [--with-webcam] [--webcam-device=<device>] [--resolution=<size>] [--stop-recording]
# omarchy:examples=omarchy screenrecord | omarchy capture screenrecord --with-desktop-audio
# omarchy:aliases=omarchy screenrecord
#
@@ -31,7 +31,6 @@ MICROPHONE_AUDIO="false"
WEBCAM="false"
WEBCAM_DEVICE=""
RESOLUTION=""
FULLSCREEN="false"
STOP_RECORDING="false"
RECORDING_FILE="/tmp/omarchy-screenrecord-filename"
LOG_FILE=$([[ ${OMARCHY_SCREENRECORD_DEBUG:-false} == "true" ]] && echo "/tmp/omarchy-screenrecord.log" || echo "/dev/null")
@@ -43,7 +42,6 @@ for arg in "$@"; do
--with-webcam) WEBCAM="true" ;;
--webcam-device=*) WEBCAM_DEVICE="${arg#*=}" ;;
--resolution=*) RESOLUTION="${arg#*=}" ;;
--fullscreen) FULLSCREEN="true" ;;
--stop-recording) STOP_RECORDING="true" ;;
esac
done
@@ -169,10 +167,7 @@ start_screenrecording() {
# the portal backend supports and the kms backend doesn't). Default flow uses
# slurp + the kms backend, which avoids the EGL DMA-BUF modifier import
# failures the portal path can hit on some configurations.
if [[ $FULLSCREEN == "true" ]]; then
target="monitor:$(omarchy-hyprland-monitor-focused)"
capture_args=(-w "${target#monitor:}" -s "${RESOLUTION:-$(default_resolution)}")
elif [[ ${OMARCHY_SCREENRECORD_USE_PORTAL:-false} == "true" ]]; then
if [[ ${OMARCHY_SCREENRECORD_USE_PORTAL:-false} == "true" ]]; then
target="portal"
capture_args=(-w portal -s "${RESOLUTION:-$(default_resolution)}")
else
@@ -238,16 +233,14 @@ stop_screenrecording() {
else
finalize_recording
local filename=$(cat "$RECORDING_FILE" 2>/dev/null)
echo "$filename"
local preview="${filename%.mp4}-preview.png"
# Generate a preview thumbnail from the first frame
ffmpeg -y -i "$filename" -ss 00:00:00.1 -vframes 1 -q:v 2 "$preview" -loglevel quiet 2>/dev/null
(
if [[ -n $(omarchy-notification-send "Screen recording saved" "Open with Super + Alt + , (or click this)" -t 10000 --image "${preview:-$filename}" -a) ]]; then
mpv "$filename"
fi
ACTION=$(notify-send "Screen recording saved" "Open with Super + Alt + , (or click this)" -t 10000 -i "${preview:-$filename}" -A "default=open")
[[ $ACTION == "default" ]] && mpv "$filename"
rm -f "$preview"
) &
fi
@@ -256,7 +249,7 @@ stop_screenrecording() {
}
toggle_screenrecording_indicator() {
omarchy-shell -q Indicators refresh
pkill -RTMIN+8 waybar
}
screenrecording_active() {
@@ -1,35 +0,0 @@
#!/bin/bash
# omarchy:summary=Pick a webcam and start a screen recording with it
# omarchy:examples=omarchy capture screenrecording-with-webcam
set -o pipefail
webcam_list() {
v4l2-ctl --list-devices 2>/dev/null | while IFS= read -r line; do
if [[ $line != $'\t'* && -n $line ]]; then
local name="$line"
local device
IFS= read -r device || break
device=$(tr -d '\t' <<<"$device" | head -1)
[[ -n $device ]] && echo "$device $name"
fi
done
}
mapfile -t devices < <(webcam_list)
if (( ${#devices[@]} == 0 )); then
omarchy-notification-send "No webcam devices found" -u critical -t 3000
exit 1
fi
if (( ${#devices[@]} == 1 )); then
device="${devices[0]%%[[:space:]]*}"
else
selection=$(omarchy-menu-select "Select Webcam" "${devices[@]}" -- --width 520 --maxheight 520) || exit 1
device="${selection%%[[:space:]]*}"
fi
exec omarchy-capture-screenrecording \
--with-desktop-audio --with-microphone-audio \
--with-webcam --webcam-device="$device"
+13 -22
View File
@@ -2,7 +2,7 @@
# omarchy:summary=Take a screenshot
# omarchy:group=capture
# omarchy:args=[smart|region|windows|fullscreen] [slurp|copy|save] [--editor=<name>]
# omarchy:args=[smart|region|windows|fullscreen] [slurp|copy] [--editor=<name>]
# omarchy:examples=omarchy screenshot | omarchy capture screenshot region
# omarchy:aliases=omarchy screenshot
@@ -11,7 +11,7 @@ OUTPUT_DIR="${OMARCHY_SCREENSHOT_DIR:-${XDG_PICTURES_DIR:-$HOME/Pictures}}"
if [[ ! -d $OUTPUT_DIR ]]; then
mkdir -p "$OUTPUT_DIR"
omarchy-notification-send "Created screenshot directory: $OUTPUT_DIR" -t 2000
notify-send "Created screenshot directory: $OUTPUT_DIR" -u normal -t 2000
fi
pkill slurp && exit 0
@@ -36,7 +36,7 @@ open_editor() {
--output-filename "$filepath" \
--actions-on-enter save-to-clipboard \
--save-after-copy \
--copy-command 'wl-copy --type image/png'
--copy-command 'wl-copy'
else
$SCREENSHOT_EDITOR "$filepath"
fi
@@ -126,23 +126,14 @@ esac
FILENAME="screenshot-$(date +'%Y-%m-%d_%H-%M-%S').png"
FILEPATH="$OUTPUT_DIR/$FILENAME"
case "$PROCESSING" in
slurp)
grim -g "$SELECTION" "$FILEPATH" || exit 1
echo "$FILEPATH"
wl-copy --type image/png <"$FILEPATH"
if [[ $PROCESSING == "slurp" ]]; then
grim -g "$SELECTION" "$FILEPATH" || exit 1
wl-copy <"$FILEPATH"
(
if [[ -n $(omarchy-notification-send "Screenshot saved to clipboard and file" "Edit with Super + Alt + , (or click this)" -t 10000 --image "$FILEPATH" -a) ]]; then
open_editor "$FILEPATH"
fi
) >/dev/null 2>&1 &
;;
copy)
grim -g "$SELECTION" - | wl-copy --type image/png
;;
save)
grim -g "$SELECTION" "$FILEPATH" || exit 1
echo "$FILEPATH"
;;
esac
(
ACTION=$(notify-send "Screenshot saved to clipboard and file" "Edit with Super + Alt + , (or click this)" -t 10000 -i "$FILEPATH" -A "default=edit")
[[ $ACTION == "default" ]] && open_editor "$FILEPATH"
) &
else
grim -g "$SELECTION" - | wl-copy
fi
+1 -1
View File
@@ -23,4 +23,4 @@ TEXT=$(grim -g "$SELECTION" - | tesseract stdin stdout --oem 1 --psm 6 -l "${OMA
[[ -z $TEXT ]] && exit 1
printf "%s" "$TEXT" | wl-copy
omarchy-notification-send -g 󰴑 "Copied text from selection to clipboard"
notify-send "󰴑 Copied text from selection to clipboard"
-136
View File
@@ -1,136 +0,0 @@
#!/bin/bash
# omarchy:summary=Native messaging host: download the URL sent by the yt-dlp Chromium extension
# omarchy:hidden=true
set -euo pipefail
SCRIPT_PATH="${BASH_SOURCE[0]}"
# The browser launches us without Omarchy's environment, so locate the repo from
# our own path when OMARCHY_PATH isn't already set. Export it — omarchy-shell (used
# by omarchy-osd) needs it to find the running shell, and silently no-ops without it.
export OMARCHY_PATH="${OMARCHY_PATH:-$(cd -- "$(dirname -- "$SCRIPT_PATH")/.." && pwd)}"
# Make sure the Omarchy bin and yt-dlp are reachable when launched by the browser.
export PATH="$OMARCHY_PATH/bin:/usr/local/bin:/usr/bin:$PATH"
DOWNLOAD_DIR="${OMARCHY_YTDLP_DIR:-$HOME/Videos}"
parse_url() {
jq -r '.url // empty' 2>/dev/null <<<"$1" || true
}
valid_url() {
[[ $1 =~ ^https?:// ]]
}
# Drive the Quickshell OSD — a single overlay that updates in place (like the
# volume/brightness bar), so download progress never stacks like notifications.
osd_progress() {
omarchy-osd -i 󰇚 -p "$1" -d 8000 >/dev/null 2>&1 || true
}
osd_close() {
omarchy-shell -q osd close >/dev/null 2>&1 || true
}
download_url() {
local url="$1"
mkdir -p "$DOWNLOAD_DIR"
# Don't show anything until yt-dlp confirms there's actually a video to grab.
if ! yt-dlp --no-playlist --simulate --quiet --no-warnings "$url" >/dev/null 2>&1; then
omarchy-notification-send -u critical -g 󰅖 "No video found for download" "$url"
exit 0
fi
osd_progress 0
# Stream the download: OMARCHY_PROG carries the percent (drives the OSD),
# OMARCHY_FILE (printed only after a successful move) carries title + path.
local line rest pct intpct last="" er nowms lastms=0 title="" filepath=""
while IFS= read -r line; do
case $line in
OMARCHY_PROG*)
pct=${line#OMARCHY_PROG$'\t'}
intpct=${pct%%.*}
intpct=${intpct//[^0-9]/}
[[ -n $intpct && $intpct != "$last" ]] || continue # skip no-op repeats
# Throttle to ~4 redraws/sec so fast downloads don't spawn a flurry of processes.
er=$EPOCHREALTIME
nowms=$((${er%[.,]*} * 1000 + 10#${er##*[.,]} / 1000))
((nowms - lastms >= 250)) || continue
last=$intpct
lastms=$nowms
osd_progress "$intpct"
;;
OMARCHY_FILE*)
rest=${line#OMARCHY_FILE$'\t'}
title=${rest%%$'\t'*}
filepath=${rest#*$'\t'}
;;
esac
done < <(PYTHONUNBUFFERED=1 yt-dlp --no-playlist --restrict-filenames --no-simulate \
--quiet --no-warnings --progress --newline \
--progress-template $'download:OMARCHY_PROG\t%(progress._percent_str)s' \
--paths "$DOWNLOAD_DIR" -o '%(title)s [%(id)s].%(ext)s' \
--print $'after_move:OMARCHY_FILE\t%(title)s\t%(filepath)s' \
"$url" 2>&1)
osd_close
# after_move only prints on a successful download+move, so a captured path == success.
if [[ -n $filepath ]]; then
((${#title} > 50)) && title="${title:0:50}…" # keep the toast compact
# Square, center-cropped thumbnail so the notification preview isn't stretched.
local preview
preview="$(mktemp --suffix=.jpg)"
ffmpeg -y -i "$filepath" -ss 00:00:00.1 -vframes 1 \
-vf "crop='min(iw,ih)':'min(iw,ih)',scale=256:256" -q:v 2 \
"$preview" -loglevel quiet 2>/dev/null || true
# Clicking the toast opens the video in mpv (-a blocks until clicked or timed out).
(
if [[ -n $(omarchy-notification-send -g 󰄬 "Download complete" "$title" -t 10000 --image "${preview:-$filepath}" -a) ]]; then
mpv "$filepath"
fi
rm -f "$preview"
) &
else
omarchy-notification-send -u critical -g 󰅖 "Download failed" "$url"
fi
exit 0
}
main() {
local length payload url
# Detached worker: this is what actually runs yt-dlp and fires notifications.
if [[ "${1:-}" == "--download" ]]; then
download_url "$2"
fi
# Native messaging frame: 4-byte little-endian length prefix, then UTF-8 JSON.
length=$(head -c4 | od -An -v -tu4 --endian=little | tr -d ' ')
[[ -n ${length:-} ]] && ((length > 0)) || exit 0
payload=$(head -c "$length")
# Ack with an empty message so the extension's sendNativeMessage callback resolves cleanly.
printf '\x02\x00\x00\x00{}'
url=$(parse_url "$payload")
[[ -n $url ]] || exit 0
valid_url "$url" || exit 0
# Detach the download so this host exits promptly and frees the browser's port.
setsid -f "$SCRIPT_PATH" --download "$url" </dev/null >/dev/null 2>&1
}
if [[ ${BASH_SOURCE[0]} == "$0" ]]; then
main "$@"
fi
-69
View File
@@ -1,69 +0,0 @@
#!/bin/bash
# omarchy:summary=Open a clipboard history entry
# omarchy:group=clipboard
# omarchy:args=--history-index <index>
history_index=""
history_path="$HOME/.local/state/omarchy/clipboard-history.json"
while [[ $# -gt 0 ]]; do
case "$1" in
--history-index)
history_index="${2:-}"
shift 2
;;
*)
echo "Usage: omarchy-clipboard-open --history-index <index>" >&2
exit 1
;;
esac
done
[[ $history_index =~ ^[0-9]+$ ]] || exit 1
[[ -r $history_path ]] || exit 1
entry_type=$(jq -er --argjson index "$history_index" '.[$index].type' "$history_path") || exit 1
open_image() {
local path="$1"
[[ -r $path ]] || exit 1
exec satty --filename "$path" --output-filename "$path"
}
open_text() {
local text="$1"
local url=""
local open_dir=""
local open_file=""
url=$(grep -Eom1 'https?://[^[:space:]"'\''<>]+' <<<"$text" || true)
if [[ -z $url && $text =~ ^[[:space:]]*([[:alnum:]][[:alnum:].-]+\.[[:alpha:]]{2,})(/[^[:space:]]*)?[[:space:]]*$ ]]; then
url="https://${BASH_REMATCH[1]}${BASH_REMATCH[2]}"
fi
if [[ -n $url ]]; then
exec omarchy-launch-browser "$url"
fi
open_dir="${XDG_STATE_HOME:-$HOME/.local/state}/omarchy/clipboard-open"
mkdir -p "$open_dir"
open_file=$(mktemp --tmpdir="$open_dir" clipboard.XXXXXX.txt) || exit 1
printf '%s' "$text" >"$open_file"
exec omarchy-launch-editor "$open_file"
}
case "$entry_type" in
image)
path=$(jq -er --argjson index "$history_index" '.[$index].path' "$history_path") || exit 1
open_image "$path"
;;
text)
text=$(jq -er --argjson index "$history_index" '.[$index].text' "$history_path") || exit 1
open_text "$text"
;;
*)
exit 1
;;
esac
-32
View File
@@ -1,32 +0,0 @@
#!/bin/bash
# omarchy:summary=Copy a file to the clipboard and paste it
# omarchy:group=clipboard
# omarchy:args=[--copy-only] <mime-type> <path>
# omarchy:examples=omarchy clipboard paste file image/png /tmp/screenshot.png
copy_only=false
if [[ ${1:-} == "--copy-only" ]]; then
copy_only=true
shift
fi
mime=${1:-}
path=${2:-}
if [[ -z $mime || -z $path ]]; then
echo "Usage: omarchy-clipboard-paste-file [--copy-only] <mime-type> <path>" >&2
exit 1
fi
[[ -r $path ]] || exit 1
wl-copy --type "$mime" < "$path"
if [[ $copy_only == "true" ]]; then
exit
fi
sleep 0.15
wtype -M shift -k Insert -m shift 2>/dev/null || true
-62
View File
@@ -1,62 +0,0 @@
#!/bin/bash
# omarchy:summary=Copy text to the clipboard and type or paste it
# omarchy:group=clipboard
# omarchy:args=[--shift-insert] [--copy-only] [--history-index <index>|<text>]
# omarchy:examples=omarchy clipboard paste text "hello" | omarchy clipboard paste text --shift-insert "hello"
use_shift_insert=false
copy_only=false
history_index=""
text=""
while [[ $# -gt 0 ]]; do
case "$1" in
--shift-insert)
use_shift_insert=true
shift
;;
--copy-only)
copy_only=true
shift
;;
--history-index)
history_index="${2:-}"
shift 2
;;
*)
break
;;
esac
done
copy_history_entry() {
local history_path="$HOME/.local/state/omarchy/clipboard-history.json"
[[ $history_index =~ ^[0-9]+$ ]] || exit
jq -e --argjson index "$history_index" '.[$index].type == "text" and (.[$index].text | type == "string")' "$history_path" >/dev/null || exit
jq -j --argjson index "$history_index" '.[$index].text' "$history_path" | wl-copy
}
if [[ -n $history_index ]]; then
copy_history_entry
if [[ $copy_only != "true" ]]; then
use_shift_insert=true
fi
else
text=${1:-}
[[ -n $text ]] || exit
printf '%s' "$text" | wl-copy
fi
if [[ $copy_only == "true" ]]; then
exit
fi
sleep 0.15
if [[ $use_shift_insert == "true" ]]; then
wtype -M shift -k Insert -m shift 2>/dev/null || true
else
wtype "$text" 2>/dev/null || true
fi
-615
View File
@@ -1,615 +0,0 @@
#!/bin/bash
# omarchy:summary=Mutate the user shell.json bar layout and active bar option
# omarchy:args=show | options [--json] | use <plugin> | reset | list [--json] [--all] | add [plugin] [left|center|right] | remove [plugin] | drop [plugin] | position <top|bottom|left|right> | transparent <true|false>
# omarchy:examples=omarchy config shell bar show | omarchy config shell bar options | omarchy config shell bar use local.neon-bar | omarchy config shell bar reset | omarchy config shell bar list | omarchy config shell bar add omarchy.tailscale | omarchy config shell bar position top | omarchy config shell bar transparent true
set -euo pipefail
CONFIG_FILE="$HOME/.config/omarchy/shell.json"
OMARCHY_ROOT="${OMARCHY_PATH:-}"
DEFAULTS_FILE="$OMARCHY_ROOT/config/omarchy/shell.json"
usage() {
echo "Usage: omarchy-config-shell-bar show | options [--json] | use <plugin> | reset | list [--json] [--all] | add [plugin] [left|center|right] | remove [plugin] | drop [plugin] | position <top|bottom|left|right> | transparent <true|false>" >&2
}
fail() {
echo "omarchy-config-shell-bar: $*" >&2
exit 1
}
refresh_shell_config() {
if ! omarchy-shell shell reloadConfig >/dev/null 2>&1; then
omarchy-shell -q shell rescanPlugins >/dev/null 2>&1 || true
fi
}
source_file() {
local source="$CONFIG_FILE"
if [[ ! -s $source ]]; then
[[ -n $OMARCHY_ROOT ]] || fail "OMARCHY_PATH is not set"
source="$DEFAULTS_FILE"
fi
[[ -s $source ]] || fail "could not find shell config or defaults"
printf '%s\n' "$source"
}
bar_widget_manifest_paths() {
{
if [[ -n $OMARCHY_ROOT && -d $OMARCHY_ROOT/shell/plugins ]]; then
find "$OMARCHY_ROOT/shell/plugins" -mindepth 2 -maxdepth 4 -type f \( -name manifest.json -o -name '*.manifest.json' \) 2>/dev/null
fi
if [[ -d $HOME/.config/omarchy/plugins ]]; then
find -L "$HOME/.config/omarchy/plugins" -mindepth 2 -maxdepth 2 -type f -name manifest.json 2>/dev/null
fi
} | sort -u
}
bar_option_manifest_paths() {
{
if [[ -n $OMARCHY_ROOT && -d $OMARCHY_ROOT/shell/plugins ]]; then
find "$OMARCHY_ROOT/shell/plugins" -mindepth 2 -maxdepth 4 -type f \( -name manifest.json -o -name '*.manifest.json' \) 2>/dev/null
fi
if [[ -d $HOME/.config/omarchy/plugins ]]; then
find -L "$HOME/.config/omarchy/plugins" -mindepth 2 -maxdepth 2 -type f -name manifest.json 2>/dev/null
fi
} | sort -u
}
current_bar_option_id() {
jq -r '(.bar.id // "omarchy.bar") | tostring' "$(source_file)"
}
available_bar_options_json() {
local current_id
local manifest_paths=()
current_id=$(current_bar_option_id)
mapfile -t manifest_paths < <(bar_option_manifest_paths)
if (( ${#manifest_paths[@]} == 0 )); then
printf '[]\n'
return
fi
jq -s --arg currentId "$current_id" '
map(
select(((.kinds // []) | index("bar")) and ((.entryPoints.bar // "") != "")) |
{
id: (.id // ""),
name: (.name // .id // ""),
description: (.description // ""),
firstParty: ((.id // "") | startswith("omarchy.")),
active: ((.id // "") == $currentId)
} |
select(.id != "")
) |
unique_by(.id) |
sort_by((if .id == "omarchy.bar" then 0 else 1 end), .name, .id)
' "${manifest_paths[@]}"
}
print_bar_options() {
local json="false"
while (( $# > 0 )); do
case "$1" in
--json)
json="true"
;;
-h|--help)
usage
exit 0
;;
*)
fail "unknown options option: $1"
;;
esac
shift
done
if [[ $json == "true" ]]; then
available_bar_options_json
return
fi
available_bar_options_json | jq -r '
.[] |
[
.id,
(if .active then "active" else "available" end),
(if .firstParty then "built-in" else "plugin" end),
.name,
.description
] | @tsv
' | awk -F '\t' '
BEGIN { printf "%-32s %-10s %-8s %-22s %s\n", "ID", "STATUS", "SOURCE", "NAME", "DESCRIPTION" }
{ printf "%-32s %-10s %-8s %-22s %s\n", $1, $2, $3, $4, $5 }
'
}
bar_option_exists() {
local id="$1"
available_bar_options_json | jq -e --arg id "$id" 'any(.[]; .id == $id)' >/dev/null
}
current_bar_ids_json() {
jq -c '
def array_or_empty: if type == "array" then . else [] end;
def entry_id: if type == "object" then (.id // "" | tostring) else tostring end;
["left", "center", "right"] as $sections |
[ $sections[] as $section |
((.bar.layout[$section] // []) | array_or_empty)[] |
entry_id |
select(length > 0)
]
' "$(source_file)"
}
current_bar_entries_json() {
jq -c '
def array_or_empty: if type == "array" then . else [] end;
def entry_id($entry): if ($entry | type) == "object" then ($entry.id // "" | tostring) else ($entry | tostring) end;
["left", "center", "right"] as $sections |
[ $sections[] as $section |
((.bar.layout[$section] // []) | array_or_empty | to_entries[]) |
{ section: $section, index: .key, id: entry_id(.value) } |
select(.id | length > 0)
]
' "$(source_file)"
}
available_widgets_json() {
local include_all="$1"
local current_ids
local manifest_paths=()
current_ids=$(current_bar_ids_json)
mapfile -t manifest_paths < <(bar_widget_manifest_paths)
if (( ${#manifest_paths[@]} == 0 )); then
printf '[]\n'
return
fi
jq -s --argjson currentIds "$current_ids" --argjson includeAll "$include_all" '
map(
select(((.kinds // []) | index("bar-widget")) and ((.entryPoints.barWidget // "") != "")) |
{
id: (.id // ""),
name: (.barWidget.displayName // .name // .id // ""),
description: (.barWidget.description // .description // ""),
category: (.barWidget.category // "Plugin"),
allowMultiple: (.barWidget.allowMultiple == true)
} |
select(.id != "")
) |
unique_by(.id) |
map(. as $widget |
$widget + {
inBar: (($currentIds | index($widget.id)) != null),
addable: ($widget.allowMultiple or (($currentIds | index($widget.id)) == null))
}
) |
map(select($includeAll or .addable)) |
sort_by(.category, .name, .id)
' "${manifest_paths[@]}"
}
print_available_widgets() {
local include_all="false"
local json="false"
while (( $# > 0 )); do
case "$1" in
--all)
include_all="true"
;;
--json)
json="true"
;;
-h|--help)
usage
exit 0
;;
*)
fail "unknown list option: $1"
;;
esac
shift
done
if [[ $json == "true" ]]; then
available_widgets_json "$include_all"
return
fi
available_widgets_json "$include_all" | jq -r '
.[] |
[
.id,
(if .inBar and .allowMultiple then "addable*" elif .inBar then "in-bar" else "addable" end),
.category,
.name,
.description
] | @tsv
' | awk -F '\t' '
BEGIN { printf "%-28s %-10s %-14s %-22s %s\n", "ID", "STATUS", "CATEGORY", "NAME", "DESCRIPTION" }
{ printf "%-28s %-10s %-14s %-22s %s\n", $1, $2, $3, $4, $5 }
'
}
choose_widget() {
local widgets
local options=()
local selected=""
widgets=$(available_widgets_json false)
if ! jq -e 'length > 0' <<<"$widgets" >/dev/null; then
fail "no addable bar widgets found"
fi
mapfile -t options < <(jq -r '
.[] |
[.id, .name, .category, .description] | @tsv
' <<<"$widgets" | awk -F '\t' '{ printf "%-28s %-22s [%s] %s\n", $1, $2, $3, $4 }')
if [[ -t 0 || -t 2 ]]; then
if omarchy-cmd-present gum; then
selected=$(printf '%s\n' "${options[@]}" | gum filter --header "Add bar widget" --placeholder "Search addable widgets..." --limit 1 --height 14) || return 1
elif omarchy-cmd-present fzf; then
selected=$(printf '%s\n' "${options[@]}" | fzf --prompt "Add bar widget > " --height 40% --layout reverse --border) || return 1
else
print_available_widgets >&2
fail "gum or fzf is required for interactive widget selection"
fi
else
print_available_widgets >&2
fail "widget id is required; use list to see addable widgets"
fi
selected="${selected%%[[:space:]]*}"
[[ -n $selected ]] || return 1
printf '%s\n' "$selected"
}
choose_removal() {
local entries
local widgets
local options=()
local selected=""
local location=""
local section=""
local index=""
entries=$(current_bar_entries_json)
widgets=$(available_widgets_json true)
if ! jq -e 'length > 0' <<<"$entries" >/dev/null; then
fail "no bar widgets are currently configured"
fi
mapfile -t options < <(jq -r --argjson widgets "$widgets" '
def widget_meta($id): first($widgets[]? | select(.id == $id)) // {};
.[] as $entry |
(widget_meta($entry.id)) as $meta |
[
"\($entry.section)[\($entry.index)]",
$entry.id,
($meta.name // $entry.id),
($meta.category // "")
] | @tsv
' <<<"$entries" | awk -F '\t' '{ printf "%-10s %-28s %-22s %s\n", $1, $2, $3, $4 }')
if [[ -t 0 || -t 2 ]]; then
if omarchy-cmd-present gum; then
selected=$(printf '%s\n' "${options[@]}" | gum filter --header "Remove bar widget" --placeholder "Search current widgets..." --limit 1 --height 14) || return 1
elif omarchy-cmd-present fzf; then
selected=$(printf '%s\n' "${options[@]}" | fzf --prompt "Remove bar widget > " --height 40% --layout reverse --border) || return 1
else
jq -r '.[] | "\(.section)[\(.index)]\t\(.id)"' <<<"$entries" >&2
fail "gum or fzf is required for interactive widget selection"
fi
else
jq -r '.[] | "\(.section)[\(.index)]\t\(.id)"' <<<"$entries" >&2
fail "widget id is required; use show to see current widgets"
fi
location="${selected%%[[:space:]]*}"
section="${location%%[*}"
index="${location#*[}"
index="${index%]}"
[[ $section =~ ^(left|center|right)$ ]] || return 1
[[ $index =~ ^[0-9]+$ ]] || return 1
printf '%s\t%s\n' "$section" "$index"
}
command="${1:-}"
case "$command" in
show|current)
if (( $# != 1 )); then
usage
exit 1
fi
jq '.bar // {}' "$(source_file)"
;;
selected|active)
if (( $# != 1 )); then
usage
exit 1
fi
current_bar_option_id
;;
options|option)
shift
print_bar_options "$@"
;;
use)
if (( $# != 2 )); then
usage
exit 1
fi
plugin="${2:-}"
[[ -n $plugin ]] || fail "bar option id is required"
if [[ $plugin == "default" || $plugin == "built-in" ]]; then
plugin="omarchy.bar"
fi
bar_option_exists "$plugin" || fail "$plugin is not a known bar option; run 'omarchy config shell bar options'"
mkdir -p "$(dirname "$CONFIG_FILE")"
source="$(source_file)"
tmp=$(mktemp)
trap 'rm -f "$tmp"' EXIT
if [[ $plugin == "omarchy.bar" ]]; then
jq '
def object_or_empty: if type == "object" then . else {} end;
object_or_empty
| .version = 1
| .bar = (.bar | object_or_empty)
| del(.bar.id)
' "$source" >"$tmp"
else
jq --arg plugin "$plugin" '
def object_or_empty: if type == "object" then . else {} end;
object_or_empty
| .version = 1
| .bar = (.bar | object_or_empty)
| .bar.id = $plugin
' "$source" >"$tmp"
fi
mv "$tmp" "$CONFIG_FILE"
trap - EXIT
omarchy-shell -q shell rescanPlugins >/dev/null 2>&1 || true
refresh_shell_config
;;
reset)
if (( $# != 1 )); then
usage
exit 1
fi
mkdir -p "$(dirname "$CONFIG_FILE")"
source="$(source_file)"
tmp=$(mktemp)
trap 'rm -f "$tmp"' EXIT
jq '
def object_or_empty: if type == "object" then . else {} end;
object_or_empty
| .version = 1
| .bar = (.bar | object_or_empty)
| del(.bar.id)
' "$source" >"$tmp"
mv "$tmp" "$CONFIG_FILE"
trap - EXIT
refresh_shell_config
;;
list|available|widgets)
shift
print_available_widgets "$@"
;;
add)
if (( $# > 3 )); then
usage
exit 1
fi
if (( $# == 1 )); then
plugin=$(choose_widget) || exit 1
else
plugin="${2:-}"
fi
section="${3:-right}"
[[ -n $plugin ]] || fail "plugin is required"
[[ $section =~ ^(left|center|right)$ ]] || fail "section must be left, center, or right"
mkdir -p "$(dirname "$CONFIG_FILE")"
source="$(source_file)"
tmp=$(mktemp)
trap 'rm -f "$tmp"' EXIT
jq --arg section "$section" --arg plugin "$plugin" '
def object_or_empty: if type == "object" then . else {} end;
def array_or_empty: if type == "array" then . else [] end;
def entry_id: if type == "object" then (.id // "" | tostring) else tostring end;
def append_anchor($section):
{
left: "omarchy.workspaces",
center: "omarchy.weather",
right: "omarchy.tray"
}[$section];
def insert_after_anchor($entries; $entry; $anchor):
($entries | map(entry_id) | index($anchor)) as $index
| if $index == null then
$entries + [$entry]
else
$entries[0:$index + 1] + [$entry] + $entries[$index + 1:]
end;
object_or_empty
| .version = 1
| .bar = (.bar | object_or_empty)
| .bar.layout = (.bar.layout | object_or_empty)
| .bar.layout.left = (.bar.layout.left | array_or_empty | map(select(entry_id != $plugin)))
| .bar.layout.center = (.bar.layout.center | array_or_empty | map(select(entry_id != $plugin)))
| .bar.layout.right = (.bar.layout.right | array_or_empty | map(select(entry_id != $plugin)))
| .plugins = (.plugins | array_or_empty)
| .bar.layout[$section] = insert_after_anchor(.bar.layout[$section]; { id: $plugin }; append_anchor($section))
' "$source" >"$tmp"
mv "$tmp" "$CONFIG_FILE"
trap - EXIT
refresh_shell_config
;;
position)
if (( $# != 2 )); then
usage
exit 1
fi
position="${2:-}"
[[ $position =~ ^(top|bottom|left|right)$ ]] || fail "position must be top, bottom, left, or right"
mkdir -p "$(dirname "$CONFIG_FILE")"
source="$(source_file)"
tmp=$(mktemp)
trap 'rm -f "$tmp"' EXIT
jq --arg position "$position" '
def object_or_empty: if type == "object" then . else {} end;
object_or_empty
| .version = 1
| .bar = (.bar | object_or_empty)
| .bar.position = $position
' "$source" >"$tmp"
mv "$tmp" "$CONFIG_FILE"
trap - EXIT
refresh_shell_config
;;
transparent)
if (( $# != 2 )); then
usage
exit 1
fi
transparent="${2:-}"
[[ $transparent =~ ^(true|false)$ ]] || fail "transparent must be true or false"
mkdir -p "$(dirname "$CONFIG_FILE")"
source="$(source_file)"
tmp=$(mktemp)
trap 'rm -f "$tmp"' EXIT
jq --argjson transparent "$transparent" '
def object_or_empty: if type == "object" then . else {} end;
object_or_empty
| .version = 1
| .bar = (.bar | object_or_empty)
| .bar.transparent = $transparent
' "$source" >"$tmp"
mv "$tmp" "$CONFIG_FILE"
trap - EXIT
refresh_shell_config
;;
drop|remove|rm)
if (( $# > 2 )); then
usage
exit 1
fi
plugin=""
target_section=""
target_index=""
if (( $# == 1 )); then
removal=$(choose_removal) || exit 1
target_section="${removal%%$'\t'*}"
target_index="${removal#*$'\t'}"
else
plugin="${2:-}"
[[ -n $plugin ]] || fail "plugin is required"
fi
mkdir -p "$(dirname "$CONFIG_FILE")"
source="$(source_file)"
tmp=$(mktemp)
trap 'rm -f "$tmp"' EXIT
if [[ -n $target_section ]]; then
jq --arg section "$target_section" --argjson index "$target_index" '
def object_or_empty: if type == "object" then . else {} end;
def array_or_empty: if type == "array" then . else [] end;
object_or_empty
| .version = 1
| .bar = (.bar | object_or_empty)
| .bar.layout = (.bar.layout | object_or_empty)
| .bar.layout.left = (.bar.layout.left | array_or_empty)
| .bar.layout.center = (.bar.layout.center | array_or_empty)
| .bar.layout.right = (.bar.layout.right | array_or_empty)
| if (.bar.layout[$section] | length) <= $index then
error("no widget at " + $section + "[" + ($index | tostring) + "]")
else
.bar.layout[$section] = (.bar.layout[$section][0:$index] + .bar.layout[$section][($index + 1):])
end
| .plugins = (.plugins | array_or_empty)
' "$source" >"$tmp"
else
jq --arg plugin "$plugin" '
def object_or_empty: if type == "object" then . else {} end;
def array_or_empty: if type == "array" then . else [] end;
def entry_id: if type == "object" then (.id // "" | tostring) else tostring end;
object_or_empty
| .version = 1
| .bar = (.bar | object_or_empty)
| .bar.layout = (.bar.layout | object_or_empty)
| .bar.layout.left = (.bar.layout.left | array_or_empty | map(select(entry_id != $plugin)))
| .bar.layout.center = (.bar.layout.center | array_or_empty | map(select(entry_id != $plugin)))
| .bar.layout.right = (.bar.layout.right | array_or_empty | map(select(entry_id != $plugin)))
| .plugins = (.plugins | array_or_empty)
' "$source" >"$tmp"
fi
mv "$tmp" "$CONFIG_FILE"
trap - EXIT
refresh_shell_config
;;
*)
usage
exit 1
;;
esac
+2 -2
View File
@@ -74,8 +74,8 @@ ACTION=$(gum choose "${OPTIONS[@]}")
case "$ACTION" in
"Upload log")
echo "Uploading debug log to logs.omarchy.org..."
URL=$(curl -sF "file=@$LOG_FILE" -Fexpires=24 https://logs.omarchy.org)
echo "Uploading debug log to 0x0.st..."
URL=$(curl -sF "file=@$LOG_FILE" -Fexpires=24 https://0x0.st)
if (( $? == 0 )) && [[ -n $URL ]]; then
echo "✓ Log uploaded successfully!"
echo "Share this URL:"
-58
View File
@@ -1,58 +0,0 @@
#!/bin/bash
# omarchy:summary=Show idle, screensaver, and lock diagnostics
# omarchy:group=debug
# omarchy:args=[log-lines]
# omarchy:examples=omarchy debug idle | omarchy-debug-idle 400
lines=${1:-200}
if [[ ! $lines =~ ^[0-9]+$ ]]; then
lines=200
fi
section() {
printf '\n== %s ==\n' "$1"
}
section "Time"
date -Is
section "Idle IPC status"
omarchy-shell idle status 2>&1 | jq . 2>/dev/null || omarchy-shell idle status 2>&1 || true
section "Quickshell instances"
quickshell list -p "$OMARCHY_PATH/shell" --any-display 2>&1 || true
section "Recent idle logs"
quickshell --no-color log -p "$OMARCHY_PATH/shell" --any-display --tail "$lines" --log-times -r 'quickshell.wayland.idle_notify=true' 2>&1 \
| grep -Ei 'omarchy idle|idle_notify|screensaver|lock|error|warn|failed' || true
section "Relevant processes"
ps -eo pid=,args= \
| grep -E 'quickshell -n -p|omarchy-system-sleep-monitor|systemd-inhibit.*Lock screen before suspend|org\.omarchy\.screensaver|omarchy-screensaver|(^|/| )tte( |$)' \
| grep -v grep || true
section "Sleep lock service"
systemctl --user status omarchy-sleep-lock.service --no-pager 2>/dev/null || true
section "Hyprland screensaver clients"
hyprctl clients -j 2>/dev/null \
| jq -r '.[] | select(.class == "org.omarchy.screensaver" or .initialClass == "org.omarchy.screensaver") | [.pid,.class,.initialClass,.title,.focusHistoryID] | @tsv' || true
section "Idle inhibitors"
hyprctl clients -j 2>/dev/null \
| jq -r '.[] | select(.inhibitingIdle == true or ((.tags // []) | index("noidle"))) | [.pid,.class,.title,((.tags // []) | join(",")),.inhibitingIdle] | @tsv' || true
section "Screensaver detector"
if hyprctl clients -j 2>/dev/null | jq -e '.[] | select(.class == "org.omarchy.screensaver" or .initialClass == "org.omarchy.screensaver")' >/dev/null; then
echo "running-window"
elif pgrep -f '[o]rg.omarchy.screensaver' >/dev/null; then
echo "running-process"
elif omarchy-toggle-enabled screensaver-off; then
echo "disabled"
else
echo "stopped"
fi
section "Lock detector"
omarchy-shell lock status 2>&1 | jq . 2>/dev/null || omarchy-shell lock status 2>&1 || true
-32
View File
@@ -1,32 +0,0 @@
#!/bin/bash
# omarchy:summary=Set the default AI harness used by omarchy-launch-ai
# omarchy:args=[pi|claude|codex|opencode]
# omarchy:examples=omarchy default ai | omarchy default ai pi | omarchy default ai claude
ai_file="$HOME/.local/state/omarchy/defaults/ai"
if (( $# == 0 )); then
if [[ -f $ai_file ]]; then
read -r harness <"$ai_file"
fi
[[ -n ${harness:-} ]] && echo "$harness" || echo "pi"
exit 0
fi
case "$1" in
pi) harness="pi"; name="Pi"; glyph=󰚩 ;;
claude) harness="claude"; name="Claude Code"; glyph=󰚩 ;;
codex) harness="codex"; name="Codex"; glyph=󰚩 ;;
opencode) harness="opencode"; name="OpenCode"; glyph=󰚩 ;;
*)
echo "Usage: omarchy-default-ai <pi|claude|codex|opencode>" >&2
exit 1
;;
esac
mkdir -p "$(dirname "$ai_file")"
printf '%s\n' "$harness" >"$ai_file"
omarchy-notification-send -g "$glyph" "$name is now the default AI harness"
+8 -8
View File
@@ -19,13 +19,13 @@ if (($# == 0)); then
fi
case "$1" in
chromium) desktop_id="chromium.desktop"; name="Chromium"; glyph= ;;
chrome) desktop_id="google-chrome.desktop"; name="Chrome"; glyph=󰊯 ;;
brave) desktop_id="brave-browser.desktop"; name="Brave"; glyph=󰖟 ;;
brave-origin) desktop_id="brave-origin-beta.desktop"; name="Brave Origin"; glyph=󰖟 ;;
edge) desktop_id="microsoft-edge.desktop"; name="Edge"; glyph=󰇩 ;;
firefox) desktop_id="firefox.desktop"; name="Firefox"; glyph=󰈹 ;;
zen) desktop_id="zen.desktop"; name="Zen"; glyph=󰖟 ;;
chromium) desktop_id="chromium.desktop"; name="Chromium"; glyph="" ;;
chrome) desktop_id="google-chrome.desktop"; name="Chrome"; glyph="󰊯" ;;
brave) desktop_id="brave-browser.desktop"; name="Brave"; glyph="󰖟" ;;
brave-origin) desktop_id="brave-origin-beta.desktop"; name="Brave Origin"; glyph="󰖟" ;;
edge) desktop_id="microsoft-edge.desktop"; name="Edge"; glyph="󰇩" ;;
firefox) desktop_id="firefox.desktop"; name="Firefox"; glyph="󰈹" ;;
zen) desktop_id="zen.desktop"; name="Zen"; glyph="󰰷" ;;
*)
echo "Usage: omarchy-default-browser <chromium|chrome|brave|brave-origin|edge|firefox|zen>"
exit 1
@@ -37,4 +37,4 @@ xdg-mime default "$desktop_id" x-scheme-handler/http
xdg-mime default "$desktop_id" x-scheme-handler/https
xdg-mime default "$desktop_id" text/html
omarchy-notification-send -g $glyph "$name is now the default browser"
notify-send -u low "$glyph $name is now the default browser"
+13 -19
View File
@@ -1,36 +1,30 @@
#!/bin/bash
# omarchy:summary=Set the default editor used by omarchy-launch-editor
# omarchy:summary=Set the default editor for $EDITOR
# omarchy:args=[code|cursor|zed|sublime_text|helix|vim|emacs|nvim]
# omarchy:examples=omarchy default editor | omarchy default editor code | omarchy default editor helix
editor_file="$HOME/.local/state/omarchy/defaults/editor"
if (($# == 0)); then
if [[ -f $editor_file ]]; then
read -r editor <"$editor_file"
fi
[[ -n $editor ]] && echo "$editor" || echo "nvim"
sed -n 's/^export EDITOR=//p' ~/.config/uwsm/default | head -n 1
exit 0
fi
case "$1" in
code) editor="code"; name="VSCode"; glyph= ;;
cursor) editor="cursor"; name="Cursor"; glyph= ;;
zed | zeditor) editor="zeditor"; name="Zed"; glyph= ;;
sublime_text) editor="sublime_text"; name="Sublime Text"; glyph= ;;
helix) editor="helix"; name="Helix"; glyph= ;;
vim) editor="vim"; name="Vim"; glyph= ;;
emacs) editor="emacs"; name="Emacs"; glyph= ;;
nvim) editor="nvim"; name="Neovim"; glyph= ;;
code) editor="code"; name="VSCode"; glyph="" ;;
cursor) editor="cursor"; name="Cursor"; glyph="" ;;
zed | zeditor) editor="zeditor"; name="Zed"; glyph="" ;;
sublime_text) editor="sublime_text"; name="Sublime Text"; glyph="" ;;
helix) editor="helix"; name="Helix"; glyph="" ;;
vim) editor="vim"; name="Vim"; glyph="" ;;
emacs) editor="emacs"; name="Emacs"; glyph="" ;;
nvim) editor="nvim"; name="Neovim"; glyph="" ;;
*)
echo "Usage: omarchy-default-editor <code|cursor|zed|sublime_text|helix|vim|emacs|nvim>"
exit 1
;;
esac
mkdir -p "$(dirname "$editor_file")"
printf '%s\n' "$editor" >"$editor_file"
sed -i "s/^export EDITOR=.*/export EDITOR=$editor/" ~/.config/uwsm/default
omarchy-notification-send -g $glyph "$name is now the default editor"
export EDITOR="$editor"
notify-send -u low "$glyph $name is now the default editor" " Effective after logging out"
+5 -5
View File
@@ -17,10 +17,10 @@ if (($# == 0)); then
fi
case "$1" in
alacritty) desktop_id="Alacritty.desktop"; name="Alacritty"; glyph= ;;
foot) desktop_id="foot.desktop"; name="Foot"; glyph= ;;
ghostty) desktop_id="com.mitchellh.ghostty.desktop"; name="Ghostty"; glyph= ;;
kitty) desktop_id="kitty.desktop"; name="Kitty"; glyph= ;;
alacritty) desktop_id="Alacritty.desktop"; name="Alacritty"; glyph="" ;;
foot) desktop_id="foot.desktop"; name="Foot"; glyph="" ;;
ghostty) desktop_id="com.mitchellh.ghostty.desktop"; name="Ghostty"; glyph="" ;;
kitty) desktop_id="kitty.desktop"; name="Kitty"; glyph="" ;;
*)
echo "Usage: omarchy-default-terminal <alacritty|foot|ghostty|kitty>"
exit 1
@@ -33,4 +33,4 @@ cat >~/.config/xdg-terminals.list <<EOF
$desktop_id
EOF
omarchy-notification-send -g $glyph "$name is now the default terminal"
notify-send -u low "$glyph $name is now the default terminal"
-160
View File
@@ -1,160 +0,0 @@
#!/bin/bash
# omarchy:summary=Measure theme switcher cache and selector prep times
# omarchy:args=[--repeat=<count>] [--keep-cache]
# omarchy:examples=omarchy dev benchmark theme switcher | omarchy dev benchmark theme-switcher --repeat=10
# omarchy:aliases=omarchy dev benchmark theme-switcher
set -euo pipefail
OMARCHY_BIN_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
REPEAT=5
KEEP_CACHE=false
show_help() {
cat <<'EOF'
Usage:
omarchy dev benchmark theme switcher [--repeat=<count>] [--keep-cache]
Measure the non-interactive parts of the theme switcher:
- theme preview index build (omarchy-theme-switcher before UI handoff)
- lazy selector row prep used by the interactive theme switcher
- full thumbnail cache warmup cost (omarchy-menu-images --cache-only)
Options:
--repeat=<count> Number of warm runs to measure for each case (default: 5)
--keep-cache Keep the temporary benchmark cache and print its path
EOF
}
now_us() {
local now="${EPOCHREALTIME/./}"
printf '%s' "$now"
}
format_ms() {
local us="$1"
printf '%d.%03d' "$(( us / 1000 ))" "$(( us % 1000 ))"
}
measure_once() {
local start_us end_us
start_us=$(now_us)
"$@" >/dev/null
end_us=$(now_us)
printf '%s' "$(( end_us - start_us ))"
}
run_case() {
local label="$1"
shift
local total_us=0
local min_us=0
local max_us=0
local elapsed_us=0
for (( i = 1; i <= REPEAT; i++ )); do
elapsed_us=$(measure_once "$@")
total_us=$(( total_us + elapsed_us ))
if (( i == 1 || elapsed_us < min_us )); then
min_us=$elapsed_us
fi
if (( elapsed_us > max_us )); then
max_us=$elapsed_us
fi
done
printf '%-34s avg %8s ms min %8s ms max %8s ms\n' \
"$label" \
"$(format_ms "$(( total_us / REPEAT ))")" \
"$(format_ms "$min_us")" \
"$(format_ms "$max_us")"
}
while (( $# > 0 )); do
case "$1" in
--repeat=*)
REPEAT="${1#*=}"
;;
--keep-cache)
KEEP_CACHE=true
;;
--help | -h)
show_help
exit 0
;;
*)
echo "Unknown option: $1" >&2
show_help >&2
exit 2
;;
esac
shift
done
if [[ ! $REPEAT =~ ^[0-9]+$ ]] || (( REPEAT < 1 )); then
echo "--repeat must be a positive integer" >&2
exit 2
fi
benchmark_cache=$(mktemp -d)
thumbnail_cache=$(mktemp -d)
stub_bin=$(mktemp -d)
cleanup() {
rm -rf "$stub_bin"
if [[ $KEEP_CACHE == "true" ]]; then
printf 'Benchmark cache: %s\n' "$benchmark_cache"
printf 'Thumbnail cache: %s\n' "$thumbnail_cache"
else
rm -rf "$benchmark_cache" "$thumbnail_cache"
fi
}
trap cleanup EXIT
cat >"$stub_bin/omarchy-menu-images" <<'EOF'
#!/bin/bash
exit 0
EOF
chmod +x "$stub_bin/omarchy-menu-images"
benchmark_env=(
env
"XDG_CACHE_HOME=$benchmark_cache"
"PATH=$stub_bin:$OMARCHY_BIN_DIR:$PATH"
)
thumbnail_env=(
env
"XDG_CACHE_HOME=$thumbnail_cache"
"PATH=$stub_bin:$OMARCHY_BIN_DIR:$PATH"
)
preview_dir="$benchmark_cache/omarchy/theme-selector/previews"
thumbnail_preview_dir="$thumbnail_cache/omarchy/theme-selector/previews"
build_theme_index() {
"${benchmark_env[@]}" "$OMARCHY_BIN_DIR/omarchy-theme-switcher"
}
prepare_selector_lazy() {
"${benchmark_env[@]}" "$OMARCHY_BIN_DIR/omarchy-menu-images" --prepare-only --lazy-thumbnails --show-labels --filterable "$preview_dir"
}
prepare_image_cache() {
"${thumbnail_env[@]}" "$OMARCHY_BIN_DIR/omarchy-menu-images" --cache-only "$thumbnail_preview_dir"
}
printf 'Theme switcher benchmark (%d warm runs each)\n\n' "$REPEAT"
printf '%-34s %s ms\n' "theme index cold" "$(format_ms "$(measure_once build_theme_index)")"
run_case "theme index warm" build_theme_index
printf '%-34s %s ms\n' "selector prep cold (lazy)" "$(format_ms "$(measure_once prepare_selector_lazy)")"
run_case "selector prep warm (lazy)" prepare_selector_lazy
"${thumbnail_env[@]}" "$OMARCHY_BIN_DIR/omarchy-theme-switcher" >/dev/null
printf '%-34s %s ms\n' "thumbnail cache cold" "$(format_ms "$(measure_once prepare_image_cache)")"
run_case "thumbnail cache warm" prepare_image_cache
printf '\nTheme previews: %d\n' "$(find -L "$preview_dir" -maxdepth 1 -type f 2>/dev/null | wc -l)"
+1 -1
View File
@@ -61,7 +61,7 @@ Do not define:
false flags or empty args
Examples:
# omarchy:summary=Restart the shell
# omarchy:summary=Restart Walker and related user services
# omarchy:summary=Take a screenshot
# omarchy:group=capture
-50
View File
@@ -1,50 +0,0 @@
#!/bin/bash
# omarchy:summary=Install and enable ydotool mouse automation for Omarchy development
# omarchy:requires-sudo=true
set -euo pipefail
RULE_FILE="/etc/udev/rules.d/80-uinput.rules"
if ! getent group input >/dev/null; then
echo "omarchy-dev-install-ydoo: input group does not exist" >&2
exit 1
fi
if ! id -nG "$USER" | tr ' ' '\n' | grep -qx input; then
echo "Adding $USER to the input group. You may need to log out and back in before this applies."
pkexec usermod -aG input "$USER"
fi
if omarchy-cmd-missing ydotool || omarchy-pkg-missing ydotool; then
omarchy-pkg-add ydotool
fi
pkexec /bin/bash -c '
set -euo pipefail
cat >"'"$RULE_FILE"'" <<'"'"'EOF'"'"'
KERNEL=="uinput", GROUP="input", MODE="0660", OPTIONS+="static_node=uinput"
EOF
modprobe uinput
udevadm control --reload-rules
udevadm trigger /dev/uinput 2>/dev/null || true
if [[ -e /dev/uinput ]]; then
chgrp input /dev/uinput
chmod 0660 /dev/uinput
fi
'
systemctl --user reset-failed ydotool.service >/dev/null 2>&1 || true
systemctl --user start ydotool.service
if ! systemctl --user is-active --quiet ydotool.service; then
echo "omarchy-dev-install-ydoo: ydotool.service did not start" >&2
systemctl --user status ydotool.service --no-pager >&2 || true
exit 1
fi
echo "ydotool is ready."
-20
View File
@@ -1,20 +0,0 @@
#!/bin/bash
# omarchy:summary=Open the omarchy-shell dev gallery (qs.Ui kit preview)
# omarchy:args=[section]
# omarchy:examples=omarchy dev ui-preview | omarchy dev ui-preview button | omarchy dev ui-preview button-group | omarchy dev ui-preview slider
#
# Pass a section name to jump straight to that component instead of
# scrolling from the top. Section names match the cursor section ids in
# shell/plugins/dev-gallery/GalleryPanel.qml — `button`, `button-group`,
# `cursor-surface`, `slider`, `toggle`, `dropdown`, etc. Unknown names
# are ignored and the gallery opens at its normal default position.
if (( $# > 0 )) && [[ -n $1 ]]; then
section="$1"
payload=$(printf '{"section":"%s"}' "${section//\"/}")
else
payload='{}'
fi
omarchy-shell shell summon omarchy.dev-gallery "$payload"
-133
View File
@@ -1,133 +0,0 @@
#!/bin/bash
# omarchy:summary=Show or configure the system DNS provider
# omarchy:args=[Cloudflare|Google|DHCP|Custom]
# omarchy:examples=omarchy dns | omarchy dns Cloudflare | omarchy dns Custom
set -euo pipefail
current_dns_provider() {
local dns=""
local compact=""
dns=$(awk -F= '
/^[[:space:]]*#/ { next }
/^[[:space:]]*DNS[[:space:]]*=/ {
value=$0
sub(/^[^=]*=/, "", value)
print value
exit
}
' /etc/systemd/resolved.conf 2>/dev/null || true)
compact=$(printf '%s' "$dns" | tr -d '[:space:]')
if [[ -z $compact ]]; then
echo "DHCP"
elif [[ $dns == *"cloudflare-dns.com"* || $dns == *"1.1.1.1"* || $dns == *"2606:4700:4700::1111"* ]]; then
echo "Cloudflare"
elif [[ $dns == *"dns.google"* || $dns == *"8.8.8.8"* || $dns == *"2001:4860:4860::8888"* ]]; then
echo "Google"
else
echo "Custom"
fi
}
require_root() {
if (( EUID != 0 )); then
exec pkexec omarchy-dns "$@"
fi
}
lock_dns_to_resolved() {
local file=""
for file in /etc/systemd/network/*.network; do
[[ -f $file ]] || continue
if ! grep -q "^\[DHCPv4\]" "$file"; then continue; fi
if ! sed -n '/^\[DHCPv4\]/,/^\[/p' "$file" | grep -q "^UseDNS="; then
sed -i '/^\[DHCPv4\]/a UseDNS=no' "$file"
fi
if grep -q "^\[IPv6AcceptRA\]" "$file" && ! sed -n '/^\[IPv6AcceptRA\]/,/^\[/p' "$file" | grep -q "^UseDNS="; then
sed -i '/^\[IPv6AcceptRA\]/a UseDNS=no' "$file"
fi
done
}
unlock_dns_to_dhcp() {
local file=""
for file in /etc/systemd/network/*.network; do
[[ -f $file ]] || continue
sed -i '/^\[DHCPv4\]/{n;/^UseDNS=no$/d}' "$file"
sed -i '/^\[IPv6AcceptRA\]/{n;/^UseDNS=no$/d}' "$file"
done
}
if (( $# == 0 )); then
current_dns_provider
exit 0
fi
provider="$1"
case "$provider" in
Cloudflare)
require_root "$provider"
tee /etc/systemd/resolved.conf >/dev/null <<'EOF'
[Resolve]
DNS=1.1.1.1#cloudflare-dns.com 1.0.0.1#cloudflare-dns.com 2606:4700:4700::1111#cloudflare-dns.com 2606:4700:4700::1001#cloudflare-dns.com
FallbackDNS=9.9.9.9#dns.quad9.net 149.112.112.112#dns.quad9.net 2620:fe::fe#dns.quad9.net 2620:fe::9#dns.quad9.net
DNSOverTLS=opportunistic
EOF
lock_dns_to_resolved
;;
Google)
require_root "$provider"
tee /etc/systemd/resolved.conf >/dev/null <<'EOF'
[Resolve]
DNS=8.8.8.8#dns.google 8.8.4.4#dns.google 2001:4860:4860::8888#dns.google 2001:4860:4860::8844#dns.google
FallbackDNS=9.9.9.9#dns.quad9.net 149.112.112.112#dns.quad9.net 2620:fe::fe#dns.quad9.net 2620:fe::9#dns.quad9.net
DNSOverTLS=opportunistic
EOF
lock_dns_to_resolved
;;
DHCP)
require_root "$provider"
tee /etc/systemd/resolved.conf >/dev/null <<'EOF'
[Resolve]
DNSOverTLS=no
EOF
unlock_dns_to_dhcp
;;
Custom)
require_root "$provider"
echo "Enter your DNS servers (space-separated, e.g. '192.168.1.1 1.1.1.1'):"
if ! read -r dns_servers; then
dns_servers=""
fi
if [[ -z $dns_servers ]]; then
echo "Error: No DNS servers provided." >&2
exit 1
fi
tee /etc/systemd/resolved.conf >/dev/null <<EOF
[Resolve]
DNS=$dns_servers
FallbackDNS=9.9.9.9#dns.quad9.net 149.112.112.112#dns.quad9.net 2620:fe::fe#dns.quad9.net 2620:fe::9#dns.quad9.net
EOF
lock_dns_to_resolved
;;
*)
echo "Usage: omarchy-dns [Cloudflare|Google|DHCP|Custom]" >&2
exit 1
;;
esac
systemctl restart systemd-networkd systemd-resolved
+4 -2
View File
@@ -10,15 +10,17 @@ FIRST_RUN_MODE=~/.local/state/omarchy/first-run.mode
if [[ -f $FIRST_RUN_MODE ]]; then
rm -f "$FIRST_RUN_MODE"
bash "$OMARCHY_PATH/install/first-run/battery-monitor.sh"
bash "$OMARCHY_PATH/install/first-run/recover-internal-monitor.sh"
bash "$OMARCHY_PATH/install/first-run/cleanup-reboot-sudoers.sh"
bash "$OMARCHY_PATH/install/first-run/firewall.sh"
bash "$OMARCHY_PATH/install/first-run/dns-resolver.sh"
bash "$OMARCHY_PATH/install/first-run/gnome-theme.sh"
bash "$OMARCHY_PATH/install/first-run/gdk-scale.sh"
bash "$OMARCHY_PATH/install/first-run/swayosd.sh"
bash "$OMARCHY_PATH/install/first-run/gtk-primary-paste.sh"
bash "$OMARCHY_PATH/install/first-run/elephant.sh"
omarchy-hook-install post-update "$OMARCHY_PATH/install/first-run/install-voxtype.hook"
sudo /bin/rm -f /etc/sudoers.d/first-run
sudo rm -f /etc/sudoers.d/first-run
bash "$OMARCHY_PATH/install/first-run/welcome.sh"
bash "$OMARCHY_PATH/install/first-run/wifi.sh"
+1 -4
View File
@@ -3,7 +3,4 @@
# omarchy:summary=Show current monospace font
# omarchy:examples=omarchy font current
# fontconfig is the source of truth. fc-match returns a comma-separated
# alias list (e.g. "JetBrainsMono Nerd Font,JetBrainsMono NF") so take
# the first entry.
fc-match monospace -f '%{family}\n' | head -n1 | cut -d, -f1
grep -oP 'font-family:\s*["'\'']?\K[^;"'\'']+' ~/.config/waybar/style.css | head -n1
+7 -5
View File
@@ -26,21 +26,23 @@ if [[ -n $font_name ]]; then
sed -i "s/^font=.*/font=$font_name:size=9/g" ~/.config/foot/foot.ini
fi
# fontconfig is the canonical source of truth — the omarchy shell, Qt
# apps, and anything else that resolves "monospace" all read from here.
sed -i "s/font_family = .*/font_family = $font_name/g" ~/.config/hypr/hyprlock.conf
sed -i "s/font-family: .*/font-family: '$font_name';/g" ~/.config/waybar/style.css
sed -i "s/font-family: .*/font-family: '$font_name';/g" ~/.config/swayosd/style.css
xmlstarlet ed -L \
-u '//match[@target="pattern"][test/string="monospace"]/edit[@name="family"]/string' \
-v "$font_name" \
~/.config/fontconfig/fonts.conf
omarchy-restart-shell
omarchy-restart-waybar
omarchy-restart-swayosd
if pgrep -x ghostty; then
omarchy-notification-send -g  "You must restart Ghostty to see font change"
notify-send -u low " You must restart Ghostty to see font change"
fi
if pgrep -x foot; then
omarchy-notification-send -g  "You must restart Foot to see font change"
notify-send -u low " You must restart Foot to see font change"
fi
omarchy-hook font-set "$font_name"
-39
View File
@@ -1,39 +0,0 @@
#!/bin/bash
# omarchy:summary=List installed RetroArch core names
set -e
core_dir="/usr/lib/libretro"
preferred_cores=(
"Amstrad CPC|cap32"
"Arcade FBNeo|fbneo"
"Arcade MAME|mame"
"Commodore Amiga|puae"
"Commodore C128|vice_x128"
"Commodore C64|vice_x64"
"Commodore VIC-20|vice_xvic"
"Nintendo DS|desmume"
"Nintendo Game Boy / Color|gambatte"
"Nintendo Game Boy Advance|mgba"
"Nintendo GameCube / Wii|dolphin"
"Nintendo NES / Famicom|mesen"
"Nintendo 64|parallel_n64"
"Nintendo SNES / SFC|snes9x"
"NEC PC Engine / TurboGrafx-16|mednafen_pce_fast"
"NEC PC Engine CD / TurboGrafx-CD|mednafen_pce"
"NEC PC Engine SuperGrafx|mednafen_supergrafx"
"Sega Dreamcast|flycast"
"Sega Mega Drive / Master System / Game Gear|genesis_plus_gx"
"Sega Saturn|kronos"
"Sony PlayStation|mednafen_psx_hw"
"Sony PlayStation Portable|ppsspp"
)
[[ -d $core_dir ]] || exit 0
for preferred_core in "${preferred_cores[@]}"; do
label="${preferred_core%%|*}"
core="${preferred_core#*|}"
[[ -f $core_dir/${core}_libretro.so ]] && printf '%s (%s)\n' "$label" "$core"
done
-76
View File
@@ -1,76 +0,0 @@
#!/bin/bash
# omarchy:summary=Create a desktop launcher for a RetroArch game
# omarchy:args=[core path-to-game]
# omarchy:examples=omarchy games retro install snes9x ~/Games/roms/snes/game.sfc | omarchy-games-retro-install /usr/lib/libretro/mgba_libretro.so ~/Games/roms/gba/game.gba
set -e
if (( $# == 0 )); then
mapfile -t cores < <(omarchy-games-retro-cores)
if (( ${#cores[@]} == 0 )); then
omarchy-notification-send -g 󰯉 "No RetroArch cores found" "/usr/lib/libretro"
exit 1
fi
core=$(omarchy-menu-select "RetroArch core" "${cores[@]}") || exit 0
[[ -n $core ]] || exit 0
core="${core##*(}"
core="${core%)}"
game_path=$(omarchy-menu-file "Retro game" "$HOME/Games/roms" "7z bin ccd chd cue dmg elf fds gb gba gbc iso lha m3u md n64 nds nes pbp sfc smc swc zip z64") || exit 0
[[ -n $game_path ]] || exit 0
elif (( $# == 2 )); then
core="$1"
game_path="$2"
else
echo "Usage: omarchy-games-retro-install [core path-to-game]"
echo "Example: omarchy-games-retro-install snes9x ~/Games/roms/snes/game.sfc"
exit 1
fi
if [[ ! -f $game_path ]]; then
echo "Game not found: $game_path"
exit 1
fi
if [[ $core == */* ]]; then
core_path="$core"
else
core_path="/usr/lib/libretro/${core}_libretro.so"
fi
if [[ ! -f $core_path ]]; then
echo "Core not found: $core_path"
exit 1
fi
game_name=$(printf '%s' "${game_path##*/}" | sed 's/\.[^.]*$//; s/[[:space:]]*([^)]*)//g; s/[[:space:]]\+/ /g; s/^ //; s/ $//' | perl -Mopen=locale -pe 's/(^|[[:space:]])([^[:space:]])/$1\U$2/g')
desktop_name="$game_name"
desktop_id=$(printf '%s' "$desktop_name" | tr '[:upper:]' '[:lower:]' | tr -cs '[:alnum:]' '-' | sed 's/^-//; s/-$//')
desktop_dir="$HOME/.local/share/applications"
desktop_file="$desktop_dir/$desktop_id.desktop"
icon_dir="$desktop_dir/icons"
icon_path="$icon_dir/Retro Gaming.png"
mkdir -p "$desktop_dir" "$icon_dir"
[[ -f $icon_path ]] || cp "$HOME/.local/share/omarchy/applications/icons/Retro Gaming.png" "$icon_path"
cat >"$desktop_file" <<EOF
[Desktop Entry]
Version=1.0
Name=$desktop_name
Comment=Play $game_name with RetroArch
Exec=retroarch -L "$core_path" "$game_path"
Terminal=false
Type=Application
Icon=$icon_path
StartupNotify=true
Categories=Game;Emulator;
EOF
chmod +x "$desktop_file"
update-desktop-database "$desktop_dir" &>/dev/null || true
omarchy-notification-send -g 󰯉 "$game_name installed" "Start it with Super + Space"
+96
View File
@@ -0,0 +1,96 @@
#!/usr/bin/env python3
# omarchy:summary=Run the Synaptics touchpad haptic feedback daemon
"""Haptic feedback daemon for Synaptics touchpads with Manual Trigger.
Monitors touchpad button press events and sends haptic pulses via HID
feature reports. Required because the kernel's HID haptic subsystem only
supports Auto Trigger with waveform enumeration, not the simpler Manual
Trigger protocol used by these Synaptics touchpads.
"""
import fcntl, glob, os, struct, sys
VENDOR = "06CB"
REPORT_ID = 0x37
INTENSITY = 40 # 0-100
# input_event: struct timeval (16 bytes on 64-bit) + type(H) + code(H) + value(i)
EVENT_FORMAT = "llHHi"
EVENT_SIZE = struct.calcsize(EVENT_FORMAT)
EV_KEY = 0x01
BTN_LEFT = 272
BTN_RIGHT = 273
BTN_MIDDLE = 274
# ioctl: HIDIOCSFEATURE(len) = _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len)
def HIDIOCSFEATURE(length):
return 0xC0000000 | (length << 16) | (ord("H") << 8) | 0x06
def find_hidraw():
for path in sorted(glob.glob("/sys/class/hidraw/hidraw*")):
uevent = os.path.join(path, "device", "uevent")
try:
with open(uevent) as f:
content = f.read().upper()
if f"0000{VENDOR}" in content:
return os.path.join("/dev", os.path.basename(path))
except OSError:
continue
return None
def find_touchpad_event():
for path in sorted(glob.glob("/sys/class/input/event*/device/name")):
try:
with open(path) as f:
name = f.read().strip().upper()
if VENDOR in name and "TOUCHPAD" in name:
event = path.split("/")[-3]
return os.path.join("/dev/input", event)
except OSError:
continue
return None
def main():
hidraw = find_hidraw()
if not hidraw:
print("No Synaptics haptic touchpad hidraw device found", file=sys.stderr)
sys.exit(1)
event = find_touchpad_event()
if not event:
print("No Synaptics haptic touchpad input device found", file=sys.stderr)
sys.exit(1)
print(f"Haptic touchpad: hidraw={hidraw} input={event} intensity={INTENSITY}", flush=True)
haptic_report = struct.pack("BB", REPORT_ID, INTENSITY)
ioctl_req = HIDIOCSFEATURE(len(haptic_report))
hidraw_fd = os.open(hidraw, os.O_RDWR)
event_fd = os.open(event, os.O_RDONLY)
try:
while True:
data = os.read(event_fd, EVENT_SIZE)
if len(data) < EVENT_SIZE:
continue
_, _, ev_type, code, value = struct.unpack(EVENT_FORMAT, data)
if ev_type == EV_KEY and code in (BTN_LEFT, BTN_RIGHT, BTN_MIDDLE) and value == 1:
try:
fcntl.ioctl(hidraw_fd, ioctl_req, haptic_report)
except OSError:
pass
except KeyboardInterrupt:
pass
finally:
os.close(event_fd)
os.close(hidraw_fd)
if __name__ == "__main__":
main()
+5
View File
@@ -44,6 +44,11 @@ if grep -Fq "$SWAP_FILE" /etc/fstab; then
sudo sed -i '/^# Btrfs swapfile for system hibernation$/d' /etc/fstab
fi
# Remove suspend-then-hibernate configuration
echo "Removing suspend-then-hibernate configuration"
sudo rm -f /etc/systemd/logind.conf.d/lid.conf
sudo rm -f /etc/systemd/sleep.conf.d/hibernate.conf
# Remove mkinitcpio resume hook
echo "Removing resume hook"
sudo rm "$MKINITCPIO_CONF"
-5
View File
@@ -1,5 +0,0 @@
#!/bin/bash
# omarchy:summary=Match Dell XPS systems with the Synaptics haptic touchpad.
omarchy-hw-match "XPS" && [[ -e /sys/bus/i2c/devices/i2c-VEN_06CB:00 ]]
-19
View File
@@ -1,19 +0,0 @@
#!/bin/bash
# omarchy:summary=Print the most likely display backlight device.
# omarchy:examples=omarchy-hw-display
# Start with the first possible output, then refine to the most likely given an order heuristic.
device="$(ls -1 /sys/class/backlight 2>/dev/null | head -n1)"
for candidate in amdgpu_bl* intel_backlight acpi_video*; do
if [[ -e /sys/class/backlight/$candidate ]]; then
device="$candidate"
break
fi
done
if [[ -n $device ]]; then
printf '%s\n' "$device"
else
exit 1
fi
+2 -2
View File
@@ -3,7 +3,7 @@
# omarchy:summary=Returns true when an external monitor is physically connected.
for status in /sys/class/drm/card*-*/status; do
[[ $status == *-eDP-*/status ]] && continue
[[ $(< $status) == "connected" ]] && exit 0
[[ "$status" == *-eDP-*/status ]] && continue
[[ "$(<"$status")" == "connected" ]] && exit 0
done
exit 1
-5
View File
@@ -1,5 +0,0 @@
#!/bin/bash
# omarchy:summary=Detect whether the computer has an NVIDIA GPU.
lspci | grep -qi 'nvidia' &>/dev/null
+1 -1
View File
@@ -2,7 +2,7 @@
# omarchy:summary=Clear the internal-monitor-disable toggle if no external display is connected.
TOGGLE="$HOME/.local/state/omarchy/toggles/hypr/internal-monitor-disable.lua"
TOGGLE="$HOME/.local/state/omarchy/toggles/hypr/internal-monitor-disable.conf"
if [[ -f $TOGGLE ]] && ! omarchy-hw-external-monitors; then
rm -f "$TOGGLE"
-23
View File
@@ -1,23 +0,0 @@
#!/bin/bash
# omarchy:summary=Focus a Hyprland window by application class
# omarchy:args=<app-name>
# omarchy:examples=omarchy hyprland focus app Slack
usage() {
echo "Usage: omarchy-hyprland-focus-app <app-name>" >&2
exit 1
}
app=${1:-}
[[ -n $app ]] || usage
address=$(
hyprctl clients -j 2>/dev/null |
jq -r --arg name "${app,,}" \
'[.[] | select((.class // "") | ascii_downcase | startswith($name))] | first.address // empty'
)
[[ -n $address ]] || exit 1
hyprctl dispatch focuswindow "address:$address" >/dev/null
+14 -24
View File
@@ -4,50 +4,40 @@
# omarchy:args=<on|off|toggle|recover>
TOGGLE="internal-monitor-disable"
TOGGLE_FLAG="$HOME/.local/state/omarchy/toggles/hypr/$TOGGLE.lua"
TOGGLE_FLAG="$HOME/.local/state/omarchy/toggles/hypr/$TOGGLE.conf"
MIRROR_TOGGLE="internal-monitor-mirror"
# Get internal monitor name dynamically
INTERNAL=$(hyprctl monitors -j | jq -r '.[] | select(.name | contains("eDP")).name' | head -n 1)
on() {
if omarchy-hyprland-toggle-enabled $TOGGLE; then
omarchy-hyprland-toggle $TOGGLE off
omarchy-notification-send -g 󰍹 "Laptop display enabled"
enable() {
if omarchy-hyprland-toggle-enabled "$TOGGLE"; then
omarchy-hyprland-toggle --disabled-notification "󰍹 Laptop display enabled" "$TOGGLE"
fi
}
off() {
disable() {
if ! omarchy-hw-external-monitors; then
omarchy-notification-send -g 󰍹 "Can't disable the only active display"
notify-send -u low "󰍹 Can't disable the only active display"
exit 1
fi
if omarchy-hyprland-toggle-disabled $TOGGLE && omarchy-hyprland-toggle-disabled $MIRROR_TOGGLE; then
printf 'hl.monitor({ output = "%s", disabled = true })\n' "$INTERNAL" >"$TOGGLE_FLAG"
omarchy-notification-send -g 󰍹 "Laptop display disabled"
if omarchy-hyprland-toggle-disabled "$TOGGLE" && omarchy-hyprland-toggle-disabled "$MIRROR_TOGGLE"; then
echo "monitor=$INTERNAL,disable" >"$TOGGLE_FLAG"
notify-send -u low "󰍹 Laptop display disabled"
hyprctl reload
fi
}
recover() {
if ! omarchy-hw-external-monitors && omarchy-hyprland-toggle-enabled $TOGGLE; then
omarchy-hyprland-toggle $TOGGLE off
fi
}
toggle() {
if omarchy-hyprland-toggle-enabled $TOGGLE; then
on
else
off
if ! omarchy-hw-external-monitors && omarchy-hyprland-toggle-enabled "$TOGGLE"; then
omarchy-hyprland-toggle "$TOGGLE"
fi
}
case "$1" in
on) on ;;
off) off ;;
toggle) toggle ;;
on) enable ;;
off) disable ;;
toggle) if omarchy-hyprland-toggle-enabled "$TOGGLE"; then enable; else disable; fi ;;
recover) recover ;;
*)
echo "Usage: $(basename "$0") {on|off|toggle|recover}" >&2
+20 -27
View File
@@ -4,7 +4,7 @@
# omarchy:args=<on|off|toggle|recover>
TOGGLE="internal-monitor-mirror"
TOGGLE_FLAG="$HOME/.local/state/omarchy/toggles/hypr/$TOGGLE.lua"
TOGGLE_FLAG="$HOME/.local/state/omarchy/toggles/hypr/$TOGGLE.conf"
DISABLE_TOGGLE="internal-monitor-disable"
# Get names dynamically
@@ -12,51 +12,44 @@ INTERNAL=$(hyprctl monitors -j | jq -r '.[] | select(.name | contains("eDP")).na
# Get the first available external monitor
EXTERNAL=$(hyprctl monitors -j | jq -r '.[] | select(.name | contains("eDP") | not).name' | head -n 1)
on() {
if [[ -z $EXTERNAL ]]; then
omarchy-notification-send -g 󰍹 "No external monitors found for mirror"
enable() {
if [[ -z "$EXTERNAL" ]]; then
notify-send -u low "󰍹 No external monitors found for mirror"
exit 1
fi
if [[ -z $INTERNAL ]]; then
omarchy-notification-send -g 󰍹 "No laptop monitor found to mirror"
if [[ -z "$INTERNAL" ]]; then
notify-send -u low "󰍹 No laptop monitor found to mirror"
exit 1
fi
omarchy-hyprland-toggle $DISABLE_TOGGLE off
if omarchy-hyprland-toggle-enabled "$DISABLE_TOGGLE"; then
omarchy-hyprland-toggle "$DISABLE_TOGGLE"
fi
if omarchy-hyprland-toggle-disabled $TOGGLE; then
printf 'hl.monitor({ output = "%s", mode = "preferred", position = "auto", scale = 1, mirror = "%s" })\n' "$EXTERNAL" "$INTERNAL" >"$TOGGLE_FLAG"
omarchy-notification-send -g 󰍹 "Mirroring enabled ($EXTERNAL)"
if omarchy-hyprland-toggle-disabled "$TOGGLE"; then
echo "monitor=$EXTERNAL, preferred, auto, 1, mirror, $INTERNAL" > "$TOGGLE_FLAG"
notify-send -u low "󰍹 Mirroring enabled ($EXTERNAL)"
hyprctl reload
fi
}
off() {
if omarchy-hyprland-toggle-enabled $TOGGLE; then
omarchy-hyprland-toggle $TOGGLE off
omarchy-notification-send -g 󰍹 "Extended mode restored"
fi
}
toggle() {
if omarchy-hyprland-toggle-enabled $TOGGLE; then
off
else
on
disable() {
if omarchy-hyprland-toggle-enabled "$TOGGLE"; then
omarchy-hyprland-toggle --disabled-notification "󰍹 Extended mode restored" "$TOGGLE"
fi
}
recover() {
if ! omarchy-hw-external-monitors && omarchy-hyprland-toggle-enabled $TOGGLE; then
omarchy-hyprland-toggle $TOGGLE off
if ! omarchy-hw-external-monitors && omarchy-hyprland-toggle-enabled "$TOGGLE"; then
omarchy-hyprland-toggle "$TOGGLE"
fi
}
case "$1" in
on) on ;;
off) off ;;
toggle) toggle ;;
on) enable ;;
off) disable ;;
toggle) if omarchy-hyprland-toggle-enabled "$TOGGLE"; then disable; else enable; fi ;;
recover) recover ;;
*)
echo "Usage: $(basename "$0") {on|off|toggle|recover}" >&2
-87
View File
@@ -1,87 +0,0 @@
#!/bin/bash
# omarchy:summary=Show, set, or adjust focused Hyprland monitor scaling
# omarchy:args=[up|down|1|1.25|1.6|2|3|4]
# omarchy:examples=omarchy hyprland monitor scaling | omarchy hyprland monitor scaling 1.6 | omarchy hyprland monitor scaling up | omarchy hyprland monitor scaling down
SCALES=(1 1.25 1.6 2 3 4)
usage() {
echo "Usage: omarchy-hyprland-monitor-scaling [up|down|1|1.25|1.6|2|3|4]"
}
focused_monitor_scale() {
hyprctl monitors -j | jq -er '.[] | select(.focused == true) | .scale'
}
set_scale() {
local new_scale="$1"
local monitor_info="$(hyprctl monitors -j | jq -e -c '.[] | select(.focused == true)')"
local active_monitor="$(echo "$monitor_info" | jq -r '.name')"
local width="$(echo "$monitor_info" | jq -r '.width')"
local height="$(echo "$monitor_info" | jq -r '.height')"
local refresh_rate="$(echo "$monitor_info" | jq -r '.refreshRate')"
local monitor_lua="$HOME/.config/hypr/monitors.lua"
hyprctl eval "hl.monitor({ output = \"$active_monitor\", mode = \"${width}x${height}@${refresh_rate}\", position = \"auto\", scale = $new_scale })" >/dev/null
# Persist to monitors.lua if the user still has Omarchy's generic catch-all
# defaults, so the scale survives reboots.
if [[ -f $monitor_lua ]] && grep -q '^local omarchy_monitor_scale = ' "$monitor_lua"; then
sed -i -E \
-e "s|^local omarchy_monitor_scale = .*|local omarchy_monitor_scale = ${new_scale}|" \
-e "s|^local omarchy_gdk_scale = .*|local omarchy_gdk_scale = ${new_scale}|" \
"$monitor_lua"
elif [[ -f $monitor_lua ]] && grep -Eq '^hl\.monitor\(\{ output = "", mode = "preferred", position = "auto", scale = ("auto"|[0-9.]+) \}\)' "$monitor_lua"; then
sed -i -E \
-e "s|^(hl\.monitor\(\{ output = \"\", mode = \"preferred\", position = \"auto\", scale = )([^ ]+)( \}\))|\\1${new_scale}\\3|" \
-e 's|^hl\.env\("GDK_SCALE", ".*"\)|hl.env("GDK_SCALE", "'"$new_scale"'")|' \
"$monitor_lua"
fi
}
scale_from_current() {
local direction="${1:-}"
# Find the preset closest to the current scale (Hyprland may snap fractional
# scales to nearby values, so we can't match exactly).
awk -v direction="$direction" -v list="${SCALES[*]}" '
NR == 1 { scale = $0; found = 1 }
END {
if (!found) exit 1
n = split(list, scales, " ")
best = 1; best_diff = 1e9
for (i = 1; i <= n; i++) {
diff = scale - scales[i]; if (diff < 0) diff = -diff
if (diff < best_diff) { best_diff = diff; best = i }
}
if (direction == "next" && best < n) best++
else if (direction == "previous" && best > 1) best--
print scales[best]
}'
}
case "${1:-}" in
"")
focused_monitor_scale | scale_from_current
;;
-h | --help)
usage
;;
up)
set_scale "$(focused_monitor_scale | scale_from_current next)"
;;
down)
set_scale "$(focused_monitor_scale | scale_from_current previous)"
;;
1 | 1.25 | 1.6 | 2 | 3 | 4)
set_scale "$1"
;;
*)
usage >&2
exit 1
;;
esac
+47
View File
@@ -0,0 +1,47 @@
#!/bin/bash
# omarchy:summary=Cycle focused Hyprland monitor scaling through 1x, 1.25x, 1.6x, 2x, and 3x
MONITOR_INFO=$(hyprctl monitors -j | jq -r '.[] | select(.focused == true)')
ACTIVE_MONITOR=$(echo "$MONITOR_INFO" | jq -r '.name')
CURRENT_SCALE=$(echo "$MONITOR_INFO" | jq -r '.scale')
WIDTH=$(echo "$MONITOR_INFO" | jq -r '.width')
HEIGHT=$(echo "$MONITOR_INFO" | jq -r '.height')
REFRESH_RATE=$(echo "$MONITOR_INFO" | jq -r '.refreshRate')
# Cycle through scales: 1 → 1.25 → 1.6 → 2 → 3 → 1 (or reverse with --reverse)
SCALES=(1 1.25 1.6 2 3)
# Find the index of the scale closest to the current one (Hyprland may
# snap fractional scales to nearby values, so we can't match exactly)
CURRENT_IDX=$(awk -v s="$CURRENT_SCALE" -v list="${SCALES[*]}" 'BEGIN {
n = split(list, arr, " ")
best = 0; best_diff = 1e9
for (i = 1; i <= n; i++) {
d = s - arr[i]; if (d < 0) d = -d
if (d < best_diff) { best_diff = d; best = i - 1 }
}
print best
}')
if [[ "$1" == "--reverse" ]]; then
NEW_IDX=$(( (CURRENT_IDX - 1 + ${#SCALES[@]}) % ${#SCALES[@]} ))
else
NEW_IDX=$(( (CURRENT_IDX + 1) % ${#SCALES[@]} ))
fi
NEW_SCALE=${SCALES[$NEW_IDX]}
hyprctl keyword monitor "$ACTIVE_MONITOR,${WIDTH}x${HEIGHT}@${REFRESH_RATE},auto,$NEW_SCALE"
# Persist to monitors.conf if the user has a single generic catch-all line
# (ignoring disabled monitors), so the scale survives reboots.
MONITOR_CONF="$HOME/.config/hypr/monitors.conf"
if [[ -f $MONITOR_CONF ]]; then
mapfile -t ACTIVE_LINES < <(grep -E '^[[:space:]]*monitor=' "$MONITOR_CONF" | grep -vE 'disable[[:space:]]*$')
if [[ ${#ACTIVE_LINES[@]} -eq 1 ]] && [[ "${ACTIVE_LINES[0]}" =~ ^monitor=,preferred,auto, ]]; then
sed -i -E "s|^(monitor=,preferred,auto,).*|\\1${NEW_SCALE}|" "$MONITOR_CONF"
fi
fi
notify-send -u low "󰍹 Display scaling set to ${NEW_SCALE}x"
+24 -46
View File
@@ -1,54 +1,32 @@
#!/bin/bash
# omarchy:summary=Toggle permanent Hyprland flags by copying them into a directory that's sourced entirely.
# omarchy:args=<flag-name> [on|off|toggle]
# omarchy:args=[--enabled-notification <text>] [--disabled-notification <text>] <flag-name>
usage() {
echo "Usage: omarchy-hyprland-toggle <flag-name> [on|off|toggle]" >&2
}
ENABLED_NOTIFICATION=""
DISABLED_NOTIFICATION=""
if (($# < 1)); then
usage
while [[ $# -gt 1 ]]; do
case $1 in
--enabled-notification) ENABLED_NOTIFICATION="$2"; shift 2 ;;
--disabled-notification) DISABLED_NOTIFICATION="$2"; shift 2 ;;
*) break ;;
esac
done
FLAG_NAME="$1"
FLAG="$HOME/.local/state/omarchy/toggles/hypr/$FLAG_NAME.conf"
FLAG_SOURCE="$OMARCHY_PATH/default/hypr/toggles/$FLAG_NAME.conf"
if [[ -f $FLAG ]]; then
rm $FLAG
[[ -n $DISABLED_NOTIFICATION ]] && notify-send -u low "$DISABLED_NOTIFICATION"
elif [[ -f $FLAG_SOURCE ]]; then
cp $FLAG_SOURCE $FLAG
[[ -n $ENABLED_NOTIFICATION ]] && notify-send -u low "$ENABLED_NOTIFICATION"
else
echo "Flag not found: $FLAG_NAME"
exit 1
fi
FLAG_NAME="$1"
ACTION="${2:-toggle}"
FLAG_FILE="$HOME/.local/state/omarchy/toggles/hypr/$FLAG_NAME.lua"
FLAG_SOURCE="$OMARCHY_PATH/default/hypr/toggles/$FLAG_NAME.lua"
on() {
if [[ -f $FLAG_SOURCE ]]; then
mkdir -p "$(dirname "$FLAG_FILE")"
cp "$FLAG_SOURCE" "$FLAG_FILE"
else
echo "Flag not found: $FLAG_NAME" >&2
exit 1
fi
}
off() {
rm -f "$FLAG_FILE"
}
toggle() {
if [[ -f $FLAG_FILE ]]; then
off
echo "off"
else
on
echo "on"
fi
}
case $ACTION in
on) on ;;
off) off ;;
toggle) toggle ;;
*)
usage
exit 1
;;
esac
hyprctl reload >/dev/null
hyprctl reload

Some files were not shown because too many files have changed in this diff Show More