One desktop, many windows, and the changes that arrive late
In a browser rebuild of Catalina, remounting the menu bar kept closing the person’s live stream. How to give each long-lived resource an owner with the right lifetime, and how numbered changes tell a page’s own late writes from someone else’s.

Macalicious is my rebuild of macOS Catalina as React components on Base UI, developed with coding agents under my direction. Its desktop runs in a single browser page: one menu bar, one Dock and as many application windows as you care to open, with a Rust application on Ferrite behind them keeping each person’s data. A real Mac keeps the lifetimes of a process, a window and a login session apart for you. In one page they all come down to React components mounting and unmounting, which means something meant to last for hours can end because of a sensible decision made somewhere else entirely.
The clearest example is the person’s live connection. Each page keeps one stream of server-sent events, carrying that person’s notifications, their Do Not Disturb setting and a count of the changes they have made in each application, and the notification banners and Notification Center share it. That is one connection per page rather than one per person, so two tabs or two browsers each hold their own. The banners live in the status area of the menu bar, and the menu bar is rebuilt whenever the front application changes, with key={front.id}, for a good reason: its menus belong to the application, and a menu left open when the application changes would vanish without telling Base UI’s menubar, which would then open menus on hover and turn the next click on a title into a close. Both decisions were right on their own. Together they meant that clicking from Notes to Safari unmounted the banners, which released the only hold on the stream, which closed it, and the new menu bar’s banners immediately opened another.
Opening that stream costs more than it looks, because it can’t authenticate itself. No request header reaches the application when a stream opens on Ferrite, so the server has no cookie to read, and the page first asks for a ticket over ordinary HTTP, naming the session the desktop was loaded for, then opens the stream with it. A ticket is good once. That rules out the one thing EventSource is good at, since its automatic reconnect would present the spent ticket, so the page closes the object on any error and reconnects by hand with a fresh ticket, backing off to thirty seconds at most. Each change of application therefore meant a ticket request, a new subscription on the server, a request the browser reported as aborted and a page that threw away its copy of the stream’s state until the next event. Several of the end-to-end journeys forgive an aborted request on that route, because a reload cuts the stream legitimately, and to those checks a stream closed by accident looks exactly like one closed on purpose.
The repair was seven lines in the desktop shell, most of them a comment. The stream is reference-counted, opening with its first holder and closing with its last, and the shell now takes a hold of its own for as long as the desktop exists, so the banners can come and go as they please. The test that came with it replaces EventSource with a stand-in, renders two applications, clicks back and forth between them, waits long enough for a replacement connection to have asked for its ticket, and then requires that the first connection is still the only one and still open. Unmounting the desktop must close it.
It would be easy to draw a rule from this about lifting everything to the top of the tree, but the right owner depends on what a resource is for, and the same desktop has three different answers. The personal stream belongs to the desktop, because a notification should arrive whichever application is in front. Music’s audio element belongs to the application: the main window and the MiniPlayer show and drive one player, so moving between them never interrupts a song, and it stops when the last of them closes, as quitting Music does. A reload is the one thing no component survives, so the player saves its place in the song with a request that outlives the page. FaceTime’s camera and microphone belong to the window, requested when it opens and let go when it closes.
Some things have no owner inside the page at all. The clipboard belongs to the browser, which may refuse to let a menu read it: Firefox allows it, Chromium allows it once the person grants access, and WebKit doesn’t. Where it is refused, Paste in the Edit menu goes dim, the menu stays open with the reason underneath, and the keyboard’s Paste still works.
Two routes for one change
Keeping the stream up means the page hears about every change by two routes. A save in Notes comes back as an ordinary HTTP reply, and the same change also shows up in the stream, which reads the server every 250 milliseconds and can beat the reply home. So the page may see the Notes count move from 41 to 42 while its own save is still unanswered. If that change is its own there is nothing to do, and loading the documents again costs a request and can rebuild a window around whatever the person is doing. If it came from another browser, the page has to load it, or the person goes on looking at an old note. At that moment the page can’t tell.
So every change gets a number. Each event on the stream carries the whole state, which means a missed event costs nothing, and the reply to a write names the number its change was given. The browser’s other tabs report the numbers of their own writes over a BroadcastChannel, because the stream can’t tell two tabs of one browser apart. When a count moves, the page looks at the numbers in between. A number it already knows needs nothing. A number it doesn’t know, with no writes of its own to that application under way, belongs to someone else, and the documents load at once. A number it doesn’t know while writes are under way has to wait, since one of their replies may claim it, though never for longer than the fifteen-second deadline on a request. And when more unknown numbers are waiting than there are writes under way, at least one of them cannot be the page’s, so it stops waiting and loads, which covers all of them.
All of this fails in one direction: an uncertain case costs an extra load and never hides a change. Writes made while nothing holds the stream aren’t noted, on purpose, so the next counts take them for someone else’s and load the application once more. The set of remembered numbers stops at 500, and one that falls off the end costs at most another load. A count that starts again, because unused counts expire, has numbers that now name different changes, so the page loads. The first state after connecting reads every document the page holds once more, because a document read before the stream opened may predate a change the stream has already counted. While the lock screen is up the stream is closed but its last counts are kept, so whatever changed elsewhere in the meantime loads when it reopens.
The same thinking applies wherever an answer can land late. A notification you clear leaves the screen at once and stays off it until a state arrives without it, because a state read before the clear may still hold it, and it comes back if the clear failed. Do Not Disturb writes go one at a time and send only the newest value still waiting, so an older one can never land after a newer one.
The most serious lateness is a whole page that has outlived its person. The server ends the session and issues a new cookie on every sign-in and sign-out, and another tab can do that at any moment, so each request from the desktop’s applications names the session the page was loaded for. A request naming an old session gets a 409 instead of reaching the next person’s data, a stale screen can’t lock or sign out the person now signed in, and a stream that finds its session has changed, which it checks every five seconds, ends itself and gets no new ticket. The page then takes everything of the previous person off the screen, asks nothing more of the next one and loads itself again. Nothing is reset in place, because a desktop that has loaded one person’s documents is never handed to another.
Chris Chabot, November 2026