Compare commits
35
Commits
No files matched your search
@@ -1,2 +0,0 @@
|
||||
# Merges to protected branches need sign-off from an org owner.
|
||||
* @dhh @ryanrhughes
|
||||
@@ -1,47 +0,0 @@
|
||||
# Security at Omarchy
|
||||
|
||||
## Report a vulnerability
|
||||
|
||||
If you believe you’ve found a security vulnerability in Omarchy, please tell the [Omarchy Security Team](https://omarchy.org/teams/#security) privately so we have an opportunity to investigate and fix it before it is made public.
|
||||
|
||||
[security@omarchy.org](mailto:security@omarchy.org?subject=Security%20report)
|
||||
|
||||
Please don’t report potential vulnerabilities publicly in GitHub Issues, Discord, or social media before they’ve been resolved.
|
||||
|
||||
## What is a vulnerability?
|
||||
|
||||
We consider a bug a security vulnerability when it can be exploited to cross a meaningful security boundary: an untrusted or lower-privileged party gains access, permissions, or control they didn’t already have.
|
||||
|
||||
Code that could be more robust but does not cross a security boundary is an improvement rather than a security vulnerability. We may still merge a proposed fix and credit the reporter in our release notes.
|
||||
|
||||
Eligibility for our [security credits](https://omarchy.org/security/credits/) page depends on whether a report identifies a confirmed security vulnerability, not on its severity.
|
||||
|
||||
## What to include
|
||||
|
||||
Give us enough information to understand and reproduce the issue:
|
||||
|
||||
- The affected component and Omarchy version.
|
||||
- An explanation of what an attacker can do before and after exploitation.
|
||||
- Steps to reproduce the issue and any proof of concept.
|
||||
- Your preferred contact details for follow-up.
|
||||
|
||||
## Responsible disclosure
|
||||
|
||||
Please act in good faith while investigating and reporting vulnerabilities:
|
||||
|
||||
- Only test systems and accounts you own or have explicit permission to test.
|
||||
- Avoid privacy violations, disruption, data destruction, and service degradation.
|
||||
- Don’t exploit a vulnerability beyond what is needed to demonstrate it.
|
||||
- Give us a reasonable opportunity to investigate and address the issue before publishing details.
|
||||
|
||||
We’ll review your report and keep you informed as we’re able while we work toward a resolution.
|
||||
|
||||
## Credits
|
||||
|
||||
Researchers who privately report a confirmed security vulnerability and give us the chance to ship a fix are thanked on the [security credits](https://omarchy.org/security/credits/) page. Accepted improvements that don’t cross a security boundary may still be credited in our release notes.
|
||||
|
||||
Credits link to each reporter’s X profile and show their avatar. For duplicate reports, only the first reporter is eligible for credit.
|
||||
|
||||
## Regular bugs and support
|
||||
|
||||
For anything that isn’t a security vulnerability, please use the [Omarchy issue tracker](https://github.com/omacom/omarchy/issues).
|
||||
@@ -9,24 +9,14 @@ matching guide before starting:
|
||||
- [`agents/skills/icon-font.md`](agents/skills/icon-font.md) - adding branded glyphs to `default/fonts/omarchy/omarchy.ttf`
|
||||
- [`agents/skills/acceptance-tests.md`](agents/skills/acceptance-tests.md) - writing or running graphical acceptance tests under `test/acceptance.d/`
|
||||
- [`agents/skills/visual-verification.md`](agents/skills/visual-verification.md) - verifying any change with a visual effect in the running UI
|
||||
- [`agents/skills/migrations.md`](agents/skills/migrations.md) - creating or changing migrations under `migrations/`
|
||||
|
||||
# Documentation Layout
|
||||
|
||||
Three documentation trees, split by genre and audience:
|
||||
|
||||
- `agents/skills/` - task procedure ("do this when doing X"), for anyone working on the codebase
|
||||
- `docs/` - reference on how the system is shaped (file layout, update pipeline, theming, shell architecture), for anyone working on the codebase; skills link here for depth
|
||||
- `manual/` - end-user documentation for using Omarchy, published; never codebase internals
|
||||
- [`docs/migrations.md`](docs/migrations.md) - creating or changing migrations under `migrations/`
|
||||
|
||||
# Style
|
||||
|
||||
- In markdown documents (`plans/`, `docs/`, `manual/`), write full lines — no hard wrapping at 80 columns; break only at structural boundaries like headings and list items
|
||||
- Two spaces for indentation, no tabs
|
||||
- Use bash 5 conditionals: use `[[ ]]` for string/file tests and `(( ))` for numeric tests
|
||||
- In `[[ ]]`, don't quote variables, but do quote string literals when comparing values (e.g., `[[ $branch == "dev" ]]`)
|
||||
- Prefer `(( ))` over numeric operators inside `[[ ]]` (e.g., `(( count < 50 ))`, not `[[ $count -lt 50 ]]`)
|
||||
- Prefer a full `if`/`else` conditional for simple two-path control flow; don't rely on `exec` or `exit` in one branch to make following statements unreachable
|
||||
- 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
|
||||
@@ -91,8 +81,7 @@ Exceptions are allowed for migration and package-helper scripts where the helper
|
||||
|
||||
# Menu
|
||||
|
||||
- The menu definition lives in `default/omarchy/omarchy-menu.jsonc`;
|
||||
[`docs/menu.md`](docs/menu.md) covers the schema, guards, and providers.
|
||||
- The menu definition lives in `default/omarchy/omarchy-menu.jsonc`.
|
||||
- Do not add `aliases` to new menu entries. Aliases are reserved for
|
||||
established alternate names users already type, kept for compatibility.
|
||||
|
||||
@@ -104,9 +93,7 @@ Exceptions are allowed for migration and package-helper scripts where the helper
|
||||
|
||||
# Tests
|
||||
|
||||
Run focused automated tests for the area you changed;
|
||||
[`docs/testing.md`](docs/testing.md) covers how the suites are shaped. Current
|
||||
test entry points:
|
||||
Run focused automated tests for the area you changed. Current test entry points:
|
||||
|
||||
- `./test/all` - aggregate runner for CLI and shell tests; it intentionally does not run graphical acceptance tests
|
||||
- `./test/cli` - CLI routing, command metadata, theme helpers, and safe dispatch coverage
|
||||
|
||||
@@ -27,7 +27,6 @@ declare -A BINARY_TO_KEY
|
||||
declare -A GROUP_DESCRIPTIONS
|
||||
|
||||
GROUP_DESCRIPTIONS[agent]="AI coding agent usage data"
|
||||
GROUP_DESCRIPTIONS[ascii]="Text drawn as ASCII art"
|
||||
GROUP_DESCRIPTIONS[audio]="Audio input and output controls"
|
||||
GROUP_DESCRIPTIONS[bar]="Omarchy shell bar layout and settings"
|
||||
GROUP_DESCRIPTIONS[battery]="Battery status helpers"
|
||||
@@ -40,7 +39,6 @@ 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[crash]="Crash notification controls"
|
||||
GROUP_DESCRIPTIONS[debug]="Diagnostics and support logs"
|
||||
GROUP_DESCRIPTIONS[finalize]="Finalize user setup"
|
||||
GROUP_DESCRIPTIONS[default]="Default application selection"
|
||||
@@ -72,7 +70,6 @@ GROUP_DESCRIPTIONS[plymouth]="Plymouth boot theme management"
|
||||
GROUP_DESCRIPTIONS[power]="Power supply detection"
|
||||
GROUP_DESCRIPTIONS[powerprofiles]="Power profile management"
|
||||
GROUP_DESCRIPTIONS[refresh]="Reset config to defaults"
|
||||
GROUP_DESCRIPTIONS[region]="Region-specific sources and input method"
|
||||
GROUP_DESCRIPTIONS[reinstall]="Reinstall and reset workflows"
|
||||
GROUP_DESCRIPTIONS[reminder]="Desktop notification reminders"
|
||||
GROUP_DESCRIPTIONS[remove]="Removal workflows"
|
||||
|
||||
+3
-10
@@ -52,14 +52,14 @@ if omarchy-cmd-missing "$agent"; then
|
||||
fi
|
||||
|
||||
# Agents launched from the keybinding or menu run unattended, so each one starts
|
||||
# with its own spelling of "don't stop to ask". Pi and Ori have none to skip.
|
||||
# with its own spelling of "don't stop to ask". Pi has no approval prompts.
|
||||
case "$agent" in
|
||||
opencode)
|
||||
command=(opencode --auto)
|
||||
[[ -n ${prompt:-} ]] && command+=(--prompt "$prompt")
|
||||
;;
|
||||
agy)
|
||||
command=(agy --dangerously-skip-permissions)
|
||||
gemini)
|
||||
command=(gemini --yolo)
|
||||
[[ -n ${prompt:-} ]] && command+=(--prompt-interactive "$prompt")
|
||||
;;
|
||||
copilot)
|
||||
@@ -90,13 +90,6 @@ omp)
|
||||
command=(omp --auto-approve)
|
||||
[[ -n ${prompt:-} ]] && command+=(-- "$prompt")
|
||||
;;
|
||||
ori)
|
||||
# Ori is a harness launcher, and `ori code` is the agent it runs itself.
|
||||
# A prompt alone means one headless turn there, printed after the turn ends,
|
||||
# so --interactive is what seeds the session with it and keeps the window.
|
||||
command=(ori code)
|
||||
[[ -n ${prompt:-} ]] && command+=(--interactive --prompt "$prompt")
|
||||
;;
|
||||
pi)
|
||||
command=(pi)
|
||||
[[ -n ${prompt:-} ]] && command+=("$prompt")
|
||||
|
||||
@@ -528,7 +528,7 @@ def fetch_codex_rpc():
|
||||
|
||||
try:
|
||||
proc = subprocess.Popen(
|
||||
[codex, "-s", "read-only", "-a", "on-request", "app-server"],
|
||||
[codex, "-s", "read-only", "-a", "untrusted", "app-server"],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.DEVNULL,
|
||||
|
||||
@@ -16,7 +16,7 @@ flags=()
|
||||
only=()
|
||||
declare -A excluded
|
||||
|
||||
while (( $# > 0 )); do
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--force | --limits-only) flags+=("$1") ;;
|
||||
--except)
|
||||
|
||||
-1217
File diff suppressed because it is too large.
Load diff
@@ -1,104 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Shared helpers for animating the About branding (source this, don't run it).
|
||||
# omarchy:group=branding
|
||||
# omarchy:name=about-animation
|
||||
# omarchy:hidden=true
|
||||
|
||||
# The About branding's animation: a sheen, and the frames that make it. *When*
|
||||
# those frames are written stays with the caller, because that is inseparable
|
||||
# from how the caller's window closes and resizes; this only says what to write,
|
||||
# and where — it is handed a logo and knows nothing about About beyond that.
|
||||
|
||||
# A band of light leans across the logo, rests, and leans across again. Two
|
||||
# columns per row puts it at 45 degrees on screen, where a cell is twice as tall
|
||||
# as it is wide.
|
||||
ESC=$'\e'
|
||||
# Bright white, so the band does not depend on how the terminal reads bold. Foot's
|
||||
# bold-text-in-bright brightens a bold regular colour into its bright counterpart,
|
||||
# which turns a bold green logo bright green — exactly the colour a bright green
|
||||
# band would have used, leaving a glint nobody can see. No regular colour
|
||||
# brightens into bright white, so this one shows either way.
|
||||
SHEEN_BAND="${ESC}[1m${ESC}[97m"
|
||||
SHEEN_SLANT=2
|
||||
SHEEN_HALF=2
|
||||
SHEEN_FRAME_SECONDS=0.025
|
||||
SHEEN_REST_TICKS=8
|
||||
# Re-measure the grid every half second, the cadence a still logo already cost,
|
||||
# rather than spawning a process per frame.
|
||||
SHEEN_POLL_FRAMES=20
|
||||
|
||||
# A frame is one string holding every row of the logo, positioned and coloured,
|
||||
# so a tick writes the whole logo at once and never shows it half drawn.
|
||||
compose_frame() {
|
||||
local centre=$1
|
||||
local row line length at from to frame=""
|
||||
|
||||
for (( row = 0; row < SHEEN_ROWS; row++ )); do
|
||||
line=${SHEEN_LINES[row]}
|
||||
length=${#line}
|
||||
|
||||
# Where the band crosses this row. Running off the right needs no clamp,
|
||||
# because a slice that starts past the end of a line is already empty, but a
|
||||
# negative offset would count from the end of it instead of off the left.
|
||||
at=$(( centre - row * SHEEN_SLANT ))
|
||||
from=$(( at - SHEEN_HALF ))
|
||||
from=$(( from < 0 ? 0 : from ))
|
||||
to=$(( at + SHEEN_HALF + 1 ))
|
||||
to=$(( to < 0 ? 0 : to ))
|
||||
|
||||
frame+="${ESC}[$((SHEEN_TOP + row));${SHEEN_LEFT}H"
|
||||
frame+="${SHEEN_BASE}${line:0:from}${SHEEN_BAND}${line:from:to - from}${SHEEN_BASE}${line:to}"
|
||||
done
|
||||
|
||||
SHEEN_COMPOSED=$frame
|
||||
}
|
||||
|
||||
# Builds every frame up front: a tick that recomputed a logo's worth of colour
|
||||
# spans in bash would not hold the frame rate, and the sweep is the same every
|
||||
# time. Leaves them in SHEEN_FRAMES, and answers whether this logo can be
|
||||
# animated at all — one it cannot put back exactly as it found it is one to leave
|
||||
# alone, because nothing on screen would say the difference.
|
||||
#
|
||||
# sheen_build <logo-file> <top-row> <left-column> <base-colour> <columns-available>
|
||||
sheen_build() {
|
||||
local file=$1 columns=$5
|
||||
SHEEN_TOP=$2
|
||||
SHEEN_LEFT=$3
|
||||
SHEEN_BASE=$4
|
||||
|
||||
SHEEN_LINES=()
|
||||
# A failing redirection reports itself before 2>/dev/null would apply, so order
|
||||
# it first: the caller's window must not get a shell error painted across it.
|
||||
mapfile -t SHEEN_LINES 2>/dev/null <"$file" || return 1
|
||||
SHEEN_ROWS=${#SHEEN_LINES[@]}
|
||||
(( SHEEN_ROWS > 0 )) || return 1
|
||||
|
||||
local row line width=0
|
||||
for (( row = 0; row < SHEEN_ROWS; row++ )); do
|
||||
line=${SHEEN_LINES[row]}
|
||||
|
||||
# A renderer substitutes $1 to $9 for colours, so a logo written with those is
|
||||
# not the text that reached the screen.
|
||||
[[ $line == *'$'* ]] && return 1
|
||||
|
||||
# These frames slice the row by character and a terminal draws it by column,
|
||||
# so one character has to be one cell — and everything that breaks that breaks
|
||||
# it here. A wide glyph, a combining mark or a joined emoji is not one cell; a
|
||||
# tab or an escape is one the renderer expanded itself; and a shell whose
|
||||
# locale is counting bytes is not counting characters at all.
|
||||
(( ${#line} == $(printf '%s' "$line" | LC_ALL=C.UTF-8 wc -L) )) || return 1
|
||||
|
||||
if (( ${#line} > width )); then
|
||||
width=${#line}
|
||||
fi
|
||||
done
|
||||
(( width > 0 && width <= columns )) || return 1
|
||||
|
||||
SHEEN_FRAMES=()
|
||||
local centre last=$(( width + SHEEN_ROWS * SHEEN_SLANT + SHEEN_HALF ))
|
||||
for (( centre = -SHEEN_HALF; centre <= last; centre++ )); do
|
||||
compose_frame "$centre"
|
||||
SHEEN_FRAMES+=("$SHEEN_COMPOSED")
|
||||
done
|
||||
}
|
||||
@@ -4,13 +4,7 @@
|
||||
# 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%
|
||||
|
||||
# Only cache under the user-private runtime dir. With no XDG_RUNTIME_DIR we skip
|
||||
# caching (detect every run) rather than fall back to a predictable, world-writable
|
||||
# /tmp path another user could pre-create.
|
||||
device_cache=""
|
||||
if [[ -n ${XDG_RUNTIME_DIR:-} ]]; then
|
||||
device_cache="$XDG_RUNTIME_DIR/omarchy-brightness-display-apple.device"
|
||||
fi
|
||||
device_cache="${XDG_RUNTIME_DIR:-/tmp}/omarchy-brightness-display-apple.device"
|
||||
no_osd=0
|
||||
if [[ ${1:-} == "--no-osd" ]]; then
|
||||
no_osd=1
|
||||
@@ -34,14 +28,9 @@ find_apple_display_device() {
|
||||
local cached=""
|
||||
local device=""
|
||||
|
||||
if [[ -n $device_cache && -r $device_cache ]]; then
|
||||
if [[ -r $device_cache ]]; then
|
||||
read -r cached <"$device_cache" || true
|
||||
# Trust a cached value only if it still names a hiddev character device. A
|
||||
# stale or unexpected cache (a regular file, a non-hiddev node) is ignored and
|
||||
# we re-detect instead of handing an arbitrary path to asdcontrol. The globs
|
||||
# are left unquoted on purpose: [[ ]] pattern-matches an unquoted right side,
|
||||
# and quoting them would turn the match into a literal string comparison.
|
||||
if [[ ( $cached == /dev/hiddev* || $cached == /dev/usb/hiddev* ) && -c $cached ]]; then
|
||||
if [[ -n $cached && -e $cached ]]; then
|
||||
printf '%s\n' "$cached"
|
||||
return 0
|
||||
fi
|
||||
@@ -50,9 +39,7 @@ find_apple_display_device() {
|
||||
device="$(detect_apple_display_device)" || return 1
|
||||
[[ -n $device ]] || return 1
|
||||
|
||||
if [[ -n $device_cache ]]; then
|
||||
printf '%s\n' "$device" >"$device_cache"
|
||||
fi
|
||||
printf '%s\n' "$device" >"$device_cache"
|
||||
printf '%s\n' "$device"
|
||||
}
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ main() {
|
||||
local length payload url
|
||||
|
||||
# Detached worker: this is what actually runs yt-dlp and fires notifications.
|
||||
if [[ ${1:-} == "--download" ]]; then
|
||||
if [[ "${1:-}" == "--download" ]]; then
|
||||
download_url "$2"
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Silence crash notifications for one program, or list what is silenced
|
||||
# omarchy:args=[--] [<program>] [on|off|toggle]
|
||||
# omarchy:examples=omarchy crash mute | omarchy crash mute hyprland | omarchy crash mute /usr/bin/hyprland | omarchy crash mute hyprland off
|
||||
|
||||
# The flag omarchy-crash-watch reads before announcing a crash. Muting is per
|
||||
# program; Trigger > Toggle > Crash Capture is the switch for all of them.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
readonly MUTES="$HOME/.local/state/omarchy/toggles/crash-ignore"
|
||||
|
||||
usage() {
|
||||
echo "Usage: omarchy crash mute [--] [<program>] [on|off|toggle]" >&2
|
||||
}
|
||||
|
||||
# Only regular files, because that is all the watcher honours: anything else in
|
||||
# there would be reported as muted while the crashes kept arriving. The dotted
|
||||
# glob is for a program legitimately called .hidden, and `.` and `..` fail the
|
||||
# same -f test that keeps them out.
|
||||
list() {
|
||||
local entry found=0
|
||||
|
||||
for entry in "$MUTES"/* "$MUTES"/.*; do
|
||||
[[ -f $entry ]] || continue
|
||||
printf '%s\n' "${entry##*/}"
|
||||
found=1
|
||||
done
|
||||
|
||||
((found)) || echo "No programs muted. Crashes all notify."
|
||||
}
|
||||
|
||||
# A program may be named -h, and the router answers that with its own help
|
||||
# before this ever runs. `omarchy crash mute -- -h` is the way through.
|
||||
[[ ${1:-} == "--" ]] && shift
|
||||
|
||||
if (($# == 0)); then
|
||||
list
|
||||
exit 0
|
||||
fi
|
||||
|
||||
program=$1
|
||||
action=${2:-on}
|
||||
|
||||
# The watcher keys the mute on the executable's basename, so accept the path it
|
||||
# reports as readily as the name, and reduce either the same way it does.
|
||||
program=${program##*/}
|
||||
|
||||
if [[ -z $program || $program == "." || $program == ".." ]]; then
|
||||
echo "Not a program name: $1" >&2
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$action" in
|
||||
on|off|toggle) ;;
|
||||
*)
|
||||
echo "Not an action: $action" >&2
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
omarchy-toggle "crash-ignore/$program" "$action" || exit 1
|
||||
|
||||
# Report what is now true rather than what was asked for: the flag is what the
|
||||
# watcher reads, and a toggle does not say which way it went.
|
||||
if omarchy-toggle-enabled "crash-ignore/$program"; then
|
||||
echo "Muted crash notifications for $program."
|
||||
else
|
||||
echo "Crash notifications for $program are back on."
|
||||
fi
|
||||
+5
-28
@@ -48,17 +48,12 @@ announce() {
|
||||
# -n 0 so a restart does not re-announce crashes already dealt with.
|
||||
journalctl -f -n 0 -o json "MESSAGE_ID=$COREDUMP_MESSAGE_ID" 2>/dev/null |
|
||||
while IFS= read -r entry; do
|
||||
# A dash for a field that is empty as well as one that is missing: tab is
|
||||
# IFS whitespace, so an empty field collapses into the next delimiter and
|
||||
# every field after it shifts along one. A process can set its own comm to
|
||||
# nothing, and that crash used to be read as somebody else's and dropped.
|
||||
IFS=$'\t' read -r uid comm pid exe signal < <(
|
||||
jq -r 'def field: if . == null or . == "" then "-" else . end;
|
||||
[(._UID | field),
|
||||
(.COREDUMP_COMM | field),
|
||||
(.COREDUMP_PID | field),
|
||||
(.COREDUMP_EXE | field),
|
||||
(.COREDUMP_SIGNAL_NAME | field)] | @tsv' <<<"$entry" 2>/dev/null
|
||||
jq -r '[(._UID // "-"),
|
||||
(.COREDUMP_COMM // "-"),
|
||||
(.COREDUMP_PID // "-"),
|
||||
(.COREDUMP_EXE // "-"),
|
||||
(.COREDUMP_SIGNAL_NAME // "-")] | @tsv' <<<"$entry" 2>/dev/null
|
||||
)
|
||||
|
||||
[[ $pid =~ ^[0-9]+$ ]] || continue
|
||||
@@ -76,29 +71,11 @@ journalctl -f -n 0 -o json "MESSAGE_ID=$COREDUMP_MESSAGE_ID" 2>/dev/null |
|
||||
name=$comm
|
||||
[[ $exe == /* ]] && name=${exe##*/}
|
||||
|
||||
# A process can set its own comm to anything prctl takes, slashes included,
|
||||
# and a crash with no recorded executable falls back to it. The mute below
|
||||
# turns this name into a path, so keep it one component: a crash must not
|
||||
# reach a flag outside crash-ignore/, nor have a diagnosis write one there.
|
||||
name=${name##*/}
|
||||
|
||||
# What that leaves is not always a name. "/" leaves nothing, which is no
|
||||
# kind of array subscript and no kind of toast; a dot component names a
|
||||
# directory rather than a flag, so a mute on it would touch that directory
|
||||
# and then never match; and a dash is what the read above puts there when
|
||||
# the crash recorded no name at all.
|
||||
[[ -n $name && $name != "-" && $name != "." && $name != ".." ]] || name=unknown
|
||||
|
||||
[[ -n $ignore_pattern && $name =~ $ignore_pattern ]] && continue
|
||||
|
||||
# Never announce our own machinery, or it notifies about itself.
|
||||
[[ $name == omarchy-crash-* || $name == omarchy-agent-* ]] && continue
|
||||
|
||||
# Muted at the end of a diagnosis, when the user was offered it and said
|
||||
# yes. A flag per program rather than one list, so omarchy-crash-mute can
|
||||
# lift one without reading, rewriting and re-parsing the rest.
|
||||
omarchy-toggle-enabled "crash-ignore/$name" && continue
|
||||
|
||||
now=$EPOCHSECONDS
|
||||
(((now - ${last_notified[$name]:-0}) < dedupe_seconds)) && continue
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Set and launch the default coding agent
|
||||
# omarchy:args=[pi|omp|opencode|ori|claude|codex|grok|agy|copilot|crush]
|
||||
# omarchy:args=[pi|omp|opencode|claude|codex|grok|gemini|copilot|crush]
|
||||
# omarchy:examples=omarchy default agent | omarchy default agent codex | omarchy default agent claude
|
||||
|
||||
installing=false
|
||||
@@ -27,15 +27,14 @@ case "$1" in
|
||||
pi) agent="pi"; name="Pi" ;;
|
||||
omp | oh-my-pi) agent="omp"; name="Oh My Pi"; agent_package="github:can1357/oh-my-pi" ;;
|
||||
opencode | open-code) agent="opencode"; name="OpenCode" ;;
|
||||
ori | openrouter) agent="ori"; name="Ori"; agent_package="github:OpenRouterLabs/ori-releases" ;;
|
||||
claude | claude-code) agent="claude"; name="Claude Code" ;;
|
||||
codex) agent="codex"; name="Codex" ;;
|
||||
crush) agent="crush"; name="Crush" ;;
|
||||
grok) agent="grok"; name="Grok"; agent_package="npm:@xai-official/grok" ;;
|
||||
agy | antigravity | antigravity-cli | gemini | gemini-cli) agent="agy"; name="Antigravity"; agent_package="antigravity-cli" ;;
|
||||
gemini | gemini-cli) agent="gemini"; name="Gemini" ;;
|
||||
copilot | github-copilot) agent="copilot"; name="GitHub Copilot" ;;
|
||||
*)
|
||||
echo "Usage: omarchy-default-agent <pi|omp|opencode|ori|claude|codex|grok|agy|copilot|crush>"
|
||||
echo "Usage: omarchy-default-agent <pi|omp|opencode|claude|codex|grok|gemini|copilot|crush>"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -4,12 +4,6 @@
|
||||
# omarchy:args=[chromium|chrome|brave|brave-origin|edge|firefox|zen]
|
||||
# omarchy:examples=omarchy default browser firefox | omarchy default browser brave
|
||||
|
||||
installing=false
|
||||
if [[ ${1:-} == "--install" ]]; then
|
||||
installing=true
|
||||
shift
|
||||
fi
|
||||
|
||||
if (($# == 0)); then
|
||||
case "$(env -u BROWSER xdg-settings get default-web-browser)" in
|
||||
chromium.desktop) echo "chromium" ;;
|
||||
@@ -25,27 +19,19 @@ if (($# == 0)); then
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
chromium) browser="chromium"; command="chromium"; desktop_id="chromium.desktop"; name="Chromium"; glyph= ;;
|
||||
chrome) browser="chrome"; command="google-chrome-stable"; desktop_id="google-chrome.desktop"; name="Chrome"; glyph= ;;
|
||||
brave) browser="brave"; command="brave"; desktop_id="brave-browser.desktop"; name="Brave"; glyph= ;;
|
||||
brave-origin) browser="brave-origin"; command="brave-origin"; desktop_id="brave-origin.desktop"; name="Brave Origin"; glyph= ;;
|
||||
edge) browser="edge"; command="microsoft-edge-stable"; desktop_id="microsoft-edge.desktop"; name="Edge"; glyph= ;;
|
||||
firefox) browser="firefox"; command="firefox"; desktop_id="firefox.desktop"; name="Firefox"; glyph= ;;
|
||||
zen) browser="zen"; command="zen-browser"; desktop_id="zen.desktop"; name="Zen"; glyph= ;;
|
||||
chromium) desktop_id="chromium.desktop"; name="Chromium"; glyph= ;;
|
||||
chrome) desktop_id="google-chrome.desktop"; name="Chrome"; glyph= ;;
|
||||
brave) desktop_id="brave-browser.desktop"; name="Brave"; glyph= ;;
|
||||
brave-origin) desktop_id="brave-origin.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
|
||||
|
||||
if omarchy-cmd-missing "$command"; then
|
||||
if [[ $installing == "false" ]]; then
|
||||
exec omarchy-launch-floating-terminal-with-presentation omarchy-default-browser --install "$browser"
|
||||
else
|
||||
omarchy-install-browser "$browser" || exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
env -u BROWSER xdg-settings set default-web-browser "$desktop_id" || exit 1
|
||||
|
||||
omarchy-notification-send -g $glyph "$name is now the default browser"
|
||||
@@ -4,12 +4,6 @@
|
||||
# omarchy:args=[code|cursor|zed|sublime_text|helix|vim|emacs|nvim]
|
||||
# omarchy:examples=omarchy default editor | omarchy default editor code | omarchy default editor helix
|
||||
|
||||
installing=false
|
||||
if [[ ${1:-} == "--install" ]]; then
|
||||
installing=true
|
||||
shift
|
||||
fi
|
||||
|
||||
editor_file="$HOME/.local/state/omarchy/defaults/editor"
|
||||
|
||||
if (($# == 0)); then
|
||||
@@ -22,37 +16,20 @@ if (($# == 0)); then
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
code) selection="code"; editor="code"; name="VSCode"; glyph= ;;
|
||||
cursor) selection="cursor"; editor="cursor"; name="Cursor"; glyph= ;;
|
||||
zed | zeditor) selection="zed"; editor="zeditor"; name="Zed"; glyph= ;;
|
||||
sublime_text) selection="sublime_text"; editor="sublime_text"; name="Sublime Text"; glyph= ;;
|
||||
helix) selection="helix"; editor="helix"; name="Helix"; glyph= ;;
|
||||
vim) selection="vim"; editor="vim"; name="Vim"; glyph= ;;
|
||||
emacs) selection="emacs"; editor="emacs"; name="Emacs"; glyph= ;;
|
||||
nvim) selection="nvim"; editor="nvim"; name="Neovim"; glyph= ;;
|
||||
code) editor="code"; name="VSCode"; glyph= ;;
|
||||
cursor) editor="cursor"; name="Cursor"; glyph= ;;
|
||||
zed | zeditor) editor="zeditor"; name="Zed"; glyph= ;;
|
||||
sublime_text) editor="sublime_text"; name="Sublime Text"; glyph= ;;
|
||||
helix) editor="helix"; name="Helix"; glyph= ;;
|
||||
vim) editor="vim"; name="Vim"; glyph= ;;
|
||||
emacs) editor="emacs"; name="Emacs"; glyph= ;;
|
||||
nvim) editor="nvim"; name="Neovim"; glyph= ;;
|
||||
*)
|
||||
echo "Usage: omarchy-default-editor <code|cursor|zed|sublime_text|helix|vim|emacs|nvim>"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if omarchy-cmd-missing "$editor"; then
|
||||
if [[ $installing == "false" ]]; then
|
||||
exec omarchy-launch-floating-terminal-with-presentation omarchy-default-editor --install "$selection"
|
||||
else
|
||||
case "$selection" in
|
||||
code) omarchy-install-editor-vscode ;;
|
||||
cursor) omarchy-pkg-add cursor-bin ;;
|
||||
zed) omarchy-install-editor-zed ;;
|
||||
sublime_text) omarchy-pkg-add sublime-text-4 ;;
|
||||
helix) omarchy-install-editor-helix ;;
|
||||
vim) omarchy-pkg-add vim ;;
|
||||
emacs) omarchy-install-editor-emacs ;;
|
||||
nvim) omarchy-pkg-add neovim ;;
|
||||
esac || exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$editor_file")"
|
||||
printf '%s\n' "$editor" >"$editor_file"
|
||||
|
||||
|
||||
@@ -4,12 +4,6 @@
|
||||
# omarchy:args=[alacritty|foot|ghostty|kitty]
|
||||
# omarchy:examples=omarchy default terminal ghostty | omarchy default terminal kitty
|
||||
|
||||
installing=false
|
||||
if [[ ${1:-} == "--install" ]]; then
|
||||
installing=true
|
||||
shift
|
||||
fi
|
||||
|
||||
if (($# == 0)); then
|
||||
desktop_id=$(xdg-terminal-exec --print-id 2>/dev/null || true)
|
||||
desktop_id=${desktop_id%%:*}
|
||||
@@ -24,24 +18,16 @@ if (($# == 0)); then
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
alacritty) terminal="alacritty"; desktop_id="Alacritty.desktop"; name="Alacritty"; glyph= ;;
|
||||
foot) terminal="foot"; desktop_id="foot.desktop"; name="Foot"; glyph= ;;
|
||||
ghostty) terminal="ghostty"; desktop_id="com.mitchellh.ghostty.desktop"; name="Ghostty"; glyph= ;;
|
||||
kitty) terminal="kitty"; desktop_id="kitty.desktop"; name="Kitty"; glyph= ;;
|
||||
alacritty) desktop_id="Alacritty.desktop"; name="Alacritty"; glyph= ;;
|
||||
foot) desktop_id="foot.desktop"; name="Foot"; glyph= ;;
|
||||
ghostty) desktop_id="com.mitchellh.ghostty.desktop"; name="Ghostty"; glyph= ;;
|
||||
kitty) desktop_id="kitty.desktop"; name="Kitty"; glyph= ;;
|
||||
*)
|
||||
echo "Usage: omarchy-default-terminal <alacritty|foot|ghostty|kitty>"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if omarchy-cmd-missing "$terminal"; then
|
||||
if [[ $installing == "false" ]]; then
|
||||
exec omarchy-launch-floating-terminal-with-presentation omarchy-default-terminal --install "$terminal"
|
||||
else
|
||||
omarchy-install-terminal "$terminal" || exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
cat >~/.config/xdg-terminals.list <<EOF
|
||||
# Terminal emulator preference order for xdg-terminal-exec
|
||||
# The first found and valid terminal will be used
|
||||
|
||||
@@ -34,12 +34,8 @@ systemd, shell, or app-launcher environment; reboot to make every layer agree.
|
||||
Affects only \$OMARCHY_PATH-resolved trees: bin/, default/, shell/,
|
||||
themes/, applications/, config/. Files installed at fixed system paths
|
||||
(/etc/, /usr/lib/systemd/, udev rule bodies, /etc/skel after user
|
||||
creation) are NOT covered — for those, use omarchy-dev-pkg-test to build
|
||||
and install the package from the checkout.
|
||||
|
||||
The Plymouth and SDDM themes under /usr/share are the exception: omarchy
|
||||
plymouth set and omarchy refresh plymouth republish them from the checkout,
|
||||
reading this link's authorization out of the root-owned /etc/omarchy.conf.
|
||||
creation, /usr/share/plymouth) are NOT covered — for those, use
|
||||
omarchy-dev-pkg-test to build and install the package from the checkout.
|
||||
|
||||
Also writes $sudoers_file so sudo resolves omarchy-*
|
||||
from the checkout instead of the packaged copies. That part takes effect
|
||||
|
||||
@@ -149,9 +149,6 @@ write_networkmanager_dns() {
|
||||
local servers="$1"
|
||||
|
||||
install -d -m 0755 "$(dirname "$NM_DNS_CONF")"
|
||||
# omarchy:heredoc-expands paths=none -- $servers is a normalized, single-line
|
||||
# DNS server list written as data, not a path or command; nothing user-writable
|
||||
# is resolved or executed from the root-owned drop-in.
|
||||
cat >"$NM_DNS_CONF" <<EOF
|
||||
# Managed by omarchy-dns. Remove this file or run omarchy dns DHCP to use DHCP DNS again.
|
||||
[global-dns]
|
||||
@@ -305,9 +302,6 @@ Custom)
|
||||
split_dns_servers "$dns_servers"
|
||||
write_networkmanager_dns "$dns_servers"
|
||||
set_connection_dns "$ipv4_dns" "$ipv6_dns"
|
||||
# omarchy:heredoc-expands paths=none -- $dns_servers is a normalized,
|
||||
# single-line DNS server list; the //,/ turns its comma separators into the
|
||||
# spaces resolved.conf wants. It is written as data, not a path or command.
|
||||
tee /etc/systemd/resolved.conf >/dev/null <<EOF
|
||||
[Resolve]
|
||||
DNS=${dns_servers//,/ }
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Match the Dell XPS 13 DX13260 that requires the sidecar amplifier workaround.
|
||||
|
||||
product_sku="${OMARCHY_DMI_PRODUCT_SKU:-/sys/class/dmi/id/product_sku}"
|
||||
|
||||
omarchy-hw-match "DX13260" &&
|
||||
grep -qix "0E53" "$product_sku" 2>/dev/null
|
||||
@@ -16,12 +16,6 @@ fi
|
||||
printf -v install_message '%q' "Installing ${name}..."
|
||||
printf -v desktop_id_arg '%q' "$desktop_id"
|
||||
|
||||
# The list has to reach omarchy-pkg-add as several words, so each word is quoted
|
||||
# rather than the whole string; -d '' reads past newlines and always ends at EOF.
|
||||
read -r -d '' -a package_list <<<"$packages" || true
|
||||
printf -v packages_arg '%q ' "${package_list[@]}"
|
||||
packages_arg="${packages_arg% }"
|
||||
|
||||
# The subshell keeps & from backgrounding the package installation too.
|
||||
exec omarchy-launch-floating-terminal-with-presentation \
|
||||
"echo ${install_message}; omarchy-pkg-add ${packages_arg} && (setsid uwsm-app -- gtk-launch ${desktop_id_arg} >/dev/null 2>&1 &)"
|
||||
"echo ${install_message}; omarchy-pkg-add ${packages} && (setsid uwsm-app -- gtk-launch ${desktop_id_arg} >/dev/null 2>&1 &)"
|
||||
+1
-10
@@ -12,13 +12,4 @@ if [[ -z $name || -z $packages ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf -v install_message '%q' "Installing ${name}..."
|
||||
|
||||
# The list has to reach omarchy-pkg-add as several words, so each word is quoted
|
||||
# rather than the whole string; -d '' reads past newlines and always ends at EOF.
|
||||
read -r -d '' -a package_list <<<"$packages" || true
|
||||
printf -v packages_arg '%q ' "${package_list[@]}"
|
||||
packages_arg="${packages_arg% }"
|
||||
|
||||
exec omarchy-launch-floating-terminal-with-presentation \
|
||||
"echo ${install_message}; omarchy-pkg-add ${packages_arg}"
|
||||
exec omarchy-launch-floating-terminal-with-presentation "echo 'Installing ${name}...'; omarchy-pkg-add ${packages}"
|
||||
+19
-24
@@ -1,15 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Install a supported browser
|
||||
# omarchy:args=<chromium|chrome|brave|brave-origin|edge|firefox|zen>
|
||||
# omarchy:examples=omarchy install browser chromium | omarchy install browser firefox
|
||||
# omarchy:args=<chrome|brave|brave-origin|edge|firefox|zen>
|
||||
# omarchy:examples=omarchy install browser firefox | omarchy install browser brave
|
||||
|
||||
set -e
|
||||
|
||||
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
||||
|
||||
setup_chromium_policy_directory() {
|
||||
browser_policy_setup_dir "$1"
|
||||
setup_policy_directory() {
|
||||
sudo mkdir -p "$1"
|
||||
sudo chmod a+rw "$1"
|
||||
}
|
||||
|
||||
announce_browser_installed() {
|
||||
@@ -24,26 +21,24 @@ copy_chromium_flags() {
|
||||
omarchy-install-chromium-ytdlp
|
||||
}
|
||||
|
||||
setup_firefox_preferences() {
|
||||
local distribution_dir="$1"
|
||||
|
||||
setup_policy_directory "$distribution_dir"
|
||||
sudo cp -f "$OMARCHY_PATH/default/firefox/policies.json" "$distribution_dir/policies.json"
|
||||
}
|
||||
|
||||
setup_firefox_wayland() {
|
||||
mkdir -p ~/.config/environment.d
|
||||
echo "MOZ_ENABLE_WAYLAND=1" > ~/.config/environment.d/omarchy-firefox-wayland.conf
|
||||
}
|
||||
|
||||
case $1 in
|
||||
chromium)
|
||||
echo "Installing Chromium..."
|
||||
omarchy-pkg-add chromium
|
||||
|
||||
setup_chromium_policy_directory /etc/chromium/policies/managed
|
||||
copy_chromium_flags ~/.config/chromium-flags.conf
|
||||
omarchy-theme-set-browser
|
||||
announce_browser_installed "Chromium"
|
||||
;;
|
||||
chrome)
|
||||
echo "Installing Chrome..."
|
||||
omarchy-pkg-aur-add google-chrome || exit 1
|
||||
|
||||
setup_chromium_policy_directory /etc/opt/chrome/policies/managed
|
||||
setup_policy_directory /etc/opt/chrome/policies/managed
|
||||
copy_chromium_flags ~/.config/chrome-flags.conf
|
||||
omarchy-theme-set-browser
|
||||
announce_browser_installed "Chrome"
|
||||
@@ -52,7 +47,7 @@ edge)
|
||||
echo "Installing Edge..."
|
||||
omarchy-pkg-aur-add microsoft-edge-stable-bin || exit 1
|
||||
|
||||
setup_chromium_policy_directory /etc/opt/edge/policies/managed
|
||||
setup_policy_directory /etc/opt/edge/policies/managed
|
||||
copy_chromium_flags ~/.config/microsoft-edge-stable-flags.conf
|
||||
omarchy-theme-set-browser
|
||||
announce_browser_installed "Edge"
|
||||
@@ -61,7 +56,7 @@ brave)
|
||||
echo "Installing Brave..."
|
||||
omarchy-pkg-aur-add brave-bin || exit 1
|
||||
|
||||
setup_chromium_policy_directory /etc/brave/policies/managed
|
||||
setup_policy_directory /etc/brave/policies/managed
|
||||
copy_chromium_flags ~/.config/brave-flags.conf
|
||||
omarchy-theme-set-browser
|
||||
announce_browser_installed "Brave"
|
||||
@@ -70,7 +65,7 @@ brave-origin)
|
||||
echo "Installing Brave Origin..."
|
||||
omarchy-pkg-aur-add brave-origin-bin || exit 1
|
||||
|
||||
setup_chromium_policy_directory /etc/brave/policies/managed
|
||||
setup_policy_directory /etc/brave/policies/managed
|
||||
copy_chromium_flags ~/.config/brave-origin-flags.conf
|
||||
omarchy-theme-set-browser
|
||||
announce_browser_installed "Brave Origin"
|
||||
@@ -79,7 +74,7 @@ firefox)
|
||||
echo "Installing Firefox..."
|
||||
omarchy-pkg-add firefox || exit 1
|
||||
|
||||
browser_policy_setup_firefox_distribution /usr/lib/firefox/distribution
|
||||
setup_firefox_preferences /usr/lib/firefox/distribution
|
||||
setup_firefox_wayland
|
||||
announce_browser_installed "Firefox"
|
||||
;;
|
||||
@@ -87,12 +82,12 @@ zen)
|
||||
echo "Installing Zen..."
|
||||
omarchy-pkg-aur-add zen-browser-bin || exit 1
|
||||
|
||||
browser_policy_setup_firefox_distribution /opt/zen-browser/distribution
|
||||
setup_firefox_preferences /opt/zen-browser/distribution
|
||||
setup_firefox_wayland
|
||||
announce_browser_installed "Zen"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: omarchy-install-browser <chromium|chrome|brave|brave-origin|edge|firefox|zen>"
|
||||
echo "Usage: omarchy-install-browser <chrome|brave|brave-origin|edge|firefox|zen>"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -13,9 +13,5 @@ if [[ -z $name || -z $package || -z $family ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf -v install_message '%q' "Installing ${name}..."
|
||||
printf -v package_arg '%q' "$package"
|
||||
printf -v family_arg '%q' "$family"
|
||||
|
||||
exec omarchy-launch-floating-terminal-with-presentation \
|
||||
"echo ${install_message}; omarchy-pkg-add ${package_arg} && sleep 2 && omarchy-font-set ${family_arg}"
|
||||
"echo 'Installing ${name}...'; omarchy-pkg-add ${package} && sleep 2 && omarchy-font-set '${family}'"
|
||||
@@ -10,4 +10,4 @@ echo "Enabling ONCE background service..."
|
||||
sudo systemctl enable --now once-background.service
|
||||
|
||||
echo -e "\nLaunching ONCE..."
|
||||
sudo once
|
||||
once
|
||||
+13
-196
@@ -10,50 +10,10 @@
|
||||
|
||||
LOGO_FILE="$HOME/.config/omarchy/branding/about.txt"
|
||||
FIT_FILE="$HOME/.local/state/omarchy/windows/about.fit"
|
||||
OMARCHY_FASTFETCH_DIR=/etc/fastfetch
|
||||
|
||||
# The logo block in the fastfetch config. The fit below reproduces this layout to
|
||||
# size the window, and the sheen has to repaint the very cells fastfetch drew the
|
||||
# logo on, so both read the padding from here.
|
||||
LOGO_PAD_LEFT=2
|
||||
LOGO_PAD_TOP=2
|
||||
LOGO_PAD_RIGHT=6
|
||||
|
||||
# The content is measured once and then goes on living: an uptime that turns
|
||||
# minutes into hours and hours into days, a version string that grows, a module
|
||||
# that shows up on the next boot. A window fitted to exactly what was measured
|
||||
# has nowhere to put any of it, and the layout clips or scrolls the moment it
|
||||
# grows — which is also the moment the logo stops being where it was drawn. Keep
|
||||
# a little in hand rather than measure again every time something ticks over.
|
||||
FIT_SPARE_COLUMNS=2
|
||||
FIT_SPARE_ROWS=1
|
||||
|
||||
POLL_SECONDS=0.5
|
||||
|
||||
# fastfetch has no animation of its own, so the sheen is ours. It knows about a
|
||||
# logo and nothing about About, which is why it is a file of its own.
|
||||
source omarchy-branding-about-animation
|
||||
|
||||
# A user's own fastfetch config can relocate or restyle the logo in ways this
|
||||
# measurement cannot see, so leave sizing to the float rule in that case. It can
|
||||
# sit in any of several directories fastfetch searches ahead of Omarchy's own, so
|
||||
# ask fastfetch for that order rather than keep a copy here for its next release
|
||||
# to outdate.
|
||||
# A user-level fastfetch config can relocate or restyle the logo in ways this
|
||||
# measurement cannot see, so leave sizing to the float rule in that case.
|
||||
custom_fastfetch_config() {
|
||||
local directory listed=false
|
||||
|
||||
# A whole line at a time, because a home directory may contain a space, and the
|
||||
# marker fastfetch puts beside the config it settled on is not part of the path.
|
||||
while IFS= read -r directory; do
|
||||
listed=true
|
||||
directory=${directory% (\*)}
|
||||
[[ ${directory%/} == "$OMARCHY_FASTFETCH_DIR" ]] && return 1
|
||||
[[ -f ${directory%/}/config.jsonc ]] && return 0
|
||||
done < <(fastfetch --list-config-paths 2>/dev/null)
|
||||
|
||||
# Silence is not the same answer as "none of them", so fall back to the
|
||||
# directory fastfetch has always looked in first rather than read it as one.
|
||||
[[ $listed == true ]] && return 1
|
||||
[[ -f $HOME/.config/fastfetch/config.jsonc ]]
|
||||
}
|
||||
|
||||
@@ -140,22 +100,17 @@ fit_window() {
|
||||
|
||||
# The guard character keeps command substitution from eating the trailing
|
||||
# break line, which provides the bottom padding row.
|
||||
local modules module_w
|
||||
local modules module_w module_h
|
||||
modules=$(fastfetch --logo none | sed 's/\x1b\[[0-9;?]*[a-zA-Z]//g'; printf X)
|
||||
modules=${modules%X}
|
||||
module_w=$(printf '%s' "$modules" | display_columns)
|
||||
|
||||
# Ask fastfetch how tall its layout came out rather than predicting it from the
|
||||
# logo and the module column: once the logo is the taller of the two, fastfetch
|
||||
# writes a row more than that arithmetic expects, and a window sized by it
|
||||
# scrolls the top padding away.
|
||||
measure_layout || return 1
|
||||
module_h=$(printf '%s' "$modules" | wc -l)
|
||||
|
||||
# Mirror the logo block in the fastfetch config: 2 columns of padding left of
|
||||
# the logo, 6 between logo and modules. Then 2 columns of right padding to
|
||||
# match, a row for the cursor so the trailing break shows, and the spare above.
|
||||
local target_c=$(( LOGO_PAD_LEFT + logo_w + LOGO_PAD_RIGHT + module_w + LOGO_PAD_LEFT + FIT_SPARE_COLUMNS ))
|
||||
local target_r=$(( LAYOUT_ROWS + 1 + FIT_SPARE_ROWS ))
|
||||
# the logo, 6 between logo and modules, 2 rows above it. Then 2 columns of
|
||||
# right padding to match, and a row for the cursor so the trailing break shows.
|
||||
local target_c=$(( 2 + logo_w + 6 + module_w + 2 ))
|
||||
local target_r=$(( (logo_h + 2 > module_h ? logo_h + 2 : module_h) + 1 ))
|
||||
|
||||
local nudges=0 rows cols address width height shift_w shift_h target_w target_h
|
||||
while :; do
|
||||
@@ -191,155 +146,17 @@ fit_window() {
|
||||
return 1
|
||||
}
|
||||
|
||||
# One run answers both questions the sheen has to ask first. How tall the layout
|
||||
# is, because a window too small for it scrolls, which moves the logo off the rows
|
||||
# the frames address. And what colour fastfetch drew the logo in, because the
|
||||
# glint has to hand every cell back in the colour it arrived in — assume it, and
|
||||
# a logo fastfetch colours differently comes out of the first glint a new one.
|
||||
measure_layout() {
|
||||
[[ -n ${LAYOUT_ROWS:-} ]] && return 0
|
||||
|
||||
local rendered plain needle offset found row
|
||||
|
||||
# --pipe false because fastfetch drops its colours when it is not writing to a
|
||||
# terminal, and it is writing to this substitution.
|
||||
rendered=$(fastfetch --pipe false 2>/dev/null; printf X)
|
||||
rendered=${rendered%X}
|
||||
LAYOUT_ROWS=$(printf '%s' "$rendered" | wc -l)
|
||||
(( LAYOUT_ROWS > 0 )) || return 1
|
||||
|
||||
# Find the logo in what fastfetch drew rather than working it out from the
|
||||
# padding this file was written against. The config that runs is the one in
|
||||
# /etc, which a checkout does not replace, so the two can disagree — and a logo
|
||||
# measured two rows above where it was drawn is a logo the sheen moves. Not
|
||||
# finding it at all is the same answer as finding it somewhere unexpected:
|
||||
# whatever is on screen is not the text in the file, so leave it alone.
|
||||
IFS=$'\t' read -r offset needle < <(logo_landmark) || return 1
|
||||
[[ -n $needle ]] || return 1
|
||||
|
||||
plain=$(printf '%s' "$rendered" | sed 's/\x1b\[[0-9;?]*[a-zA-Z]//g')
|
||||
found=$(printf '%s' "$plain" | LC_ALL=C.UTF-8 awk -v needle="$needle" \
|
||||
'index($0, needle) { print NR, index($0, needle); exit }')
|
||||
[[ -n $found ]] || return 1
|
||||
read -r LOGO_ROW LOGO_COLUMN <<<"$found"
|
||||
LOGO_ROW=$(( LOGO_ROW - offset ))
|
||||
(( LOGO_ROW >= 1 && LOGO_COLUMN >= 1 )) || return 1
|
||||
|
||||
# Whatever fastfetch set before that row is what the sheen has to give back.
|
||||
row=$(printf '%s' "$rendered" | sed -n "$(( LOGO_ROW + offset ))p")
|
||||
LOGO_COLOR=""
|
||||
[[ $row =~ ^(($ESC\[[0-9;]*m)+) ]] && LOGO_COLOR=${BASH_REMATCH[1]}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# The longest line of the logo, and how far down the logo it sits — the most
|
||||
# distinctive thing to look for in the render, and the offset that turns where it
|
||||
# was found back into where the logo starts.
|
||||
# Tab-separated, and the offset first, because the line may hold spaces of its
|
||||
# own and splitting on them would cut the landmark short.
|
||||
logo_landmark() {
|
||||
LC_ALL=C.UTF-8 awk '
|
||||
{ if (length($0) > best) { best = length($0); line = $0; at = NR - 1 } }
|
||||
END { if (best > 0) printf "%d\t%s\n", at, line }
|
||||
' "$LOGO_FILE" 2>/dev/null
|
||||
}
|
||||
|
||||
# The About screen's own reasons the logo might not be where these frames would
|
||||
# draw it. Whether the logo itself can be animated is the sheen's own question.
|
||||
build_sheen() {
|
||||
# Whatever the last build left is not this window's, and the loop below plays
|
||||
# whatever is here — so a build that fails has to leave nothing to play.
|
||||
SHEEN_FRAMES=()
|
||||
|
||||
custom_fastfetch_config && return 1
|
||||
|
||||
# fastfetch honours NO_COLOR when it writes to a terminal but not when it writes
|
||||
# to the measurement below, so a logo drawn without colour would be measured as
|
||||
# green and left green by the first glint. A glint is colour anyway, which is
|
||||
# the thing NO_COLOR asks for none of.
|
||||
[[ -n ${NO_COLOR:-} ]] && return 1
|
||||
|
||||
measure_layout || return 1
|
||||
|
||||
# The layout needs a row for the cursor past its last line. Without one it has
|
||||
# scrolled, and the logo is no longer on the rows the frames address. Ask the
|
||||
# terminal where the cursor actually is rather than trust the arithmetic, and
|
||||
# keep the arithmetic for a terminal that will not say.
|
||||
local rows cols
|
||||
read -r rows cols <<<"$(stty size)"
|
||||
(( rows > LAYOUT_ROWS )) || return 1
|
||||
|
||||
# The cell the logo's first row starts on, every attribute fastfetch left on
|
||||
# those cells so a glint that has passed leaves them as it found them, and the
|
||||
# room it has to work in left of the module column.
|
||||
sheen_build "$LOGO_FILE" "$LOGO_ROW" "$LOGO_COLUMN" "${ESC}[0m${LOGO_COLOR}" "$(( cols - LOGO_COLUMN + 1 ))"
|
||||
}
|
||||
|
||||
# What the frames were built against. A window that resized, or a logo that was
|
||||
# rebranded, needs fastfetch run again before anything is drawn over it.
|
||||
content_changed() {
|
||||
[[ $resized == true ]] && return 0
|
||||
|
||||
[[ $(stty size) != "$grid" || $(stat -c %Y "$LOGO_FILE" 2>/dev/null) != "$logo_stamp" ]]
|
||||
}
|
||||
|
||||
# A tick either times out, which is the delay, or a key arrives and About closes.
|
||||
# Anything else on stdin is a terminal that went away, which closes it too.
|
||||
tick() {
|
||||
read -t "$1" -n 1 -s && exit
|
||||
(( $? > 128 )) || exit
|
||||
}
|
||||
|
||||
play_sheen() {
|
||||
local index
|
||||
|
||||
for (( index = 0; index < ${#SHEEN_FRAMES[@]}; index++ )); do
|
||||
# Stop before painting a frame rather than after: a resize has already moved
|
||||
# the cells these address, and the rest of a sweep would land across them.
|
||||
# The grid costs a process, so it stays on the poll interval. The trap costs
|
||||
# nothing, so it is read last — a signal that arrived while the grid was being
|
||||
# read would otherwise be seen only after another frame had gone out.
|
||||
(( index % SHEEN_POLL_FRAMES == 0 )) && content_changed && return 1
|
||||
[[ $resized == true ]] && return 1
|
||||
|
||||
printf '%s' "${SHEEN_FRAMES[index]}"
|
||||
tick "$SHEEN_FRAME_SECONDS"
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# The logo is still between glints, so About is a quiet window to leave open.
|
||||
rest_sheen() {
|
||||
local ticks
|
||||
|
||||
for (( ticks = 0; ticks < SHEEN_REST_TICKS; ticks++ )); do
|
||||
tick "$POLL_SECONDS"
|
||||
content_changed && return 1
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
if [[ ${1:-} == "--render" ]]; then
|
||||
printf '\e[?25l'
|
||||
|
||||
# A sweep runs for seconds between polls, so it reads this instead. The polling
|
||||
# stays as the backstop, for a signal that arrived while it could not be taken.
|
||||
resized=false
|
||||
trap 'resized=true' WINCH
|
||||
|
||||
# Give the compositor a moment to apply the window rules before measuring cells.
|
||||
settle_grid
|
||||
|
||||
fitted=false
|
||||
passes=0
|
||||
while :; do
|
||||
grid=$(stty size)
|
||||
size=$(stty size)
|
||||
logo_stamp=$(stat -c %Y "$LOGO_FILE" 2>/dev/null)
|
||||
resized=false
|
||||
LAYOUT_ROWS=""
|
||||
clear
|
||||
fastfetch
|
||||
# A second pass picks up a fit that could not measure the window the first
|
||||
@@ -348,10 +165,10 @@ if [[ ${1:-} == "--render" ]]; then
|
||||
if [[ $fitted == false ]] && (( ++passes <= 2 )); then
|
||||
fit_window && fitted=true
|
||||
fi
|
||||
# An empty frame list plays nothing, so a logo that cannot be animated waits
|
||||
# here exactly as the still one did, and there is one loop rather than two.
|
||||
build_sheen
|
||||
while play_sheen && rest_sheen; do :; done
|
||||
while [[ $(stty size) == $size && $(stat -c %Y "$LOGO_FILE" 2>/dev/null) == $logo_stamp ]]; do
|
||||
read -t 0.5 -n 1 -s && exit
|
||||
(( $? > 128 )) || exit
|
||||
done
|
||||
# A rebranded logo changes the content dimensions, so measure again.
|
||||
if [[ $(stat -c %Y "$LOGO_FILE" 2>/dev/null) != $logo_stamp ]]; then
|
||||
fitted=false
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
# omarchy:summary=Launch the Omarchy screensaver in the default terminal on the system with the correct font configuration.
|
||||
|
||||
if omarchy-cmd-missing ttfx; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Exit early if screensaver is already running
|
||||
pgrep -f '[o]rg.omarchy.screensaver' && exit 0
|
||||
|
||||
|
||||
@@ -38,12 +38,6 @@ parse_keycodes() {
|
||||
if (code != "" && symbol != "" && symbol != "NoSymbol") keycode_symbol[code] = toupper(symbol)
|
||||
}
|
||||
|
||||
# Read off the keysym rather than the keycode, so the key left of 1 reads
|
||||
# as the symbol printed on it whichever key the active layout puts it on.
|
||||
for (code in keycode_symbol) {
|
||||
if (keycode_symbol[code] == "GRAVE") keycode_symbol[code] = "~"
|
||||
}
|
||||
|
||||
mouse_symbol["272"] = "LEFT MOUSE BUTTON"
|
||||
mouse_symbol["273"] = "RIGHT MOUSE BUTTON"
|
||||
mouse_symbol["274"] = "MIDDLE MOUSE BUTTON"
|
||||
@@ -311,7 +305,6 @@ dynamic_bindings() {
|
||||
|
||||
case "$key" in
|
||||
comma) key="COMMA" ;;
|
||||
grave) key="~" ;;
|
||||
period) key="PERIOD" ;;
|
||||
minus) key="MINUS" ;;
|
||||
equal) key="EQUAL" ;;
|
||||
@@ -331,41 +324,15 @@ static_bindings() {
|
||||
echo "SHIFT ALT,D,Download Video from Web App,sendshortcut,SHIFT ALT,D,"
|
||||
}
|
||||
|
||||
# Actions Omarchy binds to a second chord meant as an alternative, named one at
|
||||
# a time. A rule would be wrong here: Alt + Tab and Shift + Alt + Tab both say
|
||||
# "Reveal active window on top" while cycling opposite ways, and a media key is
|
||||
# nobody's idea of an alternative to a Super chord. Only chords a user reaches
|
||||
# for interchangeably belong on one row.
|
||||
alternative_chord_actions() {
|
||||
cat <<'ACTIONS'
|
||||
Close window
|
||||
Calculator
|
||||
Toggle scratchpad
|
||||
Move window to scratchpad
|
||||
ACTIONS
|
||||
}
|
||||
|
||||
# Parse and format keybindings
|
||||
#
|
||||
# `awk` does the heavy lifting:
|
||||
# - Set the field separator to a comma ','.
|
||||
# - Joins the key combination (e.g., "SUPER + Q").
|
||||
# - Joins the command that the key executes.
|
||||
# - Puts an action's alternative chord on the row with the first one.
|
||||
# - Prints display text and dispatch metadata as tab-separated fields.
|
||||
parse_binding_records() {
|
||||
awk -F, -v alternatives="$(alternative_chord_actions)" '
|
||||
BEGIN {
|
||||
# The column every row pads its chords to. Nothing is allowed past it: the
|
||||
# menu renders in monospace, and a row that overruns pushes its arrow out
|
||||
# of a column the eye reads straight down.
|
||||
column = 35;
|
||||
|
||||
split(alternatives, named, "\n");
|
||||
for (i in named) {
|
||||
if (named[i] != "") shares_a_row[named[i]] = 1;
|
||||
}
|
||||
}
|
||||
awk -F, '
|
||||
{
|
||||
# Combine the modifier and key (first two fields)
|
||||
key_combo = $1 " + " $2;
|
||||
@@ -398,35 +365,8 @@ BEGIN {
|
||||
gsub(/[ \t]+/, " ", key_combo); # Collapse multiple spaces to one
|
||||
}
|
||||
|
||||
# An alternative chord joins the row the first one opened: "SUPER + W /
|
||||
# SUPER + Q -> Close window". Both halves have to agree on what they run,
|
||||
# since a label is only what a chord is called; an unresolved dispatcher
|
||||
# says nothing at all, so it never counts as agreement.
|
||||
if (action != "") {
|
||||
action_key = action SUBSEP dispatcher SUBSEP arg;
|
||||
together = "";
|
||||
|
||||
if (action in shares_a_row && dispatcher != "" && action_key in leads) {
|
||||
together = chords[leads[action_key]] " / " key_combo;
|
||||
}
|
||||
|
||||
if (together != "" && length(together) <= column) {
|
||||
chords[leads[action_key]] = together;
|
||||
} else {
|
||||
# Also the path a pair too wide for the column takes: two rows in
|
||||
# line beat one that juts out of it.
|
||||
entries++;
|
||||
chords[entries] = key_combo;
|
||||
actions[entries] = action;
|
||||
dispatchers[entries] = dispatcher;
|
||||
args[entries] = arg;
|
||||
leads[action_key] = entries;
|
||||
}
|
||||
}
|
||||
}
|
||||
END {
|
||||
for (entry = 1; entry <= entries; entry++) {
|
||||
printf "%-*s → %s\t%s\t%s\n", column, chords[entry], actions[entry], dispatchers[entry], args[entry];
|
||||
printf "%-35s → %s\t%s\t%s\n", key_combo, action, dispatcher, arg;
|
||||
}
|
||||
}'
|
||||
}
|
||||
@@ -434,13 +374,7 @@ END {
|
||||
prioritize_entries() {
|
||||
awk -F '\t' '
|
||||
{
|
||||
# Alternative chords are display only. Classifying on them would read
|
||||
# "SUPER SHIFT + RETURN / SUPER SHIFT + B" as the alternate browser chord
|
||||
# and drop the browser 20 rows down its own list, so match the chord that
|
||||
# leads the row and ignore the rest.
|
||||
line = $1
|
||||
sub(/ \/ [^→]*→/, " →", line)
|
||||
|
||||
prio = 50
|
||||
if (match(line, /Keybindings/)) prio = 0
|
||||
if (match(line, /Omarchy menu/)) prio = 1
|
||||
@@ -502,12 +436,6 @@ prioritize_entries() {
|
||||
cut -f2-
|
||||
}
|
||||
|
||||
# Drop repeated records while keeping the order Hyprland reported them in, so
|
||||
# the chord a user declared first is the one that leads a merged entry.
|
||||
dedupe_binding_records() {
|
||||
awk '!seen[$0]++'
|
||||
}
|
||||
|
||||
output_binding_records_uncached() {
|
||||
local dynamic
|
||||
|
||||
@@ -518,7 +446,7 @@ output_binding_records_uncached() {
|
||||
[[ -n $dynamic ]] && printf '%s\n' "$dynamic"
|
||||
static_bindings
|
||||
} |
|
||||
dedupe_binding_records |
|
||||
sort -u |
|
||||
parse_keycodes |
|
||||
parse_binding_records |
|
||||
prioritize_entries
|
||||
@@ -530,7 +458,7 @@ output_binding_records_uncached() {
|
||||
|
||||
keybindings_cache_key() {
|
||||
{
|
||||
printf 'v13\n'
|
||||
printf 'v11\n'
|
||||
hyprctl devices 2>/dev/null | grep -F 'active keymap:'
|
||||
hyprctl binds 2>/dev/null
|
||||
} | sha256sum | awk '{ print $1 }'
|
||||
|
||||
+3
-3
@@ -85,16 +85,16 @@ wait_for_pacman_transaction
|
||||
mkdir -p "$STATE_DIR"
|
||||
[[ -d $MIGRATIONS_DIR ]] || exit 0
|
||||
|
||||
while IFS=$'\t' read -r name file marker <&3; do
|
||||
while IFS=$'\t' read -r name file marker; do
|
||||
[[ -n $name ]] || continue
|
||||
|
||||
if [[ ! -f $marker ]]; then
|
||||
echo -e "\e[32m\nRunning migration (${name%.sh})\e[0m"
|
||||
OMARCHY_PATH="$OMARCHY_PATH" bash -euo pipefail "$file" 3<&-
|
||||
OMARCHY_PATH="$OMARCHY_PATH" bash -euo pipefail "$file"
|
||||
mkdir -p "$(dirname "$marker")"
|
||||
touch "$marker"
|
||||
fi
|
||||
done 3< <(migration_entries)
|
||||
done < <(migration_entries)
|
||||
|
||||
# Clear a login-time notification the user left sitting there and then resolved
|
||||
# by running migrations some other way. The substring matches both the current
|
||||
|
||||
@@ -34,18 +34,23 @@ print_status() {
|
||||
return
|
||||
fi
|
||||
|
||||
nm=$(nmcli -t -f GENERAL.STATE,GENERAL.CONNECTION dev show "$device" 2>/dev/null)
|
||||
state=$(awk -F: '$1 == "GENERAL.STATE" { print $2; exit }' <<<"$nm")
|
||||
ssid=$(awk -F: '$1 == "GENERAL.CONNECTION" { print $2; exit }' <<<"$nm")
|
||||
signal=$(nmcli -t -f IN-USE,SIGNAL dev wifi list ifname "$device" --rescan no 2>/dev/null | awk -F: '$1 == "*" { print $2; exit }')
|
||||
freq=$(iw dev "$device" link 2>/dev/null | awk '/freq:/ { print $2; exit }')
|
||||
if omarchy-cmd-present nmcli; then
|
||||
nm=$(nmcli -t -f GENERAL.STATE,GENERAL.CONNECTION dev show "$device" 2>/dev/null)
|
||||
state=$(awk -F: '$1 == "GENERAL.STATE" { print $2; exit }' <<<"$nm")
|
||||
ssid=$(awk -F: '$1 == "GENERAL.CONNECTION" { print $2; exit }' <<<"$nm")
|
||||
signal=$(nmcli -t -f IN-USE,SIGNAL dev wifi list ifname "$device" --rescan no 2>/dev/null | awk -F: '$1 == "*" { print $2; exit }')
|
||||
freq=$(iw dev "$device" link 2>/dev/null | awk '/freq:/ { print $2; exit }')
|
||||
|
||||
if [[ $state != 100* ]]; then
|
||||
printf 'disconnected\t\t\t\n'
|
||||
if [[ $state != 100* ]]; then
|
||||
printf 'disconnected\t\t\t\n'
|
||||
return
|
||||
fi
|
||||
|
||||
printf 'wifi\t%s\t%s\t%s\n' "${ssid:-$device}" "$signal" "$freq"
|
||||
return
|
||||
fi
|
||||
|
||||
printf 'wifi\t%s\t%s\t%s\n' "${ssid:-$device}" "$signal" "$freq"
|
||||
printf 'wifi\t%s\t\t\n' "$device"
|
||||
}
|
||||
|
||||
ping_latency_ms() {
|
||||
|
||||
@@ -3,7 +3,5 @@
|
||||
# omarchy:summary=Restore the default Omarchy Plymouth boot theme and SDDM login screen
|
||||
# omarchy:requires-sudo=true
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
"$OMARCHY_PATH/bin/omarchy-refresh-plymouth"
|
||||
"$OMARCHY_PATH/bin/omarchy-refresh-sddm"
|
||||
omarchy-refresh-plymouth
|
||||
omarchy-refresh-sddm
|
||||
+72
-361
@@ -5,377 +5,88 @@
|
||||
# omarchy:examples=omarchy plymouth set '#1d2021' '#ebdbb2' ~/.local/state/omarchy/current/theme/plymouth/logo.png
|
||||
# omarchy:requires-sudo=true
|
||||
|
||||
set -euo pipefail
|
||||
# Configure the Plymouth boot theme with a custom background color, text color, and logo.
|
||||
# Stages the change in a temp dir, then commits the staged files to /usr/share and
|
||||
# rebuilds the initramfs. Also syncs the SDDM login screen (the post-logout
|
||||
# screen) with the same colors and logo so boot/login stay visually unified.
|
||||
|
||||
# Build the authoritative theme in a root-owned directory, then publish each
|
||||
# fixed destination atomically. The caller opens the selected logo before sudo,
|
||||
# so the privileged process never resolves a user-controlled input path.
|
||||
|
||||
usage() {
|
||||
if (( $# != 3 )); then
|
||||
echo "Usage: omarchy-plymouth-set <background-hex> <text-hex> <path-to-logo.png>" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (( $# == 3 )); then
|
||||
mode=set
|
||||
elif (( $# == 1 )); then
|
||||
case "$1" in
|
||||
--refresh-default)
|
||||
mode=refresh-plymouth
|
||||
;;
|
||||
--refresh-sddm-default)
|
||||
mode=refresh-sddm
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
else
|
||||
usage
|
||||
fi
|
||||
|
||||
if (( EUID == 0 )); then
|
||||
echo "Error: run omarchy-plymouth-set as your user, not under sudo." >&2
|
||||
bg_hex="${1#\#}"
|
||||
text_hex="${2#\#}"
|
||||
logo_path="$3"
|
||||
|
||||
if ! [[ $bg_hex =~ ^[0-9a-fA-F]{6}$ ]]; then
|
||||
echo "Invalid background color: $1 (expected #RRGGBB)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
logo_fd=
|
||||
if [[ $mode != "set" ]]; then
|
||||
bg_hex=
|
||||
text_hex=
|
||||
if ! [[ $text_hex =~ ^[0-9a-fA-F]{6}$ ]]; then
|
||||
echo "Invalid text color: $2 (expected #RRGGBB)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f $logo_path ]]; then
|
||||
echo "Logo file not found: $logo_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# omarchy-plymouth-set-by-theme passes a theme's unlock.png straight from
|
||||
# ~/.config/omarchy/themes, where an installed theme can make it a symlink to
|
||||
# anything. The copies below land in world-readable /usr/share, so following one
|
||||
# would republish whatever it points at.
|
||||
if [[ -L $logo_path ]]; then
|
||||
echo "Logo file is a symlink, which is not accepted: $logo_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
bg_r=$(awk -v n=$((16#${bg_hex:0:2})) 'BEGIN{printf "%.3f", n/255}')
|
||||
bg_g=$(awk -v n=$((16#${bg_hex:2:2})) 'BEGIN{printf "%.3f", n/255}')
|
||||
bg_b=$(awk -v n=$((16#${bg_hex:4:2})) 'BEGIN{printf "%.3f", n/255}')
|
||||
|
||||
theme_dir="/usr/share/plymouth/themes/omarchy"
|
||||
staging_dir=$(mktemp -d)
|
||||
trap 'rm -rf "$staging_dir"' EXIT
|
||||
|
||||
find "$OMARCHY_PATH/default/plymouth" -maxdepth 1 -type f -exec cp -t "$staging_dir/" {} +
|
||||
cp "$logo_path" "$staging_dir/logo.png"
|
||||
|
||||
sed -i \
|
||||
-e "s/^Window.SetBackgroundTopColor.*/Window.SetBackgroundTopColor($bg_r, $bg_g, $bg_b);/" \
|
||||
-e "s/^Window.SetBackgroundBottomColor.*/Window.SetBackgroundBottomColor($bg_r, $bg_g, $bg_b);/" \
|
||||
"$staging_dir/omarchy.script"
|
||||
|
||||
for asset in bullet.png entry.png lock.png progress_bar.png; do
|
||||
magick "$staging_dir/$asset" -channel RGB +level-colors "#$text_hex","#$text_hex" "$staging_dir/$asset"
|
||||
done
|
||||
|
||||
sudo cp -a --no-preserve=mode,ownership "$staging_dir/." "$theme_dir/"
|
||||
sudo plymouth-set-default-theme omarchy
|
||||
|
||||
if omarchy-cmd-present limine-mkinitcpio; then
|
||||
sudo limine-mkinitcpio
|
||||
else
|
||||
bg_hex="${1#\#}"
|
||||
text_hex="${2#\#}"
|
||||
logo_path="$3"
|
||||
|
||||
if ! [[ $bg_hex =~ ^[0-9a-fA-F]{6}$ ]]; then
|
||||
echo "Invalid background color: $1 (expected #RRGGBB)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [[ $text_hex =~ ^[0-9a-fA-F]{6}$ ]]; then
|
||||
echo "Invalid text color: $2 (expected #RRGGBB)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f $logo_path ]]; then
|
||||
echo "Logo file not found: $logo_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -L $logo_path ]]; then
|
||||
echo "Logo file is a symlink, which is not accepted: $logo_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Open the logo while still unprivileged. A replacement symlink to a root-only
|
||||
# file therefore fails here instead of being followed after sudo starts.
|
||||
if ! exec {logo_fd}<"$logo_path"; then
|
||||
echo "Unable to open logo file as the current user: $logo_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -f /proc/$$/fd/$logo_fd ]]; then
|
||||
echo "Logo input is no longer a regular file: $logo_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
sudo mkinitcpio -P
|
||||
fi
|
||||
|
||||
run_root_transaction() {
|
||||
sudo /bin/bash -c '
|
||||
set -eEuo pipefail
|
||||
PATH=/usr/bin:/bin
|
||||
export PATH
|
||||
umask 077
|
||||
# Sync the SDDM login screen with the same colors and logo.
|
||||
sddm_dir="/usr/share/sddm/themes/omarchy"
|
||||
sddm_template="$OMARCHY_PATH/default/sddm/omarchy/Main.qml"
|
||||
|
||||
# Every check below is a bare assertion that aborts under set -e. Name the
|
||||
# subject of each one so a refusal reaches the user instead of exiting mute.
|
||||
failure_context="the privileged Plymouth transaction"
|
||||
failure_reported=
|
||||
report_failure() {
|
||||
[[ -z $failure_reported ]] || return 0
|
||||
failure_reported=1
|
||||
printf "omarchy-plymouth-set: refusing to publish: %s failed validation\n" "$failure_context" >&2
|
||||
if [[ -n ${failure_hint:-} ]]; then
|
||||
printf "omarchy-plymouth-set: %s\n" "$failure_hint" >&2
|
||||
fi
|
||||
}
|
||||
trap report_failure ERR
|
||||
sed \
|
||||
-e "s/#1a1b26/#$bg_hex/g" \
|
||||
-e "s/#ffffff/#$text_hex/g" \
|
||||
"$sddm_template" | sudo tee "$sddm_dir/Main.qml" >/dev/null
|
||||
|
||||
mode=$1
|
||||
source_root=$2
|
||||
bg_hex=$3
|
||||
text_hex=$4
|
||||
max_asset_size=$5
|
||||
|
||||
failure_context="the arguments of the privileged transaction"
|
||||
[[ $mode == "set" || $mode == "refresh-plymouth" || $mode == "refresh-sddm" ]]
|
||||
[[ $source_root == /* ]]
|
||||
[[ $max_asset_size =~ ^[0-9]+$ ]]
|
||||
(( max_asset_size > 0 ))
|
||||
|
||||
failure_context="the Omarchy source tree $source_root"
|
||||
canonical_source_root=$(realpath -e -- "$source_root")
|
||||
[[ $canonical_source_root == "$source_root" ]]
|
||||
|
||||
validate_trusted_directory() {
|
||||
local directory=$1 canonical uid directory_mode
|
||||
|
||||
failure_context="directory $directory"
|
||||
canonical=$(realpath -e -- "$directory")
|
||||
[[ $canonical == "$directory" && -d $directory && ! -L $directory ]]
|
||||
|
||||
while :; do
|
||||
failure_context="directory $directory (must be root-owned and not group- or world-writable)"
|
||||
uid=$(stat -c %u -- "$directory")
|
||||
directory_mode=$(stat -c %a -- "$directory")
|
||||
(( uid == 0 ))
|
||||
(( (8#$directory_mode & 0022) == 0 ))
|
||||
[[ $directory == "/" ]] && break
|
||||
directory=${directory%/*}
|
||||
[[ -n $directory ]] || directory=/
|
||||
done
|
||||
}
|
||||
|
||||
validate_trusted_configuration_file() {
|
||||
local configuration=$1 canonical uid configuration_mode size
|
||||
|
||||
failure_context="root configuration $configuration"
|
||||
[[ -f $configuration && ! -L $configuration ]]
|
||||
canonical=$(realpath -e -- "$configuration")
|
||||
[[ $canonical == "$configuration" ]]
|
||||
validate_trusted_directory "${configuration%/*}"
|
||||
uid=$(stat -c %u -- "$configuration")
|
||||
configuration_mode=$(stat -c %a -- "$configuration")
|
||||
size=$(stat -c %s -- "$configuration")
|
||||
(( uid == 0 ))
|
||||
(( (8#$configuration_mode & 0022) == 0 ))
|
||||
(( size > 0 && size <= 4096 ))
|
||||
}
|
||||
|
||||
# A packaged tree must be root-owned. A development checkout is the one
|
||||
# deliberate exception: omarchy dev link records its canonical path in a
|
||||
# root-owned /etc/omarchy.conf. That is already an explicit decision to run
|
||||
# privileged Omarchy commands from user-editable code in the checkout, so
|
||||
# reading its packaged assets does not widen the development trust boundary.
|
||||
development_source=false
|
||||
source_root_uid=$(stat -c %u -- "$source_root")
|
||||
if (( source_root_uid != 0 )); then
|
||||
omarchy_conf=/etc/omarchy.conf
|
||||
failure_context="$source_root is user-owned and $omarchy_conf must contain its trusted dev-link authorization; run omarchy dev link to authorize it"
|
||||
failure_hint="$source_root is user-owned; run omarchy dev link to authorize this development checkout, or omarchy dev unlink to use the packaged tree"
|
||||
validate_trusted_configuration_file "$omarchy_conf"
|
||||
|
||||
# validate_trusted_configuration_file walks /etc up to / and leaves its own
|
||||
# subject behind in failure_context. Without restoring ours, a checkout
|
||||
# that simply is not the authorized one refuses with "directory / must be
|
||||
# root-owned and not group- or world-writable" -- naming a directory that
|
||||
# passed, and sending the reader after a filesystem problem that is not
|
||||
# there.
|
||||
failure_context="the dev-link authorization in $omarchy_conf, which must name $source_root"
|
||||
|
||||
quoted_source_root=$source_root
|
||||
quoted_source_root=${quoted_source_root//\\/\\\\}
|
||||
quoted_source_root=${quoted_source_root//\"/\\\"}
|
||||
quoted_source_root=${quoted_source_root//\$/\\\$}
|
||||
quoted_source_root=${quoted_source_root//\`/\\\`}
|
||||
expected_config_line="export OMARCHY_PATH=\"$quoted_source_root\""
|
||||
mapfile -t omarchy_config_lines <"$omarchy_conf"
|
||||
(( ${#omarchy_config_lines[@]} == 1 ))
|
||||
[[ ${omarchy_config_lines[0]} == "$expected_config_line" ]]
|
||||
development_source=true
|
||||
failure_hint=
|
||||
fi
|
||||
|
||||
if [[ $mode == "set" ]]; then
|
||||
[[ $bg_hex =~ ^[0-9a-fA-F]{6}$ ]]
|
||||
[[ $text_hex =~ ^[0-9a-fA-F]{6}$ ]]
|
||||
fi
|
||||
|
||||
theme_dir=/usr/share/plymouth/themes/omarchy
|
||||
sddm_dir=/usr/share/sddm/themes/omarchy
|
||||
plymouth_theme_assets=(
|
||||
bullet.png
|
||||
entry.png
|
||||
lock.png
|
||||
logo.png
|
||||
omarchy.plymouth
|
||||
omarchy.script
|
||||
preview-unlock.png
|
||||
progress_bar.png
|
||||
progress_box.png
|
||||
)
|
||||
plymouth_default_assets=("${plymouth_theme_assets[@]}" logos/oma.png)
|
||||
sddm_theme_assets=(Main.qml bullet.png entry-failed.png entry.png lock-failed.png lock.png logo.png)
|
||||
sddm_default_assets=("${sddm_theme_assets[@]}" metadata.desktop theme.conf)
|
||||
|
||||
plymouth_assets=()
|
||||
sddm_assets=()
|
||||
case "$mode" in
|
||||
set)
|
||||
plymouth_assets=("${plymouth_theme_assets[@]}")
|
||||
sddm_assets=("${sddm_theme_assets[@]}")
|
||||
;;
|
||||
refresh-plymouth)
|
||||
plymouth_assets=("${plymouth_default_assets[@]}")
|
||||
;;
|
||||
refresh-sddm)
|
||||
sddm_assets=("${sddm_default_assets[@]}")
|
||||
;;
|
||||
esac
|
||||
|
||||
validate_trusted_file() {
|
||||
local source=$1 canonical uid file_mode size
|
||||
|
||||
failure_context="packaged source file $source"
|
||||
[[ -f $source && ! -L $source ]]
|
||||
canonical=$(realpath -e -- "$source")
|
||||
[[ $canonical == "$source" ]]
|
||||
file_mode=$(stat -c %a -- "$source")
|
||||
size=$(stat -c %s -- "$source")
|
||||
(( size > 0 && size <= max_asset_size ))
|
||||
|
||||
if ! $development_source; then
|
||||
validate_trusted_directory "${source%/*}"
|
||||
uid=$(stat -c %u -- "$source")
|
||||
(( uid == 0 ))
|
||||
(( (8#$file_mode & 0022) == 0 ))
|
||||
fi
|
||||
}
|
||||
|
||||
copy_trusted_file() {
|
||||
local source=$1 destination=$2
|
||||
|
||||
validate_trusted_file "$source"
|
||||
install -o 0 -g 0 -m 0600 -- "$source" "$destination"
|
||||
}
|
||||
|
||||
staging_dir=$(mktemp -d /tmp/omarchy-plymouth.XXXXXXXX)
|
||||
temporary=
|
||||
cleanup() {
|
||||
[[ -z $temporary ]] || rm -f -- "$temporary"
|
||||
rm -rf -- "$staging_dir"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
chown 0:0 -- "$staging_dir"
|
||||
chmod 0700 -- "$staging_dir"
|
||||
|
||||
plymouth_stage=$staging_dir/plymouth
|
||||
sddm_stage=$staging_dir/sddm
|
||||
mkdir -m 0700 -p -- "$plymouth_stage/logos" "$sddm_stage"
|
||||
|
||||
for asset in "${plymouth_assets[@]}"; do
|
||||
copy_trusted_file "$source_root/default/plymouth/$asset" "$plymouth_stage/$asset"
|
||||
done
|
||||
|
||||
if [[ $mode == "set" ]]; then
|
||||
# stdin was opened by the unprivileged caller. Read no more than the
|
||||
# documented limit into the root-owned stage before doing other work.
|
||||
failure_context="the selected logo (expected 1 to $max_asset_size bytes)"
|
||||
head -c "$((max_asset_size + 1))" >"$plymouth_stage/logo.png"
|
||||
logo_size=$(stat -c %s -- "$plymouth_stage/logo.png")
|
||||
(( logo_size > 0 && logo_size <= max_asset_size ))
|
||||
chown 0:0 -- "$plymouth_stage/logo.png"
|
||||
chmod 0600 -- "$plymouth_stage/logo.png"
|
||||
cp --reflink=never -- "$plymouth_stage/logo.png" "$sddm_stage/logo.png"
|
||||
|
||||
bg_r=$(awk -v n=$((16#${bg_hex:0:2})) "BEGIN{printf \"%.3f\", n/255}")
|
||||
bg_g=$(awk -v n=$((16#${bg_hex:2:2})) "BEGIN{printf \"%.3f\", n/255}")
|
||||
bg_b=$(awk -v n=$((16#${bg_hex:4:2})) "BEGIN{printf \"%.3f\", n/255}")
|
||||
|
||||
sed -i \
|
||||
-e "s/^Window.SetBackgroundTopColor.*/Window.SetBackgroundTopColor($bg_r, $bg_g, $bg_b);/" \
|
||||
-e "s/^Window.SetBackgroundBottomColor.*/Window.SetBackgroundBottomColor($bg_r, $bg_g, $bg_b);/" \
|
||||
"$plymouth_stage/omarchy.script"
|
||||
|
||||
for asset in bullet.png entry.png lock.png progress_bar.png; do
|
||||
magick "$plymouth_stage/$asset" -channel RGB +level-colors "#$text_hex","#$text_hex" "$plymouth_stage/$asset"
|
||||
done
|
||||
|
||||
copy_trusted_file "$source_root/default/sddm/omarchy/Main.qml" "$sddm_stage/Main.qml"
|
||||
sed -i \
|
||||
-e "s/#1a1b26/#__OMARCHY_SDDM_BG__/g" \
|
||||
-e "s/#ffffff/#__OMARCHY_SDDM_TEXT__/g" \
|
||||
-e "s/#__OMARCHY_SDDM_BG__/#$bg_hex/g" \
|
||||
-e "s/#__OMARCHY_SDDM_TEXT__/#$text_hex/g" \
|
||||
"$sddm_stage/Main.qml"
|
||||
|
||||
for asset in bullet.png entry.png lock.png; do
|
||||
cp --reflink=never -- "$plymouth_stage/$asset" "$sddm_stage/$asset"
|
||||
done
|
||||
for asset in entry lock; do
|
||||
magick "$plymouth_stage/$asset.png" -channel RGB +level-colors "#f7768e","#f7768e" "$sddm_stage/$asset-failed.png"
|
||||
done
|
||||
chown -R 0:0 -- "$staging_dir"
|
||||
find "$staging_dir" -type f -exec chmod 0600 -- {} +
|
||||
elif (( ${#sddm_assets[@]} )); then
|
||||
for asset in "${sddm_assets[@]}"; do
|
||||
copy_trusted_file "$source_root/default/sddm/omarchy/$asset" "$sddm_stage/$asset"
|
||||
done
|
||||
fi
|
||||
|
||||
if (( ${#plymouth_assets[@]} )); then
|
||||
validate_trusted_directory "$theme_dir"
|
||||
if [[ $mode == "refresh-plymouth" ]]; then
|
||||
validate_trusted_directory "$theme_dir/logos"
|
||||
fi
|
||||
fi
|
||||
if (( ${#sddm_assets[@]} )); then
|
||||
validate_trusted_directory "$sddm_dir"
|
||||
fi
|
||||
|
||||
publish_asset() {
|
||||
local source=$1 destination=$2 parent filename source_size copied_size
|
||||
|
||||
failure_context="destination $destination"
|
||||
[[ -f $source && ! -L $source ]]
|
||||
(( $(stat -c %u -- "$source") == 0 ))
|
||||
source_size=$(stat -c %s -- "$source")
|
||||
(( source_size > 0 && source_size <= max_asset_size ))
|
||||
|
||||
[[ $destination == /* && $destination != */ && $destination != *"/../"* ]]
|
||||
parent=${destination%/*}
|
||||
filename=${destination##*/}
|
||||
[[ -n $parent && -n $filename && $filename != "." && $filename != ".." ]]
|
||||
validate_trusted_directory "$parent"
|
||||
|
||||
temporary=$(mktemp --tmpdir="$parent" ".$filename.omarchy-new.XXXXXXXX")
|
||||
install -o 0 -g 0 -m 0644 -- "$source" "$temporary"
|
||||
copied_size=$(stat -c %s -- "$temporary")
|
||||
(( copied_size == source_size ))
|
||||
cmp -s -- "$source" "$temporary"
|
||||
sync -f -- "$temporary"
|
||||
mv --no-copy -fT -- "$temporary" "$destination"
|
||||
temporary=
|
||||
}
|
||||
|
||||
if (( ${#plymouth_assets[@]} )); then
|
||||
for asset in "${plymouth_assets[@]}"; do
|
||||
publish_asset "$plymouth_stage/$asset" "$theme_dir/$asset"
|
||||
done
|
||||
fi
|
||||
|
||||
if (( ${#sddm_assets[@]} )); then
|
||||
for asset in "${sddm_assets[@]}"; do
|
||||
publish_asset "$sddm_stage/$asset" "$sddm_dir/$asset"
|
||||
done
|
||||
validate_trusted_directory "$sddm_dir"
|
||||
rm -f -- "$sddm_dir/logo.svg"
|
||||
fi
|
||||
' bash "$mode" "$OMARCHY_PATH" "$bg_hex" "$text_hex" "$((64 * 1024 * 1024))"
|
||||
}
|
||||
|
||||
if [[ $mode == "set" ]]; then
|
||||
run_root_transaction <&"$logo_fd"
|
||||
else
|
||||
run_root_transaction </dev/null
|
||||
fi
|
||||
|
||||
if [[ $mode != "refresh-sddm" ]]; then
|
||||
sudo plymouth-set-default-theme omarchy
|
||||
|
||||
if omarchy-cmd-present limine-mkinitcpio; then
|
||||
sudo limine-mkinitcpio
|
||||
else
|
||||
sudo mkinitcpio -P
|
||||
fi
|
||||
fi
|
||||
sudo cp "$staging_dir/logo.png" "$sddm_dir/logo.png"
|
||||
for asset in bullet.png entry.png lock.png; do
|
||||
sudo cp "$staging_dir/$asset" "$sddm_dir/$asset"
|
||||
done
|
||||
for asset in entry lock; do
|
||||
magick "$staging_dir/$asset.png" -channel RGB +level-colors "#f7768e","#f7768e" "$staging_dir/$asset-failed.png"
|
||||
sudo cp "$staging_dir/$asset-failed.png" "$sddm_dir/$asset-failed.png"
|
||||
done
|
||||
sudo rm -f "$sddm_dir/logo.svg"
|
||||
@@ -677,15 +677,11 @@ user_groups() {
|
||||
if [[ -f $PROVISIONING_DIR/groups ]]; then
|
||||
while IFS= read -r group; do
|
||||
[[ -n $group ]] || continue
|
||||
# Never replay old privileged group defaults. Docker is always opt-in.
|
||||
# Input is only retained when the factory image has one of the features
|
||||
# whose installer deliberately grants access to raw input devices.
|
||||
# Never grant docker at first boot, even if an older install recorded it
|
||||
# (or a factory snapshot predating the opt-in default carries it): the
|
||||
# docker group is root-equivalent. It is opt-in via
|
||||
# omarchy-setup-security-sudoless-docker.
|
||||
[[ $group == "docker" ]] && continue
|
||||
if [[ $group == "input" ]] &&
|
||||
! pacman -Qq xpadneo-dkms &>/dev/null &&
|
||||
! pacman -Qq ydotool &>/dev/null; then
|
||||
continue
|
||||
fi
|
||||
getent group "$group" >/dev/null || continue
|
||||
[[ ",$groups," == *",$group,"* ]] || groups+=",$group"
|
||||
done <"$PROVISIONING_DIR/groups"
|
||||
@@ -746,12 +742,6 @@ create_user() {
|
||||
# for specific commands), and a duplicate grant is harmless.
|
||||
echo "%wheel ALL=(ALL:ALL) ALL" >/etc/sudoers.d/00-omarchy-wheel
|
||||
chmod 440 /etc/sudoers.d/00-omarchy-wheel
|
||||
|
||||
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
||||
for dir in "${BROWSER_POLICY_MANAGED_DIRS[@]}"; do
|
||||
[[ -d $dir || -L $dir ]] || continue
|
||||
browser_policy_setup_dir "$dir"
|
||||
done
|
||||
}
|
||||
|
||||
install_authorized_keys() {
|
||||
@@ -792,7 +782,7 @@ configure_login() {
|
||||
# After=) is what makes it deterministic — no sleep/race against SDDM's startup.
|
||||
install_autologin_once_cleanup() {
|
||||
local unit=omarchy-provision-autologin-once.service
|
||||
sed "s|@UNIT@|$unit|g" >"/etc/systemd/system/$unit" <<'UNIT'
|
||||
cat >"/etc/systemd/system/$unit" <<UNIT
|
||||
[Unit]
|
||||
Description=Drop the first-boot autologin before the next login
|
||||
Before=display-manager.service
|
||||
@@ -801,7 +791,7 @@ ConditionPathExists=/etc/sddm.conf.d/autologin.conf
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/bin/rm -f /etc/sddm.conf.d/autologin.conf
|
||||
ExecStartPost=/usr/bin/rm -f /etc/systemd/system/graphical.target.wants/@UNIT@ /etc/systemd/system/@UNIT@
|
||||
ExecStartPost=/usr/bin/rm -f /etc/systemd/system/graphical.target.wants/$unit /etc/systemd/system/$unit
|
||||
|
||||
[Install]
|
||||
WantedBy=graphical.target
|
||||
|
||||
@@ -84,7 +84,7 @@ fi
|
||||
# Dev-aware skill symlinks. Cannot live in /etc/skel because OMARCHY_PATH may
|
||||
# point at a dev checkout (omarchy dev link) where the target differs.
|
||||
# Loops every skill directory, so shipping a new one needs no edit here.
|
||||
mkdir -p ~/.agents/skills ~/.claude/skills ~/.codex/skills ~/.pi/agent/skills ~/.gemini/config/skills
|
||||
mkdir -p ~/.agents/skills ~/.claude/skills ~/.codex/skills ~/.pi/agent/skills
|
||||
for skill in "$OMARCHY_PATH"/default/agents/skills/*/; do
|
||||
skill=${skill%/}
|
||||
name=${skill##*/}
|
||||
@@ -92,7 +92,6 @@ for skill in "$OMARCHY_PATH"/default/agents/skills/*/; do
|
||||
ln -sfn "$skill" ~/.claude/skills/"$name"
|
||||
ln -sfn "$skill" ~/.codex/skills/"$name"
|
||||
ln -sfn "$skill" ~/.pi/agent/skills/"$name"
|
||||
ln -sfn "$skill" ~/.gemini/config/skills/"$name"
|
||||
done
|
||||
|
||||
mkdir -p ~/Downloads ~/Pictures ~/Videos ~/.config/gtk-3.0
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
# omarchy:summary=Overwrite the user config for the Plymouth drive decryption and boot sequence with the Omarchy default and rebuild it.
|
||||
# omarchy:requires-sudo=true
|
||||
|
||||
# Reuse the fixed-file publisher so root never resolves the source checkout or
|
||||
# follows a destination symlink while restoring the packaged assets.
|
||||
exec "$OMARCHY_PATH/bin/omarchy-plymouth-set" --refresh-default
|
||||
sudo cp -r "$OMARCHY_PATH/default/plymouth/." /usr/share/plymouth/themes/omarchy/
|
||||
sudo plymouth-set-default-theme omarchy
|
||||
|
||||
if omarchy-cmd-present limine-mkinitcpio; then
|
||||
sudo limine-mkinitcpio
|
||||
else
|
||||
sudo mkinitcpio -P
|
||||
fi
|
||||
@@ -3,6 +3,5 @@
|
||||
# omarchy:summary=Refresh the SDDM theme from default
|
||||
# omarchy:requires-sudo=true
|
||||
|
||||
# Reuse the fixed-file publisher so root never resolves an untrusted source or
|
||||
# follows a destination symlink while restoring the packaged SDDM theme.
|
||||
exec "$OMARCHY_PATH/bin/omarchy-plymouth-set" --refresh-sddm-default
|
||||
sudo rm -rf /usr/share/sddm/themes/omarchy
|
||||
sudo cp -r "$OMARCHY_PATH/default/sddm/omarchy" /usr/share/sddm/themes/omarchy
|
||||
@@ -1,120 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Show or set the region the machine is set up for
|
||||
# omarchy:args=[world|china]
|
||||
# omarchy:examples=omarchy region | omarchy region china
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
region_file="$HOME/.config/omarchy/region"
|
||||
|
||||
if (($# == 0)); then
|
||||
region="World"
|
||||
if [[ -f $region_file ]] && read -r stored <"$region_file"; then
|
||||
region=$stored
|
||||
fi
|
||||
echo "$region"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if (($# > 1)); then
|
||||
echo "Usage: omarchy-region <world|china>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
world | World) region="World" ;;
|
||||
china | China) region="China" ;;
|
||||
*)
|
||||
echo "Usage: omarchy-region <world|china>"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
menu_overlay="$HOME/.config/omarchy/extensions/omarchy-menu.jsonc"
|
||||
menu_sample="$OMARCHY_PATH/config/omarchy/extensions/omarchy-menu.jsonc"
|
||||
overlay_marker="// Omarchy region managed"
|
||||
|
||||
if [[ $region == "China" ]]; then
|
||||
# The stock machine carries the commented sample extension; a menu extension
|
||||
# someone wrote by hand refuses the switch instead of being overwritten.
|
||||
if [[ -s $menu_overlay ]] && ! grep -qFx "$overlay_marker" "$menu_overlay" &&
|
||||
! cmp -s "$menu_overlay" "$menu_sample"; then
|
||||
echo "menu: $menu_overlay has content Omarchy does not manage" >&2
|
||||
echo "Resolve it by hand, then rerun: omarchy region china" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "${menu_overlay%/*}"
|
||||
cp "$OMARCHY_PATH/default/omarchy/omarchy-menu.zh-cn.jsonc" "$menu_overlay"
|
||||
echo "menu -> Chinese labels"
|
||||
|
||||
# localectl writes /etc/locale.conf; the session picks it up at next login.
|
||||
if ! grep -qsFx 'LANG=zh_CN.UTF-8' /etc/locale.conf; then
|
||||
sudo sed -i 's/^#\(zh_CN\.UTF-8 UTF-8\)/\1/' /etc/locale.gen
|
||||
sudo locale-gen
|
||||
sudo localectl set-locale LANG=zh_CN.UTF-8
|
||||
fi
|
||||
echo "language -> zh_CN.UTF-8 (from the next login)"
|
||||
else
|
||||
if [[ -f $menu_overlay ]] && grep -qFx "$overlay_marker" "$menu_overlay"; then
|
||||
cp "$menu_sample" "$menu_overlay"
|
||||
echo "menu -> stock labels"
|
||||
fi
|
||||
|
||||
if grep -qsFx 'LANG=zh_CN.UTF-8' /etc/locale.conf; then
|
||||
sudo localectl set-locale LANG=en_US.UTF-8
|
||||
echo "language -> en_US.UTF-8 (from the next login)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Rime Ice's TUI drives its own sudo and fcitx5 restart; the schema file is
|
||||
# what a finished install leaves behind. A fresh install has no repo databases
|
||||
# until its first sync, and both pkg-add and the installer's pacman need them.
|
||||
if [[ $region == "China" && ! -f $HOME/.local/share/fcitx5/rime/rime_ice.schema.yaml ]]; then
|
||||
sudo pacman -Sy
|
||||
omarchy-pkg-add rime-ice-installer
|
||||
rime-ice-installer
|
||||
fi
|
||||
|
||||
# Rime first makes Chinese the active default; keyboard-us stays one
|
||||
# Ctrl+Space away, pinned explicitly rather than left to fcitx5's default.
|
||||
if [[ $region == "China" ]]; then
|
||||
fcitx_dir="$HOME/.config/fcitx5"
|
||||
mkdir -p "$fcitx_dir"
|
||||
cat >"$fcitx_dir/profile" <<EOF
|
||||
[Groups/0]
|
||||
Name=Default
|
||||
Default Layout=us
|
||||
DefaultIM=rime
|
||||
|
||||
[Groups/0/Items/0]
|
||||
Name=rime
|
||||
Layout=
|
||||
|
||||
[Groups/0/Items/1]
|
||||
Name=keyboard-us
|
||||
Layout=
|
||||
|
||||
[GroupOrder]
|
||||
0=Default
|
||||
EOF
|
||||
|
||||
touch "$fcitx_dir/config"
|
||||
awk '
|
||||
/^\[Hotkey\/TriggerKeys\]$/ { skip = 1; next }
|
||||
/^\[/ { skip = 0 }
|
||||
!skip { print }
|
||||
' "$fcitx_dir/config" >"$fcitx_dir/config.tmp"
|
||||
printf '[Hotkey/TriggerKeys]\n0=Control+space\n' >>"$fcitx_dir/config.tmp"
|
||||
mv "$fcitx_dir/config.tmp" "$fcitx_dir/config"
|
||||
echo "input method -> Rime Ice by default, Ctrl+Space toggles"
|
||||
|
||||
if systemctl --user is-active omarchy-fcitx5.service >/dev/null 2>&1; then
|
||||
systemctl --user restart omarchy-fcitx5.service
|
||||
fi
|
||||
fi
|
||||
|
||||
mkdir -p "${region_file%/*}"
|
||||
printf '%s\n' "$region" >"$region_file"
|
||||
echo "Region set to $region"
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Remove the ChatGPT desktop app along with its configuration and caches.
|
||||
# omarchy:requires-sudo=true
|
||||
|
||||
set -e
|
||||
|
||||
omarchy-pkg-drop openai-codex-desktop
|
||||
|
||||
# Not ~/.cache/codex-runtimes: the Codex CLI resolves its own runtime under
|
||||
# there, and it ships in a different package that survives this one.
|
||||
rm -rf \
|
||||
"$HOME/.config/Codex" \
|
||||
"$HOME/.cache/Codex"
|
||||
|
||||
echo ""
|
||||
echo "ChatGPT has been removed."
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Remove Grok Bot along with its settings and data.
|
||||
# omarchy:requires-sudo=true
|
||||
|
||||
set -e
|
||||
|
||||
omarchy-pkg-drop grok-bot
|
||||
|
||||
# Not ~/.grok: that is the Grok CLI, which is installed and used on its own.
|
||||
rm -rf \
|
||||
"$HOME/.config/Grok Bot" \
|
||||
"$HOME/.grokbot"
|
||||
|
||||
echo ""
|
||||
echo "Grok Bot has been removed."
|
||||
@@ -1,29 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Remove LM Studio along with its configuration and every model it downloaded.
|
||||
# omarchy:requires-sudo=true
|
||||
|
||||
set -e
|
||||
|
||||
omarchy-pkg-drop lmstudio-bin
|
||||
|
||||
# LM Studio's home is relocatable and the pointer is the only record of where
|
||||
# the models went, so it has to be read before it is deleted with everything else.
|
||||
lmstudio_home=""
|
||||
if [[ -f $HOME/.lmstudio-home-pointer ]]; then
|
||||
read -r lmstudio_home <"$HOME/.lmstudio-home-pointer" || true
|
||||
fi
|
||||
|
||||
rm -rf \
|
||||
"$HOME/.config/LM Studio" \
|
||||
"$HOME/.config/LM-Studio" \
|
||||
"$HOME/.lmstudio" \
|
||||
"$HOME/.lmstudio-home-pointer"
|
||||
|
||||
if [[ -n $lmstudio_home && $lmstudio_home == /* && -d $lmstudio_home ]] &&
|
||||
[[ $lmstudio_home != "/" && $lmstudio_home != "$HOME" ]]; then
|
||||
rm -rf "$lmstudio_home"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "LM Studio and its models have been removed."
|
||||
@@ -1,18 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Remove Ollama along with every model it pulled.
|
||||
# omarchy:requires-sudo=true
|
||||
|
||||
set -e
|
||||
|
||||
# The service is what holds the models open, so it goes before the package.
|
||||
sudo systemctl disable --now ollama.service 2>/dev/null || true
|
||||
|
||||
# Whichever acceleration variant is installed provides the same command.
|
||||
omarchy-pkg-drop ollama ollama-cuda ollama-rocm ollama-vulkan
|
||||
|
||||
sudo rm -rf /var/lib/ollama
|
||||
rm -rf "$HOME/.ollama"
|
||||
|
||||
echo ""
|
||||
echo "Ollama and its models have been removed."
|
||||
@@ -1,18 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Remove T3 Code along with its configuration and workspaces.
|
||||
# omarchy:requires-sudo=true
|
||||
|
||||
set -e
|
||||
|
||||
omarchy-pkg-drop t3code-bin
|
||||
|
||||
# T3 Code drives agents that keep their own state -- ~/.claude.json, ~/.grok,
|
||||
# ~/.local/share/opencode -- and those outlive it, so only its own goes.
|
||||
rm -rf \
|
||||
"$HOME/.config/t3code" \
|
||||
"$HOME/.t3"
|
||||
rm -f "$HOME/.config/t3code-flags.conf"
|
||||
|
||||
echo ""
|
||||
echo "T3 Code has been removed."
|
||||
@@ -10,7 +10,7 @@ if [[ -z $1 ]]; then
|
||||
fi
|
||||
|
||||
remove_php() {
|
||||
omarchy-pkg-drop php composer php-sqlite xdebug
|
||||
sudo pacman -Rns --noconfirm php composer php-sqlite xdebug 2>/dev/null || true
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
@@ -50,7 +50,7 @@ laravel)
|
||||
;;
|
||||
symfony)
|
||||
echo -e "Removing Symfony CLI...\n"
|
||||
omarchy-pkg-drop symfony-cli
|
||||
sudo pacman -Rns --noconfirm symfony-cli 2>/dev/null || true
|
||||
;;
|
||||
python)
|
||||
echo -e "Removing Python...\n"
|
||||
|
||||
@@ -13,9 +13,9 @@ if gum confirm "Are you sure you want to remove all preinstalled web apps, TUI w
|
||||
hyprctl reload
|
||||
|
||||
# Remove mise stubs
|
||||
rm -f ~/.local/bin/codex ~/.local/bin/claude ~/.local/bin/agy ~/.local/bin/copilot \
|
||||
rm -f ~/.local/bin/codex ~/.local/bin/claude ~/.local/bin/gemini ~/.local/bin/copilot \
|
||||
~/.local/bin/gh ~/.local/bin/opencode ~/.local/bin/playwright ~/.local/bin/playwright-cli ~/.local/bin/pi \
|
||||
~/.local/bin/omp ~/.local/bin/ori ~/.local/bin/grok ~/.local/bin/crush ~/.local/bin/ghui ~/.local/bin/hunk
|
||||
~/.local/bin/omp ~/.local/bin/grok ~/.local/bin/crush ~/.local/bin/ghui ~/.local/bin/hunk
|
||||
|
||||
omarchy-pkg-drop \
|
||||
aether \
|
||||
|
||||
@@ -41,9 +41,6 @@ setup_pam_config() {
|
||||
fi
|
||||
else
|
||||
echo "Creating polkit configuration with fingerprint authentication..."
|
||||
# omarchy:heredoc-expands paths=none -- $fprintd_gate is the literal PAM
|
||||
# line defined above, shared with the two sed insertions so the gate cannot
|
||||
# drift between files. The only path in it is the fixed /usr/bin one.
|
||||
sudo tee /etc/pam.d/polkit-1 >/dev/null <<EOF
|
||||
$fprintd_gate
|
||||
auth sufficient pam_fprintd.so
|
||||
|
||||
+15
-100
@@ -1,62 +1,32 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Set up the OpenSSH server, open the firewall, and authorize an SSH key
|
||||
# omarchy:args=[--key=<public-key>] [--gh-keys <github-username>]
|
||||
# omarchy:examples=omarchy-setup-security-sshd | omarchy-setup-security-sshd --gh-keys dhh | omarchy-setup-security-sshd --key="ssh-ed25519 AAAA... user@host"
|
||||
# omarchy:args=[--key=<public-key>]
|
||||
# omarchy:examples=omarchy-setup-security-sshd | omarchy-setup-security-sshd --key="ssh-ed25519 AAAA... user@host"
|
||||
# omarchy:requires-sudo=true
|
||||
|
||||
set -e
|
||||
|
||||
AUTHORIZED_KEYS="$HOME/.ssh/authorized_keys"
|
||||
KEY=""
|
||||
GITHUB_USER=""
|
||||
|
||||
# Checked while parsing, before anything is installed or opened: an empty or
|
||||
# option-shaped username otherwise falls through to the interactive menu having
|
||||
# already changed the machine, and `--gh-keys --help` would take --help as the
|
||||
# username and set the server up on its way to failing.
|
||||
require_github_user() {
|
||||
if [[ -z $1 || $1 == -* ]]; then
|
||||
echo "omarchy-setup-security-sshd: --gh-keys needs a GitHub username." >&2
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
--key=*) KEY="${1#--key=}" ;;
|
||||
--gh-keys=*)
|
||||
GITHUB_USER="${1#--gh-keys=}"
|
||||
require_github_user "$GITHUB_USER"
|
||||
;;
|
||||
--gh-keys)
|
||||
shift
|
||||
GITHUB_USER="${1:-}"
|
||||
require_github_user "$GITHUB_USER"
|
||||
;;
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--key=*) KEY="${arg#--key=}" ;;
|
||||
-h | --help)
|
||||
echo "Usage: omarchy-setup-security-sshd [--key=<public-key>] [--gh-keys <github-username>]"
|
||||
echo "Usage: omarchy-setup-security-sshd [--key=<public-key>]"
|
||||
echo
|
||||
echo "Sets up the OpenSSH server, opens the SSH port in the UFW firewall,"
|
||||
echo "and authorizes an SSH key (from GitHub, pasted, or passed via --key)."
|
||||
echo
|
||||
echo "Passing --key or --gh-keys skips the prompts, so the command can run"
|
||||
echo "unattended from a script or a fresh machine's first login."
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "omarchy-setup-security-sshd: unknown option '$1'. Try --help." >&2
|
||||
echo "omarchy-setup-security-sshd: unknown option '$arg'. Try --help." >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ -n $KEY && -n $GITHUB_USER ]]; then
|
||||
echo "omarchy-setup-security-sshd: pass either --key or --gh-keys, not both." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
setup_sshd() {
|
||||
echo "Installing and starting the OpenSSH server..."
|
||||
omarchy-pkg-add openssh
|
||||
@@ -100,7 +70,13 @@ authorize_key() {
|
||||
}
|
||||
|
||||
authorize_keys_from_github() {
|
||||
local username="$1" keys added=0
|
||||
local username keys added=0
|
||||
|
||||
username=$(gum input --prompt "GitHub username> " --placeholder "dhh") || exit 1
|
||||
if [[ -z $username ]]; then
|
||||
echo -e "\e[31mNo GitHub username given.\e[0m" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Fetching keys from https://github.com/$username.keys..."
|
||||
if ! keys=$(curl -fsSL "https://github.com/$username.keys") || [[ -z $keys ]]; then
|
||||
@@ -119,18 +95,6 @@ authorize_keys_from_github() {
|
||||
fi
|
||||
}
|
||||
|
||||
prompt_for_github_user() {
|
||||
local username
|
||||
|
||||
username=$(gum input --prompt "GitHub username> " --placeholder "dhh") || exit 1
|
||||
if [[ -z $username ]]; then
|
||||
echo -e "\e[31mNo GitHub username given.\e[0m" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
authorize_keys_from_github "$username"
|
||||
}
|
||||
|
||||
authorize_pasted_key() {
|
||||
local key
|
||||
|
||||
@@ -143,50 +107,6 @@ authorize_pasted_key() {
|
||||
authorize_key "$key" || exit 1
|
||||
}
|
||||
|
||||
# Only called after a key is authorized. Disabling password authentication
|
||||
# before then could lock the owner out of the machine.
|
||||
disable_password_auth() {
|
||||
local config=/etc/ssh/sshd_config.d/10-omarchy-hardening.conf
|
||||
local effective_config
|
||||
|
||||
if [[ ! -s $AUTHORIZED_KEYS ]]; then
|
||||
echo -e "\e[31mCannot disable SSH password authentication without an authorized key.\e[0m" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "Disabling SSH password authentication, now that a key is authorized..."
|
||||
sudo install -Dm644 /dev/stdin "$config" <<'CONF'
|
||||
# Written by omarchy-setup-security-sshd once an SSH key was authorized.
|
||||
# Delete this file and reload sshd to allow password logins again.
|
||||
PasswordAuthentication no
|
||||
KbdInteractiveAuthentication no
|
||||
CONF
|
||||
|
||||
# Validate before reloading: a config sshd rejects would otherwise take the
|
||||
# service down on its next restart, potentially stranding a remote owner.
|
||||
if ! sudo sshd -t; then
|
||||
echo -e "\e[31msshd rejected the hardening config; removing it and leaving passwords on.\e[0m" >&2
|
||||
sudo rm -f "$config"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Syntax alone is insufficient because sshd uses the first value it reads for
|
||||
# these settings. An earlier administrator rule could leave passwords enabled.
|
||||
# Match keywords case-insensitively: OpenSSH 9.x dumps them lowercase, 10.x
|
||||
# in CamelCase.
|
||||
if ! effective_config=$(sudo sshd -T) ||
|
||||
! grep -qixF "passwordauthentication no" <<<"$effective_config" ||
|
||||
! grep -qixF "kbdinteractiveauthentication no" <<<"$effective_config"; then
|
||||
echo -e "\e[31msshd did not apply the password-authentication restrictions; removing the ineffective config.\e[0m" >&2
|
||||
sudo rm -f "$config"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Reload rather than restart so an administrator already connected keeps
|
||||
# their session.
|
||||
sudo systemctl reload sshd.service
|
||||
}
|
||||
|
||||
echo -e "\e[32mSetting up SSH server access with key-based authentication.\n\e[0m"
|
||||
|
||||
setup_sshd
|
||||
@@ -195,18 +115,13 @@ open_firewall
|
||||
echo
|
||||
if [[ -n $KEY ]]; then
|
||||
authorize_key "$KEY" || exit 1
|
||||
elif [[ -n $GITHUB_USER ]]; then
|
||||
authorize_keys_from_github "$GITHUB_USER"
|
||||
else
|
||||
case $(gum choose "Grab key from GitHub" "Paste key manually" --header "How would you like to add your SSH key?") in
|
||||
"Grab key from GitHub") prompt_for_github_user ;;
|
||||
"Grab key from GitHub") authorize_keys_from_github ;;
|
||||
"Paste key manually") authorize_pasted_key ;;
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
disable_password_auth
|
||||
|
||||
echo -e "\e[32m\nPerfect! The SSH server is running and your key is authorized.\e[0m"
|
||||
echo "Password logins are off; this machine now accepts authorized keys only."
|
||||
echo "You can now connect with: ssh $USER@$(hostname)"
|
||||
@@ -23,26 +23,16 @@ omarchy-git-url-check "$REPO_URL" || exit 1
|
||||
|
||||
THEMES_DIR="$HOME/.config/omarchy/themes"
|
||||
|
||||
# Strip user@host: prefix from scp-style SSH URLs so basename sees just the path.
|
||||
# git reads a URL as scp-style when a colon appears before any slash, so the path
|
||||
# after it need not hold one: `git@host:omarchy-blue-theme.git` is a repo in that
|
||||
# user's home, and leaving its prefix on names the theme after the whole URL.
|
||||
# Strip user@host: prefix from scp-style SSH URLs so basename sees just the path
|
||||
REPO_PATH="$REPO_URL"
|
||||
[[ $REPO_PATH != *"://"* && $REPO_PATH == *:* && ${REPO_PATH%%:*} != */* ]] && REPO_PATH="${REPO_PATH#*:}"
|
||||
[[ $REPO_PATH != *"://"* && $REPO_PATH == *:*/* ]] && REPO_PATH="${REPO_PATH#*:}"
|
||||
THEME_NAME=$(basename -- "$REPO_PATH" .git | sed -E 's/^omarchy-//; s/-theme$//' | tr '[:upper:]' '[:lower:]')
|
||||
THEME_PATH="$THEMES_DIR/$THEME_NAME"
|
||||
|
||||
# The name comes from the URL, is joined into a path that is about to be
|
||||
# removed, and then names a directory the rest of Omarchy passes around by
|
||||
# name: Style > Unlock builds a command line out of the one the picker
|
||||
# returned. So it is held to the characters a theme name needs rather than
|
||||
# screened for the harm of the day -- a repo called `..` would take
|
||||
# ~/.config/omarchy with it, and one called `a';'id` would carry its own
|
||||
# command into that picker. The leading character is kept out of `.` and `-`,
|
||||
# which also covers `host:-s/foo.git` leaving basename with `.git`.
|
||||
# A bracket range follows the locale's collation, not ASCII: `[a-z]` takes in
|
||||
# `é` under en_US.UTF-8. Pin the locale so the set is the one written here.
|
||||
if ! (LC_ALL=C; [[ $THEME_NAME =~ ^[a-z0-9_][a-z0-9._+-]*$ ]]); then
|
||||
# The name comes from the URL and is joined into a path that is about to be
|
||||
# removed, so a repo called `..` would take ~/.config/omarchy with it. A leading
|
||||
# dot is refused with it: `host:-s/foo.git` leaves basename with `.git`.
|
||||
if [[ -z $THEME_NAME || $THEME_NAME == .* || $THEME_NAME == */* ]]; then
|
||||
echo "Error: '$REPO_URL' does not give a usable theme name."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -3,15 +3,23 @@
|
||||
# omarchy:summary=Apply the current theme color to Chromium, Chrome, Edge, and Brave
|
||||
# omarchy:hidden=true
|
||||
|
||||
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
||||
|
||||
CHROMIUM_THEME=$HOME/.local/state/omarchy/current/theme/chromium.theme
|
||||
THEME_HEX_COLOR=$BROWSER_POLICY_DEFAULT_COLOR
|
||||
|
||||
if [[ -f $CHROMIUM_THEME ]]; then
|
||||
THEME_HEX_COLOR=$(browser_policy_theme_hex "$(<$CHROMIUM_THEME)")
|
||||
THEME_RGB_COLOR=$(<$CHROMIUM_THEME)
|
||||
THEME_HEX_COLOR=$(printf '#%02x%02x%02x' ${THEME_RGB_COLOR//,/ })
|
||||
else
|
||||
# Use a default, neutral grey if theme doesn't have a color
|
||||
THEME_HEX_COLOR="#1c2027"
|
||||
fi
|
||||
|
||||
set_browser_policy() {
|
||||
local policy_dir="$1"
|
||||
|
||||
[[ -d $policy_dir ]] || return
|
||||
echo "{\"BrowserThemeColor\": \"$THEME_HEX_COLOR\", \"BrowserColorScheme\": \"device\"}" | tee "$policy_dir/color.json" >/dev/null
|
||||
}
|
||||
|
||||
refresh_running_browser() {
|
||||
local process="$1"
|
||||
local command="$2"
|
||||
@@ -22,15 +30,17 @@ refresh_running_browser() {
|
||||
fi
|
||||
}
|
||||
|
||||
failed=0
|
||||
omarchy-theme-set-browser-policy "${THEME_HEX_COLOR#\#}" || failed=1
|
||||
|
||||
set_browser_policy /etc/chromium/policies/managed
|
||||
refresh_running_browser chromium chromium
|
||||
|
||||
set_browser_policy /etc/opt/chrome/policies/managed
|
||||
refresh_running_browser chrome google-chrome-stable || refresh_running_browser chrome google-chrome
|
||||
|
||||
set_browser_policy /etc/opt/edge/policies/managed
|
||||
refresh_running_browser msedge microsoft-edge-stable
|
||||
|
||||
set_browser_policy /etc/brave/policies/managed
|
||||
refresh_running_browser brave brave
|
||||
# Match on the binary path: the running process is named plain "brave", and a
|
||||
# bare -f brave-origin pattern would also match the installer's own terminal.
|
||||
refresh_running_browser /opt/brave-origin-bin/ brave-origin -f
|
||||
|
||||
exit "$failed"
|
||||
@@ -1,123 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Write the current theme color into the browser policy directories
|
||||
# omarchy:args=<rrggbb>
|
||||
# omarchy:hidden=true
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Whenever this runs as root — invoked directly through the passwordless
|
||||
# sudoers rule, or re-execed by require_root below — sudo's secure_path decides
|
||||
# where a bare helper resolves, and a dev link (etc/sudoers.d/omarchy-dev-path)
|
||||
# prepends a user-writable checkout bin/ to it. Every helper this script calls
|
||||
# by bare name (printf's builtin aside: install, mktemp, rm) is a system tool,
|
||||
# never an omarchy-* command, so pin PATH to trusted system directories and keep
|
||||
# root from resolving one out of that checkout. The unprivileged wrapper phase
|
||||
# keeps the caller's PATH so it can still find sudo/pkexec.
|
||||
if (( EUID == 0 )); then
|
||||
export PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin
|
||||
fi
|
||||
|
||||
# Enterprise policy trust roots. The list is fixed here rather than taken from
|
||||
# the caller: the caller chooses a color, never a path.
|
||||
POLICY_DIRS=(
|
||||
/etc/chromium/policies/managed
|
||||
/etc/opt/chrome/policies/managed
|
||||
/etc/opt/edge/policies/managed
|
||||
/etc/brave/policies/managed
|
||||
)
|
||||
|
||||
# The path etc/sudoers.d/omarchy-theme-browser names. The privileged half always
|
||||
# runs from there rather than from whichever copy was invoked, so the rule
|
||||
# matches even where $OMARCHY_PATH points at a checkout.
|
||||
PACKAGED_PATH=/usr/bin/omarchy-theme-set-browser-policy
|
||||
|
||||
usage() {
|
||||
echo "Usage: omarchy-theme-set-browser-policy <rrggbb>" >&2
|
||||
}
|
||||
|
||||
if (( $# != 1 )); then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
color="$1"
|
||||
|
||||
# Six lowercase hex digits is the whole of what this accepts. The leading "#"
|
||||
# is added when the JSON is written rather than passed in: "#" opens a comment
|
||||
# in sudoers, and keeping it out of argv lets the sudoers rule spell the
|
||||
# argument as a plain six-character glob.
|
||||
if [[ ! $color =~ ^[0-9a-f]{6}$ ]]; then
|
||||
echo "omarchy-theme-set-browser-policy: expected six lowercase hex digits, got '$color'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# True when sudo would run this exact command without stopping for a password.
|
||||
# `sudo -l` on its own reports whether a command is permitted, which the blanket
|
||||
# %wheel rule answers yes to for everything; the long listing prints the matched
|
||||
# entry's tags, so !authenticate is the grant in
|
||||
# etc/sudoers.d/omarchy-theme-browser and nothing else. Listing runs nothing
|
||||
# and, under -n, prompts for nothing.
|
||||
sudo_grants_passwordless() {
|
||||
sudo -n -l -l "$PACKAGED_PATH" "$@" 2>/dev/null | grep -q '!authenticate'
|
||||
}
|
||||
|
||||
require_root() {
|
||||
if (( EUID == 0 )); then
|
||||
return
|
||||
elif [[ -t 0 ]] || sudo_grants_passwordless "$@"; then
|
||||
exec sudo "$PACKAGED_PATH" "$@"
|
||||
else
|
||||
exec pkexec "$PACKAGED_PATH" "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
require_root "$color"
|
||||
|
||||
failed=0
|
||||
staged=""
|
||||
# Bash 5.3 makes the EXIT trap's last command decide the script's exit status,
|
||||
# so this handler must not end on a false test. Every successful run clears
|
||||
# staged, and a trailing `[[ -n $staged ]] && ...` would report that as failure.
|
||||
cleanup() {
|
||||
if [[ -n $staged ]]; then
|
||||
rm -f "$staged"
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
for policy_dir in "${POLICY_DIRS[@]}"; do
|
||||
# Only browsers Omarchy has installed have a policy directory. Creating one
|
||||
# here would hand a browser a managed-policy root it does not otherwise have.
|
||||
[[ -d $policy_dir && ! -L $policy_dir ]] || continue
|
||||
|
||||
dest=$policy_dir/color.json
|
||||
staged=$(mktemp) || {
|
||||
failed=1
|
||||
continue
|
||||
}
|
||||
printf '{"BrowserThemeColor": "#%s", "BrowserColorScheme": "device"}\n' "$color" >"$staged"
|
||||
|
||||
if [[ -L $dest || -d $dest ]]; then
|
||||
if ! rm -rf -- "$dest"; then
|
||||
rm -f "$staged"
|
||||
staged=""
|
||||
echo "omarchy-theme-set-browser-policy: cannot replace $dest" >&2
|
||||
failed=1
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! install -m 0644 -o root -g root -T "$staged" "$dest"; then
|
||||
rm -f "$staged"
|
||||
staged=""
|
||||
echo "omarchy-theme-set-browser-policy: cannot write $dest" >&2
|
||||
failed=1
|
||||
continue
|
||||
fi
|
||||
|
||||
rm -f "$staged"
|
||||
staged=""
|
||||
done
|
||||
|
||||
exit "$failed"
|
||||
@@ -5,9 +5,3 @@
|
||||
# omarchy:examples=omarchy toggle bar | omarchy toggle bar off | omarchy toggle bar on
|
||||
|
||||
omarchy-toggle bar-off "${1:-toggle}"
|
||||
|
||||
# The shell's watch on the toggles directory can miss flag changes that land in
|
||||
# quick succession, stranding the bar off screen until the shell restarts.
|
||||
# Nudge the bar to re-read the flag; quiet best-effort so the toggle still
|
||||
# works when the shell is not up.
|
||||
omarchy-shell -q omarchy.bar syncHidden
|
||||
@@ -101,6 +101,11 @@ if [[ ! -f $image_path ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if omarchy-cmd-missing magick; then
|
||||
echo "ImageMagick is required to convert logo images" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $mode == "braille" ]]; then
|
||||
pixel_width=$((width * 2))
|
||||
pixel_height=$((height * 4))
|
||||
|
||||
@@ -597,9 +597,6 @@ preserve_kernel_cmdline_root() {
|
||||
fi
|
||||
|
||||
log "Preserving the kernel cmdline root parameters in $default_conf"
|
||||
# omarchy:heredoc-expands paths=none -- ${boot_params[*]} is the kernel
|
||||
# cmdline fragment assembled above from findmnt output (root=, rootflags=),
|
||||
# not a filesystem path this script controls.
|
||||
as_root tee -a "$default_conf" >/dev/null <<EOF
|
||||
# Written by omarchy-upgrade-to-quattro. The += drop-ins in
|
||||
# /etc/limine-entry-tool.d/ stop limine-entry-tool from reading
|
||||
@@ -1190,19 +1187,11 @@ ensure_sleep_lock_service() {
|
||||
}
|
||||
|
||||
run_post_upgrade_migrations() {
|
||||
local pending_status
|
||||
PATH="$package_path" command -v omarchy-migrate >/dev/null 2>&1 || return 0
|
||||
|
||||
log "Running Omarchy migrations"
|
||||
if ! run_as_user_omarchy OMARCHY_UPGRADE_TO_QUATTRO_LIVE=1 omarchy-migrate; then
|
||||
fail "Omarchy migrations did not complete. Fix the error above and rerun the upgrade before rebooting."
|
||||
fi
|
||||
if run_as_user_omarchy omarchy-migrate --pending >/dev/null; then
|
||||
fail "Omarchy migrations are still pending. Rerun the upgrade before rebooting."
|
||||
else
|
||||
pending_status=$?
|
||||
if (( pending_status != 1 )); then
|
||||
fail "Could not verify that Omarchy migrations completed. Rerun the upgrade before rebooting."
|
||||
fi
|
||||
warn "Could not run Omarchy migrations; the user may be prompted to run them after login."
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -1323,23 +1312,9 @@ apply_system_transition() {
|
||||
/usr/share/icons/Yaru/scalable/actions/go-next-symbolic.svg
|
||||
as_root gtk-update-icon-cache /usr/share/icons/Yaru >/dev/null 2>&1 || true
|
||||
|
||||
local browser_policy_helper=/usr/share/omarchy/install/helpers/browser-policy.sh
|
||||
if ! as_root test -f "$browser_policy_helper"; then
|
||||
warn "$browser_policy_helper is unavailable; Chromium policy directories were not hardened."
|
||||
else
|
||||
as_root env OMARCHY_PATH=/usr/share/omarchy \
|
||||
bash -euo pipefail -c '
|
||||
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
||||
browser_policy_setup_dir /etc/chromium/policies/managed
|
||||
for dir in "${BROWSER_POLICY_MANAGED_DIRS[@]}"; do
|
||||
[[ $dir == "/etc/chromium/policies/managed" ]] && continue
|
||||
[[ -d $dir || -L $dir ]] || continue
|
||||
browser_policy_setup_dir "$dir"
|
||||
done
|
||||
'
|
||||
fi
|
||||
as_root install -d -m 0777 /etc/chromium/policies/managed
|
||||
as_root install -d -m 0755 /usr/lib/chromium
|
||||
printf '%s\n' '{"distribution":{"require_eula":false},"browser":{"theme":{"color_scheme":0,"color_scheme2":0}}}' | \
|
||||
printf '%s\n' '{"browser":{"theme":{"color_scheme":0,"color_scheme2":0}}}' | \
|
||||
as_root tee /usr/lib/chromium/initial_preferences >/dev/null
|
||||
|
||||
# Deliberately do NOT add the user to the docker group. That group is
|
||||
@@ -1375,6 +1350,7 @@ EOF
|
||||
as_root systemctl disable docker.service >/dev/null 2>&1 || true
|
||||
|
||||
enable_system_service cups.service
|
||||
enable_system_service cups-browsed.service
|
||||
enable_system_service avahi-daemon.service
|
||||
enable_system_service linux-modules-cleanup.service
|
||||
enable_system_service docker.socket
|
||||
@@ -1406,9 +1382,6 @@ EOF
|
||||
autologin_user=$(as_root awk -F= '/^User=/ { print $2; exit }' /etc/sddm.conf.d/autologin.conf 2>/dev/null || true)
|
||||
fi
|
||||
[[ -n ${autologin_user:-} ]] || autologin_user="$target_user"
|
||||
# omarchy:heredoc-expands paths=none -- $autologin_user is a username, read
|
||||
# back from the root-owned drop-in or falling back to $target_user. Same
|
||||
# mechanism as the old getty override: a name expands, no path does.
|
||||
cat <<EOF | as_root tee /etc/sddm.conf.d/autologin.conf >/dev/null
|
||||
[Autologin]
|
||||
User=$autologin_user
|
||||
@@ -1419,8 +1392,6 @@ EOF
|
||||
fi
|
||||
|
||||
as_root install -d -m 0755 -o sddm -g sddm /var/lib/sddm 2>/dev/null || as_root install -d -m 0755 /var/lib/sddm
|
||||
# omarchy:heredoc-expands paths=none -- $target_user is a username, not a
|
||||
# path; SDDM's state file records who logged in last.
|
||||
cat <<EOF | as_root tee /var/lib/sddm/state.conf >/dev/null
|
||||
[Last]
|
||||
Session=omarchy.desktop
|
||||
@@ -2335,11 +2306,6 @@ refresh_current_theme_after_upgrade() {
|
||||
# hooks because one of them runs `hyprctl reload`. Still poke terminal
|
||||
# emulators so the active upgrade terminal picks up generated theme files.
|
||||
run_as_user_omarchy omarchy-restart-terminal >/dev/null 2>&1 || true
|
||||
|
||||
# apply_system_transition purged user-owned color.json. Headless theme-set
|
||||
# skipped omarchy-theme-set-browser, so rewrite the colour here.
|
||||
run_as_user_omarchy omarchy-theme-set-browser >/dev/null 2>&1 ||
|
||||
warn "Could not apply browser theme colour. Run 'omarchy theme set \"$theme_name\"' after reboot if Chromium's theme looks stale."
|
||||
}
|
||||
|
||||
# Everything below mutates the system, so a non-zero exit from here on leaves a
|
||||
@@ -2374,8 +2340,8 @@ run_as_user_omarchy omarchy-bar defaults ||
|
||||
cleanup_retired_services
|
||||
ensure_sleep_lock_service
|
||||
remove_retired_default_packages
|
||||
run_final_system_package_upgrade
|
||||
run_post_upgrade_migrations
|
||||
run_final_system_package_upgrade
|
||||
run_post_upgrade_update_steps
|
||||
refresh_current_theme_after_upgrade
|
||||
# Do not force-reload Hyprland in the live upgraded session. The legacy
|
||||
|
||||
@@ -14,8 +14,6 @@ fi
|
||||
|
||||
if grep -q "https://pkgs.omarchy.org/stable/" /etc/pacman.conf; then
|
||||
pkgs="stable"
|
||||
elif grep -q "https://pkgs.omarchy.org/rc/" /etc/pacman.conf; then
|
||||
pkgs="rc"
|
||||
elif grep -q "https://pkgs.omarchy.org/edge/" /etc/pacman.conf; then
|
||||
pkgs="edge"
|
||||
else
|
||||
|
||||
+16
-98
@@ -13,18 +13,6 @@ safe_icon_name() {
|
||||
| sed 's/[^[:alnum:]]\+/-/g; s/^-//; s/-$//'
|
||||
}
|
||||
|
||||
require_plain_name() {
|
||||
# The name becomes a filename. A slash would turn it into directory levels, so
|
||||
# the launcher lands somewhere omarchy-webapp-remove cannot address and the app
|
||||
# is stuck in the launcher; a leading ../ leaves the applications directory
|
||||
# altogether. Refuse rather than silently renaming what the user typed -- most
|
||||
# often it is a URL entered in the name field.
|
||||
if [[ $1 == */* ]]; then
|
||||
echo "App name cannot contain '/': $1"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
icon_name_from_ref() {
|
||||
local ref="$1"
|
||||
local name
|
||||
@@ -54,34 +42,6 @@ download_icon() {
|
||||
[[ -s $2 && $(file -b --mime-type "$2") == image/* ]]
|
||||
}
|
||||
|
||||
# Chromium --app= treats javascript:, file:, and data: as a document to
|
||||
# run. Prefix schemeless input with https as before, then refuse anything
|
||||
# that is not http(s).
|
||||
normalize_webapp_url() {
|
||||
local url=$1
|
||||
if [[ ! $url =~ ^[a-zA-Z][a-zA-Z0-9+.-]*: ]]; then
|
||||
url="https://$url"
|
||||
fi
|
||||
printf '%s' "$url"
|
||||
}
|
||||
|
||||
# Raw whitespace must be percent-encoded in a URL. Refuse it before serializing
|
||||
# the desktop entry; before Exec argument quoting, it also split browser flags
|
||||
# and additional URLs into separate arguments. Schemes are case-insensitive.
|
||||
require_http_url() {
|
||||
local url=$1
|
||||
|
||||
if [[ $url =~ [[:space:]] ]]; then
|
||||
echo "Error: web app URL must not contain whitespace." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! ${url,,} =~ ^https?:// ]]; then
|
||||
echo "Error: web app URL must be http or https." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
fetch_site_icon() {
|
||||
local site_url="$1" dest="$2"
|
||||
local origin page icon_url
|
||||
@@ -105,44 +65,13 @@ fetch_site_icon() {
|
||||
download_icon "https://www.google.com/s2/favicons?domain=${site_url}&sz=256" "$dest"
|
||||
}
|
||||
|
||||
desktop_string_escape() {
|
||||
# Desktop Entry "string" value (freedesktop Desktop Entry Spec, "Value types"):
|
||||
# a raw newline would start a new key line and let a value inject a second
|
||||
# Exec=. Escape backslash first, then tab/CR/LF and a leading space. Every value
|
||||
# written into the .desktop file passes through here.
|
||||
#
|
||||
# Parameter expansion rather than sed: GNU sed's N auto-prints the pattern space
|
||||
# and exits at end of input, so a `:a;N;$!ba` slurp skips every following s///
|
||||
# for a value with no newline in it - which is every value except the injection
|
||||
# attempt this exists to stop.
|
||||
local value="$1"
|
||||
|
||||
value=${value//\\/\\\\}
|
||||
value=${value//$'\t'/\\t}
|
||||
value=${value//$'\r'/\\r}
|
||||
value=${value//$'\n'/\\n}
|
||||
[[ $value == " "* ]] && value="\\s${value# }"
|
||||
|
||||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
desktop_exec_arg() {
|
||||
# One Exec argument, double-quoted per the freedesktop Exec spec: inside quotes
|
||||
# " ` $ \ take a backslash and a literal % becomes %%. Only the default Exec's
|
||||
# URL needs this; $CUSTOM_EXEC stays a whole command line (file-syntax only).
|
||||
local escaped
|
||||
escaped=$(printf '%s' "$1" \
|
||||
| sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/`/\\`/g' -e 's/\$/\\$/g' -e 's/%/%%/g')
|
||||
printf '"%s"' "$escaped"
|
||||
}
|
||||
|
||||
if (( $# < 3 )); then
|
||||
echo -e "\e[32mLet's create a new web app you can start with the app launcher.\n\e[0m"
|
||||
APP_NAME=$(gum input --prompt "Name> " --placeholder "My favorite web app")
|
||||
require_plain_name "$APP_NAME"
|
||||
APP_URL=$(gum input --prompt "URL> " --placeholder "https://example.com")
|
||||
APP_URL=$(normalize_webapp_url "$APP_URL")
|
||||
require_http_url "$APP_URL"
|
||||
if [[ ! $APP_URL =~ ^[a-zA-Z][a-zA-Z0-9+.-]*: ]]; then
|
||||
APP_URL="https://$APP_URL"
|
||||
fi
|
||||
|
||||
# Try to fetch the site's icon automatically first.
|
||||
mkdir -p "$ICON_DIR"
|
||||
@@ -159,8 +88,10 @@ if (( $# < 3 )); then
|
||||
INTERACTIVE_MODE=true
|
||||
else
|
||||
APP_NAME="$1"
|
||||
APP_URL=$(normalize_webapp_url "$2")
|
||||
require_http_url "$APP_URL"
|
||||
APP_URL="$2"
|
||||
if [[ ! $APP_URL =~ ^[a-zA-Z][a-zA-Z0-9+.-]*: ]]; then
|
||||
APP_URL="https://$APP_URL"
|
||||
fi
|
||||
ICON_REF="$3"
|
||||
CUSTOM_EXEC="$4" # Optional custom exec command
|
||||
MIME_TYPES="$5" # Optional mime types
|
||||
@@ -173,8 +104,6 @@ if [[ -z $APP_NAME || -z $APP_URL ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
require_plain_name "$APP_NAME"
|
||||
|
||||
if [[ -z $ICON_REF ]]; then
|
||||
ICON_VALUE=$(safe_icon_name "$APP_NAME")
|
||||
mkdir -p "$ICON_DIR"
|
||||
@@ -199,39 +128,28 @@ else
|
||||
ICON_VALUE=$(icon_name_from_ref "$ICON_REF")
|
||||
fi
|
||||
|
||||
# Default Exec quotes the URL as one Exec-spec argument; the whole line then gets
|
||||
# the file-syntax escaping below (unescaped first at read time per spec, so the
|
||||
# layers compose). $CUSTOM_EXEC is a full command line, so it gets file-syntax only.
|
||||
if [[ -n $CUSTOM_EXEC ]]; then
|
||||
EXEC_COMMAND=$CUSTOM_EXEC
|
||||
else
|
||||
EXEC_COMMAND="omarchy-launch-webapp $(desktop_exec_arg "$APP_URL")"
|
||||
fi
|
||||
# Use custom exec if provided, otherwise default behavior
|
||||
EXEC_COMMAND="${CUSTOM_EXEC:-omarchy-launch-webapp $APP_URL}"
|
||||
|
||||
# Create application .desktop file
|
||||
DESKTOP_DIR="$HOME/.local/share/applications"
|
||||
DESKTOP_FILE="$DESKTOP_DIR/$APP_NAME.desktop"
|
||||
mkdir -p "$DESKTOP_DIR"
|
||||
|
||||
name_field=$(desktop_string_escape "$APP_NAME")
|
||||
exec_field=$(desktop_string_escape "$EXEC_COMMAND")
|
||||
icon_field=$(desktop_string_escape "$ICON_VALUE")
|
||||
DESKTOP_FILE="$HOME/.local/share/applications/$APP_NAME.desktop"
|
||||
mkdir -p "$(dirname "$DESKTOP_FILE")"
|
||||
|
||||
cat >"$DESKTOP_FILE" <<EOF
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=$name_field
|
||||
Comment=$name_field
|
||||
Exec=$exec_field
|
||||
Name=$APP_NAME
|
||||
Comment=$APP_NAME
|
||||
Exec=$EXEC_COMMAND
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Icon=$icon_field
|
||||
Icon=$ICON_VALUE
|
||||
StartupNotify=true
|
||||
EOF
|
||||
|
||||
# Add mime types if provided
|
||||
if [[ -n $MIME_TYPES ]]; then
|
||||
printf 'MimeType=%s\n' "$(desktop_string_escape "$MIME_TYPES")" >>"$DESKTOP_FILE"
|
||||
echo "MimeType=$MIME_TYPES" >>"$DESKTOP_FILE"
|
||||
fi
|
||||
|
||||
chmod +x "$DESKTOP_FILE"
|
||||
|
||||
@@ -9,31 +9,14 @@ ICON_DIR="$HOME/.local/share/icons/hicolor/256x256/apps"
|
||||
OLD_ICON_DIR="$HOME/.local/share/applications/icons"
|
||||
DESKTOP_DIR="$HOME/.local/share/applications/"
|
||||
|
||||
# Always index the launchers, so removal deletes the file that was found rather
|
||||
# than a path rebuilt from the displayed name. Installs predating the name
|
||||
# validation could nest the launcher inside directories, and those are exactly
|
||||
# the ones a reconstructed path cannot reach.
|
||||
WEB_APP_PATHS=()
|
||||
while IFS= read -r -d '' file; do
|
||||
if grep -q '^Exec=.*\(omarchy-launch-webapp\|omarchy-webapp-handler\).*' "$file"; then
|
||||
WEB_APPS+=("$(basename "${file%.desktop}")")
|
||||
WEB_APP_PATHS+=("$file")
|
||||
fi
|
||||
done < <(find "$DESKTOP_DIR" -name '*.desktop' -print0 2>/dev/null)
|
||||
|
||||
# The launcher matching a chosen name, or empty when nothing was indexed under
|
||||
# it (an app removed between the scan and the pick, say).
|
||||
path_for_web_app() {
|
||||
local wanted="$1" i
|
||||
for i in "${!WEB_APPS[@]}"; do
|
||||
if [[ ${WEB_APPS[$i]} == "$wanted" ]]; then
|
||||
printf '%s\n' "${WEB_APP_PATHS[$i]}"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
if (( $# == 0 )); then
|
||||
# Find all web apps
|
||||
while IFS= read -r -d '' file; do
|
||||
if grep -q '^Exec=.*\(omarchy-launch-webapp\|omarchy-webapp-handler\).*' "$file"; then
|
||||
WEB_APPS+=("$(basename "${file%.desktop}")")
|
||||
fi
|
||||
done < <(find "$DESKTOP_DIR" -name '*.desktop' -print0)
|
||||
|
||||
if ((${#WEB_APPS[@]})); then
|
||||
mapfile -t SORTED_WEB_APPS < <(printf '%s\n' "${WEB_APPS[@]}" | sort)
|
||||
APP_NAME=$(omarchy-menu-select "Select web app to remove" "${SORTED_WEB_APPS[@]}" -- --width 520 --maxheight 520)
|
||||
@@ -51,8 +34,7 @@ if [[ -z $APP_NAME ]]; then
|
||||
fi
|
||||
|
||||
icon_name=$(printf '%s\n' "$APP_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^[:alnum:]]\+/-/g; s/^-//; s/-$//')
|
||||
desktop_file=$(path_for_web_app "$APP_NAME")
|
||||
rm -f "${desktop_file:-$DESKTOP_DIR/$APP_NAME.desktop}"
|
||||
rm -f "$DESKTOP_DIR/$APP_NAME.desktop"
|
||||
rm -f "$ICON_DIR/$icon_name.png" "$ICON_DIR/$APP_NAME.png" "$OLD_ICON_DIR/$APP_NAME.png"
|
||||
|
||||
if [[ ${OMARCHY_REMOVE_NOTIFY:-true} != "false" ]]; then
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Print the OEM Windows product key stored in firmware
|
||||
# omarchy:requires-sudo=true
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
MSDM=${OMARCHY_MSDM_PATH:-/sys/firmware/acpi/tables/MSDM}
|
||||
|
||||
if [[ ! -e $MSDM ]]; then
|
||||
echo "No Windows license key found in firmware." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
read_msdm() {
|
||||
if [[ -r $MSDM ]]; then
|
||||
"$@" "$MSDM"
|
||||
else
|
||||
sudo "$@" "$MSDM"
|
||||
fi
|
||||
}
|
||||
|
||||
extract_key() {
|
||||
grep -aoEm1 '[A-Z0-9]{5}(-[A-Z0-9]{5}){4}'
|
||||
}
|
||||
|
||||
key=""
|
||||
if omarchy-cmd-present strings; then
|
||||
key=$(read_msdm strings | extract_key) || true
|
||||
fi
|
||||
|
||||
if [[ -z $key ]]; then
|
||||
key=$(read_msdm cat | extract_key) || true
|
||||
fi
|
||||
|
||||
if [[ -z $key ]]; then
|
||||
echo "Firmware license table found, but no Windows product key could be extracted." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '%s\n' "$key"
|
||||
+124
-897
File diff suppressed because it is too large.
Load diff
@@ -1,10 +1,10 @@
|
||||
-- See https://wiki.hypr.land/Configuring/Basics/Monitors/
|
||||
-- List current monitors and supported resolutions with: hyprctl monitors all
|
||||
|
||||
-- Monitor scale is Hyprland's scale for the output. It sizes everything
|
||||
-- Wayland-native, accepts fractions (1.6, 1.75), and applies immediately.
|
||||
-- "auto" lets Hyprland pick per display.
|
||||
local omarchy_gdk_scale = 2
|
||||
local omarchy_monitor_scale = "auto"
|
||||
|
||||
hl.env("GDK_SCALE", tostring(omarchy_gdk_scale))
|
||||
hl.monitor({ output = "", mode = "preferred", position = "auto", scale = omarchy_monitor_scale })
|
||||
|
||||
-- Configure a specific monitor.
|
||||
@@ -12,11 +12,3 @@ hl.monitor({ output = "", mode = "preferred", position = "auto", scale = omarchy
|
||||
|
||||
-- Portrait/rotated secondary monitor (transform: 1 = 90°, 3 = 270°).
|
||||
-- hl.monitor({ output = "DP-2", mode = "preferred", position = "auto", scale = 1, transform = 1 })
|
||||
|
||||
-- GDK scale is GDK_SCALE, the factor GTK draws its own UI at. It's what
|
||||
-- sizes X11/XWayland windows, which Omarchy leaves unscaled so they stay
|
||||
-- crisp instead of being stretched by the compositor. GTK only honors whole
|
||||
-- numbers, so use the nearest integer to the monitor scale, and restart an
|
||||
-- app for a change to reach it.
|
||||
local omarchy_gdk_scale = 2
|
||||
hl.env("GDK_SCALE", tostring(omarchy_gdk_scale))
|
||||
@@ -10,7 +10,7 @@
|
||||
// label Visible row title.
|
||||
// action Shell command to run. If omitted, the row is a submenu.
|
||||
// target Existing submenu id to open. Use for links/aliases.
|
||||
// provider Name of a shell-defined runtime row source (e.g. "fonts").
|
||||
// provider Runtime provider function/command returning JSON rows.
|
||||
// aliases alternate `omarchy menu summon <name>` routes; also searchable.
|
||||
// description Optional subtitle and extra search text.
|
||||
// when Shell condition; hide row when it fails.
|
||||
@@ -21,8 +21,8 @@
|
||||
// "personal.notes": {"icon":"","label":"Notes","action":"omarchy-launch-editor ~/notes"},
|
||||
// "personal.files": {"icon":"","label":"Files","action":"uwsm-app -- nautilus ~/Documents"},
|
||||
//
|
||||
// provider can only reference row sources the shell already defines (the
|
||||
// providers map in Menu.qml). Static submenus only need dotted ids.
|
||||
// Only use provider when a provider_name function or command named "name"
|
||||
// returns JSON rows. Static submenus only need dotted ids.
|
||||
//
|
||||
// Example: replace the default About action by reusing the same id. Existing
|
||||
// fields are kept unless overridden.
|
||||
|
||||
@@ -87,38 +87,7 @@ ambiguous, say so rather than assembling confidence out of guesswork.
|
||||
|
||||
**Leave the system as you found it.** Diagnosis reads; it does not fix, tidy, or
|
||||
reconfigure. The one thing to clean up is your own: delete the core you extracted
|
||||
above, which is a copy of the crashed process's memory. The single change a
|
||||
diagnosis may make is the mute below, and only when the user asks for it.
|
||||
|
||||
## Offer to stop the notifications for this program
|
||||
|
||||
A crash you have explained often keeps happening anyway. Finish by offering to
|
||||
silence notifications for **that one program**, and never run it unprompted. Say
|
||||
how to lift it in the same breath, so it is not a one-way door.
|
||||
|
||||
```bash
|
||||
omarchy-crash-mute '<program>' # silence it
|
||||
omarchy-crash-mute '<program>' off # let it speak again
|
||||
omarchy-crash-mute # list what is muted
|
||||
```
|
||||
|
||||
Pass the `binary:` path from the crash facts, or the `process:` name where no
|
||||
binary was recorded; the command reduces either to the name the watcher keys on.
|
||||
A diagnosis run by hand from `omarchy agent crash <pid>` has neither, so take
|
||||
them from `coredumpctl info`. Prefer the binary: a process name is truncated to
|
||||
15 characters and a basename is not, so muting the truncated form matches
|
||||
nothing, forever, while looking like it worked.
|
||||
|
||||
Quote it. The name is whatever the crashed program's author called a file, and a
|
||||
single quote inside one closes yours and runs the rest as your shell.
|
||||
|
||||
The key is a bare name, so anything run through an interpreter is keyed as the
|
||||
interpreter: muting `python3.13` silences every Python program on the machine.
|
||||
Say so rather than quietly doing it.
|
||||
|
||||
None of this fixes anything, and a mute offered in place of a fix that was within
|
||||
reach is the wrong answer. For every program rather than one, the switch is
|
||||
_Trigger > Toggle > Crash Capture_.
|
||||
above, which is a copy of the crashed process's memory.
|
||||
|
||||
## If it is an Omarchy bug
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ validated_on="2026-07-24"
|
||||
validated_hardware="XPS 14 DA14260 (0DB9)"
|
||||
|
||||
## Measured against that reference under a dense pink-weighted multitone of 104
|
||||
## bin-aligned tones. See docs/audio-tuning.md for how to reproduce these.
|
||||
## bin-aligned tones. See docs/AUDIO-TUNING.md for how to reproduce these.
|
||||
magnitude_rms_db="1.24"
|
||||
bass_group_delay_swing_ms="13.2"
|
||||
limiter_headroom_db="1.6" ## worst-case peak on a hot master vs -1 dBFS
|
||||
|
||||
@@ -10,8 +10,6 @@ The private-use glyphs in `omarchy.ttf` are:
|
||||
- `U+E905` — Codex, from the OpenAI mark on <https://developers.openai.com/codex/>
|
||||
- `U+E906` — LM Studio, from <https://simpleicons.org/icons/lmstudio.svg>
|
||||
- `U+E907` — Ollama, from <https://simpleicons.org/icons/ollama.svg>
|
||||
- `U+E908` — T3 Code, traced from the app icon in <https://aur.archlinux.org/cgit/aur.git/plain/t3code-icon.png?h=t3code-bin>, since upstream publishes no monochrome SVG
|
||||
- `U+E909` — Ori, from <https://openrouter.ai/brand/v2/openrouter-glyph-dark.svg>, OpenRouter's own mark: Ori ships no separate logo and its product page uses this one
|
||||
|
||||
The agent marks are monochrome so the menu can render them using the active
|
||||
theme's foreground and selection colors.
|
||||
Binary file not shown.
@@ -3,12 +3,9 @@
|
||||
o.window(".*[Rr]esolve.*", {
|
||||
float = true,
|
||||
stay_focused = true,
|
||||
-- Prevent modal dialog pointer warps when focus follows the mouse.
|
||||
no_follow_mouse = true,
|
||||
tag = "-default-opacity",
|
||||
opacity = "1 1",
|
||||
})
|
||||
|
||||
o.window({ class = ".*[Rr]esolve.*", title = "^DaVinci Resolve( Studio)? - .+$" }, { fullscreen = true })
|
||||
-- Resolve exposes the Voiceover panel under the generic "Dialog" title.
|
||||
o.window({ class = ".*[Rr]esolve.*", title = "^(DaVinci Resolve( Studio)? - .+|Project Manager|Preferences|Find Directory|Dialog)$" }, { stay_focused = false })
|
||||
o.window({ class = ".*[Rr]esolve.*", title = "^(DaVinci Resolve( Studio)? - .+|Project Manager)$" }, { stay_focused = false })
|
||||
@@ -1,5 +0,0 @@
|
||||
-- Keep the Windows VM display opaque instead of applying the default window opacity.
|
||||
o.window({ class = "^xfreerdp$", title = "^Windows VM - Omarchy$" }, {
|
||||
tag = "-default-opacity",
|
||||
opacity = "1 1",
|
||||
})
|
||||
@@ -1,5 +1,4 @@
|
||||
o.bind("SUPER + W", "Close window", hl.dsp.window.close())
|
||||
o.bind("SUPER + Q", "Close window", hl.dsp.window.close())
|
||||
o.bind("CTRL + ALT + DELETE", "Close all windows", "omarchy-hyprland-window-close-all")
|
||||
|
||||
o.bind("SUPER + J", "Toggle window split", hl.dsp.layout("togglesplit"))
|
||||
@@ -27,8 +26,6 @@ end
|
||||
|
||||
o.bind("SUPER + S", "Toggle scratchpad", hl.dsp.workspace.toggle_special("scratchpad"))
|
||||
o.bind("SUPER + ALT + S", "Move window to scratchpad", hl.dsp.window.move({ workspace = "special:scratchpad", follow = false }))
|
||||
o.bind("SUPER + grave", "Toggle scratchpad", hl.dsp.workspace.toggle_special("scratchpad"))
|
||||
o.bind("SUPER + SHIFT + grave", "Move window to scratchpad", hl.dsp.window.move({ workspace = "special:scratchpad", follow = false }))
|
||||
|
||||
o.bind("SUPER + TAB", "Next workspace", hl.dsp.focus({ workspace = "e+1" }))
|
||||
o.bind("SUPER + SHIFT + TAB", "Previous workspace", hl.dsp.focus({ workspace = "e-1" }))
|
||||
|
||||
@@ -86,6 +86,7 @@ hl.animation({ leaf = "layersOut", enabled = true, speed = 1.5, bezier = "linear
|
||||
hl.animation({ leaf = "fadeLayersIn", enabled = true, speed = 1.79, bezier = "almostLinear" })
|
||||
hl.animation({ leaf = "fadeLayersOut", enabled = true, speed = 1.39, bezier = "almostLinear" })
|
||||
hl.animation({ leaf = "workspaces", enabled = false })
|
||||
hl.animation({ leaf = "specialWorkspace", enabled = true, speed = 3, bezier = "easeOutQuint", style = "slidevert" })
|
||||
|
||||
hl.config({
|
||||
dwindle = {
|
||||
|
||||
@@ -15,7 +15,6 @@ if _G.omarchy_default_bindings ~= false then
|
||||
end
|
||||
require("default.hypr.envs")
|
||||
require("default.hypr.looknfeel")
|
||||
require("default.hypr.qconsole")
|
||||
require("default.hypr.input")
|
||||
require("default.hypr.windows")
|
||||
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
-- The scratchpad, presented as a Quake console: a dimmed overlay that drops
|
||||
-- down over whatever workspace you are on. Its bindings live in
|
||||
-- bindings/tiling.lua, and its slide is animated below.
|
||||
|
||||
-- How much of the usable screen the console covers, measured from the top.
|
||||
local share = 0.5
|
||||
|
||||
-- Seed the console with the default agent the first time it opens, rather than
|
||||
-- at boot, so nothing is running until it is wanted. The exec rule has to pin
|
||||
-- the workspace itself: Hyprland only tags a spawn with the workspace it came
|
||||
-- from while misc.initial_workspace_tracking is on, and looknfeel turns it off.
|
||||
-- Omarchy ships without a default agent, and omarchy-agent exits without
|
||||
-- opening anything when none is set, so until one is picked this just opens an
|
||||
-- empty console.
|
||||
local seed = "[workspace special:scratchpad silent] omarchy-agent"
|
||||
|
||||
-- Dimming only applies while a special workspace is open, so the console gets
|
||||
-- its separation from the workspace underneath without costing anything the
|
||||
-- rest of the time.
|
||||
hl.config({
|
||||
decoration = {
|
||||
dim_special = 0.6,
|
||||
},
|
||||
})
|
||||
|
||||
-- Refitting replaces the rule in place rather than stacking a new one, but it
|
||||
-- still schedules a monitor and window state refresh, and monitor.focused fires
|
||||
-- on every hop between screens. Most of those hops do not change the number, so
|
||||
-- only write the rule when it actually moves.
|
||||
local covering = nil
|
||||
|
||||
local function cover(bottom)
|
||||
if covering == bottom then
|
||||
return
|
||||
end
|
||||
covering = bottom
|
||||
|
||||
hl.workspace_rule({
|
||||
workspace = "special:scratchpad",
|
||||
gaps_in = 0,
|
||||
gaps_out = { top = 0, right = 0, bottom = bottom, left = 0 },
|
||||
|
||||
-- Nothing to highlight in a console that is only ever focused when it is
|
||||
-- open, and the active border reads as a stray frame around a panel that
|
||||
-- is already set apart by the dimming behind it.
|
||||
no_border = true,
|
||||
|
||||
on_created_empty = seed,
|
||||
})
|
||||
end
|
||||
|
||||
-- Sizing the console with a window rule would freeze it at whatever the screen
|
||||
-- measured when it first opened, because Hyprland resolves those expressions
|
||||
-- once, as the window maps. Rescaling the monitor afterwards would leave a
|
||||
-- console that is no longer half of anything. Gaps are re-applied by the layout
|
||||
-- instead, so the console is sized by the gap left underneath it and that gap
|
||||
-- is recomputed whenever the monitor layout changes.
|
||||
local function fit()
|
||||
local monitor = hl.get_active_monitor()
|
||||
|
||||
-- A monitor handle whose output has gone away answers nil to every field, and
|
||||
-- layout changes are exactly when that happens, so this also covers reading
|
||||
-- height and reserved below.
|
||||
if not monitor or not monitor.scale or monitor.scale <= 0 then
|
||||
return
|
||||
end
|
||||
|
||||
-- Monitor dimensions are in physical pixels; gaps are logical, so the scale
|
||||
-- has to come out before the reserved area (already logical) comes off.
|
||||
local reserved = monitor.reserved
|
||||
local usable = monitor.height / monitor.scale - reserved.top - reserved.bottom
|
||||
|
||||
cover(math.max(0, math.floor(usable * (1 - share))))
|
||||
end
|
||||
|
||||
-- Until a monitor can be read, cover the whole work area rather than leaving
|
||||
-- the console unruled, so it is never seeded without its placement.
|
||||
cover(0)
|
||||
fit()
|
||||
|
||||
hl.on("monitor.layout_changed", fit)
|
||||
hl.on("monitor.focused", fit)
|
||||
|
||||
-- The direction names the edge the offset is measured from, not where the
|
||||
-- workspace goes: "slide top" drops it down into view, and "slide bottom"
|
||||
-- retracts it back up the way a Quake console does.
|
||||
hl.animation({ leaf = "specialWorkspaceIn", enabled = true, speed = 3, bezier = "easeOutQuint", style = "slide top" })
|
||||
hl.animation({ leaf = "specialWorkspaceOut", enabled = true, speed = 2, bezier = "easeInOutCubic", style = "slide bottom" })
|
||||
@@ -4,9 +4,8 @@
|
||||
// IDs are object keys. Dotted IDs imply hierarchy: trigger.share.file belongs
|
||||
// under trigger.share.
|
||||
// Kind is inferred: action -> action, target -> link, otherwise submenu.
|
||||
// Dotted IDs define the tree. provider:"name" points a submenu at one of
|
||||
// the shell's providers (the providers map in shell/plugins/menu/Menu.qml);
|
||||
// new provider names cannot be declared here. See docs/menu.md.
|
||||
// Dotted IDs define the tree. Use provider:"name" only when the submenu
|
||||
// calls provider_name() or a command named "name" to return JSON rows.
|
||||
// Optional fields:
|
||||
// aliases alternate `omarchy menu summon <name>` routes; also searchable.
|
||||
// Reserved for established names users already type; new entries
|
||||
@@ -15,10 +14,6 @@
|
||||
// title header text shown when the submenu is open; defaults to label
|
||||
// when shell condition; hide row when it fails
|
||||
// checked shell condition; append ✓ when it succeeds
|
||||
// disabled shell condition; when it succeeds the row stays listed but
|
||||
// goes dim, takes a ✓ and can no longer be selected. Install
|
||||
// rows use it so software already on the machine reads as
|
||||
// installed instead of vanishing from the list.
|
||||
|
||||
// Root Menu
|
||||
"apps": {"icon":"","label":"Apps","aliases":["app","applications"],"provider":"apps"},
|
||||
@@ -103,7 +98,7 @@
|
||||
// Style
|
||||
"style.theme": {"icon":"","label":"Theme","aliases":["theme","themes"],"action":"theme=$(omarchy-theme-switcher); [[ -n $theme ]] && omarchy-theme-set \"$theme\""},
|
||||
"style.background": {"icon":"","label":"Background","aliases":["background","wallpaper"],"action":"background=$(omarchy-theme-bg-switcher); [[ -n $background ]] && omarchy-theme-bg-set \"$background\""},
|
||||
"style.unlock": {"icon":"","label":"Unlock","aliases":["unlock"],"action":"unlock=$(omarchy-plymouth-switcher); if [[ $unlock == default ]]; then omarchy-launch-floating-terminal-with-presentation omarchy-plymouth-reset; elif [[ -n $unlock ]]; then omarchy-launch-floating-terminal-with-presentation \"omarchy-plymouth-set-by-theme $(printf %q \"$unlock\")\"; fi"},
|
||||
"style.unlock": {"icon":"","label":"Unlock","aliases":["unlock"],"action":"unlock=$(omarchy-plymouth-switcher); if [[ $unlock == default ]]; then omarchy-launch-floating-terminal-with-presentation omarchy-plymouth-reset; elif [[ -n $unlock ]]; then omarchy-launch-floating-terminal-with-presentation \"omarchy-plymouth-set-by-theme '$unlock'\"; fi"},
|
||||
"style.font": {"icon":"","label":"Font","provider":"fonts"},
|
||||
"style.bar": {"icon":"","label":"Menu Bar"},
|
||||
"style.bar.position": {"icon":"","label":"Position"},
|
||||
@@ -135,41 +130,37 @@
|
||||
"setup.network.qr": {"icon":"","label":"QR Code","aliases":["wifi-qr"],"when":"[[ $(omarchy-network-status) == wifi* ]]","action":"omarchy-shell shell summon omarchy.wifiqr"},
|
||||
"setup.default": {"icon":"","label":"Defaults","aliases":["default","defaults"]},
|
||||
"setup.default.agent": {"icon":"","label":"Agent","title":"Default Agent"},
|
||||
"setup.default.agent.agy": {"icon":"","label":"Antigravity","checked":"[[ \"$(omarchy-default-agent)\" == \"agy\" ]]","action":"omarchy-default-agent agy"},
|
||||
"setup.default.agent.claude": {"icon":"","label":"Claude","checked":"[[ \"$(omarchy-default-agent)\" == \"claude\" ]]","action":"omarchy-default-agent claude"},
|
||||
"setup.default.agent.codex": {"icon":"","iconFont":"omarchy","label":"Codex","checked":"[[ \"$(omarchy-default-agent)\" == \"codex\" ]]","action":"omarchy-default-agent codex"},
|
||||
"setup.default.agent.copilot": {"icon":"","label":"Copilot","checked":"[[ \"$(omarchy-default-agent)\" == \"copilot\" ]]","action":"omarchy-default-agent copilot"},
|
||||
"setup.default.agent.crush": {"icon":"","label":"Crush","checked":"[[ \"$(omarchy-default-agent)\" == \"crush\" ]]","action":"omarchy-default-agent crush"},
|
||||
"setup.default.agent.gemini": {"icon":"","label":"Gemini","checked":"[[ \"$(omarchy-default-agent)\" == \"gemini\" ]]","action":"omarchy-default-agent gemini"},
|
||||
"setup.default.agent.grok": {"icon":"","iconFont":"omarchy","label":"Grok","checked":"[[ \"$(omarchy-default-agent)\" == \"grok\" ]]","action":"omarchy-default-agent grok"},
|
||||
"setup.default.agent.omp": {"icon":"","iconFont":"omarchy","label":"omp","checked":"[[ \"$(omarchy-default-agent)\" == \"omp\" ]]","action":"omarchy-default-agent omp"},
|
||||
"setup.default.agent.opencode": {"icon":"","iconFont":"omarchy","label":"OpenCode","checked":"[[ \"$(omarchy-default-agent)\" == \"opencode\" ]]","action":"omarchy-default-agent opencode"},
|
||||
"setup.default.agent.ori": {"icon":"","iconFont":"omarchy","label":"Ori","checked":"[[ \"$(omarchy-default-agent)\" == \"ori\" ]]","action":"omarchy-default-agent ori"},
|
||||
"setup.default.agent.pi": {"icon":"","iconFont":"omarchy","label":"Pi","checked":"[[ \"$(omarchy-default-agent)\" == \"pi\" ]]","action":"omarchy-default-agent pi"},
|
||||
"setup.default.browser": {"icon":"","label":"Browser","title":"Default Browser"},
|
||||
"setup.default.browser.chromium": {"icon":"","label":"Chromium","checked":"[[ \"$(omarchy-default-browser)\" == \"chromium\" ]]","action":"omarchy-default-browser chromium"},
|
||||
"setup.default.browser.chrome": {"icon":"","label":"Chrome","checked":"[[ \"$(omarchy-default-browser)\" == \"chrome\" ]]","action":"omarchy-default-browser chrome"},
|
||||
"setup.default.browser.brave": {"icon":"","label":"Brave","checked":"[[ \"$(omarchy-default-browser)\" == \"brave\" ]]","action":"omarchy-default-browser brave"},
|
||||
"setup.default.browser.brave-origin": {"icon":"","label":"Brave Origin","checked":"[[ \"$(omarchy-default-browser)\" == \"brave-origin\" ]]","action":"omarchy-default-browser brave-origin"},
|
||||
"setup.default.browser.edge": {"icon":"","label":"Edge","checked":"[[ \"$(omarchy-default-browser)\" == \"edge\" ]]","action":"omarchy-default-browser edge"},
|
||||
"setup.default.browser.firefox": {"icon":"","label":"Firefox","checked":"[[ \"$(omarchy-default-browser)\" == \"firefox\" ]]","action":"omarchy-default-browser firefox"},
|
||||
"setup.default.browser.zen": {"icon":"","label":"Zen","checked":"[[ \"$(omarchy-default-browser)\" == \"zen\" ]]","action":"omarchy-default-browser zen"},
|
||||
"setup.default.browser.chromium": {"icon":"","label":"Chromium","when":"omarchy-cmd-present chromium","checked":"[[ \"$(omarchy-default-browser)\" == \"chromium\" ]]","action":"omarchy-default-browser chromium"},
|
||||
"setup.default.browser.chrome": {"icon":"","label":"Chrome","when":"omarchy-cmd-present google-chrome-stable","checked":"[[ \"$(omarchy-default-browser)\" == \"chrome\" ]]","action":"omarchy-default-browser chrome"},
|
||||
"setup.default.browser.brave": {"icon":"","label":"Brave","when":"omarchy-cmd-present brave","checked":"[[ \"$(omarchy-default-browser)\" == \"brave\" ]]","action":"omarchy-default-browser brave"},
|
||||
"setup.default.browser.brave-origin": {"icon":"","label":"Brave Origin","when":"omarchy-cmd-present brave-origin","checked":"[[ \"$(omarchy-default-browser)\" == \"brave-origin\" ]]","action":"omarchy-default-browser brave-origin"},
|
||||
"setup.default.browser.edge": {"icon":"","label":"Edge","when":"omarchy-cmd-present microsoft-edge-stable","checked":"[[ \"$(omarchy-default-browser)\" == \"edge\" ]]","action":"omarchy-default-browser edge"},
|
||||
"setup.default.browser.firefox": {"icon":"","label":"Firefox","when":"omarchy-cmd-present firefox","checked":"[[ \"$(omarchy-default-browser)\" == \"firefox\" ]]","action":"omarchy-default-browser firefox"},
|
||||
"setup.default.browser.zen": {"icon":"","label":"Zen","when":"omarchy-cmd-present zen-browser","checked":"[[ \"$(omarchy-default-browser)\" == \"zen\" ]]","action":"omarchy-default-browser zen"},
|
||||
"setup.default.terminal": {"icon":"","label":"Terminal","title":"Default Terminal"},
|
||||
"setup.default.terminal.alacritty": {"icon":"","label":"Alacritty","checked":"[[ \"$(omarchy-default-terminal)\" == \"alacritty\" ]]","action":"omarchy-default-terminal alacritty"},
|
||||
"setup.default.terminal.foot": {"icon":"","label":"Foot","checked":"[[ \"$(omarchy-default-terminal)\" == \"foot\" ]]","action":"omarchy-default-terminal foot"},
|
||||
"setup.default.terminal.ghostty": {"icon":"","label":"Ghostty","checked":"[[ \"$(omarchy-default-terminal)\" == \"ghostty\" ]]","action":"omarchy-default-terminal ghostty"},
|
||||
"setup.default.terminal.kitty": {"icon":"","label":"Kitty","checked":"[[ \"$(omarchy-default-terminal)\" == \"kitty\" ]]","action":"omarchy-default-terminal kitty"},
|
||||
"setup.default.terminal.alacritty": {"icon":"","label":"Alacritty","when":"omarchy-cmd-present alacritty","checked":"[[ \"$(omarchy-default-terminal)\" == \"alacritty\" ]]","action":"omarchy-default-terminal alacritty"},
|
||||
"setup.default.terminal.foot": {"icon":"","label":"Foot","when":"omarchy-cmd-present foot","checked":"[[ \"$(omarchy-default-terminal)\" == \"foot\" ]]","action":"omarchy-default-terminal foot"},
|
||||
"setup.default.terminal.ghostty": {"icon":"","label":"Ghostty","when":"omarchy-cmd-present ghostty","checked":"[[ \"$(omarchy-default-terminal)\" == \"ghostty\" ]]","action":"omarchy-default-terminal ghostty"},
|
||||
"setup.default.terminal.kitty": {"icon":"","label":"Kitty","when":"omarchy-cmd-present kitty","checked":"[[ \"$(omarchy-default-terminal)\" == \"kitty\" ]]","action":"omarchy-default-terminal kitty"},
|
||||
"setup.default.editor": {"icon":"","label":"Editor","title":"Default Editor"},
|
||||
"setup.default.editor.neovim": {"icon":"","label":"Neovim","checked":"[[ \"$(omarchy-default-editor)\" == \"nvim\" ]]","action":"omarchy-default-editor nvim"},
|
||||
"setup.default.editor.vscode": {"icon":"","label":"VSCode","checked":"[[ \"$(omarchy-default-editor)\" == \"code\" ]]","action":"omarchy-default-editor code"},
|
||||
"setup.default.editor.cursor": {"icon":"","label":"Cursor","checked":"[[ \"$(omarchy-default-editor)\" == \"cursor\" ]]","action":"omarchy-default-editor cursor"},
|
||||
"setup.default.editor.zed": {"icon":"","label":"Zed","checked":"[[ \"$(omarchy-default-editor)\" == \"zeditor\" ]]","action":"omarchy-default-editor zed"},
|
||||
"setup.default.editor.sublime": {"icon":"","label":"Sublime Text","checked":"[[ \"$(omarchy-default-editor)\" == \"sublime_text\" ]]","action":"omarchy-default-editor sublime_text"},
|
||||
"setup.default.editor.helix": {"icon":"","label":"Helix","checked":"[[ \"$(omarchy-default-editor)\" == \"helix\" ]]","action":"omarchy-default-editor helix"},
|
||||
"setup.default.editor.vim": {"icon":"","label":"Vim","checked":"[[ \"$(omarchy-default-editor)\" == \"vim\" ]]","action":"omarchy-default-editor vim"},
|
||||
"setup.default.editor.emacs": {"icon":"","label":"Emacs","checked":"[[ \"$(omarchy-default-editor)\" == \"emacs\" ]]","action":"omarchy-default-editor emacs"},
|
||||
"setup.region": {"icon":"","label":"Region"},
|
||||
"setup.region.world": {"icon":"","label":"World","checked":"[[ \"$(omarchy-region)\" == \"World\" ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-region world'"},
|
||||
"setup.region.china": {"icon":"","label":"China","checked":"[[ \"$(omarchy-region)\" == \"China\" ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-region china'"},
|
||||
"setup.default.editor.neovim": {"icon":"","label":"Neovim","when":"omarchy-cmd-present nvim","checked":"[[ \"$(omarchy-default-editor)\" == \"nvim\" ]]","action":"omarchy-default-editor nvim"},
|
||||
"setup.default.editor.vscode": {"icon":"","label":"VSCode","when":"omarchy-cmd-present code","checked":"[[ \"$(omarchy-default-editor)\" == \"code\" ]]","action":"omarchy-default-editor code"},
|
||||
"setup.default.editor.cursor": {"icon":"","label":"Cursor","when":"omarchy-cmd-present cursor","checked":"[[ \"$(omarchy-default-editor)\" == \"cursor\" ]]","action":"omarchy-default-editor cursor"},
|
||||
"setup.default.editor.zed": {"icon":"","label":"Zed","when":"omarchy-cmd-present zeditor","checked":"[[ \"$(omarchy-default-editor)\" == \"zeditor\" ]]","action":"omarchy-default-editor zed"},
|
||||
"setup.default.editor.sublime": {"icon":"","label":"Sublime Text","when":"omarchy-cmd-present sublime_text","checked":"[[ \"$(omarchy-default-editor)\" == \"sublime_text\" ]]","action":"omarchy-default-editor sublime_text"},
|
||||
"setup.default.editor.helix": {"icon":"","label":"Helix","when":"omarchy-cmd-present helix","checked":"[[ \"$(omarchy-default-editor)\" == \"helix\" ]]","action":"omarchy-default-editor helix"},
|
||||
"setup.default.editor.vim": {"icon":"","label":"Vim","when":"omarchy-cmd-present vim","checked":"[[ \"$(omarchy-default-editor)\" == \"vim\" ]]","action":"omarchy-default-editor vim"},
|
||||
"setup.default.editor.emacs": {"icon":"","label":"Emacs","when":"omarchy-cmd-present emacs","checked":"[[ \"$(omarchy-default-editor)\" == \"emacs\" ]]","action":"omarchy-default-editor emacs"},
|
||||
"setup.plugin": {"icon":"","label":"Plugins","aliases":["plugin","plugins"]},
|
||||
"setup.plugin.enable": {"icon":"","label":"Enable Plugin","action":"omarchy-menu-plugin enable"},
|
||||
"setup.plugin.disable": {"icon":"","label":"Disable Plugin","action":"omarchy-menu-plugin disable"},
|
||||
@@ -192,10 +183,8 @@
|
||||
// Install
|
||||
"install.package": {"icon":"","label":"Package","action":"xdg-terminal-exec --app-id=org.omarchy.terminal omarchy-pkg-install"},
|
||||
"install.aur": {"icon":"","label":"AUR","action":"xdg-terminal-exec --app-id=org.omarchy.terminal omarchy-pkg-aur-install"},
|
||||
"install.ai": {"icon":"","label":"AI"},
|
||||
"install.service": {"icon":"","label":"Service"},
|
||||
"install.development": {"icon":"","label":"Development"},
|
||||
"install.editor": {"icon":"","label":"Editor"},
|
||||
"install.webapp": {"icon":"","label":"Web App","action":"omarchy-launch-floating-terminal-with-presentation omarchy-webapp-install"},
|
||||
"install.tui": {"icon":"","label":"TUI","action":"omarchy-launch-floating-terminal-with-presentation omarchy-tui-install"},
|
||||
"install.style": {"icon":"","label":"Style"},
|
||||
"install.style.theme": {"icon":"","label":"Theme","action":"omarchy-launch-floating-terminal-with-presentation omarchy-theme-install"},
|
||||
"install.style.background": {"icon":"","label":"Background","action":"omarchy-theme-bg-install"},
|
||||
@@ -206,88 +195,89 @@
|
||||
"install.style.font.victor": {"icon":"","label":"Victor Code","action":"omarchy-install-font 'Victor Code' ttf-victor-mono-nerd 'VictorMono Nerd Font'"},
|
||||
"install.style.font.bitstream": {"icon":"","label":"Bitstream Vera Mono","action":"omarchy-install-font 'Bitstream Vera Code' ttf-bitstream-vera-mono-nerd 'BitstromWera Nerd Font'"},
|
||||
"install.style.font.iosevka": {"icon":"","label":"Iosevka","action":"omarchy-install-font Iosevka ttf-iosevka-nerd 'Iosevka Nerd Font Mono'"},
|
||||
"install.gaming": {"icon":"","label":"Gaming"},
|
||||
"install.browser": {"icon":"","label":"Browser"},
|
||||
"install.webapp": {"icon":"","label":"Web App","action":"omarchy-launch-floating-terminal-with-presentation omarchy-webapp-install"},
|
||||
"install.service": {"icon":"","label":"Service"},
|
||||
"install.development": {"icon":"","label":"Development"},
|
||||
"install.editor": {"icon":"","label":"Editor"},
|
||||
"install.terminal": {"icon":"","label":"Terminal"},
|
||||
"install.tui": {"icon":"","label":"TUI","action":"omarchy-launch-floating-terminal-with-presentation omarchy-tui-install"},
|
||||
"install.windows": {"icon":"","label":"Windows","disabled":"[[ -f $HOME/.local/share/applications/windows-vm.desktop ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-windows-vm install'"},
|
||||
"install.preinstalls": {"icon":"","label":"Preinstalls","disabled":"[[ ! -f $HOME/.local/state/omarchy/preinstalls-removed ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-preinstalls"},
|
||||
"install.browser.chrome": {"icon":"","label":"Chrome","disabled":"omarchy-pkg-present google-chrome","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-browser chrome'"},
|
||||
"install.browser.edge": {"icon":"","label":"Edge","disabled":"omarchy-pkg-present microsoft-edge-stable-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-browser edge'"},
|
||||
"install.browser.brave": {"icon":"","label":"Brave","disabled":"omarchy-pkg-present brave-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-browser brave'"},
|
||||
"install.browser.brave-origin": {"icon":"","label":"Brave Origin","disabled":"omarchy-pkg-present brave-origin-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-browser brave-origin'"},
|
||||
"install.browser.firefox": {"icon":"","label":"Firefox","disabled":"omarchy-pkg-present firefox","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-browser firefox'"},
|
||||
"install.browser.zen": {"icon":"","label":"Zen","disabled":"omarchy-pkg-present zen-browser-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-browser zen'"},
|
||||
"install.service.1password": {"icon":"","label":"1Password","disabled":"omarchy-pkg-present 1password","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-service-1password"},
|
||||
"install.service.dropbox": {"icon":"","label":"Dropbox","disabled":"omarchy-pkg-present dropbox","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-service-dropbox"},
|
||||
"install.service.spotify": {"icon":"","label":"Spotify","disabled":"omarchy-pkg-present spotify","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-service-spotify"},
|
||||
"install.service.signal": {"icon":"","label":"Signal","disabled":"omarchy-pkg-present signal-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-service-signal"},
|
||||
"install.service.tailscale": {"icon":"","label":"Tailscale","disabled":"omarchy-pkg-present tailscale","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-service-tailscale"},
|
||||
"install.service.nordvpn": {"icon":"","label":"NordVPN","disabled":"omarchy-pkg-present nordvpn-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-service-nordvpn"},
|
||||
"install.service.once": {"icon":"","label":"ONCE","disabled":"omarchy-pkg-present once-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-service-once"},
|
||||
"install.service.bitwarden": {"icon":"","label":"Bitwarden","disabled":"omarchy-pkg-present bitwarden","action":"omarchy-install-and-launch Bitwarden 'bitwarden bitwarden-cli' bitwarden"},
|
||||
"install.service.chromium-account": {"icon":"","label":"Chromium Account","when":"[[ -f ~/.config/chromium-flags.conf ]]","disabled":"grep -q oauth2-client-id ~/.config/chromium-flags.conf","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-chromium-google-account"},
|
||||
"install.editor.vscode": {"icon":"","label":"VSCode","disabled":"omarchy-pkg-present visual-studio-code-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-editor-vscode"},
|
||||
"install.editor.cursor": {"icon":"","label":"Cursor","disabled":"omarchy-pkg-present cursor-bin","action":"omarchy-install-and-launch Cursor cursor-bin cursor"},
|
||||
"install.editor.zed": {"icon":"","label":"Zed","disabled":"omarchy-pkg-present zed","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-editor-zed"},
|
||||
"install.editor.sublime": {"icon":"","label":"Sublime Text","disabled":"omarchy-pkg-present sublime-text-4","action":"omarchy-install-and-launch 'Sublime Text' sublime-text-4 sublime_text"},
|
||||
"install.editor.helix": {"icon":"","label":"Helix","disabled":"omarchy-pkg-present helix","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-editor-helix"},
|
||||
"install.editor.vim": {"icon":"","label":"Vim","disabled":"omarchy-pkg-present vim","action":"omarchy-install-app Vim vim"},
|
||||
"install.editor.emacs": {"icon":"","label":"Emacs","disabled":"omarchy-pkg-present omarchy-emacs","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-editor-emacs"},
|
||||
"install.terminal.alacritty": {"icon":"","label":"Alacritty","disabled":"omarchy-pkg-present alacritty","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-terminal alacritty'"},
|
||||
"install.terminal.foot": {"icon":"","label":"Foot","disabled":"omarchy-pkg-present foot","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-terminal foot'"},
|
||||
"install.terminal.ghostty": {"icon":"","label":"Ghostty","disabled":"omarchy-pkg-present ghostty","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-terminal ghostty'"},
|
||||
"install.terminal.kitty": {"icon":"","label":"Kitty","disabled":"omarchy-pkg-present kitty","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-terminal kitty'"},
|
||||
"install.ai.chatgpt": {"icon":"","iconFont":"omarchy","label":"ChatGPT Desktop","disabled":"omarchy-pkg-present openai-codex-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-ai-chatgpt"},
|
||||
"install.ai.dictation": {"icon":"","label":"Dictation","disabled":"omarchy-pkg-present voxtype-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-voxtype-install"},
|
||||
"install.ai.grok-bot": {"icon":"","iconFont":"omarchy","label":"Grok Bot","disabled":"omarchy-pkg-present grok-bot","action":"omarchy-install-and-launch 'Grok Bot' grok-bot grok-bot"},
|
||||
"install.ai.lm-studio": {"icon":"","iconFont":"omarchy","label":"LM Studio","disabled":"omarchy-pkg-present lmstudio-bin","action":"omarchy-install-app 'LM Studio' lmstudio-bin"},
|
||||
"install.ai.ollama": {"icon":"","iconFont":"omarchy","label":"Ollama","disabled":"omarchy-cmd-present ollama","action":"if omarchy-cmd-present nvidia-smi; then ollama_pkg=ollama-cuda; elif omarchy-cmd-present rocminfo; then ollama_pkg=ollama-rocm; else ollama_pkg=ollama; fi; omarchy-install-app Ollama \"$ollama_pkg\""},
|
||||
"install.ai.t3-code": {"icon":"","iconFont":"omarchy","label":"T3 Code","disabled":"omarchy-pkg-present t3code-bin","action":"omarchy-install-and-launch 'T3 Code' t3code-bin t3code"},
|
||||
"install.gaming.steam": {"icon":"","label":"Steam","disabled":"omarchy-pkg-present steam","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-steam"},
|
||||
"install.gaming.retroarch": {"icon":"","label":"RetroArch","disabled":"omarchy-pkg-present retroarch","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-retroarch"},
|
||||
"install.gaming.minecraft": {"icon":"","label":"Minecraft","disabled":"omarchy-pkg-present minecraft-launcher","action":"omarchy-install-and-launch Minecraft minecraft-launcher minecraft-launcher"},
|
||||
"install.gaming.geforce-now": {"icon":"","label":"NVIDIA GeForce NOW","disabled":"flatpak info com.nvidia.geforcenow","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-geforce-now"},
|
||||
"install.gaming.xbox-cloud": {"icon":"","label":"Xbox Cloud Gaming","disabled":"[[ -f \"$HOME/.local/share/applications/Xbox Cloud Gaming.desktop\" ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-xbox-cloud"},
|
||||
"install.gaming.xbox-controllers": {"icon":"","label":"Xbox Controllers","disabled":"omarchy-pkg-present xpadneo-dkms","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-xbox-controllers"},
|
||||
"install.gaming.battlenet": {"icon":"","label":"Battle.net","disabled":"[[ -d $HOME/Games/battlenet ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-battlenet"},
|
||||
"install.gaming.lutris": {"icon":"","label":"Lutris","disabled":"omarchy-pkg-present lutris","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-lutris"},
|
||||
"install.gaming.heroic": {"icon":"","label":"Heroic (Epic Games)","disabled":"omarchy-pkg-present heroic-games-launcher-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-heroic"},
|
||||
"install.browser": {"icon":"","label":"Browser"},
|
||||
"install.ai": {"icon":"","label":"AI"},
|
||||
"install.gaming": {"icon":"","label":"Gaming"},
|
||||
"install.windows": {"icon":"","label":"Windows","when":"[[ ! -f $HOME/.local/share/applications/windows-vm.desktop ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-windows-vm install'"},
|
||||
"install.preinstalls": {"icon":"","label":"Preinstalls","when":"[[ -f $HOME/.local/state/omarchy/preinstalls-removed ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-preinstalls"},
|
||||
"install.browser.chrome": {"icon":"","label":"Chrome","when":"! omarchy-pkg-present google-chrome","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-browser chrome'"},
|
||||
"install.browser.edge": {"icon":"","label":"Edge","when":"! omarchy-pkg-present microsoft-edge-stable-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-browser edge'"},
|
||||
"install.browser.brave": {"icon":"","label":"Brave","when":"! omarchy-pkg-present brave-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-browser brave'"},
|
||||
"install.browser.brave-origin": {"icon":"","label":"Brave Origin","when":"! omarchy-pkg-present brave-origin-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-browser brave-origin'"},
|
||||
"install.browser.firefox": {"icon":"","label":"Firefox","when":"! omarchy-pkg-present firefox","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-browser firefox'"},
|
||||
"install.browser.zen": {"icon":"","label":"Zen","when":"! omarchy-pkg-present zen-browser-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-browser zen'"},
|
||||
"install.service.1password": {"icon":"","label":"1Password","when":"! omarchy-pkg-present 1password","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-service-1password"},
|
||||
"install.service.dropbox": {"icon":"","label":"Dropbox","when":"! omarchy-pkg-present dropbox","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-service-dropbox"},
|
||||
"install.service.spotify": {"icon":"","label":"Spotify","when":"! omarchy-pkg-present spotify","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-service-spotify"},
|
||||
"install.service.signal": {"icon":"","label":"Signal","when":"! omarchy-pkg-present signal-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-service-signal"},
|
||||
"install.service.tailscale": {"icon":"","label":"Tailscale","when":"! omarchy-pkg-present tailscale","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-service-tailscale"},
|
||||
"install.service.nordvpn": {"icon":"","label":"NordVPN","when":"! omarchy-pkg-present nordvpn-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-service-nordvpn"},
|
||||
"install.service.once": {"icon":"","label":"ONCE","when":"! omarchy-pkg-present once-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-service-once"},
|
||||
"install.service.bitwarden": {"icon":"","label":"Bitwarden","when":"! omarchy-pkg-present bitwarden","action":"omarchy-install-and-launch Bitwarden 'bitwarden bitwarden-cli' bitwarden"},
|
||||
"install.service.chromium-account": {"icon":"","label":"Chromium Account","when":"[[ -f ~/.config/chromium-flags.conf ]] && ! grep -q oauth2-client-id ~/.config/chromium-flags.conf","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-chromium-google-account"},
|
||||
"install.editor.vscode": {"icon":"","label":"VSCode","when":"! omarchy-pkg-present visual-studio-code-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-editor-vscode"},
|
||||
"install.editor.cursor": {"icon":"","label":"Cursor","when":"! omarchy-pkg-present cursor-bin","action":"omarchy-install-and-launch Cursor cursor-bin cursor"},
|
||||
"install.editor.zed": {"icon":"","label":"Zed","when":"! omarchy-pkg-present zed","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-editor-zed"},
|
||||
"install.editor.sublime": {"icon":"","label":"Sublime Text","when":"! omarchy-pkg-present sublime-text-4","action":"omarchy-install-and-launch 'Sublime Text' sublime-text-4 sublime_text"},
|
||||
"install.editor.helix": {"icon":"","label":"Helix","when":"! omarchy-pkg-present helix","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-editor-helix"},
|
||||
"install.editor.vim": {"icon":"","label":"Vim","when":"! omarchy-pkg-present vim","action":"omarchy-install-app Vim vim"},
|
||||
"install.editor.emacs": {"icon":"","label":"Emacs","when":"! omarchy-pkg-present omarchy-emacs","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-editor-emacs"},
|
||||
"install.terminal.alacritty": {"icon":"","label":"Alacritty","when":"! omarchy-pkg-present alacritty","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-terminal alacritty'"},
|
||||
"install.terminal.foot": {"icon":"","label":"Foot","when":"! omarchy-pkg-present foot","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-terminal foot'"},
|
||||
"install.terminal.ghostty": {"icon":"","label":"Ghostty","when":"! omarchy-pkg-present ghostty","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-terminal ghostty'"},
|
||||
"install.terminal.kitty": {"icon":"","label":"Kitty","when":"! omarchy-pkg-present kitty","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-terminal kitty'"},
|
||||
"install.ai.chatgpt": {"icon":"","iconFont":"omarchy","label":"ChatGPT Desktop","when":"! omarchy-pkg-present openai-codex-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-ai-chatgpt"},
|
||||
"install.ai.dictation": {"icon":"","label":"Dictation","when":"! omarchy-pkg-present voxtype-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-voxtype-install"},
|
||||
"install.ai.grok-bot": {"icon":"","iconFont":"omarchy","label":"Grok Bot","when":"! omarchy-pkg-present grok-bot","action":"omarchy-install-and-launch 'Grok Bot' grok-bot grok-bot"},
|
||||
"install.ai.lm-studio": {"icon":"","iconFont":"omarchy","label":"LM Studio","when":"! omarchy-pkg-present lmstudio-bin","action":"omarchy-install-app 'LM Studio' lmstudio-bin"},
|
||||
"install.ai.ollama": {"icon":"","iconFont":"omarchy","label":"Ollama","when":"! omarchy-cmd-present ollama","action":"if omarchy-cmd-present nvidia-smi; then ollama_pkg=ollama-cuda; elif omarchy-cmd-present rocminfo; then ollama_pkg=ollama-rocm; else ollama_pkg=ollama; fi; omarchy-install-app Ollama \"$ollama_pkg\""},
|
||||
"install.gaming.steam": {"icon":"","label":"Steam","when":"! omarchy-pkg-present steam","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-steam"},
|
||||
"install.gaming.retroarch": {"icon":"","label":"RetroArch","when":"! omarchy-pkg-present retroarch","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-retroarch"},
|
||||
"install.gaming.minecraft": {"icon":"","label":"Minecraft","when":"! omarchy-pkg-present minecraft-launcher","action":"omarchy-install-and-launch Minecraft minecraft-launcher minecraft-launcher"},
|
||||
"install.gaming.geforce-now": {"icon":"","label":"NVIDIA GeForce NOW","when":"! flatpak info com.nvidia.geforcenow","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-geforce-now"},
|
||||
"install.gaming.xbox-cloud": {"icon":"","label":"Xbox Cloud Gaming","when":"[[ ! -f \"$HOME/.local/share/applications/Xbox Cloud Gaming.desktop\" ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-xbox-cloud"},
|
||||
"install.gaming.xbox-controllers": {"icon":"","label":"Xbox Controllers","when":"! omarchy-pkg-present xpadneo-dkms","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-xbox-controllers"},
|
||||
"install.gaming.battlenet": {"icon":"","label":"Battle.net","when":"[[ ! -d $HOME/Games/battlenet ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-battlenet"},
|
||||
"install.gaming.lutris": {"icon":"","label":"Lutris","when":"! omarchy-pkg-present lutris","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-lutris"},
|
||||
"install.gaming.heroic": {"icon":"","label":"Heroic (Epic Games)","when":"! omarchy-pkg-present heroic-games-launcher-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-heroic"},
|
||||
"install.gaming.retro-launcher": {"icon":"","label":"RetroArch Game Launcher","action":"omarchy-games-retro-install"},
|
||||
"install.development.rails": {"icon":"","label":"Ruby on Rails","disabled":"[[ -d $HOME/.local/share/mise/installs/ruby ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env ruby'"},
|
||||
"install.development.rails": {"icon":"","label":"Ruby on Rails","when":"[[ ! -d $HOME/.local/share/mise/installs/ruby ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env ruby'"},
|
||||
"install.development.docker-dbs": {"icon":"","label":"Docker DB","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-docker-dbs"},
|
||||
"install.development.javascript": {"icon":"","label":"JavaScript"},
|
||||
"install.development.go": {"icon":"","label":"Go","disabled":"[[ -d $HOME/.local/share/mise/installs/go ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env go'"},
|
||||
"install.development.go": {"icon":"","label":"Go","when":"[[ ! -d $HOME/.local/share/mise/installs/go ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env go'"},
|
||||
"install.development.php": {"icon":"","label":"PHP"},
|
||||
"install.development.python": {"icon":"","label":"Python","disabled":"[[ -d $HOME/.local/share/mise/installs/python ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env python'"},
|
||||
"install.development.python": {"icon":"","label":"Python","when":"[[ ! -d $HOME/.local/share/mise/installs/python ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env python'"},
|
||||
"install.development.elixir": {"icon":"","label":"Elixir"},
|
||||
"install.development.zig": {"icon":"","label":"Zig","disabled":"[[ -d $HOME/.local/share/mise/installs/zig ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env zig'"},
|
||||
"install.development.rust": {"icon":"","label":"Rust","disabled":"[[ -d $HOME/.rustup ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env rust'"},
|
||||
"install.development.java": {"icon":"","label":"Java","disabled":"[[ -d $HOME/.local/share/mise/installs/java ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env java'"},
|
||||
"install.development.dotnet": {"icon":"","label":".NET","disabled":"[[ -d $HOME/.local/share/mise/installs/dotnet ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env dotnet'"},
|
||||
"install.development.ocaml": {"icon":"","label":"OCaml","disabled":"[[ -d $HOME/.opam ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env ocaml'"},
|
||||
"install.development.clojure": {"icon":"","label":"Clojure","disabled":"[[ -d $HOME/.local/share/mise/installs/clojure ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env clojure'"},
|
||||
"install.development.scala": {"icon":"","label":"Scala","disabled":"[[ -d $HOME/.local/share/mise/installs/scala ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env scala'"},
|
||||
"install.development.javascript.node": {"icon":"","label":"Node.js","disabled":"[[ -d $HOME/.local/share/mise/installs/node ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env node'"},
|
||||
"install.development.javascript.bun": {"icon":"","label":"Bun","disabled":"[[ -d $HOME/.local/share/mise/installs/bun ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env bun'"},
|
||||
"install.development.javascript.deno": {"icon":"","label":"Deno","disabled":"[[ -d $HOME/.local/share/mise/installs/deno ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env deno'"},
|
||||
"install.development.php.php": {"icon":"","label":"PHP","disabled":"omarchy-pkg-present php","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env php'"},
|
||||
"install.development.php.laravel": {"icon":"","label":"Laravel","disabled":"[[ -x $HOME/.config/composer/vendor/bin/laravel ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env laravel'"},
|
||||
"install.development.php.symfony": {"icon":"","label":"Symfony","disabled":"omarchy-pkg-present symfony-cli","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env symfony'"},
|
||||
"install.development.elixir.elixir": {"icon":"","label":"Elixir","disabled":"[[ -d $HOME/.local/share/mise/installs/elixir ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env elixir'"},
|
||||
"install.development.elixir.phoenix": {"icon":"","label":"Phoenix","disabled":"compgen -G \"$HOME/.mix/archives/phx_new*\"","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env phoenix'"},
|
||||
"install.development.zig": {"icon":"","label":"Zig","when":"[[ ! -d $HOME/.local/share/mise/installs/zig ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env zig'"},
|
||||
"install.development.rust": {"icon":"","label":"Rust","when":"[[ ! -d $HOME/.rustup ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env rust'"},
|
||||
"install.development.java": {"icon":"","label":"Java","when":"[[ ! -d $HOME/.local/share/mise/installs/java ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env java'"},
|
||||
"install.development.dotnet": {"icon":"","label":".NET","when":"[[ ! -d $HOME/.local/share/mise/installs/dotnet ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env dotnet'"},
|
||||
"install.development.ocaml": {"icon":"","label":"OCaml","when":"[[ ! -d $HOME/.opam ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env ocaml'"},
|
||||
"install.development.clojure": {"icon":"","label":"Clojure","when":"[[ ! -d $HOME/.local/share/mise/installs/clojure ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env clojure'"},
|
||||
"install.development.scala": {"icon":"","label":"Scala","when":"[[ ! -d $HOME/.local/share/mise/installs/scala ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env scala'"},
|
||||
"install.development.javascript.node": {"icon":"","label":"Node.js","when":"[[ ! -d $HOME/.local/share/mise/installs/node ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env node'"},
|
||||
"install.development.javascript.bun": {"icon":"","label":"Bun","when":"[[ ! -d $HOME/.local/share/mise/installs/bun ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env bun'"},
|
||||
"install.development.javascript.deno": {"icon":"","label":"Deno","when":"[[ ! -d $HOME/.local/share/mise/installs/deno ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env deno'"},
|
||||
"install.development.php.php": {"icon":"","label":"PHP","when":"! omarchy-pkg-present php","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env php'"},
|
||||
"install.development.php.laravel": {"icon":"","label":"Laravel","when":"[[ ! -x $HOME/.config/composer/vendor/bin/laravel ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env laravel'"},
|
||||
"install.development.php.symfony": {"icon":"","label":"Symfony","when":"! omarchy-pkg-present symfony-cli","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env symfony'"},
|
||||
"install.development.elixir.elixir": {"icon":"","label":"Elixir","when":"[[ ! -d $HOME/.local/share/mise/installs/elixir ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env elixir'"},
|
||||
"install.development.elixir.phoenix": {"icon":"","label":"Phoenix","when":"! compgen -G \"$HOME/.mix/archives/phx_new*\"","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-dev-env phoenix'"},
|
||||
|
||||
// Remove
|
||||
"remove.package": {"icon":"","label":"Package","action":"xdg-terminal-exec --app-id=org.omarchy.terminal omarchy-pkg-remove"},
|
||||
"remove.ai": {"icon":"","label":"AI","title":"Remove"},
|
||||
"remove.service": {"icon":"","label":"Services","title":"Remove"},
|
||||
"remove.development": {"icon":"","label":"Development","title":"Remove"},
|
||||
"remove.theme": {"icon":"","label":"Theme","action":"omarchy-theme-remove"},
|
||||
"remove.gaming": {"icon":"","label":"Gaming","title":"Remove"},
|
||||
"remove.browser": {"icon":"","label":"Browser","title":"Remove"},
|
||||
"remove.webapp": {"icon":"","label":"Web App","when":"grep -qE '^Exec=.*(omarchy-launch-webapp|omarchy-webapp-handler)' $HOME/.local/share/applications/*.desktop","action":"omarchy-webapp-remove"},
|
||||
"remove.tui": {"icon":"","label":"TUI","when":"grep -qE '^Exec=.*(\\$TERMINAL|xdg-terminal-exec).*-e' $HOME/.local/share/applications/*.desktop","action":"omarchy-tui-remove"},
|
||||
"remove.development": {"icon":"","label":"Development","title":"Remove"},
|
||||
"remove.theme": {"icon":"","label":"Theme","action":"omarchy-theme-remove"},
|
||||
"remove.browser": {"icon":"","label":"Browser","title":"Remove"},
|
||||
"remove.dictation": {"icon":"","label":"Dictation","when":"omarchy-pkg-present voxtype-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-voxtype-remove"},
|
||||
"remove.gaming": {"icon":"","label":"Gaming","title":"Remove"},
|
||||
"remove.service": {"icon":"","label":"Services","title":"Remove"},
|
||||
"remove.windows": {"icon":"","label":"Windows","when":"[[ -f $HOME/.local/share/applications/windows-vm.desktop ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-windows-vm remove'"},
|
||||
"remove.preinstalls": {"icon":"","label":"Preinstalls","when":"[[ ! -f $HOME/.local/state/omarchy/preinstalls-removed ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-preinstalls"},
|
||||
"remove.security": {"icon":"","label":"Security","title":"Remove"},
|
||||
@@ -303,12 +293,6 @@
|
||||
"remove.browser.zen": {"icon":"","label":"Zen","when":"omarchy-pkg-present zen-browser-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-remove-browser zen'"},
|
||||
"remove.service.dropbox": {"icon":"","label":"Dropbox","when":"omarchy-pkg-present dropbox","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-service-dropbox"},
|
||||
"remove.service.tailscale": {"icon":"","label":"Tailscale","when":"omarchy-pkg-present tailscale","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-service-tailscale"},
|
||||
"remove.ai.chatgpt": {"icon":"","iconFont":"omarchy","label":"ChatGPT Desktop","when":"omarchy-pkg-present openai-codex-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-chatgpt"},
|
||||
"remove.ai.dictation": {"icon":"","label":"Dictation","when":"omarchy-pkg-present voxtype-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-voxtype-remove"},
|
||||
"remove.ai.grok-bot": {"icon":"","iconFont":"omarchy","label":"Grok Bot","when":"omarchy-pkg-present grok-bot","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-grok-bot"},
|
||||
"remove.ai.lm-studio": {"icon":"","iconFont":"omarchy","label":"LM Studio","when":"omarchy-pkg-present lmstudio-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-lm-studio"},
|
||||
"remove.ai.ollama": {"icon":"","iconFont":"omarchy","label":"Ollama","when":"omarchy-pkg-present ollama","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-ollama"},
|
||||
"remove.ai.t3-code": {"icon":"","iconFont":"omarchy","label":"T3 Code","when":"omarchy-pkg-present t3code-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-t3-code"},
|
||||
"remove.gaming.steam": {"icon":"","label":"Steam","when":"omarchy-pkg-present steam","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-gaming-steam"},
|
||||
"remove.gaming.retroarch": {"icon":"","label":"RetroArch","when":"omarchy-pkg-present retroarch","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-gaming-retroarch"},
|
||||
"remove.gaming.minecraft": {"icon":"","label":"Minecraft","when":"omarchy-pkg-present minecraft-launcher","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-gaming-minecraft"},
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
// Omarchy region managed
|
||||
// Chinese labels for the shipped menu. `omarchy region china` installs this
|
||||
// as the user menu extension; ids keep their actions and icons.
|
||||
{
|
||||
"apps": {"label":"应用"},
|
||||
"learn": {"label":"学习"},
|
||||
"trigger": {"label":"触发"},
|
||||
"style": {"label":"风格"},
|
||||
"setup": {"label":"设置"},
|
||||
"install": {"label":"安装"},
|
||||
"remove": {"label":"移除"},
|
||||
"update": {"label":"更新"},
|
||||
"about": {"label":"关于"},
|
||||
"system": {"label":"系统"},
|
||||
"system.screensaver": {"label":"屏保"},
|
||||
"system.lock": {"label":"锁屏"},
|
||||
"system.suspend": {"label":"睡眠"},
|
||||
"system.hibernate": {"label":"休眠"},
|
||||
"system.logout": {"label":"注销"},
|
||||
"system.reboot": {"label":"重启"},
|
||||
"system.shutdown": {"label":"关机"},
|
||||
"learn.keybindings": {"label":"快捷键"},
|
||||
"learn.omarchy": {"label":"Omarchy 手册"},
|
||||
"learn.community": {"label":"社区"},
|
||||
"trigger.emoji": {"label":"表情"},
|
||||
"trigger.reminder": {"label":"提醒"},
|
||||
"trigger.capture": {"label":"截取"},
|
||||
"trigger.capture.screenshot": {"label":"截图"},
|
||||
"trigger.capture.screenrecord.stop": {"label":"停止录屏"},
|
||||
"trigger.capture.screenrecord": {"label":"录屏"},
|
||||
"trigger.capture.text": {"label":"OCR 取字"},
|
||||
"trigger.capture.qr": {"label":"二维码"},
|
||||
"trigger.capture.color": {"label":"取色"},
|
||||
"trigger.capture.screenrecord.no-audio": {"label":"无音频"},
|
||||
"trigger.capture.screenrecord.desktop-audio": {"label":"含桌面音频"},
|
||||
"trigger.capture.screenrecord.microphone": {"label":"含桌面与麦克风音频"},
|
||||
"trigger.capture.screenrecord.webcam": {"label":"含桌面、麦克风与摄像头"},
|
||||
"trigger.transcode": {"label":"转码"},
|
||||
"trigger.share": {"label":"分享"},
|
||||
"trigger.toggle": {"label":"开关"},
|
||||
"trigger.hardware": {"label":"硬件"},
|
||||
"trigger.tests": {"label":"测速"},
|
||||
"trigger.hardware.laptop-display": {"label":"笔记本屏幕"},
|
||||
"trigger.hardware.mirror-display": {"label":"镜像显示"},
|
||||
"trigger.hardware.hybrid-gpu": {"label":"混合显卡"},
|
||||
"trigger.hardware.touchpad": {"label":"触控板"},
|
||||
"trigger.hardware.touchpad-haptics": {"label":"触控板振动"},
|
||||
"trigger.hardware.touchpad-haptics.low": {"label":"低"},
|
||||
"trigger.hardware.touchpad-haptics.mid": {"label":"中"},
|
||||
"trigger.hardware.touchpad-haptics.high": {"label":"高"},
|
||||
"trigger.hardware.touchscreen": {"label":"触摸屏"},
|
||||
"trigger.reminder.set": {"label":"设一个提醒"},
|
||||
"trigger.reminder.show": {"label":"查看全部"},
|
||||
"trigger.reminder.clear": {"label":"清除全部"},
|
||||
"trigger.share.clipboard": {"label":"剪贴板"},
|
||||
"trigger.share.file": {"label":"文件"},
|
||||
"trigger.share.folder": {"label":"文件夹"},
|
||||
"trigger.share.receive": {"label":"接收"},
|
||||
"trigger.toggle.idle-lock": {"label":"保持唤醒"},
|
||||
"trigger.toggle.notifications": {"label":"通知"},
|
||||
"trigger.toggle.crash-capture": {"label":"崩溃捕获"},
|
||||
"trigger.toggle.screensaver": {"label":"屏保"},
|
||||
"trigger.toggle.nightlight": {"label":"夜间模式"},
|
||||
"trigger.toggle.top-bar": {"label":"菜单栏"},
|
||||
"trigger.toggle.battery-percentage": {"label":"电量百分比"},
|
||||
"trigger.toggle.workspace-layout": {"label":"工作区布局"},
|
||||
"trigger.toggle.window-gaps": {"label":"窗口间距"},
|
||||
"trigger.toggle.one-window-ratio": {"label":"单窗口比例"},
|
||||
"trigger.tests.network-speedtest": {"label":"网络测速"},
|
||||
"trigger.tests.disk-speedtest": {"label":"磁盘测速"},
|
||||
"style.theme": {"label":"主题"},
|
||||
"style.background": {"label":"壁纸"},
|
||||
"style.unlock": {"label":"解锁画面"},
|
||||
"style.font": {"label":"字体"},
|
||||
"style.bar": {"label":"菜单栏"},
|
||||
"style.bar.position": {"label":"位置"},
|
||||
"style.bar.transparency": {"label":"透明"},
|
||||
"style.screensaver": {"label":"屏保"},
|
||||
"style.about": {"label":"关于"},
|
||||
"style.bar.position.top": {"label":"顶部"},
|
||||
"style.bar.position.bottom": {"label":"底部"},
|
||||
"style.bar.position.left": {"label":"左侧"},
|
||||
"style.bar.position.right": {"label":"右侧"},
|
||||
"style.about.text": {"label":"编辑文字"},
|
||||
"style.about.image": {"label":"取自图片"},
|
||||
"style.about.default": {"label":"恢复默认"},
|
||||
"style.screensaver.text": {"label":"编辑文字"},
|
||||
"style.screensaver.image": {"label":"取自图片"},
|
||||
"style.screensaver.default": {"label":"恢复默认"},
|
||||
"setup.monitors": {"label":"显示器"},
|
||||
"setup.keybindings": {"label":"快捷键"},
|
||||
"setup.input": {"label":"输入设备"},
|
||||
"setup.network": {"label":"网络"},
|
||||
"setup.network.dns.custom": {"label":"自定义"},
|
||||
"setup.network.qr": {"label":"二维码"},
|
||||
"setup.default": {"label":"默认程序"},
|
||||
"setup.default.agent": {"title":"默认 Agent"},
|
||||
"setup.default.browser": {"label":"浏览器","title":"默认浏览器"},
|
||||
"setup.default.terminal": {"label":"终端","title":"默认终端"},
|
||||
"setup.default.editor": {"label":"编辑器","title":"默认编辑器"},
|
||||
"setup.region": {"label":"区域"},
|
||||
"setup.region.world": {"label":"世界"},
|
||||
"setup.region.china": {"label":"中国"},
|
||||
"setup.plugin": {"label":"插件"},
|
||||
"setup.plugin.enable": {"label":"启用插件"},
|
||||
"setup.plugin.disable": {"label":"停用插件"},
|
||||
"setup.plugin.add": {"label":"添加插件"},
|
||||
"setup.plugin.clone": {"label":"克隆插件"},
|
||||
"setup.plugin.remove": {"label":"移除插件"},
|
||||
"setup.security": {"label":"安全"},
|
||||
"setup.config": {"label":"配置"},
|
||||
"setup.security.fingerprint": {"label":"指纹"},
|
||||
"setup.security.passwordless-sudo": {"label":"免密 sudo"},
|
||||
"setup.security.sudoless-docker": {"label":"免 sudo Docker"},
|
||||
"setup.direct-boot": {"label":"直接启动"},
|
||||
"setup.reset": {"label":"恢复出厂设置"},
|
||||
"install.package": {"label":"软件包"},
|
||||
"install.service": {"label":"服务"},
|
||||
"install.development": {"label":"开发环境"},
|
||||
"install.editor": {"label":"编辑器"},
|
||||
"install.style": {"label":"风格"},
|
||||
"install.style.theme": {"label":"主题"},
|
||||
"install.style.background": {"label":"壁纸"},
|
||||
"install.style.font": {"label":"字体"},
|
||||
"install.gaming": {"label":"游戏"},
|
||||
"install.browser": {"label":"浏览器"},
|
||||
"install.webapp": {"label":"网页应用"},
|
||||
"install.terminal": {"label":"终端"},
|
||||
"install.preinstalls": {"label":"预装应用"},
|
||||
"install.service.chromium-account": {"label":"Chromium 账号"},
|
||||
"install.ai.dictation": {"label":"语音听写"},
|
||||
"install.gaming.xbox-cloud": {"label":"Xbox 云游戏"},
|
||||
"install.gaming.xbox-controllers": {"label":"Xbox 手柄"},
|
||||
"install.gaming.retro-launcher": {"label":"RetroArch 启动器"},
|
||||
"install.development.docker-dbs": {"label":"Docker 数据库"},
|
||||
"remove.package": {"label":"软件包"},
|
||||
"remove.ai": {"title":"移除"},
|
||||
"remove.service": {"label":"服务","title":"移除"},
|
||||
"remove.development": {"label":"开发环境","title":"移除"},
|
||||
"remove.theme": {"label":"主题"},
|
||||
"remove.gaming": {"label":"游戏","title":"移除"},
|
||||
"remove.browser": {"label":"浏览器","title":"移除"},
|
||||
"remove.webapp": {"label":"网页应用"},
|
||||
"remove.preinstalls": {"label":"预装应用"},
|
||||
"remove.security": {"label":"安全","title":"移除"},
|
||||
"remove.security.fingerprint": {"label":"指纹"},
|
||||
"remove.security.sudoless-docker": {"label":"免 sudo Docker"},
|
||||
"remove.ai.dictation": {"label":"语音听写"},
|
||||
"remove.gaming.xbox-cloud": {"label":"Xbox 云游戏"},
|
||||
"remove.gaming.xbox-controllers": {"label":"Xbox 手柄 ()"},
|
||||
"remove.development.javascript": {"title":"移除"},
|
||||
"remove.development.php": {"title":"移除"},
|
||||
"remove.development.elixir": {"title":"移除"},
|
||||
"update.channel": {"label":"通道"},
|
||||
"update.config": {"label":"配置","title":"重置为默认"},
|
||||
"update.themes": {"label":"附加主题"},
|
||||
"update.process": {"label":"进程","title":"重启"},
|
||||
"update.hardware": {"label":"硬件","title":"重启"},
|
||||
"update.firmware": {"label":"固件"},
|
||||
"update.password": {"label":"密码"},
|
||||
"update.timezone": {"label":"时区"},
|
||||
"update.time": {"label":"时间"},
|
||||
"update.hardware.audio": {"label":"音频"},
|
||||
"update.hardware.bluetooth": {"label":"蓝牙"},
|
||||
"update.hardware.trackpad": {"label":"触控板"},
|
||||
"update.password.drive": {"label":"磁盘加密"},
|
||||
"update.password.user": {"label":"用户"},
|
||||
}
|
||||
@@ -26,6 +26,7 @@ Include = /etc/pacman.d/mirrorlist
|
||||
Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
[omarchy]
|
||||
SigLevel = Optional TrustAll
|
||||
Server = https://pkgs.omarchy.org/edge/$arch
|
||||
|
||||
# Repositories for debug symbol packages.
|
||||
|
||||
@@ -26,4 +26,5 @@ Include = /etc/pacman.d/mirrorlist
|
||||
Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
[omarchy]
|
||||
Server = https://pkgs.omarchy.org/rc/$arch
|
||||
SigLevel = Optional TrustAll
|
||||
Server = https://pkgs.omarchy.org/edge/$arch
|
||||
@@ -26,4 +26,5 @@ Include = /etc/pacman.d/mirrorlist
|
||||
Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
[omarchy]
|
||||
SigLevel = Optional TrustAll
|
||||
Server = https://pkgs.omarchy.org/stable/$arch
|
||||
@@ -1,6 +1,6 @@
|
||||
[Unit]
|
||||
Description=Omarchy speaker tuning filter-chain
|
||||
Documentation=https://github.com/basecamp/omarchy/blob/master/docs/audio-tuning.md
|
||||
Documentation=https://github.com/basecamp/omarchy/blob/master/docs/AUDIO-TUNING.md
|
||||
# WirePlumber does the linking, so starting before it is up risks the output being
|
||||
# linked before the speaker device has been discovered.
|
||||
After=pipewire.service wireplumber.service
|
||||
|
||||
@@ -12,9 +12,7 @@ default/audio/tunings/<vendor>-<model>/
|
||||
```
|
||||
|
||||
`on` renders the graph into `~/.config/pipewire/omarchy-speaker-tuning.conf.d/`
|
||||
(plus the shared host config `omarchy-speaker-tuning.conf`, from
|
||||
`default/audio/filter-chain-host.conf`) and runs it as its own PipeWire client via
|
||||
`omarchy-speaker-tuning.service`, rather than
|
||||
and runs it as its own PipeWire client via `omarchy-speaker-tuning.service`, rather than
|
||||
loading it into the audio daemon. The daemon only reads its own config at startup,
|
||||
so a daemon-loaded tuning could only be switched by restarting PipeWire — which
|
||||
drops every PulseAudio client's connection, and applications that do not reconnect
|
||||
@@ -42,13 +40,6 @@ omarchy audio tuning status # installed? in use? what matches?
|
||||
`match` and `fronted-sink` are also accepted; they exist for the install hooks
|
||||
and the sink-listing scripts rather than for daily use.
|
||||
|
||||
`on` verifies its own work: it waits for the tuning sink to appear and confirms
|
||||
its output linked to the expected physical sink, and uninstalls everything again
|
||||
on either failure — so a broken tuning cannot be left half-applied. It also
|
||||
switches the default sink to the tuning and moves existing app streams onto it
|
||||
(`off` reverses both). When everything is already installed and linked, `on` is
|
||||
a no-op; pass `--force` when iterating on a tuning in place.
|
||||
|
||||
## Adding a tuning
|
||||
|
||||
Add a directory with a `tuning.conf` and a `filter-chain.conf`. No new command is
|
||||
@@ -68,16 +59,8 @@ been validated on:
|
||||
match_sku=("0DB9" "0DBA") # XPS 14 and XPS 16
|
||||
```
|
||||
|
||||
Only the first *defined* key is consulted, in the order `match_command`,
|
||||
`match_sku`, `match_dmi` — keys below a defined one are ignored, and a tuning
|
||||
defining none never matches.
|
||||
|
||||
`sink_pattern` is required whichever method you use, since the graph's target sink
|
||||
is substituted from it. The graph must also keep two fixed properties: the sink
|
||||
node is named `omarchy_speaker_tuning` (the install checks for that exact name,
|
||||
so renaming it silently fails verification), and its output stream sets
|
||||
`node.dont-fallback` alongside `node.dont-move`, so WirePlumber cannot link it
|
||||
somewhere else when the target sink is absent at host start.
|
||||
is substituted from it.
|
||||
|
||||
Gate narrowly and widen as models are validated; a tuning aimed at the wrong
|
||||
drivers can sound worse than none and can stress them. When a tuning covers a model
|
||||
@@ -1,130 +0,0 @@
|
||||
# The Omarchy CLI router
|
||||
|
||||
`bin/omarchy` maps spaced commands onto the flat `bin/omarchy-*` namespace:
|
||||
`omarchy theme set foo` becomes `exec bin/omarchy-theme-set foo`. There is no
|
||||
registry to maintain — every executable `bin/omarchy-*` file is a command, and
|
||||
its filename is its default route. Metadata comments in the file header refine
|
||||
how it presents and routes; the keys are documented in
|
||||
[`agents/skills/command-metadata.md`](../agents/skills/command-metadata.md).
|
||||
This document covers what that guide does not: how resolution and dispatch
|
||||
actually work.
|
||||
|
||||
## How a binary becomes routes
|
||||
|
||||
The stem after `omarchy-` splits at the first hyphen: `omarchy-theme-set` gets
|
||||
group `theme` and name `set`, with remaining hyphens becoming spaces
|
||||
(`omarchy-hw-asus-rog` → group `hw`, name `asus rog`). A single-segment stem
|
||||
(`omarchy-update`) is the root command of its own group, with an empty name.
|
||||
|
||||
Every command registers two routes: the canonical route `omarchy <group>
|
||||
<name>` after metadata overrides, and the filename route with *all* hyphens
|
||||
turned to spaces. When metadata moves nothing, they are the same route. When it
|
||||
does, both keep working — `# omarchy:name=gaming xbox-cloud` on
|
||||
`omarchy-install-gaming-xbox-cloud` keeps the hyphen inside the name, so
|
||||
`omarchy install gaming xbox-cloud` is canonical while the filename route
|
||||
`omarchy install gaming xbox cloud` still resolves. An explicitly *empty* `#
|
||||
omarchy:name=` makes a command the root of its group: `omarchy-menu-share` sets
|
||||
`group=share` and an empty name, so its canonical route is `omarchy share`
|
||||
while `omarchy menu share` remains as the filename route. Alias routes register
|
||||
the same way but are flagged, so listings show them as aliases rather than
|
||||
commands.
|
||||
|
||||
Two routes claiming different binaries is a collision: the first registration
|
||||
wins, dispatch is unaffected, and the conflict is recorded for `omarchy
|
||||
commands --check` to report. Hidden commands (`# omarchy:hidden=true`) still
|
||||
register and dispatch normally — hiding only removes them from listings, which
|
||||
is how install-time plumbing like `omarchy apply hardware` stays callable
|
||||
without being browsable.
|
||||
|
||||
Metadata is read from the comment header only: the first 80 lines, stopping at
|
||||
the first non-comment line, so metadata-shaped comments after code never take
|
||||
effect. Malformed `omarchy:` lines and unknown keys are ignored rather than
|
||||
fatal — a typo degrades a command to its filename route instead of breaking the
|
||||
router. The first plain comment line doubles as a fallback summary, and a
|
||||
command with no comments at all still gets a generated one, though `--check`
|
||||
demands the explicit form (below).
|
||||
|
||||
## Dispatch
|
||||
|
||||
Resolution is longest-prefix: the router tries the full argument list as a
|
||||
route, then drops trailing words until something matches. Whatever it drops is
|
||||
passed to the binary as arguments. This runs in two passes.
|
||||
|
||||
The fast path joins argument prefixes with hyphens and checks for an
|
||||
executable file: `omarchy theme set foo` probes `omarchy-theme-set-foo`, then
|
||||
`omarchy-theme-set`, which exists — resolved without reading a single metadata
|
||||
header. This exists because plain dispatch is the hot path: parsing the
|
||||
headers of several hundred binaries on every invocation is measurable
|
||||
latency (`omarchy dev benchmark cli` tracks it), and a filename probe is a few
|
||||
stat calls. Metadata loads lazily, only for the resolved command when help is
|
||||
needed.
|
||||
|
||||
When no filename matches — metadata-moved routes like `omarchy share`, and
|
||||
aliases like `omarchy screenshot` — the router falls back to loading all
|
||||
metadata and resolving against the registered route table, with the same
|
||||
longest-prefix rule.
|
||||
|
||||
Both paths intercept `--help`/`-h` *anywhere* in the leftover arguments, not
|
||||
just the first one. Resolution can succeed with unresolved words still ahead of
|
||||
the flag — `omarchy update aur --help` resolves `update` with leftovers `aur
|
||||
--help` — and checking only the first leftover once let that invocation start a
|
||||
real update. A `--` ends the scan: everything after it belongs to the command,
|
||||
so `omarchy foo run -- --help` forwards the flag. `--json` alongside `--help`
|
||||
switches the help output to the command's JSON record; `--json` alone is just
|
||||
an argument for the command.
|
||||
|
||||
Bare invocations are also guarded. If a command declares required arguments
|
||||
(its `args` metadata, minus `[bracketed]` optional parts, is non-empty) and
|
||||
none were given, the router shows help instead of executing — `omarchy theme
|
||||
set` prints usage rather than running an interactive setter. A bare group name
|
||||
with child commands shows the group help.
|
||||
|
||||
Dispatch is `exec`: the router process is replaced, the binary sees only the
|
||||
leftover arguments, and the exit code is the binary's own. The router itself
|
||||
exits 127 for unknown routes or missing binaries.
|
||||
|
||||
When nothing resolves, the router tries a prefix listing — `omarchy hw asus`
|
||||
prints every command whose usage starts with that prefix — and otherwise
|
||||
errors with a "did you mean" suggestion (a known route extending the first
|
||||
word) and a pointer to `omarchy commands --all`.
|
||||
|
||||
## Groups and the top-level listing
|
||||
|
||||
Group help is synthesized from metadata, not written anywhere. A command
|
||||
belongs to a group when either its metadata group or its filename group
|
||||
matches, listed under the route that fits the group being viewed: `omarchy
|
||||
menu --help` shows `omarchy-menu-share` as `omarchy menu share`, while its
|
||||
canonical `omarchy share` stands alone. On the fast path, group help loads
|
||||
only that group's filename-prefixed binaries rather than everything.
|
||||
|
||||
The top-level `omarchy` listing is driven entirely by the hand-curated
|
||||
`GROUP_DESCRIPTIONS` table in `bin/omarchy`, which also titles each group's
|
||||
help. An entry there advertises the group even when every command in it is
|
||||
hidden — which is exactly why `apply` and `provision` have none: they route,
|
||||
but a listing entry would put install-time plumbing back in front of users
|
||||
(see the Command Naming section of `AGENTS.md`). Adding a browsable command
|
||||
group means adding its `GROUP_DESCRIPTIONS` entry; adding hidden plumbing
|
||||
means deliberately not doing so.
|
||||
|
||||
## Introspection
|
||||
|
||||
`omarchy commands` prints every non-hidden command with its summary, plus an
|
||||
alias table. `--all` includes hidden commands, `--markdown` emits a table, and
|
||||
`--json` emits full records: route, binary, group, name, summary, flags, args,
|
||||
examples, aliases, `filename_route`, and `routes` (the union of everything
|
||||
that resolves to the binary). Per-command JSON comes from `omarchy <route>
|
||||
--help --json`.
|
||||
|
||||
`omarchy commands --check` is the metadata lint, run by `test/cli`. It fails
|
||||
on:
|
||||
|
||||
- route collisions between binaries
|
||||
- a missing explicit `# omarchy:summary=` — a plain-comment fallback renders
|
||||
in help but does not satisfy the check
|
||||
- invalid boolean metadata: `hidden` and `requires-sudo` must be `true` or
|
||||
omitted, never `false`
|
||||
- a registered command whose binary is missing or not executable
|
||||
|
||||
When debugging a routing surprise, `omarchy <route> --help` shows the resolved
|
||||
binary and, when it differs, the filename route; `omarchy commands --all
|
||||
--json` shows every route the router knows.
|
||||
+47
-81
@@ -5,8 +5,8 @@ system.
|
||||
|
||||
## Mental model
|
||||
|
||||
Two Arch packages are built from this one repo (PKGBUILDs live in the
|
||||
separate `omarchy-pkgs` repository, under `pkgbuilds/`):
|
||||
Two Arch packages are built from this one repo (PKGBUILDs live in
|
||||
`omarchy-pkgs/pkgbuilds/`):
|
||||
|
||||
- **`omarchy`** — runtime binaries (`bin/`, including `bin/omarchy-dev-*`),
|
||||
install/finalize scripts (`install/`), migrations, themes, and the
|
||||
@@ -22,24 +22,19 @@ separate `omarchy-pkgs` repository, under `pkgbuilds/`):
|
||||
(`omarchy-debug`, `omarchy-debug-idle`, `omarchy-upload-log`) needed by
|
||||
the live ISO env.
|
||||
|
||||
Two other packages live in `omarchy-pkgs` but stand alone:
|
||||
Two other packages live in `omarchy-pkgs/` but stand alone:
|
||||
`omarchy-keyring` (GPG keys for pacman) and `omarchy-nvim` (the Neovim
|
||||
setup; independently seeds `/etc/skel`).
|
||||
|
||||
Some trees ship in neither package and exist only in the repo: `manual/`
|
||||
(user manual chapters), `agents/skills/` (contributor task guides), `docs/`,
|
||||
`test/`, and `plans/`.
|
||||
|
||||
Three layers populate `$HOME`:
|
||||
|
||||
1. **Seed** — `omarchy-settings` ships static defaults to `/etc/skel/`.
|
||||
Arch's `useradd -m` copies that tree into a new user's `$HOME` at user
|
||||
creation. This is the only mechanism that touches a brand-new user's home
|
||||
for these files.
|
||||
2. **Finalize** — `omarchy-provision-user` (routed as `omarchy finalize
|
||||
user`) runs once per user and handles the things `/etc/skel` can't do
|
||||
because they need `$HOME` expansion, the live `$OMARCHY_PATH`, or runtime
|
||||
detection of system state.
|
||||
2. **Finalize** — `omarchy-finalize-user` runs once per user and handles the
|
||||
things `/etc/skel` can't do because they need `$HOME` expansion, the live
|
||||
`$OMARCHY_PATH`, or runtime detection of system state.
|
||||
3. **Resync** — `omarchy-reinstall-configs` is the explicit, destructive
|
||||
command for an existing user to clobber their configs back to shipped
|
||||
defaults.
|
||||
@@ -47,13 +42,6 @@ Three layers populate `$HOME`:
|
||||
`/etc/skel` only fires at user creation. Existing users picking up new
|
||||
defaults must use the resync command.
|
||||
|
||||
Deferred-provisioning installs (`omarchy-apply-system --defer-provisioning`)
|
||||
create no user at all: the ISO leaves `/var/lib/omarchy/provisioning/pending`
|
||||
behind, which arms `omarchy-provision-owner.service` (shipped from
|
||||
`install/provisioning/`, alongside the factory-reset finish unit and
|
||||
`setup-form.sh`). On first boot `bin/omarchy-provision-owner` creates the
|
||||
user on tty1 and runs the finalize step itself.
|
||||
|
||||
Current generated theme state lives under
|
||||
`~/.local/state/omarchy/current/`. Keep `~/.config/omarchy/` for files a user
|
||||
may intentionally version in a dotfile manager, such as user themes, hooks,
|
||||
@@ -94,12 +82,7 @@ applications/icons/* ──► omarchy-settings /usr/share/icons/h
|
||||
|
||||
etc/** ──► omarchy-settings /etc/** (drop-ins we own outright)
|
||||
├─ mkinitcpio.conf.d/{omarchy_hooks,thunderbolt_module}.conf
|
||||
├─ limine-entry-tool.d/{omarchy-defaults,omarchy-uki}.conf
|
||||
├─ NetworkManager/, sudoers.d/, sysctl.d/, tmpfiles.d/,
|
||||
│ profile.d/omarchy.sh, … (a summary — `ls etc/` for the full ~17-entry tree)
|
||||
└─ security/faillock.conf, nsswitch.conf,
|
||||
cups/cups-browsed.conf, plymouth/plymouthd.conf /usr/share/omarchy/etc-overrides/
|
||||
→ /etc/* (post_install cp -f, see below)
|
||||
└─ limine-entry-tool.d/{omarchy-defaults,omarchy-uki}.conf
|
||||
|
||||
default/limine/limine.conf ──► omarchy-settings /usr/share/omarchy/default/limine/limine.conf
|
||||
default/limine/default.conf ──► omarchy-settings /usr/share/omarchy/default/limine/default.conf
|
||||
@@ -112,8 +95,7 @@ default/** ──► omarchy-settings /usr/share/omarchy
|
||||
│ (sourced by every shell/session entry point; see "Env bootstrap")
|
||||
├─ bashrc /usr/share/omarchy/etc-overrides/dot.bashrc
|
||||
│ → /etc/skel/.bashrc (post_install cp -f)
|
||||
├─ hypr/toggles/*.lua (flags,
|
||||
│ single-window-aspect-ratio, window-no-gaps) /etc/skel/.local/state/omarchy/toggles/hypr/
|
||||
├─ hypr/toggles/flags.lua /etc/skel/.local/state/omarchy/toggles/hypr/
|
||||
├─ nautilus-python/extensions/*.py /etc/skel/.local/share/nautilus-python/extensions/
|
||||
├─ tensaku/state.toml /etc/skel/.local/state/tensaku/state.toml
|
||||
├─ uwsm/env.d/10-omarchy /usr/share/uwsm/env.d/
|
||||
@@ -122,16 +104,18 @@ default/** ──► omarchy-settings /usr/share/omarchy
|
||||
│ + symlink /etc/fonts/conf.d/50-omarchy.conf
|
||||
├─ xdg-terminal-exec/*.list /usr/share/xdg-terminal-exec/
|
||||
├─ applications/mimeapps.list /usr/share/applications/mimeapps.list
|
||||
├─ systemd/user/*.service /usr/lib/systemd/user/
|
||||
├─ systemd/user/*.{service,path} /usr/lib/systemd/user/
|
||||
├─ systemd/user/app.slice.d/10-oomd.conf /usr/lib/systemd/user/app.slice.d/
|
||||
├─ systemd/system-sleep/{force-igpu,
|
||||
│ keyboard-backlight,unmount-fuse} /usr/lib/systemd/system-sleep/
|
||||
├─ systemd/system-sleep/unmount-fuse /usr/lib/systemd/system-sleep/
|
||||
├─ systemd/zram-generator.conf.d/90-omarchy.conf /usr/lib/systemd/zram-generator.conf.d/
|
||||
├─ fonts/omarchy/omarchy.ttf /usr/share/fonts/omarchy/
|
||||
├─ sddm/omarchy/ /usr/share/sddm/themes/omarchy/
|
||||
├─ sddm/hyprland.lua /usr/share/sddm/hyprland.lua
|
||||
├─ wayland-sessions/omarchy.desktop /usr/local/share/wayland-sessions/
|
||||
└─ plymouth/ /usr/share/plymouth/themes/omarchy/
|
||||
├─ plymouth/ /usr/share/plymouth/themes/omarchy/
|
||||
└─ security/faillock, nsswitch, cups-browsed,
|
||||
plymouthd.conf, os-release /usr/share/omarchy/etc-overrides/
|
||||
→ /etc/* (post_install cp -f, see below)
|
||||
|
||||
logo.{txt,svg}, icon.{txt,png} ──► omarchy-settings /usr/share/omarchy/ (resync source)
|
||||
/usr/share/pixmaps/omarchy.png
|
||||
@@ -142,10 +126,9 @@ logo.{txt,svg}, icon.{txt,png} ──► omarchy-settings /usr/share/omarchy
|
||||
### Why `etc-overrides/` exists
|
||||
|
||||
Some files under `/etc/` (`.bashrc` in `/etc/skel`, `nsswitch.conf`,
|
||||
`security/faillock.conf`, `cups/cups-browsed.conf`, `plymouth/plymouthd.conf`)
|
||||
are owned by upstream Arch packages, so we can't install over them via pacman
|
||||
without a file conflict. Instead their sources (under `etc/` in the repo;
|
||||
`.bashrc` from `default/bashrc`) ship at
|
||||
`security/faillock.conf`, `cups/cups-browsed.conf`, `plymouth/plymouthd.conf`,
|
||||
`os-release`) are owned by upstream Arch packages, so we can't install over
|
||||
them via pacman without a file conflict. Instead they ship at
|
||||
`/usr/share/omarchy/etc-overrides/` and the `omarchy-settings` `post_install`
|
||||
/ `post_upgrade` scriptlet `cp -f`'s them into place.
|
||||
|
||||
@@ -163,10 +146,6 @@ Single source of truth for `OMARCHY_PATH` and dev-link-aware `PATH`. It:
|
||||
- Prepends `$OMARCHY_PATH/bin` to `PATH` **only when** `OMARCHY_PATH` is
|
||||
not `/usr/share/omarchy`. On a production install the binaries are
|
||||
already on `PATH` as `/usr/bin/omarchy-*` via the `omarchy` package.
|
||||
- Appends `~/.local/share/mise/shims` and `~/.local/bin` so login shells and
|
||||
the uwsm session find mise-managed tools — kept in sync with the PAM `PATH`
|
||||
line written by `install/config/ssh-command-path.sh`, which covers SSH
|
||||
commands that run no shell setup at all.
|
||||
|
||||
Sourced by every entry point that needs the env set:
|
||||
|
||||
@@ -192,17 +171,15 @@ yet and silently runs the packaged copy of one it has. The drop-in is validated
|
||||
with `visudo -c` before install and removed by `omarchy-dev-unlink`; unlike
|
||||
`/etc/omarchy.conf`, it takes effect without a reboot.
|
||||
|
||||
## Runtime finalization (`omarchy-provision-user`)
|
||||
## Runtime finalization (`omarchy-finalize-user`)
|
||||
|
||||
Runs once per user. It does **not** copy `~/.config/**`, `~/.bashrc`,
|
||||
`flags.lua`, or the nautilus extensions — `/etc/skel` already seeded those.
|
||||
It only does the things `/etc/skel` can't:
|
||||
|
||||
- Skill symlinks `~/.{agents,claude,codex,pi/agent}/skills/<name>` →
|
||||
`$OMARCHY_PATH/default/agents/skills/<name>`, looping over every skill
|
||||
directory there (currently `omarchy` and `diagnose-crash`) so new skills
|
||||
need no edit. Symlinks (not copies) so `omarchy dev link` against a dev
|
||||
checkout repoints them correctly.
|
||||
- Skill symlinks `~/.{agents,claude,codex,pi/agent}/skills/omarchy` →
|
||||
`$OMARCHY_PATH/default/agents/skills/omarchy`. Symlinks (not copies) so
|
||||
`omarchy dev link` against a dev checkout repoints them correctly.
|
||||
- `xdg-user-dirs-update` (Templates/Public/Desktop folded back into `$HOME`)
|
||||
and `~/.config/gtk-3.0/bookmarks` (needs `$HOME` expansion).
|
||||
- Hyprland's package-owned default input reads `XKBLAYOUT` / `XKBVARIANT`
|
||||
@@ -210,23 +187,21 @@ It only does the things `/etc/skel` can't:
|
||||
- `xdg-settings set default-web-browser chromium.desktop` and
|
||||
`xdg-mime default HEY.desktop x-scheme-handler/mailto` (XDG-aware paths).
|
||||
- `omarchy-refresh-applications` (composes generated `.desktop` launchers).
|
||||
- Sources `install/user/all.sh` — theme, chromium, git, xcompose, mise,
|
||||
keyring, per-user hardware quirks (asus mic/mixer, framework f13 audio, …).
|
||||
- Sources `install/user/all.sh` — theme, git, mise, keyring, per-user
|
||||
hardware quirks (asus mic/mixer, framework f13 audio, …).
|
||||
- On `--first-install`, marks every shipped user migration as already applied
|
||||
for the freshly-created user.
|
||||
|
||||
Idempotency marker: `~/.local/state/omarchy/done/finalize-user`, managed
|
||||
by `omarchy-done`.
|
||||
|
||||
The ISO calls it as `omarchy-provision-user --force --first-install` in the
|
||||
The ISO calls it as `omarchy-finalize-user --force --first-install` in the
|
||||
target chroot as the install user, after `omarchy-apply-system` has finished
|
||||
the root-side work. `omarchy-provision-owner` makes the same call (with
|
||||
`OMARCHY_SETUP_CONTEXT=provision-owner`) when it creates the user during
|
||||
deferred first-boot provisioning.
|
||||
the root-side work.
|
||||
|
||||
## Migrations (`omarchy-migrate`)
|
||||
|
||||
See [`migrations.md`](../agents/skills/migrations.md) for the full migration model, authoring
|
||||
See [`migrations.md`](migrations.md) for the full migration model, authoring
|
||||
guidelines, and troubleshooting notes.
|
||||
|
||||
Omarchy migrations live in `migrations/*.sh` and run per-user through
|
||||
@@ -238,10 +213,9 @@ machine-wide repairs should no-op when another user already applied them.
|
||||
|
||||
Each graphical user has `omarchy-migrate-notify.service`, started once per login
|
||||
through `WantedBy=graphical-session.target` and ordered after that target so
|
||||
notification actions can safely launch through UWSM. The `omarchy-pkgs`
|
||||
PKGBUILD has shipped `omarchy-update-user-notify.service` as a symlink onto
|
||||
it, so users enabled under the old unit name keep working before they reach
|
||||
migration `1785095882`.
|
||||
notification actions can safely launch through UWSM. The package also ships
|
||||
`omarchy-update-user-notify.service` as a symlink onto it, so users enabled
|
||||
under the old unit name keep working before they reach migration `1785095882`.
|
||||
It runs `omarchy-migrate-notify` as
|
||||
that user, which checks `omarchy-migrate --pending`. If this user has missing
|
||||
migration state, it shows a notification that opens a terminal for
|
||||
@@ -260,31 +234,27 @@ transaction in the already-visible update terminal, then runs
|
||||
|
||||
## First-run (`omarchy-provision-first-run`)
|
||||
|
||||
Runs once on first interactive login, after the user manager is live. It
|
||||
first runs `omarchy-provision-user || true` so finalize catches up if it
|
||||
never ran, then handles the steps that need a running graphical session
|
||||
and/or a working user systemd instance:
|
||||
Runs once on first interactive login, after the user manager is live. Used
|
||||
for steps that need a running graphical session and/or a working user
|
||||
systemd instance:
|
||||
|
||||
- `omarchy-hook-install post-update` for the three shipped hooks
|
||||
(`install-voxtype.hook`, `setup-fingerprint.hook`, `setup-agent.hook`).
|
||||
- `install/user/first-run/enable-user-units.sh` — daemon-reload, then
|
||||
`systemctl --user enable --now` the shipped user units (`bt-agent`,
|
||||
`omarchy-sleep-lock`, `omarchy-recover-internal-monitor`,
|
||||
`omarchy-migrate-notify.service`, `omarchy-fcitx5.service`,
|
||||
`omarchy-crash-watch.service`) so they run in the first session too.
|
||||
- `omarchy-hook-install post-update install-voxtype.hook` — register the
|
||||
Voxtype post-update hook.
|
||||
- `install/user/first-run/enable-user-units.sh` — `systemctl --user enable`
|
||||
the shipped user units (`bt-agent`, `omarchy-sleep-lock`,
|
||||
`omarchy-recover-internal-monitor`, `omarchy-migrate-notify.service`,
|
||||
`omarchy-fcitx5.service`).
|
||||
Done here, not at finalize, because
|
||||
the user manager isn't reachable from the ISO chroot; `ConditionPath*`
|
||||
in the unit files keeps services inert when they don't apply.
|
||||
- `install/user/first-run/gnome-theme.sh`,
|
||||
`install/user/first-run/gtk-primary-paste.sh` — GNOME/GTK settings that
|
||||
need the dconf daemon.
|
||||
- `install/user/first-run/audio-tuning.sh` — apply speaker tuning.
|
||||
- `install/user/first-run/welcome.sh` — keybindings toast that greets the
|
||||
first login and opens the cheatsheet when clicked. The caller runs
|
||||
`omarchy-notification-wait` once before this and the Wi-Fi step, so both
|
||||
toasts land on a live notification server.
|
||||
- `install/user/first-run/wifi.sh` — Wi-Fi/update toasts (waits detached on
|
||||
`nm-online` so the update prompt only lands once there is a connection).
|
||||
first login and opens the cheatsheet when clicked.
|
||||
- `install/user/first-run/wifi.sh` — Wi-Fi/update toasts (waits for a live
|
||||
notification server before firing, then waits detached on `nm-online` so the
|
||||
update prompt only lands once there is a connection).
|
||||
|
||||
The entire sequence has one idempotency marker:
|
||||
`~/.local/state/omarchy/done/first-run-user`, managed by `omarchy-done`.
|
||||
@@ -304,8 +274,8 @@ the legacy finalization marker from `~/.local/state/omarchy/` into `done/`.
|
||||
finalization. It sources:
|
||||
|
||||
- `install/config/all.sh` — theme links, lockout limits, lockscreen PAM,
|
||||
powerprofilesctl shebang fix, SSH command path and keepalive, docker setup,
|
||||
Snapper retention, locate index tuning, service enablement, firewall.
|
||||
powerprofilesctl shebang fix, docker setup, Snapper retention, locate
|
||||
index tuning, service enablement, firewall.
|
||||
- `install/hardware/all.sh` via `omarchy-apply-hardware` — vendor- and
|
||||
device-specific kernel modules, udev rules, microcode, wireless regdom,
|
||||
ASUS / Framework / Intel / Apple / Lenovo quirks.
|
||||
@@ -315,10 +285,6 @@ finalization. It sources:
|
||||
Logging goes to `/var/log/omarchy-install.log` via
|
||||
`install/helpers/logging.sh`.
|
||||
|
||||
The package lists the ISO pacstraps live at `install/omarchy-base.packages`
|
||||
and `install/omarchy-other.packages`; the ISO builder also reads them when
|
||||
constructing its offline mirror.
|
||||
|
||||
## Explicit resync (`omarchy-reinstall-configs`)
|
||||
|
||||
When an existing user wants to reset to shipped defaults:
|
||||
@@ -344,10 +310,10 @@ return to the packaged default.
|
||||
| --- | --- |
|
||||
| Default file at `~/.config/foo/` | `config/foo/` |
|
||||
| `/etc/` drop-in we own outright | `etc/` |
|
||||
| `/etc/` file owned by an upstream package | `etc/` (see `etc/security/faillock.conf`), then add to `etc-overrides` in `omarchy-settings` PKGBUILD + scriptlet |
|
||||
| Package-owned system file (e.g. systemd user service in `/usr/lib`) | `default/`, then add the `install -Dm644` line in `omarchy-settings` PKGBUILD |
|
||||
| `/etc/` file owned by an upstream package | `default/`, then add to `etc-overrides` in `omarchy-settings` PKGBUILD + scriptlet |
|
||||
| Package-owned system file (e.g. systemd user service/path in `/usr/lib`) | `default/`, document the mapping in `default/package-defaults.tsv`, then add the `install -Dm644` line in `omarchy-settings` PKGBUILD |
|
||||
| Per-user file that's static but lives outside `~/.config` | `default/`, then add `install -Dm644 ... $pkgdir/etc/skel/...` in `omarchy-settings` PKGBUILD |
|
||||
| Runtime tweak that needs `$HOME` or live system state | extend `omarchy-provision-user`, or add a per-user leaf under `install/user/` and wire into `install/user/all.sh` |
|
||||
| Runtime tweak that needs `$HOME` or live system state | extend `omarchy-finalize-user`, or add a per-user leaf under `install/user/` and wire into `install/user/all.sh` |
|
||||
| One-time root-side setup step | `install/config/*.sh` or `install/hardware/*.sh`, wire into `install/config/all.sh` or `install/hardware/all.sh` |
|
||||
| One-time fix for existing installs | `migrations/<unix-timestamp>.sh` |
|
||||
| Package-owned path something else may already write | Prefer a path nothing else writes, such as a vendor drop-in under `/usr/lib`. Otherwise the `--overwrite` entry in `bin/omarchy-update-system-pkgs` has to ship a release before the file |
|
||||
|
||||
-167
@@ -1,167 +0,0 @@
|
||||
# The Omarchy menu
|
||||
|
||||
The menu is the `omarchy.menu` plugin of the Quickshell desktop, with its
|
||||
content defined as data in `default/omarchy/omarchy-menu.jsonc` (read at
|
||||
runtime from `$OMARCHY_PATH`) and overlaid by the user's
|
||||
`~/.config/omarchy/extensions/omarchy-menu.jsonc`. The shell parses both files
|
||||
at startup and watches them for changes, so the keybind → IPC → visible path
|
||||
never shells out to parse anything, and edits to either file take effect
|
||||
without restarting the shell. Rendering and behavior live in
|
||||
`shell/plugins/menu/Menu.qml`; the pure logic lives in
|
||||
`shell/plugins/menu/MenuModel.js`, which is plain JavaScript that Node can
|
||||
also load — the shell tests in `test/shell.d/menu-test.sh` and
|
||||
`menu-guards-test.sh` exercise it directly.
|
||||
|
||||
JSONC here means JSON plus comments and trailing commas, stripped by the
|
||||
parser rather than a real JSONC grammar: only whole-line `//` comments are
|
||||
removed, so an inline trailing comment breaks the parse. A file that fails to
|
||||
parse contributes no entries — a broken user extension silently drops every
|
||||
user entry while the shipped menu keeps working.
|
||||
|
||||
## Entry schema
|
||||
|
||||
Entries are object keys. The dotted id is the tree: `trigger.share.file` is a
|
||||
child of `trigger.share`, and an id with no dot sits on the root menu. There
|
||||
is no separate parent field to keep in sync — where an entry appears follows
|
||||
from what it is called (an explicit `parent` is accepted but nothing shipped
|
||||
uses one).
|
||||
|
||||
Kind is inferred rather than declared: an entry with `action` is an action,
|
||||
one with `target` is a link to another submenu, and anything else is a
|
||||
submenu. The fields:
|
||||
|
||||
| Field | Meaning |
|
||||
|---|---|
|
||||
| `icon` | Glyph in the icon column (usually Nerd Font) |
|
||||
| `iconFont` | Font family for the glyph when it differs from the menu font — how the private `omarchy` font's brand glyphs render |
|
||||
| `label` | Visible row title; defaults to the id |
|
||||
| `title` | Header text when the submenu is open; defaults to `label`. Lets a row read "Browser" under Defaults while the open menu says "Default Browser" |
|
||||
| `action` | Shell command to run, detached, when selected |
|
||||
| `target` | Existing submenu id to open; makes the row a link |
|
||||
| `provider` | Runtime row source for this submenu (see Providers) |
|
||||
| `aliases` | Alternate `omarchy menu summon <name>` routes; also searchable |
|
||||
| `description` | Subtitle shown while searching, and extra search text matched by whole word |
|
||||
| `when` / `checked` / `disabled` | Shell conditions (see Guards) |
|
||||
|
||||
Do not add `aliases` to new entries. They are reserved for established
|
||||
alternate names users already type (`power-menu`, `settings`), kept for
|
||||
compatibility — see `AGENTS.md`. Search does not need them: labels, the last
|
||||
id segment, and descriptions are all searchable.
|
||||
|
||||
## Load and merge
|
||||
|
||||
`mergeMenuSources` overlays user entries on the defaults per key: reusing a
|
||||
shipped id replaces only the fields you declare, so an extension can retitle
|
||||
or re-icon a row without re-declaring its action, and an overridden entry
|
||||
keeps its original position in the list. New ids append. A `root` entry is
|
||||
injected if neither file declares one.
|
||||
|
||||
The sample extension at `config/omarchy/extensions/omarchy-menu.jsonc`
|
||||
(refreshed into `~/.config/`) documents the format in its header and ships
|
||||
only comments, so the default state adds nothing.
|
||||
|
||||
## Guards
|
||||
|
||||
`when`, `checked`, and `disabled` are bash conditions. The shell never
|
||||
evaluates them on the open path: all guards in the menu are batched into a
|
||||
single bash process per (re)load and per open, reporting `<id>:<w|c|d>:<0|1>`
|
||||
lines. The menu opens immediately on the previous evaluation's answers, so
|
||||
the batch's runtime is exactly how long a row can contradict the state it
|
||||
describes — which is why the batch works hard to be fast:
|
||||
|
||||
- Package and command presence (`omarchy-pkg-present` and friends) are
|
||||
answered in-process from one `pacman -Q` snapshot instead of a fork per
|
||||
row. The snapshot resolves provides too, so gvim answers for vim.
|
||||
- Commands that several rows read a value from — every Defaults > Browser row
|
||||
compares against `$(omarchy-default-browser)` — run once, with the captured
|
||||
answer substituted into each expression. The reader list is
|
||||
`GUARD_READERS` in `MenuModel.js`; a new `$(omarchy-...)` reader used by
|
||||
more than one row must be added there, and `menu-guards-test.sh` fails the
|
||||
build if it is not.
|
||||
|
||||
The three guards differ in what failure means:
|
||||
|
||||
- `when` hides the row when it fails. A submenu whose visible descendants
|
||||
are all hidden disappears with them (provider-backed submenus stay, since
|
||||
their rows load on demand).
|
||||
- `checked` appends ✓ when it succeeds — the "this is the current choice"
|
||||
marker on defaults, DNS, channel rows.
|
||||
- `disabled` keeps the row listed but dims it, marks it ✓, and makes it
|
||||
unselectable: cursor, pointer, and Enter all step over it, and search omits
|
||||
it. The Install submenus use it so software already on the machine reads as
|
||||
installed rather than vanishing from the list it was installed from — the
|
||||
list stays a catalog of what Omarchy can install. Since a dimmed row means
|
||||
"you already have this", it earns the same ✓ as `checked` does elsewhere.
|
||||
|
||||
Install rows should therefore carry `disabled:` with the presence check, not
|
||||
`when:`; Remove rows are the opposite, hiding via `when:` what is not there
|
||||
to remove. `menu-test.sh` enforces the Install side of this convention.
|
||||
|
||||
## Providers
|
||||
|
||||
A submenu with `provider: "name"` gets its rows at runtime instead of from
|
||||
JSONC. The names are defined by the shell, not the menu file — an extension
|
||||
can point a submenu at an existing provider but cannot declare a new one:
|
||||
|
||||
- `apps` is QML-native: rows come from the shared AppLibrary (desktop
|
||||
entries), carrying image icons, launch feedback, and uninstall support like
|
||||
the launcher. App rows are searchable by their desktop Keywords but never
|
||||
routable, so an installed app cannot capture a menu route (htop ships
|
||||
`Keywords=system;...`, and SUPER+ESCAPE must still open the system menu).
|
||||
- `fonts` and `power-profiles` are bash one-liners in the `providers` map in
|
||||
`Menu.qml`. The contract is one tab-delimited line per row:
|
||||
`label\tvalue\tcurrent`. The row whose value equals `current` gets the ✓
|
||||
icon, and selection runs the spec's `actionFor(value)`. Row ids are
|
||||
`<menuId>.<slugify(value)>`, with a `-` appended on collision so two values
|
||||
that slugify alike cannot silently drop a row.
|
||||
|
||||
A provider marked `volatile` re-runs every time its submenu is entered — a
|
||||
font installed since the shell started shows up without a restart — but not
|
||||
on search keystrokes, which would restart the same enumeration per key.
|
||||
|
||||
`swapProviderRows` in `MenuModel.js` merges the results: rows carry the id of
|
||||
the submenu that produced them, so a provider that runs again drops its
|
||||
previous batch without disturbing static children declared in JSONC. Both it
|
||||
and the app merge return fresh item maps for the caller to assign in one go —
|
||||
writing into a map held by a QML `var` property occasionally loses the write,
|
||||
which used to duplicate launcher rows. Never mutate `root.items` in place.
|
||||
|
||||
Adding a provider means adding an entry to the `providers` map in `Menu.qml`
|
||||
(script, icon, `actionFor`, optionally `volatile`) and pointing a submenu at
|
||||
it with `provider:`.
|
||||
|
||||
## Driving the menu from the CLI
|
||||
|
||||
`bin/omarchy-menu` is a thin wrapper over the standard plugin IPC surface:
|
||||
|
||||
```bash
|
||||
omarchy menu # toggle the root menu
|
||||
omarchy menu toggle system # open at a route, or close if already open
|
||||
omarchy menu summon style.theme # always open (no close-if-visible)
|
||||
omarchy menu close
|
||||
omarchy menu refresh # re-parse the JSONC files
|
||||
omarchy menu ping
|
||||
```
|
||||
|
||||
A route is an item id or a declared alias, case-insensitive, with
|
||||
underscores normalized to dashes. An exact id beats any alias; empty input,
|
||||
`go`, and `menu` mean root; an unknown string falls through as a literal id
|
||||
so a misspelling still attempts to open that id. Summoning a route that
|
||||
resolves to an action — an alias for a leaf, like `screenrecord-stop` — runs
|
||||
the action directly instead of opening an action with no children, and a
|
||||
link is followed to its target. The default Hyprland bindings in
|
||||
`default/hypr/bindings/utilities.lua` all go through this surface
|
||||
(SUPER+SPACE toggles root, SUPER+ESCAPE the system menu, and so on).
|
||||
|
||||
## Select and input modes
|
||||
|
||||
The same plugin doubles as the system's dmenu. `omarchy-menu-select` and
|
||||
`omarchy-menu-input` summon it with a `mode: select` or `mode: input`
|
||||
payload, then block on a tempfile handshake: the shell writes the selection
|
||||
to `selectionFile` and touches `doneFile`, and cancellation (empty
|
||||
selection) exits 1. A select option is `label`, `glyph\tlabel`, or
|
||||
`glyph\tlabel\tsubtext` — the glyph shows but never returns, the subtext
|
||||
renders under the label, filters with it, and comes back as
|
||||
`label\tsubtext` so callers with same-named rows get a stable key. This is
|
||||
how the pickers behind menu actions (`omarchy-menu-plugin`,
|
||||
`omarchy-menu-timezone`, ...) present lists without owning any UI.
|
||||
@@ -1,7 +1,5 @@
|
||||
# Omarchy migrations
|
||||
|
||||
Read this before creating or changing migrations under `migrations/`.
|
||||
|
||||
Omarchy migrations are one-time repair scripts for existing installs. They are
|
||||
used when a package update needs to change state that pacman cannot safely own by
|
||||
itself.
|
||||
@@ -126,7 +124,6 @@ New migration format:
|
||||
- Start with an `echo` describing what the migration does.
|
||||
- Use `$OMARCHY_PATH` to reference the Omarchy directory.
|
||||
- Be idempotent. Check existing state before changing it.
|
||||
- Migrations are strictly ordered and synchronous. A migration that cannot finish must exit non-zero, remain pending, and stop the queue; never mark later migrations complete against state an earlier migration has not established.
|
||||
- Use helper commands such as `omarchy-cmd-present`, `omarchy-cmd-missing`,
|
||||
`omarchy-pkg-add`, `omarchy-pkg-drop`, `omarchy-pkg-present`, and
|
||||
`omarchy-pkg-missing` when appropriate.
|
||||
@@ -166,5 +163,3 @@ omarchy-migrate
|
||||
Omarchy 4.0 is upgraded through `bin/omarchy-upgrade-to-quattro`, not through the
|
||||
normal migration runner. Do not add compatibility migrations for old installer
|
||||
layouts; put pre-4 package-layout transition work in the upgrade command instead.
|
||||
|
||||
Clearing a privileged file that a retired installer left on disk is the exception, and belongs in a migration whether or not that installer was part of a package layout transition. The upgrade command only runs on a machine still making the 3 to 4 crossing, so anything put there never reaches an install that crossed already, and it never runs at all for an installer that was retired on its own — while the file the installer wrote is still sitting on those machines. The upgrade command finishes by running `omarchy-migrate` (`run_post_upgrade_migrations`), so one migration reaches every population; a copy in the upgrade command would only be a second copy of the same predicate to keep correct. Such a migration must name the defect it clears and match what the old installer actually produced before deleting it. Leave safe administrator-authored files alone; if one still contains the vulnerable privileged action, preserve it under an inactive name rather than discarding custom content or leaving the action executable. A user config that depends on the same retired compatibility path may be repaired in that migration when doing so eliminates an overlapping migration, but only by matching and replacing the exact legacy path while preserving the rest of the file.
|
||||
@@ -1,199 +0,0 @@
|
||||
# Notifications
|
||||
|
||||
The shell is the notification daemon: `shell/plugins/notifications/Service.qml`
|
||||
hosts a Quickshell `NotificationServer` that claims `org.freedesktop.Notifications`
|
||||
on the session bus. There is no dunst or mako — anything that speaks the
|
||||
freedesktop notification protocol (notify-send, libnotify apps, Chromium web
|
||||
apps) lands in the shell, which renders it as a toast card stacked in the
|
||||
top-right corner. The pure decision logic lives in `NotificationLogic.js`,
|
||||
which is also loadable from Node so `test/shell.d/` can exercise it without
|
||||
a compositor.
|
||||
|
||||
The end-user view (hotkey notices for time, battery, weather) is in
|
||||
`manual/10-notices.md`; this document is the system shape behind it.
|
||||
|
||||
## Toast lifecycle
|
||||
|
||||
A toast lives on screen for at least 5s (low), 8s (normal), or forever
|
||||
(critical), stretched up to 30s if the sender asked for a longer
|
||||
`expire_timeout`. Hovering pauses the countdown, and a content update restarts
|
||||
it — new text deserves a full look. Left-click invokes the default action,
|
||||
right-click or the hover-revealed close button dismisses.
|
||||
|
||||
Every on-screen popup is mirrored to its own file under
|
||||
`~/.local/state/omarchy/notifications/` (one JSON line per file, named
|
||||
`<timestamp>-<id>.json`), so live toasts survive the shell restart that
|
||||
`omarchy-update` performs. When a toast leaves the screen — expiry, dismissal,
|
||||
or click — its file moves into `notifications/history/`, trimmed to the newest
|
||||
ten. That directory *is* the history: `showHistory` replays exactly what has
|
||||
been moved in there. Referenced avatars/images are copied into
|
||||
`notifications/images/`, because senders delete their originals on close.
|
||||
|
||||
`replaces_id` updates never produce a second notification signal: the server
|
||||
writes new content onto the object the service already holds, so the service
|
||||
watches the object's property-change signals and rewrites the row and its file
|
||||
in place, under the popup's original file identity. Restored rows carry ids
|
||||
from a dead server generation (ids restart from 1 each shell process), so
|
||||
they are keyed by timestamp+id and never matched against live objects — a
|
||||
fresh notification reusing an old id must not dismiss or replace them.
|
||||
|
||||
## Silencing
|
||||
|
||||
Do-not-disturb is a single boolean, persisted as the `dnd` key in
|
||||
`~/.local/state/omarchy/notifications.json` and toggled via shell IPC
|
||||
(`omarchy-shell notifications toggleDnd` / `setDnd` / `dndState`).
|
||||
`omarchy-toggle-notification-silencing` wraps the toggle and refreshes the
|
||||
bar's `omarchy.indicators` widget, whose Dnd indicator binds directly to the
|
||||
service's `doNotDisturb` property.
|
||||
|
||||
Two kinds of notification punch through DND, chosen to be intentional and
|
||||
rare:
|
||||
|
||||
- `app_name` = `omarchy-action` — Omarchy's own user-action confirmation
|
||||
toasts ("Theme changed"). The user just did something; their feedback shows.
|
||||
- urgency critical *and* `app_name` = `notify-send` — bare-CLI emergency
|
||||
alerts. Critical alone is not enough, because chat apps abuse it to force
|
||||
visibility; they set `app_name` to their brand, which fails this rule.
|
||||
|
||||
A silenced notification that anyone might look back at is written straight
|
||||
into history — "what did I miss while silenced" is what history is for.
|
||||
Ephemeral ones (the freedesktop `transient` hint, or an `app_name` of
|
||||
`notify-send`/`omarchy-action`) are dropped entirely.
|
||||
|
||||
## The sender contract
|
||||
|
||||
`bin/omarchy-notification-send` is the one way Omarchy code sends
|
||||
notifications — never raw `notify-send`. It calls
|
||||
`org.freedesktop.Notifications.Notify` directly over the session bus (via
|
||||
`busctl --user`), so each value is one typed D-Bus parameter and there is no
|
||||
argv layer that could reinterpret a relayed headline as an option or a hint. Its
|
||||
flags map onto that call:
|
||||
|
||||
| Flag | Becomes | Meaning |
|
||||
|---|---|---|
|
||||
| `-g` / `--glyph` | hint `omarchy-glyph` | Nerd Font glyph for the icon slot when no image icon resolves |
|
||||
| `--exec <program> [args…]` | hint `omarchy-exec-argv` | the click command; consumes the rest of the line, so it comes last. Each word is a discrete argument the shell runs without re-parsing (see below) |
|
||||
| `--image` | hint `image-path` | the standard freedesktop image hint |
|
||||
| `-i` / `--icon` | `app_icon` | themed icon name for the toast |
|
||||
| `--app-name` | `app_name` | defaults to `omarchy-action` |
|
||||
| `-u` / `--urgency` | hint `urgency` (byte) | `low`/`normal`/`critical`; defaults to `low` |
|
||||
| `-t` / `--expire-time` | `expire_timeout` | milliseconds on screen; server default otherwise |
|
||||
|
||||
Unknown flags are a hard error, not a silent pass-through: `--exec` is the only
|
||||
door to a click command, and there is no generic option pass-through to smuggle
|
||||
one through.
|
||||
|
||||
The defaults are the point: an unadorned `omarchy-notification-send "Done"`
|
||||
is a low-urgency user-action toast that pops through DND and is treated as
|
||||
ephemeral noise when silenced.
|
||||
|
||||
The click command is deliberately not a libnotify action. An action keeps the
|
||||
sender blocked waiting for `ActionInvoked`, and dies unanswered whenever the
|
||||
shell restarts underneath it — the installer toasts restart the shell as their
|
||||
first act. Carrying the command as a hint means the shell executes the click
|
||||
itself (detached, so the command outlives the shell process) from the copy it
|
||||
keeps with the popup, which the persistence files preserve: a restored toast
|
||||
clicks through exactly like a live one, and oneshot senders can exit
|
||||
immediately. For third-party clients the click falls back to the libnotify
|
||||
`default` action while the sender is alive, then to focusing the sender's
|
||||
window by class via `omarchy-hyprland-focus-app` — chat apps rarely register
|
||||
an action and just expect click-to-jump.
|
||||
|
||||
### Click commands are argv, never shell strings
|
||||
|
||||
`--exec` consumes the rest of the line as the click command:
|
||||
|
||||
```bash
|
||||
omarchy-notification-send "Download complete" "$title" --exec mpv -- "$file"
|
||||
```
|
||||
|
||||
The caller's shell has already split those words into discrete arguments, and a
|
||||
quoted argument (`"$file"`) stays one argument even with spaces. On the shell
|
||||
side they are run through `Util.execArgv`, which invokes `bash -lc 'exec "$@"'`
|
||||
with the arguments as **positional parameters** — never interpolated into the
|
||||
script text. bash expands `"$@"` without re-tokenizing or re-evaluating it, so a
|
||||
value carrying data an attacker controls — a downloaded video's title, a
|
||||
received filename, a crashed process's name — is only ever a single argument and
|
||||
can never be reparsed as a command. The login shell keeps the PATH and session
|
||||
environment that GUI click targets (the screenshot editor, mpv, xdg-open) expect.
|
||||
|
||||
The critical rule: **the splitting must happen at the call site, not inside the
|
||||
tool.** Passing a single quoted string (`--exec "mpv $title"`) and letting the
|
||||
tool whitespace-split it would hand argument boundaries to whoever controls the
|
||||
string's content — a title with a space could inject an extra option or program.
|
||||
So `--exec` refuses a lone quoted-string argument and points at the unquoted
|
||||
form. There is no "take a command string and sanitize it" path; that is the
|
||||
escaping trap (string-concatenated SQL) the yt-dlp title RCE exploited.
|
||||
|
||||
The shell fails closed on a malformed argv hint (it must be a JSON array of
|
||||
strings whose program is present and not a leading-dash option) rather than
|
||||
running anything it can't validate. A caller can still deliberately name a shell
|
||||
as the program (`--exec sh -c …`), but that runs code because the *developer*
|
||||
wrote it, not because attacker data became a command — a reviewable red flag
|
||||
(greppable as `--exec sh`/`--exec bash`), not an injection. Insulating against a
|
||||
native same-user process is out of scope: it already runs with your privileges
|
||||
and needs no notification to execute code. What is fully closed is untrusted
|
||||
*content* — web notifications can't set the exec hint at all, and any relayed
|
||||
title/filename is confined to inert argument data.
|
||||
|
||||
The sender keeps that last part true structurally rather than leaving it to each
|
||||
caller. Because it calls `Notify` directly, the headline and description are
|
||||
typed string parameters — a relayed value like `--hint=…` or `-rf` is the
|
||||
summary or body, never an option or a hint, and there is no argv/option layer
|
||||
(no `notify-send`) left to reinterpret it. `--exec` is the only thing that can
|
||||
build the `omarchy-exec-argv` hint. (The leading `--` on the `busctl` call is a
|
||||
belt for `busctl`'s own getopt, which would otherwise read a dash-leading value
|
||||
as a `busctl` option; the summary/body themselves are never parsed as options.)
|
||||
|
||||
## Helper commands
|
||||
|
||||
- `omarchy-notification-wait [timeout]` — polls until the shell answers IPC
|
||||
*and* has claimed the bus name. Anything sending near session start or a
|
||||
shell restart uses it, or the toast is sent into the void.
|
||||
- `omarchy-notification-dismiss <summary>` — dismiss by summary substring,
|
||||
used by the first-run toasts once their action has been clicked.
|
||||
- `omarchy-notification-time` / `-battery` — the hotkey notices: one-line
|
||||
low-urgency glyph toasts wrapping `date` and `omarchy-battery-status`.
|
||||
- `omarchy-notification-weather` — despite the name, not a sender: it toggles
|
||||
the `omarchy.weather` shell panel.
|
||||
|
||||
Keybindings live in `default/hypr/bindings/utilities.lua`: `Super+comma`
|
||||
variants map to the IPC methods `dismissOne`, `dismissAll`, `invokeLast`,
|
||||
`showHistory`, and the silencing toggle.
|
||||
|
||||
## How subsystems plug in
|
||||
|
||||
Everything goes through the same sender contract, so the pieces are small:
|
||||
|
||||
- **Low battery** — `omarchy-battery-low` sends a critical toast and runs the
|
||||
`battery-low` hook.
|
||||
- **Crash capture** — `omarchy-crash-watch` follows the systemd-coredump
|
||||
journal stream and announces each crashed program (deduped per minute) as a
|
||||
critical toast whose click runs `omarchy-agent-crash` (via `--exec`, so a
|
||||
hostile process name stays a discrete argument). It waits for the
|
||||
server first: a shell crash takes the notification server down with it, and
|
||||
that crash is the one most worth reporting.
|
||||
- **Pending migrations** — `omarchy-migrate-notify` (from its user service
|
||||
after `graphical-session.target`) waits for the server, then sends a
|
||||
critical toast whose click opens a terminal running `omarchy-migrate`,
|
||||
falling back to printing in the terminal if the hand-off fails.
|
||||
|
||||
## Reminders
|
||||
|
||||
Reminders ride on notifications rather than being their own daemon.
|
||||
`bin/omarchy-reminder <minutes> [message]` creates a transient systemd user
|
||||
timer via `systemd-run --user --collect --on-active=<minutes>m` under the
|
||||
unit name `omarchy-reminder-<minutes>m-<epoch>`; the timer's payload sends the
|
||||
reminder toast, deletes its message file, and refreshes the bar indicator.
|
||||
Custom messages are stashed in `$XDG_RUNTIME_DIR/omarchy-reminders/<unit>.message`
|
||||
since a unit name cannot carry arbitrary text. `--collect` means fired timers
|
||||
leave nothing behind.
|
||||
|
||||
The state therefore lives entirely in systemd: `show` and `clear` enumerate
|
||||
`systemctl --user list-timers "omarchy-reminder-*.timer"` — `show` as a
|
||||
summary toast, `show --json` as the JSON the bar's Reminder indicator polls
|
||||
(refreshed by the same `omarchy-shell -q omarchy.indicators refresh` call the
|
||||
timers and mutations make). `omarchy-reminder -i` summons the
|
||||
`omarchy.reminders` overlay (`shell/plugins/reminders/ReminderFlow.qml`), a
|
||||
two-step minutes/message prompt that shells back out to `omarchy-reminder` to
|
||||
do the setting.
|
||||
+23
-51
@@ -2,12 +2,9 @@
|
||||
|
||||
A single long-running [Quickshell](https://quickshell.org/) instance
|
||||
that hosts the Omarchy desktop. The bar, panels, overlays, menus, and
|
||||
services all run inside as plugins. Hyprland autostart launches the
|
||||
shell via `omarchy-launch-shell`; restart it with `omarchy-restart-shell`.
|
||||
IPC is the canonical way for CLIs to talk to a running shell —
|
||||
`omarchy-shell` forwards a call and fails when the shell is not running
|
||||
(`-q` makes it quiet best-effort; `OMARCHY_SHELL_IPC_TIMEOUT` bounds the
|
||||
wait).
|
||||
services all run inside as plugins. IPC is the canonical way for CLIs
|
||||
to talk to a running shell — `omarchy-shell-ipc` auto-starts it on
|
||||
first call.
|
||||
|
||||
## Plugin manifest
|
||||
|
||||
@@ -37,14 +34,9 @@ wait).
|
||||
|
||||
Only one full bar option is active at a time. The built-in `omarchy.bar` is
|
||||
used when `bar.id` is omitted or when a selected third-party bar cannot load.
|
||||
Panels, overlays, and menus are loaded when summoned. Plugins can set the
|
||||
top-level manifest key `keepLoaded: true` to survive between summons.
|
||||
First-party services are loaded at startup.
|
||||
|
||||
Entry points are QML `Item`s. Panel, overlay, and menu entry points expose
|
||||
`open(payloadJson)` and `close()` for summon/hide; on load the host injects
|
||||
`omarchyPath`, `shell`, `manifest`, and the registries (`pluginRegistry` /
|
||||
`barWidgetRegistry`) as properties.
|
||||
Panels, overlays, and menus are loaded when summoned. Plugins can set
|
||||
`keepLoaded: true` to survive between summons. First-party services are
|
||||
loaded at startup.
|
||||
|
||||
Full schema: [`shell/services/PluginRegistry.qml`](../shell/services/PluginRegistry.qml).
|
||||
|
||||
@@ -84,25 +76,20 @@ widgets that omit it default to `center`.
|
||||
Plugins run as **unsandboxed code** inside `omarchy-shell`. Adding warns you
|
||||
before cloning, plugins land disabled so you can review the code before
|
||||
`omarchy plugin enable`, and updates show a diff before touching anything.
|
||||
Commands confirm in a terminal even when given arguments; without one they
|
||||
refuse rather than guess. Add `--yes` to skip every prompt (the path for
|
||||
scripts and agents).
|
||||
Commands prompt when run bare in a terminal and run unattended when given
|
||||
arguments — add `--yes` to skip every prompt (the path for scripts and agents).
|
||||
|
||||
You can still install by hand: drop a plugin into
|
||||
`~/.config/omarchy/plugins/<id>/`, run `omarchy-shell shell rescanPlugins`, then
|
||||
`omarchy plugin enable <id>`. A bar widget starts in its declared default
|
||||
section; enabling a full bar replaces the one in use. `omarchy bar` drives the
|
||||
bar from the CLI — `use | reset | defaults | position | transparent | put |
|
||||
move | set`, with placement flags such as `--section` and `--index`.
|
||||
section and can be moved with `omarchy bar move`; enabling a full bar
|
||||
replaces the one in use.
|
||||
The lower-level IPC methods remain available through `omarchy-shell shell ...`.
|
||||
|
||||
## IPC
|
||||
|
||||
The shell exposes a `shell` target (the host also registers
|
||||
`image-selector`) plus targets registered by individual plugins, named
|
||||
for the plugin rather than for where it appears: `background`, `osd`,
|
||||
`media`, `notifications`, and per-widget targets such as `omarchy.clock`
|
||||
or `omarchy.power`. There is no `bar` target.
|
||||
The shell exposes a `shell` target plus extra targets registered by
|
||||
individual plugins (`bar`, `image-selector`, …).
|
||||
|
||||
| Method | Effect |
|
||||
|---------------------------------------|---------------------------------|
|
||||
@@ -110,24 +97,17 @@ or `omarchy.power`. There is no `bar` target.
|
||||
| `summon <id> <payloadJson>` | load + open a plugin |
|
||||
| `hide <id>` | close a previously-summoned |
|
||||
| `toggle <id> <payloadJson>` | summon if closed, hide if open |
|
||||
| `togglePanelAt <section> <index>` | toggle the panel at a bar position |
|
||||
| `call <id> <method> <arg>` | call an already-loaded plugin |
|
||||
| `rescanPlugins` | re-walk plugin dirs and hot-reload plugin code |
|
||||
| `reloadConfig` | reload shell.json |
|
||||
| `applyTheme <colorsB64> <shellB64>` | push theme colors + shell.toml |
|
||||
| `toggleBarTransparency` | flip the bar background between solid and transparent |
|
||||
| `setPluginEnabled <id> <"true"\|…>` | flip enabled bit (`ok` / `unknown`) |
|
||||
| `enablePlugin <id> <placementJson>` | enable and place in one mutation |
|
||||
| `putBarWidget <id> <placementJson>` | place a widget only where absent (`omarchy bar put`) |
|
||||
| `moveBarWidget <id> <placementJson>` | move a configured widget |
|
||||
| `setBarWidget <id> <key> <valueJson> <selectorJson>` | set an inline widget option |
|
||||
| `listPlugins` | JSON of every discovered plugin |
|
||||
| `listShellConfig` | effective shell.json as JSON |
|
||||
| `debugBarGeometry` | bar geometry dump for debugging |
|
||||
|
||||
`setPluginEnabled` takes a string; only literal `"true"` enables. Methods
|
||||
answer on stdout with exit 0 — `ok` on success, `unknown` or an error
|
||||
string on a miss.
|
||||
`setPluginEnabled` takes a string; only literal `"true"` enables.
|
||||
|
||||
## shell.json
|
||||
|
||||
@@ -142,7 +122,8 @@ string on a miss.
|
||||
"id": "omarchy.bar",
|
||||
"position": "top",
|
||||
"transparent": false,
|
||||
"centerAnchor": "omarchy.clock",
|
||||
"centerAnchor": "calendar",
|
||||
"fontFamily": "JetBrainsMono Nerd Font",
|
||||
"layout": {
|
||||
"left": [ { "id": "omarchy.menu" } ],
|
||||
"center": [ { "id": "omarchy.clock", "format": "HH:mm" } ],
|
||||
@@ -165,9 +146,10 @@ Rules:
|
||||
3. Settings are inline on the entry. No `config:` sub-object, no
|
||||
merge layers.
|
||||
4. Built-in bar widget ids are namespaced (`omarchy.clock`, `omarchy.audio`, …).
|
||||
The migration rewrites older ids such as `Clock` and `AudioPanel` forward.
|
||||
5. Third-party enabled ⇔ present; for full bar options that means `bar.id`.
|
||||
First-party non-bar plugins are enabled unless listed in `disabledPlugins[]`.
|
||||
6. `barWidget.allowMultiple: true` in the manifest permits multiple instances.
|
||||
6. `allowMultiple: true` in the manifest permits multiple instances.
|
||||
7. `idle.screensaver` and `idle.lock` are seconds since user idle began.
|
||||
8. `version: 1` is required.
|
||||
|
||||
@@ -175,12 +157,6 @@ Rules:
|
||||
user `shell.json` exists, defaults are used verbatim. Once the user
|
||||
customizes, `shell.json` is canonical — there is no deep-merge.
|
||||
|
||||
`shell.json` is shell configuration; theme tokens live in `shell.toml`
|
||||
(next section). Both are current — they answer different questions. A
|
||||
machine-level `~/.config/omarchy/shell.toml` is watched live by the
|
||||
shell and its keys win over the active theme's `shell.toml`, so
|
||||
overrides like `omarchy display text size` survive theme switches.
|
||||
|
||||
## Theme tokens
|
||||
|
||||
See [`theming.md`](theming.md) for the full theme/template workflow,
|
||||
@@ -189,23 +165,20 @@ including generated `*.tpl` files, gradient helpers, and shell border syntax.
|
||||
Themes ship colors in `themes/<name>/colors.toml` and surface roles +
|
||||
sizing in `themes/<name>/shell.toml`. Defaults are generated from
|
||||
`default/themed/shell.toml.tpl`; a theme may also drop a hand-written
|
||||
`shell.toml` next to its `colors.toml` to replace the generated file,
|
||||
or override a single section with `shell.<section>.toml`, merged in by
|
||||
`omarchy-theme-set-templates` (see [`theming.md`](theming.md)).
|
||||
`shell.toml` next to its `colors.toml` to replace the generated file.
|
||||
|
||||
`colors.toml` uses `foreground` and `background` for the foundational
|
||||
text/background palette, exposed to QML as `Color.foreground` and
|
||||
`Color.background`.
|
||||
|
||||
The shell exposes these tokens to QML via three singletons in
|
||||
The shell exposes these tokens to QML via two singletons in
|
||||
`qs.Commons`:
|
||||
|
||||
- `Color` — palette (`foreground`, `background`, `accent`, `urgent`)
|
||||
and per-surface roles (`Color.bar.*`, `Color.popups.*`,
|
||||
`Color.tooltip.*`, `Color.notifications.*`, `Color.menu.*`,
|
||||
`Color.polkit.*`, `Color.lock.*`, `Color.imagePicker.*`). Clipboard
|
||||
and emojis share `Color.menu.*`; the `[launcher]` section is consumed
|
||||
by the launcher outside shell QML.
|
||||
`Color.launcher.*`, `Color.imagePicker.*`, `Color.polkit.*`,
|
||||
`Color.lock.*`). Clipboard and emojis share `Color.menu.*`.
|
||||
- `Style` — structural tokens (`cornerRadius`), shared interactive
|
||||
state tokens/helpers, spacing (`Style.spacing.*` / `Style.space(px)`),
|
||||
the type scale (`Style.font.*`), and bar dimensions
|
||||
@@ -398,6 +371,5 @@ Then `~/.config/omarchy/bar/modules/gpu.qml` (or set `source` to point
|
||||
elsewhere). The module is an `Item` and receives `bar`, `moduleName`,
|
||||
`settings` properties. `bar` exposes `foreground` / `background` /
|
||||
`urgent` / `fontFamily` / `position` / `vertical` / `barSize`, plus
|
||||
`run(cmd)`, `showTooltip(t, s)` / `hideTooltip(t)`,
|
||||
`requestPopout(o)` / `releasePopout(o)`. To shell-quote arguments for
|
||||
`run`, use `Util.shellQuote(v)` from `qs.Commons`.
|
||||
`run(cmd)`, `shellQuote(v)`, `showTooltip(t, s)` / `hideTooltip(t)`,
|
||||
`requestPopout(o)` / `releasePopout(o)`.
|
||||
-138
@@ -1,138 +0,0 @@
|
||||
# Testing
|
||||
|
||||
How the non-graphical test suites are organized: what each runner owns, the
|
||||
protocol test files speak, and the conventions that keep them runnable on any
|
||||
machine — including headless CI sandboxes with no compositor. The graphical
|
||||
acceptance suite is a separate thing that drives a live session in a disposable
|
||||
VM; see [`agents/skills/acceptance-tests.md`](../agents/skills/acceptance-tests.md).
|
||||
|
||||
## Suite map
|
||||
|
||||
`./test/all` runs both suites below and keeps going when one fails, so a single
|
||||
failure cannot hide the other suite behind it. It reports the failed suites at
|
||||
the end and exits non-zero.
|
||||
|
||||
- **`./test/cli`** — one big script, one suite. It owns the CLI router: help
|
||||
and group rendering, route resolution, aliases, hidden commands, and the
|
||||
guarantee that a trailing `--help` never executes the target. It also owns
|
||||
the metadata lint — every `omarchy-*` executable under `bin/` is checked for a
|
||||
`# omarchy:summary=` header and against removed or redundant fields — plus
|
||||
the theme pipeline: template rendering (`omarchy-theme-set-templates`,
|
||||
`omarchy-theme-color`, `omarchy-theme-osc`), the theme sync commands
|
||||
(tmux, GNOME, VS Code, Pi, Claude) run against stub binaries and a fake
|
||||
`$HOME`, and the theme-state migrations.
|
||||
- **`./test/shell`** — runs every `test/shell.d/*-test.sh` (except
|
||||
`base-test.sh` itself). Each file is an independent suite covering one area:
|
||||
a shell plugin, a `bin/` command, a config invariant, a migration. This is
|
||||
where new tests go.
|
||||
- **Acceptance** — everything that needs a real desktop doing real things.
|
||||
Deliberately excluded from `./test/all`; it runs in a VM, not the
|
||||
development session.
|
||||
|
||||
A new shell test only needs the right name: drop `<area>-test.sh` into
|
||||
`test/shell.d/` and `./test/shell` picks it up automatically. Shared fixtures
|
||||
live under `test/shell.d/fixtures/`.
|
||||
|
||||
## The base-test.sh contract
|
||||
|
||||
Every shell test starts the same way:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||
```
|
||||
|
||||
`base-test.sh` refuses to be executed directly — it is a library. It discovers
|
||||
the repo root from its own location and exports it as `ROOT`, so tests
|
||||
reference files as `$ROOT/bin/...` and never depend on the caller's working
|
||||
directory or an installed Omarchy.
|
||||
|
||||
Assertions are TAP-flavored and blunt:
|
||||
|
||||
- `pass "description"` prints `ok - description`.
|
||||
- `fail "description" [detail]` prints the optional detail and
|
||||
`not ok - description` to stderr, then **exits the file**. There is no
|
||||
counting or continuing within a file: the first failed assertion ends it,
|
||||
which keeps later assertions from reporting against state the failure
|
||||
already invalidated.
|
||||
- `require_command <cmd>` fails the file when a needed tool is absent.
|
||||
|
||||
The runner compensates for that early exit: `./test/shell` continues past a
|
||||
failing file and summarizes the failures at the end. Aborting the whole run at
|
||||
the first bad file once let a single packaging failure mask 114 of 134 files.
|
||||
Failure granularity is therefore per file inside a run, per assertion inside a
|
||||
file.
|
||||
|
||||
## Compositor-dependent tests
|
||||
|
||||
Some tests launch Quickshell or query Hyprland, but the suite must stay green
|
||||
on headless machines. `require_compositor "description"` handles this: when no
|
||||
compositor answers it prints `ok - no Wayland compositor; skipping ...` and
|
||||
exits 0 — a skip is a passing test — and otherwise returns so the file
|
||||
proceeds.
|
||||
|
||||
The probe is more than an environment check, because `WAYLAND_DISPLAY` only
|
||||
proves the variable was inherited. Sandboxes pass the environment through
|
||||
while blocking `$XDG_RUNTIME_DIR`, so Quickshell clears a bare variable check
|
||||
and then aborts inside QGuiApplication — a core dump per launch where a skip
|
||||
belonged. So `compositor_reachable` checks the socket actually exists, then
|
||||
asks Hyprland itself (`hyprctl -j monitors`, retried, and only when
|
||||
`HYPRLAND_INSTANCE_SIGNATURE` makes it askable), since a compositor that died
|
||||
mid-session leaves its socket behind. When the compositor is reachable,
|
||||
`require_compositor` also sets `ulimit -c 0`: Quickshell leaves through
|
||||
`qFatal()` if its connection drops mid-run, and the test should fail without
|
||||
writing a core dump as debris.
|
||||
|
||||
Gate only what needs gating — put `require_compositor` in files whose runtime
|
||||
half needs a live session, and keep static analysis of the same area in code
|
||||
that runs unconditionally before or beside it.
|
||||
|
||||
## Unit-testing shell JavaScript from bash
|
||||
|
||||
The Quickshell plugins keep their logic in plain `.js` modules
|
||||
(`shell/plugins/menu/MenuModel.js`, `bar/BarModel.js`, ...) that end in a
|
||||
guarded `if (typeof module !== "undefined") module.exports = {...}` block. QML
|
||||
imports them directly and ignores the guard; Node loads them as CommonJS. That
|
||||
dual citizenship is what makes the shell's model logic unit-testable without a
|
||||
compositor.
|
||||
|
||||
`run_node_test` is the bridge: it prepends a JS prelude to a heredoc and pipes
|
||||
the result into `node`. The prelude mirrors the bash assertion protocol
|
||||
(`pass`, `fail`, `assert`, `assertEqual`, `assertDeepEqual` — same
|
||||
`ok`/`not ok` lines, same exit-on-first-failure) and provides `root` (from the
|
||||
exported `ROOT`), `path`, and `requireFromRoot(relativePath)`:
|
||||
|
||||
```bash
|
||||
run_node_test <<'JS'
|
||||
const menu = requireFromRoot('shell/plugins/menu/MenuModel.js')
|
||||
|
||||
const parsed = menu.parseMenuJsonc('{ "items": { "root": { "label": "Go" }, }, }')
|
||||
assertEqual(parsed.length, 1, 'menu parses JSONC with trailing commas')
|
||||
JS
|
||||
```
|
||||
|
||||
Roughly a quarter of the shell test files use this to test parsing, merging,
|
||||
and layout logic as pure functions, reserving compositor-gated tests for what
|
||||
only a live session can prove.
|
||||
|
||||
## Conventions worth copying
|
||||
|
||||
- **Stub the world, run the real code.** Tests build a scratch `bin/` of stub
|
||||
executables (`sudo`, `tmux`, `gsettings`, helper commands) that log their
|
||||
arguments to a file, prepend it to `PATH`, and then run the real script
|
||||
under test. Assertions grep the call log and the files the script wrote.
|
||||
- **Fake `$HOME`, real `$OMARCHY_PATH`.** Anything touching user state runs
|
||||
with `HOME` pointed at a `mktemp -d` directory (cleaned up via
|
||||
`trap ... EXIT`) and `OMARCHY_PATH="$ROOT"`, so tests exercise the checkout
|
||||
without touching the developer's machine.
|
||||
- **Migrations run directly.** A migration test builds the legacy state in a
|
||||
fake `$HOME`, runs `bash -euo pipefail "$ROOT/migrations/<ts>.sh"`, and
|
||||
asserts the resulting state — including running it twice to prove
|
||||
idempotence, and once against non-legacy state to prove it leaves user
|
||||
customization alone.
|
||||
- **Assert the invariant, not the snapshot.** Config tests pin the property a
|
||||
test is named for (this widget stays adjacent to that one) rather than whole
|
||||
structures, so unrelated churn does not fail them.
|
||||
+5
-7
@@ -101,8 +101,7 @@ shell palette is loaded from:
|
||||
`color4`
|
||||
- `muted` — de-emphasized elements (comments, placeholders, dividers); also
|
||||
serves as ANSI `color8`
|
||||
- `red` / `color1` — populate the shell's urgent role; there is no `urgent`
|
||||
palette key (one defined in `colors.toml` is ignored)
|
||||
- `urgent` / `red` / `color1`
|
||||
|
||||
Themes and user templates using the legacy short names remain supported.
|
||||
Canonical names take precedence when both forms are defined, and resolved
|
||||
@@ -342,11 +341,10 @@ BorderSurface {
|
||||
}
|
||||
```
|
||||
|
||||
Use `Border.surfaceSpec(section, token, fallbackColor, fallbackWidth, alphaKey)`
|
||||
for shell theme tokens (the optional `alphaKey` names the alpha token, e.g.
|
||||
`"border-alpha"`), `Border.controlSpec(state, foreground, accent, urgent)` for
|
||||
shared controls, and `Border.flat(color, width)` for a deliberate local border
|
||||
that should not be overridden by the active theme. `Color.<section>.border` is the
|
||||
Use `Border.surfaceSpec(section, token, fallbackColor, fallbackWidth)` for
|
||||
shell theme tokens, `Border.controlSpec(state, foreground, accent)` for shared
|
||||
controls, and `Border.flat(color, width)` for a deliberate local border that
|
||||
should not be overridden by the active theme. `Color.<section>.border` is the
|
||||
flat first-stop color for consumers that cannot render full border specs.
|
||||
|
||||
## Hyprland templates
|
||||
|
||||
+6
-31
@@ -30,7 +30,7 @@ The design goal is:
|
||||
|
||||
## Migration layout
|
||||
|
||||
See [`migrations.md`](../agents/skills/migrations.md) for the full migration model, authoring
|
||||
See [`migrations.md`](migrations.md) for the full migration model, authoring
|
||||
guidelines, and troubleshooting notes.
|
||||
|
||||
Migrations live in:
|
||||
@@ -82,7 +82,7 @@ Omarchy update command, the hook exits non-zero with `AbortOnFail`, which stops
|
||||
the transaction before packages are changed.
|
||||
|
||||
`omarchy-update-system-pkgs`, `omarchy-refresh-pacman`, `omarchy-reinstall-pkgs`,
|
||||
`omarchy-channel-set`, and the v4 upgrader run pacman through:
|
||||
and the v4 upgrader run pacman through:
|
||||
|
||||
```bash
|
||||
env OMARCHY_UPDATE_PACMAN=1 pacman ...
|
||||
@@ -116,14 +116,9 @@ omarchy-update
|
||||
├─ omarchy-update-lock
|
||||
│ └─ acquire the update lock and run omarchy-update inside it
|
||||
├─ omarchy-update-requires-free-space
|
||||
│ └─ abort below the configured free-space threshold on /
|
||||
│ └─ check free space on / and warn below the configured threshold
|
||||
├─ confirm unless -y
|
||||
├─ omarchy-update-pkg-prune
|
||||
│ └─ trim the pacman cache to two versions per package, deliberately
|
||||
│ before the snapshot since the cache lives on the snapshotted subvolume
|
||||
├─ create snapper snapshot (skipped silently without snapper; snapper
|
||||
│ installed but unconfigured fails the snapshot loudly, pointing at
|
||||
│ install/config/snapper.sh, and the update continues without one)
|
||||
├─ create snapper snapshot, if snapper is installed
|
||||
├─ omarchy-update-stay-awake start
|
||||
├─ run package updates, migrations, hooks, and log analysis
|
||||
├─ omarchy-update-status
|
||||
@@ -137,9 +132,6 @@ Important behavior:
|
||||
|
||||
- In dev-link mode, `omarchy update` fast-forwards the active checkout from its
|
||||
configured upstream before changing system packages or running migrations.
|
||||
- `-y` exports `OMARCHY_UPDATE_UNATTENDED=1` — a promise not to ask anything.
|
||||
Steps that would prompt (orphan removal, conflict handoff) report and skip
|
||||
instead of blocking.
|
||||
- The free-space requirement uses a 10 GiB threshold and stops the update before
|
||||
confirmation when it is not met. If free space cannot be determined, the
|
||||
check is silently skipped. Set `OMARCHY_UPDATE_FORCE=1` to bypass the check.
|
||||
@@ -243,20 +235,6 @@ Exit codes:
|
||||
The widget runs this check on shell startup and every six hours. Clicking the
|
||||
update icon launches `omarchy-update` in a floating terminal.
|
||||
|
||||
## Channels and versions
|
||||
|
||||
Updates install whatever the active channel points at. `omarchy-channel-set
|
||||
<stable|rc|edge|dev>` switches channels: the three package channels select
|
||||
which pacman repo the mirrorlist points at (and swap between the `omarchy` and
|
||||
`omarchy-dev` packages through a guard-allowed pacman run), while `dev` links
|
||||
the runtime to a git checkout via the dev-link mechanism, after which
|
||||
`omarchy update` fast-forwards that checkout instead of upgrading a package.
|
||||
|
||||
There is no version file at runtime. `omarchy-version` derives the version from
|
||||
`pacman -Q` on whichever package is installed, or reports `dev (<hash>)` for a
|
||||
linked checkout, and `omarchy-version-channel` sniffs the mirrorlist and
|
||||
pacman.conf to answer which channel is active.
|
||||
|
||||
## Update-related binaries
|
||||
|
||||
This inventory is intentionally opinionated. Some commands are useful as stable
|
||||
@@ -272,17 +250,14 @@ scripts.
|
||||
| `omarchy-update-confirm` | Gum confirmation copy for `omarchy update`. | **Question.** Could be inlined into `omarchy-update`; separate file only helps keep copy isolated. |
|
||||
| `omarchy-update-dev` | Fast-forwards the active dev-linked checkout from its configured upstream; no-ops for package-backed installs. | **Keep.** Runs before package updates so a checkout conflict stops the update before system mutation. |
|
||||
| `omarchy-update-keyring` | Ensures Omarchy keyring and Arch keyring are current before the main transaction. | **Keep, but review.** It uses targeted `pacman -Sy` for keyring bootstrapping; acceptable for this special case but should remain tightly scoped. |
|
||||
| `omarchy-update-system-pkgs` | Runs `sudo env OMARCHY_UPDATE_PACMAN=1 pacman -Syu --noconfirm` with `--overwrite '/usr/share/omarchy/*'`, capturing stderr to a report file; on failure it execs `omarchy-update-system-pkgs-when-conflicted`. | **Keep for now.** Small leaf command, clear/testable. |
|
||||
| `omarchy-update-system-pkgs-when-conflicted` | Hidden conflict handler: quarantines unowned conflicting files under `/var/lib/omarchy/replaced`, retries the upgrade once, restores files the upgrade didn't claim, and hands package-vs-package conflicts to an interactive pacman run (never under `-y`). | **Keep internal/hidden.** Keeps conflict recovery out of the happy path. |
|
||||
| `omarchy-update-pkg-prune` | Trims the pacman cache to two versions per package (`paccache -rk2`) before the snapshot, keeping the offline downgrade path while capping snapshot growth. | **Keep internal/hidden.** |
|
||||
| `omarchy-update-requires-free-space` | Aborts the update below a 10 GiB free-space threshold on `/`; silently skipped when free space cannot be determined; `OMARCHY_UPDATE_FORCE=1` bypasses. | **Keep internal/hidden.** |
|
||||
| `omarchy-update-system-pkgs` | Runs `sudo env OMARCHY_UPDATE_PACMAN=1 pacman -Syu --noconfirm` with targeted transition `--overwrite` entries so the ALPM guard allows the transaction and early package-layout conflicts are handled. | **Keep for now.** Small leaf command, clear/testable. |
|
||||
| `omarchy-migrate` | Public migration command. Waits for pacman, then runs all pending migrations for the current user. Supports `--pending`. | **Keep.** This replaces the discarded `omarchy-update-user-finalize` name and no longer needs `--force`. |
|
||||
| `omarchy-update-pacman-guard` | ALPM pre-transaction guard that aborts direct `pacman -Syu` style upgrades unless Omarchy set `OMARCHY_UPDATE_PACMAN=1` or the user explicitly set `OMARCHY_ALLOW_DIRECT_PACMAN=1`. | **Keep internal/hidden.** This is what nudges users back to `omarchy update`. |
|
||||
| `omarchy-migrate-notify` | Internal login-time notification helper. Uses `omarchy-migrate --pending` and shows a notification only when this user has pending migrations. | **Keep internal/hidden.** Clear name now that the public command is `omarchy-migrate`. |
|
||||
| `omarchy-update-user-notify` | Hidden compatibility wrapper for `omarchy-migrate-notify`. | **Temporary.** Keep only for old callers. |
|
||||
| `omarchy-update-available` | Update checker for shell widget and post-update refresh. | **Keep.** Could eventually be renamed `omarchy-update-check`, but current name matches widget semantics. |
|
||||
| `omarchy-update-aur-pkgs` | Updates AUR packages with `yay -Sua` if foreign packages exist and AUR is reachable. | **Question.** Omarchy is package-backed now, but users may still install AUR packages. Keep for now. |
|
||||
| `omarchy-update-mise` | Runs `MISE_MINIMUM_RELEASE_AGE=0 mise up` for mise-managed tools — the override of mise's release-age cooldown is the point. | **Keep.** Mise-managed tools are intentionally part of the blessed update path. |
|
||||
| `omarchy-update-mise` | Runs `mise up` for mise-managed tools. | **Keep.** Mise-managed tools are intentionally part of the blessed update path. |
|
||||
| `omarchy-update-orphan-pkgs` | Lists orphans and prompts before removal; noninteractive mode never removes. | **Keep for now.** Safe because it is prompt-only. |
|
||||
| `omarchy-update-analyze-logs` | Scans `/tmp/omarchy-update.log` for known failure patterns, currently initramfs generation. | **Keep/expand.** Useful safety net; should grow only for high-signal checks. |
|
||||
| `omarchy-update-restart` | Prompts for reboot after kernel/Hyprland updates, restarts components with `restart-*-required` markers, and always restarts the shell. | **Keep.** Important final step; may eventually include service-restart checks. |
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
# Keep state away from /var/cache/cups, which is writable by the account CUPS
|
||||
# uses for print filters. cups-browsed is the only writer to this directory.
|
||||
CacheDir /var/cache/cups-browsed
|
||||
|
||||
# Auto-create queues only for modern driverless IPP printers. Remote queues
|
||||
# exported by another CUPS server can still be added manually when needed.
|
||||
CreateIPPPrinterQueues Driverless
|
||||
CreateRemoteCUPSPrinterQueues No
|
||||
# Omarchy override of cups-browsed's shipped config. The only behavioural
|
||||
# change vs the upstream default (all-commented) is enabling auto-registration
|
||||
# of remote IPP printers discovered via Avahi/mDNS.
|
||||
CreateRemotePrinters Yes
|
||||
@@ -1,90 +0,0 @@
|
||||
#
|
||||
# File/directory/user/group configuration file for the CUPS scheduler.
|
||||
# See "man cups-files.conf" for a complete description of this file.
|
||||
#
|
||||
|
||||
# List of events that are considered fatal errors for the scheduler...
|
||||
#FatalErrors config
|
||||
|
||||
# Strip domain in local username?
|
||||
#StripUserDomain No
|
||||
|
||||
# Do we call fsync() after writing configuration or status files?
|
||||
#SyncOnClose No
|
||||
|
||||
# Default user and group for filters/backends/helper programs; this cannot be
|
||||
# any user or group that resolves to ID 0 for security reasons...
|
||||
User 209
|
||||
Group 209
|
||||
|
||||
# Administrator user group, used to match @SYSTEM in cupsd.conf policy rules...
|
||||
# This cannot contain the Group value for security reasons...
|
||||
SystemGroup cups-browsed sys root
|
||||
|
||||
|
||||
# Are Unix domain socket peer credentials used for authorization?
|
||||
PeerCred on
|
||||
|
||||
# User that is substituted for unauthenticated (remote) root accesses...
|
||||
#RemoteRoot remroot
|
||||
|
||||
# Do we allow file: device URIs other than to /dev/null?
|
||||
#FileDevice No
|
||||
|
||||
# Permissions for configuration and log files...
|
||||
#ConfigFilePerm 0640
|
||||
#LogFilePerm 0644
|
||||
|
||||
# Location of the file logging all access to the scheduler; may be the name
|
||||
# "syslog". If not an absolute path, the value of ServerRoot is used as the
|
||||
# root directory. Also see the "AccessLogLevel" directive in cupsd.conf.
|
||||
AccessLog /var/log/cups/access_log
|
||||
|
||||
# Location of cache files used by the scheduler...
|
||||
#CacheDir /var/cache/cups
|
||||
|
||||
# Location of data files used by the scheduler...
|
||||
#DataDir /usr/share/cups
|
||||
|
||||
# Location of the static web content served by the scheduler...
|
||||
#DocumentRoot /usr/share/cups/doc
|
||||
|
||||
# Location of the file logging all messages produced by the scheduler and any
|
||||
# helper programs; may be the name "syslog". If not an absolute path, the value
|
||||
# of ServerRoot is used as the root directory. Also see the "LogLevel"
|
||||
# directive in cupsd.conf.
|
||||
ErrorLog /var/log/cups/error_log
|
||||
|
||||
# Location of the file logging all pages printed by the scheduler and any
|
||||
# helper programs; may be the name "syslog". If not an absolute path, the value
|
||||
# of ServerRoot is used as the root directory. Also see the "PageLogFormat"
|
||||
# directive in cupsd.conf.
|
||||
PageLog /var/log/cups/page_log
|
||||
|
||||
# Location of the file listing all of the local printers...
|
||||
#Printcap /etc/printcap
|
||||
|
||||
# Format of the Printcap file...
|
||||
#PrintcapFormat bsd
|
||||
#PrintcapFormat plist
|
||||
#PrintcapFormat solaris
|
||||
|
||||
# Location of all spool files...
|
||||
#RequestRoot /var/spool/cups
|
||||
|
||||
# Location of helper programs...
|
||||
#ServerBin /usr/lib/cups
|
||||
|
||||
# SSL/TLS keychain for the scheduler...
|
||||
#ServerKeychain ssl
|
||||
|
||||
# Location of other configuration files...
|
||||
#ServerRoot /etc/cups
|
||||
|
||||
# Location of scheduler state files...
|
||||
#StateDir /run/cups
|
||||
|
||||
# Location of scheduler/helper temporary files. This directory is emptied on
|
||||
# scheduler startup and cannot be one of the standard (public) temporary
|
||||
# directory locations for security reasons...
|
||||
#TempDir /var/spool/cups/tmp
|
||||
@@ -0,0 +1 @@
|
||||
%wheel ALL=(ALL) NOPASSWD: /usr/bin/asdcontrol
|
||||
@@ -1,8 +0,0 @@
|
||||
# Theme switching is a menu action with no terminal to carry a password prompt,
|
||||
# and it repaints the browser accent on every switch, so this one write must not
|
||||
# stop for a password. The argument is spelled out as six hex digits rather than
|
||||
# a wildcard: the grant covers a color and nothing else, and sudoers matches a
|
||||
# command's arguments exactly, so it cannot be stretched into extra ones. The
|
||||
# helper revalidates the same shape, since the terminal path does not come
|
||||
# through this rule.
|
||||
%wheel ALL=(root) NOPASSWD: /usr/bin/omarchy-theme-set-browser-policy [0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]
|
||||
@@ -1 +1 @@
|
||||
%wheel ALL=(root) NOPASSWD: /usr/bin/timedatectl ^set-timezone [A-Za-z0-9_+][A-Za-z0-9_+.-]*(/[A-Za-z0-9_+][A-Za-z0-9_+.-]*)*$
|
||||
%wheel ALL=(root) NOPASSWD: /usr/bin/timedatectl set-timezone *
|
||||
@@ -1,11 +0,0 @@
|
||||
[Service]
|
||||
User=cups-browsed
|
||||
Group=cups-browsed
|
||||
CacheDirectory=cups-browsed
|
||||
CacheDirectoryMode=0750
|
||||
UMask=0027
|
||||
NoNewPrivileges=yes
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
PrivateTmp=yes
|
||||
RestrictSUIDSGID=yes
|
||||
@@ -1 +0,0 @@
|
||||
u cups-browsed - "CUPS printer discovery" / -
|
||||
@@ -1,5 +1,4 @@
|
||||
run_logged "$OMARCHY_INSTALL/config/theme-system.sh"
|
||||
run_logged "$OMARCHY_INSTALL/config/browser-policy.sh"
|
||||
run_logged "$OMARCHY_INSTALL/config/increase-lockout-limit.sh"
|
||||
run_logged "$OMARCHY_INSTALL/config/lockscreen-pam.sh"
|
||||
run_logged "$OMARCHY_INSTALL/config/fix-powerprofilesctl-shebang.sh"
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
||||
browser_policy_setup_dir /etc/chromium/policies/managed
|
||||
@@ -1,14 +1,16 @@
|
||||
# Enable services only. Installs are followed by reboot, so don't start/reload
|
||||
# daemons mid-install. UFW and hardware-gated services stay in their own scripts.
|
||||
systemctl enable cups.service
|
||||
systemctl enable cups-browsed.service
|
||||
systemctl enable avahi-daemon.service
|
||||
systemctl enable linux-modules-cleanup.service
|
||||
systemctl enable docker.socket
|
||||
systemctl enable systemd-resolved.service
|
||||
systemctl enable NetworkManager.service
|
||||
# Don't let network-online.target hold up graphical.target waiting for
|
||||
# DHCP/Wi-Fi association. Nothing in the session needs to block on the network.
|
||||
# Mirrors the systemd-networkd-wait-online mask in install/hardware/network.sh.
|
||||
# Don't let network-online.target (pulled in by cups-browsed) hold up
|
||||
# graphical.target waiting for DHCP/Wi-Fi association. Nothing in the session
|
||||
# needs to block on the network. Mirrors the systemd-networkd-wait-online mask
|
||||
# in install/hardware/network.sh.
|
||||
systemctl mask NetworkManager-wait-online.service
|
||||
systemctl enable power-profiles-daemon.service
|
||||
systemctl enable sddm.service
|
||||
|
||||
@@ -6,8 +6,11 @@ ln -snf /usr/share/icons/Adwaita/symbolic/actions/go-next-symbolic.svg \
|
||||
/usr/share/icons/Yaru/scalable/actions/go-next-symbolic.svg
|
||||
gtk-update-icon-cache /usr/share/icons/Yaru &>/dev/null || true
|
||||
|
||||
# Seed Chromium's first run: follow system appearance ("device") instead of dark,
|
||||
# and skip the terms-of-service dialog Chromium 151 turned on by default.
|
||||
# Chromium policy directory for theme
|
||||
mkdir -p /etc/chromium/policies/managed
|
||||
chmod a+rw /etc/chromium/policies/managed
|
||||
|
||||
# Default Chromium to follow system appearance ("device") instead of dark
|
||||
mkdir -p /usr/lib/chromium
|
||||
echo '{"distribution":{"require_eula":false},"browser":{"theme":{"color_scheme":0,"color_scheme2":0}}}' > \
|
||||
echo '{"browser":{"theme":{"color_scheme":0,"color_scheme2":0}}}' > \
|
||||
/usr/lib/chromium/initial_preferences
|
||||
@@ -4,6 +4,7 @@ run_logged "$OMARCHY_INSTALL/hardware/dell-xps-touchpad-haptics.sh"
|
||||
run_logged "$OMARCHY_INSTALL/hardware/surface.sh"
|
||||
|
||||
run_logged "$OMARCHY_INSTALL/hardware/network.sh"
|
||||
run_logged "$OMARCHY_INSTALL/hardware/input-group.sh"
|
||||
run_logged "$OMARCHY_INSTALL/hardware/set-wireless-regdom.sh"
|
||||
run_logged "$OMARCHY_INSTALL/hardware/fix-fkeys.sh"
|
||||
run_logged "$OMARCHY_INSTALL/hardware/fix-synaptic-touchpad.sh"
|
||||
@@ -24,10 +25,6 @@ run_logged "$OMARCHY_INSTALL/hardware/intel/fred.sh"
|
||||
run_logged "$OMARCHY_INSTALL/hardware/intel/fix-wifi7-eht.sh"
|
||||
run_logged "$OMARCHY_INSTALL/hardware/intel/sof-firmware.sh"
|
||||
|
||||
# Rebuilds the boot image, so it has to follow the Panther Lake kernel swap
|
||||
# above rather than sit with the other Dell leaf at the top of this file.
|
||||
run_logged "$OMARCHY_INSTALL/hardware/dell-xps13-sidecar-amps.sh"
|
||||
|
||||
run_logged "$OMARCHY_INSTALL/hardware/asus/fix-asus-ptl-display-backlight.sh"
|
||||
run_logged "$OMARCHY_INSTALL/hardware/asus/fix-asus-ptl-b9406-display.sh"
|
||||
run_logged "$OMARCHY_INSTALL/hardware/asus/fix-asus-ptl-b9406-touchpad.sh"
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
# Enable the temporary sidecar amplifier workaround on the exact Dell XPS 13 model that needs it.
|
||||
#
|
||||
# Pacman registers a package even when its post_install scriptlet fails, so the
|
||||
# apply command runs explicitly here: a failed cleanup or boot-image rebuild has
|
||||
# to reach the caller rather than hide behind a successfully registered package.
|
||||
|
||||
if omarchy-hw-dell-xps13-sidecar-amps; then
|
||||
omarchy-pkg-add dell-xps13-sidecar-amps &&
|
||||
sudo dell-xps13-sidecar-amps-apply
|
||||
fi
|
||||
@@ -0,0 +1,11 @@
|
||||
# Give this user privileged input access for dictation tools + xbox controllers to work.
|
||||
# Recorded for provisioning first-boot user creation and factory reset, granted directly
|
||||
# when the install user already exists (deferred-provisioning installs create the user at
|
||||
# first boot instead).
|
||||
provisioning_dir="${OMARCHY_PROVISIONING_DIR:-/var/lib/omarchy/provisioning}"
|
||||
mkdir -p "$provisioning_dir"
|
||||
grep -qxF input "$provisioning_dir/groups" 2>/dev/null || echo input >>"$provisioning_dir/groups"
|
||||
|
||||
if [[ -n ${OMARCHY_INSTALL_USER:-} ]] && getent passwd "$OMARCHY_INSTALL_USER" >/dev/null; then
|
||||
usermod -aG input "$OMARCHY_INSTALL_USER"
|
||||
fi
|
||||
@@ -1,7 +0,0 @@
|
||||
as_root() {
|
||||
if (( EUID == 0 )); then
|
||||
"$@"
|
||||
else
|
||||
sudo "$@"
|
||||
fi
|
||||
}
|
||||
Loaded 100 of 430 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user