theamify-cli 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +286 -0
- package/bin/theamify.js +2 -0
- package/package.json +52 -0
- package/src/cli.js +109 -0
- package/src/commands/manage.js +122 -0
- package/src/core/engine.js +100 -0
- package/src/lib/chafa.js +143 -0
- package/src/lib/conf.js +55 -0
- package/src/lib/tools.js +76 -0
- package/src/wizard/browse.js +165 -0
- package/vendor/config/themes.conf +36 -0
- package/vendor/lib/colors.sh +151 -0
- package/vendor/lib/grub.sh +179 -0
- package/vendor/lib/themes.sh +283 -0
- package/vendor/lib/utils.sh +135 -0
- package/vendor/theamify +565 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# -----------------------------------------------------------------------------
|
|
3
|
+
# lib/colors.sh - ANSI Colors, Print Functions & UI Components
|
|
4
|
+
# -----------------------------------------------------------------------------
|
|
5
|
+
|
|
6
|
+
RESET=$'\033[0m'; BOLD=$'\033[1m'; DIM=$'\033[2m'
|
|
7
|
+
ITALIC=$'\033[3m'; UNDERLINE=$'\033[4m'
|
|
8
|
+
|
|
9
|
+
BLACK=$'\033[0;30m'; RED=$'\033[0;31m'; GREEN=$'\033[0;32m'
|
|
10
|
+
YELLOW=$'\033[0;33m';BLUE=$'\033[0;34m'; MAGENTA=$'\033[0;35m'
|
|
11
|
+
CYAN=$'\033[0;36m'; WHITE=$'\033[0;37m'
|
|
12
|
+
|
|
13
|
+
BOLD_BLACK=$'\033[1;30m'; BOLD_RED=$'\033[1;31m'; BOLD_GREEN=$'\033[1;32m'
|
|
14
|
+
BOLD_YELLOW=$'\033[1;33m'; BOLD_BLUE=$'\033[1;34m'; BOLD_MAGENTA=$'\033[1;35m'
|
|
15
|
+
BOLD_CYAN=$'\033[1;36m'; BOLD_WHITE=$'\033[1;37m'
|
|
16
|
+
|
|
17
|
+
BRIGHT_RED=$'\033[0;91m'; BRIGHT_GREEN=$'\033[0;92m'; BRIGHT_YELLOW=$'\033[0;93m'
|
|
18
|
+
BRIGHT_BLUE=$'\033[0;94m'; BRIGHT_MAGENTA=$'\033[0;95m';BRIGHT_CYAN=$'\033[0;96m'
|
|
19
|
+
BRIGHT_WHITE=$'\033[0;97m'
|
|
20
|
+
|
|
21
|
+
BG_BLACK=$'\033[40m'; BG_RED=$'\033[41m'; BG_GREEN=$'\033[42m'
|
|
22
|
+
BG_YELLOW=$'\033[43m'; BG_BLUE=$'\033[44m'; BG_MAGENTA=$'\033[45m'
|
|
23
|
+
BG_CYAN=$'\033[46m'; BG_WHITE=$'\033[47m'
|
|
24
|
+
|
|
25
|
+
C_TITLE="${BOLD_CYAN}"
|
|
26
|
+
C_SUBTITLE="${BOLD_MAGENTA}"
|
|
27
|
+
C_SECTION="${BOLD_YELLOW}"
|
|
28
|
+
C_SUCCESS="${BOLD_GREEN}"
|
|
29
|
+
C_ERROR="${BOLD_RED}"
|
|
30
|
+
C_WARNING="${YELLOW}"
|
|
31
|
+
C_INFO="${CYAN}"
|
|
32
|
+
C_DIM="${DIM}${WHITE}"
|
|
33
|
+
C_HIGHLIGHT="${BOLD_WHITE}"
|
|
34
|
+
C_PROMPT="${BOLD_BLUE}"
|
|
35
|
+
C_ACTIVE="${BOLD_GREEN}"
|
|
36
|
+
C_INACTIVE="${DIM}${WHITE}"
|
|
37
|
+
C_STEP="${BRIGHT_CYAN}"
|
|
38
|
+
C_CMD="${DIM}${GREEN}"
|
|
39
|
+
C_LINK="${UNDERLINE}${BRIGHT_BLUE}"
|
|
40
|
+
C_TAG="${BRIGHT_MAGENTA}"
|
|
41
|
+
C_NUM="${BOLD_YELLOW}"
|
|
42
|
+
C_BORDER="${BOLD_BLUE}"
|
|
43
|
+
|
|
44
|
+
if [[ -n "${NO_COLOR:-}" ]] || [[ ! -t 1 ]]; then
|
|
45
|
+
RESET="" BOLD="" DIM="" ITALIC="" UNDERLINE=""
|
|
46
|
+
BLACK="" RED="" GREEN="" YELLOW="" BLUE="" MAGENTA="" CYAN="" WHITE=""
|
|
47
|
+
BOLD_BLACK="" BOLD_RED="" BOLD_GREEN="" BOLD_YELLOW="" BOLD_BLUE=""
|
|
48
|
+
BOLD_MAGENTA="" BOLD_CYAN="" BOLD_WHITE=""
|
|
49
|
+
BRIGHT_RED="" BRIGHT_GREEN="" BRIGHT_YELLOW="" BRIGHT_BLUE=""
|
|
50
|
+
BRIGHT_MAGENTA="" BRIGHT_CYAN="" BRIGHT_WHITE=""
|
|
51
|
+
BG_BLACK="" BG_RED="" BG_GREEN="" BG_YELLOW="" BG_BLUE=""
|
|
52
|
+
BG_MAGENTA="" BG_CYAN="" BG_WHITE=""
|
|
53
|
+
C_TITLE="" C_SUBTITLE="" C_SECTION="" C_SUCCESS="" C_ERROR=""
|
|
54
|
+
C_WARNING="" C_INFO="" C_DIM="" C_HIGHLIGHT="" C_PROMPT=""
|
|
55
|
+
C_ACTIVE="" C_INACTIVE="" C_STEP="" C_CMD="" C_LINK=""
|
|
56
|
+
C_TAG="" C_NUM="" C_BORDER=""
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
print_title() { echo -e "${C_TITLE}${*}${RESET}"; }
|
|
60
|
+
print_section() { echo -e "\n${C_SECTION}== ${*}${RESET}"; }
|
|
61
|
+
print_success() { echo -e "${C_SUCCESS}[OK]${RESET} ${*}"; }
|
|
62
|
+
print_error() { echo -e "${C_ERROR}[ERR]${RESET} ${*}" >&2; }
|
|
63
|
+
print_warning() { echo -e "${C_WARNING}[WARN]${RESET} ${*}"; }
|
|
64
|
+
print_info() { echo -e "${C_INFO}[INFO]${RESET} ${*}"; }
|
|
65
|
+
print_step() { echo -e "${C_STEP}->${RESET} ${*}"; }
|
|
66
|
+
print_dim() { echo -e "${C_DIM}${*}${RESET}"; }
|
|
67
|
+
print_cmd() { echo -e "${C_CMD} \$ ${*}${RESET}"; }
|
|
68
|
+
print_link() { echo -e "${C_LINK}${*}${RESET}"; }
|
|
69
|
+
print_bold() { echo -e "${BOLD_WHITE}${*}${RESET}"; }
|
|
70
|
+
|
|
71
|
+
_term_width() {
|
|
72
|
+
local w; w="$(tput cols 2>/dev/null || echo 80)"
|
|
73
|
+
(( w > 100 )) && w=100
|
|
74
|
+
echo "${w}"
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
print_rule() {
|
|
78
|
+
local char="${1:--}"
|
|
79
|
+
local w; w="$(_term_width)"
|
|
80
|
+
printf '%s' "${C_BORDER}"
|
|
81
|
+
printf '%*s' "${w}" '' | tr ' ' "${char}"
|
|
82
|
+
printf '%s\n' "${RESET}"
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
print_box_title() {
|
|
86
|
+
local title="${1}"
|
|
87
|
+
local w; w="$(_term_width)"
|
|
88
|
+
local inner=$(( w - 4 ))
|
|
89
|
+
local tlen="${#title}"
|
|
90
|
+
local lpad=$(( (inner - tlen) / 2 ))
|
|
91
|
+
(( lpad < 0 )) && lpad=0
|
|
92
|
+
local rpad=$(( inner - tlen - lpad ))
|
|
93
|
+
(( rpad < 0 )) && rpad=0
|
|
94
|
+
|
|
95
|
+
print_rule "-"
|
|
96
|
+
printf '%s|%s %s%*s%s%*s %s|%s\n' \
|
|
97
|
+
"${C_BORDER}" "${RESET}" "${C_TITLE}" \
|
|
98
|
+
"${lpad}" "" "${title}" "${rpad}" "" "${C_BORDER}" "${RESET}"
|
|
99
|
+
print_rule "-"
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
print_kv() {
|
|
103
|
+
local key="${1}" val="${2}"
|
|
104
|
+
printf " ${BOLD_WHITE}%-20s${RESET} %b\n" "${key}:" "${val}"
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
print_status() {
|
|
108
|
+
case "${1:-}" in
|
|
109
|
+
active) printf '%s[ACTIVE]%s' "${C_ACTIVE}" "${RESET}" ;;
|
|
110
|
+
cached) printf '%s[CACHED]%s' "${C_INFO}" "${RESET}" ;;
|
|
111
|
+
remote) printf '%s[REMOTE]%s' "${C_DIM}" "${RESET}" ;;
|
|
112
|
+
error) printf '%s[ERROR]%s' "${C_ERROR}" "${RESET}" ;;
|
|
113
|
+
*) printf '%s[UNKNOWN]%s' "${C_DIM}" "${RESET}" ;;
|
|
114
|
+
esac
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
print_banner() {
|
|
118
|
+
print_rule "="
|
|
119
|
+
echo -e " ${C_TITLE}${BOLD}theamify${RESET} ${C_DIM}v${VERSION:-1.0.0}${RESET} ${C_DIM}- GRUB Theme Manager${RESET}"
|
|
120
|
+
echo -e " ${C_DIM}by Don Artkins${RESET}"
|
|
121
|
+
print_rule "="
|
|
122
|
+
echo
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
SPINNER_FRAMES=('|' '/' '-' '\')
|
|
126
|
+
SPINNER_PID=""
|
|
127
|
+
|
|
128
|
+
spinner_start() {
|
|
129
|
+
local msg="${1:-Working}"
|
|
130
|
+
(
|
|
131
|
+
local i=0
|
|
132
|
+
while true; do
|
|
133
|
+
# >&2 is mandatory: get_repo() and other functions run this while
|
|
134
|
+
# their own stdout is being captured via $(...). Anything the
|
|
135
|
+
# spinner writes to fd1 lands inside that captured string.
|
|
136
|
+
printf '\r %s%s%s %s...' "${C_STEP}" "${SPINNER_FRAMES[$((i % 4))]}" "${RESET}" "${msg}" >&2
|
|
137
|
+
sleep 0.1
|
|
138
|
+
i=$(( i + 1 ))
|
|
139
|
+
done
|
|
140
|
+
) &
|
|
141
|
+
SPINNER_PID=$!
|
|
142
|
+
disown "${SPINNER_PID}" 2>/dev/null || true
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
spinner_stop() {
|
|
146
|
+
if [[ -n "${SPINNER_PID:-}" ]]; then
|
|
147
|
+
kill "${SPINNER_PID}" 2>/dev/null || true
|
|
148
|
+
SPINNER_PID=""
|
|
149
|
+
printf '\r\033[K' >&2
|
|
150
|
+
fi
|
|
151
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# -----------------------------------------------------------------------------
|
|
3
|
+
# lib/grub.sh - GRUB Detection & Management
|
|
4
|
+
# -----------------------------------------------------------------------------
|
|
5
|
+
|
|
6
|
+
# -- Detect GRUB themes directory ---------------------------------------------
|
|
7
|
+
grub_detect_dir() {
|
|
8
|
+
local candidates=(
|
|
9
|
+
"/boot/grub/themes"
|
|
10
|
+
"/boot/grub2/themes"
|
|
11
|
+
"/usr/share/grub/themes"
|
|
12
|
+
)
|
|
13
|
+
for d in "${candidates[@]}"; do
|
|
14
|
+
[[ -d "${d}" ]] && echo "${d}" && return 0
|
|
15
|
+
done
|
|
16
|
+
echo "/boot/grub/themes" # default fallback - will be created on apply
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
# -- Detect GRUB update command ------------------------------------------------
|
|
20
|
+
grub_detect_update_cmd() {
|
|
21
|
+
if command -v update-grub &>/dev/null; then
|
|
22
|
+
echo "update-grub"
|
|
23
|
+
elif command -v grub-mkconfig &>/dev/null; then
|
|
24
|
+
echo "grub-mkconfig -o /boot/grub/grub.cfg"
|
|
25
|
+
elif command -v grub2-mkconfig &>/dev/null; then
|
|
26
|
+
if command -v zypper &>/dev/null; then
|
|
27
|
+
echo "grub2-mkconfig -o /boot/grub2/grub.cfg"
|
|
28
|
+
elif command -v dnf &>/dev/null; then
|
|
29
|
+
echo "grub2-mkconfig -o /boot/efi/EFI/fedora/grub.cfg"
|
|
30
|
+
else
|
|
31
|
+
echo "grub2-mkconfig -o /boot/grub2/grub.cfg"
|
|
32
|
+
fi
|
|
33
|
+
else
|
|
34
|
+
return 1
|
|
35
|
+
fi
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
# -- Get name of currently active GRUB theme ----------------------------------
|
|
39
|
+
grub_get_active_theme() {
|
|
40
|
+
local cfg="${GRUB_CFG:-/etc/default/grub}"
|
|
41
|
+
[[ ! -f "${cfg}" ]] && echo "" && return 0
|
|
42
|
+
|
|
43
|
+
local line
|
|
44
|
+
line="$(grep '^GRUB_THEME=' "${cfg}" 2>/dev/null | tail -1 || true)"
|
|
45
|
+
[[ -z "${line}" ]] && echo "" && return 0
|
|
46
|
+
|
|
47
|
+
local path
|
|
48
|
+
path="$(echo "${line}" | cut -d= -f2- | tr -d '"' | tr -d "'")"
|
|
49
|
+
# The theme folder is the parent dir of theme.txt
|
|
50
|
+
dirname "${path}" 2>/dev/null | xargs basename 2>/dev/null || echo ""
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# -- Backup GRUB config --------------------------------------------------------
|
|
54
|
+
grub_backup() {
|
|
55
|
+
local cfg="${GRUB_CFG:-/etc/default/grub}"
|
|
56
|
+
local bak="${cfg}.bak.$(date +%Y%m%d_%H%M%S)"
|
|
57
|
+
cp -a "${cfg}" "${bak}"
|
|
58
|
+
print_dim " Backed up: ${bak}"
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# ==============================================================================
|
|
62
|
+
# Apply theme to GRUB (4-stage process)
|
|
63
|
+
# ==============================================================================
|
|
64
|
+
grub_apply_theme() {
|
|
65
|
+
local theme_name="${1}"
|
|
66
|
+
local cached="${THEMES_CACHE_DIR}/${theme_name}"
|
|
67
|
+
local grub_dir
|
|
68
|
+
grub_dir="$(grub_detect_dir)"
|
|
69
|
+
local cfg="${GRUB_CFG:-/etc/default/grub}"
|
|
70
|
+
|
|
71
|
+
echo
|
|
72
|
+
print_box_title " Applying Theme: ${theme_name} "
|
|
73
|
+
|
|
74
|
+
# -- Stage 1 : Verify cached theme -----------------------------------------
|
|
75
|
+
print_section "Stage 1 of 4 - Verify"
|
|
76
|
+
|
|
77
|
+
if [[ ! -d "${cached}" ]]; then
|
|
78
|
+
print_error "Theme '${theme_name}' is not cached."
|
|
79
|
+
print_step "Run: theamify get ${theme_name}"
|
|
80
|
+
exit 1
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
local theme_txt
|
|
84
|
+
theme_txt="$(find_theme_txt "${cached}")" || {
|
|
85
|
+
print_error "No theme.txt found in cache for '${theme_name}'."
|
|
86
|
+
print_warning "This theme may require its own install script."
|
|
87
|
+
print_step "Check the repo: theamify open ${theme_name}"
|
|
88
|
+
exit 1
|
|
89
|
+
}
|
|
90
|
+
print_success "theme.txt found: ${theme_txt}"
|
|
91
|
+
|
|
92
|
+
# -- Stage 2 : Install theme files -----------------------------------------
|
|
93
|
+
print_section "Stage 2 of 4 - Install Files"
|
|
94
|
+
|
|
95
|
+
local dest="${grub_dir}/${theme_name}"
|
|
96
|
+
mkdir -p "${grub_dir}"
|
|
97
|
+
[[ -d "${dest}" ]] && rm -rf "${dest}"
|
|
98
|
+
|
|
99
|
+
print_step "Copying to: ${dest}"
|
|
100
|
+
cp -r "${cached}" "${dest}"
|
|
101
|
+
|
|
102
|
+
local dest_txt
|
|
103
|
+
dest_txt="$(find_theme_txt "${dest}")" || {
|
|
104
|
+
print_error "theme.txt not found after copying. Something went wrong."
|
|
105
|
+
exit 1
|
|
106
|
+
}
|
|
107
|
+
print_success "Theme files installed."
|
|
108
|
+
|
|
109
|
+
# -- Stage 3 : Configure /etc/default/grub --------------------------------
|
|
110
|
+
print_section "Stage 3 of 4 - Configure GRUB"
|
|
111
|
+
|
|
112
|
+
grub_backup
|
|
113
|
+
|
|
114
|
+
# Remove any old conflicting options
|
|
115
|
+
sed -i '/^GRUB_THEME=/d' "${cfg}"
|
|
116
|
+
sed -i '/^GRUB_TERMINAL_OUTPUT=/d' "${cfg}"
|
|
117
|
+
sed -i '/^GRUB_TIMEOUT_STYLE=/d' "${cfg}"
|
|
118
|
+
sed -i '/^GRUB_GFXMODE=/d' "${cfg}"
|
|
119
|
+
|
|
120
|
+
# Set timeout style to show menu
|
|
121
|
+
echo 'GRUB_TIMEOUT_STYLE="menu"' >> "${cfg}"
|
|
122
|
+
echo 'GRUB_GFXMODE="auto"' >> "${cfg}"
|
|
123
|
+
# Set timeout if not already defined
|
|
124
|
+
grep -q '^GRUB_TIMEOUT=' "${cfg}" || echo 'GRUB_TIMEOUT="30"' >> "${cfg}"
|
|
125
|
+
# Point GRUB at theme
|
|
126
|
+
echo "GRUB_THEME=\"${dest_txt}\"" >> "${cfg}"
|
|
127
|
+
|
|
128
|
+
print_success "GRUB config updated."
|
|
129
|
+
print_kv "GRUB_THEME" "${dest_txt}"
|
|
130
|
+
|
|
131
|
+
# -- Stage 4 : Rebuild GRUB config ----------------------------------------
|
|
132
|
+
print_section "Stage 4 of 4 - Rebuild GRUB"
|
|
133
|
+
|
|
134
|
+
local update_cmd
|
|
135
|
+
update_cmd="$(grub_detect_update_cmd)" || {
|
|
136
|
+
print_error "No GRUB update command found."
|
|
137
|
+
print_step "Run manually: update-grub OR grub-mkconfig -o /boot/grub/grub.cfg"
|
|
138
|
+
exit 1
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
print_step "Running: ${update_cmd}"
|
|
142
|
+
if eval "${update_cmd}"; then
|
|
143
|
+
print_success "GRUB rebuilt successfully."
|
|
144
|
+
else
|
|
145
|
+
print_error "GRUB rebuild failed. Check your GRUB setup."
|
|
146
|
+
exit 1
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
# -- Done ------------------------------------------------------------------
|
|
150
|
+
echo
|
|
151
|
+
print_rule "="
|
|
152
|
+
echo -e "\n ${C_SUCCESS}${BOLD}Theme '${theme_name}' is now active!${RESET}"
|
|
153
|
+
echo
|
|
154
|
+
print_step "Next steps:"
|
|
155
|
+
echo -e " ${C_DIM}1.${RESET} Reboot -> ${C_CMD}sudo reboot${RESET}"
|
|
156
|
+
echo -e " ${C_DIM}2.${RESET} Switch -> ${C_CMD}sudo theamify use <name>${RESET}"
|
|
157
|
+
echo -e " ${C_DIM}3.${RESET} Browse -> ${C_CMD}theamify list${RESET}"
|
|
158
|
+
echo
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
# -- Remove theme from GRUB (does NOT remove cache) ---------------------------
|
|
162
|
+
grub_remove_theme() {
|
|
163
|
+
local theme_name="${1}"
|
|
164
|
+
local grub_dir
|
|
165
|
+
grub_dir="$(grub_detect_dir)"
|
|
166
|
+
local dest="${grub_dir}/${theme_name}"
|
|
167
|
+
local cfg="${GRUB_CFG:-/etc/default/grub}"
|
|
168
|
+
|
|
169
|
+
[[ -d "${dest}" ]] && rm -rf "${dest}" && \
|
|
170
|
+
print_success "Removed GRUB theme dir: ${dest}"
|
|
171
|
+
|
|
172
|
+
if grep -q "${theme_name}" "${cfg}" 2>/dev/null; then
|
|
173
|
+
sed -i "/GRUB_THEME=.*${theme_name}.*/d" "${cfg}"
|
|
174
|
+
print_success "Removed theme from GRUB config."
|
|
175
|
+
local update_cmd
|
|
176
|
+
update_cmd="$(grub_detect_update_cmd)" && eval "${update_cmd}" || \
|
|
177
|
+
print_warning "GRUB rebuild had issues. Run update-grub manually."
|
|
178
|
+
fi
|
|
179
|
+
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# -----------------------------------------------------------------------------
|
|
3
|
+
# lib/themes.sh - Theme Registry CRUD & Download Logic
|
|
4
|
+
# -----------------------------------------------------------------------------
|
|
5
|
+
|
|
6
|
+
parse_conf() {
|
|
7
|
+
grep -v '^[[:space:]]*$' "${THEMES_CONF}" 2>/dev/null \
|
|
8
|
+
| grep -v '^#' \
|
|
9
|
+
|| true
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
theme_get_entry() {
|
|
13
|
+
local name="${1}"
|
|
14
|
+
local line
|
|
15
|
+
line="$(parse_conf | grep "^${name}|" | head -1 || true)"
|
|
16
|
+
[[ -z "${line}" ]] && return 1
|
|
17
|
+
echo "${line}"
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
theme_generate_subdir() {
|
|
21
|
+
local name="${1}" repo_path="${2}" gen_args="${3}"
|
|
22
|
+
|
|
23
|
+
local gen_script="${repo_path}/generate.sh"
|
|
24
|
+
if [[ ! -f "${gen_script}" ]]; then
|
|
25
|
+
print_error "Registry entry '${name}' uses 'generate:' but no"
|
|
26
|
+
print_error "generate.sh was found at: ${gen_script}"
|
|
27
|
+
return 1
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
local build_dir="${REPO_CACHE_DIR}/.build/${name}"
|
|
31
|
+
rm -rf "${build_dir}"
|
|
32
|
+
mkdir -p "${build_dir}"
|
|
33
|
+
|
|
34
|
+
print_step "Running: generate.sh -d <build_dir> ${gen_args}" >&2
|
|
35
|
+
# shellcheck disable=SC2086
|
|
36
|
+
if ! bash "${gen_script}" -d "${build_dir}" ${gen_args} >&2; then
|
|
37
|
+
print_error "generate.sh failed for '${name}'."
|
|
38
|
+
print_step "Reproduce manually: cd ${repo_path} && ./generate.sh -d <dir> ${gen_args}" >&2
|
|
39
|
+
return 1
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
local -a produced=()
|
|
43
|
+
while IFS= read -r -d '' d; do
|
|
44
|
+
produced+=("${d}")
|
|
45
|
+
done < <(find "${build_dir}" -mindepth 1 -maxdepth 1 -type d -print0)
|
|
46
|
+
|
|
47
|
+
if (( ${#produced[@]} == 0 )); then
|
|
48
|
+
print_error "generate.sh ran but produced no output directory in: ${build_dir}"
|
|
49
|
+
return 1
|
|
50
|
+
fi
|
|
51
|
+
if (( ${#produced[@]} > 1 )); then
|
|
52
|
+
print_warning "generate.sh produced ${#produced[@]} directories; using: $(basename "${produced[0]}")" >&2
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
echo "${produced[0]}"
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
theme_download() {
|
|
59
|
+
local name="${1}"
|
|
60
|
+
local force="${2:-}"
|
|
61
|
+
|
|
62
|
+
local line
|
|
63
|
+
line="$(theme_get_entry "${name}")" || {
|
|
64
|
+
print_error "Theme '${name}' not found in registry."
|
|
65
|
+
print_info "Run 'theamify list' to see all themes."
|
|
66
|
+
return 1
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
IFS='|' read -r t_name t_url t_sub t_desc t_source t_tags <<< "${line}"
|
|
70
|
+
local dest="${THEMES_CACHE_DIR}/${t_name}"
|
|
71
|
+
|
|
72
|
+
if [[ -d "${dest}" && "${force}" != "--force" ]]; then
|
|
73
|
+
print_info "Theme '${t_name}' is already cached."
|
|
74
|
+
print_step "Re-download with: theamify update ${t_name}"
|
|
75
|
+
return 0
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
print_section "Downloading: ${t_name}"
|
|
79
|
+
echo -e " ${C_DIM}${t_desc}${RESET}"
|
|
80
|
+
echo -e " ${C_LINK}${t_url}${RESET}"
|
|
81
|
+
echo
|
|
82
|
+
|
|
83
|
+
check_deps_required
|
|
84
|
+
|
|
85
|
+
print_step "[1/4] Fetching repository..."
|
|
86
|
+
local force_clone="false"
|
|
87
|
+
[[ "${force}" == "--force" ]] && force_clone="true"
|
|
88
|
+
|
|
89
|
+
local repo_path
|
|
90
|
+
repo_path="$(get_repo "${t_url}" "${force_clone}")" || return 1
|
|
91
|
+
print_success "Repository ready."
|
|
92
|
+
|
|
93
|
+
print_step "[2/4] Locating theme files..."
|
|
94
|
+
|
|
95
|
+
local src_path
|
|
96
|
+
if [[ "${t_sub}" == generate:* ]]; then
|
|
97
|
+
src_path="$(theme_generate_subdir "${t_name}" "${repo_path}" "${t_sub#generate:}")" || return 1
|
|
98
|
+
elif [[ "${t_sub}" == "." ]]; then
|
|
99
|
+
src_path="${repo_path}"
|
|
100
|
+
else
|
|
101
|
+
src_path="${repo_path}/${t_sub}"
|
|
102
|
+
if [[ ! -d "${src_path}" ]]; then
|
|
103
|
+
local found_dir
|
|
104
|
+
found_dir="$(find "${repo_path}" -maxdepth 4 -type d \
|
|
105
|
+
-iname "$(basename "${t_sub}")" 2>/dev/null | head -1 || true)"
|
|
106
|
+
if [[ -n "${found_dir}" ]]; then
|
|
107
|
+
print_warning "Subdir '${t_sub}' not found exactly; using: ${found_dir}"
|
|
108
|
+
src_path="${found_dir}"
|
|
109
|
+
else
|
|
110
|
+
print_error "Theme subdir '${t_sub}' not found in repo."
|
|
111
|
+
echo
|
|
112
|
+
print_dim " Repo contents:"
|
|
113
|
+
ls "${repo_path}" 2>/dev/null | head -20 | while read -r f; do
|
|
114
|
+
print_dim " ${f}"
|
|
115
|
+
done
|
|
116
|
+
return 1
|
|
117
|
+
fi
|
|
118
|
+
fi
|
|
119
|
+
fi
|
|
120
|
+
print_success "Source path: ${src_path}"
|
|
121
|
+
|
|
122
|
+
print_step "[3/4] Caching theme..."
|
|
123
|
+
[[ -d "${dest}" ]] && rm -rf "${dest}"
|
|
124
|
+
mkdir -p "${dest}"
|
|
125
|
+
cp -r "${src_path}/." "${dest}/"
|
|
126
|
+
|
|
127
|
+
rm -rf "${dest}/.git" "${dest}/.github" 2>/dev/null || true
|
|
128
|
+
|
|
129
|
+
local preview_img
|
|
130
|
+
preview_img="$(find "${dest}" -maxdepth 3 \
|
|
131
|
+
\( -name "*.png" -o -name "*.jpg" -o -name "*.jpeg" \) \
|
|
132
|
+
-not -path "*/.git/*" 2>/dev/null | sort | head -1 || true)"
|
|
133
|
+
if [[ -n "${preview_img}" ]]; then
|
|
134
|
+
mkdir -p "${dest}/.preview"
|
|
135
|
+
cp "${preview_img}" "${dest}/.preview/" 2>/dev/null || true
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
print_success "Cached: ${dest}"
|
|
139
|
+
|
|
140
|
+
print_step "[4/4] Validating..."
|
|
141
|
+
local theme_txt
|
|
142
|
+
theme_txt="$(find_theme_txt "${dest}")" || {
|
|
143
|
+
print_warning "No theme.txt found in '${t_name}'."
|
|
144
|
+
print_dim " This usually means the repo has no static theme folder and"
|
|
145
|
+
print_dim " no compatible generate.sh - SUBDIR may need fixing, or the"
|
|
146
|
+
print_dim " repo needs a 'generate:<args>' entry (see CONTRIBUTING.md)."
|
|
147
|
+
print_dim " Check the repo README for instructions:"
|
|
148
|
+
print_dim " ${t_url}"
|
|
149
|
+
print_dim " Cache is saved at: ${dest}"
|
|
150
|
+
echo
|
|
151
|
+
return 1
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
print_success "Valid theme - theme.txt: ${theme_txt}"
|
|
155
|
+
echo
|
|
156
|
+
echo -e " ${C_DIM}Apply with:${RESET} ${C_CMD}sudo theamify use ${t_name}${RESET}"
|
|
157
|
+
echo -e " ${C_DIM}Preview at:${RESET} ${C_CMD}theamify info ${t_name}${RESET}"
|
|
158
|
+
echo
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
theme_remove_cache() {
|
|
162
|
+
local name="${1}"
|
|
163
|
+
|
|
164
|
+
theme_get_entry "${name}" &>/dev/null || {
|
|
165
|
+
print_error "Theme '${name}' not found in registry."
|
|
166
|
+
return 1
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
local dest="${THEMES_CACHE_DIR}/${name}"
|
|
170
|
+
if [[ ! -d "${dest}" ]]; then
|
|
171
|
+
print_info "Theme '${name}' is not currently cached."
|
|
172
|
+
return 0
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
local active
|
|
176
|
+
active="$(grub_get_active_theme)"
|
|
177
|
+
if [[ "${name}" == "${active}" ]]; then
|
|
178
|
+
print_warning "Theme '${name}' is currently active in GRUB."
|
|
179
|
+
confirm "Remove cache anyway?" || { print_info "Cancelled."; return 0; }
|
|
180
|
+
else
|
|
181
|
+
confirm "Remove local cache for '${name}'?" || { print_info "Cancelled."; return 0; }
|
|
182
|
+
fi
|
|
183
|
+
|
|
184
|
+
rm -rf "${dest}"
|
|
185
|
+
print_success "Cache removed for '${name}'."
|
|
186
|
+
print_dim " Registry entry kept. Re-download: theamify get ${name}"
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
theme_add_to_registry() {
|
|
190
|
+
local url="${1}"
|
|
191
|
+
|
|
192
|
+
print_section "Add New Theme"
|
|
193
|
+
echo -e " ${C_DIM}URL: ${url}${RESET}"
|
|
194
|
+
|
|
195
|
+
if ! echo "${url}" | grep -qE '^https?://(github|gitlab)\.com/'; then
|
|
196
|
+
print_error "URL must be a GitHub or GitLab repository URL."
|
|
197
|
+
return 1
|
|
198
|
+
fi
|
|
199
|
+
|
|
200
|
+
local default_name
|
|
201
|
+
default_name="$(basename "${url%.git}")"
|
|
202
|
+
|
|
203
|
+
echo
|
|
204
|
+
echo -ne " ${C_PROMPT}Theme name${RESET} ${C_DIM}[${default_name}]${RESET}: "
|
|
205
|
+
read -r name
|
|
206
|
+
name="${name:-${default_name}}"
|
|
207
|
+
|
|
208
|
+
if parse_conf | grep -q "^${name}|"; then
|
|
209
|
+
print_error "A theme named '${name}' already exists."
|
|
210
|
+
return 1
|
|
211
|
+
fi
|
|
212
|
+
|
|
213
|
+
echo -ne " ${C_PROMPT}Subdir within repo${RESET} ${C_DIM}[.]${RESET} ${C_DIM}(or 'generate:<args>' if the repo builds via its own generate.sh)${RESET}: "
|
|
214
|
+
read -r subdir
|
|
215
|
+
subdir="${subdir:-.}"
|
|
216
|
+
|
|
217
|
+
echo -ne " ${C_PROMPT}Description${RESET}: "
|
|
218
|
+
read -r desc
|
|
219
|
+
desc="${desc:-Custom GRUB theme}"
|
|
220
|
+
|
|
221
|
+
echo -ne " ${C_PROMPT}Source page URL${RESET} ${C_DIM}[${url}]${RESET}: "
|
|
222
|
+
read -r source
|
|
223
|
+
source="${source:-${url}}"
|
|
224
|
+
|
|
225
|
+
echo -ne " ${C_PROMPT}Tags${RESET} ${C_DIM}(comma-separated)${RESET}: "
|
|
226
|
+
read -r tags
|
|
227
|
+
tags="${tags:-custom}"
|
|
228
|
+
|
|
229
|
+
local entry="${name}|${url}|${subdir}|${desc}|${source}|${tags}"
|
|
230
|
+
|
|
231
|
+
echo
|
|
232
|
+
print_section "Preview Entry"
|
|
233
|
+
echo -e " ${C_DIM}${entry}${RESET}"
|
|
234
|
+
echo
|
|
235
|
+
|
|
236
|
+
if confirm "Add '${name}' to registry?"; then
|
|
237
|
+
echo "${entry}" >> "${THEMES_CONF}"
|
|
238
|
+
print_success "Theme '${name}' added to registry!"
|
|
239
|
+
echo -e " ${C_DIM}Download:${RESET} ${C_CMD}theamify get ${name}${RESET}"
|
|
240
|
+
echo -e " ${C_DIM}Apply:${RESET} ${C_CMD}sudo theamify use ${name}${RESET}"
|
|
241
|
+
else
|
|
242
|
+
print_info "Cancelled."
|
|
243
|
+
fi
|
|
244
|
+
echo
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
theme_delete_from_registry() {
|
|
248
|
+
local name="${1}"
|
|
249
|
+
|
|
250
|
+
local line
|
|
251
|
+
line="$(theme_get_entry "${name}")" || {
|
|
252
|
+
print_error "Theme '${name}' not found in registry."
|
|
253
|
+
return 1
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
local active
|
|
257
|
+
active="$(grub_get_active_theme)"
|
|
258
|
+
if [[ "${name}" == "${active}" ]]; then
|
|
259
|
+
print_error "Cannot delete the currently active GRUB theme '${name}'."
|
|
260
|
+
print_step "Switch to another theme first: sudo theamify use <other>"
|
|
261
|
+
return 1
|
|
262
|
+
fi
|
|
263
|
+
|
|
264
|
+
echo -e " ${C_WARNING}Entry to delete:${RESET}"
|
|
265
|
+
echo -e " ${C_DIM}${line}${RESET}"
|
|
266
|
+
echo
|
|
267
|
+
|
|
268
|
+
confirm "Permanently delete '${name}' from registry?" || {
|
|
269
|
+
print_info "Cancelled."
|
|
270
|
+
return 0
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
local dest="${THEMES_CACHE_DIR}/${name}"
|
|
274
|
+
[[ -d "${dest}" ]] && rm -rf "${dest}" && print_dim " Cache removed: ${dest}"
|
|
275
|
+
|
|
276
|
+
local tmp
|
|
277
|
+
tmp="$(mktemp)"
|
|
278
|
+
grep -v "^${name}|" "${THEMES_CONF}" > "${tmp}"
|
|
279
|
+
mv "${tmp}" "${THEMES_CONF}"
|
|
280
|
+
|
|
281
|
+
print_success "Theme '${name}' deleted from registry."
|
|
282
|
+
echo
|
|
283
|
+
}
|