diff --git a/bin/omarchy-dev-benchmark-theme-switcher b/bin/omarchy-dev-benchmark-theme-switcher index b814025f..97460bed 100755 --- a/bin/omarchy-dev-benchmark-theme-switcher +++ b/bin/omarchy-dev-benchmark-theme-switcher @@ -19,7 +19,8 @@ Usage: Measure the non-interactive parts of the theme switcher: - theme preview index build (omarchy-theme-switcher before UI handoff) -- image selector row/thumbnail cache prep (omarchy-menu-images --cache-only) +- lazy selector row prep used by the interactive theme switcher +- full thumbnail cache warmup cost (omarchy-menu-images --cache-only) Options: --repeat= Number of warm runs to measure for each case (default: 5) @@ -100,6 +101,7 @@ if [[ ! $REPEAT =~ ^[0-9]+$ ]] || (( REPEAT < 1 )); then fi benchmark_cache=$(mktemp -d) +thumbnail_cache=$(mktemp -d) stub_bin=$(mktemp -d) cleanup() { @@ -107,8 +109,9 @@ cleanup() { if [[ $KEEP_CACHE == "true" ]]; then printf 'Benchmark cache: %s\n' "$benchmark_cache" + printf 'Thumbnail cache: %s\n' "$thumbnail_cache" else - rm -rf "$benchmark_cache" + rm -rf "$benchmark_cache" "$thumbnail_cache" fi } trap cleanup EXIT @@ -126,20 +129,35 @@ benchmark_env=( "PATH=$stub_bin:$OMARCHY_BIN_DIR:$PATH" ) +thumbnail_env=( + env + "OMARCHY_PATH=$OMARCHY_PATH" + "XDG_CACHE_HOME=$thumbnail_cache" + "PATH=$stub_bin:$OMARCHY_BIN_DIR:$PATH" +) + preview_dir="$benchmark_cache/omarchy/theme-selector/previews" +thumbnail_preview_dir="$thumbnail_cache/omarchy/theme-selector/previews" build_theme_index() { "${benchmark_env[@]}" "$OMARCHY_BIN_DIR/omarchy-theme-switcher" } +prepare_selector_lazy() { + "${benchmark_env[@]}" "$OMARCHY_BIN_DIR/omarchy-menu-images" --prepare-only --lazy-thumbnails --show-labels --filterable "$preview_dir" +} + prepare_image_cache() { - "${benchmark_env[@]}" "$OMARCHY_BIN_DIR/omarchy-menu-images" --cache-only "$preview_dir" + "${thumbnail_env[@]}" "$OMARCHY_BIN_DIR/omarchy-menu-images" --cache-only "$thumbnail_preview_dir" } printf 'Theme switcher benchmark (%d warm runs each)\n\n' "$REPEAT" printf '%-34s %s ms\n' "theme index cold" "$(format_ms "$(measure_once build_theme_index)")" run_case "theme index warm" build_theme_index -printf '%-34s %s ms\n' "selector cache cold" "$(format_ms "$(measure_once prepare_image_cache)")" -run_case "selector cache warm" prepare_image_cache +printf '%-34s %s ms\n' "selector prep cold (lazy)" "$(format_ms "$(measure_once prepare_selector_lazy)")" +run_case "selector prep warm (lazy)" prepare_selector_lazy +"${thumbnail_env[@]}" "$OMARCHY_BIN_DIR/omarchy-theme-switcher" >/dev/null +printf '%-34s %s ms\n' "thumbnail cache cold" "$(format_ms "$(measure_once prepare_image_cache)")" +run_case "thumbnail cache warm" prepare_image_cache printf '\nTheme previews: %d\n' "$(find -L "$preview_dir" -maxdepth 1 -type f 2>/dev/null | wc -l)" diff --git a/bin/omarchy-menu-images b/bin/omarchy-menu-images index 86369daf..970f9392 100755 --- a/bin/omarchy-menu-images +++ b/bin/omarchy-menu-images @@ -1,7 +1,7 @@ #!/bin/bash # omarchy:summary=Open a generic image selector menu -# omarchy:args=[--selected ] [--colors-file ] [--print-name] [--show-labels] [--filterable] [--cache-only] ... +# omarchy:args=[--selected ] [--colors-file ] [--print-name] [--show-labels] [--filterable] [--lazy-thumbnails] [--cache-only] ... OMARCHY_PATH=${OMARCHY_PATH:-$HOME/.local/share/omarchy} @@ -10,11 +10,13 @@ colors_file="" print_name=false show_labels=false filterable=false +lazy_thumbnails=false +prepare_only=false cache_only=false image_dirs=() usage() { - echo "Usage: omarchy-menu-images [--selected ] [--colors-file ] [--print-name] [--show-labels] [--filterable] [--cache-only] ..." + echo "Usage: omarchy-menu-images [--selected ] [--colors-file ] [--print-name] [--show-labels] [--filterable] [--lazy-thumbnails] [--cache-only] ..." } while [[ $# -gt 0 ]]; do @@ -49,6 +51,14 @@ while [[ $# -gt 0 ]]; do filterable=true shift ;; + --lazy-thumbnails) + lazy_thumbnails=true + shift + ;; + --prepare-only) + prepare_only=true + shift + ;; --cache-only) cache_only=true shift @@ -108,7 +118,8 @@ mkdir -p "$cache_dir" cache_key=$(printf '%s' "$image_dirs_env" | md5sum | cut -d ' ' -f 1) rows_cache_file="$cache_dir/$cache_key.rows" rows_signature_file="$cache_dir/$cache_key.signature" -rows_signature="" +rows_signature="v2"$'\n' +rows_cacheable=true image_files=() for dir in "${image_dirs[@]}"; do @@ -123,6 +134,29 @@ for dir in "${image_dirs[@]}"; do fi done +generate_thumbnail() { + local image="$1" + local thumbnail="$2" + local lock="$thumbnail.lock" + local tmp="$thumbnail.$$.jpg" + + if mkdir "$lock" 2>/dev/null; then + if magick "${image}[0]" -auto-orient -resize '1536x864^' -gravity center -extent '1536x864' -strip -quality 82 "$tmp"; then + mv -f "$tmp" "$thumbnail" + else + rm -f "$tmp" "$thumbnail" + fi + + rmdir "$lock" 2>/dev/null || true + else + for ((i = 0; i < 3000; i++)); do + [[ -f $thumbnail ]] && return + [[ -d $lock ]] || break + sleep 0.01 + done + fi +} + thumbnail_for() { local image="$1" local signature hash thumbnail @@ -131,14 +165,25 @@ thumbnail_for() { hash=$(awk -F '\t' -v path="$image" -v sig="$signature" '$1 == path && $2 == sig { print $3; exit }' "$index_file" 2>/dev/null) if [[ -z $hash ]]; then - hash=$(md5sum "$image" | cut -d ' ' -f 1) + hash=$(printf '%s\t%s' "$image" "$signature" | md5sum | cut -d ' ' -f 1) printf '%s\t%s\t%s\n' "$image" "$signature" "$hash" >>"$index_file" fi thumbnail="$cache_dir/$hash.jpg" if [[ ! -f $thumbnail ]]; then - magick "${image}[0]" -auto-orient -resize '1536x864^' -gravity center -extent '1536x864' -strip -quality 82 "$thumbnail" + if [[ $lazy_thumbnails == true && $cache_only != true ]]; then + rows_cacheable=false + + if [[ $prepare_only != true ]]; then + generate_thumbnail "$image" "$thumbnail" >/dev/null 2>&1 & + fi + + printf '%s' "$image" + return + fi + + generate_thumbnail "$image" "$thumbnail" fi [[ -f $thumbnail ]] && printf '%s' "$thumbnail" @@ -150,6 +195,9 @@ else for image in "${image_files[@]}"; do thumbnail=$(thumbnail_for "$image") [[ -n $thumbnail ]] || continue + if [[ $lazy_thumbnails == true && $cache_only != true && $thumbnail == "$image" ]]; then + rows_cacheable=false + fi if [[ -z $rows ]]; then rows="$image"$'\t'"$thumbnail" @@ -158,8 +206,12 @@ else fi done - printf '%s' "$rows" >"$rows_cache_file" - printf '%s' "$rows_signature" >"$rows_signature_file" + if [[ $rows_cacheable == true ]]; then + printf '%s' "$rows" >"$rows_cache_file" + printf '%s' "$rows_signature" >"$rows_signature_file" + else + rm -f "$rows_cache_file" "$rows_signature_file" + fi fi rows_payload=${rows//$'\t'/$'\f'} @@ -173,7 +225,7 @@ if [[ -f $colors_file ]]; then colors_payload=${colors_payload//$'\n'/$'\v'} fi -if [[ $cache_only == true ]]; then +if [[ $cache_only == true || $prepare_only == true ]]; then exit 0 fi diff --git a/bin/omarchy-theme-switcher b/bin/omarchy-theme-switcher index 2b3209ec..4a1e55e5 100755 --- a/bin/omarchy-theme-switcher +++ b/bin/omarchy-theme-switcher @@ -93,5 +93,6 @@ exec omarchy-menu-images \ --print-name \ --show-labels \ --filterable \ + --lazy-thumbnails \ --selected "$selected_preview" \ "$preview_dir"