#!/bin/bash # GoTelegram v2.2 — Каталог шаблонов сайтов # Выбор из ~200 шаблонов, превью-ссылки, скачивание через git sparse-checkout CATALOG_FILE="$(dirname "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")")/templates_catalog.json" TEMPLATES_CACHE="/tmp/gotelegram_templates" # ── Загрузка каталога ──────────────────────────────────────────────────────── load_catalog() { if [ ! -f "$CATALOG_FILE" ]; then log_error "Каталог шаблонов не найден: $CATALOG_FILE" return 1 fi return 0 } # ── Категории ──────────────────────────────────────────────────────────────── get_categories() { jq -r '.categories[] | "\(.id)|\(.name)|\(.icon)|\(.templates | length)"' "$CATALOG_FILE" 2>/dev/null } get_category_name() { local cat_id="$1" jq -r ".categories[] | select(.id == \"$cat_id\") | .name" "$CATALOG_FILE" 2>/dev/null } # ── Шаблоны по категории ──────────────────────────────────────────────────── get_templates_by_category() { local cat_id="$1" jq -r ".categories[] | select(.id == \"$cat_id\") | .templates[] | \"\(.id)|\(.name)|\(.source)|\(.preview_url)\"" "$CATALOG_FILE" 2>/dev/null } # ── Информация о шаблоне ──────────────────────────────────────────────────── get_template_info() { local tpl_id="$1" jq ".categories[].templates[] | select(.id == \"$tpl_id\")" "$CATALOG_FILE" 2>/dev/null } get_template_field() { local tpl_id="$1" local field="$2" jq -r ".categories[].templates[] | select(.id == \"$tpl_id\") | .$field" "$CATALOG_FILE" 2>/dev/null } # ── Интерактивный выбор категории ──────────────────────────────────────────── select_category() { load_catalog || return 1 echo "" >&2 echo -e " ${BOLD}${WHITE}📂 Категории шаблонов сайтов:${NC}" >&2 echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" >&2 local cats=() local i=1 while IFS='|' read -r id name icon count; do [ "$count" -eq 0 ] && continue local emoji case "$icon" in briefcase) emoji="🏢" ;; shopping-cart) emoji="🛒" ;; heart) emoji="🏥" ;; book) emoji="🎓" ;; palette) emoji="📸" ;; home) emoji="🏠" ;; utensils) emoji="🍕" ;; rocket) emoji="🎨" ;; chart-bar) emoji="🔧" ;; *) emoji="📄" ;; esac printf " ${CYAN}%2d)${NC} ${emoji} %-30s ${DIM}(%d шаблонов)${NC}\n" "$i" "$name" "$count" >&2 cats+=("$id") ((i++)) done < <(get_categories) printf " ${CYAN}%2d)${NC} 🎲 Случайный шаблон\n" "$i" >&2 echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" >&2 echo -ne " ${WHITE}Выбор:${NC} " >&2 read -r choice # Случайный if [ "$choice" -eq "$i" ] 2>/dev/null; then local random_cat="${cats[$((RANDOM % ${#cats[@]}))]}" echo "$random_cat" return 0 fi if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -lt "$i" ]; then echo "${cats[$((choice-1))]}" return 0 fi log_error "Неверный выбор" return 1 } # ── Интерактивный выбор шаблона ────────────────────────────────────────────── select_template() { local cat_id="$1" local cat_name cat_name=$(get_category_name "$cat_id") echo "" >&2 echo -e " ${BOLD}${WHITE}📋 $cat_name — доступные шаблоны:${NC}" >&2 echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}" >&2 local tpls=() local i=1 while IFS='|' read -r id name source preview; do printf " ${CYAN}%2d)${NC} %-30s ${DIM}[%s]${NC}\n" "$i" "$name" "$source" >&2 tpls+=("$id") ((i++)) done < <(get_templates_by_category "$cat_id") if [ ${#tpls[@]} -eq 0 ]; then log_info "В этой категории нет шаблонов" return 1 fi echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}" >&2 echo -ne " ${WHITE}Выбор (1-$((i-1))):${NC} " >&2 read -r choice if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -lt "$i" ]; then local selected_id="${tpls[$((choice-1))]}" # Показываем превью show_template_preview "$selected_id" echo "$selected_id" return 0 fi log_error "Неверный выбор" return 1 } # ── Показ превью шаблона ──────────────────────────────────────────────────── show_template_preview() { local tpl_id="$1" local info info=$(get_template_info "$tpl_id") local name source preview_url repo_url description name=$(echo "$info" | jq -r '.name') source=$(echo "$info" | jq -r '.source') preview_url=$(echo "$info" | jq -r '.preview_url // empty') repo_url=$(echo "$info" | jq -r '.repo_url // empty') description=$(echo "$info" | jq -r '.description // "—"') echo "" >&2 echo -e " ${BOLD}${WHITE}🔍 Превью шаблона:${NC}" >&2 echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" >&2 echo -e " ${WHITE}Название:${NC} $name" >&2 echo -e " ${WHITE}Источник:${NC} $source" >&2 echo -e " ${WHITE}Описание:${NC} $description" >&2 if [ -n "$preview_url" ]; then echo "" >&2 echo -e " ${GREEN}👁 Превью:${NC} ${CYAN}${preview_url}${NC}" >&2 echo -e " ${DIM}Откройте ссылку в браузере для просмотра шаблона${NC}" >&2 fi if [ -n "$repo_url" ]; then echo -e " ${DIM}📦 Репо: ${repo_url}${NC}" >&2 fi # Благодарность автору echo "" >&2 echo -e " ${MAGENTA}💜 Спасибо авторам ${source} за открытый код!${NC}" >&2 echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" >&2 echo "" >&2 if ! confirm "Установить этот шаблон?"; then return 1 fi return 0 } # ── Скачивание шаблона ─────────────────────────────────────────────────────── download_template() { local tpl_id="$1" local output_dir="${2:-$TEMPLATES_CACHE}" local info info=$(get_template_info "$tpl_id") local repo_url sparse_path source name repo_url=$(echo "$info" | jq -r '.repo_url') sparse_path=$(echo "$info" | jq -r '.sparse_path') source=$(echo "$info" | jq -r '.source') name=$(echo "$info" | jq -r '.name') local clone_dir="$output_dir/${tpl_id}" rm -rf "$clone_dir" mkdir -p "$clone_dir" log_info "Скачивание шаблона \"$name\"..." # Для HTML5 UP — отдельный репо с папками if [ "$source" = "html5up" ]; then local tmp_clone="/tmp/html5up_clone_$$" rm -rf "$tmp_clone" # Sparse checkout git clone --depth 1 --filter=blob:none --sparse "$repo_url" "$tmp_clone" 2>/dev/null if [ $? -ne 0 ]; then # Fallback: полный clone git clone --depth 1 "$repo_url" "$tmp_clone" 2>/dev/null fi if [ -d "$tmp_clone" ]; then cd "$tmp_clone" && git sparse-checkout set "$sparse_path" 2>/dev/null if [ -d "$tmp_clone/$sparse_path" ]; then cp -r "$tmp_clone/$sparse_path"/* "$clone_dir/" fi cd - >/dev/null fi rm -rf "$tmp_clone" # Для learning-zone — один большой репо elif [ "$source" = "learning-zone" ]; then local tmp_clone="/tmp/lz_clone_$$" rm -rf "$tmp_clone" git clone --depth 1 --filter=blob:none --sparse "$repo_url" "$tmp_clone" 2>/dev/null if [ $? -ne 0 ]; then git clone --depth 1 "$repo_url" "$tmp_clone" 2>/dev/null fi if [ -d "$tmp_clone" ]; then cd "$tmp_clone" && git sparse-checkout set "$sparse_path" 2>/dev/null if [ -d "$tmp_clone/$sparse_path" ]; then cp -r "$tmp_clone/$sparse_path"/* "$clone_dir/" fi cd - >/dev/null fi rm -rf "$tmp_clone" # Для StartBootstrap — каждый шаблон в своём репо elif [ "$source" = "startbootstrap" ]; then local sb_tmp="/tmp/sb_clone_$$" rm -rf "$sb_tmp" git clone --depth 1 "$repo_url" "$sb_tmp" 2>/dev/null if [ -d "$sb_tmp" ]; then rm -rf "$sb_tmp/.git" # StartBootstrap хранит production-файлы в dist/ if [ -f "$sb_tmp/dist/index.html" ]; then cp -r "$sb_tmp/dist/"* "$clone_dir/" elif [ -f "$sb_tmp/index.html" ]; then cp -r "$sb_tmp/"* "$clone_dir/" else local found_index found_index=$(find "$sb_tmp" -name "index.html" -type f 2>/dev/null | head -1) if [ -n "$found_index" ]; then local found_dir found_dir=$(dirname "$found_index") cp -r "$found_dir/"* "$clone_dir/" fi fi fi rm -rf "$sb_tmp" # Для ThemeWagon / ColorlibHQ — каждый шаблон в отдельном репо elif [ "$source" = "themewagon" ] || [ "$source" = "colorlib" ]; then local tw_tmp="/tmp/tw_clone_$$" rm -rf "$tw_tmp" git clone --depth 1 "$repo_url" "$tw_tmp" 2>/dev/null if [ -d "$tw_tmp" ]; then rm -rf "$tw_tmp/.git" if [ -f "$tw_tmp/dist/index.html" ]; then cp -r "$tw_tmp/dist/"* "$clone_dir/" elif [ -f "$tw_tmp/index.html" ]; then cp -r "$tw_tmp/"* "$clone_dir/" else local found_index found_index=$(find "$tw_tmp" -name "index.html" -type f -maxdepth 3 2>/dev/null | head -1) if [ -n "$found_index" ]; then local found_dir found_dir=$(dirname "$found_index") cp -r "$found_dir/"* "$clone_dir/" fi fi fi rm -rf "$tw_tmp" # Для dawidolko — один большой репо с папками (как learning-zone) elif [ "$source" = "dawidolko" ]; then local tmp_clone="/tmp/dw_clone_$$" rm -rf "$tmp_clone" git clone --depth 1 --filter=blob:none --sparse "$repo_url" "$tmp_clone" 2>/dev/null if [ $? -ne 0 ]; then git clone --depth 1 "$repo_url" "$tmp_clone" 2>/dev/null fi if [ -d "$tmp_clone" ]; then cd "$tmp_clone" && git sparse-checkout set "$sparse_path" 2>/dev/null if [ -d "$tmp_clone/$sparse_path" ]; then cp -r "$tmp_clone/$sparse_path"/* "$clone_dir/" fi cd - >/dev/null fi rm -rf "$tmp_clone" fi # Проверяем результат if [ -f "$clone_dir/index.html" ]; then log_success "Шаблон \"$name\" скачан" echo "$clone_dir" return 0 else # fallback: ищем index.html в подпапках (нестандартная структура) local fallback_index fallback_index=$(find "$clone_dir" -name "index.html" -type f 2>/dev/null | head -1) if [ -n "$fallback_index" ]; then local fallback_dir fallback_dir=$(dirname "$fallback_index") if [ "$fallback_dir" != "$clone_dir" ]; then cp -r "$fallback_dir/"* "$clone_dir/" log_success "Шаблон \"$name\" скачан (из подпапки)" echo "$clone_dir" return 0 fi fi log_error "Шаблон не содержит index.html" log_dim "Путь: $clone_dir" ls -la "$clone_dir" 2>/dev/null >&2 return 1 fi } # ── Полный интерактивный процесс выбора ────────────────────────────────────── interactive_template_selection() { load_catalog || return 1 # Выбор категории local cat_id cat_id=$(select_category) [ $? -ne 0 ] && return 1 # Выбор шаблона local tpl_id tpl_id=$(select_template "$cat_id") [ $? -ne 0 ] && return 1 # Скачивание local template_dir template_dir=$(download_template "$tpl_id") [ $? -ne 0 ] && return 1 echo "$template_dir" return 0 }