diff --git a/README.md b/README.md index 9254412..ae56a41 100644 --- a/README.md +++ b/README.md @@ -23,15 +23,17 @@ My current setup is built around these core components: - **`hypr/`**: Hyprland configurations (keybindings, window rules, animations, monitor layout, lid-close display handling, and autostart). See [`hypr/keybinds.md`](hypr/keybinds.md) for custom keybindings. - **`waybar/`**: Status bar layout, CSS styling, and custom interactive modules. -- **`ags/`**: Custom desktop widgets built with TypeScript and GTK — media player, notification hub, quick settings, system monitor, and wallpaper picker. +- **`ags/`**: Custom desktop widgets built with TypeScript and GTK — media player, notification hub, quick settings, system monitor, wallpaper picker, and theme picker. - **`scripts/`**: Global utility scripts seamlessly exposed as commands by the installer. See [`commands.md`](commands.md). - **`wallpapers/`**: A collection of local custom wallpapers for dynamic theming. See [`wallpaper-gallery/`](wallpaper-gallery/index.md) for the full gallery (split alphabetically across multiple pages). +- **`themes/`**: Drop-in local color themes (one `colors.toml` per theme) that `install.sh` deploys into Omarchy's theme directory. See [`themes/README.md`](themes/README.md). - **`omarchy/hooks/`**: Event hooks for the Omarchy system (e.g. automatically applying dynamic themes when changing wallpapers). - **`branding/`**: Custom ASCII art and system branding assets. ## Docs - [Wallpaper Gallery](wallpaper-gallery/index.md) — preview of every wallpaper in `wallpapers/`, split alphabetically across [0-9, A-D](wallpaper-gallery/a-d.md), [E-M](wallpaper-gallery/e-m.md), [M-R](wallpaper-gallery/m-r.md), [R-Y](wallpaper-gallery/r-z.md). +- [Themes](themes/README.md) — how to add a local color theme. - [Keybinds](hypr/keybinds.md) — custom Hyprland keybindings on top of Omarchy's defaults. - [Commands](commands.md) — custom `blob_*` CLI commands exposed by the installer. diff --git a/ags/app.ts b/ags/app.ts index 279ac07..560e453 100644 --- a/ags/app.ts +++ b/ags/app.ts @@ -4,6 +4,7 @@ import Media from "./widget/Media" import NotificationCenter from "./widget/Notifications" import QuickSettings from "./widget/QuickSettings" import SysMonitor from "./widget/SysMonitor" +import ThemePicker from "./widget/ThemePicker" import WallPicker from "./widget/WallPicker" const SHOW_DESKTOP_MEDIA = false @@ -25,6 +26,7 @@ app.start({ start("notification-center", NotificationCenter) start("quick-settings", QuickSettings) start("sys-monitor", SysMonitor) + start("theme-picker", ThemePicker) start("wall-picker", WallPicker) }, }) diff --git a/ags/style.css b/ags/style.css index 9668486..0ecc271 100644 --- a/ags/style.css +++ b/ags/style.css @@ -84,7 +84,8 @@ .QuickSettings, .SysMonitor, .Calendar, -.WallPicker { +.WallPicker, +.ThemePicker { background-color: transparent; } @@ -349,6 +350,64 @@ font-size: 13px; } +.theme-picker { + min-width: 680px; + min-height: 460px; +} + +.theme-scroll { + min-height: 360px; +} + +.theme-chip { + padding: 8px; + border-radius: 10px; + border: 2px solid transparent; + background-color: alpha(@color0, 0.6); + transition: all 0.2s; + min-width: 200px; +} + +.theme-chip:hover { + border: 2px solid alpha(@accent, 0.6); +} + +.theme-chip.active { + border: 2px solid @accent; +} + +.theme-chip-dynamic { + background-color: alpha(@accent, 0.15); +} + +.theme-chip-dynamic.active { + background-color: @accent; +} + +.theme-chip-dynamic.active .theme-chip-label, +.theme-chip-dynamic.active .theme-chip-icon { + color: @background; +} + +.theme-chip-icon { + font-size: 16px; + color: @accent; +} + +.theme-swatches { + min-height: 36px; + border-radius: 6px; +} + +.theme-swatch { + min-width: 10px; +} + +.theme-chip-label { + font-size: 12px; + color: @foreground; +} + .qs-side-panel { min-width: 240px; } diff --git a/ags/widget/QuickSettings.tsx b/ags/widget/QuickSettings.tsx index 291780e..8800843 100644 --- a/ags/widget/QuickSettings.tsx +++ b/ags/widget/QuickSettings.tsx @@ -176,6 +176,14 @@ function Toggles() { { closePanel(); sh("hyprpicker -a") }} /> + { + closePanel() + sh("blob_theme") + }} + /> ) diff --git a/ags/widget/ThemePicker.tsx b/ags/widget/ThemePicker.tsx new file mode 100644 index 0000000..b7d8587 --- /dev/null +++ b/ags/widget/ThemePicker.tsx @@ -0,0 +1,164 @@ +import app from "ags/gtk3/app" +import { Astal, Gtk } from "ags/gtk3" +import { createPoll } from "ags/time" +import { For } from "ags" +import { sh, shell } from "../lib/utils" + +export const THEMEPICKER_WINDOW = "theme-picker" + +const COLUMNS = 3 + +type Theme = { slug: string; name: string; swatches: string[] } + +const listCommand = + "{ find \"$HOME/.config/omarchy/themes\" -mindepth 1 -maxdepth 1 -type d 2>/dev/null; " + + "find \"$OMARCHY_PATH/themes\" -mindepth 1 -maxdepth 1 -type d 2>/dev/null; } | " + + "while read -r d; do " + + "[ -f \"$d/colors.toml\" ] || continue; " + + "n=$(basename \"$d\"); " + + "[ \"$n\" = blob-dynamic ] && continue; " + + "c=$(grep -E '^color[1-6] *=' \"$d/colors.toml\" | sed -E 's/^color[0-9]+ *= *\"?([^\"]*)\"?.*/\\1/' | paste -sd,); " + + "p=$(echo \"$n\" | sed -E 's/(^|-)([a-z])/\\1\\u\\2/g; s/-/ /g'); " + + "echo \"$n|$p|$c\"; " + + "done | awk -F'|' '!seen[$1]++' | sort -t'|' -k2,2" + +const themes = createPoll( + [], + 5000, + shell(listCommand), + (stdout) => + stdout + .split("\n") + .filter((line) => line.length > 0) + .map((line) => { + const [slug, name, swatchStr] = line.split("|") + return { slug, name: name ?? slug, swatches: (swatchStr ?? "").split(",").filter(Boolean) } + }), +) + +const currentThemeName = createPoll( + "", + 2000, + shell("cat \"$HOME/.config/omarchy/current/theme.name\" 2>/dev/null"), + (stdout) => stdout.trim(), +) + +const isDynamic = currentThemeName.as((name) => name === "blob-dynamic") + +const rows = themes.as((list) => { + const chunks: Theme[][] = [] + for (let index = 0; index < list.length; index += COLUMNS) { + chunks.push(list.slice(index, index + COLUMNS)) + } + return chunks +}) + +const applyTheme = (slug: string) => { + app.toggle_window(THEMEPICKER_WINDOW) + sh(`"$HOME/scripts/blob_theme.sh" "${slug}"`) +} + +const applyDynamic = () => { + app.toggle_window(THEMEPICKER_WINDOW) + sh(`"$HOME/scripts/blob_theme.sh" --dynamic`) +} + +function Swatches({ colors }: { colors: string[] }) { + return ( + + {colors.map((color) => ( + + ))} + + ) +} + +function ThemeChip({ theme }: { theme: Theme }) { + const active = currentThemeName.as((name) => name === theme.slug) + return ( + + ) +} + +function DynamicChip() { + return ( + + ) +} + +export default function ThemePicker() { + const hasThemes = themes.as((list) => list.length > 0) + const isEmpty = themes.as((list) => list.length === 0) + + return ( + + + + + + + + + + row.map((t) => t.slug).join("|")}> + {(row: Theme[]) => ( + + {row.map((theme) => ( + + ))} + + )} + + + + + + + + + ) +} diff --git a/commands.md b/commands.md index aa99955..418232e 100644 --- a/commands.md +++ b/commands.md @@ -4,8 +4,8 @@ The installer automatically exposes scripts from [`scripts/`](scripts/) as globa | Command | Description | | --- | --- | -| `blob_wallpaper [path]` | Sets your background using Omarchy's background system. If used with an image from `~/wallpapers/` or a valid path, it leverages Pywal to generate a full system color palette and dynamically updates the `blob-dynamic` theme, AGS widgets, and Waybar. With no argument, it opens the AGS wallpaper picker. | -| `blob_theme ` | Pulls a color palette shared from the wall-styles site and applies it as the `blob-dynamic` theme (e.g. `blob_theme "https://wall-styles.vercel.app/?id=ab12cd34ef"`). | +| `blob_wallpaper [path]` | Sets your background using Omarchy's background system. If used with an image from `~/wallpapers/` or a valid path, it leverages Pywal to generate a full system color palette into the `blob-dynamic` theme and always updates the desktop background — but only switches your active color theme to it if you're already in dynamic mode (see `blob_theme --mode`), so it won't pull you out of a static theme. With no argument, it opens the AGS wallpaper picker. | +| `blob_theme [name\|--dynamic\|--mode\|--print\|share-link-or-id]` | Manages color themes. With no argument, opens the AGS theme picker. `--dynamic` recolors from the current wallpaper without changing it; `--mode` prints `static` or `dynamic`; `--print` dumps the currently active theme's `colors.toml` to stdout, e.g. `blob_theme --print > themes/my-theme/colors.toml` to save a dynamically-generated palette you like. A `name` applies a local theme (see [`themes/`](themes/README.md)) or, if no local theme matches, falls back to pulling a shared palette from the wall-styles site by link or id (e.g. `blob_theme "https://wall-styles.vercel.app/?id=ab12cd34ef"`). | | `blob_glass [on\|off\|toggle]` | A quick toggle to enable or disable window transparency on the fly. | | `blob_boot [path]` | Safely updates your Plymouth boot splash image (defaults to `branding/boot_flash.png`) and rebuilds the `initramfs` (GRUB compatible via `mkinitcpio`). | | `blob_wifi` | A streamlined script to connect to the GMU Eduroam Wi-Fi network using `iwd` and `systemd-resolved` (replaces NetworkManager). | diff --git a/elephant/menus/blob_theme_selector.lua b/elephant/menus/blob_theme_selector.lua new file mode 100644 index 0000000..977e138 --- /dev/null +++ b/elephant/menus/blob_theme_selector.lua @@ -0,0 +1,76 @@ +Name = "blobThemeSelector" +NamePretty = "Blob's Theme Selector" +Cache = false +HideFromProviderlist = true +SearchName = true + +local function ShellEscape(s) + return "'" .. s:gsub("'", "'\\''") .. "'" +end + +function FormatName(slug) + local name = slug:gsub("-", " ") + name = name:gsub("%S+", function(word) + return word:sub(1, 1):upper() .. word:sub(2):lower() + end) + return name +end + +function GetEntries() + local entries = {} + local home = os.getenv("HOME") + local omarchy_path = os.getenv("OMARCHY_PATH") or "" + + entries[1] = { + Text = "Dynamic (from wallpaper)", + Value = "dynamic", + Actions = { + activate = "blob_theme --dynamic", + }, + } + + local dirs = { + home .. "/.config/omarchy/themes", + omarchy_path .. "/themes", + } + + local seen = { ["blob-dynamic"] = true } + + for _, themes_dir in ipairs(dirs) do + local handle = io.popen( + "find " .. ShellEscape(themes_dir) .. " -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort" + ) + if handle then + for theme_dir in handle:lines() do + local slug = theme_dir:match("([^/]+)$") + if slug and not seen[slug] then + local colors_file = io.open(theme_dir .. "/colors.toml", "r") + if colors_file then + colors_file:close() + seen[slug] = true + + local entry = { + Text = FormatName(slug), + Value = slug, + Actions = { + activate = "blob_theme " .. ShellEscape(slug), + }, + } + + local preview_file = io.open(theme_dir .. "/preview.png", "r") + if preview_file then + preview_file:close() + entry.Preview = theme_dir .. "/preview.png" + entry.PreviewType = "file" + end + + table.insert(entries, entry) + end + end + end + handle:close() + end + end + + return entries +end diff --git a/install.sh b/install.sh index b338ee7..a30515a 100755 --- a/install.sh +++ b/install.sh @@ -69,7 +69,7 @@ backup_and_copy() { if [ ! -e "$dest" ]; then echo "[COPY] $name: New directory (creating)" - mkdir -p "$(dirname "$dest")" + mkdir -p "$dest" cp -r "$src"/* "$dest"/ return fi @@ -87,7 +87,7 @@ backup_and_copy() { rm -rf "$dest.bak" mkdir -p "$dest.bak" cp -r "$dest"/* "$dest.bak/" 2>/dev/null || true - + echo "[COPY] $name: Overwriting (--force)" cp -r "$src"/* "$dest"/ else @@ -95,6 +95,40 @@ backup_and_copy() { fi } +# Like backup_and_copy, but for destinations that are pure git-tracked +# mirrors with no local state worth protecting (wallpapers, themes). +# No .bak sibling is created — a stale one would otherwise show up +# alongside the real thing (e.g. in the theme picker's directory scan). +replace_and_copy() { + local src="$1" + local dest="$2" + local name="$3" + + if [ ! -e "$dest" ]; then + echo "[COPY] $name: New directory (creating)" + mkdir -p "$dest" + cp -r "$src"/* "$dest"/ + return + fi + + local src_hash=$(compute_hash "$src") + local dest_hash=$(compute_hash "$dest") + + if [ "$src_hash" = "$dest_hash" ]; then + echo "[SKIP] $name: Up to date" + return + fi + + if [ "$FORCE" = true ]; then + echo "[COPY] $name: Replacing (--force)" + rm -rf "$dest" + mkdir -p "$dest" + cp -r "$src"/* "$dest"/ + else + echo "[SKIP] $name: Has local changes (use --force to overwrite)" + fi +} + install_dependencies() { echo "=== Checking Dependencies ===" local deps_needed=() @@ -146,10 +180,12 @@ check_file "$SCRIPT_DIR/ags/widget/Media.tsx" "$HOME_DIR/.config/ags/widget/Medi check_file "$SCRIPT_DIR/ags/widget/Notifications.tsx" "$HOME_DIR/.config/ags/widget/Notifications.tsx" "ags/widget/Notifications.tsx" || check_status=1 check_file "$SCRIPT_DIR/ags/widget/QuickSettings.tsx" "$HOME_DIR/.config/ags/widget/QuickSettings.tsx" "ags/widget/QuickSettings.tsx" || check_status=1 check_file "$SCRIPT_DIR/ags/widget/SysMonitor.tsx" "$HOME_DIR/.config/ags/widget/SysMonitor.tsx" "ags/widget/SysMonitor.tsx" || check_status=1 +check_file "$SCRIPT_DIR/ags/widget/ThemePicker.tsx" "$HOME_DIR/.config/ags/widget/ThemePicker.tsx" "ags/widget/ThemePicker.tsx" || check_status=1 check_file "$SCRIPT_DIR/ags/widget/WallPicker.tsx" "$HOME_DIR/.config/ags/widget/WallPicker.tsx" "ags/widget/WallPicker.tsx" || check_status=1 check_file "$SCRIPT_DIR/ags/widget/WidgetCard.tsx" "$HOME_DIR/.config/ags/widget/WidgetCard.tsx" "ags/widget/WidgetCard.tsx" || check_status=1 check_file "$SCRIPT_DIR/waybar/style.css" "$HOME_DIR/.config/waybar/style.css" "waybar/style.css" || check_status=1 check_file "$SCRIPT_DIR/elephant/menus/blob_background_selector.lua" "$HOME_DIR/.config/elephant/menus/blob_background_selector.lua" "elephant/menus/blob_background_selector.lua" || check_status=1 +check_file "$SCRIPT_DIR/elephant/menus/blob_theme_selector.lua" "$HOME_DIR/.config/elephant/menus/blob_theme_selector.lua" "elephant/menus/blob_theme_selector.lua" || check_status=1 check_file "$SCRIPT_DIR/branding/about.txt" "$HOME_DIR/.config/omarchy/branding/about.txt" "branding/about.txt" || check_status=1 check_file "$SCRIPT_DIR/branding/screensaver.txt" "$HOME_DIR/.config/omarchy/branding/screensaver.txt" "branding/screensaver.txt" || check_status=1 @@ -165,6 +201,14 @@ for script in "$SCRIPT_DIR/scripts"/*.sh; do fi done +if [ -d "$SCRIPT_DIR/themes" ]; then + for theme_dir in "$SCRIPT_DIR/themes"/*/; do + [ -d "$theme_dir" ] || continue + theme_name=$(basename "$theme_dir") + check_file "$theme_dir/colors.toml" "$HOME_DIR/.config/omarchy/themes/$theme_name/colors.toml" "themes/$theme_name/colors.toml" || check_status=1 + done +fi + echo "" if [ "$CHECK" = true ]; then @@ -191,7 +235,15 @@ backup_and_copy "$SCRIPT_DIR/hypr" "$HOME_DIR/.config/hypr" "Hyprland config" backup_and_copy "$SCRIPT_DIR/branding" "$HOME_DIR/.config/omarchy/branding" "Branding files" backup_and_copy "$SCRIPT_DIR/elephant" "$HOME_DIR/.config/elephant" "Elephant configs" backup_and_copy "$SCRIPT_DIR/omarchy/hooks" "$HOME_DIR/.config/omarchy/hooks" "Omarchy hooks" -backup_and_copy "$SCRIPT_DIR/wallpapers" "$HOME_DIR/wallpapers" "Custom wallpapers" +replace_and_copy "$SCRIPT_DIR/wallpapers" "$HOME_DIR/wallpapers" "Custom wallpapers" + +if [ -d "$SCRIPT_DIR/themes" ]; then + for theme_dir in "$SCRIPT_DIR/themes"/*/; do + [ -d "$theme_dir" ] || continue + theme_name=$(basename "$theme_dir") + replace_and_copy "$theme_dir" "$HOME_DIR/.config/omarchy/themes/$theme_name" "Theme: $theme_name" + done +fi zen_profile=$(find "$HOME_DIR/.config/zen" -maxdepth 1 -type d -name "*.Default (release)*" 2>/dev/null | head -n 1) if [ -n "$zen_profile" ]; then diff --git a/scripts/blob_theme.sh b/scripts/blob_theme.sh index e96ac47..50e1fe1 100644 --- a/scripts/blob_theme.sh +++ b/scripts/blob_theme.sh @@ -1,52 +1,119 @@ #!/bin/bash -# Pull a color theme shared from the wall-styles website and apply it. +# Manage color themes: local static themes installed under +# ~/.config/omarchy/themes/, the pywal-generated dynamic theme +# (blob-dynamic), and themes shared from the wall-styles website. THEME_DIR="$HOME/.config/omarchy/themes/blob-dynamic" +USER_THEMES_DIR="$HOME/.config/omarchy/themes" +CURRENT_DIR="$HOME/.config/omarchy/current" # Used only when a bare id is passed instead of a full share link. # Override with: export BLOB_THEME_URL="https://your-deployment.vercel.app" BASE_URL="${BLOB_THEME_URL:-https://wall-styles.vercel.app}" -if [ -z "$1" ]; then - echo "Usage: blob_theme " - echo "Example: blob_theme \"https://wall-styles.vercel.app/?id=ab12cd34ef\"" - exit 1 -fi +slugify() { + echo "$1" | tr '[:upper:]' '[:lower:]' | tr ' ' '-' +} -INPUT="$1" +current_mode() { + local name + name=$(cat "$CURRENT_DIR/theme.name" 2>/dev/null) + if [ "$name" = "blob-dynamic" ]; then + echo "dynamic" + else + echo "static" + fi +} -if [[ "$INPUT" == http*://* ]]; then - HOST=$(echo "$INPUT" | sed -E 's#^(https?://[^/]+).*#\1#') - ID=$(echo "$INPUT" | grep -oE 'id=[A-Za-z0-9]+' | head -1 | cut -d= -f2) - if [ -z "$ID" ]; then - echo "Error: no theme id found in link '$INPUT'." +theme_exists_locally() { + local slug + slug=$(slugify "$1") + [ -d "$USER_THEMES_DIR/$slug" ] || [ -d "$OMARCHY_PATH/themes/$slug" ] +} + +apply_static() { + local name="$1" + # Clean up a lingering gif-animation daemon from a previous dynamic + # wallpaper before switching to a static theme's own background. + awww kill --all >/dev/null 2>&1 + omarchy-theme-set "$name" +} + +apply_dynamic() { + if [ ! -f "$THEME_DIR/colors.toml" ]; then + echo "No dynamic theme yet. Pick a wallpaper first: blob_wallpaper" exit 1 fi - API="$HOST/api/theme?id=$ID&format=toml" -else - API="$BASE_URL/api/theme?id=$INPUT&format=toml" -fi + # Skip the background step: this is purely a recolor of whatever + # wallpaper is already showing, not a wallpaper change. + OMARCHY_THEME_SKIP_BACKGROUND=1 omarchy-theme-set "blob-dynamic" +} -mkdir -p "$THEME_DIR/backgrounds" +case "$1" in + "") + ags toggle theme-picker + ;; + --menu) + omarchy-launch-walker -m menus:blobThemeSelector --width 800 --minheight 400 -p "Select Theme…" + ;; + --mode) + current_mode + ;; + --print) + CURRENT_COLORS="$CURRENT_DIR/theme/colors.toml" + if [ ! -f "$CURRENT_COLORS" ]; then + echo "No active theme colors.toml found at $CURRENT_COLORS" >&2 + exit 1 + fi + # awk always terminates the last line with a newline, even if the + # source file doesn't. Omarchy's template engine reads colors.toml + # with a bash `while read` loop, which silently drops a final line + # missing its newline (e.g. color15) — some built-in themes are + # missing it too, so this guards every theme saved via --print. + awk '1' "$CURRENT_COLORS" + ;; + --dynamic) + apply_dynamic + ;; + *) + if theme_exists_locally "$1"; then + apply_static "$1" + else + # Not a local theme: treat it as a wall-styles share link or id. + INPUT="$1" -echo "Fetching theme..." -TMP=$(mktemp) -if ! curl -fsSL "$API" -o "$TMP"; then - echo "Error: could not fetch theme. The link may be expired (themes last one hour)." - rm -f "$TMP" - exit 1 -fi + if [[ "$INPUT" == http*://* ]]; then + HOST=$(echo "$INPUT" | sed -E 's#^(https?://[^/]+).*#\1#') + ID=$(echo "$INPUT" | grep -oE 'id=[A-Za-z0-9]+' | head -1 | cut -d= -f2) + if [ -z "$ID" ]; then + echo "Error: '$INPUT' is not a local theme, and no share id was found in the link." + exit 1 + fi + API="$HOST/api/theme?id=$ID&format=toml" + else + API="$BASE_URL/api/theme?id=$INPUT&format=toml" + fi -if ! grep -q '^background = ' "$TMP"; then - echo "Error: the response did not look like a valid colors.toml." - rm -f "$TMP" - exit 1 -fi + mkdir -p "$THEME_DIR/backgrounds" -mv "$TMP" "$THEME_DIR/colors.toml" + echo "Fetching theme..." + TMP=$(mktemp) + if ! curl -fsSL "$API" -o "$TMP"; then + echo "Error: could not fetch theme, and '$1' is not a local theme name. The link may be expired (themes last one hour)." + rm -f "$TMP" + exit 1 + fi -cat < "$THEME_DIR/neovim.lua" + if ! grep -q '^background = ' "$TMP"; then + echo "Error: the response did not look like a valid colors.toml." + rm -f "$TMP" + exit 1 + fi + + mv "$TMP" "$THEME_DIR/colors.toml" + + cat < "$THEME_DIR/neovim.lua" return { { "LazyVim/LazyVim", @@ -57,7 +124,10 @@ return { } EOF -# Apply the Blob-Dynamic theme (reloads waybar, ags, etc.) -omarchy-theme-set "blob-dynamic" + # Apply the Blob-Dynamic theme (reloads waybar, ags, etc.) + omarchy-theme-set "blob-dynamic" -echo "Applied shared theme to blob-dynamic." + echo "Applied shared theme to blob-dynamic." + fi + ;; +esac diff --git a/scripts/blob_wallpaper.sh b/scripts/blob_wallpaper.sh index 3898a78..49a4c48 100755 --- a/scripts/blob_wallpaper.sh +++ b/scripts/blob_wallpaper.sh @@ -85,10 +85,19 @@ return { } EOF -# Apply the Blob-Dynamic theme, but skip its own background step: it +# Only switch the active color theme to blob-dynamic if you're already +# in dynamic mode. The palette above and the background below are kept +# up to date regardless, so `blob_theme --dynamic` always reflects the +# latest wallpaper — but picking a wallpaper while on a static theme +# should change the wallpaper, not silently pull you out of it. +# +# When it does apply, skip omarchy-theme-set's own background step: it # launches swaybg asynchronously, which races with the gif handling # below and can leave a static swaybg frame on top of an animated gif. -OMARCHY_THEME_SKIP_BACKGROUND=1 omarchy-theme-set "blob-dynamic" +CURRENT_THEME_NAME=$(cat "$HOME/.config/omarchy/current/theme.name" 2>/dev/null) +if [ "$CURRENT_THEME_NAME" = "blob-dynamic" ]; then + OMARCHY_THEME_SKIP_BACKGROUND=1 omarchy-theme-set "blob-dynamic" +fi CURRENT_BACKGROUND_LINK="$HOME/.config/omarchy/current/background" ln -nsf "$THEME_DIR/backgrounds/$(basename "$IMAGE_PATH")" "$CURRENT_BACKGROUND_LINK" diff --git a/themes/README.md b/themes/README.md new file mode 100644 index 0000000..a552423 --- /dev/null +++ b/themes/README.md @@ -0,0 +1,60 @@ +# Themes + +Drop-in static color themes. Each subdirectory here becomes a theme +selectable from `blob_theme`, the theme picker widget, and the walker +menu, once `install.sh` copies it into `~/.config/omarchy/themes/`. + +## Adding a theme + +Create a directory named after the theme (lowercase, hyphens instead +of spaces — e.g. `tokyo-night/`) containing a `colors.toml`: + +```toml +accent = "#7aa2f7" +cursor = "#c0caf5" +foreground = "#c0caf5" +background = "#1a1b26" +selection_foreground = "#1a1b26" +selection_background = "#7aa2f7" + +color0 = "#1a1b26" +color1 = "#f7768e" +color2 = "#9ece6a" +color3 = "#e0af68" +color4 = "#7aa2f7" +color5 = "#bb9af7" +color6 = "#7dcfff" +color7 = "#c0caf5" +color8 = "#414868" +color9 = "#f7768e" +color10 = "#9ece6a" +color11 = "#e0af68" +color12 = "#7aa2f7" +color13 = "#bb9af7" +color14 = "#7dcfff" +color15 = "#c0caf5" +``` + +All 22 keys are required — this is the same format Omarchy themes and +`blob_wallpaper`'s pywal-generated `blob-dynamic` theme already use, so +`omarchy-theme-set` and its app-config templates pick it up with no +extra work. + +### Saving a dynamic palette you like + +If a wallpaper's pywal-generated colors are worth keeping permanently, +dump the currently active theme's `colors.toml` straight into a new +theme folder instead of typing it out by hand: + +```bash +mkdir -p themes/my-new-theme +blob_theme --print > themes/my-new-theme/colors.toml +``` + +Run `./install.sh` (add `--force` if the theme already exists on disk +and you're updating it) to deploy new/changed theme folders. + +Any theme already installed under `~/.config/omarchy/themes/` or +bundled with Omarchy itself (`$OMARCHY_PATH/themes/`) shows up +automatically too — this directory is just for ones you want tracked +in dotfiles. diff --git a/themes/flats/colors.toml b/themes/flats/colors.toml new file mode 100644 index 0000000..4718f2a --- /dev/null +++ b/themes/flats/colors.toml @@ -0,0 +1,23 @@ +accent = "#7babe3" +cursor = "#cbd2d9" +foreground = "#cbd2d9" +background = "#0f1012" +selection_foreground = "#0f1012" +selection_background = "#7babe3" + +color0 = "#000000" +color1 = "#d53838" +color2 = "#38d4d5" +color3 = "#d58638" +color4 = "#d53886" +color5 = "#38d586" +color6 = "#3886d5" +color7 = "#d4d4d4" +color8 = "#949494" +color9 = "#d53838" +color10 = "#38d4d5" +color11 = "#d58638" +color12 = "#d53886" +color13 = "#38d586" +color14 = "#3886d5" +color15 = "#d4d4d4" diff --git a/themes/pitch-dark/colors.toml b/themes/pitch-dark/colors.toml new file mode 100644 index 0000000..cf6db88 --- /dev/null +++ b/themes/pitch-dark/colors.toml @@ -0,0 +1,23 @@ +accent = "#d53838" +cursor = "#d4d4d4" +foreground = "#d4d4d4" +background = "#000000" +selection_foreground = "#000000" +selection_background = "#d53838" + +color0 = "#000000" +color1 = "#d53838" +color2 = "#38d4d5" +color3 = "#d58638" +color4 = "#d53886" +color5 = "#38d586" +color6 = "#3886d5" +color7 = "#d4d4d4" +color8 = "#949494" +color9 = "#d53838" +color10 = "#38d4d5" +color11 = "#d58638" +color12 = "#d53886" +color13 = "#38d586" +color14 = "#3886d5" +color15 = "#d4d4d4"