fpf-cli 1.6.35 → 1.6.37

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.
Files changed (3) hide show
  1. package/README.md +2 -1
  2. package/fpf +206 -8
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -98,7 +98,8 @@ Installed packages are marked with `*` in the result list.
98
98
  - `FPF_DYNAMIC_RELOAD`: `always` (default), `single`, or `never`
99
99
  - Live reload uses `change:reload` by default for reliability (`ctrl-r` uses the same reload command).
100
100
  - Set `FPF_DYNAMIC_RELOAD_TRANSPORT=ipc` to opt into `--listen` + IPC query notifications on supported `fzf` builds.
101
- - Set `FPF_LOADING_INDICATOR=0` to disable the pre-search loading spinner.
101
+ - Startup now shows a DynamicProgress-style pre-search bar with concurrent per-manager task text + elapsed time.
102
+ - Set `FPF_LOADING_INDICATOR=0` to disable the pre-search loading indicator.
102
103
  - `FPF_RELOAD_MIN_CHARS`: minimum query length before live reload (default `2`)
103
104
  - `FPF_RELOAD_DEBOUNCE`: reload debounce seconds (default `0.12`)
104
105
  - `FPF_DISABLE_INSTALLED_CACHE=1` disables installed-package marker cache
package/fpf CHANGED
@@ -3,7 +3,7 @@
3
3
  set -euo pipefail
4
4
 
5
5
  SCRIPT_NAME="fpf"
6
- SCRIPT_VERSION="1.6.35"
6
+ SCRIPT_VERSION="1.6.37"
7
7
  TMP_ROOT="${TMPDIR:-/tmp}/fpf"
8
8
  SESSION_TMP_ROOT=""
9
9
  HELP_FILE=""
@@ -11,6 +11,7 @@ KBINDS_FILE=""
11
11
  CACHE_FORMAT_VERSION="1"
12
12
  CACHE_ROOT=""
13
13
  LOADING_INDICATOR_PID=""
14
+ LOADING_PROGRESS_DIR=""
14
15
 
15
16
  ACTION="search"
16
17
  MANAGER_OVERRIDE=""
@@ -43,6 +44,154 @@ loading_indicator_enabled() {
43
44
  [[ -t 2 ]]
44
45
  }
45
46
 
47
+ loading_progress_begin() {
48
+ local managers=("$@")
49
+ local manager=""
50
+ local progress_file=""
51
+
52
+ loading_progress_end
53
+ loading_indicator_enabled || return 0
54
+ [[ "${#managers[@]}" -gt 0 ]] || return 0
55
+
56
+ LOADING_PROGRESS_DIR="$(mktemp -d "${SESSION_TMP_ROOT}/loading-progress.XXXXXX")"
57
+ for manager in "${managers[@]-}"; do
58
+ progress_file="${LOADING_PROGRESS_DIR}/${manager}.status"
59
+ printf "pending\tqueued\n" >"${progress_file}"
60
+ done
61
+ }
62
+
63
+ loading_progress_end() {
64
+ if [[ -z "${LOADING_PROGRESS_DIR}" ]]; then
65
+ return 0
66
+ fi
67
+
68
+ if [[ -d "${LOADING_PROGRESS_DIR}" ]]; then
69
+ rm -rf "${LOADING_PROGRESS_DIR}" >/dev/null 2>&1 || true
70
+ fi
71
+
72
+ LOADING_PROGRESS_DIR=""
73
+ }
74
+
75
+ loading_progress_mark_state() {
76
+ local manager="$1"
77
+ local state="$2"
78
+ local detail="${3:-}"
79
+ local progress_file=""
80
+
81
+ if [[ -z "${LOADING_PROGRESS_DIR}" || ! -d "${LOADING_PROGRESS_DIR}" ]]; then
82
+ return 0
83
+ fi
84
+
85
+ progress_file="${LOADING_PROGRESS_DIR}/${manager}.status"
86
+ printf "%s\t%s\n" "${state}" "${detail}" >"${progress_file}" 2>/dev/null || true
87
+ }
88
+
89
+ loading_progress_mark_running() {
90
+ local manager="$1"
91
+ local detail="${2:-working}"
92
+ loading_progress_mark_state "${manager}" "running" "${detail}"
93
+ }
94
+
95
+ loading_progress_mark_done() {
96
+ local manager="$1"
97
+ local detail="${2:-done}"
98
+ loading_progress_mark_state "${manager}" "done" "${detail}"
99
+ }
100
+
101
+ loading_progress_repeat_char() {
102
+ local count="$1"
103
+ local char="$2"
104
+ local chunk=""
105
+
106
+ if ! [[ "${count}" =~ ^[0-9]+$ ]] || [[ "${count}" -le 0 ]]; then
107
+ return 0
108
+ fi
109
+
110
+ printf -v chunk "%*s" "${count}" ""
111
+ printf "%s" "${chunk// /${char}}"
112
+ }
113
+
114
+ loading_progress_snapshot() {
115
+ local status_file=""
116
+ local manager=""
117
+ local status=""
118
+ local detail=""
119
+ local bar_width="${FPF_LOADING_BAR_WIDTH:-26}"
120
+ local total=0
121
+ local done=0
122
+ local running=0
123
+ local queued=0
124
+ local progress_percent=0
125
+ local filled_width=0
126
+ local remaining_width=0
127
+ local bar=""
128
+ local -a active_details=()
129
+ local active_text=""
130
+ local phase_text=""
131
+ local idx
132
+
133
+ if [[ -z "${LOADING_PROGRESS_DIR}" || ! -d "${LOADING_PROGRESS_DIR}" ]]; then
134
+ return 0
135
+ fi
136
+
137
+ if ! [[ "${bar_width}" =~ ^[0-9]+$ ]] || [[ "${bar_width}" -lt 10 ]]; then
138
+ bar_width=26
139
+ fi
140
+ if [[ "${bar_width}" -gt 60 ]]; then
141
+ bar_width=60
142
+ fi
143
+
144
+ for status_file in "${LOADING_PROGRESS_DIR}"/*.status; do
145
+ [[ -f "${status_file}" ]] || continue
146
+ total=$((total + 1))
147
+ manager="$(basename "${status_file}" .status)"
148
+ IFS=$'\t' read -r status detail <"${status_file}" || true
149
+ case "${status}" in
150
+ done)
151
+ done=$((done + 1))
152
+ ;;
153
+ running)
154
+ running=$((running + 1))
155
+ active_details+=("$(manager_label "${manager}"): ${detail:-working}")
156
+ ;;
157
+ *)
158
+ queued=$((queued + 1))
159
+ ;;
160
+ esac
161
+ done
162
+
163
+ [[ "${total}" -gt 0 ]] || return 0
164
+
165
+ progress_percent=$((done * 100 / total))
166
+ filled_width=$((done * bar_width / total))
167
+ if [[ "${done}" -ge "${total}" ]]; then
168
+ bar="$(loading_progress_repeat_char "${bar_width}" "=")"
169
+ else
170
+ remaining_width=$((bar_width - filled_width - 1))
171
+ bar="$(loading_progress_repeat_char "${filled_width}" "=")>"
172
+ if [[ "${remaining_width}" -gt 0 ]]; then
173
+ bar+=$(printf "%${remaining_width}s" "")
174
+ fi
175
+ fi
176
+
177
+ if [[ "${#active_details[@]}" -gt 0 ]]; then
178
+ active_text=""
179
+ for idx in "${!active_details[@]}"; do
180
+ if [[ -n "${active_text}" ]]; then
181
+ active_text+="; "
182
+ fi
183
+ active_text+="${active_details[$idx]}"
184
+ done
185
+ phase_text="active: ${active_text}"
186
+ elif [[ "${queued}" -gt 0 ]]; then
187
+ phase_text="starting queued managers (${queued} remaining)"
188
+ else
189
+ phase_text="finalizing combined results"
190
+ fi
191
+
192
+ printf "[%s] %3s%% (%s/%s) | %s" "${bar}" "${progress_percent}" "${done}" "${total}" "${phase_text}"
193
+ }
194
+
46
195
  start_loading_indicator() {
47
196
  local message="${1:-Loading}"
48
197
 
@@ -54,11 +203,27 @@ start_loading_indicator() {
54
203
 
55
204
  (
56
205
  trap 'exit 0' TERM INT
57
- local frames='|/-\'
58
- local frame_index=0
206
+ local started_epoch
207
+ local elapsed_seconds=0
208
+ local snapshot=""
209
+
210
+ started_epoch="$(date +%s)"
211
+ if ! [[ "${started_epoch}" =~ ^[0-9]+$ ]]; then
212
+ started_epoch=0
213
+ fi
214
+
59
215
  while true; do
60
- printf "\r\033[2K%s [%s]" "${message}" "${frames:frame_index:1}" >&2
61
- frame_index=$(((frame_index + 1) % 4))
216
+ elapsed_seconds="$(( $(date +%s) - started_epoch ))"
217
+ if [[ "${elapsed_seconds}" -lt 0 ]]; then
218
+ elapsed_seconds=0
219
+ fi
220
+
221
+ snapshot="$(loading_progress_snapshot)"
222
+ if [[ -n "${snapshot}" ]]; then
223
+ printf "\r\033[2K%s %s | elapsed: %ss" "${message}" "${snapshot}" "${elapsed_seconds}" >&2
224
+ else
225
+ printf "\r\033[2K%s [working] | elapsed: %ss" "${message}" "${elapsed_seconds}" >&2
226
+ fi
62
227
  sleep 0.1
63
228
  done
64
229
  ) &
@@ -929,6 +1094,7 @@ run_preview_item_action() {
929
1094
 
930
1095
  cleanup_session_tmp_root() {
931
1096
  stop_loading_indicator
1097
+ loading_progress_end
932
1098
 
933
1099
  if [[ -n "${SESSION_TMP_ROOT}" && -d "${SESSION_TMP_ROOT}" ]]; then
934
1100
  rm -rf "${SESSION_TMP_ROOT}"
@@ -2780,6 +2946,10 @@ collect_search_display_rows() {
2780
2946
  part_files+=("${part_file}")
2781
2947
 
2782
2948
  (
2949
+ local local_source_file
2950
+ local result_count=0
2951
+
2952
+ loading_progress_mark_running "${manager}" "querying package index"
2783
2953
  local_source_file="$(mktemp "${SESSION_TMP_ROOT}/source.XXXXXX")"
2784
2954
  manager_search_entries "${manager}" "${query}" >"${local_source_file}" || true
2785
2955
  if [[ -s "${local_source_file}" ]]; then
@@ -2792,6 +2962,11 @@ collect_search_display_rows() {
2792
2962
  ' "${local_source_file}" >"${part_file}"
2793
2963
  fi
2794
2964
  rm -f "${local_source_file}"
2965
+
2966
+ if [[ -s "${part_file}" ]]; then
2967
+ result_count="$(awk 'END { print NR + 0 }' "${part_file}")"
2968
+ fi
2969
+ loading_progress_mark_done "${manager}" "${result_count} packages matched"
2795
2970
  ) &
2796
2971
  gather_pids+=("$!")
2797
2972
  done
@@ -2842,6 +3017,9 @@ collect_installed_display_rows() {
2842
3017
 
2843
3018
  (
2844
3019
  local local_source_file
3020
+ local result_count=0
3021
+
3022
+ loading_progress_mark_running "${manager}" "reading installed package list"
2845
3023
  local_source_file="$(mktemp "${SESSION_TMP_ROOT}/source.XXXXXX")"
2846
3024
  manager_installed_entries "${manager}" >"${local_source_file}" || true
2847
3025
  if [[ -s "${local_source_file}" ]]; then
@@ -2854,6 +3032,11 @@ collect_installed_display_rows() {
2854
3032
  ' "${local_source_file}" >"${part_file}"
2855
3033
  fi
2856
3034
  rm -f "${local_source_file}"
3035
+
3036
+ if [[ -s "${part_file}" ]]; then
3037
+ result_count="$(awk 'END { print NR + 0 }' "${part_file}")"
3038
+ fi
3039
+ loading_progress_mark_done "${manager}" "${result_count} installed packages"
2857
3040
  ) &
2858
3041
  gather_pids+=("$!")
2859
3042
  done
@@ -3405,9 +3588,19 @@ main() {
3405
3588
  ensure_fzf "${managers[@]-}"
3406
3589
 
3407
3590
  if [[ "${ACTION}" == "search" ]]; then
3408
- run_with_loading_indicator "Loading packages from ${manager_display}" collect_search_display_rows "${query}" "${display_file}" "${managers[@]-}"
3591
+ loading_progress_begin "${managers[@]-}"
3592
+ if ! run_with_loading_indicator "Loading packages from ${manager_display}" collect_search_display_rows "${query}" "${display_file}" "${managers[@]-}"; then
3593
+ loading_progress_end
3594
+ die "Failed to load package search results."
3595
+ fi
3596
+ loading_progress_end
3409
3597
  else
3410
- run_with_loading_indicator "Loading installed packages from ${manager_display}" collect_installed_display_rows "${display_file}" "${managers[@]-}"
3598
+ loading_progress_begin "${managers[@]-}"
3599
+ if ! run_with_loading_indicator "Loading installed packages from ${manager_display}" collect_installed_display_rows "${display_file}" "${managers[@]-}"; then
3600
+ loading_progress_end
3601
+ die "Failed to load installed package results."
3602
+ fi
3603
+ loading_progress_end
3411
3604
  fi
3412
3605
 
3413
3606
  if [[ ! -s "${display_file}" ]]; then
@@ -3450,7 +3643,12 @@ main() {
3450
3643
  if dynamic_reload_enabled "${#managers[@]}"; then
3451
3644
  if [[ -n "${query}" ]]; then
3452
3645
  reload_fallback_file="$(mktemp "${SESSION_TMP_ROOT}/reload-fallback.XXXXXX")"
3453
- run_with_loading_indicator "Preparing baseline search results" collect_search_display_rows "" "${reload_fallback_file}" "${managers[@]-}"
3646
+ loading_progress_begin "${managers[@]-}"
3647
+ if ! run_with_loading_indicator "Preparing baseline search results" collect_search_display_rows "" "${reload_fallback_file}" "${managers[@]-}"; then
3648
+ loading_progress_end
3649
+ die "Failed to prepare baseline search results."
3650
+ fi
3651
+ loading_progress_end
3454
3652
  if [[ ! -s "${reload_fallback_file}" ]]; then
3455
3653
  cp "${display_file}" "${reload_fallback_file}"
3456
3654
  fi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fpf-cli",
3
- "version": "1.6.35",
3
+ "version": "1.6.37",
4
4
  "description": "Cross-platform fuzzy package finder powered by fzf",
5
5
  "bin": {
6
6
  "fpf": "fpf"