Default to native border for Zellij floating pane (#4854)

The native border is the handle for moving and resizing the pane with
the mouse, so use it by default, consistent with tmux. fzf draws its
own border only when a border style is explicitly specified with
--border. Extract the shared native-border decision into a helper.
This commit is contained in:
Junegunn Choi
2026-07-06 21:04:35 +09:00
committed by GitHub
parent f7392a8b63
commit e54f11c64e
4 changed files with 55 additions and 20 deletions
+2
View File
@@ -14,6 +14,8 @@ CHANGELOG
```sh
fzf --popup --border-label ' fzf '
```
- On Zellij, `--popup` uses the native border by default, consistent with tmux, so that the pane can be moved and resized with the mouse; fzf draws its own border when a border style is explicitly specified with `--border`
- `--border-label` is set as the name of the pane, displayed on the native border
- Added `result-final` event, a variant of `result` that is not triggered while the input stream is still open (#4835)
- Use it for one-shot, per-query actions that would otherwise re-fire on every intermediate snapshot during loading
```sh
+13 -12
View File
@@ -422,19 +422,20 @@ Start fzf in a tmux or Zellij floating pane (default \fBcenter,50%\fR).
Requires tmux 3.3+ or Zellij 0.44+. This option is ignored if you
are not running fzf inside tmux or Zellij. \fB\-\-tmux\fR is an alias for this option.
On tmux 3.7 or above, the floating pane is not modal; you can switch to
other panes and windows while fzf is running, move and resize the pane with
the mouse, zoom it to fullscreen, and use copy\-mode in it. The pane always
has a native border, which is what makes it movable and resizable, so
\fBborder\-native\fR is implied. \fB\-\-border\-label\fR is set as the title
of the pane, and is displayed on the border if \fBpane\-border\-status\fR
is enabled in tmux (\fB\-\-border\-label\-pos\fR is ignored).
On tmux 3.7 or above and on Zellij, the floating pane is not modal; you can
switch to other panes and windows while fzf is running, and move and resize
the pane with the mouse. The native border of the pane is the handle for
moving and resizing it, so it is used by default and \fBborder\-native\fR is
implied. On tmux, \fB\-\-border\-label\fR is set as the title of the pane,
and is displayed on the border if \fBpane\-border\-status\fR is enabled in
tmux (\fB\-\-border\-label\-pos\fR is ignored).
A modal tmux popup is used instead on tmux versions below 3.7, or when a
border style is explicitly specified with \fB\-\-border\fR, so that the
fzf\-drawn border is the only border shown. \fBnone\fR and \fBline\fR are
treated as no border. Give \fBborder\-native\fR to
force a floating pane nonetheless.
fzf draws its own border instead when a border style is explicitly specified
with \fB\-\-border\fR, so that it is the only border shown. \fBnone\fR and
\fBline\fR are treated as no border. Give \fBborder\-native\fR to keep the
native border nonetheless. On tmux, the fzf\-drawn border is shown in a modal
popup, since the native border of a tmux floating pane cannot be removed;
this is also the case on tmux versions below 3.7.
e.g.
\fB# Popup in the center with 70% width and height
+15 -8
View File
@@ -183,17 +183,24 @@ exit "$code"`, newPane, code, signal, signal, code, code, code)
}, opts, true)
}
// Whether to use the multiplexer's native border for the floating pane. Its
// native border is the handle that makes the pane movable and resizable with
// the mouse, so it is the default; 'border-native' forces it. It is not used
// when a border style is explicitly specified with --border, so that the
// fzf-drawn border is the only one shown. 'none' and 'line' are treated as no
// border; fzf draws no box for either, and 'line' only makes sense with
// --height.
func nativeBorder(opts *Options) bool {
return opts.Tmux.border || opts.BorderShape == tui.BorderUndefined ||
opts.BorderShape == tui.BorderLine || opts.BorderShape == tui.BorderNone
}
func runTmux(args []string, opts *Options) (int, error) {
// On tmux 3.7 or above, fzf runs in a floating pane instead of a popup.
// A floating pane always has a native border, so 'border-native' is
// implied. When a border style is explicitly specified with --border, a
// When the native border is not used (an explicit --border style), a
// popup is used instead so that the fzf-drawn border is the only border
// shown; the native border of a floating pane cannot be removed. 'none'
// and 'line' are treated as no border; fzf draws no box for either, and
// 'line' only makes sense with --height. Give 'border-native' to force
// a floating pane nonetheless.
if opts.Tmux.border || opts.BorderShape == tui.BorderUndefined ||
opts.BorderShape == tui.BorderLine || opts.BorderShape == tui.BorderNone {
// shown; the native border of a tmux floating pane cannot be removed.
if nativeBorder(opts) {
if windowWidth, windowHeight, ok := tmuxFloatingPaneInfo(); ok {
opts.Tmux.border = true
argStr, dir := popupArgStr(args, opts)
+25
View File
@@ -2,9 +2,18 @@ package fzf
import (
"os/exec"
"github.com/junegunn/fzf/src/tui"
)
func runZellij(args []string, opts *Options) (int, error) {
// Use the native Zellij border by default, consistent with tmux, so that
// the pane can be moved and resized with the mouse. Set before
// popupArgStr so that it does not inject an fzf border. fzf draws its own
// border instead when a border style is explicitly specified.
if nativeBorder(opts) {
opts.Tmux.border = true
}
argStr, dir := popupArgStr(args, opts)
zellijArgs := []string{
@@ -13,6 +22,22 @@ func runZellij(args []string, opts *Options) (int, error) {
}
if !opts.Tmux.border {
zellijArgs = append(zellijArgs, "--borderless", "true")
} else {
// Set --border-label as the name of the pane, displayed on the
// native border. The label is left to fzf when it draws its own
// border with the label on it (border-native with an explicit
// --border style). Empty otherwise, to override the default name
// (the running command). Passed as a single argument in the
// --name=label form; the detached form fails to parse when the
// label starts with a hyphen. No escaping is needed beyond
// stripping ANSI sequences fzf would otherwise render itself.
// --border-label-pos is ignored.
label := ""
if opts.BorderShape == tui.BorderUndefined || opts.BorderShape == tui.BorderLine ||
opts.BorderShape == tui.BorderNone {
label, _, _ = extractColor(opts.BorderLabel.label, nil, nil)
}
zellijArgs = append(zellijArgs, "--name="+label)
}
switch opts.Tmux.position {
case posUp: