Skip to content

Releases: ChiFungHillmanChan/setpiece

v0.8.2 — Two-Monitor Fixes + WhatsApp Tiling

Choose a tag to compare

@ChiFungHillmanChan ChiFungHillmanChan released this 13 Oct 23:54

Setpiece v0.8.2 fixes three things that went wrong on Macs with more than one display: drag-to-swap across two monitors, an empty strip along the bottom of your MacBook's screen, and WhatsApp floating over your layout. Click Update available in the menu bar to get it.

What's new

  • Drag-to-swap works across two monitors. Each display now keeps its own layout. A window you drag to the other display stays where you drop it and is never pulled back by a later swap. Letting go with the pointer on the other display no longer leaves the window straddling both screens at the wrong size. Applying a layout on one display no longer switches drag-to-swap off on the other.
  • WhatsApp takes its slot again. WhatsApp's main window calls itself a dialog, and since v0.7.6 Setpiece skips dialogs, so it floated over the windows around it. Setpiece now tiles any "dialog" with working minimise, zoom or full-screen buttons. Real dialogs, save sheets and floating palettes are still left alone.
  • No more gap under your MacBook's screen. "Reserve Dock space on every display" is now off by default, and this update turns it off for you. A display keeps room for the Dock only while the Dock is on it, so with the Dock on an external monitor, MacBook layouts reach the bottom edge. Re-applying a layout after the Dock moves still keeps every window in its slot.

Why this matters

With a laptop and an external monitor, these are everyday moves: you push a window to the other screen, you rearrange both screens, the Dock follows your pointer. Before this release, moving a window between screens could get it yanked back or squashed across both, drag-to-swap on one screen stopped working as soon as you used the other, and the laptop gave up a Dock-high strip of space for a Dock that was not there. WhatsApp, a window many people keep open all day, was the one app that ignored your layout entirely.

Under the hood

  • Per-display placements. The single global snapshot of "the last layout applied" is replaced by PlacementRegistry, one snapshot per display. Recording a display strips its windows from every other display's snapshot and prunes unplugged displays. Drag-swap and seam resize look up the snapshot that holds the dragged window.
  • Pointer-aware drops. DragSwapController hides the preview while the pointer is off the layout's display, leaves a window dropped on another display alone, and never pulls back a displaced window that has already left. "On the display" uses NSMouseInRect, because on a display's top row the pointer reports y == frame.maxY, which CGRect.contains excludes.
  • Window chrome beats the label. WhatsApp's main window reports AXSubrole = AXDialog. A denied subrole is now rescued when the window's full-screen button is enabled, or both minimise and zoom are. Sheets and drawers never are.
  • Stable re-apply without the reserve. Before deciding a window has moved, the planner checks it against every rect it could have been tiled into on that display (current visibleFrame, Dock reserved, menu bar only), so a Dock hop no longer reshuffles slots.
  • CI builds again. The Xcode project found SceneCore through the repository's old folder name, so every push since the rename to setpiece failed. It now resolves ...
  • Verified: the multi-monitor drag scenarios were reproduced, then re-run after the fix, on a real MacBook + Dell by driving Setpiece's own drag-swap code against throwaway windows; each now ends with every window in its slot. WhatsApp enumerates as tileable on macOS 27, and the MacBook's tiling rect reaches its bottom edge with the Dock on the external display. Re-applying after a real Dock move is covered by unit tests only, since synthetic pointer events do not move the Dock. Only macOS 27 was tested.
  • Tests: 438 → 467. Universal (arm64 + x86_64), macOS 14+, Apple notarized.

Install

Homebrew:

brew trust chifunghillmanchan/tap       # Homebrew 7+ requires this for third-party taps
brew install --cask chifunghillmanchan/tap/setpiece

Or download Scene-0.8.2.dmg below and drag it to /Applications. See docs/INSTALL.md for the one-time Accessibility step.

Upgrade notes

v0.5.6+ users: click Update available in the menu bar and the app swaps itself for the new bundle and relaunches, with your Accessibility grant intact. Layouts, workspaces and hotkeys are untouched.

Settings: the first launch upgrades settings.json to schema v5 and switches "Reserve Dock space on every display" off. To keep a Dock-sized strip free on every display as before, turn it back on in Settings → Interaction → Dock space; your choice is kept from then on.

Downgrading: v0.7.6 – v0.8.1 open a v5 settings file without trouble. If you downgrade below v0.7.6, delete ~/Library/Application Support/Scene/settings.json first: those releases refuse a settings schema newer than their own and will not launch. The app reseeds it with defaults.

v0.8.1 — Settings Opens Again on macOS 27

Choose a tag to compare

@ChiFungHillmanChan ChiFungHillmanChan released this 05 Oct 16:29

Setpiece v0.8.1 fixes one bug: on macOS 27, opening Settings froze the window and then crashed the app. If you are on v0.8.0, click Update available in the menu bar to get this release. The updater lives in the menu bar panel, not in Settings, so it works even while Settings is crashing.

What's new

  • Settings opens again on macOS 27. On v0.8.0, the Settings window locked up the moment it appeared, so nothing in it could be clicked, and a few seconds later macOS reported that Setpiece quit unexpectedly. It happened every time, because Settings always opens on the Workspaces tab. The tab now looks and works exactly as before.

Why this matters

Settings is the only place to create, edit or delete layouts and workspaces, change hotkeys or adjust animation. With it crashing, the app was stuck on whatever you had already set up. Hotkeys, the menu bar panel and workspace triggers worked normally until you opened Settings; the crash then took the whole app down until you relaunched it. The crash happened while the window was being drawn, before anything could be edited, so no data was at risk.

Under the hood

  • The crash: NSGenericException, "The window has been marked as needing another Update Constraints in Window pass, but it has already had more Update Constraints in Window passes than there are views in the window." It reproduced on every open of v0.8.0 under macOS 27.0.1.
  • The cause: a NavigationSplitView nested inside another. The Workspaces tab was its own NavigationSplitView, sitting in the detail column of the Settings window's. On the window's first layout, the inner split's SwiftUI column kept reporting a new minimum size, and each report invalidated the window's constraints mid-pass, so the pass never settled and AppKit threw.
  • How it was pinned down: bisected in a real build. The same window opens cleanly if it starts on Layouts (an HSplitView in the same column) or About. It still crashes with the macOS 26 title-bar styling turned off. Switching to Workspaces after the window is up, resizing, and closing and reopening never crashed: the bug needs the nested split to be there on the very first layout.
  • The fix: the Workspaces tab is now an HSplitView, the same structure as the Layouts tab. Its New / Duplicate / Delete toolbar still reaches the window toolbar, and the editor pane is the same 304 pt wide at the default window size.
  • Verified on the real app: before, 2 of 2 opens crashed, as did the shipped v0.8.0. After, 3 of 3 Debug opens and 2 of 2 Release builds with this DMG's build flags opened cleanly. Real mouse clicks were checked on every Settings tab, on a workspace row and on the toolbar. Only macOS 27.0.1 was tested.
  • Tests: 438, unchanged. Universal (arm64 + x86_64), macOS 14+, Apple notarized.

Install

Homebrew:

brew trust chifunghillmanchan/tap       # Homebrew 7+ requires this for third-party taps
brew install --cask chifunghillmanchan/tap/setpiece

Or download Scene-0.8.1.dmg below and drag it to /Applications. See docs/INSTALL.md for the one-time Accessibility step.

Upgrade notes

v0.5.6+ users: click Update available in the menu bar and the app swaps itself for the new bundle and relaunches, with your Accessibility grant intact. Layouts, workspaces, hotkeys and settings are untouched.

If you downgrade below v0.7.6, delete ~/Library/Application Support/Scene/settings.json first: v0.7.6 upgraded it to a schema older releases refuse to open, and they will not launch. The app reseeds it with defaults.

v0.8.0 — Scene is now Setpiece

Choose a tag to compare

@ChiFungHillmanChan ChiFungHillmanChan released this 16 Sep 14:27

Scene is now Setpiece. That is the whole release — no layout, hotkey, workspace or update behaviour changed.

Your install is not affected

This is worth stating plainly, because a renamed app is exactly the kind of thing that makes people check their permissions:

  • You will not be asked to re-authorize Accessibility. The bundle identifier is still com.hillman.SceneApp, and that is what macOS ties the grant to.
  • Your layouts, workspaces, hotkeys and settings stay put, in ~/Library/Application Support/Scene.
  • scene:// URLs keep working, permanently, alongside the new setpiece://. Any Shortcut, Raycast script or bookmark you built against the old scheme is safe.
  • brew upgrade keeps working. The cask token moved to setpiece, and the old scene token still resolves to it.

What changes is the name you see: the menu bar, the Settings window, the Accessibility row in System Settings. In Finder the app is still Scene.app — see below.

Why now

The old name collided with SwiftUI's own Scene type, so a Swift project called Scene was effectively invisible in search next to Apple's own documentation.

Renaming only gets more expensive with every release, and v0.7.6 had six downloads when this was cut. Almost the entire install base is still on v0.7.5 or earlier, so nearly everyone upgrading sees exactly one name change rather than two. This was the cheapest moment it will ever be.

Under the hood

  • The app on disk is still Scene.app, deliberately. Every updater shipped up to v0.7.6 looks for "$MOUNT/Scene.app" by name inside the mounted DMG. A DMG carrying any other filename would abort in-app updates for the entire install base — so the bundle filename is frozen until a name-agnostic updater has been in the field for a release.
  • That updater is in this release. UpdateInstaller now takes whichever .app the mounted DMG carries instead of hard-coding the name. This is what would let the bundle itself be renamed later without breaking anyone.
  • setpiece:// is an addition, never a replacement. Automations built against scene:// cannot be seen or migrated from inside the app, so it stays. Five URLRouterSchemeTests cases pin both schemes, and were run against the pre-rename router first — three fail there, so they discriminate rather than pass vacuously.
  • Scope of the text change. 30 English strings and 20 in each Chinese locale, plus five AppIntents descriptions whose English sentence is the catalog key. The Accessibility upgrade hint still names Scene, because v0.4.3 really was called that. SceneCore, SceneApp, Scene.app, scene:// and Support/Scene were verified to have identical occurrence counts before and after the rename pass.
  • Tests: 433 → 438. Universal (arm64 + x86_64), macOS 14+, Apple notarized.

Install

Homebrew:

brew trust chifunghillmanchan/tap       # Homebrew 7+ requires this for third-party taps
brew install --cask chifunghillmanchan/tap/setpiece

Homebrew 7 refuses to load casks from an untrusted tap and raises rather than prompting, so without the first line you get Refusing to load cask … from untrusted tap. Homebrew 6 and earlier need only the second line.

Or download Scene-0.8.0.dmg below and drag it to /Applications. See docs/INSTALL.md for the one-time Accessibility step.

Upgrade notes

v0.5.6+ users: click Update available in the menu bar and the app swaps itself for the new bundle and relaunches, Accessibility grant intact. It will come back named Setpiece.

If you downgrade below v0.7.6, delete ~/Library/Application Support/Scene/settings.json first — v0.7.6 upgraded it to a schema older releases refuse to open, and they will not launch. The app reseeds it with defaults.

v0.7.6 — Cleaner Window Filtering + Dock Space Control

Choose a tag to compare

@ChiFungHillmanChan ChiFungHillmanChan released this 15 Sep 23:45

Scene V0.7.6 fixes two ways a layout could come out wrong, and closes a crash-on-launch that testing this release uncovered. It also asks for a GitHub star — once, and only after you've actually used the app.

What's new

  • Dialogs and floating panels no longer eat a slot. A preferences dialog, a Get Info panel, an inspector or a floating palette used to count as a tileable window, so opening one re-shuffled your whole layout and pushed a real window into the minimize pile. Scene now reads each window's accessibility subrole and skips the ones that aren't yours to arrange.
  • Windows reach the bottom edge again. Settings → Interaction → Dock space lets you turn off the every-display Dock reserve that V0.7.4 introduced, so windows sit flush on the bottom of a display the Dock isn't on.
  • A newer settings file can no longer stop Scene from launching.
  • A one-shot GitHub star nudge, after twenty layouts that actually landed. Answer it either way and it never comes back.
  • The repository is now scene-macos — old links redirect permanently, nothing you have installed breaks.

Why this matters

V0.7.4 made layouts stable under a roaming Dock by reserving the Dock's thickness on every display, because macOS reserves it only on whichever display the Dock currently sits on and the Dock follows your pointer. That was the right default, but it left a dead strip along the bottom of your other display — 55 pt on a typical two-display setup — and there was no way to opt out. Now there is, with the trade-off stated plainly instead of hidden: turn it off and windows reach the bottom edge, but re-applying a layout can shift them when the Dock moves. Single-display Macs are unaffected either way.

The window filter is the other half. Scene tiled every on-screen window an app owned, dialogs included, so a three-way split could be scrambled by something as ordinary as ⌘I in Finder.

Under the hood

  • WindowSubrole is a deny-list, never an allow-list. It rejects AXDialog, AXSystemDialog, AXFloatingWindow, AXSystemFloatingWindow, AXSheet and AXDrawer. Plenty of apps leave the subrole unset or report something bespoke, and refusing to tile those would silently break tiling for an entire app rather than for one stray panel — a failure you could never diagnose. So anything missing, empty or unrecognized still tiles. AXUnknown is deliberately off the list too: it means "the app didn't classify this", which is ambiguous rather than disqualifying, and the layer-0 test already discards the overlays that usually carry it. Verified against live windows, not just unit tests: of four probe windows reaching the layer-0 gate, the AXDialog and AXFloatingWindow ones are now skipped and document windows still tile.
  • All three consumers of the tiling rect read the same Dock setting — plan, drag-swap nearest-slot, seam reflow — because a drag measured against a different rect than the layout was tiled into snaps windows to the wrong slot.
  • Settings schema v3 → v4, and forward-compatible from here on. AppDelegate.init turns any store-init throw into fatalError, and the migration used to throw on every unrecognized schema version — so running a newer Scene and then going back gave you an app that refused to launch with no indication why. This was reproduced live while testing this release, not theorised. A version above the current one is now decoded leniently, and the file is deliberately left alone so returning to the newer build finds its settings intact. This cannot retroactively help V0.7.5 — see Upgrade notes.
  • The star nudge counts only layouts that actually landed. A fire that finds no windows returns early and doesn't count. The counter saturates at its threshold so a long-lived install can't overflow it, stops moving once you answer, and decodes defensively so a hand-edited payload can neither crash the app nor re-arm a prompt you already dismissed. It lives in UserDefaults, not settings.json, because it ticks on a hotkey-rate path.
  • Tests: 394 → 433. Twelve for the subrole deny-list, ten for the Dock reserve, four for the future-settings-file path, thirteen for the star nudge.
  • Universal binary (arm64 + x86_64), macOS 14+, Apple notarized.

Install

Download Scene-0.7.6.dmg below or via brew install --cask chifunghillmanchan/tap/scene. Drag Scene.app to /Applications, then follow docs/INSTALL.md for the one-time Accessibility-permission step.

Upgrade notes

V0.5.6+ users: click Update available in the menu bar and Scene swaps itself for the new bundle and relaunches; Accessibility permission survives the swap. Your layouts, hotkeys and workspaces are untouched, and the new Dock setting defaults to V0.7.4's behaviour, so nothing moves until you change it.

One caveat if you downgrade. V0.7.6 upgrades settings.json to schema v4, and V0.7.5 and earlier refuse a schema they don't recognize — they will not launch. If you go back to an older release, delete ~/Library/Application Support/Scene/settings.json first; Scene will reseed it with defaults. This release makes every later downgrade safe, but it can't change code that already shipped.

v0.7.5 — Settings Toolbar Restored on macOS 26

Choose a tag to compare

@ChiFungHillmanChan ChiFungHillmanChan released this 10 Aug 00:31

Scene V0.7.5 is a single-bug hotfix for macOS 26 (Tahoe): every button in the Settings toolbar was missing, so layouts and workspaces could be edited but never created, duplicated, or deleted. If you're on Tahoe and running V0.7.2, V0.7.3 or V0.7.4, this is the release you want. macOS 14 and 15 were never affected.

What's new

  • The Layouts toolbar is back — +, + Custom, delete, reset-to-default and Restore Default Presets.
  • The Workspaces toolbar is back — New, Duplicate and Delete.
  • Nothing else changed. No layout geometry, hotkey, workspace, or update-checker changes. The Liquid Glass window still looks exactly the same.

Why this matters

Creating and deleting layouts and workspaces happens only through those toolbar buttons. Without them the app still tiled windows correctly from the menu bar and hotkeys, but its entire authoring surface was unreachable — new users on Tahoe opened Settings, found no way to add anything, and reasonably concluded the app was half-finished. Thanks to @nphxexp-create for reporting it (#4).

Under the hood

  • Root cause. V0.7.2's whole-window translucency made an NSVisualEffectView container view controller the window's contentViewController and nested the SwiftUI NSHostingController inside it as a child. NSHostingController only publishes its .toolbar content into view.window.toolbar when it is the window's content view controller; as a child it publishes nothing, with no warning and no compile error. The window ended up with window.toolbar == nil — no empty toolbar, no toolbar at all — so nothing the tabs declared could ever render.
  • How it was isolated. All four combinations of (hosting controller wrapped in a container vs. as the window's root) × (Tahoe transparent-title-bar chrome vs. plain chrome) were built and their window.toolbar inspected. Only the two wrapped cases produced nil. The transparent title bar, the prime suspect, was innocent.
  • Fix. The hosting controller is the window's root contentViewController again, and the .underWindowBackground material moves into SwiftUI as a background layer via a small NSViewRepresentable. The two-branch window construction collapses into one path shared by all macOS versions.
  • Tests: 394/394, unchanged. The defect lives in AppKit/SwiftUI window plumbing inside SceneApp, which swift test (SceneCore only) structurally cannot reach — so no unit test would have caught it and none is added to pretend otherwise. Verified against the running app instead: the Layouts toolbar now reports Add, square.on.square.squareshape.controlhandles, Bin, arrow.counterclockwise, Restore Default Presets, and Workspaces reports New, Duplicate, Bin. The code path now carries a comment naming this trap.
  • Universal binary (arm64 + x86_64), macOS 14+, Apple notarized.

Install

Download Scene-0.7.5.dmg below or via brew install --cask chifunghillmanchan/tap/scene. Drag Scene.app to /Applications, then follow docs/INSTALL.md for the one-time Accessibility-permission step.

Upgrade notes

V0.5.6+ users: click Update available in the menu bar and Scene swaps itself for the new bundle and relaunches; Accessibility permission survives the swap. No data migration — your layouts, hotkeys and workspaces are untouched.

v0.7.4 — Dock-Stable Layouts + One-Hop Updates

Choose a tag to compare

@ChiFungHillmanChan ChiFungHillmanChan released this 26 Jul 13:58

Scene V0.7.4 fixes a layout bug that only showed up on multi-display Macs — the Dock quietly moving your windows around — plus two smaller things: you can now see which layout is applied, and users far behind on versions update straight to the newest release instead of climbing one version at a time.

What's new

  • The Dock no longer drags your layout around. On a multi-display Mac the Dock follows your pointer, and macOS reserves space for it only on whichever display it currently sits on. Scene tiled into that reserved space, so moving the Dock to your other monitor and re-firing a layout resized and shifted every window by the Dock's height — defeating the sticky re-apply that V0.7.3 had just introduced. Scene now reserves the Dock's thickness on every display, so a layout's target rects depend only on your display arrangement. Re-apply is stationary no matter where the Dock has wandered.
  • Windows parked over the Dock are visible to Scene again. A window whose center sat in the Dock strip was previously dropped from the plan entirely — and dropped only while the Dock happened to be on that screen.
  • The menu bar panel ticks the layout you applied. A checkmark sits at the far right of the active layout's row and its name goes bold, so you can tell at a glance which layout is live without counting windows.
  • Updating from an old build goes straight to the newest release. Scene asked GitHub for releases/latest, which sounds like "newest version" but is defined as "most recent release sorted by the date of the commit its tag points at". Cut a hotfix from an older commit and that endpoint hands back the release it supersedes — so someone far behind could be offered an older build, install it, then be offered the next one on relaunch. Scene now reads the full release list and picks the highest version itself.

Why this matters

V0.7.3 made re-applying a layout non-destructive, which only works if "the same layout on the same screen" means the same rectangles every time. It didn't: NSScreen.visibleFrame made the tiling area a function of where your pointer had recently been, because that's what decides which display the Dock is on. Every window fell outside the 10 pt sticky tolerance and got re-assigned by z-order. The fix makes the geometry depend on your display arrangement alone, which is what everyone assumed it already did.

Trade-off worth knowing

The display your Dock is not on now reserves the Dock strip too — that's the cost of the tiling area no longer moving underneath you. If you run Dock auto-hide there's no cost at all, and single-display Macs are byte-identical to V0.7.3.

Under the hood

  • New TilingFrame replaces raw NSScreen.visibleFrame for all slot math. It reserves the Dock uniformly across screens (max of the left/bottom/right insets across the arrangement; the top inset stays per-screen because the menu bar doesn't follow the pointer). Since the Dock occupies exactly one display at a time, that maximum is its thickness and stays constant as it moves. Degenerate arrangements fall back to visibleFrame, and the maximum over a single screen is that screen's own visibleFrame — hence no change for single-display users.
  • DragSwapController and SeamResizeController are constructed with a visibleFrameOverride pointing at the same frame, so nearest-slot and seam-reflow math agree with where windows were actually placed.
  • AXWindowEnumerator's display-ownership test moves from visibleFrame to screen.frame — frame is the correct question for "which display does this window belong to".
  • Coordinator.activeLayoutID is set on the success path of performApplyLayout only, so a fire that finds no windows or loses Accessibility mid-call never ticks a row your screen doesn't reflect. In-memory like Free Mode: after a relaunch nothing is ticked, because Scene can't know whether your windows are still tiled.
  • UpdateChecker lists releases?per_page=100, drops drafts and prereleases, and selects the highest tag with numeric per-component comparison — so v0.10.0 correctly beats v0.9.0, which a string sort gets backwards. The update banner also clears when no newer release exists, so it can't survive a manual DMG install.
  • Tests: 371 → 394. Twelve cases pin the Dock contract, including Dock-on-A vs Dock-on-B yielding identical frames and single-display identity in both Dock states. Eleven pin the release selector, including the out-of-order created_at case.
  • Universal binary (arm64 + x86_64), macOS 14+, Apple notarized.

Install

Download Scene-0.7.4.dmg below or via brew install --cask chifunghillmanchan/tap/scene. Drag Scene.app to /Applications, then follow docs/INSTALL.md for the one-time Accessibility-permission step.

Upgrade notes

V0.5.6+ users: click Update available in the menu bar and Scene swaps itself for the new bundle and relaunches; Accessibility permission survives the swap. No data migration, no changes to layouts, hotkeys, or workspaces.

If you run more than one display, expect your layouts to sit slightly differently the first time you fire one after upgrading — that's the Dock strip now being reserved consistently. It won't move again after that.

v0.7.3 — Sticky Re-apply + Persistent Menu Panel

Choose a tag to compare

@ChiFungHillmanChan ChiFungHillmanChan released this 26 Jul 13:21

Scene V0.7.3 fixes the most annoying thing about re-using a layout: re-firing it no longer reshuffles the windows that were already sitting in it. The menu bar dropdown also stops closing every time you click something in it.

What's new

  • Re-applying a layout keeps placed windows where they are. Open Quads with four windows, close one, open a different app, then click Quads again: the three windows still in their slots do not move, and only the new window slides into the freed slot. Previously every window was re-mapped by z-order, so one small change reshuffled the whole screen. Windows you drag-swapped keep their swapped positions across re-applies too, and windows nudged slightly out of place snap back to exact alignment.
  • The menu bar dropdown stays open while you use it. Clicking Free Mode, a layout, or a workspace no longer dismisses it — you can toggle Free Mode off and fire a layout in a single visit instead of opening the menu twice. Checkmarks and the active-workspace mark now update live in front of you rather than on the next open. The panel closes when you click outside it, press Escape, or open Settings.

Why this matters

Scene's layouts were previously all-or-nothing: any re-fire was a full re-shuffle, which made "just put this new window in the empty slot" impossible — you had to accept every other window moving too. Sticky re-apply makes a layout something you can return to incrementally, which is how people actually work through a day. And the dropdown change removes the one interaction in the app that punished you for using it: toggling Free Mode off, only to have the menu close before you could do the thing you turned it off for.

Under the hood

  • LayoutEngine.plan now runs a sticky pass before assignment: a window whose current frame matches a slot rect within 10 pt (via the existing rectsApproxEqual) claims that slot, then remaining windows fill remaining slots in z-order, and leftovers minimize as before. Stateless by design — no persisted window→slot map that can go stale when windows close and reopen. Switching to a different layout still remaps everything by z-order, since no frame matches the new slot rects.
  • Placement carries an explicit slotIndex. Sticky assignment can claim slots {0, 2, 3} while slot 1 stays free, so placements are no longer index-parallel to layout.slots; the drag-swap and seam-resize window→slot snapshot reads the field instead of inferring the slot from array position.
  • MenuBarExtra moves from .menu to .window style. Native macOS menus always dismiss on item click and that is not overridable, so the dropdown is now a 280 pt panel of custom rows carrying the same content, order, thumbnails, hotkey glyphs, and localization strings as the old menu — no new strings, all three locales unchanged.
  • Tests: 363 → 371. Eight new cases pin the sticky contract, including the exact close-one-add-one scenario, swap preservation, overflow, and the 8 pt vs 12 pt tolerance edges.
  • Universal binary (arm64 + x86_64), macOS 14+, Apple notarized.

Known limitation

Apps that refuse Accessibility resize requests — Apple's own System Settings is the usual one — never land exactly on a slot rect, so they are not recognized as already-placed and may still be re-slotted when you re-fire a layout.

Install

Download Scene-0.7.3.dmg below or via brew install --cask chifunghillmanchan/tap/scene. Drag Scene.app to /Applications, then follow docs/INSTALL.md for the one-time Accessibility-permission step.

Upgrade notes

V0.5.6+ users: click Update available in the menu bar and Scene swaps itself for the new bundle and relaunches; Accessibility permission survives the swap. No data migration, no changes to layouts, hotkeys, or workspaces.

v0.7.2 — Liquid Glass Settings

Choose a tag to compare

@ChiFungHillmanChan ChiFungHillmanChan released this 11 Jul 14:42

Scene V0.7.2 brings the Settings window into macOS 26's Liquid Glass design language: transparent chrome, a floating glass sidebar, and a translucent window that blurs whatever is behind it — plus settings-layout polish on every macOS version.

What's new

  • Liquid Glass Settings window (macOS 26 Tahoe). The separate title bar is gone — traffic lights float over the sidebar, the sidebar renders as the system's floating glass panel, and the entire window sits on a translucent material that blurs the desktop and windows behind it. On macOS 14/15 the window keeps its existing opaque look.
  • Consistent chrome across all five tabs. Workspaces, Layouts, Hotkeys, Interaction, and About now share the same top strip and title. Previously only Workspaces and Layouts (the tabs with toolbar buttons) got one — the other three rendered with no strip and a different top inset.
  • Top-aligned tab content everywhere. About and Interaction were vertically centered while the other tabs hugged the top; every tab now starts from the top, on all macOS versions.

Why this matters

A menu bar utility's Settings window is most of its face. On Tahoe, Scene now looks like it belongs to the OS it's running on — while macOS 14/15 users keep exactly the window they had, byte-for-byte behavior aside from the alignment fix.

Under the hood

  • All Tahoe-only behavior sits behind if #available(macOS 26.0, *). SwiftUI's containerBackground(for: .window) does not bridge into a manually hosted NSWindow, so the backdrop is an NSVisualEffectView (.behindWindow, .underWindowBackground) beneath the hosting view, with scroll backgrounds cleared so the material shows through.
  • SceneApp-only change; SceneCore untouched. Tests: 363/363.
  • Universal binary (arm64 + x86_64), macOS 14+, Apple notarized.

Install

Download Scene-0.7.2.dmg below or via brew install --cask chifunghillmanchan/tap/scene. Drag Scene.app to /Applications, then follow docs/INSTALL.md for the one-time Accessibility-permission step.

Upgrade notes

V0.5.6+ users: click Update available in the menu bar and Scene swaps itself for the new bundle and relaunches; Accessibility permission survives the swap. No data migration, no changes to layouts, hotkeys, or workspaces.

v0.7.1 — Layout orientation fix

Choose a tag to compare

@ChiFungHillmanChan ChiFungHillmanChan released this 10 Jul 01:25

Scene V0.7.1 is a bug-fix release: vertically asymmetric layouts no longer apply upside down, and resizing windows placed by a canvas-built custom layout no longer scrambles the arrangement.

What's new

Nothing new — this is a pure fix release. Skip to the fixes.

Fixes

  • Vertically asymmetric layouts no longer apply mirrored. The layout editor, the thumbnails, and the built-in templates all describe slots with y measured from the top of the screen, but the step that converts those slots into real window frames measured y from the bottom. Any layout whose top half differed from its bottom half therefore applied vertically flipped: a custom "2 slots on top, 3 on bottom" landed as 3-top/2-bottom, Main + Side (Vertical) put the main window at the bottom, and the L-shape variants swapped. The symmetric presets (Halves/Thirds Vertical) hid the bug — a 5-slot custom layout exposed it. The conversion now flips the y-axis exactly once, and the seam-drag math flips its inverse mapping to match, so what you draw is what you get.
  • Seam-resize no longer scrambles tree-based custom layouts. Custom layouts built in the canvas editor carry leftover template metadata from whatever they were duplicated from. The companion-resize (seam drag) feature read that stale metadata and could shove other windows to positions belonging to a completely different layout — which windows moved depended on which window happened to echo a resize event, so it looked random. Canvas-built layouts now resize freely (no companion reflow); template layouts keep the companion-resize behavior.

Why this matters

Custom layouts are only useful if the screen ends up looking like the editor. Before this fix every vertically asymmetric arrangement silently mirrored, and terminal-heavy custom layouts could rearrange themselves mid-drag. Both failure modes are gone, pinned by regression tests.

Under the hood

  • SceneCore: unit slot rects are canonically top-left origin; Slot.absoluteRect(in:) performs the single top-left→bottom-left flip at materialization (mirroring the single NS→AX flip at the write boundary). LayoutReflow flips its NS→unit inverse and swaps the row templates' seam-edge mapping. The Coordinator's seam-resize context provider returns no context for tree-based layouts. Tests: 363/363 (6 new, including the exact reported 5-slot scenario).
  • Universal binary (arm64 + x86_64), macOS 14+, Apple notarized.

Install

Download Scene-0.7.1.dmg below or via brew install --cask chifunghillmanchan/tap/scene. Drag Scene.app to /Applications, then follow docs/INSTALL.md for the one-time Accessibility-permission step.

Upgrade notes

V0.5.6+ users: click Update available in the menu bar and Scene swaps itself for the new bundle and relaunches; Accessibility permission survives the swap. One heads-up: if you had rebuilt a layout upside down to compensate for the old mirrored behavior, re-edit it once after updating — layouts now apply exactly as drawn in the editor.

v0.7.0 — Automation surface + per-display layouts

Choose a tag to compare

@ChiFungHillmanChan ChiFungHillmanChan released this 08 Jul 02:01

Scene V0.7.0 opens Scene up to automation — a scene:// URL scheme and 5 AppIntents so you can drive it from Terminal, Raycast, Alfred, Stream Deck, Shortcuts.app, or Siri voice — plus per-display layouts for multi-monitor Workspaces, and two reliability fixes to drag-to-swap and Free Mode.

What's new

  • Automation surface. Scene now exposes a scene:// URL scheme and 5 AppIntents: Activate Workspace, Apply Layout, List Workspaces, Toggle Free Mode, Set Free Mode. Trigger layouts and workspaces from a keyboard launcher, a Stream Deck button, a shell script, or a Siri phrase. AppIntents require macOS 14.1+; the URL scheme works on macOS 14+.
  • Per-display layouts. A Workspace can assign a different layout to each connected display, applied together on activation. Screens without an explicit assignment fall back to the workspace's primary layout — a first step toward full multi-monitor support.

Fixes

  • Drag-to-swap stays reliable across repeated swaps. The window→slot snapshot no longer goes stale after the first swap — previously the second consecutive swap dropped the displaced window, leaving two windows overlapping in one slot.
  • Free Mode reliably toggles back off from the menu. The menu now observes the coordinator directly (the same fix already used for the menu-bar icon), so toggling Free Mode off always takes effect without relaunching.

Why this matters

Automation lets Scene disappear into the workflows you already use — a Raycast hotkey, a Shortcut, "Hey Siri, activate my Coding workspace." Per-display layouts make multi-monitor Workspaces behave the way you'd expect. And the two fixes clear the most-reported rough edges in the drag and Free Mode surfaces.

Under the hood

  • SceneCore: AutomationCommand + URLRouter (scene:// parsing), DisplayLayoutAssignment + Workspace.displayLayouts, plus the drag-swap snapshot fix. Tests: 357/357.
  • Universal binary (arm64 + x86_64), macOS 14+, Apple notarized.

Install

Download Scene-0.7.0.dmg below or via brew install --cask chifunghillmanchan/tap/scene. Drag Scene.app to /Applications, then follow docs/INSTALL.md for the one-time Accessibility-permission step.

Upgrade notes

V0.5.6+ users: the in-app installer handles this — click Update available in the menu bar, confirm, and Scene swaps itself for the new bundle and relaunches. Accessibility permission survives the swap. Existing v0.6.1 users should see the V0.7.0 prompt within an hour of next launch (or immediately on quit + relaunch). No re-grant required.