| Commit message (Collapse) | Author | Age |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A click target with no hover feedback gives you no way to tell what you are about
to hit, which matters more here than elsewhere because the buttons sit in a frame
row that is only one cell tall.
The hovered button is inverted rather than merely recoloured: swapping
fore/background reads regardless of which colours a theme uses, and makes "the
pointer is on THIS one" unmistakable next to its unhovered neighbours.
Tracked per client, because hover belongs to a pointer rather than to a pane --
with two users attached, one pointer must not light up the other's button. Stored
on the pane keyed by client id, which the frame renderer already has.
Updated before the action dispatch rather than in the hover action, because
motion over the ACTIVE pane is dispatched as SendToTerminal, not UpdateHover --
and the pane you are about to click a button in is usually the one you are
already in. Every other pane's hover for that client is cleared on each motion,
so moving between panes cannot leave a button lit behind.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Second slice of the status/action frame. A program declares buttons, and a click
is reported back to it:
printf '\033]5577;1;buttons;approve:Approve;reject:Reject\033\\'
-> click -> program's stdin receives OSC 5577 ; 1 ; click ; approve ST
The report is a FIXED format carrying only the id, never bytes the payload chose,
and ids are restricted to [A-Za-z0-9_-]. The sequence that creates a button is
in-band and untrusted -- `cat` of a hostile file emits it as readily as a
cooperating program -- so a click that could send arbitrary bytes would let
viewing a file inject into the stdin of whatever is running, a shell included.
Labels are '%'-escaped since the wire format is ';'-separated, and both the
button count and id length are capped because the list lives as long as the pane.
Positions come from one layout_status_bar function that the renderer draws from
and the hit-test resolves against. Computing that geometry twice is how the
stacked-pane picker once selected the wrong pane; a test asserts every drawn
column resolves back to the button drawn there, across 100 widths.
Two things the drive caught that unit tests could not:
- The first delivery path pushed onto grid.pending_messages_to_pty, which is
only drained when the PANE produces output -- so a click on an idle program sat
in the queue forever. The write now happens at the tab level, which owns the
senders.
- The hit-test derived the frame row from the pane's raw geometry, but this fork
draws the frame inset by gap_offset, so with `gap 1` every click missed by one
row. It now derives from the CONTENT rect, which is by definition the inside of
the drawn frame and therefore the same columns the renderer lays out.
Buttons serialise with the session like the status text, so a resurrected pane
comes back with its buttons intact.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Upstream's mouse_hover_effects governs two unrelated things: the frame highlight
on hover, and the "Ctrl <MouseScroll> or <drag borders> to resize" hint. Its own
comment in default.kdl admits as much. So silencing a beginner reminder you have
outgrown also costs you a highlight that stays useful indefinitely.
mouse_hover_help_text (default true) gates only the hint. One line in
execute_send_to_terminal; the highlight is set in execute_update_hover and is
untouched.
Config-file only, deliberately. The option has no protobuf slot, so it does not
cross the client/server IPC contract -- the roundtrip test asserts exactly that,
with the fixture set to None and a comment explaining why. It still works from
the config file because the server parses the file itself and Options::merge
keeps the file value when the client sends None; a CLI flag would be silently
ignored.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The cap made the picker a scrolling viewport -- everything stays reachable by
moving focus -- but there was no way to see the whole stack at once.
Clicking "+N more" expands it to every member; the indicator becomes "- fewer"
and collapses it again. Collapse gets its own label because an invisible way
back out is not an affordance.
Hangs off the indicator row because that row is already reserved, already
rendered, and resolves to no member, so it is free real estate. It is also why
this needs no new keybinding: mouse handling is entirely server-side, while
Action is a protobuf enum we cannot extend without protoc.
A click rather than hover, as expanding changes the reserved row count and so
resizes the visible pane's PTY -- acceptable once per deliberate click, not on
every mouse move.
All three consumers of the row -> member mapping (renderer, reserved-row
budget, mouse hit-test) now go through stack_list_window_for, so expansion
cannot desync them the way the cap desynced the hit-test in 844617b4.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Dragging showed only a green border on the target. The swap is now applied as
you drag, so the real layout IS the preview and releasing just keeps it.
Dragging elsewhere undoes it and previews the new arrangement; dragging off all
panes restores the original.
Because swap_panes is symmetric, applying it twice is the identity, so 'undo
the preview' is just the same call again. Drop needs no work at all now - the
swap already happened - which also removes the old drop-time swap.
THE TRAP, which a naive version hits immediately: once the swap is applied the
dragged pane sits under the cursor, so the next motion computes the dragged
pane as its own target, undoes the swap, which puts the original pane back
under the cursor, which re-applies it - oscillating on every motion event and
spamming both PTYs with resizes.
Avoided by treating two cases as 'the layout already shows what you would get'
and doing nothing:
- the cursor is over the dragged pane, wherever the preview has moved it;
- the cursor is over the pane it is currently swapped with, which after a
preview sits at the dragged pane's original position.
So a swap is only re-applied when the cursor reaches a THIRD pane, which also
keeps PTY resizes down to one pair per target change rather than per event.
The dragged pane is kept highlighted rather than the target, so it stays
identifiable while the layout moves around it.
Note: the state machine lives in the executor and needs a real Tab, so it is
reasoned through in the commit and README rather than unit-tested; the
decision-level tests cover only which action is produced.
Baselines held: zellij-server 1441/30.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Dragging a pane by its title was blind until release. The pane a drop would
land on is now highlighted with the theme's frame_highlight colour.
Reuses the existing hover highlight rather than adding a second highlighting
mechanism, but sets tab.mouse_hover_pane_id directly instead of going through
execute_update_hover, which is gated on the mouse_hover_effects option. During
a drag this is essential feedback rather than decoration, so it must not be
switchable off.
The pane being dragged is deliberately NOT highlighted when hovered: dropping
onto itself is a no-op, so showing no highlight is the honest signal that
releasing there changes nothing.
The highlight is cleared on drop, so a completed drag does not leave a pane
looking hovered.
Baselines held: zellij-server 1441/30.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Dragging a tiled pane by its title bar and dropping it on another pane swaps
the two. Previously the only way to rearrange panes was MOVE mode.
The underlying operation already existed: the directional move_pane_* functions
are all the same geometry swap, differing only in how they pick the other pane.
Extracted as TiledPanes::swap_panes(a, b) so a pane can be swapped with an
arbitrary target - the one a drag was dropped onto - which also de-duplicates
what was four copies of the same swap.
Gesture: the title row EXCLUDING its corners. Corners stay resize handles, so
two-axis resize from the top is preserved. Plain top-edge resize is gone and
costs nothing: every boundary is shared, so the boundary above a pane is the
bottom edge of the pane above and can be dragged from there. A pane at the top
of the viewport has the screen edge above it, which was never resizable.
Ordering in determine_mouse_action matters three ways:
- an in-progress drag is checked FIRST, so motion cannot be reinterpreted as
text selection or a resize;
- the drag check sits AFTER the double-click check, so the second click of a
rename is not swallowed;
- and BEFORE the resize branch, which would otherwise claim the title row.
Two behaviours had to be preserved deliberately:
- Dropping a pane on itself is a no-op, which is what keeps a plain click and
a double-click working on the title bar.
- Starting a drag focuses the pane. Pressing a title used to start a resize,
which focused it as a side effect; without this a plain click on the title
would do nothing at all.
Stacked panes are excluded from the handle. Their title row is the affordance
for focusing/expanding a stack member, and intercepting it broke
focus_stacked_pane_under_flexible_pane_with_the_mouse - caught by the suite,
not by hand.
Baselines held: zellij-server 1440/30 (the 30 are pre-existing).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Enter/Esc still did not leave rename mode after entering it by double-click.
The previous attempt fixed a real trap (a None base_mode makes
change_to_default_mode a silent no-op) but not this one. The actual cause:
zellij tracks the client's input mode in TWO places.
- Screen's ModeInfo, which drives what is rendered.
- SessionData::current_input_modes on the server, which is what ROUTES key
input to the right handler.
The double-click path sent ScreenInstruction::ChangeMode directly, so only the
first was updated: the rename prompt appeared and accepted text, while the
server still interpreted keys in the previous mode - so Enter and Esc were
never treated as rename-mode keys and there was no way out.
Action::SwitchToMode (the rename keybind) sends ServerInstruction::ChangeMode
instead, and the server handler updates current_input_modes AND derives
base_mode from the session config before forwarding to Screen. The mouse path
now sends exactly that, so it is the same code path as the keybind rather than
an imitation of it.
Tab::client_base_mode is dropped: the server supplies base_mode correctly, so
it is no longer needed.
Lesson worth keeping: 'send the instruction the existing feature sends' beats
reconstructing its effects downstream. Both bugs here came from entering the
system one layer too low.
Baselines held: zellij-server 1429/30.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Double-click-to-rename trapped the client: once in RenamePane, Esc and Enter
did nothing and there was no way out without killing the session.
Cause: Screen::change_mode() does `mode_info.base_mode = base_mode`, i.e. it
OVERWRITES the client's base mode with whatever it is handed. The
double-click path passed None, believing that meant 'leave it alone'. It does
not - ModeInfo::change_to_default_mode() is:
if let Some(base_mode) = self.base_mode { self.mode = base_mode; }
so a None base_mode makes leaving ANY temporary mode a silent no-op. The
legitimate caller in lib.rs passes Some(base_mode) from the session config;
this path had no equivalent.
Fix: Tab::client_base_mode() reads the client's existing base_mode (falling
back to the tab default) and the rename path passes it through, so entering
rename via the mouse leaves the return path exactly as the keybind does.
Added a test on ModeInfo pinning the trap itself: change_to_default_mode is a
no-op with base_mode None, and returns to the base mode when it is set. That
is the property that makes passing None dangerous, and it now fails loudly if
anyone assumes otherwise.
Immediate escape hatch for a session stuck on an older build:
zellij action switch-mode locked
(that goes through the server path, which sets base_mode correctly)
Baselines held: zellij-server 1429/30, zellij-utils 436/5.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Reported after double-click-to-rename landed: rename one pane, then hovering
over other panes puts them in 'edit title' state too.
Cause is focus_follows_mouse, not the rename itself. RenamePane is a CLIENT
mode that acts on whatever pane is ACTIVE, so with focus-follows-mouse enabled
every pane the mouse passes over becomes active and inherits the prompt. The
double-click entry point just made it easy to hit, since you finish the
gesture with the mouse sitting on a pane frame.
Focus-follows-mouse is now suppressed for a client that is in a modal
text-entry mode: RenamePane, RenameTab, EnterSearch or Prompt. Those all act
on the active pane/tab, so moving focus underneath them is never what the user
meant. Keyboard focus movement is untouched.
Baselines held: zellij-server 1429/30.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Two independent bugs, either of which alone made the feature dead code.
1. WRONG EDGE. The check was `details.edge == Some(PaneEdge::Top)`, but
get_edge_at_position() quadrant-splits a pane and only ever returns the four
CORNER variants - PaneEdge::Top exists in the enum and is never produced. A
title-row click reports as TopLeft/TopRight, so the condition was never true.
Replaced with Pane::position_is_on_title_row(), which tests the geometry
directly the same way position_is_on_frame() does for the top border.
2. RESET ON RELEASE. Click recording reset the tracker on any event that was
not a frame press - which includes the RELEASE between the two clicks of a
double-click. The sequence is press/release/press, so the count went
1 -> reset -> 1 and never reached 2.
The tests passed anyway because they hand-built a MouseEventContext with
edge: Some(PaneEdge::Top) and called the decision function directly. They
asserted my assumption rather than the behaviour, and exercised neither the
edge lookup nor the click sequence.
Click recording is now a small pure helper (record_frame_click) covered by
tests that replay real event sequences: press/release/press is a double-click,
motion in between does not break it, a press off the frame does break it, and
two clicks at different positions are not a double-click. The decision tests
now use the corner edge value that is actually produced.
Baselines held: zellij-server 1428/30 (7 new tests).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Double-clicking the top edge of a pane's frame focuses that pane and switches
the client into RenamePane mode.
Everything needed already existed; this is wiring, not new machinery:
- grid.rs already tracks repeated clicks (Click, 400ms threshold). It is now
pub and reused rather than inventing a second notion of 'double click'
that could drift from the one used for text selection. A frame click never
reaches the grid, so Tab tracks frame clicks per client separately.
- pane.position_is_on_frame() / get_edge_at_position() already give the hit
edge, and mouse_handler already computes both.
- Screen::change_mode() already calls store_pane_name() when entering
RenamePane, so issuing ChangeMode(RenamePane) does the whole setup exactly
as the keybind path does. No rename logic is duplicated.
Placement in determine_mouse_action matters twice:
- AFTER the floating 'start moving' branch, so floating panes keep dragging
by their title (that is their move handle). Tiled panes only for now.
- BEFORE the resize branch, which would otherwise consume the click as a
top-edge resize drag.
Only PaneEdge::Top renames; corners stay resize handles. The pane is focused
first because RenamePane acts on the ACTIVE pane, so renaming an unfocused
pane would otherwise rename the wrong one.
Tests cover all three: double-click on Top renames, single click on Top still
resizes, and double-click on any other edge or corner does not rename.
Baselines held: zellij-server 1424/30 (3 new tests), zellij-utils 435/5.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* fix: theme query and updated pixel dimensions
* add no-ui-fullscreen key/action/capability
* protocol core
* integration test harness
* fix integration tests
* fix flaky tests
* nested ui (dimming) and directional focus initial implementation
* integration tests for nested ui dimming and directional focus
* initial full screen nesteed implementation
* fix: keep hidden floating panes hidden across nested no-ui fullscreen
* fix integration tests
* no ui fullscreen integration tests
* hardening behavior/tests
* fix(nested): re-derive descend/dim on host tab switch and new-tab so clicking dimmed chrome ascends
* initial modal implementation
* initial ui improvements
* fix integration tests
* improve nested selection ui
* nested session modal refactoring and tests
* improve integration test speed somewhat
* some ui adjustments
* simplify full/auto nested session handling
* hints in compact-bar
* guest stdin leak fix
* allow floating panes to be fullscreen
* make nested session handling configurable
* protocol reannounce
* refactor: naming confusion
* improve ux somewhat
* rustfmt
* merge fixes
* improve integration test rate limiting (fix ci)
* add pr
|
| |
|
|
|
|
|
| |
* fix: mouse scroll resize stack issue
* regression test
* cleanups
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* initial implementation
* improve ui
* fix: stacked resize interaction
* title list boundaries fix
* fix BEL
* mouse parity
* float/embed + bulk-op features
* fix in-place bug
* improve ui
* config live reload
* improve ui
* fix issues, add exit status
* fix for serialization
* fix tests
* integration tests
* comments
* refactor: duplicate title logic
* refactor: consolidate logic
* fix: flaky tests
* add a stacked swap layout
* rustfmt
* fix e2e
* add pr
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* initial implementation
* improve single line title design
* hint text plugin infra
* add proto regen task
* display alt text in status bar
* don't display text with full frames
* style alt hover text
* fix + style control own pane hover
* make multiplayer indications responsive
* update snapshots
* plugin api
* single pane as tab
* better scroll styling
* fix tests
* tab-bar plus hover
* plugin surface tests
* integration tests
* adjust all integration tests
* reduce hover noise
* add pane id to in place
* more integration tests
* style: rustfmt
* update plugin assets
* update assets and snapshots
* add pr
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* phase 2+3
* initial mobile plugin (phases 4 + 5 + 6)
* some bugfixes
* implement stage 7
* mobile interface improvements
* improve mobile pane/tab picker
* more ui improvements and fixes
* various mobile fixes
* some mobile fixes
* mobile keyboard triggering fix
* some cleanups
* initial plugin keyboard implementation
* improve keyboard hit zones
* improve keyboard
* pinch resize
* pinching fixes
* implement scrolling + panning
* initial keyboard resizing
* fix: mobile first render issues
* keyboard responsiveness
* fix some issues with the keyboard
* implement fit
* fix mobile scrolling
* add tilde cell to compact
* improve mobile keyboard
* fix starting mobile terminal font to natural keyboard size
* fix mobile resize blank screen flash
* shadow focus for mobile
* more keyboard fixes
* improve mobile status bar
* fix: render plugins to mobile clients
* allow opening new panes/tabs from the mobile plugin
* allow configuring
* fix dot slash
* improve mobile native soft keyboard interaction
* switch to native keyboard - initial implementation
* dirty hacks, but phone keyboard works now
* adjust keyboard help strip
* fix cursor in mobile
* fix keyboard and help line
* rename keyboard to modifier bar
* allow starting new session
* make clicking session name bring us to new session menu
* allow switching to desktop from the mobile ui
* mobile welcome screen initial implementation
* mobile welcome screen fixes
* various welcome screen fixes
* more mobile fixes
* moar mobile fixes
* mobile ui redesign
* fix: swallowed backspace bug
* switch session screen improvement
* touch up some interfaces
* refactor: move mobile state to own struct
* refactor: move fit resizing to the server
* remove dead flash code
* some cleanups
* refactor: clear fit on close
* refactor: improve fit api
* refactor: pane sync functions
* refactor: mouse
* refactor: break into modules
* some cleanups for the sessions screen
* fix scrolling and other stuff
* refactor: sessions screen
* refactor: panes screen
* refactor: new session screen
* refactor: menu screen
* refactor: render
* cleanups
* refactor: plugin structure
* remove unused welcome screen path
* Rename api
* refactor input.js
* cleanup useless comments
* fit/mobile refactoring
* take out the garbage
* remove more comments
* remove mobile flash on startup/attach
* fix: various mobile startup issues
* fix space bugs
* cleanup
* rustfmt
* fix windows build
* adjust modifier bar ui
* add pr
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* initial implementation
* fix mouse cpu spam
* prevent excess metadata write and prune sessions on startup
* consolidate CommandChanged and PaneCommandChanged
* tests
* rustfmt
* add pr
|
| |
|
|
|
| |
* cleanup compilation warnings
* consolidate EditScrollbackRaw into EditScrollback
|
| |
|
|
|
|
|
|
|
| |
* implement focus follows mouse
* mouse click through
* rustfmt
* add pr
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
of viewport (#4802)
* initial implementation
* implement hover
* add styles and foreground/background emphases, fix upsert hover collisions
* initial implementation of link plugin
* suppress highlighting + clicks on unfocused and mouse mode panes
* also make italic
* fix env var and ~ expansions
* fix regex issues and add defensive validations
* fix tests and add plugin assets
* add tests
* refactor all the things
* add pr
|
|
|
* initial implementation
* improve edge detection
* fix up/down resize increment bug
* temporarily disable alt-mouse grouping
* fix inverting strategies and simplify edge detection
* resize of floating -> ctrl click, resize of tiled -> (ctrl)click
* fix old functionality and tests
* refactor and fixes
* refactor mouse click handling
* move mouse click handling to separate module
* resize scroll up/down with scroll wheel
* tests for scroll resize
* implement hover help
* fix occlusion bug
* allow disabling with config
* fix focus pinned pane on frame
* use strategy invert resize directly rather than function parameter
* remove annoying log message
* cleanups and formatting
* add pr
|