Repository navigation
Releases: Jeshii/iterm2axis
Release list
iTerm2Axis v0.1.3
2026-06-18
- Claude Code sidebar now shows model name, no Python dependency —
fetchClaudeAgentsDatarunsclaude agents --jsondirectly via/usr/bin/env, parses JSON withhs.json.decode, and reads the session's.jsonlfile in pure Lua to extractmodelID. RemovedRESOLVE_PYTHON_PATH(),buildCCModelCmd(), and thepythonPathconfig option. The entire enrichment pipeline is now implemented in Lua usingio.openandhs.json.decode. Displayed as aDETAIL_COLORrow under the "claude" header. Also fixed astring.formaterror caused by an unescaped%in the old Python inline script. - Claude CLI path auto-resolution — replaces the broken
which claudeasync check withRESOLVE_CLAUDE_PATH()that probes~/.local/bin/claude,/opt/homebrew/bin/claude, and/usr/local/bin/claudeat startup. The claude binary is invoked via/bin/sh -cwith the full resolved path. - Parsed-agents log gated behind
debugconfig,SHORT_MODEL_NAMEstripsclaude-prefix.
2026-06-16
-
Removed tiling on/off feature (
_tilingEnabled,toggleTiling(),⌘⇧Thotkey) — hiding the sidebar already disables tiling, making a separate toggle misleading. Tiling is now always active when the sidebar is visible. The tiling cascade guard (_tilingInProgress,_withTilingGuard) is preserved as a stability mechanism. Removed:_tilingEnabledstate var and all 4 guard conditions acrosswindows.lua/render.lua/watchers.lua,toggleTiling()function,toggleTilingfromACTION_LABELSandbindHotkeys(), and the Phase 3 "sidebar follows focus when tiling off" block inwindowFocusedhandler. -
Cleaned up
_sidebarEnableddead code — this variable was initialized totrueand never mutated. Removed frominit(),syncCanvasLevel(),refreshSleepWake(),_setupSpaceWatcher(), and 5 async callback guards infetchers.lua. -
Fixed sidebar-hidden dead-end state where
_sidebarVisiblecould only be recovered via explicit toggle —_doBuildSidebar()was setting_sidebarVisible = falsein two non-user-intent paths: (1) the#wins == 0branch (no iTerm windows), and (2) thepcallerror handler. Once_sidebarVisiblewasfalse, the earlyif not self._sidebarVisible then return endguard prevented ALL future rebuilds through watchers/refresh — onlytoggleSidebar()could recover it. Fixed by removing both_sidebarVisible = falseassignments — the system now only hides/deletes the canvas without overwriting user intent.toggleSidebar()no longer callstileITermWindows()on hide (canvas just hides, windows keep their positions). Addedpcallerror capture (local ok, err = pcall(...)) withprint()in the error handler so the root cause of any "sidebar disappears on refresh" crash can be diagnosed. -
macOS native full-screen (green button) now hides sidebar and excludes full-screened windows from tiling — Space watcher calls
_checkFullScreenAndAdjust()on every Space switch; whenhs.spaces.spaceType()returns"fullscreen", the sidebar canvas is hidden (removed from full-screen Spaces for other apps and full-screened iTerm windows)._doBuildSidebar()also checks_isCurrentSpaceFullScreen()as a guard on every rebuild. Full-screened iTerm windows are filtered fromgetITermWindows()viaw:isFullScreen(), keeping them out of the tiling pool. Option-click (Zoom/fill) is unaffected —isFullScreen()returnsfalseand no Space change occurs. -
Added
screensDidWake/screensDidUnlockto sleep watcher —refreshSleepWake()now also fires when the display wakes or the user unlocks the computer (not just system sleep wake), re-creating event taps and rebuilding the sidebar to fix unresponsive clicks after sleep/lock.
2026-06-15
- Fixed sidebar not showing on build and eliminated double-resize — reordered
_doBuildSidebarso that_sidebarVisible = trueandtileITermWindows(sb)run beforesyncCanvasLevel()andshow(). Previously windows were tiled after the canvas appeared, causing: (a) two visual transitions (canvas shows, then windows resize); (b)setFramereshuffling z-order and burying the just-shown canvas. Now windows resize first while the canvas is still hidden, then the canvas appears already at the correct level above the tiled windows — single visual transition.
2026-06-12
- Tiling cascade guard — added
_tilingInProgressflag set before anysetFrameloop (via_withTilingGuard) and cleared aftersettleDelay + 0.1s.handleWindowMoveOrResizechecks and bails early if a tile is in flight, breaking thesetFrame→windowMoved→ retile cascade. Also guards the drift-pathsetFrameloop inhandleWindowMoveOrResize. Cleaned up instop()/init(). - Eliminated double-tile on sidebar show — show path in
toggleSidebarnow inlines anchor computation and callsbuildSidebar()directly (no synchronoustileITermWindows), letting_doBuildSidebarbe the sole tile trigger.refreshLayoutsets_skipTileOnThisBuild = trueafter its sync tile to prevent_doBuildSidebarfrom tiling again. - Guard watchers during hide —
_rebuildAfterSettlenow checks_toggleLockbefore scheduling any rebuild, preventing stale work during the 0.5s hide/show lock window. - Reload hygiene —
stop()now cleans up_resizeDebounceTimer,_tilingClearTimer,_tilingInProgress,_pendingSidebarFrame, and_currentScreen.init()initializes_tilingInProgressand_tilingClearTimer.
2026-06-09
- Phase 1: Decouple tiling from sidebar — added
_tilingEnabledstate variable (init'dtrue),toggleTiling()function, and⌘⇧Thotkey. No behavioral change yet — the toggle flips state and re-tiles when re-enabled, but nothing is gated behind it (Phase 2 adds the actual guards). - Phase 2: Gate all window frame manipulation behind
_tilingEnabled—tileITermWindows(), thesetFrame()loop inhandleWindowMoveOrResize(), the retile guard in_doBuildSidebar(), and_rebuildAfterSettle()all check_tilingEnabledbefore touching any window frame. Toggling tiling OFF now actually prevents window layout changes; the sidebar repositions itself to the focused window but does not resize/move any iTerm windows. - Phase 3: Sidebar follows focused window when tiling is off —
windowFocusedhandler repositions the sidebar canvas to the newly focused iTerm window using directcanvas:setFrame()instead of going through thebuildSidebar()pipeline. This bypasses the snapshot-check/debounce/rebuild machinery and moves the canvas instantly. Only fires when position actually differs (avoids redundant calls on stacked windows).
2026-06-06
- Fixed canvas unresponsive after sleep/wake — added
hs.caffeinate.watcher(_setupSleepWatcher) that re-creates the click and drag event taps and rebuilds the sidebar canvas on wake from sleep, sincehs.eventtapinstances can be silently invalidated by the system after macOS sleep. AddedrefreshSleepWake()helper that invalidates all state, re-creates event taps, and conditionally rebuilds if the sidebar was visible. - Attempted to fix sidebar toggle reliability —
_sidebarVisibleis now the single source of truth intoggleSidebar(decoupled fromsidebarCanvasexistence). Screen watcher (_setupScreenWatcher) no longer forces_sidebarVisible = trueand only rebuilds if the sidebar was already visible. Space watcher (_setupSpaceWatcher) now checks_sidebarVisiblebefore rebuilding. Added_toggleLockto the show path for consistency with the hide path. Added_sidebarVisibleguards to all async fetcher callbacks to prevent unnecessary rebuilds when the sidebar is hidden. - Attempted to fix tiling never firing after first build —
_doBuildSidebarbody is now wrapped inpcallso_buildPendingis always reset (preventing the "stuck" state where any Lua error silently blocks all future rebuilds and tiling). AddedtileITermWindows()call directly inrefreshLayoutso tiling always fires on explicit layout commands regardless of_doBuildSidebar's snapshot-based early-exit logic.
2026-06-05
-
Fixed sidebar context loss on focused Claude Code window during spinner animation — when the focused iTerm2 window had a Claude Code session running, every spinner tick in the title (
⠋ /path,⠙ /path, etc.) triggeredwindowTitleChanged. ThestableCore()comparison was intended to detect these as no-ops, butlastRawTitlewas never seeded onwindowFocused— it was only written bywindowTitleChangeditself. So the very first spinner frame after focusing always diffed against a nil/stale baseline,newCore != prevCore, which invalidatedwd/tabInfoand firedFETCH_WINDOW_INFOwith the spinning title. The AppleScriptfirst window whose name = "⠋ /path"failed to match, leavingtabInfoasMISSINGand the sidebar falling back toparts.basename(the last word of the title). Fixed by seedingCACHE.wc(winId).lastRawTitle = win:title()in thewindowFocusedhandler so the first spinner diff always has a clean baseline. The bug only manifested on the focused window because unfocused windows don't receivewindowFocusedand theirlastRawTitlewas already populated from prior title changes. -
Attempts to fix sidebar cache corruption during Claude Code spinner animation — CC's dynamic spinner frames (braille U+2800-U+28FF) in the window title lacked the
✳/·/🔔prefix, sowindowTitleChangedtreated them as real navigation events, callingCACHE.invalidateWindowwhich wipedwd/tabInfobefore the async AppleScript could re-populate them. The AppleScript then failed because the spinner-frame title didn't match any iTerm window name. Replaced the fragile CC-prefix-based invalidation gate with astableCore()comparison — strips all stable CC decorations (✳,·,🔔, braille U+2800-U+28FF, trailing resolution suffix) and only invalidates when the meaningful path portion actually...
iTerm2Axis v0.1.2
Bug Fixes
- Sidebar no longer disrupts other apps. Clicks in the sidebar region are only intercepted when iTerm2 is the frontmost application and the sidebar is visible. Previously, the canvas silently absorbed clicks across all apps.
- Sidebar visibility properly tracked. Replaced unreliable
hs.canvas:isVisible()with an explicit_sidebarVisibleflag, fixing the bug where the hidden canvas still consumed mouse events. - Sidebar z-order restored on focus return. Uses
hs.application.watcherto set the canvas to floating level when iTerm activates and drop to normal level when another app takes focus. Previously, switching away and back via Mission Control left the canvas stuck behind other windows. - Graceful error handling. Wrapped
isITerm(),buildSidebar(), and filesystem operations inpcallto prevent cascading failures.
New Features
- Bell flash notification. Windows with a bell indicator (🔔) now flash with a separate configurable color and interval (
config.bell). Previously, only Claude Code "waiting" state triggered a flash with a hardcoded color. - Consolidated flash timer. Replaced per-window flash timers with a single shared timer, reducing resource usage.
- Path-keyed custom names. Window renames can now be keyed by working directory path, so the same name persists across sessions for the same project even if the window ID changes.
- Persistent window ordering and names. Custom window order and renames are saved to
hs.settingsand restored across reloads and restarts. - Space change handling. Added
hs.spaces.watcherto detect macOS Space changes and rebuild/retile accordingly. - Canvas level polling. Periodic 1-second timer ensures the canvas level stays correct even when Hammerspoon misses a focus event.
- Sidebar build optimization. Snapshot diffing avoids full canvas rebuilds when window structure hasn't changed; uses in-place element attribute updates instead. Debounced rebuilds coalesce rapid successive events.
Refactoring
buildSidebarsplit into publicbuildSidebar(snapshot + debounce) and private_doBuildSidebar(actual canvas manipulation).startFlashingnow accepts aflashTypeparameter ("waiting" or "bell") instead of having a single hardcoded behavior.- OpenCode data finalization extracted into
_finalizeOpenCodeData().
iTerm2Axis v0.1.1
See AGENTS.md for usage instructions.
To install: unzip and copy iTerm2Axis.spoon to ~/.hammerspoon/Spoons/, then add hs.loadSpoon("iTerm2Axis") to your init.lua.
iTerm2Axis v0.1.0
See AGENTS.md for usage instructions.
To install: unzip and copy iTerm2Axis.spoon to ~/.hammerspoon/Spoons/, then add hs.loadSpoon("iTerm2Axis") to your init.lua.