Skip to content

Commit f25c9fd

Browse files
committed
feat: refactor menus, iTerm2 plist flow, fastfetch menu, broken cask detection
- brew: detect casks registered in Homebrew but missing from /Applications, offer to reinstall - customize: split Terminal into Terminal / Shell / FastFetch items - terminal: iTerm2 uses defaults export/import plist instead of static profile.json - terminal: add fastfetch_menu with install + apply config; support cat.txt logo - terminal: rename setup_shell → shell_menu (loop), remove FastFetch from shell menu - common: show_menu supports "---" separator (blank line, skipped in numbering) - security: simplify privacy menu labels, add visual separator - config: rename fastfetch.jsonc → config.jsonc, remove profile.json
1 parent a020afa commit f25c9fd

11 files changed

Lines changed: 273 additions & 109 deletions

File tree

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,24 @@
11
# Changelog
22

3+
## 29.03
4+
5+
### Features
6+
7+
- **brew:** detect broken casks (registered in Homebrew but missing from `/Applications`) — prompt to reinstall silently
8+
- **customize:** split Terminal entry into separate Terminal / Shell / FastFetch items
9+
- **terminal:** iTerm2 now uses `defaults export/import` plist flow instead of a static `profile.json`
10+
- **terminal:** `fastfetch_menu` — dedicated menu with install + apply config options; supports `cat.txt` logo
11+
- **common:** `show_menu` supports `"---"` separator — renders as blank line, skipped in numbering
12+
- **security:** simplified privacy menu labels, added visual separator
13+
14+
### Changes
15+
16+
- `setup_shell` renamed to `shell_menu` (persistent loop); FastFetch moved to its own menu
17+
- `config/shell/fastfetch.jsonc``config/shell/config.jsonc`
18+
- `config/iterm2/profile.json` removed — replaced by plist-based export/import
19+
20+
---
21+
322
## 26.03
423

524
Initial release.

apps/brew.sh

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ brew_menu() {
1212
"Utilities" \
1313
"Media" \
1414
"Communication" \
15+
"---" \
1516
"Install ALL bundles" \
1617
"Import from .brewbak" \
1718
"Export to .brewbak" \
@@ -47,9 +48,10 @@ install_bundle() {
4748
local installed
4849
installed=$(brew list --formula -1 2>/dev/null; brew list --cask -1 2>/dev/null)
4950

50-
# Parse Brewfile — split into new and already installed
51+
# Parse Brewfile — split into new, broken, and already installed
5152
local new_lines=()
5253
local new_labels=()
54+
local broken_casks=()
5355
local installed_count=0
5456
while IFS= read -r line; do
5557
[[ "$line" =~ ^[[:space:]]*#.*$ || -z "${line// /}" ]] && continue
@@ -64,26 +66,65 @@ install_bundle() {
6466
continue
6567
fi
6668
if echo "$installed" | grep -qxF "$name"; then
69+
# For casks, verify the .app actually exists in /Applications
70+
if [[ "$line" =~ ^cask ]]; then
71+
# || true prevents set -e from triggering if find exits non-zero
72+
app_path=$(find /opt/homebrew/Caskroom/"$name" -name "*.app" -maxdepth 3 2>/dev/null | head -1) || true
73+
if [[ -n "$app_path" ]]; then
74+
appname=$(basename "$app_path")
75+
if [[ ! -e "/Applications/$appname" ]]; then
76+
# Registered in brew but .app is missing — queue for silent reinstall
77+
broken_casks+=("$name")
78+
continue
79+
fi
80+
fi
81+
fi
6782
installed_count=$((installed_count + 1))
6883
else
6984
new_lines+=("$line")
7085
new_labels+=("$label")
7186
fi
7287
done < "$path"
7388

74-
# Show installed count
7589
if [[ $installed_count -gt 0 ]]; then
7690
log_ok "$installed_count already installed"
7791
fi
7892

93+
# Handle missing apps separately — ask user once, then fix silently
94+
if [[ ${#broken_casks[@]} -gt 0 ]]; then
95+
printf "\n"
96+
log_warn "${#broken_casks[@]} app(s) are missing from Applications"
97+
for cask in "${broken_casks[@]}"; do
98+
printf " ${DIM}· %s${RESET}\n" "$cask"
99+
done
100+
printf "\n"
101+
printf " ${DIM}This will reinstall the apps listed above.${RESET}\n"
102+
printf "\n"
103+
if confirm "Fix them now?"; then
104+
printf "\n"
105+
for cask in "${broken_casks[@]}"; do
106+
log_info "Reinstalling $cask..."
107+
if brew reinstall --cask "$cask"; then
108+
log_ok "$cask reinstalled"
109+
else
110+
log_warn "Failed to reinstall $cask"
111+
fi
112+
printf "\n"
113+
done
114+
printf " ${DIM}press enter to continue${RESET} "
115+
read -r < /dev/tty || true
116+
fi
117+
printf "\n"
118+
fi
119+
79120
if [[ ${#new_labels[@]} -eq 0 ]]; then
80121
log_ok "Everything installed"
81122
printf "\n"
82123
confirm "Back" || true
83124
return 0
84125
fi
85126

86-
# Multiselect only new packages
127+
# Multiselect for new packages only
87128
local selected
88129
selected=$(show_multiselect "$brewfile" "${new_labels[@]}")
89130

@@ -92,7 +133,7 @@ install_bundle() {
92133
return 0
93134
fi
94135

95-
# Build temp Brewfile with selected packages only
136+
# Build temp Brewfile with selected packages
96137
local tmp
97138
tmp=$(mktemp /tmp/macrift_brew_XXXXXX)
98139

@@ -175,6 +216,20 @@ import_brewbak() {
175216
continue
176217
fi
177218
if echo "$installed" | grep -qxF "$name"; then
219+
# For casks, verify the .app actually exists in /Applications
220+
if [[ "$line" =~ ^cask ]]; then
221+
# || true prevents set -e from triggering if find exits non-zero
222+
app_path=$(find /opt/homebrew/Caskroom/"$name" -name "*.app" -maxdepth 3 2>/dev/null | head -1) || true
223+
if [[ -n "$app_path" ]]; then
224+
appname=$(basename "$app_path")
225+
if [[ ! -e "/Applications/$appname" ]]; then
226+
# Registered in brew but .app is missing — treat as broken
227+
new_lines+=("$line")
228+
new_labels+=("$label [broken]")
229+
continue
230+
fi
231+
fi
232+
fi
178233
installed_count=$((installed_count + 1))
179234
else
180235
new_lines+=("$line")

apps/customize_menu.sh

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,22 @@ customize_menu() {
77
set_title "macrift > customize"
88
local choice
99
choice=$(show_menu "Customize" \
10-
"Terminal (iTerm2 / Ghostty)" \
10+
"Terminal" \
11+
"Shell" \
12+
"FastFetch" \
1113
"Code Editor" \
12-
"Spicetify (restore marketplace)" \
14+
"Spicetify" \
15+
"---" \
1316
"Wallpaper" \
1417
"Back")
1518

1619
case "$choice" in
1720
1) source "$MACRIFT_DIR/apps/terminal.sh" && terminal_menu ;;
18-
2) source "$MACRIFT_DIR/apps/editor.sh" && editor_menu ;;
19-
3) source "$MACRIFT_DIR/apps/spicetify.sh" && restore_marketplace ;;
20-
4) source "$MACRIFT_DIR/apps/wallpaper.sh" && wallpaper_menu ;;
21+
2) source "$MACRIFT_DIR/apps/terminal.sh" && shell_menu ;;
22+
3) source "$MACRIFT_DIR/apps/terminal.sh" && fastfetch_menu ;;
23+
4) source "$MACRIFT_DIR/apps/editor.sh" && editor_menu ;;
24+
5) source "$MACRIFT_DIR/apps/spicetify.sh" && restore_marketplace ;;
25+
6) source "$MACRIFT_DIR/apps/wallpaper.sh" && wallpaper_menu ;;
2126
0) return ;;
2227
*) ;;
2328
esac

apps/editor.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ editor_menu() {
77
set_title "macrift > editor"
88

99
local choice
10-
choice=$(show_menu "Code Editor - copy settings.json" \
10+
choice=$(show_menu "Code Editor" \
1111
"VSCode" \
1212
"Cursor" \
1313
"Windsurf" \

apps/terminal.sh

Lines changed: 108 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,14 @@ terminal_menu() {
66
clear
77
set_title "macrift > terminal"
88
local choice
9-
choice=$(show_menu "Terminal Setup" \
10-
"iTerm2 — install + import config" \
11-
"Ghostty — install + copy config" \
12-
"Shell setup (Starship + FastFetch)" \
9+
choice=$(show_menu "Terminal" \
10+
"iTerm2" \
11+
"Ghostty" \
1312
"Back")
1413

1514
case "$choice" in
1615
1) setup_iterm2 ;;
1716
2) setup_ghostty ;;
18-
3) setup_shell ;;
1917
0) return ;;
2018
*) ;;
2119
esac
@@ -27,19 +25,38 @@ setup_iterm2() {
2725

2826
if ! brew_install "iterm2" "cask"; then return; fi
2927

30-
local config_source="$MACRIFT_DIR/config/iterm2/profile.json"
31-
if [[ ! -f "$config_source" ]]; then
32-
log_warn "No iTerm2 profile found in config/iterm2/profile.json"
33-
log_info "You can add your profile there and re-run this"
34-
return
35-
fi
28+
local config_dir="$MACRIFT_DIR/config/iterm2"
29+
local config_plist="$config_dir/iterm2.plist"
3630

37-
if confirm "Import iTerm2 profile?"; then
38-
# Tell iTerm2 to use custom prefs folder
39-
defaults write com.googlecode.iterm2 PrefsCustomFolder -string "$MACRIFT_DIR/config/iterm2"
40-
defaults write com.googlecode.iterm2 LoadPrefsFromCustomFolder -bool true
41-
log_ok "iTerm2 configured to load profile from macrift config"
42-
fi
31+
mkdir -p "$config_dir"
32+
33+
local choice
34+
choice=$(show_menu "iTerm2" \
35+
"Export current settings to macrift config" \
36+
"Import settings from macrift config" \
37+
"Back")
38+
39+
case "$choice" in
40+
1)
41+
defaults export com.googlecode.iterm2 "$config_plist"
42+
log_ok "Settings exported to config/iterm2/iterm2.plist"
43+
;;
44+
2)
45+
if [[ ! -f "$config_plist" ]]; then
46+
log_err "No settings found in config/iterm2/iterm2.plist"
47+
log_info "Run export first to save your current settings"
48+
return
49+
fi
50+
if confirm "Import iTerm2 settings? (restart iTerm2 to apply)"; then
51+
defaults import com.googlecode.iterm2 "$config_plist"
52+
# Remove stale custom folder setting that causes startup errors
53+
defaults delete com.googlecode.iterm2 PrefsCustomFolder 2>/dev/null || true
54+
defaults delete com.googlecode.iterm2 LoadPrefsFromCustomFolder 2>/dev/null || true
55+
log_ok "Settings imported — restart iTerm2 to apply"
56+
fi
57+
;;
58+
0) return ;;
59+
esac
4360
}
4461

4562
setup_ghostty() {
@@ -62,25 +79,26 @@ setup_ghostty() {
6279
fi
6380
}
6481

65-
setup_shell() {
66-
divider "Shell Setup"
82+
shell_menu() {
83+
while true; do
84+
clear
85+
set_title "macrift > shell"
6786

68-
local choice
69-
choice=$(show_menu "Shell Components" \
70-
"Starship prompt" \
71-
"FastFetch" \
72-
"Copy .zshrc" \
73-
"All of the above" \
74-
"Back")
87+
local choice
88+
choice=$(show_menu "Shell" \
89+
"Starship prompt" \
90+
"Copy .zshrc" \
91+
"Both" \
92+
"Back")
7593

76-
case "$choice" in
77-
1) install_starship ;;
78-
2) install_fastfetch ;;
79-
3) install_zshrc ;;
80-
4) install_starship; install_fastfetch; install_zshrc ;;
81-
0) return ;;
82-
*) ;;
83-
esac
94+
case "$choice" in
95+
1) install_starship ;;
96+
2) install_zshrc ;;
97+
3) install_starship; install_zshrc ;;
98+
0) return ;;
99+
*) ;;
100+
esac
101+
done
84102
}
85103

86104
install_starship() {
@@ -109,15 +127,69 @@ install_starship() {
109127
install_fastfetch() {
110128
if ! brew_install "fastfetch"; then return; fi
111129

112-
local config_source="$MACRIFT_DIR/config/shell/fastfetch.jsonc"
130+
local config_source="$MACRIFT_DIR/config/shell/config.jsonc"
113131
local config_target="$HOME/.config/fastfetch/config.jsonc"
114132

115133
if [[ -f "$config_source" ]]; then
116134
if confirm "Copy FastFetch config?"; then
117135
copy_config "$config_source" "$config_target"
118136
fi
119137
else
120-
log_info "Add your fastfetch.jsonc to config/shell/ to auto-import"
138+
log_info "Add your config.jsonc to config/shell/ to auto-import"
139+
fi
140+
}
141+
142+
fastfetch_menu() {
143+
while true; do
144+
clear
145+
set_title "macrift > fastfetch"
146+
local choice
147+
choice=$(show_menu "FastFetch" \
148+
"Install FastFetch" \
149+
"Apply config from macrift" \
150+
"Back")
151+
152+
case "$choice" in
153+
1) install_fastfetch ;;
154+
2) apply_fastfetch_config ;;
155+
0) return ;;
156+
*) ;;
157+
esac
158+
done
159+
}
160+
161+
apply_fastfetch_config() {
162+
divider "FastFetch Config"
163+
164+
local config_source="$MACRIFT_DIR/config/shell/config.jsonc"
165+
local config_target="$HOME/.config/fastfetch/config.jsonc"
166+
167+
if [[ ! -f "$config_source" ]]; then
168+
log_err "No config found at config/shell/config.jsonc"
169+
return
170+
fi
171+
172+
# Warn if host format is hardcoded to a specific model
173+
if grep -q '"format"' "$config_source" && grep -A1 '"type": "host"' "$config_source" | grep -q '"format"'; then
174+
local host_format
175+
host_format=$(grep -A2 '"type": "host"' "$config_source" | grep '"format"' | sed 's/.*"format": *"\(.*\)".*/\1/')
176+
if [[ "$host_format" != "{name}" && -n "$host_format" ]]; then
177+
log_warn "Host is hardcoded to: $host_format"
178+
if confirm "Replace with dynamic {name}?"; then
179+
sed -i '' "s|\"format\": \"$host_format\"|\"format\": \"{name}\"|" "$config_source"
180+
log_ok "Fixed — will now show actual model name"
181+
fi
182+
fi
183+
fi
184+
185+
if confirm "Copy FastFetch config?"; then
186+
copy_config "$config_source" "$config_target"
187+
# Copy logo file if present
188+
local logo_source="$MACRIFT_DIR/config/shell/cat.txt"
189+
local logo_target="$HOME/.config/fastfetch/cat.txt"
190+
if [[ -f "$logo_source" ]]; then
191+
copy_config "$logo_source" "$logo_target"
192+
fi
121193
fi
122194
}
123195

common.sh

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,11 @@ show_menu() {
4343
local count=${#items[@]}
4444
local last_idx=$((count - 1))
4545

46-
# Calculate box width from longest item
46+
# Calculate box width from longest item (skip separators)
4747
local max_len=0
4848
local i
4949
for ((i=0; i<count; i++)); do
50+
[[ "${items[$i]}" == "---" ]] && continue
5051
if [[ ${#items[$i]} -gt $max_len ]]; then
5152
max_len=${#items[$i]}
5253
fi
@@ -68,9 +69,14 @@ show_menu() {
6869
printf "${RESET}\n" >&2
6970
# │ (empty) │
7071
printf " ${BOLD}${PURPLE}${RESET}%*s${BOLD}${PURPLE}${RESET}\n" "$inner_w" "" >&2
71-
# │ N › Item │ (items 1..N-1)
72+
# │ N › Item │ (items 1..N-1, "---" renders as blank separator line)
73+
local num=0
7274
for ((i=0; i<last_idx; i++)); do
73-
local num=$((i + 1))
75+
if [[ "${items[$i]}" == "---" ]]; then
76+
printf " ${BOLD}${PURPLE}${RESET}%*s${BOLD}${PURPLE}${RESET}\n" "$inner_w" "" >&2
77+
continue
78+
fi
79+
num=$((num + 1))
7480
local vis=$((2 + 1 + 3 + ${#items[$i]}))
7581
local pad=$((inner_w - vis))
7682
printf " ${BOLD}${PURPLE}${RESET} ${CYAN}%d${RESET} ${DIM}${RESET} %s%*s${BOLD}${PURPLE}${RESET}\n" "$num" "${items[$i]}" "$pad" "" >&2

0 commit comments

Comments
 (0)