Skip to content

Windows & Linux: catch up with the Mac (0.1.1 → 0.2.x) - #307

Merged
Louis-CFM merged 96 commits into
mainfrom
windows-catch-up
Oct 7, 2026
Merged

Louis-CFM merged 96 commits into
mainfrom
windows-catch-up

Conversation

@Louis-CFM

@Louis-CFM Louis-CFM commented Oct 7, 2026 •

Copy link
Copy Markdown
Owner

The Windows and Linux app catches up with the Mac, from 0.1.1 to 0.2.1 — everything except Apple Music and the iPhone (macOS / iCloud only). Version bumped to Windows and Linux 0.2.0; nothing is tagged or published. See the new CHANGELOG.md section for the user-facing notes.

Merge with "Create a merge commit" (not squash): the community PRs below are merged with their authors' commits, so they close as merged with credit, as in #294 and #295.

Community PRs

Each one was audited for the repo rules (secrets, no telemetry, never block an agent, never write a config without diff + backup + click, never approve without a click, no restyle of shipped views, stable pill IDs).

PR Decision Fixes on top
#202 Vite EBUSY merged —
#265 ticker stops at 20 steps merged —
#203 Dynamic Island look first commit only (ticker frame-loop fix) the restyle is left out (no-restyle rule)
#273 ticker overlap superseded by #203's first commit (same line) —
#240 file drop from every Explorer view (#126) merged ingest_file now accepts only paths a real drop delivered (WebView2 on Windows, Tauri's drag-drop event on Linux), once, within 2 min
#279 file drop (other approach) not merged polls every 50 ms forever and reorders Explorer's window every 2 s
#160 auto-close on KDE/Wayland (#136) merged the pointer watch also runs for regular windows (GNOME, X11)
#147, #233 auto-close not merged restyle of the header / changes shipped behaviour; #160 covers it
#149 island at the top on GNOME merged forces x11 only when DISPLAY is set
#152 GNOME placement not merged gtk_layer_is_supported() before GTK init turns layer-shell off everywhere
#122 GNOME large text new fix GDK_DPI_SCALE = 1 / text-scaling-factor unless the user set it
#227 Hyprland display pin + display picker merged a display is remembered by place, name and size (found again after a rearrangement or a resolution change); old keys still match
#299 Arch PKGBUILD (#230) merged license matches LICENSE-ASSETS.md
#216 answer questions from the island merged answers checked against the questions asked (Mac's accepts rule); multi-select sent as a list like the Mac; a question answered in the terminal takes the card down
#172 questions (stacked on #158/#171) parts taken into #216 —
#278 Gemini CLI merged its writer became the one hardened writer for every agent; Claude Code wording restored
#298 Antigravity re-applied, author credited removed an auto-allow: it printed {"decision":"allow"} on every tool call, even with Coucou closed
#231 Cursor Agent re-applied, author credited removed an auto-allow ({"permission":"allow"}); builds on Linux; one agent_cursor pill
#161 OpenRouter, #166 Gemini chat merged into one provider table, authors credited model lists fetched only for the chosen provider; no attribution headers; history never sent in another provider's format
#173 local models + Markdown re-applied without its stack, author credited bounded reads, loopback-only "nothing leaves your PC"
#262 Ollama ideas taken (loopback normalisation) superseded by #173's design
#206 custom Anthropic base URL (#180) re-applied, author credited only COUCOU_ANTHROPIC_BASE_URL (not Claude Code's ANTHROPIC_BASE_URL, which would silently send the key to its proxy); https only except loopback
#171 plan usage its own commit re-applied, author credited the user's previous status line runs through Git Bash like Claude Code does, not cmd
#226 English/Spanish picker and setting reused, author credited replaced by key-based lookup from the Mac's Localizable.xcstrings, 10 languages, no reload when switching
#300 GTK crash + Escape not merged wrong root cause; focusWindow(true) on every expand would steal the keyboard from the editor
#219 minimize button left for you visible change to the shipped header; merge with the Mac's #145 if wanted
#253 Devin left for later wait for the Mac's #252 to fix the pill ID
#158 Linux fixes + language + Claude Code chat not merged claude -p --allowedTools Read WebSearch WebFetch pre-approves tools without a click

Ported from the Mac (no PR existed)

Wardrobe and seasonal outfits, greeting v2 and its sound, Mochi on the desktop, live diff and the final message, GitHub pulse / alerts / contribution grid, keyboard shortcuts (AltGr-safe defaults), weekly recap, Codex / Copilot CLI / Muse Code with approvals, OpenCode / Amp / Hermes plugins, Claude Desktop sessions (Windows), pill catalog and main tool, hook pills without a key (#183), fold a pending approval (#290), auto-close delay followed live (#25), full chat answers (#67), first-name greeting (#154), "Open terminal" brings the session's window forward on Windows, 10 languages.

Still not on Windows/Linux: Apple Music, the iPhone app, sending a dropped file by email, dragging Mochi onto a window to attach it, the desktop-Mochi shortcut. Auto-update is not added: it would be a network call to a service the user didn't configure (rule), so it needs an explicit opt-in decision and a signing key.

Independent review

A separate review of the whole diff found and this branch fixes: the chat could attach any readable file named by the page (now only Coucou's inbox copies); the custom OpenAI-compatible key could be pointed at another address from the page (now bound to the address it was entered for, never over plain http off this computer); an unexpected statusLine type was replaced instead of refused; the relay waited for decisions from agents whose decisions are thrown away; symlinked configs on Windows; Codex's process tree on Windows; an unused innerHTML path.

Tests

  • npm test: 314 pass · cargo test --workspace: 200 + 34 pass · tsc --noEmit clean · vite build OK · string catalog in sync
  • Windows CI and Linux build green on GitHub

Needs a real machine (can't be tested in CI): the drag-out of Mochi on Windows, Cursor / Antigravity / Muse hook commands on Windows (their shell isn't documented), Claude Desktop detection path, layer-shell margins on KDE/COSMIC, Arabic layout and fonts.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DMzyoPKtGVNBUemdZhnWRG


Generated by Claude Code

betodoescher and others added 30 commits October 2, 2026 16:20
GNOME has no layer-shell and ignores where a Wayland window asks to go, so
the island opened in the middle of the screen with no way to move it. On a
GNOME Wayland session Coucou now runs through XWayland (GDK_BACKEND=x11,
overriding one inherited from an editor or terminal), where set_position
works, and the regular island window is a keep-above Dock: the only kind of
window, besides the desktop, that "show desktop" leaves on screen.

COUCOU_X11=0 keeps the native Wayland window, COUCOU_DOCK=0 makes it a
utility window instead of a dock.

Checked on Ubuntu GNOME (Wayland) with xprop: _NET_WM_WINDOW_TYPE_DOCK,
_NET_WM_STATE_ABOVE and _NET_WM_STATE_STICKY, input hint False, top centre
of the primary display.

Co-authored-by: Cursor <cursoragent@cursor.com>
On KDE Plasma (layer-shell, Wayland) the expanded island often never
folded back after the mouse left:

- WebKitGTK does not send mouseout when the pointer really leaves the
  layer surface;
- GTK's leave-notify fires spuriously while the pointer is still over
  the island, immediately followed by a re-enter, which cancels the
  collapse timer;
- WebKitGTK can deliver a queued mousemove after the pointer has left,
  pulling the island back to "inside".

Rust now polls GDK's pointer focus (it follows the compositor's
wl_pointer enter/leave) every 100 ms while the island is shown, reports
both edges to the page as `pointer-inside`, and emits the usual
far-away cursor on leave. The page ignores its own mouse events while
the compositor says the pointer is outside. The timer is removed while
the island is hidden (set_pointer_watch from set_collapsed), so a hidden
island still costs nothing. Windows is unchanged (no-op).
On a fresh Windows build, `npm run tauri dev` dies when Cargo reaches the
last crates: Vite's watcher tries to watch target/debug/deps/coucou_lib.dll
while Cargo holds it locked, throws EBUSY, and the beforeDevCommand exits.

Ignore target/ and src-tauri/ in the Vite watcher. Tauri already watches
src-tauri/ itself.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AskUserQuestion arrived as a permission request and got the Deny / Allow
card, which says nothing about what is being asked and cannot answer it.

The question view now shows the question and its options as buttons. Picking
one sends the answer back to the session; several questions are stepped
through in turn, pick-several questions get toggles and a Done button, and
"Answer in terminal" hands the request back as before.

The answer travels as {"answers":{...}} on the relay connection. coucou-hook
turns it into an allow whose updatedInput is the question's own input plus
that map, and refuses anything else: no other field and no other tool's
input can be changed from the island.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A layer surface ignores set_position, so the island landed on whichever
output had focus when it was mapped. Pin it with gtk_layer_set_monitor to the
display target_monitor picked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BNUKEFycKsFCP5fKkhY1Uf
"Main display" means little on Wayland: GDK has no primary monitor, so it is
whichever output GDK lists first. List every display in "Island lives on" and
store the choice as at:<x>,<y> (logical origin). Names cannot be the key: two
monitors of the same model report the same one. A display that is gone falls
back to the primary one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BNUKEFycKsFCP5fKkhY1Uf
The card opens under a cursor that was busy with the window underneath, so a
click meant for that window could land on Allow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BNUKEFycKsFCP5fKkhY1Uf
Files dragged from the classic Explorer folder view (Downloads, Documents...)
got the "no drop" cursor: the island's innermost windows belong to
msedgewebview2.exe and those drags never reached wry's drop target. Only the
WinUI views (Home, Recent) got through. On Windows the island now lets WebView2
take the drop, as Edge does, and reads the real paths back through
postMessageWithAdditionalObjects (webview_drop.rs). Linux keeps Tauri's drag
events (tauri.linux.conf.json still enables them).

Cancel on the dropped-file card never received the click: the hidden view and
#content sat above the canvas buttons. It now also forgets the file, so the
chat no longer picks it up, and a copy finishing late cannot bring it back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h step

The ticker found new steps by their index in task.steps. That list is capped
at 20: once it is full, every new step shifts the oldest out and the index
stays at 19, so the ticker saw nothing new and showed steps 18 and 19 for the
rest of the session, while the header went on reading 20/20.

Tasks now carry stepSeq, the position of the newest step in the whole
session, which keeps counting past the cap. The ticker scrolls by that, and
takes the texts from the end of the list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Builds the existing Tauri app from windows/ with makepkg and installs it
with its relay, icons and licenses. No change to the app itself.
Port of GithubPoller.swift, GitHubPulse.swift and GitHubActivity.swift:
a GraphQL pulse (my PRs with CI, PRs waiting for my review, default-branch
CI of recent repos) on the Mac cadence (10 s, then 60 s while CI runs,
5 min otherwise) and the contribution calendar (15 s, then 30 min).
headSha-aware alert detection is emitted as github-alerts. Nothing polls
while the pill is off or Coucou is paused; the island can ask for a
refresh of stale data, and a token change drops in-flight answers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
recap.rs keeps per-turn counts (time, files and lines changed, commands,
questions) and permission decisions in recap.json under the app data dir,
with a 12-week window, a hard size cap and atomic writes. Only the agent's
pill ID and the project folder's name are stored, never commands, paths or
prompts. Also saves the shared PNG into Pictures/Downloads without ever
replacing a file, and adds "Weekly recap" to the tray menu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The GitHub card follows GitHubPulseCardView once the pulse is in: stars
and the last 7 days of contributions in the header, then My PRs, To
review and Default branch CI. Each row opens its list; an item opens on
github.com (http/https to github.com only). The header opens the past 23
weeks, and a day shows its count on hover or click. Without a pulse the
card stays the existing stars + repositories overview.

github-alerts from Rust badge the pill (when it isn't on screen) and play
the Mac's sound: error for red CI, question for a review request, finish
for green CI. Opening the card asks Rust to refresh stale data. The open
button now goes to github.com/pulls, as on macOS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Classic token with the repo scope, or fine-grained with read access to
Pull requests, Commit statuses and Actions — the Mac's hint, shown with
the existing hint style.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LCS line diff with 3-line context hunks, the same 200 KB / 4 000-line /
1M-cell guards, toOneLine and the diff step encoding, plus buildFileDiff for
Edit / MultiEdit / Write tool inputs. Tests mirror tests/DiffEngineTests.swift.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PostToolUse of Edit / MultiEdit / Write keeps old_string / new_string /
content up to 256 KB each and 512 KB per event (other fields and events keep
the 2 000-byte cap). Anything cut sets coucou_diff_truncated so the island
shows no counts it cannot trust.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- PostToolUse of a file edit stores its diff (50 per pill, forgotten after an
  hour or at SessionEnd / pill removal) and adds a ticker step: file name +N -M.
- Clicking a diff row opens the diff in the overview's left card (DiffCardView);
  < or Esc goes back, leaving the overview closes it, and the arrow opens the file
  in VS Code through open_file_in_vscode (existing file by full path only; the
  fallback shows its folder, never launches the file).
- Stop reads last_assistant_message (fallback message) through toOneLine: the
  first paragraph becomes the last step and finalLine. The ticker then holds
  still (no shimmer, check mark, #C9CDD4) until the next turn, and the finished
  card shows it on one line with an ellipsis.
- Error and question views skip diff steps when showing the last step.
- The ticker's dim text is pinned to top:0, where a one-line row already put it,
  so a text wider than the row no longer drops below it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port of the Mac's wardrobe (0.1.5, #195): party hat, beanie, crown, witch
hat, Santa hat, bunny ears, bow, sunglasses, round glasses, scarf and
pumpkin, from design/outfits/mochi-outfits.js and MochiOutfitDrawing.swift.
Outfits follow the head in 3D, glasses sit on the eyes, pompoms and hat tips
lag on a spring that reacts to taps and head turns, and outfits drop in and
fade out. With an outfit on, a roll turns the whole character. Only the main
Mochi is ever dressed. Auto mode picks the outfit from the season.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New mochiOutfit field (default "auto", the Mac's raw values). The struct's
serde default keeps settings.json files from older builds loading; the island
reads anything unknown as "auto". Node tests mirror MochiWardrobeTests.swift.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port of GreetingCanvasView.swift (0.1.5, #196): Mochi falls into the island
through warp streaks, bounces, slides to the side and waves with a quick
little hand, then comes back. Plays the Mac's new greeting.wav, faded out
when the greeting is left (SoundEngine.fadeOut).

The collapse into the compact island now uses the island's own 340 ms close
curve: with the Mac's ease the island shrank ahead of Mochi and cut his body
off when the greeting was left early. A Node test runs the island geometry
frame by frame and checks he always fits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New island view styled like the others: hover an outfit to try it on Mochi,
click to keep it (pop + proud). Right-click on Mochi toggles it, from the
compact island too. The bot canvas gets side room for wide hats, and leaving
the greeting view fades its sound.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tauri-plugin-global-shortcut registers the Mac's global actions with
AltGr-safe Ctrl+Alt defaults, stored in settings.shortcuts with serde
defaults. Each press reaches the island as a `shortcut` event (the
wardrobe as `open-wardrobe`). On Windows a Ctrl+Alt combination that
types a character on an installed layout is left unregistered. On
Wayland nothing is registered and `coucou --shortcut <id>` runs an
action through the single-instance relay instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DMzyoPKtGVNBUemdZhnWRG
core/shortcuts.ts ports ShortcutLogic: accelerator parsing and display,
the key recorder, duplicates, the AltGr table the defaults are checked
against, card and pill navigation. island/shortcuts.ts runs the global
actions and the in-island keys (Ctrl instead of Cmd). Tests mirror
tests/ShortcutTests.swift and keep the Rust and TS defaults in step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DMzyoPKtGVNBUemdZhnWRG
Change, record or turn off each global shortcut, see the ones another
app holds, the duplicates and the combinations that type a character,
and the Wayland commands to bind instead. The island's own keys are
listed read-only, as on the Mac.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DMzyoPKtGVNBUemdZhnWRG
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DMzyoPKtGVNBUemdZhnWRG
claude added 3 commits October 8, 2026 00:12
…kout)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
window_owner is the rule the tests pin down; the Windows side walks
ancestors itself, so it is test-only. Only the advisory's package is bumped
in the lockfile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Microsoft reviewed the Trojan:Win32/Wacatac.H!ml false positive and removed
the detection, so the READMEs link the installers again (windows-latest) and
the Windows and Linux workflows publish on their tags. The workflow comment
says to scan a manual build with Defender before tagging.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Louis-CFM
Louis-CFM marked this pull request as ready for review October 7, 2026 23:07
@Louis-CFM
Louis-CFM merged commit ca72e1a into main Oct 7, 2026
3 checks passed
JhoanG956 added a commit to JhoanG956/coucou that referenced this pull request Oct 7, 2026
… CI / Google Calendar branch

Main's Windows catch-up (Louis-CFM#307) brings its own GitHub card, and the Windows
side of this work already lives in its own pull request (Louis-CFM#86). This branch
now takes main's windows/ as is and stays the macOS part: the CHANGELOG
entries and INTEGRATIONS section 8 no longer describe Windows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.