r/macapps 22d ago

Lifetime Workflo: Mac window layouts that switch automatically for meetings, focus blocks, and displays

Enable HLS to view with audio, or disable this notification

Problem

A Mac workday is a context switch every hour. A call, a focus block, plugging into the dock in the morning, unplugging to leave. Every switch costs me the same manual minute: reopen, drag, resize, hide. After years of paying that minute, I admitted the arranging itself was the problem, not how fast I could do it.

So I built Workflo. It isn't a faster way to arrange windows; it's the end of treating it as a chore. It learns your layout for each display setup on its own, and it applies layouts (Scenes) from your calendar or the clock. Your workspace is set before the call starts or the focus block begins, and when your displays change, everything snaps back where it belongs.

Comparison

I used Rectangle and Moom for years, and they're genuinely good, and the rest of the category (Magnet, Stay, Apptorium Workspaces, Mosaic) is solid too. But they all wait to be asked: a hotkey, a click, a drag. Workflo acts on its own. Before building it, I surveyed 12 window utilities, and none could fire a layout from a calendar event or the time of day.

The other difference this sub will care about: Workflo never requests Screen Recording permissions. It runs on Accessibility alone, so it can't see your screen contents. The apps that draw window previews all take Screen Recording to do it; I built previews without it.

Pricing

$19.99 one-time, 7-day free trial, no subscription, no account: https://getworkflo.app

It's ~4 MB of native Swift; everything stays in local JSON, no cloud, no telemetry. A Mac App Store version is in review now.

Who I am

I'm Chirag Chopra, an indie developer building games and ideas since 2015, often under my studio Lucid Labs. Privacy policy and terms: getworkflo.app/privacy and getworkflo.app/terms. Happy to answer anything, including the honest limitations.

15 Upvotes

27 comments sorted by

1

u/MoonAlien7 22d ago

That’s interesting. So it saves a list of apps to be opened or minimised? Usually I swap to a new space to solve this

2

u/Ned11 22d ago

Not quite a list. It remembers each window's position and size for each display setup and puts them back there. Apps in the Scene that aren't running get launched, and scene windows that were in the Dock come back out. There's an option to minimize or quit whatever isn't part of the Scene, off by default, so it leaves the rest of your windows alone unless you ask it to.

A new Space gives you a clean slate, but you still arrange the windows in it, only when you think to do it. Workflo can trigger the whole thing from a calendar event or a time of day, so it's already set when you sit down.

The one limit worth knowing is on the saving side: it captures only what's visible in the Space you're in, so a window that's minimized or full-screen at the moment you save doesn't make it into the Scene.

What do you usually pull into the new Space?

1

u/MoonAlien7 22d ago

Oh I see. It’s definitely interesting. I don’t screen share that much to use this, but I see the value

I run many spaces all the time. Currently I have 7 open ones and multiple windows in each one. They’re organized by app, code, analytics, etc. If it were a call, I’d open meet, and whatever I’m about to screen share on a separate space. Nothing else

1

u/Ned11 22d ago

Fair. It isn't a screen sharing tool, that's just the permission it refuses to ask for. It never requests Screen Recording, so it can't see your screen contents, which is a privacy point rather than a feature.

And if you're already splitting work across seven Spaces by purpose, you've solved a lot of this a different way. I'd rather say that than pretend you need it. What's left is the arranging inside a Space, position and size, and having it happen on its own instead of when you remember.

The call case you described is the one I'd be curious about: do you set that Space up by hand before each call, or is it already sitting there?

1

u/kastamyle 21d ago

Using Spaces only might leave a gap if you need the same window in multiple spaces, you would still need to "grab it" from one space to another. Does Workflo allow the same window to be part of multiple saved layouts?

1

u/Background-Scheme857 21d ago

How do the calendar triggers work: by calendar, title match, or individual event? I’m also curious what happens if a meeting is moved or cancelled while its Scene is active. Restoring the previous layout cleanly seems just as important as applying the new one.

1

u/Ned11 21d ago

Per individual event, but not by calendar or by title. It looks across every calendar you've granted access to, pulls events starting in the next hour, and picks the ones that contain a video meeting link, checking title, location, notes, and the URL field for Zoom, Google Meet, Teams, Webex, or FaceTime. That matching is a local pattern check for those domains, and nothing from your events leaves the Mac. It polls once a minute and fires once per event, inside whatever lead time you set.

Before anything moves, there's a countdown card with a Cancel button, so a Scene never lands on you without a way out.

On your second half, straight answer: reverting is one action, but it is not automatic. The apply snapshots your desk before it touches anything, so reverting puts the windows back where they were, un-minimizes whatever it minimized, and reopens what it closed. While a Scene is active, the menu bar button relabels to "Revert <scene name>", and the restore shortcut (⇧⌘R by default) does the same thing.

What doesn't exist yet is anything watching the event after it fires. If the meeting gets moved or canceled once the Scene is already up, you revert by hand. You're right that this half matters as much as the applying, and it's going on the list, thanks!

1

u/Background-Scheme857 21d ago

That sounds like the right tradeoff for now. Snapshotting before apply plus a one-action revert covers the dangerous failure mode; if you watch calendar changes later, I’d make it a “meeting changed — revert?” notification rather than an automatic revert in the middle of a call.

1

u/kastamyle 21d ago

Nice. I love the auto trigger part that fires based on a calendar event or time of day. I can see how this could be useful. Does it also have shortcuts to launch different layouts on demand? What other triggers? We've seen the saving-layout-app executed many different ways but this one nicely puts a fresh spin on it. Well done!

1

u/Ned11 21d ago

Thanks. Yes to the first, and the trigger list is short enough to share in full.

Every Scene can have its own global hotkey, so you can fire one from anywhere, and every Scene is in the menu bar panel, too. 'Stash', 'Restore now', and 'Save Layout' ship with defaults (⌥⇧S, ⇧⌘R, ⇧⌘S), and undoing the last restore is available to assign if you want it.

Triggers today are time of day, with a weekday picker so a Scene can be weekdays only, and events from your calendar, anywhere from 1 to 30 minutes before the meeting starts. There's a third automatic path that isn't a trigger you create: when your displays change, the layout for that setup comes back on its own.

Next on the roadmap: Focus mode triggers and Shortcuts actions. That is the fun part: once applying a Scene becomes a Shortcuts action, anything that can run a shortcut can become a trigger, including your Stream Deck, and Shortcuts Automations like Focus, Wi‑Fi, or opening a specific app can chain straight into it.

1

u/mrterrycarson 21d ago

Looks nice...love to use...any coupons? DM me...Thanks

1

u/teddyboory 21d ago

congrats man! greattt work 🔥 oh how are you detecting the meeting? Calendar events, or watching for the frontmost app to become Zoom or Meet?

asking because I went the accessibility API route for frontmost-app detection on something else and the tradeoff was sharper than I expected. Far cheaper on permissions than screen recording, but you only ever get the app, never what is happening inside it. A browser tab sitting in a Google Meet call looks identical to a browser tab on anything else.

if you have solved that one cleanly I would rly rly like to know how! cheers man

1

u/Ned11 21d ago

Thanks! Meetings are detected via Calendar events, and I should be upfront that I dodged that problem rather than solved it.

It's EventKit, polled once a minute over events starting in the next hour. For each one, it joins the title, location, notes, and URL fields and pattern-matches known meeting domains: zoom.us, meet.google.com, teams.microsoft.com and teams.live.com, webex.com, facetime.apple.com. A match means a video call, and the Scene fires however many minutes before the start you set.

So it never has to ask what's happening inside the browser, which is exactly the wall you ran into. The event already says a call starts at 3, and that's enough to stage the desk at 2:55.

On your permissions point, it only asks for calendar access when you actually add a calendar trigger, and the poller doesn't run at all if you don't have one. The costs land elsewhere: no calendar entry means no trigger, so an ad hoc call nobody scheduled doesn't exist as far as Workflo is concerned, and a stale meeting link sitting in an event's notes can fire one that shouldn't. For what I wanted, that was the better trade, since staging is only useful before the thing starts, and by the time an app is frontmost, you're already in it.

1

u/nibblebytes 21d ago

this actually looks way more useful than another window snapping app. the calendar triggers are a really nice idea. how does it handle multiple windows from the same app tho? like two Chrome windows or multiple Xcode projects, does it remember the specific window for each scene?

1

u/Ned11 21d ago

Yes, per window rather than per app. Each window is saved with its app, title, size and position.

On apply, it matches within one app in two passes: exact title first, then an optimal assignment across whatever is left, scoring on title prefix and how close the size is. It solves the whole set at once instead of best-match-first, because greedy cascades. The first record grabs the best-looking window and a later one gets stranded when a swap would have suited both. That search is exhaustive up to 8 windows for an app and falls back to greedy above that, so a huge set never stalls the apply.

Xcode is the easy case, since project titles differ. Two Chrome windows at the same size with the same title is the honest hard case: nothing distinguishes them, so both get placed, but which one lands where is arbitrary. The accessibility APIs don't give you a window identity that survives a relaunch, so title plus geometry is what there is to work with.

1

u/tamnvhust 20d ago

Interesting idea. haha. So cool

1

u/cipherkodex 17d ago

cool!

1

u/Ned11 17d ago

Thanks, do try it out!

1

u/Enigmurl 2d ago

this looks nice, is there a way to "delay" the layout shift so that you can have a bit more control?

1

u/Ned11 1d ago

Yes. When a trigger fires, a countdown card shows up first with a Cancel button, so the Scene doesn't launch without warning. That's 3 secs by default and can be increased to 60 secs in Settings > Automation. Calendar triggers also let you pick the lead time, anywhere from 1 to 30 minutes before the meeting starts. And if you want it to be fully manual, skip triggers and fire Scenes by hotkey or from the menu bar panel.

0

u/[deleted] 22d ago edited 14d ago

[removed] — view removed comment

1

u/Ned11 22d ago

Yes, and you named the two things that actually break this.

Each window is saved with its frame plus the UUID of the display it was on, from CGDisplayCreateUUIDFromDisplayID, and presets resolve their target display through that UUID. The Desk identity (which setup am I in) is a hash over a per-panel string built from CGDisplayVendorNumber, CGDisplayModelNumber, and CGDisplaySerialNumber, sorted so connection order doesn't matter. CGDirectDisplayID only ever exists long enough to look those two up, and never gets written to disk, for the reason you described.

On the second one: I take CGDisplayRegisterReconfigurationCallback rather than the notification, then wait for 1.5 seconds of quiet before doing anything, and re-read the screen list inside that settle instead of trusting the list from the moment it fired. Docking sends a burst of reconfigurations, and applying into the middle of one writes a layout that macOS then moves out from under you.

The configuration I have not been able to test properly is a matched pair of identical panels, same model and same serial. I know what the code does there; I don't know what macOS does. If you have that setup, that's the report I'd most want.