adflow

Technical details

How adflow for macOS measures, places and runs itself, what you can set on the command line, and what the server sends. None of this is needed to use it.

Behavior

It behaves like system chrome.

No window elbowing its way forward. The bar sits in the strip macOS claims for itself anyway, and keeps to its measurements.

OPENING
By click only

Nothing expands on its own. The spot changes every 60 s in the compact state too, and the bar stays narrow while it does. One click expands and holds it open, the next collapses it.

GEOMETRY
Only the width moves

The height never changes. Widths follow the content. Compact: badge, brand, tagline, equalizer; expanded: plus body text, button and ✕. All of it is eased along smoothly, including on a spot change.

MEASUREMENTS
Measured at runtime

screen.frame.maxY − screen.visibleFrame.maxY, falling back to NSStatusBar.thickness. On Macs with a notch the height comes out larger by itself; type, badge and button scale along via k = barHeight / 24.

LAYERING
Above everything, on every Space

Window level above the menu bar. Position and height are set again on every resolution or display change. The bar stays flush against the edge.

TIMING
Progress line

A thin line runs along the bottom until the next spot. Right-click jumps ahead at once, Esc or ✕ quits.

BUILD
One file, no baggage

Native, with Cocoa and ApplicationServices. main.swift, compiled with swiftc -O. No packages, no bundle, no Dock icon.

It never covers your menus.

If the front app's menus reach far to the right (Xcode, Photoshop), or many status icons sit to their left, the display slides aside instead of lying on top. And only as far as it has to. Drag the sliders.

free
Status
NEBULA COFFEE
free … to …   width …   x …   centered

The position of the app menus and the status icons can only be read through the Accessibility API. The menu bar itself is a single window of the Window Server. Without permission everything works, but the display always stays centered. With permission it yields. If it fits nowhere, it centers itself in the largest gap so the overlap stays minimal and even.

Double-click. That is the setup.

The download is a finished program. You do not need a compiler, and you do not need the Terminal.

  1. Open Adflow.app

    It is in the file you downloaded. Double-click it like any other app. There is no installer and nothing to agree to.

  2. Allow it once, in System Settings

    Open System Settings, go to Privacy & Security, scroll down and click Open Anyway. That is a one-time step for the archive route; the one-line install skips it. After that the app starts normally, every time.

  3. Enter your ID and passkey

    The app asks for them itself, in a small window, right after the first start. Paste in the two lines you got with the download. You can also click Skip for now: the bar then shows demo spots and earns nothing.

The ready-made app runs on Macs with Apple Silicon, that is M1 and newer. On an older Intel Mac, use the one-line install in Terminal instead: it notices the Intel chip and builds the app for your Mac.

Once it runs

Click the baropens it, click again and it closes
Right-clickjumps to the next spot
Esc, or the crossquits
If you would rather use the Terminal

None of this is necessary. It is here for people who want it, and for anyone who would rather build it from source.

Run from source

# start, recompiles when needed
~/adflow/macos/run.sh

# or directly
swiftc -O main.swift -o island && ./island

Account

./island --pair DIA-XXXXX-XXXXX XXXXX-XXXXX-XXXXX-XXXXX
./island --account
./island --logout

The same thing the app asks for in its own window. The passkey goes over the wire exactly once; after that only a device token sits in ~/Library/Application Support/Adflow, readable by you alone.

Yielding to your menus

./island --setup

Opens the system dialog that grants the permission. Afterwards restart the program that launches it, so Terminal.app, not island. Without the permission everything still works, the bar just always stays centered.

Placement

./island --offset 140
./island --probe

--offset pushes the bar a fixed distance to the right, negative to the left, and needs no permission. --probe shows for 20 seconds which app owns the menu bar and where the bar slides to. --selftest and --snapshot preview.png exist too.

Where the spots come from

Nothing to configure.

The app fetches the current spots from the server when it starts, and again every 15 minutes. You never edit anything, and you never restart anything. If you want to run a spot yourself, you book one, same as any other advertiser.

What a spot looks like on the wire

For the curious. This is the JSON the app receives from ads.php. Nothing here is anything you have to do.

{
  "brand":  "YOUR BRAND",
  "tag":    "Line under the name",
  "body":   "Line that appears only when the bar is open.",
  "cta":    "Button text",
  "mark":   "M",          // letter in the badge
  "accent": "#FF9F0A"     // accent colour
}
Always there
brand, tag, accent

Without these a spot is skipped. If mark is missing, the first letter of the brand is used.

Optional
body, cta

Left empty, the body line and the button simply do not appear. The bar then shows the brand and the line under it.

If the server is unreachable
It keeps running

The spots built into the program take over, so the bar always has something to show. Those do not earn anything, and nothing is reported.

Widths are not configured anywhere. The app measures the text and works out the two widths from it, which is why a longer brand name simply makes the bar wider.