Skip to content

Windows: follow Claude Code sessions from the island, and answer them - #71

Open
Edurique wants to merge 29 commits into
Louis-CFM:mainfrom
Edurique:windows-claude-sessions
Open

Edurique wants to merge 29 commits into
Louis-CFM:mainfrom
Edurique:windows-claude-sessions

Conversation

@Edurique

@Edurique Edurique commented Oct 1, 2026 •

Copy link
Copy Markdown

A Claude Code session followed from the island: the card, the journal, a file's diff, a question answered, several sessions, the reply

What

Follows Claude Code sessions from the Windows island, and lets the two things Claude waits on — a question, a permission — be answered where they are seen. The island stays a companion: everything done from it goes through Claude Code's own hooks and shows in Claude Code, whether the session runs in the Claude desktop app, VS Code or a terminal.

This is stacked on #57 (the GitHub panel): it reuses its panel layout, its diff and its syntax colours. Until #57 is merged, GitHub shows its commits here too; the ones that are this pull request are the last eleven. Without the panel it is about 4 200 lines in 28 files.

Windows only. Nothing under NotchBuddy/ is touched. As with the panel, the aim is to port it to Swift if you like where it goes.

Issues this closes

  • Closes Multi-session support: track multiple Claude Code terminals simultaneously #18 — Multi-session support. Every Claude Code session is followed on its own (keyed by its session_id), a card names the conversation its request comes from, each session's request is answered on its own, and requests made at the same time wait their turn. The list of sessions shows where each one is at.

  • Closes Overlapping Text on Windows #39 — Overlapping text on Windows. The screenshot there is two rows of the overview's ticker on one line. The ticker is replaced by the session's card, so there is nothing left to overlap.

  • Closes Answer AskUserQuestion from the island (not only Allow / Deny) #94 — Answer AskUserQuestion from the island. The card shows the question and its options (one choice or several, several questions in a row) and the chosen answer goes back to Claude Code, so nothing is asked again in the terminal.
    All three on Windows. They are linked so that merging this closes them; if you would rather close them yourself, say so and I will turn these into plain mentions.

What it looks like

The session's card

As the prototype draws it: the step under way by its icon and its name, with the file or the command it is at, and a look at what it did — the lines of a file read, a command with the end of what it printed.

Once the turn is over: Done, and the first lines of what Claude replied.

The session panel

A click on the card. Mochi's column with the last steps, and the session's journal, read like a conversation in the order things happened: what was asked, each file read, each edit with its diff typed as it lands, each command with the end of what it printed.

Questions and permissions are in the journal too — the options with the ones picked, a tool that had to ask with what it was told — and it ends on Claude's reply, as it wrote it.

Behind the journal, every file the session changed, each with its whole diff.

Answering a question

A question from Claude's question tool, on the island: each option with what it means. One click answers.

Several to pick, then Send; several questions in a row, with a way back; an answer typed with Other…; Skip, or handing it back to Claude Code.

Permissions

A permission request for an edit shows the diff it would make before Allow. A command shows the command.

A turn's end

The finished card says what Claude replied, with the way to the whole reply and to what was changed. An error says so, with the way to where the session runs.

Several sessions at once

Each is followed on its own, and one is in front. With more than one, the session's card says how many there are; that chip opens the list of them, and a click puts one in front. The island's bar is untouched.

A request made while another is on the card waits its turn: the card says how many are waiting, and the next comes forward once this one is answered.

All of these are the real island playing a made-up session through its own hook handler (dev/claude-preview.html), not mock-ups.

How to read it

Eleven commits on top of #57 and a merge of main, each of which builds:

Commit What Where
1 The relay carries what a session does, and takes an answer back hook/src/main.rs
2 Follow sessions from the island, and answer them src/island/hooks.ts, src/views/session.ts, step.ts, markdown.ts, views.ts, core/state.ts, style.css, and pipe.rs / lib.rs for the answer
3 A preview page dev/claude-preview.*
4 README and screenshots windows/README.md, windows/screenshots/
5–11 Follow-ups from using it: the screenshots redone, a least air above and below the lines of every card, and the other sessions as a list behind a chip on the session's card views.ts, session.ts, style.css

How it works

  • Everything comes from the hooks Claude Code already sends. Nothing is asked of Anthropic and nothing leaves the machine.
  • The relay forwards a few lines of a tool's response instead of dropping it whole: an edit's diff (from the structuredPatch Claude Code puts in the response), the first lines of a file read, the last lines a command printed, what a search found, the answers to a question. Forty lines at most.
  • A question is answered through the PermissionRequest hook: allowing the question tool with updatedInput set to its own input plus answers. The session goes on as if the answer had been picked in Claude Code. Skip is a deny with a message saying so.
  • Two things are read from disk by the relay: the file an edit asks permission for, to show its diff (never written), and the end of the session's transcript at a turn's ends, for the conversation's title and Claude's last message.
  • Your third-party agents (coucou_agent) are untouched: an event tagged for an agent goes to that agent's pill, in a handler of its own with your logic; everything else is a Claude Code session.

How to try it

cd windows
npm install
npm run dev

then open http://127.0.0.1:1420/dev/claude-preview.html. A few addresses:

  • ?view=session&live — the journal, an edit typed every few seconds
  • ?view=session&asked — a question and a permission in the journal
  • ?view=question — the question card (&many for three questions, &multi to pick several)
  • ?view=approval&diff — a permission request with its diff
  • ?view=overview&sessions — three sessions at once (the "3 sessions" chip on the card)

With Claude Code: npm run tauri dev, and ask Claude something that reads a file, edits one, runs a command and asks a question.

The rules of the house

  • No new dependency: Cargo.toml, Cargo.lock, package.json and package-lock.json are unchanged.
  • Never block Claude Code: still only a permission request waits for a human, with the same ack and timeout as before. A paused island, a session it does not follow, or a second request from the same session is declined at once, and Claude Code asks in its own window.
  • Never approve without an explicit click: an answer, a skip, an allow and a deny are each a click on the island.
  • ~/.claude/settings.json is not touched: the hooks installed today are enough.
  • No telemetry, no network: nothing here makes a request.
  • Nothing sensitive in the log: pipe.rs logs the event's name and the first word of a reply, never a diff, an output or an answer.
  • 0 % CPU when hidden: the journal is drawn only while the panel is on screen; its animations run once and stop, and are paused with the rest when the island is folded (Windows: nothing in the views moves while the island is folded or hidden #56).
  • Memory: a session keeps its last 80 journal lines and a file its last 12 edits; four sessions are followed at most; all of it goes with the session.
  • Build: tsc --noEmit, vite build, cargo check pass; cargo test 70 passed (12 in the relay, 10 of them new); cargo clippy shows nothing new.
  • Docs: a Claude Code section in windows/README.md. I left CHANGELOG.md to you.

Things you should know before merging

  • The overview's ticker is gone on Windows. A session's card is the prototype's step-and-preview instead, and ticker.ts is removed. It is the part most likely to be a matter of taste.
  • The Claude pill mirrors the session in front. State.sessions holds every session; the integration_claude task takes the front one's project, state and steps, so Mochi, the mini grid and the cards work as before.
  • The transcript's format is Claude Code's own and undocumented. If it changes, the title and the last message simply stop showing; nothing else depends on it.
  • Sessions started by the Agent SDK are ignored (CLAUDE_CODE_ENTRYPOINT starting with sdk): a plugin that runs a review on each commit sends the same hooks from the same folder and was taking the island away from the conversation in hand.
  • The fallback step labels are in English ("Runs · cargo test") where they were the Mac's French ones, like the rest of the Windows island.

What I did not check

  • Only the Claude desktop app was used for real. Nothing in the code is specific to it, but I have not run a session from a terminal or from the VS Code extension against this build.
  • Answering a question from the island was done for real many times (one choice, several, two questions in a row). Skip, and the diff on a permission request, were only exercised on the preview's data.
  • Several sessions at once, and a request waiting its turn, were exercised on the preview and in daily use with two conversations, not with four.
  • Display scales other than 100 % and 125 %.

The island was centred with translateX(-50%). While its width was animating, that put it on a fraction of a pixel, and anything painted during the animation in a compositing layer of its own — a scrolling list — kept that fraction once the island had settled: its text stayed blurry until something made it paint again. The offset is now rounded to a device pixel.
Folded or hidden, the views stay in the page with their opacity at 0, and their endless CSS animations (shimmer, typing, dash-march, pulse) went on being computed for nobody. They are paused while the island is not expanded and pick up when it unfolds.
The GitHub poller becomes a module of its own. With a read-only fine-grained token from the Credential Manager, it reads the year's contributions, the latest activity, the projects and their builds every two minutes — every twenty seconds for the builds alone while one is running — and, on demand, the sheet behind a project, a pull request, an issue, a push, a release, a run or a pull request's comments.

Requests go out one at a time through one client, with ETags so unchanged answers cost nothing, and the rate limit is kept track of. A part the token cannot read is reported by the name of the permission it lacks. The token never leaves this side: the front only ever asks whether it exists.

It also tells the island about two things as they happen: a build of yours that breaks, and a pull request of yours that is merged.
The GitHub card in the overview shows the year's contributions, the last seven days and the latest activity. Its figure opens a panel as tall as the window allows, with two tabs — Activity (the contribution graph, a day's work on a click, the latest events) and Projects — and a sheet behind every line: a project, a pull request with its checks and its files, an issue, a push, a release, a run with one mini Mochi per job, followed live, a pull request's comments, and a file's diff, syntax-coloured, opened on the line a thread is about. The trail on the left is the way back.

A broken build or a merged pull request opens the island on a card with Open and OK. OK folds the island and leaves the news on the pill for five minutes; a click on Mochi or on the card goes to it.

Mochi takes the colour of the day under the mouse and the state of the run on show; both are the view's to ask through ViewActions.
Switching GitHub on in Integrations opens its setup under its line: the token, the permissions to grant, and a connection test that checks each of them and names the one that is missing. An integration that needs more than its fields can declare a `setup` the same way.
dev/github-preview.html runs the real island on made-up GitHub data in a plain browser, so the card and the panel can be worked on without a token or the Rust side. Like upload-preview, it never ships in the app.
The relay dropped a tool's response whole and forwarded the rest as it came. The island can now follow a session and answer it, and each of these comes from the relay:

- what an edit did to its file, as a unified diff with the lines' real numbers, from the hunks Claude Code puts in the tool's response; for an edit asking permission, the diff it would make, worked out from the file as it is — which is read, never written;
- a few lines of what a tool gave back: the last lines a command printed, without the escape sequences that colour them, the first lines of a file that was read, the names a search found, the answers to a question. Forty lines at most, each cut at 240 characters;
- the conversation's title and Claude's last message, read from the end of the session's transcript at a turn's ends. That file's format is Claude Code's own: if it changes, they simply stop showing;
- which Claude Code the session runs in (CLAUDE_CODE_ENTRYPOINT).

A question Claude asks with its question tool can be answered on the island: the answers go back on the permission request, in the tool's own input, and the session goes on as if they had been picked in Claude Code. A question left unanswered says so. Still only a permission request waits for a human; nothing here is written to the log.
The island showed one Claude Code session as a ticker of step names, and asked Allow or Deny on a tool's name. It now follows what a session does and lets the two things Claude waits on be answered where they are seen.

The session's card is the prototype's: the step under way by its icon and its name, with the file or the command it is at, and a look at what it did; once the turn is over, Done and the first lines of what Claude replied. A click opens the session panel: Mochi's column with the last steps, and the session's journal, read like a conversation in the order things happened — what was asked, each file read, each edit with its diff typed as it lands, each command with the end of what it printed, questions with what was picked, permissions with what was decided, and Claude's reply. Behind it, every file the session changed with its whole diff.

A question from Claude's question tool has its own card: its options with what they mean, several to pick and Send, a typed answer, Skip, or handing it back to Claude Code. A permission request for an edit shows the diff before Allow.

Several sessions are followed at once, each with its own journal and its own request, one of them in front; the others are a tab away, and a request made while another is on the card waits its turn. Sessions started by a program (the Agent SDK) are left alone, and an event tagged for a third-party agent still goes to that agent's pill.

Nothing changes at a stroke: what is new rises into place the way the island's views do. Windows only; nothing here is fetched, it is all in the hooks.
dev/claude-preview.html plays a made-up session through the island's own hook handler, so the card, the journal, the question and the approval cards can be worked on in a browser without Claude Code: a file being written, a command and what it printed, a question, a permission request with its diff, the end of a turn, several sessions at once.
What the session card and the journal show, answering from the island, several sessions at once, and what is read to do it — with their screenshots.
The two screenshots of the session panel were taken before its tab was settled: they showed the name of the last file Claude was at where the panel says Journal.
…'s diff

With several sessions open their tabs sat under the session's card, which made it taller and crowded, and again at the foot of the panel's column, which took steps away from it. They are in the middle of the island's bar instead: out of the cards' way, and one click away from any view. Picking one brings Claude's pill forward if another had the front.

An approval card that shows a diff let the diff take all the height, so the card's first line and its buttons touched its edges. It keeps the air the other cards have, and the island gives the diff the room the window allows.
The other sessions' tabs sat in the middle of the island's bar. On a Mac that is where the notch has its camera, so nothing can go there if this is to be ported, and a dot with a clipped title did not say what it was for.

With more than one session the bar gets one more tab, with the others on the left, showing how many there are. It opens the list of them in the session panel: each with its name, its project and where it is at, the one on show marked as such. A click puts one in front, on its journal. The tab takes a colour when a session behind the one on show wants looking at.
The count of sessions was a tab of the island's bar, next to the ones that change what the island shows — it is not one of those: it is about Claude Code's sessions. It is a chip on the session's card, at the right of the conversation's title, and again in the panel's head beside the files: where a session is looked at. The bar is back to what it was.
FabioLukas123 added a commit to FabioLukas123/coucou that referenced this pull request Oct 1, 2026
…e GitHub panel

Brings in upstream's draft pull requests Louis-CFM#57 (GitHub panel, read from the
Rust side) and Louis-CFM#71 (follow Claude Code sessions from the island: each step,
each edit's diff, the turn's last message, questions and permissions
answered there), and fits them to the Linux port:

- Claude Code uses the new session model; Codex and OpenCode go through the
  external-agent path into their own pills (which keep their usage cards
  when a session ends), with a card of their last steps while they work.
- Clicking a session on Linux still brings its terminal forward: sessions
  and agent pills carry the process chain the hook sends.
- Answers given on one display's island (permission or question) clear
  the card on the others; secrets-changed reaches every island.
- GitHub keeps the gh CLI login fallback; open_claude_app uses xdg-open.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… above its first

The look at what a step did was cut for three lines in a card made for two rows of ticker: its last line sat on the card's bottom edge. The card now fills its height with the same margin at both ends and the preview takes what is left, and the island is twelve pixels taller while a session's card is on show, so the three lines fit.
… its lines

The cards that tell something — a permission to give, a turn's end, an error — are a fixed height, cut for three short lines. With a conversation's title, a reply that takes two lines, or a detail under the title, their lines came within a few pixels of the card's edges, where the question card next to them keeps a wide margin.

Each of these cards now measures what it holds and asks the island for the height that leaves at least 14 px above and below; one with room to spare stays as tall as it always was, and a card that tells an integration's news keeps its own height. The error card's second button opens where the session runs instead of saying n8n for a Claude Code session.
The six screenshots of the Claude Code section, taken again after the cards got their margins and the sessions their list.
…ne of your projects

The pill spoke up for two things: a build of yours that broke, and a pull request of yours that was merged. A pull request opened by somebody else on a project you work on went by unannounced, when it is the one that asks something of you.

The profile query now brings back the newest open pull requests of each project the Projects tab lists, at no extra request. One opened since the last look by anybody but you is news: a card that says who opened it, where and how big, and Open goes to it. As with merges, the first refresh only learns how things stand, and a broken build or a merge of yours is told first when several things happened at once.

The card's line ("pull request opened") now comes with the news instead of being guessed from good or bad.
@Edurique
Edurique marked this pull request as ready for review October 1, 2026 17:16
News came from every project the Projects tab lists, with no way to keep one quiet: a busy repository you only follow drowns the ones you care about.

Each project of the tab has a bell. A click mutes it — no broken build, merge or opened pull request is told for it — and another gives it its voice back. The choice is kept in the settings (githubMuted, the projects that are quiet, so a new project speaks up by default), and the Rust side reads it before telling any news. What happens on a muted project is still learnt as it happens: unmuting it later does not replay it.
# Conflicts:
#	windows/src/style.css
#	windows/src/views/icons.ts
…thers

The time column of a row was sized for "19m": a project pushed less than a minute ago says "just now", which is wider, and its build badge and counts sat a few pixels left of every other row's. The column is as wide as the longest thing it says.
The rule that gave the session's lists a wider time column is now the GitHub panel's for every list: the copy goes.
A question with four options that each take two lines is taller than the island can be. Its card then filled up to its edges: the conversation's name sat against the top. The card keeps the least air a card has, and the list of options takes what is left and scrolls.
main moved everything that differs between Windows and Linux into
platform/, and with it CREATE_NO_WINDOW and the Windows way of spawning
a process. Opening the Claude desktop app still spawned rundll32 by
hand, so the branch no longer compiled once main was merged in. It now
goes through platform::open_url, like every other address the app opens,
which also makes it work on Linux.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant