aboutsummaryrefslogtreecommitdiffstats
path: root/docs
AgeCommit message (Collapse)AuthorFilesLines
20 hoursdocs(window-switcher): focusHistoryID is not on HyprlandToplevelDanilo M.1-2/+12
Implementing the model found it. The property reads undefined, so the planned sort compared NaN and silently did nothing, leaving the list in arbitrary tracker order while looking entirely plausible. It is on lastIpcObject instead. Carried into the plan's later tasks so the grid does not reintroduce the bare property, and into the notes that Task 6 puts in AGENTS.md, since a sort that quietly does nothing is the kind of wrong answer this repo's notes exist to prevent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SYg4wYHq5XNbiVmMeKRb1S
20 hoursfix(window-switcher): pick the screen the way the other components doDanilo M.1-1/+4
The screen was found by a name written inline, falling back to null. The other three panels all take a monitor property and fall back to screens[0], so this now does too: a machine without a DP-1 gets its first monitor rather than a null screen, and the name is in one place if it ever needs changing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SYg4wYHq5XNbiVmMeKRb1S
20 hoursdocs(window-switcher): the implementation planDanilo M.1-0/+849
Six tasks, each ending in a commit: the skeleton and the keyboard grab, the window model, one card, the grid and empty state, the Hyprland wiring, and the docs. Two things were checked against the running system while writing it rather than left for the implementation to discover. The keyboard grab works: Keys.onEscapePressed on a focused Item inside the layer surface fires, so the AGENTS.md workaround holds and keyboard navigation is safe to build on. And the dispatcher for sending a key is send_shortcut with the underscore; sendshortcut does not exist, and a test written against that name would have failed in a way that looks exactly like the key never arriving. No qmldir is added. There is none anywhere in this repo and Theme resolves without one, since quickshell scans the config directory itself; the AGENTS.md note about needing one is about singletons outside it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SYg4wYHq5XNbiVmMeKRb1S
20 hoursdocs(window-switcher): an empty state, rather than opening on nothingDanilo M.1-0/+19
ALT + TAB with no windows would otherwise give a full-screen dim with nothing in it, which reads as a hang. It is reachable from a fresh session, and from closing the last window in the switcher itself, which stays open by design. It now says so instead. The empty state has no card to click, so the backdrop closes the overlay on click as well as on Escape. That applies generally, not only when there is nothing to show. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SYg4wYHq5XNbiVmMeKRb1S
20 hoursdocs(window-switcher): dispatch args are Lua, and focus raises in monocleDanilo M.1-6/+31
Two findings from testing against the running compositor, both of which would have cost time during implementation. Hyprland 0.56.2 evaluates dispatch arguments as Lua, so the form this design previously specified, dispatch focuswindow address:0x..., is a syntax error rather than a command. It fails silently unless stderr is read: the window does not focus and nothing is reported. hypr-windows.sh was written with hl.dsp.focus({ window = "..." }) for that reason, and this design had misread that as legacy oddity. The fullscreen batch sequence from that script is not carried over. Workspaces 1 and 8 are monocle and 4 and 5 are scrolling, so a stacked window is the normal case here rather than an edge case, and the worry was that focus would land without the window becoming visible. Tested with two stacked windows on workspace 1: focus alone raised the buried one, confirmed on screen in both directions, so no alterzorder and no batch sequence are needed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SYg4wYHq5XNbiVmMeKRb1S
20 hoursdocs(window-switcher): cards fit both orientations, and grow to a ceilingDanilo M.1-7/+32
The card cannot take its shape from its preview. DP-3 reports transform=1, so its windows are 1076x1916 portrait against 2556x1034 on DP-1, a 4.4x span of aspect ratio in one grid, and a fullscreen app on that monitor is portrait too. So the card is a fixed 16:10 box with the preview fitted inside it, which keeps rows aligned; sizing each card to its own preview would give ragged rows and break the alignment of the three text lines that makes the grid scannable. Cards now grow to fill their row up to 640px rather than sitting at a fixed size, so a lone window is shown large without a preview becoming a full-screen mirror of the window it stands for. Column count is the window count capped at six, centred, so one card sits dead centre and two straddle the middle. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SYg4wYHq5XNbiVmMeKRb1S
20 hoursdocs: design for the window-switcher componentDanilo M.1-0/+159
A full-screen grid of live window previews to replace the rofi list on ALT + TAB. The script it replaces is not broken, so this is an upgrade from a textual list to something that shows what is in each window. Settled during design: toggle-style rather than hold-and-release, since a missed ALT release would leave a stuck full-screen overlay; cards wrap into a grid rather than shrinking, so a high window count degrades by growing downwards; only special: workspaces are filtered, because jumping to another desktop is the point of a switcher. Feasibility was checked against quickshell 0.3.1 rather than assumed. ScreencopyView is compiled in and captured all four open windows, the scratchpad on special:special included, so hidden windows need no icon-only fallback. Hyprland.toplevels reads 0 until refreshToplevels() is called, and its address field omits the 0x that dispatches need. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SYg4wYHq5XNbiVmMeKRb1S
28 hoursdocs: the plan does not launch anything on the user's screenDanilo M.1-4/+11
The user works at this machine and runs the shell themselves. An agent executing this plan writes code and runs non-GUI checks, then hands over the command and the numbers to compare against; the four steps that need a running qs are now marked as the user's. Also warns against a bare pkill -x qs, which would take down the three components the user already has running. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WWL8JYHu7yhAdtx5pU9PMU
28 hoursdocs: correct the notmuch malformed-query claimDanilo M.2-11/+23
The previous commit, the spec and the plan all said notmuch exits 0 on a malformed query while printing something that is not a count, and that validating the output as an integer therefore catches it. Measured properly, that is wrong in a way worth recording, because the truth is worse. notmuch fails two different ways. A rejected query prints nothing and exits 1: `notmuch count 'tag:unread and ('`. A query Xapian merely misparses returns a plausible wrong number and exits 0: `notmuch count 'tag:unread and (('` gives 41, and `'tag:unread and tag:'` gives 3. The second is undetectable by any check on the output, which is why the original claim was not just imprecise but inverted: the case it described as caught is the case nothing can catch. The integer validation still earns its place, on the first failure mode, where empty output would otherwise render as an empty inbox. The real defence against the second is that QUERY is a fixed string and is never built from anything, which the comment now says. The earlier measurement that produced the wrong claim read 40 as mangled output when it was a successful parse answering a different question. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WWL8JYHu7yhAdtx5pU9PMU
28 hoursdocs: implementation plan for mail-overviewDanilo M.2-13/+1363
Seven tasks, ordered so the thing most likely to be wrong is checked first. The watcher script comes before any QML because it is the only piece with a real runnable check and needs no compositor. The component skeleton gets its own commit because the failure it guards against is invisible: a config whose only window is hidden exits straight after logging Configuration Loaded and reports no error. Two decisions changed while reading the existing components. The drawer is a fullscreen overlay with content anchored top right, which is the idiom both other panels use and what gives click-outside and Escape for free, rather than a small anchored window. And its exclusionMode is Normal rather than the Ignore those two use, so waybar's exclusive zone places it below the bar without this component carrying the bar's 42px height as a constant to drift. Also records a trap measured this session: notmuch exits 0 even for a malformed query, printing something that is not a count, so every count is validated as an integer rather than trusting the exit status. A failure that rendered as 0 would look exactly like an empty inbox. The live-config task needs real absolute paths, because waybar's exec and Hyprland's Lua strings do not expand a tilde. They are written as <repo> and <home> placeholders with a note to substitute, since the gitleaks hook blocks committed home paths and was right to reject the first attempt. The spec was updated to match both changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WWL8JYHu7yhAdtx5pU9PMU
29 hoursdocs: record why mail-overview counts by tag, not pathDanilo M.1-0/+23
qtmaildir scopes an account with a path glob, so the obvious move was to copy that query. It is wrong for this panel. notmuch deduplicates by message id, so a message that arrived at two configured addresses is one message with two paths, and a path glob counts it under both accounts: the five accounts here sum to 102 against a global total of 101. The icon shows one global total and the panel header repeats it, so rows that sum to a different number would look like a bug. Counting by the account tag, which is a property of the message and therefore singular, makes the rows sum to the header. The cost is that a cross posted message appears under only the account the post-new hook attributed it to. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WWL8JYHu7yhAdtx5pU9PMU
29 hoursdocs: design for mail-overview, a notmuch mail drawerDanilo M.1-0/+259
Replaces three waybar modules that each poll the Gmail API through a python script with its own credentials file, cover three of five accounts, and open Thunderbird. Everything they fetch over the network is already in the local notmuch index, tagged per account. Two decisions carry most of the design. Counts are scoped to "tag:unread and tag:inbox" rather than plain "tag:unread", which for one account is the difference between 32 and 41, because the inbox number is the one that means new mail worth looking at. And the waybar module runs in continuous mode with an inotifywait loop, so waybar owns the watcher process and there is no daemon to supervise on a machine with no systemd; the watch is on the xapian directory rather than on named files because a commit replaces them, which breaks a watch held on a file. The account list is not in the component. qtmaildir.conf already has one [account.<key>] section per account, where <key> is the notmuch tag suffix, carrying a display label and a colour, so the panel parses that and a new account appears without touching QML. Thread level actions are out of scope because they are blocked rather than deferred: qtmaildir takes no arguments, so nothing can tell it which thread to open. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WWL8JYHu7yhAdtx5pU9PMU