From 4174ff7b133a4af08f4a5b201a96c84d6d6894b8 Mon Sep 17 00:00:00 2001 From: Darcy Brown Date: Fri, 17 Oct 2025 04:22:38 -0500 Subject: [PATCH] Dynamic Color Match Obsidian theme to current Omarchy theme (#791) * 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. --- bin/omarchy-theme-set | 1 + bin/omarchy-theme-set-obsidian | 659 +++++++++++++++++++++++++++++++++ migrations/1760441237.sh | 3 + 3 files changed, 663 insertions(+) create mode 100755 bin/omarchy-theme-set-obsidian create mode 100644 migrations/1760441237.sh diff --git a/bin/omarchy-theme-set b/bin/omarchy-theme-set index 5dd9edd8..b729dee6 100755 --- a/bin/omarchy-theme-set +++ b/bin/omarchy-theme-set @@ -38,3 +38,4 @@ omarchy-theme-set-gnome omarchy-theme-set-browser omarchy-theme-set-vscode omarchy-theme-set-cursor +omarchy-theme-set-obsidian diff --git a/bin/omarchy-theme-set-obsidian b/bin/omarchy-theme-set-obsidian new file mode 100755 index 00000000..e67f0b62 --- /dev/null +++ b/bin/omarchy-theme-set-obsidian @@ -0,0 +1,659 @@ +#!/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 //.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 </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" diff --git a/migrations/1760441237.sh b/migrations/1760441237.sh new file mode 100644 index 00000000..1c2d89d3 --- /dev/null +++ b/migrations/1760441237.sh @@ -0,0 +1,3 @@ +echo "Install Omarchy theme on Obsidian vaults" + +omarchy-theme-set-obsidian