mirror of
https://github.com/arthur-pbty/arthur-os.git
synced 2026-08-02 12:47:49 +02:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
620c6e606c |
-11
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"workspace": {
|
||||
"library": [
|
||||
"/usr/share/hypr/stubs"
|
||||
],
|
||||
"checkThirdParty": false
|
||||
},
|
||||
"diagnostics": {
|
||||
"globals": ["hl"]
|
||||
}
|
||||
}
|
||||
@@ -6,15 +6,10 @@
|
||||
- Prefer `(( ))` over numeric operators inside `[[ ]]` (e.g., `(( count < 50 ))`, not `[[ $count -lt 50 ]]`)
|
||||
- For strings/paths with spaces, quote them instead of escaping spaces with `\ ` (e.g., `"$APP_DIR/Disk Usage.desktop"`, not `$APP_DIR/Disk\ Usage.desktop`)
|
||||
- Shebangs must use `#!/bin/bash` consistently (never `#!/usr/bin/env bash`)
|
||||
- Scripts under `install/` and `migrations/` may be sourced and intentionally omit shebangs
|
||||
|
||||
# Command Naming
|
||||
|
||||
All commands start with `omarchy-`. Prefixes indicate purpose.
|
||||
|
||||
The authoritative command group list lives in `bin/omarchy` in `GROUP_DESCRIPTIONS`. Keep `GROUP_DESCRIPTIONS` updated when adding a new command prefix.
|
||||
|
||||
Common prefixes include:
|
||||
All commands start with `omarchy-`. Prefixes indicate purpose:
|
||||
|
||||
- `cmd-` - check if commands exist, misc utility commands
|
||||
- `capture-` - screenshots, screen recordings, and other capture tools
|
||||
@@ -29,138 +24,20 @@ Common prefixes include:
|
||||
- `theme-` - theme management
|
||||
- `update-` - update components
|
||||
|
||||
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-`
|
||||
|
||||
# Command Metadata
|
||||
|
||||
Commands in `bin/` can declare CLI metadata in comments near the top of the file. `bin/omarchy` scans the first 80 lines, and tests expect command metadata to remain valid.
|
||||
|
||||
Supported metadata keys:
|
||||
|
||||
- `# omarchy:summary=...` - short help text
|
||||
- `# 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:args=[smart|region|windows|fullscreen] [slurp|copy]
|
||||
# omarchy:examples=omarchy screenshot | omarchy capture screenshot region
|
||||
```
|
||||
|
||||
# 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.
|
||||
|
||||
Install stage files follow this pattern:
|
||||
|
||||
- `install/*/all.sh` lists scripts in execution order
|
||||
- leaf scripts are sourced by `run_logged $OMARCHY_INSTALL/path/to/script.sh`
|
||||
- avoid `exit` in sourced install scripts unless intentionally aborting the install
|
||||
- use `$OMARCHY_INSTALL` and `$OMARCHY_PATH` instead of hard-coded Omarchy paths
|
||||
- keep hardware-specific logic under `install/config/hardware/`
|
||||
- prefer helper commands for package and command checks where available
|
||||
|
||||
Raw `command -v`, `pacman`, and `pacman-key` are acceptable in bootstrap/preflight/package-helper contexts where the helper commands may not be available yet or where direct package-manager behavior is the point of the script.
|
||||
|
||||
# Helper Commands
|
||||
|
||||
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.
|
||||
|
||||
# Config Structure
|
||||
|
||||
- `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
|
||||
|
||||
@@ -176,13 +53,16 @@ This copies `~/.local/share/omarchy/config/hypr/hyprlock.conf` to `~/.config/hyp
|
||||
|
||||
To create a new migration, run `omarchy-dev-add-migration --no-edit`. This creates a migration file named after the unix timestamp of the last commit.
|
||||
|
||||
New migration format:
|
||||
- File permissions must be `0644` (`-rw-r--r--`); migrations are sourced, not executed directly
|
||||
Migration format:
|
||||
- No shebang line
|
||||
- Start with an `echo` describing what the migration does
|
||||
- Use `$OMARCHY_PATH` to reference the omarchy directory
|
||||
- Prefer helper commands such as `omarchy-cmd-present`, `omarchy-cmd-missing`, `omarchy-pkg-present`, and `omarchy-pkg-missing`
|
||||
|
||||
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`.
|
||||
Example:
|
||||
```bash
|
||||
echo "Disable fingerprint in hyprlock if fingerprint auth is not configured"
|
||||
|
||||
Migrations may use raw `pacman`, `command -v`, or direct config edits when needed for historical compatibility or one-off repair work.
|
||||
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,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).
|
||||
|
||||
@@ -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
|
||||
@@ -1,21 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
TryExec=foot
|
||||
Exec=foot
|
||||
Icon=foot
|
||||
Terminal=false
|
||||
Categories=System;TerminalEmulator;
|
||||
Name=Foot
|
||||
GenericName=Terminal
|
||||
Comment=A fast, lightweight and minimalistic Wayland terminal emulator
|
||||
StartupNotify=true
|
||||
StartupWMClass=foot
|
||||
Actions=New;
|
||||
X-TerminalArgExec=-e
|
||||
X-TerminalArgAppId=--app-id=
|
||||
X-TerminalArgTitle=--title=
|
||||
X-TerminalArgDir=--working-directory=
|
||||
|
||||
[Desktop Action New]
|
||||
Name=New Terminal
|
||||
Exec=foot
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,2 @@
|
||||
[Desktop Entry]
|
||||
Hidden=true
|
||||
@@ -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 |
+30
-71
@@ -17,7 +17,6 @@ declare -A COMMAND_USAGE
|
||||
declare -A COMMAND_ARGS
|
||||
declare -A COMMAND_EXAMPLES
|
||||
declare -A COMMAND_REQUIRES_SUDO
|
||||
declare -A COMMAND_HIDDEN
|
||||
declare -A COMMAND_ALIASES
|
||||
declare -A COMMAND_HAS_SUMMARY
|
||||
declare -A COMMAND_METADATA_ERRORS
|
||||
@@ -27,24 +26,17 @@ 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[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,36 +45,30 @@ 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"
|
||||
GROUP_DESCRIPTIONS[reinstall]="Reinstall and reset workflows"
|
||||
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[share]="Share clipboard, files, and folders"
|
||||
GROUP_DESCRIPTIONS[snapshot]="System snapshots"
|
||||
GROUP_DESCRIPTIONS[style]="Global UI style controls"
|
||||
GROUP_DESCRIPTIONS[state]="Persistent Omarchy state"
|
||||
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"
|
||||
GROUP_DESCRIPTIONS[tui]="Terminal UI launchers"
|
||||
GROUP_DESCRIPTIONS[tz]="Timezone selection"
|
||||
GROUP_DESCRIPTIONS[update]="Omarchy and system updates"
|
||||
GROUP_DESCRIPTIONS[upload]="Upload helpers"
|
||||
GROUP_DESCRIPTIONS[version]="Version and channel information"
|
||||
GROUP_DESCRIPTIONS[voxtype]="Voxtype dictation"
|
||||
GROUP_DESCRIPTIONS[weather]="Weather status"
|
||||
GROUP_DESCRIPTIONS[webapp]="Web app launchers"
|
||||
GROUP_DESCRIPTIONS[wifi]="Wi-Fi helpers"
|
||||
GROUP_DESCRIPTIONS[windows]="Windows VM management"
|
||||
@@ -144,7 +130,6 @@ register_command() {
|
||||
local examples=""
|
||||
local aliases=""
|
||||
local requires_sudo=""
|
||||
local hidden=""
|
||||
local line=""
|
||||
local metadata_key=""
|
||||
local metadata_value=""
|
||||
@@ -200,10 +185,6 @@ register_command() {
|
||||
requires_sudo="$metadata_value"
|
||||
[[ $metadata_value == "true" ]] || metadata_errors=$(append_pipe_value "$metadata_errors" "requires-sudo must be omitted or true")
|
||||
;;
|
||||
hidden)
|
||||
hidden="$metadata_value"
|
||||
[[ $metadata_value == "true" ]] || metadata_errors=$(append_pipe_value "$metadata_errors" "hidden must be omitted or true")
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
@@ -247,7 +228,6 @@ register_command() {
|
||||
fi
|
||||
|
||||
[[ $requires_sudo == "true" ]] || requires_sudo="false"
|
||||
[[ $hidden == "true" ]] || hidden="false"
|
||||
|
||||
local key="$file_binary"
|
||||
COMMAND_KEYS+=("$key")
|
||||
@@ -261,7 +241,6 @@ register_command() {
|
||||
COMMAND_ARGS["$key"]="$args"
|
||||
COMMAND_EXAMPLES["$key"]="$examples"
|
||||
COMMAND_REQUIRES_SUDO["$key"]="$requires_sudo"
|
||||
COMMAND_HIDDEN["$key"]="$hidden"
|
||||
COMMAND_HAS_SUMMARY["$key"]="$has_summary"
|
||||
COMMAND_METADATA_ERRORS["$key"]="$metadata_errors"
|
||||
|
||||
@@ -341,6 +320,19 @@ command_requires_args() {
|
||||
[[ -n $required ]]
|
||||
}
|
||||
|
||||
load_group_extra_commands() {
|
||||
local group="$1"
|
||||
|
||||
case "$group" in
|
||||
install)
|
||||
load_command_by_binary omarchy-pkg-add
|
||||
;;
|
||||
system)
|
||||
load_command_by_binary omarchy-lock-screen
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
load_group_commands() {
|
||||
local group="$1"
|
||||
local file=""
|
||||
@@ -354,6 +346,7 @@ load_group_commands() {
|
||||
register_command "$file"
|
||||
done
|
||||
|
||||
load_group_extra_commands "$group"
|
||||
}
|
||||
|
||||
resolve_direct_route() {
|
||||
@@ -386,9 +379,6 @@ sorted_keys() {
|
||||
local key=""
|
||||
|
||||
for key in "${COMMAND_KEYS[@]}"; do
|
||||
if [[ $include_all != "true" && ${COMMAND_HIDDEN[$key]} == "true" ]]; then
|
||||
continue
|
||||
fi
|
||||
printf '%s\t%s\n' "${COMMAND_ROUTE[$key]}" "$key"
|
||||
done | sort -u | cut -f2-
|
||||
}
|
||||
@@ -432,10 +422,6 @@ sorted_group_keys() {
|
||||
local route=""
|
||||
|
||||
for key in "${COMMAND_KEYS[@]}"; do
|
||||
if [[ $include_all != "true" && ${COMMAND_HIDDEN[$key]} == "true" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
fallback_group=$(fallback_group_for_key "$key")
|
||||
if [[ ${COMMAND_GROUP[$key]} != "$group" && $fallback_group != "$group" ]]; then
|
||||
continue
|
||||
@@ -513,6 +499,8 @@ Discovery:
|
||||
omarchy commands --all Include commands explicitly marked hidden
|
||||
omarchy commands --json Machine-readable command list
|
||||
omarchy commands --check Validate command metadata and routes
|
||||
omarchy dev benchmark Measure CLI response times
|
||||
omarchy dev bin metadata Show bin metadata fields and defaults
|
||||
EOF
|
||||
}
|
||||
|
||||
@@ -524,7 +512,7 @@ Usage:
|
||||
List commands known to the Omarchy command center.
|
||||
|
||||
Options:
|
||||
--all Include commands explicitly marked hidden
|
||||
--all Accepted for compatibility
|
||||
--json Emit machine-readable JSON
|
||||
--markdown Emit a Markdown command table
|
||||
--check Validate command metadata and route collisions
|
||||
@@ -576,9 +564,6 @@ show_commands() {
|
||||
|
||||
for route in "${!ROUTE_IS_ALIAS[@]}"; do
|
||||
key="${ROUTE_TO_KEY[$route]}"
|
||||
if [[ $include_all != "true" && ${COMMAND_HIDDEN[$key]} == "true" ]]; then
|
||||
continue
|
||||
fi
|
||||
alias_rows+="$route"$'\t'"${COMMAND_ROUTE[$key]}"$'\n'
|
||||
done
|
||||
|
||||
@@ -617,14 +602,13 @@ emit_command_records() {
|
||||
|
||||
while IFS= read -r key; do
|
||||
[[ -n $key ]] || continue
|
||||
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
|
||||
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
|
||||
"${COMMAND_ROUTE[$key]}" \
|
||||
"${COMMAND_BINARY[$key]}" \
|
||||
"${COMMAND_GROUP[$key]}" \
|
||||
"${COMMAND_NAME[$key]}" \
|
||||
"${COMMAND_SUMMARY[$key]}" \
|
||||
"${COMMAND_REQUIRES_SUDO[$key]}" \
|
||||
"${COMMAND_HIDDEN[$key]}" \
|
||||
"${COMMAND_ARGS[$key]}" \
|
||||
"${COMMAND_EXAMPLES[$key]}" \
|
||||
"${COMMAND_ALIASES[$key]}" \
|
||||
@@ -642,12 +626,11 @@ commands_json_filter() {
|
||||
name: .[3],
|
||||
summary: .[4],
|
||||
requires_sudo: (.[5] == "true"),
|
||||
hidden: (.[6] == "true"),
|
||||
args: .[7],
|
||||
examples: (.[8] | split("|") | map(gsub("^ +| +$"; "")) | map(select(length > 0))),
|
||||
aliases: (.[9] | split("|") | map(gsub("^ +| +$"; "")) | map(select(length > 0))),
|
||||
filename_route: .[10],
|
||||
routes: ([.[0], .[10]] + (.[9] | split("|") | map(gsub("^ +| +$"; "")) | map(select(length > 0))) | unique)
|
||||
args: .[6],
|
||||
examples: (.[7] | split("|") | map(gsub("^ +| +$"; "")) | map(select(length > 0))),
|
||||
aliases: (.[8] | split("|") | map(gsub("^ +| +$"; "")) | map(select(length > 0))),
|
||||
filename_route: .[9],
|
||||
routes: ([.[0], .[9]] + (.[8] | split("|") | map(gsub("^ +| +$"; "")) | map(select(length > 0))) | unique)
|
||||
}] | {ok: true, commands: .}
|
||||
EOF
|
||||
}
|
||||
@@ -701,14 +684,13 @@ show_command_json() {
|
||||
local key="$1"
|
||||
|
||||
printf '%s\n' "$key" | while IFS= read -r key; do
|
||||
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
|
||||
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
|
||||
"${COMMAND_ROUTE[$key]}" \
|
||||
"${COMMAND_BINARY[$key]}" \
|
||||
"${COMMAND_GROUP[$key]}" \
|
||||
"${COMMAND_NAME[$key]}" \
|
||||
"${COMMAND_SUMMARY[$key]}" \
|
||||
"${COMMAND_REQUIRES_SUDO[$key]}" \
|
||||
"${COMMAND_HIDDEN[$key]}" \
|
||||
"${COMMAND_ARGS[$key]}" \
|
||||
"${COMMAND_EXAMPLES[$key]}" \
|
||||
"${COMMAND_ALIASES[$key]}" \
|
||||
@@ -801,25 +783,6 @@ show_group_help() {
|
||||
fi
|
||||
}
|
||||
|
||||
show_prefix_help() {
|
||||
local prefix="$1"
|
||||
local prefix_with_space="$prefix "
|
||||
local key=""
|
||||
local rows=""
|
||||
|
||||
while IFS= read -r key; do
|
||||
[[ -n $key ]] || continue
|
||||
if [[ ${COMMAND_USAGE[$key]} == $prefix_with_space* ]]; then
|
||||
rows+="${COMMAND_USAGE[$key]}"$'\t'$'\t'"${COMMAND_SUMMARY[$key]}"$'\n'
|
||||
fi
|
||||
done < <(sorted_keys false)
|
||||
|
||||
[[ -n $rows ]] || return 1
|
||||
|
||||
echo "${prefix#omarchy } commands:"
|
||||
print_command_table "$rows"
|
||||
}
|
||||
|
||||
show_related_commands() {
|
||||
local key="$1"
|
||||
local group="${COMMAND_GROUP[$key]}"
|
||||
@@ -1037,10 +1000,6 @@ dispatch_or_help() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
if show_prefix_help "omarchy ${args[*]}"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "Unknown Omarchy command: omarchy ${args[*]}" >&2
|
||||
suggestion=$(suggest_command "${args[0]}")
|
||||
if [[ -n $suggestion ]]; then
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
@@ -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()
|
||||
}
|
||||
'
|
||||
@@ -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
|
||||
@@ -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"
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Designed to be run by systemd timer every 30 seconds and alerts if battery is low
|
||||
|
||||
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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,67 +1,26 @@
|
||||
#!/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=<step>
|
||||
|
||||
no_osd=0
|
||||
args=()
|
||||
step="${1:-+5%}"
|
||||
|
||||
for arg in "$@"; do
|
||||
if [[ $arg == "--no-osd" ]]; then
|
||||
no_osd=1
|
||||
else
|
||||
args+=("$arg")
|
||||
if omarchy-hyprland-monitor-focused-apple; then
|
||||
omarchy-brightness-display-apple "$step"
|
||||
exit
|
||||
fi
|
||||
|
||||
# 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
|
||||
exit 0
|
||||
elif [[ $step == "on" ]]; then
|
||||
hyprctl dispatch 'hl.dsp.dpms({ action = "enable" })' >/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
|
||||
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 +44,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 '%')"
|
||||
|
||||
@@ -1,101 +1,32 @@
|
||||
#!/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.
|
||||
|
||||
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")"
|
||||
|
||||
@@ -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>
|
||||
|
||||
direction="${1:-up}"
|
||||
|
||||
@@ -32,14 +19,6 @@ if [[ -z $device ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $direction == "off" ]]; then
|
||||
brightnessctl -sd "$device" set 0 >/dev/null
|
||||
exit 0
|
||||
elif [[ $direction == "restore" ]]; then
|
||||
brightnessctl -rd "$device" >/dev/null
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Get current and max brightness to determine step size.
|
||||
max_brightness="$(brightnessctl -d "$device" max)"
|
||||
current_brightness="$(brightnessctl -d "$device" get)"
|
||||
@@ -62,4 +41,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"
|
||||
|
||||
@@ -2,21 +2,9 @@
|
||||
|
||||
# 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
|
||||
#
|
||||
# Env: OMARCHY_SCREENRECORD_USE_PORTAL=true skips the built-in slurp picker and
|
||||
# uses gpu-screen-recorder's xdg-desktop-portal capture backend instead. The
|
||||
# portal backend was originally added (PR #3401) for HDR-aware capture, support
|
||||
# for monitors driven by external GPUs, and window capture — enable it if any
|
||||
# of those matter to you. Off by default because the portal path can fail EGL
|
||||
# DMA-BUF modifier import on some configurations, leaving recording unable to
|
||||
# start.
|
||||
#
|
||||
# Env: OMARCHY_SCREENRECORD_DEBUG=true appends gpu-screen-recorder's stderr (and
|
||||
# the picker target it was launched with) to /tmp/omarchy-screenrecord.log so
|
||||
# users can attach a log when reporting capture failures.
|
||||
|
||||
[[ -f ~/.config/user-dirs.dirs ]] && source ~/.config/user-dirs.dirs
|
||||
OUTPUT_DIR="${OMARCHY_SCREENRECORD_DIR:-${XDG_VIDEOS_DIR:-$HOME/Videos}}"
|
||||
@@ -31,10 +19,8 @@ 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")
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
@@ -43,7 +29,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
|
||||
@@ -102,95 +87,7 @@ default_resolution() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Monitor + window rectangles on the focused workspace, in slurp's "X,Y WxH" format.
|
||||
# Mirrors omarchy-capture-screenshot so the picker UX is identical.
|
||||
get_rectangles() {
|
||||
local active_workspace=$(hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .activeWorkspace.id')
|
||||
hyprctl monitors -j | jq -r --arg ws "$active_workspace" '
|
||||
.[] | select(.activeWorkspace.id == ($ws | tonumber)) |
|
||||
"\(.x),\(.y) \(.width / .scale | floor)x\(.height / .scale | floor)"'
|
||||
hyprctl clients -j | jq -r --arg ws "$active_workspace" '
|
||||
.[] | select(.workspace.id == ($ws | tonumber)) |
|
||||
"\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"'
|
||||
}
|
||||
|
||||
# Echoes "monitor:NAME" when the selection matches an entire monitor, otherwise
|
||||
# "region:WxH+X+Y" with physical-pixel coordinates ready for gpu-screen-recorder.
|
||||
# Returns non-zero if the user cancelled the picker.
|
||||
select_capture_target() {
|
||||
local rects=$(get_rectangles)
|
||||
hyprpicker -r -z >/dev/null 2>&1 &
|
||||
local picker_pid=$!
|
||||
sleep .1
|
||||
local selection=$(echo "$rects" | slurp 2>/dev/null)
|
||||
kill $picker_pid 2>/dev/null
|
||||
|
||||
# X and Y can be negative (Hyprland monitor positions in multi-display layouts);
|
||||
# widths and heights are always positive.
|
||||
[[ $selection =~ ^(-?[0-9]+),(-?[0-9]+)[[:space:]]([0-9]+)x([0-9]+)$ ]] || return 1
|
||||
local sx=${BASH_REMATCH[1]} sy=${BASH_REMATCH[2]}
|
||||
local sw=${BASH_REMATCH[3]} sh=${BASH_REMATCH[4]}
|
||||
|
||||
# A bare click (area < 20px²) snaps to whichever rectangle the click landed
|
||||
# inside, so users don't end up with accidental 2px recordings.
|
||||
if ((sw * sh < 20)); then
|
||||
while IFS= read -r rect; do
|
||||
[[ $rect =~ ^(-?[0-9]+),(-?[0-9]+)[[:space:]]([0-9]+)x([0-9]+)$ ]] || continue
|
||||
local rx=${BASH_REMATCH[1]} ry=${BASH_REMATCH[2]}
|
||||
local rw=${BASH_REMATCH[3]} rh=${BASH_REMATCH[4]}
|
||||
if ((sx >= rx && sx < rx + rw && sy >= ry && sy < ry + rh)); then
|
||||
sx=$rx sy=$ry sw=$rw sh=$rh
|
||||
break
|
||||
fi
|
||||
done <<<"$rects"
|
||||
fi
|
||||
|
||||
# When the selection exactly matches a monitor, prefer -w <monitor> over a
|
||||
# region capture — same kms backend, but no scaling math and full native res.
|
||||
local monitor=$(hyprctl monitors -j | jq -r --argjson x "$sx" --argjson y "$sy" --argjson w "$sw" --argjson h "$sh" '
|
||||
.[] | select(.x == $x and .y == $y and (.width / .scale | floor) == $w and (.height / .scale | floor) == $h) | .name' | head -1)
|
||||
|
||||
if [[ -n $monitor ]]; then
|
||||
echo "monitor:$monitor"
|
||||
return
|
||||
fi
|
||||
|
||||
# gpu-screen-recorder wants region geometry in the compositor's logical
|
||||
# coordinate space — same space slurp returns — so pass the values through
|
||||
# untouched. (gsr scales to physical pixels itself based on the monitor.)
|
||||
echo "region:${sw}x${sh}+${sx}+${sy}"
|
||||
}
|
||||
|
||||
start_screenrecording() {
|
||||
local capture_args=()
|
||||
local target
|
||||
|
||||
# Opt-in path for HDR, external-GPU monitors, and window capture (all things
|
||||
# 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
|
||||
target="portal"
|
||||
capture_args=(-w portal -s "${RESOLUTION:-$(default_resolution)}")
|
||||
else
|
||||
target=$(select_capture_target) || return 1
|
||||
|
||||
case $target in
|
||||
monitor:*)
|
||||
capture_args=(-w "${target#monitor:}" -s "${RESOLUTION:-$(default_resolution)}")
|
||||
;;
|
||||
region:*)
|
||||
capture_args=(-w "${target#region:}")
|
||||
[[ -n $RESOLUTION ]] && capture_args+=(-s "$RESOLUTION")
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
[[ $WEBCAM == "true" ]] && start_webcam_overlay
|
||||
|
||||
local filename="$OUTPUT_DIR/screenrecording-$(date +'%Y-%m-%d_%H-%M-%S').mp4"
|
||||
local audio_devices=""
|
||||
local audio_args=()
|
||||
@@ -205,10 +102,12 @@ start_screenrecording() {
|
||||
|
||||
[[ -n $audio_devices ]] && audio_args+=(-a "$audio_devices" -ac aac)
|
||||
|
||||
echo "===== $(date '+%F %T') args: $* target: $target =====" >>"$LOG_FILE"
|
||||
gpu-screen-recorder "${capture_args[@]}" -k auto -f 60 -fm cfr -fallback-cpu-encoding yes -o "$filename" "${audio_args[@]}" 2>>"$LOG_FILE" &
|
||||
local resolution="${RESOLUTION:-$(default_resolution)}"
|
||||
|
||||
gpu-screen-recorder -w portal -k auto -s "$resolution" -f 60 -fm cfr -fallback-cpu-encoding yes -o "$filename" "${audio_args[@]}" &
|
||||
local pid=$!
|
||||
|
||||
# Wait for recording to actually start (file appears after portal selection)
|
||||
while kill -0 $pid 2>/dev/null && [[ ! -f $filename ]]; do
|
||||
sleep 0.2
|
||||
done
|
||||
@@ -238,16 +137,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 +153,7 @@ stop_screenrecording() {
|
||||
}
|
||||
|
||||
toggle_screenrecording_indicator() {
|
||||
omarchy-shell -q Indicators refresh
|
||||
pkill -RTMIN+8 waybar
|
||||
}
|
||||
|
||||
screenrecording_active() {
|
||||
@@ -278,10 +175,7 @@ finalize_recording() {
|
||||
# Trim the first frame, and normalize audio to -14 LUFS if present, in a single pass
|
||||
local args=(-y -ss 0.1 -i "$latest" "${video_codec[@]}")
|
||||
if ffprobe -v error -select_streams a -show_entries stream=codec_type -of csv=p=0 "$latest" 2>/dev/null | grep -q audio; then
|
||||
# Hard-mute the first 400ms to drop the PipeWire capture-open pop (a near-clipping
|
||||
# transient around 130-200ms that a gentle fade-in can't attenuate enough), then a
|
||||
# 50ms fade avoids a click at the boundary before loudnorm normalizes the rest.
|
||||
args+=(-af "volume=enable='lt(t,0.4)':volume=0,afade=t=in:st=0.4:d=0.05,loudnorm=I=-14:TP=-1.5:LRA=11")
|
||||
args+=(-af loudnorm=I=-14:TP=-1.5:LRA=11)
|
||||
fi
|
||||
|
||||
local processed="${latest%.mp4}-processed.mp4"
|
||||
@@ -297,5 +191,7 @@ if screenrecording_active; then
|
||||
elif [[ $STOP_RECORDING == "true" ]]; then
|
||||
exit 1
|
||||
else
|
||||
[[ $WEBCAM == "true" ]] && start_webcam_overlay
|
||||
|
||||
start_screenrecording || cleanup_webcam
|
||||
fi
|
||||
|
||||
@@ -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"
|
||||
@@ -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,26 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Extract text from a screenshot region with OCR
|
||||
# omarchy:group=capture
|
||||
# omarchy:examples=omarchy capture ocr
|
||||
|
||||
# Keep hyprpicker alive until after grim captures so the screenshot sees the
|
||||
# frozen overlay rather than live content shifting during teardown.
|
||||
cleanup_freeze() {
|
||||
[[ -n $PID ]] && kill $PID 2>/dev/null
|
||||
}
|
||||
trap cleanup_freeze EXIT
|
||||
|
||||
hyprpicker -r -z >/dev/null 2>&1 &
|
||||
PID=$!
|
||||
sleep .1
|
||||
SELECTION=$(slurp 2>/dev/null)
|
||||
|
||||
[[ -z $SELECTION ]] && exit 0
|
||||
|
||||
TEXT=$(grim -g "$SELECTION" - | tesseract stdin stdout --oem 1 --psm 6 -l "${OMARCHY_OCR_LANGS:-eng}" --dpi 300 -c preserve_interword_spaces=1 2>/dev/null) || exit 1
|
||||
|
||||
[[ -z $TEXT ]] && exit 1
|
||||
|
||||
printf "%s" "$TEXT" | wl-copy
|
||||
omarchy-notification-send -g "Copied text from selection to clipboard"
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,7 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Print the current working directory of the active terminal window
|
||||
# omarchy:hidden=true
|
||||
|
||||
terminal_pid=$(hyprctl activewindow | awk '/pid:/ {print $2}')
|
||||
shell_pid=$(pgrep -P "$terminal_pid" | tail -n1)
|
||||
|
||||
@@ -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
@@ -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:"
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Set the default browser for Omarchy and XDG handlers
|
||||
# omarchy:args=[chromium|chrome|brave|brave-origin|edge|firefox|zen]
|
||||
# omarchy:examples=omarchy default browser firefox | omarchy default browser brave
|
||||
|
||||
if (($# == 0)); then
|
||||
case "$(xdg-settings get default-web-browser)" in
|
||||
chromium.desktop) echo "chromium" ;;
|
||||
google-chrome.desktop) echo "chrome" ;;
|
||||
brave-browser.desktop) echo "brave" ;;
|
||||
brave-origin-beta.desktop) echo "brave-origin" ;;
|
||||
microsoft-edge.desktop) echo "edge" ;;
|
||||
firefox.desktop) echo "firefox" ;;
|
||||
zen.desktop) echo "zen" ;;
|
||||
*) xdg-settings get default-web-browser ;;
|
||||
esac
|
||||
exit 0
|
||||
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= ;;
|
||||
*)
|
||||
echo "Usage: omarchy-default-browser <chromium|chrome|brave|brave-origin|edge|firefox|zen>"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
xdg-settings set default-web-browser "$desktop_id"
|
||||
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"
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Set the default editor used by omarchy-launch-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"
|
||||
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= ;;
|
||||
*)
|
||||
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"
|
||||
|
||||
omarchy-notification-send -g $glyph "$name is now the default editor"
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Set the default terminal used by xdg-terminal-exec
|
||||
# omarchy:args=[alacritty|foot|ghostty|kitty]
|
||||
# omarchy:examples=omarchy default terminal ghostty | omarchy default terminal kitty
|
||||
|
||||
if (($# == 0)); then
|
||||
desktop_id=$(grep -vE '^($|#)' ~/.config/xdg-terminals.list 2>/dev/null | head -n 1)
|
||||
case "$desktop_id" in
|
||||
Alacritty.desktop) echo "alacritty" ;;
|
||||
foot.desktop) echo "foot" ;;
|
||||
com.mitchellh.ghostty.desktop) echo "ghostty" ;;
|
||||
kitty.desktop) echo "kitty" ;;
|
||||
*) echo "$desktop_id" ;;
|
||||
esac
|
||||
exit 0
|
||||
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= ;;
|
||||
*)
|
||||
echo "Usage: omarchy-default-terminal <alacritty|foot|ghostty|kitty>"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
cat >~/.config/xdg-terminals.list <<EOF
|
||||
# Terminal emulator preference order for xdg-terminal-exec
|
||||
# The first found and valid terminal will be used
|
||||
$desktop_id
|
||||
EOF
|
||||
|
||||
omarchy-notification-send -g $glyph "$name is now the default terminal"
|
||||
@@ -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)"
|
||||
@@ -22,8 +22,7 @@ show_json() {
|
||||
{name: "args", required: false, type: "string", note: "Only set when the command accepts arguments."},
|
||||
{name: "examples", required: false, type: "string", note: "Pipe-separated examples."},
|
||||
{name: "aliases", required: false, type: "string", note: "Pipe-separated alternate routes, e.g. omarchy screenshot."},
|
||||
{name: "requires-sudo", required: false, type: "true", default: false, note: "Only include when true."},
|
||||
{name: "hidden", required: false, type: "true", default: false, note: "Hide from default command listings; visible with --all."}
|
||||
{name: "requires-sudo", required: false, type: "true", default: false, note: "Only include when true."}
|
||||
]
|
||||
}'
|
||||
}
|
||||
@@ -44,7 +43,6 @@ Inferred defaults:
|
||||
route omarchy <group> <name>
|
||||
binary filename
|
||||
requires-sudo false
|
||||
hidden false
|
||||
|
||||
Optional fields:
|
||||
# omarchy:group=<group> route override only
|
||||
@@ -53,7 +51,6 @@ Optional fields:
|
||||
# omarchy:examples=<cmd> | <cmd> pipe-separated examples
|
||||
# omarchy:aliases=<route> | <route> pipe-separated alternate routes
|
||||
# omarchy:requires-sudo=true only when true
|
||||
# omarchy:hidden=true hide from default command listings
|
||||
|
||||
Do not define:
|
||||
binary inferred from filename
|
||||
@@ -61,7 +58,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
|
||||
|
||||
@@ -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."
|
||||
@@ -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
@@ -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
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Select a drive from a list with info that includes space and brand. Used by omarchy-drive-password.
|
||||
# omarchy:summary=Select a drive from a list with info that includes space and brand. Used by omarchy-drive-set-password.
|
||||
|
||||
if (($# == 0)); then
|
||||
drives=$(lsblk -dpno NAME | grep -E '/dev/(sd|hd|vd|nvme|mmcblk|xv)')
|
||||
|
||||
@@ -10,15 +10,14 @@ 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/gtk-primary-paste.sh"
|
||||
omarchy-hook-install post-update "$OMARCHY_PATH/install/first-run/install-voxtype.hook"
|
||||
sudo /bin/rm -f /etc/sudoers.d/first-run
|
||||
bash "$OMARCHY_PATH/install/first-run/elephant.sh"
|
||||
sudo rm -f /etc/sudoers.d/first-run
|
||||
|
||||
bash "$OMARCHY_PATH/install/first-run/welcome.sh"
|
||||
bash "$OMARCHY_PATH/install/first-run/wifi.sh"
|
||||
|
||||
@@ -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
|
||||
|
||||
+6
-12
@@ -22,25 +22,19 @@ if [[ -n $font_name ]]; then
|
||||
pkill -SIGUSR2 ghostty
|
||||
fi
|
||||
|
||||
if [[ -f ~/.config/foot/foot.ini ]]; 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"
|
||||
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 Ghostty to see font change"
|
||||
fi
|
||||
|
||||
omarchy-hook font-set "$font_name"
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
Executable
+96
@@ -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()
|
||||
@@ -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"
|
||||
|
||||
+2
-11
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Run a named hook from ~/.config/omarchy/hooks/<name> and ~/.config/omarchy/hooks/<name>.d/.
|
||||
# omarchy:summary=Run a named hook, like post-update (available in ~/.config/omarchy/hooks/post-update).
|
||||
# omarchy:args=[name] [args...]
|
||||
|
||||
set -e
|
||||
@@ -12,17 +12,8 @@ fi
|
||||
|
||||
HOOK=$1
|
||||
HOOK_PATH="$HOME/.config/omarchy/hooks/$1"
|
||||
HOOK_DIR="$HOOK_PATH.d"
|
||||
shift
|
||||
|
||||
if [[ -f $HOOK_PATH ]]; then
|
||||
bash "$HOOK_PATH" "$@" || echo "Hook failed: $HOOK_PATH"
|
||||
fi
|
||||
|
||||
if [[ -d $HOOK_DIR ]]; then
|
||||
for hook in "$HOOK_DIR"/*; do
|
||||
[[ -f $hook ]] || continue
|
||||
[[ $hook == *.sample ]] && continue
|
||||
bash "$hook" "$@" || echo "Hook failed: $hook"
|
||||
done
|
||||
bash "$HOOK_PATH" "$@"
|
||||
fi
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Install a hook into ~/.config/omarchy/hooks/<type>.d/
|
||||
# omarchy:group=hook
|
||||
# omarchy:name=install
|
||||
# omarchy:args=<type> <file>
|
||||
# omarchy:examples=omarchy hook install post-update ~/my-hook
|
||||
|
||||
set -e
|
||||
|
||||
if (( $# != 2 )); then
|
||||
echo "Usage: omarchy-hook-install <type> <file>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
HOOK_TYPE=$1
|
||||
HOOK_FILE=$2
|
||||
HOOK_DIR="$HOME/.config/omarchy/hooks/$HOOK_TYPE.d"
|
||||
HOOK_NAME=$(basename "$HOOK_FILE")
|
||||
HOOK_PATH="$HOOK_DIR/$HOOK_NAME"
|
||||
|
||||
if [[ ! -f $HOOK_FILE ]]; then
|
||||
echo "Hook file not found: $HOOK_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$HOOK_DIR"
|
||||
cp "$HOOK_FILE" "$HOOK_PATH"
|
||||
chmod 755 "$HOOK_PATH"
|
||||
|
||||
echo "Installed $HOOK_TYPE hook: $HOOK_PATH"
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Detect ASUS Zenbook UX5406AA series laptops on Intel Panther Lake.
|
||||
|
||||
omarchy-hw-match "ux5406aa" && omarchy-hw-intel-ptl
|
||||
@@ -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 ]]
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Detect whether the computer has an NVIDIA GPU.
|
||||
|
||||
lspci | grep -qi 'nvidia' &>/dev/null
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Detect whether the computer has an NVIDIA GPU with GSP firmware (Turing or newer).
|
||||
|
||||
# GTX 16xx, RTX 20xx-50xx, RTX Pro, Quadro RTX, datacenter A/H/T/L series.
|
||||
lspci | grep -i 'nvidia' | grep -qE "GTX 16[0-9]{2}|RTX [2-5][0-9]{3}|RTX PRO [0-9]{4}|Quadro RTX|RTX A[0-9]{4}|A[1-9][0-9]{2}|H[1-9][0-9]{2}|T4|L[0-9]+"
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Detect whether the computer has an NVIDIA GPU without GSP firmware (Maxwell/Pascal/Volta).
|
||||
|
||||
# GTX 9xx/10xx, GT 10xx, Quadro P/M/GV, MX series, Titan X/Xp/V, Tesla V100.
|
||||
lspci | grep -i 'nvidia' | grep -qE "GTX (9[0-9]{2}|10[0-9]{2})|GT 10[0-9]{2}|Quadro [PM][0-9]{3,4}|Quadro GV100|MX *[0-9]+|Titan (X|Xp|V)|Tesla V100"
|
||||
@@ -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"
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Toggles transparency for the currently focused window.
|
||||
|
||||
hyprctl dispatch setprop "address:$(hyprctl activewindow -j | jq -r '.address')" opaque toggle
|
||||
@@ -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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user