Cupertino 1.24.0
Version 1.24.0 ·
Added
The Simulator surface works on Xcode 27, through DeviceHub. Xcode 27 ships no Simulator.app; the simulator's window belongs to DeviceHub, which bridges the device's accessibility tree the same way. The surface now drives whichever of the two is showing the simulator, finds the device screen DeviceHub nests beside its sidebar and inspector, and still reaches those two applications and nothing else. Every tool was measured working against an iPhone 18 Pro on iOS 27.0, including rotation, which DeviceHub's menus no longer list but whose shortcuts it keeps.
Two Xcode 27 changes had to be worked around, and both fail without a sign. AppKit reports DeviceHub's process id as -1, so every Accessibility call went to no process and answered like an app with no accessibility at all; the driver now finds the process by its executable. And device types no longer carry their screen size in
profile.plist, so the point size is read fromcapabilities.plist, where Xcode 27 moved it. An earlier entry claimed the surface was working on Xcode 27; only its icon was. A physical device shown in DeviceHub remains out of reach: its screen is a video with no tree behind it.The on-screen notice can be placed, resized and given a sound. Three settings in Settings → General: which of six places along the top or bottom edge of the display the card sits in, how large it is drawn, and whether it plays a sound arriving and leaving. The top-right corner it shipped in is not free for everyone — it is where menu bar apps drop their panels, where Xcode parks its inspector, and on a laptop with a notch it is the narrowest part of the visible frame.
Six places rather than eight, because the vertical middle is where the person is looking and is the one place a notice must not cover. The position control draws them inside the outline of a display rather than listing them in a menu, since the answer is a geometry rather than a phrase, and a Preview button puts a sample card where the real one would go — the setting is otherwise unanswerable from the window it is set in, because the card only appears while an agent is working.
The size is a scale applied to every length in the card, the panel and padding as well as both type sizes, rather than a transform on the rendered layer — which looks identical in a screenshot and wrong on the screen, because it would blow up a bitmap of the text. Medium is scale 1 exactly, so a Mac that never opens the setting sees no change.
The sound is a choice of the Mac's own alert sounds, one for the card arriving and one for it going, with a volume slider. They ship with every Mac, so there is nothing to bundle, and the menu is read from
/System/Library/Soundsrather than written out — it is whatever this macOS has. There is no separate switch: "None" is a row in each menu, because a switch beside two menus has a fourth state that says nothing the menus do not already say. Picking a sound plays it, since a sound is chosen by ear, and the volume plays a sample when the slider is let go rather than on every step. It starts silent, at a third of full if chosen, where an alert sound reads as a cue under what you are doing rather than as an alert.All of it is appearance and none of it can stop the card appearing, change what it says, or move it off the display holding the application being driven. This is the only warning the person at the keyboard gets, and
docs/desktop.mdrecords what happened without one. Sounds are silent under a screenshot run whatever is chosen, and fire when the card arrives and when it goes rather than on each step — an agent working through a sequence shows one card, so it is twice a session and not twice a keystroke.The placement arithmetic lives in
NoticeStylewith no dependency beyondCGRectandmake unitpins it, because it is silent when wrong: AppKit's y grows upward, and a sign error puts the card under the Dock, where it looks exactly like a card that never appeared. So is the volume, which is the other value that can be wrong invisibly —NSArgumentDomainhands back strings, so a volume of 0 read as aDoubleis nil and falls through to a default that is a third of full, which is the opposite of what was asked for.