--preview-window ~N draws the header in its own pass, resuming the body
in the middle of an image: an image arrives as one line but takes up many
rows. ~N is typically global and meant for text, while the same preview
command also renders images.
--border-label of a tmux floating pane was stored in the pane title and
read back by pane-border-format as '#{pane_title}', so any program in the
pane could replace the border text by setting the title. On Zellij the
name was only set when the pane was created. Either way the built-in
label actions could not touch it, as fzf draws no border of its own
there.
- tmux: hold the label in pane-scoped @fzf-border-label. Set the option
even without a label, so a later change-border-label has somewhere to
write. Drop select-pane -T; the pane title is left to the user
- Zellij: run 'zellij action rename-pane' on label change. 'zellij run'
has no option to set the environment of the new pane, so the command
exports the target itself
- change-border-label and transform-border-label now update the native
border, through the setter nativeLabelSetter picks by multiplexer
- The pane is named in a __FZF_INTERNAL_ variable that runProxy withholds
from the environment replay, so the same name in the outer environment
cannot redirect the update to another pane
A Kitty graphics command is an APC sequence, which tmux takes as a
request to set the pane title. A bare one therefore never reaches the
terminal and overwrites the title on the way, which is visible on the
border of a floating pane. Programs are expected to wrap it in the tmux
passthrough sequence themselves, but 'kitten icat --clear' does not,
unlike the image data it emits right after.
Sixel and iTerm2 sequences are left alone; tmux renders the former
itself and neither affects the title.
Related: #4870
Decision must not depend on threshold chain resolution
(computePreviewSize consults noSeparatorLine); separator is hidden only
when every spec in the chain displays a 'next' preview window with a
border line facing the input section. Otherwise err on the side of
showing it.
- Without active previewer, preview position is resolved from first spec
alone, so 'next' in a threshold alternative does not create input
window and cannot hide the separator
- Empty preview command is undecidable; preview(...) action can display
ad-hoc window at 'next' position at any time
- change-preview-window relayouts or repaints when separator visibility
changes even if the active spec is unchanged, without dropping preview
window forced by preview(...) action
Visible input border suppressed separator even when it draws no line
toward list section (bottom in default layout, top in reverse, vertical).
Suppress only when input border has a line on the list side; otherwise
fall through to adjacent section check.
- Requires header window to have content before treating it as facing
section; hasHeaderWindow returns true with empty header when input
border is visible
- Extract inputBorderFacesList and resolveInputBorderShape shared by
options and terminal
- Add tests for separator suppression rules
- Applies when adjacent section (header, header lines, or list) draws
border line facing input section
# No separator below header border
fzf --style full --input-border none --header foo
# Separator shown; nothing separates input from list
fzf --style full --input-border none --header foo --no-header-border
- Evaluated dynamically so change-header/toggle-header update it
- Inline header is drawn inside list border and not adjacent to input;
decide from section actually next to it
- Separator is kept when a preview window can be at 'next' position,
right next to the input section
[]rune(t.input) is a same-type conversion that aliases t.input;
in-place query edits during the paste corrupt the snapshot and skew
the paste-end comparison.
A pasted character firing a wait-arming binding swallowed the
subsequent bracketed-paste-end, leaving t.pasting set forever and
suppressing queryChanged for all later input. Let paste begin/end
through the block so pasting state is maintained and the search for
the edited query is dispatched.
Background transform results are unrelated to the block; dropping them
would silently lose completed work. Applies both the dedicated
bg-transform-* setters and actions parsed from generic bg-transform
output (inBgCallback); a 'wait' in such output joins the ongoing wait.
Searches initiated by a bg result are not waited for.
Blocks execution of subsequent actions until the current search
completes, so motion actions operate on the complete result set.
# Move to the best match only after the search is done
fzf --bind 'start:change-query(foo)+wait+best'
- Blocks when a search is in progress, will be triggered by preceding
actions, or the query was edited in the same binding; no-op otherwise
- Initial input load counts as a search in progress, so 'start:wait'
blocks until the input is fully loaded and searched
- User input ignored while blocked, including --expect keys; keys bound
to abort/cancel cancel the wait and discard pending actions instead
- Actions deferred by the wait survive across nested lists (trigger
chords) and cancel/re-arm chains
- Debounced feedback after 200ms: dimmed prompt, hidden cursor, and
(..) on the info line
- Fires like result, but only after the input stream closes
- Use for one-shot per-query actions that would otherwise re-fire on
every intermediate snapshot during loading
Close#4835
- exec(2) rejects env entries containing NUL, breaking preview and
other child commands when the input has NUL bytes
- skip the export and document the limitation
Fix#2395
Places preview adjacent to input on the list side: above input in the
default layout, below it in --layout=reverse.
fzf --preview 'cat {}' --preview-window=next
Close#4798
Async callbacks fire a later iteration than the one that scheduled
them, so newCommand/reloadSync/denylist must persist across iterations.
fzf --bind 'space:bg-transform:echo reload:date'
- every(N) fires every N seconds (fractional, floored to 0.01s)
- Encoded as tui.Every with duration in Char as milliseconds, so
every(1) and every(2) coexist as distinct keymap entries
- FZF_IDLE_TIME exposes whole seconds since the last user activity
(keystroke or mouse event); pair with every() for idle-based
patterns like auto-accept/auto-quit
Close#1211
toggle-preview-wrap (and -wrap-word) modifies t.activePreviewOpts.wrap,
but change-preview-window resets t.previewOpts to t.initialPreviewOpts,
discarding the user's toggle. Carry wrap and wrapWord over so toggles
survive a layout change. Explicit wrap / nowrap tokens in the new spec
still win, so cycling and the empty-token reset are unaffected.
Close#4791
Mirror of the earlier change-header fix. The inline footer slot's row
budget depends on footer content length, but resizeIfNeeded() tolerates
a shorter-than-wanted inline window, so extra lines get clipped. Drive
a redraw on length change to re-run the layout.
New --border=dashed / --list-border=dashed / --header-border=dashed etc.
Uses U+2576 (╶) for horizontal edges and U+2506 (┆) for verticals, with
rounded corners (╭╮╰╯) and sharp T-junction mids (├┤). Terminal cells
are taller than wide (~2:1), so horizontals use a sparse stub per cell
while verticals need more dashes per cell to look evenly dashed.
Works with inline sections.
The inline header slot's row budget depends on header content length,
but resizeIfNeeded() tolerates a shorter-than-wanted inline window, so
the stale slot stays. Drive a redraw on length change to re-run the
layout.
--header-first previously was rejected with --header-border=inline or
--header-lines-border=inline. Now, inline placement wins: an inline
section stays inside the list frame, and --header-first only affects
non-inline sections (mainly the main --header).
The guard fires when hasHeaderWindow() returned false at resize time,
not when addInline had no budget (placeInlineStack always leaves a
non-nil 0-height placeholder).
InitTheme was called before the runtime coerced BorderInline to
BorderLine, so HeaderBorder / FooterBorder inherited from ListBorder
even when the effective shape was 'line'. Mirror the coercion so
color inheritance matches the rendered shape.
New BorderShape that embeds the section inside the --list-border
frame, joined to the list content by a horizontal separator with
T-junctions where the list shape has side borders. Requires a list
border with both top and bottom segments; falls back to 'line'
otherwise. Stacks when multiple sections are inline.
Sections inherit --color list-border by default and are colored as a
uniform block via their own --color *-border and *-bg.
Incompatible with --header-first. --header-border=inline requires
--header-lines-border to be inline or unset.
Separate item identity from cursor tracking:
- Add --id-nth=NTH to define item identity fields for cross-reload ops
- --track reverts to a simple boolean flag
- track-current action no longer accepts nth argument
- With --multi, selections are preserved across reload-sync by matching
identity keys in the reloaded list
Close#4718Close#4701Close#4483Close#4409Close#3460Close#2441
Allow --track to accept an optional nth expression for cross-reload
tracking. When a reload is triggered, fzf extracts a tracking key from
the current item using the nth expression, blocks the UI, and searches
for a matching item in the reloaded list.
- --track=.. tracks by entire line, --track=1 by first field, etc.
- --track without NTH retains existing index-based behavior
- UI is blocked during search (dimmed query, hidden cursor, +T*/+t*)
- reload unblocks eagerly on match; reload-sync waits for stream end
- Escape/Ctrl-C cancels blocked state without quitting
- track-current action accepts optional nth: track-current(1)
- Validate nth expression at parse time for both --track and track()
- Cache trackKeyFor results per item to avoid redundant computation
- Rename executeRegexp to argActionRegexp
Close#4701Close#3460
Fixes bugs reported in https://github.com/junegunn/fzf/pull/4703:
* Clamp followOffset return value to avoid going past the end of lines
* Account for t.previewed.filled when determining scrollability
* Fix nth attr merge order to respect precedence hierarchy
nth attrs were merged ON TOP of current-fg/selected-fg attrs, so
nth:regular would clear attrs like underline from current-fg. Fix the
merge chain to apply nth BEFORE the line-type overlay:
fg < nth < selected-fg < current-fg < hl < selected-hl < current-hl
Store raw current-fg and selected-fg attrs in ColorTheme before they
get merged with fg/ListFg, then pass them as nthOverlay through
printHighlighted to colorOffsets where the correct merge chain is
computed: fgAttr.Merge(nthAttr).Merge(nthOverlay).
Fix#4687
* Make current-fg inherit from list-fg instead of fg
current-fg was inheriting from fg, not list-fg, so setting list-fg
had no effect on the current line. e.g.
fzf --color fg:dim,list-fg:underline,nth:regular -d / --nth -1
The non-nth part of the current line was dim (from fg) instead of
underline (from list-fg). Resolve ListFg/ListBg earlier in InitTheme
so Current can inherit from them.
* Make selected-fg inherit from list-fg via merge instead of override
selected-fg used o() which replaces the attr from list-fg entirely.
e.g. with fg:dim,selected-fg:italic, the dim was lost on selected
lines because o() replaced dim with italic instead of merging them.
Use ColorAttr.Merge() so attrs are combined additively, consistent
with how current-fg inherits from list-fg.
* Apply selected-fg attrs on current line when item is selected
When an item is both current and selected, the current-line rendering
ignored selected-fg attrs entirely. e.g.
echo foo | fzf --color fg:dim,nth:regular,current-fg:underline,selected-fg:italic --bind result:select --multi
The italic from selected-fg was lost. Merge NthSelectedAttr into the
overlay and rebuild colBase attr with the correct precedence chain:
fg < selected-fg < current-fg.
All input lines now enter the chunklist with sequential indices, and
header lines are excluded from matching via Pattern.startIndex and
PassMerger offset. This allows the number of header lines to be changed
at runtime with change-header-lines(N), transform-header-lines, and
bg-transform-header-lines actions.
- Remove EvtHeader event; header items are read directly from chunks
- Add startIndex to Pattern and PassMerger for skipping header items
- Add targetIndex field to Terminal for cursor repositioning across
header-lines changes
Close#4659