fpf-cli 1.6.9 → 1.6.10

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 +5 -3
  2. package/fpf +95 -17
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -34,11 +34,11 @@ fpf -U
34
34
 
35
35
  By default, `fpf` auto-detects your package manager.
36
36
 
37
- On every OS, default auto mode now includes all supported package managers that are detected on your system in one combined list.
37
+ On every OS, default auto mode includes all supported detected managers. If both `bun` and `npm` are available, auto mode keeps `bun` and skips `npm`.
38
38
 
39
39
  For no-query startup (`fpf`), each manager uses a lighter default query and per-manager result cap to keep startup responsive.
40
40
 
41
- Live reload is enabled by default for single-manager mode (`--manager`), and disabled by default in all-manager mode to avoid lag/flicker while navigating.
41
+ Live reload is enabled by default, with a minimum query length and debounce to reduce lag while typing.
42
42
 
43
43
  ## Supported Managers
44
44
 
@@ -88,5 +88,7 @@ Installed packages are marked with `*` in the result list.
88
88
  - Requires: `bash` + `fzf`
89
89
  - If `fzf` is missing, `fpf` auto-installs it using a compatible detected manager.
90
90
  - Root managers (`apt`, `dnf`, `pacman`, `zypper`, `emerge`, `snap`) use `sudo` when needed.
91
- - `FPF_DYNAMIC_RELOAD`: `single` (default), `always`, or `never`
91
+ - `FPF_DYNAMIC_RELOAD`: `always` (default), `single`, or `never`
92
+ - `FPF_RELOAD_MIN_CHARS`: minimum query length before live reload (default `2`)
93
+ - `FPF_RELOAD_DEBOUNCE`: reload debounce seconds (default `0.12`)
92
94
  - `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.9"
6
+ SCRIPT_VERSION="1.6.10"
7
7
  TMP_ROOT="${TMPDIR:-/tmp}/fpf"
8
8
  SESSION_TMP_ROOT=""
9
9
  HELP_FILE=""
@@ -300,8 +300,8 @@ detect_default_manager() {
300
300
  if command_exists winget; then printf "winget"; return; fi
301
301
  if command_exists choco; then printf "choco"; return; fi
302
302
  if command_exists scoop; then printf "scoop"; return; fi
303
- if command_exists npm; then printf "npm"; return; fi
304
303
  if command_exists bun; then printf "bun"; return; fi
304
+ if command_exists npm; then printf "npm"; return; fi
305
305
  fi
306
306
 
307
307
  if [[ "${os}" == "Linux" ]]; then
@@ -349,16 +349,16 @@ detect_default_manager() {
349
349
  if command_exists emerge; then printf "emerge"; return; fi
350
350
  if command_exists snap; then printf "snap"; return; fi
351
351
  if command_exists flatpak; then printf "flatpak"; return; fi
352
- if command_exists npm; then printf "npm"; return; fi
353
352
  if command_exists bun; then printf "bun"; return; fi
353
+ if command_exists npm; then printf "npm"; return; fi
354
354
  fi
355
355
 
356
356
  if command_exists brew; then printf "brew"; return; fi
357
357
  if command_exists winget; then printf "winget"; return; fi
358
358
  if command_exists choco; then printf "choco"; return; fi
359
359
  if command_exists scoop; then printf "scoop"; return; fi
360
- if command_exists npm; then printf "npm"; return; fi
361
360
  if command_exists bun; then printf "bun"; return; fi
361
+ if command_exists npm; then printf "npm"; return; fi
362
362
 
363
363
  die "Unable to auto-detect a supported package manager. Use --manager."
364
364
  }
@@ -367,10 +367,20 @@ detect_default_managers() {
367
367
  local emitted=""
368
368
  local primary_manager=""
369
369
  local manager
370
+ local prefer_bun=0
371
+
372
+ if manager_command_ready bun; then
373
+ prefer_bun=1
374
+ fi
370
375
 
371
376
  add_detected_manager() {
372
377
  local manager_name="$1"
373
378
  [[ -n "${manager_name}" ]] || return
379
+
380
+ if [[ "${prefer_bun}" -eq 1 && "${manager_name}" == "npm" ]]; then
381
+ return
382
+ fi
383
+
374
384
  case " ${emitted} " in
375
385
  *" ${manager_name} "*)
376
386
  return
@@ -385,7 +395,7 @@ detect_default_managers() {
385
395
  primary_manager="$(detect_default_manager)"
386
396
  add_detected_manager "${primary_manager}"
387
397
 
388
- for manager in apt dnf pacman zypper emerge brew winget choco scoop snap flatpak npm bun; do
398
+ for manager in apt dnf pacman zypper emerge brew winget choco scoop snap flatpak bun npm; do
389
399
  add_detected_manager "${manager}"
390
400
  done
391
401
 
@@ -610,21 +620,77 @@ join_query() {
610
620
 
611
621
  dynamic_reload_enabled() {
612
622
  local manager_count="$1"
613
- local mode="${FPF_DYNAMIC_RELOAD:-single}"
623
+ local mode="${FPF_DYNAMIC_RELOAD:-always}"
614
624
 
615
625
  case "${mode}" in
616
- always|on|1|true|yes)
626
+ always|auto|on|1|true|yes)
617
627
  return 0
618
628
  ;;
619
629
  never|off|0|false|no)
620
630
  return 1
621
631
  ;;
622
- single|auto|*)
632
+ single)
623
633
  [[ "${manager_count}" -eq 1 ]]
624
634
  ;;
635
+ *)
636
+ return 0
637
+ ;;
625
638
  esac
626
639
  }
627
640
 
641
+ rank_display_rows_by_query() {
642
+ local query="$1"
643
+ local input_file="$2"
644
+ local ranked_file
645
+
646
+ [[ -n "${query}" ]] || return 0
647
+
648
+ ranked_file="$(mktemp "${SESSION_TMP_ROOT}/ranked.XXXXXX")"
649
+
650
+ awk -F'\t' -v query="${query}" '
651
+ function lower(s) { return tolower(s) }
652
+ BEGIN {
653
+ q = lower(query)
654
+ }
655
+ {
656
+ mgr = $1
657
+ pkg = $2
658
+ desc = $3
659
+
660
+ pkg_l = lower(pkg)
661
+ desc_l = lower(desc)
662
+
663
+ score = 8
664
+ if (q != "") {
665
+ if (pkg_l == q) {
666
+ score = 0
667
+ } else if (index(pkg_l, q) == 1) {
668
+ score = 1
669
+ } else if (index(pkg_l, q) > 0) {
670
+ score = 2
671
+ } else if (index(desc_l, q) > 0) {
672
+ score = 3
673
+ }
674
+ }
675
+
676
+ mgr_score = 9
677
+ if (mgr == "bun") {
678
+ mgr_score = 0
679
+ } else if (mgr == "npm") {
680
+ mgr_score = 1
681
+ } else if (mgr == "brew") {
682
+ mgr_score = 2
683
+ }
684
+
685
+ print score "\t" mgr_score "\t" length(pkg) "\t" pkg_l "\t" $0
686
+ }
687
+ ' "${input_file}" |
688
+ sort -t $'\t' -k1,1n -k2,2n -k3,3n -k4,4 |
689
+ awk -F'\t' '{ print $5 "\t" $6 "\t" $7 }' >"${ranked_file}"
690
+
691
+ mv "${ranked_file}" "${input_file}"
692
+ }
693
+
628
694
  query_is_single_token() {
629
695
  local query="$1"
630
696
  [[ -n "${query}" && "${query}" != *[[:space:]]* ]]
@@ -661,7 +727,7 @@ manager_search_entries() {
661
727
 
662
728
  if [[ -z "${query}" ]]; then
663
729
  npm_search_limit="${FPF_NO_QUERY_NPM_LIMIT:-120}"
664
- line_limit="${FPF_NO_QUERY_RESULT_LIMIT:-200}"
730
+ line_limit="${FPF_NO_QUERY_RESULT_LIMIT:-120}"
665
731
  fi
666
732
 
667
733
  if ! [[ "${npm_search_limit}" =~ ^[0-9]+$ ]] || [[ "${npm_search_limit}" -eq 0 ]]; then
@@ -838,7 +904,7 @@ manager_search_entries() {
838
904
  exact_match_entry "${manager}" "${query}"
839
905
  } || true
840
906
  ;;
841
- esac | awk -F'\t' 'NF >= 1 { if ($2 == "") $2 = "-"; print $1 "\t" $2 }' | sort -u | {
907
+ esac | awk -F'\t' 'NF >= 1 { if ($2 == "") $2 = "-"; print $1 "\t" $2 }' | awk -F'\t' '!seen[$1]++' | {
842
908
  if [[ "${line_limit}" -gt 0 ]]; then
843
909
  awk -v limit="${line_limit}" 'NR <= limit'
844
910
  else
@@ -1073,21 +1139,33 @@ collect_search_display_rows() {
1073
1139
 
1074
1140
  if [[ -s "${output_file}" ]]; then
1075
1141
  sort -u "${output_file}" -o "${output_file}"
1142
+ rank_display_rows_by_query "${query}" "${output_file}"
1076
1143
  fi
1077
1144
  }
1078
1145
 
1079
1146
  build_dynamic_reload_command() {
1080
1147
  local manager_override="$1"
1148
+ local fallback_file="$2"
1081
1149
  local script_path="${BASH_SOURCE[0]}"
1150
+ local min_chars="${FPF_RELOAD_MIN_CHARS:-2}"
1151
+ local reload_debounce="${FPF_RELOAD_DEBOUNCE:-0.12}"
1152
+
1153
+ if ! [[ "${min_chars}" =~ ^[0-9]+$ ]]; then
1154
+ min_chars=2
1155
+ fi
1156
+
1157
+ if ! [[ "${reload_debounce}" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
1158
+ reload_debounce=0.12
1159
+ fi
1082
1160
 
1083
1161
  if [[ "${script_path}" != /* ]]; then
1084
1162
  script_path="$(pwd)/${script_path}"
1085
1163
  fi
1086
1164
 
1087
1165
  if [[ -n "${manager_override}" ]]; then
1088
- printf "FPF_SKIP_INSTALLED_MARKERS=1 %q --feed-search --manager %q -- {q} 2>/dev/null || true" "${script_path}" "${manager_override}"
1166
+ printf 'q={q}; if [ ${#q} -lt %s ]; then cat %q; else sleep %s; FPF_SKIP_INSTALLED_MARKERS=1 %q --feed-search --manager %q -- "$q" 2>/dev/null || cat %q; fi' "${min_chars}" "${fallback_file}" "${reload_debounce}" "${script_path}" "${manager_override}" "${fallback_file}"
1089
1167
  else
1090
- printf "FPF_SKIP_INSTALLED_MARKERS=1 %q --feed-search -- {q} 2>/dev/null || true" "${script_path}"
1168
+ printf 'q={q}; if [ ${#q} -lt %s ]; then cat %q; else sleep %s; FPF_SKIP_INSTALLED_MARKERS=1 %q --feed-search -- "$q" 2>/dev/null || cat %q; fi' "${min_chars}" "${fallback_file}" "${reload_debounce}" "${script_path}" "${fallback_file}"
1091
1169
  fi
1092
1170
  }
1093
1171
 
@@ -1330,9 +1408,10 @@ run_fuzzy_selector() {
1330
1408
  --delimiter=$'\t' \
1331
1409
  --with-nth=1,2,3 \
1332
1410
  --preview="${preview_cmd}" \
1333
- --preview-window=55%:wrap:border-sharp \
1411
+ --preview-window=55%:wrap:border-sharp:hidden \
1334
1412
  --layout=reverse \
1335
1413
  --marker='>>' \
1414
+ --prompt='Search> ' \
1336
1415
  --header="${header_line}" \
1337
1416
  --info=inline-right \
1338
1417
  --margin="2%,1%,2%,1%" \
@@ -1341,11 +1420,10 @@ run_fuzzy_selector() {
1341
1420
  --bind=ctrl-k:preview:"cat ${KBINDS_FILE}" \
1342
1421
  --bind=ctrl-h:preview:"cat ${HELP_FILE}" \
1343
1422
  --bind='ctrl-/:change-preview-window(hidden|)' \
1344
- --bind=ctrl-n:next-selected,ctrl-b:prev-selected \
1345
- --bind='focus:transform-preview-label:echo [{1}] {2}')
1423
+ --bind=ctrl-n:next-selected,ctrl-b:prev-selected)
1346
1424
 
1347
1425
  if [[ -n "${reload_cmd}" ]]; then
1348
- fzf_args+=(--disabled --bind="start:reload:${reload_cmd}" --bind="change:reload:${reload_cmd}")
1426
+ fzf_args+=(--bind="change:reload:${reload_cmd}")
1349
1427
  else
1350
1428
  fzf_args+=(-e)
1351
1429
  fi
@@ -1513,7 +1591,7 @@ main() {
1513
1591
  local reload_cmd=""
1514
1592
  if [[ "${ACTION}" == "search" && -z "${query}" ]]; then
1515
1593
  if dynamic_reload_enabled "${#managers[@]}"; then
1516
- reload_cmd="$(build_dynamic_reload_command "${MANAGER_OVERRIDE}")"
1594
+ reload_cmd="$(build_dynamic_reload_command "${MANAGER_OVERRIDE}" "${display_file}")"
1517
1595
  fi
1518
1596
  fi
1519
1597
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fpf-cli",
3
- "version": "1.6.9",
3
+ "version": "1.6.10",
4
4
  "description": "Cross-platform fuzzy package finder powered by fzf",
5
5
  "bin": {
6
6
  "fpf": "fpf"