Skip to content

feat: pin rows to the top, and a Nerd Font icon set (v1.0.0) - #21

Merged
mohseenrm merged 2 commits into
mainfrom
feat/pinning-and-icons
Sep 25, 2026
Merged

mohseenrm merged 2 commits into
mainfrom
feat/pinning-and-icons

Conversation

@mohseenrm

@mohseenrm mohseenrm commented Sep 25, 2026 •

Copy link
Copy Markdown
Owner

Pinning and the new icons

Pinning (p)

Rows sort by urgency, which is right for "who needs me" and wrong for the other thing the panel gets used for: watching two or three specific agents while the rest of the fleet churns. Those agents had no fixed address, and 1-9 landed on whoever was loudest.

p lifts a row into a block at the top, above everything else, whatever its status. Inside the block urgency still decides, so pinning changes which rows are on top, not how they rank among themselves.

The design rule that settles every grouping interaction: the pinned block is group zero. Computed before grouping, own heading in every mode, and its rows are invisible to the grouping below.

  • Urgency gains exactly one heading where it has none today. Without it, a pinned working row above an unpinned failed one reads as a sort bug.
  • Project / session put the block first, and every group count drops its pinned members: api (1), not (2). A heading whose number exceeds the rows beneath it would be worse than no heading.
  • Group rank uses unpinned members only. Pin a group's only blocked agent and the group falls back to where its remaining members put it. Easy to miss, so it has its own test.
  • Find ignores pins entirely, for the reason it already suppresses group headings: match order is score order.

Persistence. Pins are keyed by AgentId, never by index, and dropped with their row, because pane ids are recycled and a pin that outlived its agent would transfer to whoever got that pane next. The set is shared through a pins file next to the status records, written atomically and read back by the existing scan, so a pin set in one session shows in every panel and survives a reload. That prune is scoped to sessions the panel can actually see, or adopting the shared set would unpin another panel's row on the next write.

q hide gave up its footer slot to p pin. Esc still hides, and it is the key people try unprompted.

Icons

Every glyph moves behind one table (src/icons.rs) with two sets, chosen by a new icons config key: nerd (default) and unicode (exactly today's glyphs). This also deletes a duplicated icon_for in tour.rs that had already drifted from the real one.

Codepoints came from Nerd Fonts' own glyphnames.json v3.5.1, searched by name across all 10,995 glyphs rather than picked from memory. Several were then changed after rendering them at 14px, which is the part no test can do:

  • cod-bell_dot for waiting collapsed into a smudge; it is fa-bullseye now.
  • cod-fold for compact drew an X and read as a failure next to the real one; it is cod-archive.
  • A bell in the notified gutter put two bells on one row when the status was also a bell. Now oct-dot_fill, and covered by an exhaustive collision test, since the hand-listed pairs it replaced are exactly what let that through.

The constraint everything was picked against: style::chars() counts characters and the layout assumes one char is one cell. Three tests enforce it - one char per glyph, nothing above the BMP, nothing double-width. That rules out nf-md-* (two UTF-16 units) and emoji (East-Asian-Wide), and it is why this is width-neutral rather than a rendering change.

Demo

A second GIF, demo/v1-pinning-and-icons.gif (189 KB), covers just these two features for the release notes; the full tour keeps its own pacing. Both are staged through the panel's own row builders - no recorder, no live session - so they cannot drift from what the panel draws. Captions are gone, the frame unit is 22ms (~45fps), and the canvas no longer reserves a caption band.

Verification

./scripts/check.sh green (all 8 checks: fmt, clippy -D warnings, 437 unit tests, hook e2e, installer e2e, live Zellij e2e, wasm build, shellcheck).

Also verified in a throwaway Zellij session, isolated from config and spool:

  • p drew the pinned (1) heading, put the pin glyph in the gutter, and wrote pins.
  • Pressing p again cleared all three.
  • A pre-seeded pins file was adopted by a panel that had never pinned anything, which is the cross-session sharing path end to end.
  • The baseline v0.13.1 wasm loaded side by side still drew the old glyphs (▶, ◌) where this build drew the new ones.

One bug caught by a test while writing it: pin lines parse with rsplit_once(' '), not split_whitespace, because a session name may contain spaces and the pane id is the only field that never does.

Pinning (`p`)

Rows sort by urgency, which is right for "who needs me" and wrong for
watching two or three specific agents while the fleet churns underneath
them. `p` lifts a row into a block at the top, so `1`-`9` and `g` become
stable addresses for the agents you chose.

The block is group zero: it heads its own run in every mode, urgency
included, because rows lifted out of rank order with nothing to explain
them read as a sort fault. A pinned row leaves its project or session
group and stops being counted in that heading, and group rank is computed
from unpinned members only, so pinning a group's only blocked agent lets
that group fall back to where its remaining members put it.

Pins are keyed by AgentId, never by index, and dropped with their row:
pane ids are recycled and a pin that outlived its agent would transfer to
whoever got that pane next. The set is shared through a `pins` file next
to the status records, so a pin set in one session shows in every panel
and survives a reload. That prune is scoped to sessions this panel can
see, or it would unpin another panel's row on the next write.

`q hide` gave up its footer slot to `p pin`; Esc still hides.

Icons

Every glyph moves behind one table with two sets, selected by the `icons`
config key: `nerd` (default) and `unicode` (the old glyphs). This also
removes a duplicated icon_for in tour.rs that had already drifted.

Several glyphs were chosen by rendering candidates side by side at 14px
rather than by name: cod-bell_dot collapsed into a smudge, cod-fold drew
an X that read as a failure next to the real one, and a bell in the
notified gutter put two bells on one row when the status was also a bell.
The last of those is now covered by an exhaustive collision test, since
the hand-listed pairs it replaced are exactly what let it through.

Three tests pin the constraint the layout rests on: one char per glyph,
nothing above the BMP, nothing double-width. That is what excludes
nf-md-* and emoji, and it is why this is width-neutral rather than a
rendering change.

Demo

A second GIF, demo/v1-pinning-and-icons.gif, covers just these two
features for the release notes; the full tour keeps its own pacing. Both
are staged through the panel's own row builders, so they cannot drift
from what it draws. Captions are gone and the frame unit is now 22ms
(~45fps), with the canvas no longer reserving a caption band.

Verified in a throwaway Zellij session: `p` draws the block and writes
the file, a seeded file is adopted by a panel that never pinned anything,
and the baseline v0.13.1 wasm side by side still draws the old glyphs.
@mohseenrm
mohseenrm force-pushed the feat/pinning-and-icons branch from 0efb27b to 40c6edf Compare September 25, 2026 05:42
@mohseenrm
mohseenrm marked this pull request as ready for review September 25, 2026 05:44
@mohseenrm
mohseenrm merged commit 89d0b7c into main Sep 25, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant