mirror of
https://github.com/basecamp/omarchy.git
synced 2026-02-17 15:25:37 +00:00
* feat: obsidian theme to tie into Omarchy theme * better color matching for search result matches * feat: simplify install flow * Removed install/remove scripts and flow * First `omarchy-theme-set-obsidian` run will look for vaults in Documents and Dropbox folders and add them to `/.local/state/omarchy/obsidian-vaults` * Point of this is such that we assume a couple locations to look for vaults but allow people to add vaults to the file for custom locations. Subsequent theme-set invocations aren't impacted by find/search on large systems * Each `omarchy-theme-set` invocation will install the themes on all found/registered vaults if missing and update the live theme * Added an option of `omarchy-theme-set-obsidian --reset` to wipe out themes in registered vaults and remove the registry file. This would be the option to re-run to the automatic vault registration. * Added migration to trigger install immediately in Obisdian, note that you still need to pick Omarchy theme.
660 lines
23 KiB
Bash
Executable File
660 lines
23 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
# omarchy-theme-set-obsidian: Bootstrap and update Omarchy theme for Obsidian
|
||
#
|
||
# - Ensures registry at ~/.local/state/omarchy/obsidian-vaults
|
||
# - If missing/empty, bootstraps by scanning ~/Documents/*/.obsidian and ~/Dropbox/*/.obsidian
|
||
# - For each valid vault:
|
||
# - Ensures .obsidian/themes/Omarchy/{manifest.json, theme.css}
|
||
# - Updates theme.css (uses current theme’s obsidian.css if present; otherwise generates -- see below)
|
||
|
||
# Theme automagic generation logic:
|
||
#
|
||
# - Background/foreground: read from ~/.config/omarchy/current/theme/alacritty.toml [colors.primary]
|
||
# (background/foreground). Fallbacks: bg=#1a1b26, fg=#a9b1d6. Compute bg brightness for light/dark handling.
|
||
# - Palette extraction: collect colors from Alacritty (primary/normal/bright/dim/selection), Waybar (@define-color),
|
||
# and Hyprland (col.*_border; rgba->hex). Normalize, dedupe, and count frequencies.
|
||
# - Slot ordering: remove bg/fg, sort remaining colors by frequency, then fill 13 slots by cycling. Map slots to:
|
||
# h1–h6, links, inline code, marks, interactive accent, blockquote border; muted/faint use border color.
|
||
# - Code colors: code background = closest color to bg (Euclidean RGB); if none, make a subtle bg variant (+/− RGB).
|
||
# code foreground = closest color to fg; fallback #e0e0e0.
|
||
# - Border color: from btop.theme theme[div_line]; else blended mix biased toward bg (≈ (bg+fg)/3).
|
||
# - Selection: from Alacritty [colors.selection] (background/text), honoring CellForeground/Background.
|
||
# If missing, background = 75% bg + 25% fg; text chosen for contrast vs selection background.
|
||
# - Fonts: monospace from Alacritty [font] or fontconfig monospace; UI font from fontconfig sans-serif.
|
||
|
||
VAULTS_FILE="$HOME/.local/state/omarchy/obsidian-vaults"
|
||
CURRENT_THEME_DIR="$HOME/.config/omarchy/current/theme"
|
||
|
||
# Ensure the vaults registry exists, or bootstrap from known locations
|
||
ensure_vaults_file() {
|
||
mkdir -p "$(dirname "$VAULTS_FILE")"
|
||
# If file exists (even empty), do not scan; treat as authoritative
|
||
if [ -f "$VAULTS_FILE" ]; then
|
||
return
|
||
fi
|
||
local tmpfile
|
||
tmpfile="$(mktemp)"
|
||
# Scan a couple of common locations for <base>/<vault>/.obsidian
|
||
for base in "$HOME/Documents" "$HOME/Dropbox"; do
|
||
[ -d "$base" ] || continue
|
||
for d in "$base"/*/.obsidian; do
|
||
[ -d "$d" ] || continue
|
||
vault_dir="${d%/.obsidian}"
|
||
printf "%s\n" "$vault_dir" >>"$tmpfile"
|
||
done
|
||
done
|
||
if [ -s "$tmpfile" ]; then
|
||
sort -u "$tmpfile" >"$VAULTS_FILE"
|
||
else
|
||
: >"$VAULTS_FILE"
|
||
fi
|
||
rm -f "$tmpfile"
|
||
}
|
||
|
||
# Ensure theme directory and minimal manifest exist in a vault
|
||
ensure_theme_scaffold() {
|
||
local vault_path="$1"
|
||
local theme_dir="$vault_path/.obsidian/themes/Omarchy"
|
||
mkdir -p "$theme_dir"
|
||
if [ ! -f "$theme_dir/manifest.json" ]; then
|
||
cat >"$theme_dir/manifest.json" <<'EOF'
|
||
{
|
||
"name": "Omarchy",
|
||
"version": "1.0.0",
|
||
"minAppVersion": "0.16.0",
|
||
"description": "Automatically syncs with your current Omarchy system theme colors and fonts",
|
||
"author": "Omarchy",
|
||
"authorUrl": "https://omarchy.org"
|
||
}
|
||
EOF
|
||
fi
|
||
[ -f "$theme_dir/theme.css" ] || : >"$theme_dir/theme.css"
|
||
}
|
||
|
||
# Function to extract hex color from string
|
||
extract_hex_color() {
|
||
echo "$1" | grep -oE '#[0-9a-fA-F]{6}' | head -1
|
||
}
|
||
|
||
# Function to convert RGB/RGBA to hex
|
||
rgb_to_hex() {
|
||
local rgb_string="$1"
|
||
if [[ $rgb_string =~ rgba?\(([0-9]+),\s*([0-9]+),\s*([0-9]+) ]]; then
|
||
printf "#%02x%02x%02x\n" "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}"
|
||
fi
|
||
}
|
||
|
||
# Convert hex to RGB components
|
||
hex_to_rgb() {
|
||
local hex="${1#\#}"
|
||
printf "%d %d %d\n" "0x${hex:0:2}" "0x${hex:2:2}" "0x${hex:4:2}"
|
||
}
|
||
|
||
# Calculate perceived brightness (0-255)
|
||
calculate_brightness() {
|
||
local hex="$1"
|
||
read -r r g b <<<"$(hex_to_rgb "$hex")"
|
||
# Use perceived brightness formula
|
||
echo $(((r * 299 + g * 587 + b * 114) / 1000))
|
||
}
|
||
|
||
# Calculate color distance (euclidean in RGB space)
|
||
color_distance() {
|
||
local hex1="$1"
|
||
local hex2="$2"
|
||
read -r r1 g1 b1 <<<"$(hex_to_rgb "$hex1")"
|
||
read -r r2 g2 b2 <<<"$(hex_to_rgb "$hex2")"
|
||
echo $(((r1 - r2) * (r1 - r2) + (g1 - g2) * (g1 - g2) + (b1 - b2) * (b1 - b2)))
|
||
}
|
||
|
||
# Extract all colors with frequency count
|
||
extract_all_colors_with_count() {
|
||
local -A color_counts
|
||
local color
|
||
|
||
# Extract from Alacritty config
|
||
if [ -f "$CURRENT_THEME_DIR/alacritty.toml" ]; then
|
||
# Primary colors
|
||
while IFS= read -r color; do
|
||
color=$(echo "$color" | tr '[:upper:]' '[:lower:]') # Lowercase for consistency
|
||
[ -n "$color" ] && ((color_counts["$color"]++))
|
||
done < <(grep -E "(background|foreground|cursor|text)" "$CURRENT_THEME_DIR/alacritty.toml" | sed "s/.*[\"']0x/#/;s/.*[\"']#/#/;s/[\"'].*//;s/.*#\([0-9a-fA-F]\{6\}\).*/\#\1/" | grep "^#")
|
||
|
||
# Normal colors
|
||
while IFS= read -r color; do
|
||
color=$(echo "$color" | tr '[:upper:]' '[:lower:]') # Lowercase for consistency
|
||
[ -n "$color" ] && ((color_counts["$color"]++))
|
||
done < <(grep -A 20 "\[colors.normal\]" "$CURRENT_THEME_DIR/alacritty.toml" | grep -E "(black|red|green|yellow|blue|magenta|cyan|white)" | sed "s/.*[\"']0x/#/;s/.*[\"']#/#/;s/[\"'].*//;s/.*#\([0-9a-fA-F]\{6\}\).*/\#\1/" | grep "^#")
|
||
|
||
# Bright colors
|
||
while IFS= read -r color; do
|
||
# Add # if missing
|
||
[[ "$color" =~ ^[0-9a-fA-F]{6}$ ]] && color="#$color"
|
||
color=$(echo "$color" | tr '[:upper:]' '[:lower:]') # Lowercase for consistency
|
||
[[ "$color" =~ ^#[0-9a-f]{6}$ ]] && ((color_counts["$color"]++))
|
||
done < <(grep -A 20 "\[colors.bright\]" "$CURRENT_THEME_DIR/alacritty.toml" | grep -E "(black|red|green|yellow|blue|magenta|cyan|white)" | sed "s/.*[\"']//;s/[\"'].*//")
|
||
|
||
# Dim colors if present
|
||
while IFS= read -r color; do
|
||
# Add # if missing
|
||
[[ "$color" =~ ^[0-9a-fA-F]{6}$ ]] && color="#$color"
|
||
color=$(echo "$color" | tr '[:upper:]' '[:lower:]') # Lowercase for consistency
|
||
[[ "$color" =~ ^#[0-9a-f]{6}$ ]] && ((color_counts["$color"]++))
|
||
done < <(grep -A 20 "\[colors.dim\]" "$CURRENT_THEME_DIR/alacritty.toml" 2>/dev/null | grep -E "(black|red|green|yellow|blue|magenta|cyan|white)" | sed "s/.*[\"']//;s/[\"'].*//")
|
||
|
||
# Selection colors
|
||
while IFS= read -r color; do
|
||
color=$(echo "$color" | tr '[:upper:]' '[:lower:]') # Lowercase for consistency
|
||
[ -n "$color" ] && ((color_counts["$color"]++))
|
||
done < <(grep -A 5 "\[colors.selection\]" "$CURRENT_THEME_DIR/alacritty.toml" 2>/dev/null | grep -E "(background|text)" | sed "s/.*[\"']0x/#/;s/.*[\"']#/#/;s/[\"'].*//;s/.*#\([0-9a-fA-F]\{6\}\).*/\#\1/" | grep "^#")
|
||
fi
|
||
|
||
# Extract from Waybar CSS
|
||
if [ -f "$CURRENT_THEME_DIR/waybar.css" ]; then
|
||
while IFS= read -r color; do
|
||
color=$(echo "$color" | tr '[:upper:]' '[:lower:]') # Lowercase for consistency
|
||
[ -n "$color" ] && ((color_counts["$color"]++))
|
||
done < <(grep -oE '@define-color [a-z_-]+ #[0-9a-fA-F]{6}' "$CURRENT_THEME_DIR/waybar.css" | grep -oE '#[0-9a-fA-F]{6}')
|
||
fi
|
||
|
||
# Extract from Hyprland config
|
||
if [ -f "$CURRENT_THEME_DIR/hyprland.conf" ]; then
|
||
while IFS= read -r color; do
|
||
if [[ $color == rgba* ]] || [[ $color == rgb* ]]; then
|
||
color=$(rgb_to_hex "$color")
|
||
fi
|
||
color=$(echo "$color" | tr '[:upper:]' '[:lower:]') # Lowercase for consistency
|
||
[ -n "$color" ] && ((color_counts["$color"]++))
|
||
done < <(grep -E "col\.(active|inactive)_border" "$CURRENT_THEME_DIR/hyprland.conf" | grep -oE 'rgba?\([^)]+\)|#[0-9a-fA-F]{6,8}' | sed 's/ff$//')
|
||
fi
|
||
|
||
# Output colors with their counts
|
||
for color in "${!color_counts[@]}"; do
|
||
echo "${color_counts[$color]} $color"
|
||
done
|
||
}
|
||
|
||
# Sort colors by frequency
|
||
sort_colors_by_frequency() {
|
||
# Input is already "count color" format
|
||
sort -rn | cut -d' ' -f2
|
||
}
|
||
|
||
# Fill color slots with cycling if needed
|
||
fill_color_slots() {
|
||
local -a colors=("$@")
|
||
local -a slots
|
||
local num_colors=${#colors[@]}
|
||
|
||
# Need 13 slots total (code colors are handled separately)
|
||
local slots_needed=13
|
||
|
||
if [ $num_colors -eq 0 ]; then
|
||
# No colors available, use defaults
|
||
colors=("#3d3d3d" "#5d5d5d" "#7d7d7d" "#9d9d9d" "#bd93f9" "#50fa7b")
|
||
num_colors=6
|
||
fi
|
||
|
||
# Fill slots, cycling if necessary
|
||
for ((i = 0; i < slots_needed; i++)); do
|
||
slots[$i]="${colors[$((i % num_colors))]}"
|
||
done
|
||
|
||
echo "${slots[@]}"
|
||
}
|
||
|
||
# Main color extraction and theme generation
|
||
extract_theme_data() {
|
||
# Get primary colors from Alacritty
|
||
local bg_color="#1a1b26"
|
||
local fg_color="#a9b1d6"
|
||
|
||
if [ -f "$CURRENT_THEME_DIR/alacritty.toml" ]; then
|
||
local extracted_bg=$(grep -A 5 "\[colors.primary\]" "$CURRENT_THEME_DIR/alacritty.toml" | grep "^background = " | sed "s/.*[\"']0x/#/;s/.*[\"']#/#/;s/[\"'].*//;s/.*#\([0-9a-fA-F]\{6\}\).*/\#\1/" | head -1 | tr '[:upper:]' '[:lower:]')
|
||
local extracted_fg=$(grep -A 5 "\[colors.primary\]" "$CURRENT_THEME_DIR/alacritty.toml" | grep "^foreground = " | sed "s/.*[\"']0x/#/;s/.*[\"']#/#/;s/[\"'].*//;s/.*#\([0-9a-fA-F]\{6\}\).*/\#\1/" | head -1 | tr '[:upper:]' '[:lower:]')
|
||
[ -n "$extracted_bg" ] && bg_color="$extracted_bg"
|
||
[ -n "$extracted_fg" ] && fg_color="$extracted_fg"
|
||
fi
|
||
|
||
# Determine if light or dark theme
|
||
local bg_brightness=$(calculate_brightness "$bg_color")
|
||
local is_light_theme=false
|
||
[ $bg_brightness -gt 127 ] && is_light_theme=true
|
||
|
||
# Extract all colors with counts
|
||
local color_data=$(extract_all_colors_with_count)
|
||
|
||
# Filter out background and foreground colors for the main array
|
||
local filtered_data=$(echo "$color_data" | grep -v "$bg_color" | grep -v "$fg_color")
|
||
|
||
# Get all unique colors (including bg/fg) for distance calculations
|
||
local -a all_unique_colors
|
||
readarray -t all_unique_colors < <(echo "$color_data" | cut -d' ' -f2 | sort -u)
|
||
|
||
# Find the 3 closest colors to background for background variations
|
||
local -a bg_distances
|
||
for color in "${all_unique_colors[@]}"; do
|
||
if [ "$color" != "$bg_color" ]; then
|
||
distance=$(color_distance "$color" "$bg_color")
|
||
bg_distances+=("$distance:$color")
|
||
fi
|
||
done
|
||
|
||
# Sort by distance and get the closest color for code background
|
||
local -a closest_to_bg
|
||
readarray -t closest_to_bg < <(printf '%s\n' "${bg_distances[@]}" | sort -n | head -1 | cut -d: -f2)
|
||
|
||
# All background variations use the same as primary background
|
||
local bg_primary_alt="$bg_color"
|
||
local bg_secondary="$bg_color"
|
||
local bg_secondary_alt="$bg_color"
|
||
|
||
# Code block background uses the closest different color
|
||
local code_bg="${closest_to_bg[0]}"
|
||
|
||
# If no different color available, create a subtle variant for code blocks
|
||
if [ -z "$code_bg" ]; then
|
||
read -r r g b <<<"$(hex_to_rgb "$bg_color")"
|
||
if [ $bg_brightness -gt 127 ]; then
|
||
r=$((r - 10))
|
||
g=$((g - 10))
|
||
b=$((b - 10))
|
||
else
|
||
r=$((r + 15))
|
||
g=$((g + 15))
|
||
b=$((b + 15))
|
||
fi
|
||
[ $r -lt 0 ] && r=0
|
||
[ $r -gt 255 ] && r=255
|
||
[ $g -lt 0 ] && g=0
|
||
[ $g -gt 255 ] && g=255
|
||
[ $b -lt 0 ] && b=0
|
||
[ $b -gt 255 ] && b=255
|
||
code_bg=$(printf "#%02x%02x%02x" "$r" "$g" "$b")
|
||
fi
|
||
|
||
# Find closest color to foreground for code block text
|
||
local code_fg=""
|
||
min_distance=999999999
|
||
for color in "${all_unique_colors[@]}"; do
|
||
if [ "$color" != "$fg_color" ]; then
|
||
distance=$(color_distance "$color" "$fg_color")
|
||
if [ $distance -lt $min_distance ]; then
|
||
min_distance=$distance
|
||
code_fg="$color"
|
||
fi
|
||
fi
|
||
done
|
||
[ -z "$code_fg" ] && code_fg="#e0e0e0" # Fallback
|
||
|
||
# Extract text selection colors from Alacritty
|
||
local selection_bg=""
|
||
local selection_fg=""
|
||
if [ -f "$CURRENT_THEME_DIR/alacritty.toml" ]; then
|
||
selection_bg=$(grep -A 5 "\[colors.selection\]" "$CURRENT_THEME_DIR/alacritty.toml" | grep "^background = " | sed "s/.*[\"']0x/#/;s/.*[\"']#/#/;s/[\"'].*//;s/.*#\([0-9a-fA-F]\{6\}\).*/\#\1/" | head -1 | tr '[:upper:]' '[:lower:]')
|
||
local selection_text=$(grep -A 5 "\[colors.selection\]" "$CURRENT_THEME_DIR/alacritty.toml" | grep "^text = " | sed "s/.*[\"']0x/#/;s/.*[\"']#/#/;s/[\"'].*//;s/.*#\([0-9a-fA-F]\{6\}\).*/\#\1/" | head -1 | tr '[:upper:]' '[:lower:]')
|
||
|
||
# If text is set to CellForeground/CellBackground, use the appropriate color
|
||
if [ -z "$selection_text" ] || [[ "$(grep -A 5 "\[colors.selection\]" "$CURRENT_THEME_DIR/alacritty.toml" | grep "^text = ")" == *"CellForeground"* ]]; then
|
||
selection_fg="$fg_color"
|
||
elif [[ "$(grep -A 5 "\[colors.selection\]" "$CURRENT_THEME_DIR/alacritty.toml" | grep "^text = ")" == *"CellBackground"* ]]; then
|
||
selection_fg="$bg_color"
|
||
else
|
||
selection_fg="$selection_text"
|
||
fi
|
||
fi
|
||
|
||
# Fallback if no selection colors found
|
||
if [ -z "$selection_bg" ]; then
|
||
read -r r1 g1 b1 <<<"$(hex_to_rgb "$bg_color")"
|
||
read -r r2 g2 b2 <<<"$(hex_to_rgb "$fg_color")"
|
||
local r=$(((r1 * 3 + r2) / 4)) # 75% background, 25% foreground
|
||
local g=$(((g1 * 3 + g2) / 4))
|
||
local b=$(((b1 * 3 + b2) / 4))
|
||
selection_bg=$(printf "#%02x%02x%02x" "$r" "$g" "$b")
|
||
fi
|
||
|
||
# Use contrasting color for selection text if not defined
|
||
if [ -z "$selection_fg" ]; then
|
||
# Calculate brightness of selection background
|
||
local sel_brightness=$(calculate_brightness "$selection_bg")
|
||
if [ $sel_brightness -gt 127 ]; then
|
||
selection_fg="$bg_color" # Dark text on light selection
|
||
else
|
||
selection_fg="$fg_color" # Light text on dark selection
|
||
fi
|
||
fi
|
||
|
||
# Extract border color from btop theme
|
||
local border_color=""
|
||
if [ -f "$CURRENT_THEME_DIR/btop.theme" ]; then
|
||
# Look for theme[div_line] in btop theme
|
||
local btop_divline=$(grep 'theme\[div_line\]' "$CURRENT_THEME_DIR/btop.theme" | head -1)
|
||
|
||
if [ -n "$btop_divline" ]; then
|
||
# Extract the color value after the = sign
|
||
local extracted=$(echo "$btop_divline" | sed 's/.*=//' | xargs)
|
||
|
||
# Check if it's a hex color and lowercase it
|
||
if [[ $extracted =~ ^#[0-9a-fA-F]{6}$ ]]; then
|
||
border_color=$(echo "$extracted" | tr '[:upper:]' '[:lower:]')
|
||
elif [[ $extracted =~ ^[0-9a-fA-F]{6}$ ]]; then
|
||
# Add # if missing and lowercase
|
||
border_color=$(echo "#$extracted" | tr '[:upper:]' '[:lower:]')
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# Fallback if no border color found
|
||
if [ -z "$border_color" ]; then
|
||
# Use a color between bg and fg
|
||
read -r r1 g1 b1 <<<"$(hex_to_rgb "$bg_color")"
|
||
read -r r2 g2 b2 <<<"$(hex_to_rgb "$fg_color")"
|
||
local r=$(((r1 + r2) / 3)) # Closer to background
|
||
local g=$(((g1 + g2) / 3))
|
||
local b=$(((b1 + b2) / 3))
|
||
border_color=$(printf "#%02x%02x%02x" "$r" "$g" "$b")
|
||
fi
|
||
|
||
# Get unique colors array (without bg/fg) sorted by frequency
|
||
local -a unique_colors
|
||
readarray -t unique_colors < <(echo "$filtered_data" | sort_colors_by_frequency)
|
||
|
||
# Fill the 13 color slots (code colors handled separately)
|
||
local -a color_slots
|
||
readarray -t color_slots < <(fill_color_slots "${unique_colors[@]}" | tr ' ' '\n')
|
||
|
||
# Extract fonts
|
||
local monospace_font="CaskaydiaMono Nerd Font"
|
||
local ui_font="Liberation Sans"
|
||
|
||
if [ -f "$CURRENT_THEME_DIR/alacritty.toml" ]; then
|
||
local alacritty_font=$(grep -A 5 "\[font\]" "$CURRENT_THEME_DIR/alacritty.toml" | grep 'family = ' | head -1 | cut -d'"' -f2)
|
||
[ -n "$alacritty_font" ] && monospace_font="$alacritty_font"
|
||
fi
|
||
|
||
if [ -f "$HOME/.config/fontconfig/fonts.conf" ]; then
|
||
local fontconfig_mono=$(xmlstarlet sel -t -v '//match[@target="pattern"][test/string="monospace"]/edit[@name="family"]/string' "$HOME/.config/fontconfig/fonts.conf" 2>/dev/null || true)
|
||
[ -n "$fontconfig_mono" ] && monospace_font="$fontconfig_mono"
|
||
|
||
local fontconfig_sans=$(xmlstarlet sel -t -v '//match[@target="pattern"][test/string="sans-serif"]/edit[@name="family"]/string' "$HOME/.config/fontconfig/fonts.conf" 2>/dev/null || true)
|
||
[ -n "$fontconfig_sans" ] && ui_font="$fontconfig_sans"
|
||
fi
|
||
|
||
# Generate CSS with 14-slot system
|
||
cat <<EOF
|
||
/* Omarchy Theme for Obsidian */
|
||
/* Generated on $(date) from theme: $(basename "$(readlink "$CURRENT_THEME_DIR" 2>/dev/null || echo "unknown")") */
|
||
/* Colors sorted by frequency, backgrounds by distance */
|
||
|
||
.theme-dark, .theme-light {
|
||
/* Core colors */
|
||
--background-primary: $bg_color;
|
||
--text-normal: $fg_color;
|
||
|
||
/* Background variations (always distance-based) */
|
||
--background-primary-alt: $bg_primary_alt;
|
||
--background-secondary: $bg_secondary;
|
||
--background-secondary-alt: $bg_secondary_alt;
|
||
|
||
/* Code block colors (always distance-based) */
|
||
--code-background: $code_bg;
|
||
--code-foreground: $code_fg;
|
||
|
||
/* Border color from btop theme */
|
||
--border-color: $border_color;
|
||
|
||
/* Selection colors from Alacritty */
|
||
--text-selection: $selection_bg;
|
||
--text-selection-fg: $selection_fg;
|
||
|
||
/* 13-slot color system for remaining elements */
|
||
--text-title-h1: ${color_slots[0]};
|
||
--text-title-h2: ${color_slots[1]};
|
||
--text-title-h3: ${color_slots[2]};
|
||
--text-title-h4: ${color_slots[3]};
|
||
--text-title-h5: ${color_slots[4]};
|
||
--text-title-h6: ${color_slots[4]}; /* Same as h5 */
|
||
--text-link: ${color_slots[5]};
|
||
--markup-code: ${color_slots[6]};
|
||
--text-mark: ${color_slots[7]};
|
||
--interactive-accent: ${color_slots[8]};
|
||
--blockquote-border: ${color_slots[9]};
|
||
--text-muted: $border_color; /* Use border color for muted text */
|
||
--text-faint: $border_color; /* Use border color for faint text */
|
||
|
||
/* Additional mappings */
|
||
--text-accent: var(--interactive-accent);
|
||
--text-accent-hover: var(--interactive-accent);
|
||
--text-error: var(--text-title-h1);
|
||
--text-error-hover: var(--text-title-h1);
|
||
--text-highlight-bg: $fg_color; /* Use text color as highlight background */
|
||
--text-on-accent: $bg_color;
|
||
|
||
--interactive-normal: var(--code-background);
|
||
--interactive-hover: var(--interactive-accent);
|
||
--interactive-accent-hover: var(--interactive-accent);
|
||
--interactive-success: var(--text-title-h2);
|
||
|
||
--scrollbar-bg: var(--background-primary);
|
||
--scrollbar-thumb-bg: var(--code-background);
|
||
--scrollbar-active-thumb-bg: var(--interactive-accent);
|
||
|
||
--background-modifier-border: var(--border-color);
|
||
--background-modifier-form-field: var(--code-background);
|
||
--background-modifier-form-field-highlighted: var(--code-background);
|
||
--background-modifier-box-shadow: rgba(0, 0, 0, 0.3);
|
||
--background-modifier-success: var(--interactive-success);
|
||
--background-modifier-error: var(--text-error);
|
||
--background-modifier-error-hover: var(--text-error);
|
||
--background-modifier-cover: rgba(0, 0, 0, 0.8);
|
||
|
||
--link-color: var(--text-link);
|
||
--link-color-hover: var(--text-link);
|
||
--link-unresolved-color: var(--text-muted);
|
||
--link-unresolved-opacity: 0.7;
|
||
|
||
--tag-color: var(--text-title-h3);
|
||
--tag-background: var(--code-background);
|
||
|
||
--graph-line: var(--text-muted);
|
||
--graph-node: var(--interactive-accent);
|
||
--graph-node-unresolved: var(--text-muted);
|
||
--graph-node-focused: var(--text-link);
|
||
--graph-node-tag: var(--text-title-h3);
|
||
--graph-node-attachment: var(--text-title-h2);
|
||
|
||
/* Fonts */
|
||
--font-interface-theme: "$ui_font";
|
||
--font-text-theme: "$ui_font";
|
||
--font-monospace-theme: "$monospace_font";
|
||
}
|
||
|
||
/* Headers */
|
||
.cm-header-1, .markdown-rendered h1 { color: var(--text-title-h1); }
|
||
.cm-header-2, .markdown-rendered h2 { color: var(--text-title-h2); }
|
||
.cm-header-3, .markdown-rendered h3 { color: var(--text-title-h3); }
|
||
.cm-header-4, .markdown-rendered h4 { color: var(--text-title-h4); }
|
||
.cm-header-5, .markdown-rendered h5 { color: var(--text-title-h5); }
|
||
.cm-header-6, .markdown-rendered h6 { color: var(--text-title-h6); }
|
||
|
||
/* Code blocks */
|
||
.markdown-rendered code {
|
||
font-family: var(--font-monospace-theme);
|
||
background-color: var(--code-background);
|
||
color: var(--markup-code);
|
||
padding: 2px 4px;
|
||
border-radius: 3px;
|
||
}
|
||
|
||
.markdown-rendered pre {
|
||
background-color: var(--code-background);
|
||
border: 1px solid var(--background-modifier-border);
|
||
border-radius: 5px;
|
||
}
|
||
|
||
.markdown-rendered pre code {
|
||
background-color: transparent;
|
||
color: var(--code-foreground);
|
||
}
|
||
|
||
/* Syntax highlighting */
|
||
.cm-s-obsidian span.cm-keyword { color: var(--text-title-h1); }
|
||
.cm-s-obsidian span.cm-string { color: var(--text-title-h2); }
|
||
.cm-s-obsidian span.cm-number { color: var(--text-title-h3); }
|
||
.cm-s-obsidian span.cm-comment { color: var(--text-muted); }
|
||
.cm-s-obsidian span.cm-operator { color: var(--text-link); }
|
||
.cm-s-obsidian span.cm-variable { color: var(--text-normal); }
|
||
.cm-s-obsidian span.cm-def { color: var(--text-link); }
|
||
|
||
/* Highlighted text */
|
||
.markdown-rendered mark,
|
||
.cm-s-obsidian span.cm-highlight,
|
||
mark {
|
||
background-color: var(--text-highlight-bg) !important;
|
||
color: var(--code-background) !important;
|
||
}
|
||
|
||
/* Links */
|
||
.markdown-rendered a {
|
||
color: var(--text-link);
|
||
}
|
||
|
||
/* Blockquotes */
|
||
.markdown-rendered blockquote {
|
||
border-left: 4px solid var(--blockquote-border);
|
||
padding-left: 1em;
|
||
}
|
||
|
||
/* Status bar */
|
||
.status-bar {
|
||
background-color: var(--code-background);
|
||
border-top: 1px solid var(--background-modifier-border);
|
||
}
|
||
|
||
/* Active file */
|
||
.workspace-leaf.mod-active .workspace-leaf-header-title {
|
||
color: var(--interactive-accent);
|
||
}
|
||
|
||
.nav-file-title.is-active {
|
||
background-color: var(--code-background);
|
||
color: var(--interactive-accent);
|
||
}
|
||
|
||
/* Text selection */
|
||
::selection {
|
||
background-color: var(--text-selection);
|
||
color: var(--text-selection-fg);
|
||
}
|
||
|
||
/* Search results */
|
||
.search-result-file-title {
|
||
color: var(--interactive-accent);
|
||
}
|
||
|
||
.search-result-file-match {
|
||
background-color: var(--code-background);
|
||
color: var(--text-normal);
|
||
border-left: 3px solid var(--interactive-accent);
|
||
}
|
||
|
||
.search-result-file-matched-text {
|
||
color: var(--code-background);
|
||
}
|
||
|
||
/* Tables */
|
||
.markdown-rendered table {
|
||
border: 1px solid var(--background-modifier-border);
|
||
}
|
||
|
||
.markdown-rendered th {
|
||
background-color: var(--code-background);
|
||
color: var(--text-accent);
|
||
}
|
||
|
||
.markdown-rendered td {
|
||
border: 1px solid var(--background-modifier-border);
|
||
}
|
||
|
||
/* Callouts */
|
||
.callout {
|
||
border-left: 4px solid var(--interactive-accent);
|
||
background-color: var(--code-background);
|
||
}
|
||
.callout * {
|
||
color: var(--text-normal);
|
||
}
|
||
|
||
/* Modal dialogs */
|
||
.modal {
|
||
background-color: var(--background-primary);
|
||
border: 2px solid var(--background-modifier-border);
|
||
}
|
||
|
||
/* Settings */
|
||
.vertical-tab-header-group-title {
|
||
color: var(--interactive-accent);
|
||
}
|
||
|
||
.vertical-tab-nav-item.is-active {
|
||
background-color: var(--code-background);
|
||
color: var(--interactive-accent);
|
||
}
|
||
EOF
|
||
}
|
||
|
||
# Option handling
|
||
if [ "${1:-}" = "--reset" ]; then
|
||
echo "♻️ Resetting Omarchy themes and registry..."
|
||
if [ -f "$VAULTS_FILE" ] && [ -s "$VAULTS_FILE" ]; then
|
||
while IFS= read -r vault_path || [ -n "$vault_path" ]; do
|
||
case "$vault_path" in ""|\#*) continue ;; esac
|
||
vault_path="${vault_path%/}"
|
||
vault_name=$(basename "$vault_path")
|
||
theme_dir="$vault_path/.obsidian/themes/Omarchy"
|
||
if [ -d "$theme_dir" ]; then
|
||
rm -rf "$theme_dir"
|
||
echo " ✅ $vault_name (theme removed)"
|
||
else
|
||
echo " ℹ️ $vault_name (no theme present)"
|
||
fi
|
||
done <"$VAULTS_FILE"
|
||
fi
|
||
rm -f "$VAULTS_FILE"
|
||
echo "✅ Registry removed"
|
||
exit 0
|
||
fi
|
||
|
||
# Main update logic
|
||
echo "🔄 Updating Obsidian vaults..."
|
||
|
||
# Step 1: ensure registry exists (bootstrap if needed)
|
||
ensure_vaults_file
|
||
|
||
while IFS= read -r vault_path || [ -n "$vault_path" ]; do
|
||
case "$vault_path" in "" | \#*) continue ;; esac
|
||
vault_path="${vault_path%/}"
|
||
vault_name=$(basename "$vault_path")
|
||
|
||
# Step 2: verify path exists; log/skip gracefully if invalid
|
||
if [ ! -d "$vault_path" ] || [ ! -d "$vault_path/.obsidian" ]; then
|
||
echo " ❌ $vault_name (invalid entry: missing directory or .obsidian)"
|
||
continue
|
||
fi
|
||
|
||
# Ensure theme files exist for this vault
|
||
ensure_theme_scaffold "$vault_path"
|
||
THEME_DIR="$vault_path/.obsidian/themes/Omarchy"
|
||
|
||
# Step 3: update theme.css
|
||
if [ -f "$CURRENT_THEME_DIR/obsidian.css" ]; then
|
||
cp "$CURRENT_THEME_DIR/obsidian.css" "$THEME_DIR/theme.css"
|
||
echo " ✅ $vault_name (custom theme)"
|
||
else
|
||
extract_theme_data >"$THEME_DIR/theme.css"
|
||
echo " ✅ $vault_name (generated theme)"
|
||
fi
|
||
done <"$VAULTS_FILE"
|