Releases: itsdezen/tili
Release list
v0.5.3
Recommended install: brew install itsdezen/tap/tili —
Homebrew downloads via curl, which doesn't set macOS's
quarantine flag, so Gatekeeper never enters the picture.
The tarballs below are codesigned with tili's self-signed
certificate (hardened runtime) — see
CONTRIBUTING.md.
That's for a stable signing identity across releases (so
your Accessibility grant survives an upgrade), not for
passing Gatekeeper — a self-signed cert isn't notarized. If
you download tili.app directly from this page instead of
using Homebrew, Gatekeeper will still prompt on first launch:
right-click → Open, or xattr -d com.apple.quarantine tili.app.
Fixed
- Finder's Quick Look preview panel (Space) and "Get Info" panel
(Cmd+I) were floating and centering instead of being left alone, even
whencom.apple.finderwasn't configured as a floating app at all.
Quick Look is aWindowKind::Popup, which already defaults toIgnore,
but afloating-rulesentry forcom.apple.finder(written for its real
browser windows) has no way to exclude this one auxiliary panel and an
explicit rule always wins over the kind-based default. Get Info reaches
WindowKind::Dialogvia the zoom-but-no-fullscreen heuristic, whose own
kind-based default isFloat— wrong regardless of any rule, since the
window should never be touched at all. Both are now force-Ignored
unconditionally, the same mechanism already used for Finder's "Copy" and
"Connect to Server" dialogs and System Settings' suggestion popup.
Full changelog: CHANGELOG.md
v0.5.2
Recommended install: brew install itsdezen/tap/tili —
Homebrew downloads via curl, which doesn't set macOS's
quarantine flag, so Gatekeeper never enters the picture.
The tarballs below are codesigned with tili's self-signed
certificate (hardened runtime) — see
CONTRIBUTING.md.
That's for a stable signing identity across releases (so
your Accessibility grant survives an upgrade), not for
passing Gatekeeper — a self-signed cert isn't notarized. If
you download tili.app directly from this page instead of
using Homebrew, Gatekeeper will still prompt on first launch:
right-click → Open, or xattr -d com.apple.quarantine tili.app.
Fixed
- A Preferences/Settings-style window (e.g. System Settings' own main
window) could be force-Ignored instead of floating/centering, even
with an explicitfloating-rulesentry asking for it to float.
is_transient_empty_dialog(added to catch the input-source-switch HUD
glyph) matched anyWindowKind::Dialogwith an empty title, but a
Preferences/Settings-style window also reachesDialogvia
classify_window_kind's zoom-but-no-fullscreen heuristic — and its
AXTitleroutinely reads empty at the exact moment tili scans it,
before AX has populated it. That race got it misidentified as the
transient HUD glyph and force-Ignored, overriding the user's own
config.is_transient_empty_dialognow also requires the window to have
no zoom button, which the HUD glyph never has but a Settings-style
window always does. - System Settings' search-suggestions dropdown (shown while typing in its
search field) was floating and centering instead of being left alone.
Confirmed via diagnostic logging it's a borderless, chrome-less,
empty-titledWindowKind::Popup— the same ambiguous shape as an
ordinary tooltip/menu, which normally defaults toIgnore. The user's own
rule app-id="com.apple.systempreferences"floating-rulesentry (meant
for the app's real Preferences windows) has no way to exclude just this
one auxiliary window, and an explicit rule always wins over the
kind-based default. Now force-Ignored unconditionally, regardless of
floating-rulesconfig, the same way Finder's protected dialogs are.
Full changelog: CHANGELOG.md
v0.5.1
Recommended install: brew install itsdezen/tap/tili —
Homebrew downloads via curl, which doesn't set macOS's
quarantine flag, so Gatekeeper never enters the picture.
The tarballs below are codesigned with tili's self-signed
certificate (hardened runtime) — see
CONTRIBUTING.md.
That's for a stable signing identity across releases (so
your Accessibility grant survives an upgrade), not for
passing Gatekeeper — a self-signed cert isn't notarized. If
you download tili.app directly from this page instead of
using Homebrew, Gatekeeper will still prompt on first launch:
right-click → Open, or xattr -d com.apple.quarantine tili.app.
Fixed
- Moving "the focused window" to another workspace could act on a stale,
previously-focused window instead of whatever the user actually had
focused — most reliably reproduced with a floating window (e.g. Note)
sharing a workspace with a tiled one (e.g. Ghostty). Root-caused to two
compounding gaps: (1)tili-daemon's event loop only applied a pending
new-window registration on the next 30ms maintenance tick, so a hotkey
fired right after opening a window could dispatch before that window was
even known about — now drained before dispatching a hotkey- or
socket-triggered command instead. (2) The system-wide focus check
(sync_focus_from_frontmost, run before every command) read
kAXFocusedWindowAttribute, an attribute macOS's system-wide
accessibility element never actually populates — confirmed on real
hardware it silently failed on every call, every time, regardless of
what was focused. Now readskAXFocusedUIElementAttribute(the one
attribute the system-wide element reliably supports) and resolves the
owning window from whatever control that returns. tili uninstallcould still delete the real config file when a
dotfiles tool symlinked the whole~/.config/tilidirectory, rather
than justtili.kdlitself — the file-level symlink case was already
handled, but a symlinked ancestor directory wasn't. Now walks every
ancestor of the config path and leaves it in place if any of them is a
symlink.brew upgrade tilididn't actually restart onto the new binary.
post_install's restart works by signaling the running process and
relying on the LaunchAgent'sKeepAliveto relaunch it — but the plist's
cached path was fully resolved to a specific, version-pinned Cellar path,
which never changes on its own, so every upgrade kept relaunching the
exact same old binary until a manualtili stop && tili start.tili startnow resolves that path through Homebrew'sopt/tilisymlink
instead, which Homebrew itself relinks to the current keg on every
upgrade. If you're upgrading from an affected version (v0.4.3 or
later), runtili stop && tili startonce after this upgrade to
regenerate the LaunchAgent plist with the corrected path — this fix only
changes what a futuretili startwrites, not an already-installed
plist.
Full changelog: CHANGELOG.md
v0.5.0
Recommended install: brew install itsdezen/tap/tili —
Homebrew downloads via curl, which doesn't set macOS's
quarantine flag, so Gatekeeper never enters the picture.
The tarballs below are codesigned with tili's self-signed
certificate (hardened runtime) — see
CONTRIBUTING.md.
That's for a stable signing identity across releases (so
your Accessibility grant survives an upgrade), not for
passing Gatekeeper — a self-signed cert isn't notarized. If
you download tili.app directly from this page instead of
using Homebrew, Gatekeeper will still prompt on first launch:
right-click → Open, or xattr -d com.apple.quarantine tili.app.
Added
tili doctor [--fix]— checks config syntax, daemon/menu-bar
LaunchAgent state, the IPC socket, Accessibility/Input Monitoring
permission grants, and the last config load's warnings. Offers
confirmed auto-fix for whatever's safely automatable (a stale socket, an
unloaded LaunchAgent, a missing half of the daemon/menu-bar pair); a bad
config file or an ungranted permission is reported only, never guessed
at.--fixskips the confirmation prompt for scripting.
Changed
tili-daemonandtili-menubarnow run as a synchronized pair,
never one without the other.tili startrequires both LaunchAgents
to install, rolling the daemon back if the menu bar badge's install
fails;tili-menubargives up and stops itself after a sustained run of
daemon-unreachable retries;tili-daemon's shutdown paths
(Command::Shutdown,stop_self) tear the menu bar's LaunchAgent down
too.
Fixed
- A hotkey-triggered command that failed (no window focused, an
undeclared workspace, ...) left no trace anywhere — unlike the
socket path, which already reported the error back totili-cli. Now
logged. - The daemon's Unix socket had no cap on an incoming command's declared
length, so a stray or malformed local client could drive an
unbounded allocation (up to ~4 GiB) just from its 4-byte length prefix.
Capped at 1 MiB. tili startcould print a spuriousLoad failed: 5: Input/output errorfromlaunchctlbefore its own "LaunchAgent installed" message.
install_launch_agentcalledlaunchctl load -wunconditionally, even
when the label was already loaded (e.g. the menu bar badge's LaunchAgent
surviving from an earliertili start) — a well-known legacy
launchctlquirk on an already-loaded label, harmless but noisy, and it
also meant a rewritten plist (e.g. a changed binary path after an
upgrade) silently didn't take effect. Now unloads first if already
loaded.tili uninstalldeleted~/.config/tili/tili.kdleven when it was a
symlink, breaking a dotfiles manager's (stow, chezmoi, ...) managed
arrangement. Now left in place if it's a symlink.- Toggling input sources (Globe/Ctrl-Space) showed the "A" HUD glyph as
a floating window tili re-centered, instead of leaving it alone like
other transient system UI. Unlike the volume/brightness HUD
(com.apple.OSDUIHelper, also added to the system-UI bundle-id
denylist while investigating this), this glyph isn't owned by a
dedicated system process at all — it's attributed to whichever app is
frontmost at the moment, confirmed via diagnostic logging. Now matched
by shape instead: any brand-newDialog-kind window with an empty title
is ignored, regardless of which app it's attributed to. - Moving a focused floating window to another workspace could move a
different, unrelated tiled window instead, when the floating window
belonged to a different app than whatever macOS still considered
frontmost.sync_focus_from_frontmostresolved "what's focused" via
frontmost_app_pid()(which app is frontmost) then that app's own
focused window — a two-hop lookup that misses a floating panel/utility
window that can hold real AX focus without its owning app ever becoming
NSWorkspace.frontmostApplication. Now queries the system-wide focused
window directly (AxWindow::system_focused_id), sidestepping the
"which app" hop entirely.
Full changelog: CHANGELOG.md
v0.4.4
Recommended install: brew install itsdezen/tap/tili —
Homebrew downloads via curl, which doesn't set macOS's
quarantine flag, so Gatekeeper never enters the picture.
The tarballs below are codesigned with tili's self-signed
certificate (hardened runtime) — see
CONTRIBUTING.md.
That's for a stable signing identity across releases (so
your Accessibility grant survives an upgrade), not for
passing Gatekeeper — a self-signed cert isn't notarized. If
you download tili.app directly from this page instead of
using Homebrew, Gatekeeper will still prompt on first launch:
right-click → Open, or xattr -d com.apple.quarantine tili.app.
Fixed
tili uninstallcould print a spuriousUnload failed: 5: Input/output errorfromlaunchctlbefore its own "uninstalled"
message.stop_daemoncalledlaunchctl unloadon a LaunchAgent
plist whenever the file existed on disk, even if launchd no longer had
that job loaded (e.g. the daemon had already unloaded itself via
stop_self). Now skips the unload when the job isn't actually loaded,
still removing the stale plist file either way.- A fresh install's first
tili startcould show notiliitem to
toggle in System Settings' Accessibility list. Right after
triggering the Accessibility prompt,tili-daemonrantccutil reset
on its own TCC entry — since the prompt's row is created
asynchronously by macOS, this reset raced it and deleted the row
before it was ever visible to toggle, on every single attempt. That
reset is gone. tili statuscould report the daemon "running" while hotkeys
silently didn't work. Input Monitoring was treated as a soft
condition — the daemon kept running in a degraded state if it wasn't
granted. It's now a hard gate like Accessibility: if Input Monitoring
isn't granted, the daemon stops itself and requires an explicittili startonce both permissions are granted, instead of half-running.brew install/brew upgradecould skip restarting the menu bar
badge (or the daemon) after an asymmetric LaunchAgent state.
post_install's restart gate checked onlycom.tili.daemon.plist's
existence before restarting both processes; each LaunchAgent is now
checked independently.
Full changelog: CHANGELOG.md
v0.4.3
Recommended install: brew install itsdezen/tap/tili —
Homebrew downloads via curl, which doesn't set macOS's
quarantine flag, so Gatekeeper never enters the picture.
The tarballs below are codesigned with tili's self-signed
certificate (hardened runtime) — see
CONTRIBUTING.md.
That's for a stable signing identity across releases (so
your Accessibility grant survives an upgrade), not for
passing Gatekeeper — a self-signed cert isn't notarized. If
you download tili.app directly from this page instead of
using Homebrew, Gatekeeper will still prompt on first launch:
right-click → Open, or xattr -d com.apple.quarantine tili.app.
Fixed
- The menu bar badge could show up in System Settings' Menu Bar list as
tili-menubarwith no icon instead oftiliwith its logo.
sibling_binary_pathderived the badge's LaunchAgent path from
current_exe()'s parent directory without canonicalizing first — since
Homebrew'sbin/prefix holds atili-menubarsymlink right next to
tili's own, an unresolvedcurrent_exe()found it there and baked
that symlink path into the plist instead of the real path inside
tili.app, which is what actually carries the bundle's name/icon.
sibling_binary_pathnow canonicalizes first. Existing installs pick
this up aftertili stop && tili start(or a freshbrew upgradeper
its caveats).
Full changelog: CHANGELOG.md
v0.4.2
Recommended install: brew install itsdezen/tap/tili —
Homebrew downloads via curl, which doesn't set macOS's
quarantine flag, so Gatekeeper never enters the picture.
The tarballs below are codesigned with tili's self-signed
certificate (hardened runtime) — see
CONTRIBUTING.md.
That's for a stable signing identity across releases (so
your Accessibility grant survives an upgrade), not for
passing Gatekeeper — a self-signed cert isn't notarized. If
you download tili.app directly from this page instead of
using Homebrew, Gatekeeper will still prompt on first launch:
right-click → Open, or xattr -d com.apple.quarantine tili.app.
Fixed
brew install/brew upgradecould report a false "post-install step
did not complete successfully" warning.post_installusedsystem
to runpkill -x tili-daemon/pkill -x tili-menubar, which treats
pkill's exit 1 (no matching process — the normal case whenever the
LaunchAgent plist exists but the process isn't currently running) as a
build failure. Switched toquiet_system, which doesn't raise on a
non-zero exit.
Full changelog: CHANGELOG.md
v0.4.1
Recommended install: brew install itsdezen/tap/tili —
Homebrew downloads via curl, which doesn't set macOS's
quarantine flag, so Gatekeeper never enters the picture.
The tarballs below are codesigned with tili's self-signed
certificate (hardened runtime) — see
CONTRIBUTING.md.
That's for a stable signing identity across releases (so
your Accessibility grant survives an upgrade), not for
passing Gatekeeper — a self-signed cert isn't notarized. If
you download tili.app directly from this page instead of
using Homebrew, Gatekeeper will still prompt on first launch:
right-click → Open, or xattr -d com.apple.quarantine tili.app.
Fixed
- Preferences/Settings-style windows no longer get tiled. Some apps
(e.g. Safari's own Settings window) present their preferences panel as
an ordinaryAXStandardWindow, indistinguishable by subrole from a real
content window, so it fell through toWindowKind::Standardand got
tiled like any other window.classify_window_kindnow also treats a
standard-subrole window asDialog(tili's default float/center
treatment) when it has a zoom button but no full-screen button — AppKit
gives fullscreen-capable content windows a full-screen button and
non-fullscreenable utility panels a classic zoom button instead, so this
generalizes to any app with the same window shape rather than a
per-bundle-id fix.
Full changelog: CHANGELOG.md
v0.4.0
Recommended install: brew install itsdezen/tap/tili —
Homebrew downloads via curl, which doesn't set macOS's
quarantine flag, so Gatekeeper never enters the picture.
The tarballs below are codesigned with tili's self-signed
certificate (hardened runtime) — see
CONTRIBUTING.md.
That's for a stable signing identity across releases (so
your Accessibility grant survives an upgrade), not for
passing Gatekeeper — a self-signed cert isn't notarized. If
you download tili.app directly from this page instead of
using Homebrew, Gatekeeper will still prompt on first launch:
right-click → Open, or xattr -d com.apple.quarantine tili.app.
Added
- Animated window movement (opt-in). New
animatesetting (#false,
#true,"medium", or"high"— default#false;#trueis
shorthand for"medium") eases tiled relayout and floating-window
placement — including a newly-centered floating window's placement —
into their new frame over a short duration (90ms) instead of jumping
straight there, fixing the visible "flick" on relayout, post-drag
mouse-resize snap, and floating centering."medium"/"high"pick the
animation's tick rate (~60fps/~120fps) —tili-daemonreconstructs its
dedicated animation timer to match on every config change, and that
timer only runs at all while something is actually animating.
Performance tradeoff: each animation step is a real AX write, not a free
interpolation, so"medium"costs roughly 6x the AX round-trips of a
plain instant move per relayout, and"high"roughly 11x (double
"medium") — negligible on responsive native apps, more noticeable as
extra per-relayout latency on a slow-to-respond one. Implemented as
TweenedFrameSetter, a secondWindowFrameSetteralongside the
existingInstantFrameSetter. Doesn't affect a user's own native
drag/resize of a floating window (tili never writes during that);
parking a window off-screen and the size-discovery step of centering a
floating window stay instant on purpose, and so does revealing a
workspace's windows on switch (they're showing at wherever they were
parked, which was never meant to be seen, so there's no start point to
meaningfully ease from).
Full changelog: CHANGELOG.md
v0.3.1
Recommended install: brew install itsdezen/tap/tili —
Homebrew downloads via curl, which doesn't set macOS's
quarantine flag, so Gatekeeper never enters the picture.
The tarballs below are codesigned with tili's self-signed
certificate (hardened runtime) — see
CONTRIBUTING.md.
That's for a stable signing identity across releases (so
your Accessibility grant survives an upgrade), not for
passing Gatekeeper — a self-signed cert isn't notarized. If
you download tili.app directly from this page instead of
using Homebrew, Gatekeeper will still prompt on first launch:
right-click → Open, or xattr -d com.apple.quarantine tili.app.
Fixed
- Reopening a single centered floating window no longer drifts
off-center. The cascade nudge added in 0.3.0 (to keep several
same-sized centered windows from stacking exactly on top of each other)
used to advance a per-workspace counter on every placement and never
reset it, so repeatedly opening and closing one floating window — with
no other centered window ever present — still cycled through the
cascade sequence on every reopen. The nudge now only applies when
another centered floating window is actually on screen at the same
time, so a lone window always reopens dead-center. - Siri AI's background panel no longer steals a floating window's
dead-center placement. Its bundle id (com.apple.campo) wasn't
recognized as system UI, so its transient panel got floated and
centered like a real app window — landing on the same dead-center spot
a genuine floating window would otherwise get, and pushing that real
window into the cascade-offset sequence instead. Added to the same
ignore-list as the Dock/Spotlight/Notification Center's own transient
chrome.
Full changelog: CHANGELOG.md