Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions contrib/omarchy-theme/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
__pycache__/
*.pyc
theme/theme.css
theme/sync-state.json
theme/.sync.lock
theme/.omarchy-television-theme
78 changes: 78 additions & 0 deletions contrib/omarchy-theme/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Follow the Omarchy theme

This optional integration keeps Television's colors and effective light/dark
appearance matched to Omarchy's active palette. It runs on the Omarchy machine
that hosts the Television server and uses Television's supported theme interface.
It is installed separately from the Linux desktop client; it is not a bundled
default theme and does not change the desktop client's installer.

Requirements: Omarchy, Python 3.11 or later, Bash, user systemd, and a
working local `tv` command/server. No root privileges or new Python packages are
needed.

The service retains the installation shell's PATH so it can find mise-managed
Node and `tv` installations. Only PATH is saved; account environment variables
and credentials are not collected. Rerun the permanent installer if the CLI's
installation location changes.

From a checkout of this fork:

```bash
bash contrib/omarchy-theme/install.sh
```

The installer saves a permanent copy under
`~/.local/share/omarchy-television-theme`, creates the `Omarchy` theme in the home
reported by `tv themes-path`, and activates it. Theme-change, login, and update
hooks sync immediately. A user timer checks again every five minutes, recovering
missed hooks and temporary server outages. An unchanged check does not rewrite
the stylesheet or refresh the theme registry. No desktop or Television restart
is needed, and channels, artifacts, account tokens, and connection settings are
not copied or changed.

The theme follows the active **palette**, including local Omarchy overrides.
It uses solid backgrounds, preserves Television's layout and fonts, and adjusts
supporting text for contrast. Live canonical artifacts receive the same semantic
colors. Frozen canonical v1 and external websites retain their own appearance.

Manual repair and refresh:

```bash
omarchy-television-theme
```

Reinstall the integration from its permanent copy:

```bash
bash ~/.local/share/omarchy-television-theme/install.sh
```

The installer backs up existing owned files under
`~/.local/state/omarchy-television-theme/backups/` and refuses unrelated existing
files. `--adopt-existing` is only for the earlier version of this exact integration;
it verifies that version's authoring record before taking ownership. It is not a
general override. The repair tool recreates a missing theme folder and repairs
damaged runtime files, while preserving someone else's replacement theme folder.

To stop automatic following, disable the timer and remove the three hooks:

```bash
systemctl --user disable --now omarchy-television-theme.timer
rm ~/.config/omarchy/hooks/{theme-set,post-boot,post-update}.d/95-television-omarchy-theme
```

You can then select another Television theme. Otherwise, the next hook or timer
check restores the Omarchy selection. Inspect failures with
`journalctl --user -u omarchy-television-theme.service`.

Tests run within the repository's `unit:desktop` surface. They use committed
dark/light palette fixtures and temporary files. When stock Omarchy palettes are
present, the same suite checks them too. Offline activation and ownership tests
substitute the `tv` command and do not stop the live server or any desktop.
The original host verification covered all 22 stock palettes, actual hook and
user-service execution, enabled timer state, a second installation, and served
CSS matching the active theme. It did not include a GUI visual review or an
actual operating-system update.

The [theme authoring record](theme/README.md) explains the color mapping,
generated state, ownership marker, and maintenance decisions.
125 changes: 125 additions & 0 deletions contrib/omarchy-theme/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/usr/bin/env bash
# Managed by omarchy-television-theme. Safe to rerun from the permanent copy.
set -euo pipefail
export PATH="$HOME/.local/bin:$PATH"
export PYTHONDONTWRITEBYTECODE=1
base=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
source_dir="$base/omarchy"
[[ -d "$source_dir" ]] || source_dir="$base/theme"
adopt=0
if [[ ${1:-} == --adopt-existing ]]; then
adopt=1
elif [[ $# != 0 ]]; then
echo 'Usage: install.sh [--adopt-existing]' >&2
exit 1
fi
themes=$(tv themes-path)
themes_dir=$(python -c 'import json,sys; print(json.loads(sys.argv[1])["themesPath"])' "$themes")
target="$themes_dir/omarchy"
permanent="$HOME/.local/share/omarchy-television-theme"
units="$HOME/.config/systemd/user"
launcher="$HOME/.local/bin/omarchy-television-theme"
config_dir="$HOME/.config/omarchy-television-theme"
owner='omarchy-television-theme:v1'
if [[ -e "$permanent" && $(cat "$permanent/theme/.omarchy-television-theme" 2>/dev/null || true) != "$owner" ]]; then
echo "Refusing to overwrite unowned integration: $permanent" >&2
exit 1
fi
if [[ -L "$target" || -L "$permanent" ]]; then
echo 'Refusing to replace symlinked installation directories' >&2
exit 1
fi
if [[ -e "$target" && $(cat "$target/.omarchy-television-theme" 2>/dev/null || true) != "$owner" ]]; then
if [[ "$adopt" != 1 ]]; then
echo "Refusing to overwrite unowned theme: $target" >&2
exit 1
fi
python - "$target" <<'PY'
import json, sys
from pathlib import Path
p = Path(sys.argv[1])
try:
owned = (
json.loads((p/'manifest.json').read_text())['name'] == 'Omarchy'
and (p/'README.md').read_text().startswith('# Omarchy Television theme\n\nMatches Television')
and (p/'sync.py').read_text().startswith('#!/usr/bin/env python3\n"""Map the active Omarchy palette')
)
except (OSError, ValueError, KeyError, TypeError):
owned = False
if not owned:
raise SystemExit('Refusing to adopt an unrelated theme')
PY
fi
paths=("$target" "$permanent" "$launcher" "$units/omarchy-television-theme.service" "$units/omarchy-television-theme.timer" "$config_dir/environment")
for event in theme-set post-boot post-update; do
paths+=("$HOME/.config/omarchy/hooks/$event.d/95-television-omarchy-theme")
done
for path in "${paths[@]:2}"; do
if [[ -e "$path" ]] && ! grep -qF 'Managed by omarchy-television-theme' "$path"; then
if [[ "$adopt" == 1 && "$path" == */95-television-omarchy-theme ]] && grep -qF '/omarchy/sync.py' "$path"; then
continue
fi
echo "Refusing to overwrite an unowned file: $path" >&2
exit 1
fi
done
backup_dir="$HOME/.local/state/omarchy-television-theme/backups"
mkdir -p "$backup_dir"
backup="$backup_dir/$(TZ=America/New_York date +%Y%m%dT%H%M%S%Z)-$$.tar.gz"
python - "$backup" "${paths[@]}" <<'PY'
import sys, tarfile
from pathlib import Path
with tarfile.open(sys.argv[1], 'w:gz') as archive:
for value in sys.argv[2:]:
path = Path(value)
if path.exists():
archive.add(path, arcname=str(path).lstrip('/'))
PY
mkdir -p "$permanent/theme" "$target" "$units" "$HOME/.local/bin" "$config_dir"
copy_changed() {
local source=$1 dest=$2 mode=${3:-644}
if ! cmp -s "$source" "$dest"; then
install -m "$mode" "$source" "$dest.new"
mv -f -- "$dest.new" "$dest"
fi
}
for file in manifest.json sync.py repair.py README.md 95-television-omarchy-theme omarchy-television-theme.service omarchy-television-theme.timer; do
copy_changed "$source_dir/$file" "$permanent/theme/$file"
done
copy_changed "$base/install.sh" "$permanent/install.sh" 755
printf '%s\n' "$owner" > "$permanent/theme/.omarchy-television-theme"
printf '%s\n' "$owner" > "$target/.omarchy-television-theme"
cat > "$launcher.new" <<'SH'
#!/usr/bin/env bash
# Managed by omarchy-television-theme.
set -euo pipefail
export PATH="$HOME/.local/bin:$PATH" PYTHONDONTWRITEBYTECODE=1
exec /usr/bin/python "$HOME/.local/share/omarchy-television-theme/theme/repair.py" "$@"
SH
chmod 755 "$launcher.new"
mv -f -- "$launcher.new" "$launcher"
python - "$config_dir/environment" "$PATH" <<'PY'
import os, sys
from pathlib import Path
path = Path(sys.argv[1])
value = sys.argv[2]
if '\n' in value or '\r' in value:
raise SystemExit('Cannot save a PATH containing line breaks')
escaped = value.replace('\\', '\\\\').replace('"', '\\"')
temporary = path.with_name(path.name + '.new')
temporary.write_text('# Managed by omarchy-television-theme. Only PATH is saved.\nPATH="' + escaped + '"\n')
temporary.chmod(0o600)
os.replace(temporary, path)
PY
for unit in omarchy-television-theme.service omarchy-television-theme.timer; do
copy_changed "$permanent/theme/$unit" "$units/$unit"
done
for event in theme-set post-boot post-update; do
omarchy hook install "$event" "$permanent/theme/95-television-omarchy-theme"
done
systemctl --user daemon-reload
systemctl --user enable --now omarchy-television-theme.timer
systemctl --user start omarchy-television-theme.service
tv focus-status
echo "Permanent integration: $permanent"
echo "Previous installation backup: $backup"
31 changes: 31 additions & 0 deletions contrib/omarchy-theme/tests/fixtures/dark/colors.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
mode = "dark"

accent = "#7aa2f7"
selection = "#292e42"
muted = "#414868"

background = "#1a1b26"
dark_background = "#13141c"
darker_background = "#0e0e14"
lighter_background = "#24283b"

foreground = "#a9b1d6"
dark_foreground = "#565f89"
light_foreground = "#b4bee6"
bright_foreground = "#c0caf5"

red = "#f7768e"
yellow = "#e0af68"
orange = "#eb927b"
green = "#9ece6a"
cyan = "#449dab"
blue = "#7aa2f7"
magenta = "#ad8ee6"
brown = "#75493d"

bright_red = "#ff7a93"
bright_yellow = "#ff9e64"
bright_green = "#b9f27c"
bright_cyan = "#0db9d7"
bright_blue = "#7da6ff"
bright_magenta = "#bb9af7"
31 changes: 31 additions & 0 deletions contrib/omarchy-theme/tests/fixtures/light/colors.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
mode = "light"

accent = "#205EA6"
selection = "#CECDC3"
muted = "#B7B5AC"

background = "#FFFCF0"
dark_background = "#f2efe4"
darker_background = "#e5e2d8"
lighter_background = "#E6E4D9"

foreground = "#100F0F"
dark_foreground = "#878580"
light_foreground = "#403E3C"
bright_foreground = "#100F0F"

red = "#D14D41"
yellow = "#D0A215"
orange = "#d0772b"
green = "#879A39"
cyan = "#3AA99F"
blue = "#205EA6"
magenta = "#CE5D97"
brown = "#683b15"

bright_red = "#D14D41"
bright_yellow = "#D0A215"
bright_green = "#879A39"
bright_cyan = "#3AA99F"
bright_blue = "#4385BE"
bright_magenta = "#CE5D97"
Loading
Loading