Release tooling, CI gates, MRU sort, and a list scrollbar - #1
Merged
Conversation
Three independent changes, all borrowed in spirit from alchemmist/lazy-tmux.
Release: .goreleaser.yaml replaces the hand-rolled build loop in
release.yml. Same four platforms, but now tar.gz archives (carrying
kaku-tab.tmux and docs/ alongside the binary), a checksums file, a
generated changelog, and a Homebrew cask. A cask rather than a formula
because goreleaser deprecated `brews` outright, and a cask also clears
the macOS quarantine attribute. Publishing it needs a dsaad68/homebrew-tap
repo and a HOMEBREW_TAP_TOKEN secret; until both exist the release still
succeeds and skips the cask.
Gates: .golangci.yml (11 linters, deliberately without revive's
`exported` rule — it wanted doc comments on every thin tmux wrapper and
bubbletea method, which is not how this tree comments) and
.testcoverage.yml, whose thresholds sit just under today's numbers so
they ratchet rather than block. Fixes the five findings that turned up:
two unchecked errors in popup(), an ST1022 on DefaultTitleFormat, and
dead minInt. CI gains lint, coverage, and goreleaser-check jobs, and
setup-go now reads go.mod instead of a pinned 1.22 that no longer
matches it.
Sort: @kaku-tab-sort takes tabs (default, unchanged), mru, or name.
tmux cannot answer "where was I?" — #{window_activity} is output-driven
so a window running top is permanently most-recent, and
#{session_last_attached} only moves on attach, which is exactly what
kaku-tab avoids doing when it retargets a tab. So internal/mru records
kaku-tab's own picks, into a tmux server option rather than a file:
window ids mean nothing outside the server that issued them, and a
server option lives exactly that long. The window you are in is demoted
one place, or the top row's Enter would be a no-op.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A list that continues below the frame looked exactly like a list that ended there: the rows just stopped, with nothing to say the next session was one keypress down. Both lists now carry a one-cell gutter at the right edge, drawn as a track with a thumb sized to the fraction on screen. The gutter is reserved always, blank when everything fits — one that appeared only on overflow would re-budget every column the moment a query filtered a row away, and column widths are a property of the table, not of the query. So renderRow now budgets against rowWidth (the list column less the gutter) while listWidth keeps its old meaning for sizing the preview. The thumb is floored at one cell; on a long enough list an exactly proportional one rounds to zero and vanishes at the size where it matters most. The scrollback search gets the same treatment, where it matters more — a grep over every pane routinely returns hundreds of hits. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The movement keys have always been there — arrows, ^k/^j, PgUp/PgDn, Home/End — and none of them were written down anywhere. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
^e drops sessions with no terminal tab, leaving only what is actually on screen. tmux marks every window of a client-less session Detached, so this removes whole sessions, header included, rather than punching holes in one. The cursor is held on its row across the toggle: unhiding inserts whole sessions above it, and without that the selection lands somewhere unrelated and Enter goes where the user did not choose. Deliberately not persisted between invocations the way @kaku-tab-preview is — a filter that quietly survived would one day hide half your sessions with nothing on screen to say why. @kaku-tab-detached 'off' starts it on for people who want that, and an empty list now names the filter as the reason instead of reading as "you have no sessions". Two glyph fixes alongside it: The active-pane marker gets a column of its own. Flush against the status glyph, "●*" read as one smudged symbol; and since only the active row carried it, that row's label, command and path all sat one cell right of its neighbours'. Reserved on every pane row, the columns line up and the markers form a clean vertical run. The cursor is a solid bar rather than ▸, which was the same glyph as a collapsed session's fold arrow two columns over — on a folded row the two sat side by side meaning entirely different things. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The block bar sat flush against the frame's │ and the two merged into one doubled edge. ▶ (U+25B6) is the same shape as the ▸ (U+25B8) a collapsed session folds with but at a visibly heavier weight, so the two stay distinguishable in the one place they appear together — a selected, folded header — helped by two spaces between them, the accent colour and the row tint. Every candidate was checked at one cell in tmux before picking: several right-pointing triangles are ambiguous-width and a few have emoji presentations, and a marker that rendered double would shift every column on the selected row and nowhere else. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
An arrowhead rather than another triangle: ▶ differed from the ▸ a collapsed session folds with only by weight, and ➤ differs by shape, which is what carries when the two meet on a selected folded header. Already measured at one cell in tmux alongside the other candidates. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Four changes, mostly independent. The first three came out of reading
alchemmist/lazy-tmux for ideas worth
borrowing; the fourth is a bug that showed up while testing them.
Release: goreleaser
.goreleaser.yamlreplaces the hand-rolledfor target in …loop inrelease.yml. Same four platforms, but now tar.gz archives (carryingkaku-tab.tmuxanddocs/alongside the binary), a checksums file, a generatedchangelog, and a Homebrew cask.
The binary keeps its plain name inside the archive — one unpacking to
kaku-tab_darwin_arm64would install somethingkaku-tab.tmux's PATH lookupcannot find.
A cask rather than a formula: goreleaser deprecated
brewsoutright(
goreleaser checkfails on it), and a cask also clears the macOS quarantineattribute.
Before merging or tagging, publishing the cask needs two things that do not
exist yet:
dsaad68/homebrew-taprepoHOMEBREW_TAP_TOKENsecret — a PAT with write access to itUntil both exist the release still succeeds and skips the cask (guarded on
skip_upload, tested with the variable unset and empty). The README's newHomebrew section is wrong until the tap is created.
CI: lint and coverage gates
.golangci.yml, 11 linters, deliberately without revive'sexportedrule —it produced 30 findings demanding doc comments on thin tmux wrappers and
bubbletea interface methods, which fights how this tree comments (why, not
what). The 5 real findings are fixed: two unchecked errors in
popup(), anST1022 on
DefaultTitleFormat, and deadminInt..testcoverage.ymlexcludescmd/,internal/tmux,internal/kaku—process wrappers whose unit tests could only assert arguments back at
themselves. Thresholds sit just under today's numbers so they ratchet rather
than block: total 40 (at 42.9),
resolve85 (at 90.8),ui40 (at 44.7),mru80 (at 88.5). Each override regex was verified to actually bind.CI gains
lint,coverage, andgoreleaser-checkjobs.setup-gonow readsgo.modinstead of a pinned1.22that no longer matches it.Picker:
@kaku-tab-sortTakes
tabs(default, unchanged),mru, orname.The interesting part is that tmux cannot answer "where was I?"
#{window_activity}is output-driven, so a window runningtopis permanentlymost-recent;
#{session_last_attached}only moves on attach, which is exactlywhat kaku-tab avoids doing when it retargets a tab. So
internal/mrurecordskaku-tab's own picks — into a tmux server option rather than a file, because
window ids mean nothing outside the server that issued them and a server option
lives exactly that long.
The window you are in is demoted one place. It always heads the history
(switching there is what recorded it), so leaving it at rank 0 would put the
cursor on a row whose Enter does nothing.
pickHeaderWindowneeded the sametreatment. Windows never picked keep index order behind the ranked ones, so a
fresh tmux server is identical to the old default.
Picker: scrollbar
A list running past the viewport looked exactly like one that ended there — the
rows just stopped at the frame. Both lists now carry a one-cell gutter with a
thumb sized to the fraction on screen.
Reserved always, blank when everything fits: a gutter appearing only on overflow
would re-budget every column the moment a query filtered a row away, and column
widths are a property of the table, not of the query.
renderRowbudgetsagainst a new
rowWidth();listWidth()keeps its old meaning for sizing thepreview.
Verification
golangci-lint run— 0 issuesgo test -race ./...— pass; 18 new tests acrossinternal/mruandinternal/uigoreleaser check+ a real--snapshotbuild: archives correct, injected version prints, cask rendersdetached-session capture in CLAUDE.md
Left alone
go.modsaysgo 1.26.5while the README says "Go 1.21+ to build". Pointingsetup-go at
go.modfixes CI either way, but which of the two is wrong is acall about the project's support floor, not a CI detail.
🤖 Generated with Claude Code