Selected Work

Locant

A Mac app for pointing a coding agent at the element you mean, so the words you type are only about the change.

Intro

A click says which element. The note says what to change.

Locant is a free, open-source Mac app for pointing a coding agent at the exact element you mean. Click it in any app, type what should change, and the agent gets the element itself: its identifier, its frame, its place in the tree, and a crop.

I design and build my own apps end to end, and a coding agent does most of the typing. Seeing what was wrong took a second. Describing it took the rest: which screen, which component, where it sits in the tree, and then the change itself, in words the agent would read the way I meant them. That attention went into translation, not design.

I made Locant for Palantir's Product Design Show & Tell. Versions 0.1 to 0.4 were built inside the three-hour window, timer running. Four days later it was at 0.8: signed, notarized, public on GitHub, and an MCP server as well.

Role

0→1 Product Designer & Builder

Timeline

3 Hours to v0.4 · 4 Days to v0.8 Sep 13 to 16, 2026

Made For

Palantir Product Design Show & Tell

Scope

A Spec for Every Version Interaction and Visual Design Swift App and MCP Server Site, Film, and Measurement

Tools

Swift 6 · AppKit + SwiftUI Claude Design · Claude Code

Output

Free and Open Source · v0.8 Signed, Notarized · MCP Server

Highlights

One capture, and the agent goes straight to the right file

The agent: the payload pasted into Cursor, and the edit lands in SeedScreenshot.swift
The element, not a screenshotAny Mac app, any agentAsked which orb: 0 of 12 runsv0.1 to v0.4 in three hours
Any agent: the same payload in Cursor, Claude Code, Codex, and Antigravity
Before & After: one click flips between the orb before and after the edit
The ring: hold the ball, then release on an action
Color: a magnifier on the pixel, and the value copied

The Problem

Every fix needed two descriptions, and the first ate the second

UI polish starts as something seen: a button that should be rounder, a card that needs room. A coding agent works in text and code. Every fix meant translating one into the other, and the translation was mine to do.

First, which element: which screen, which component, where it sits in the tree, in names the agent could match to the code. Then, what should change. The first kept eating the second. By the time I had written which button, I had lost what I was actually going to say about it.

Before: a whole sentence about which orb, and not yet a word about the change

A screenshot doesn't remove that work. It hands the first half to the agent. Given a crop of the phone and a note that already said how to make the change, this is what it said:

Claude Code, given a screenshot

“I haven’t changed anything yet, because the screenshot doesn’t show which orb you mean. …

Which orb should get them?”

Opus 5 at medium effort, given a crop of the phone and a note that already said how to make the change. Measurement run, September 15, 2026.

It understood the change, down to the mechanism. It could not know which orb. That is the half Locant takes.

Landscape

Web tools point at elements. Mac tools send the window.

In September 2026, three kinds of tool handed a screen to a coding agent, and screenshot tools sat under all of them. None let a person point at one element in a native app and hand it to whichever agent they use.

Codex Appshots, Claude Desktop quick entry

You point: yes. A double-tap

One element: no. The window or a region

Any agent: no. Their own agent

Native apps: yes. Any Mac app

Agentation, Stagewise, Cursor Design Mode

You point: yes. A click

One element: yes. The DOM node

Any agent: partly. Depends on the tool

Native apps: no. Web pages only

Peekaboo, Xcode 27 Device Hub

You point: no. The agent looks for itself

One element: yes. The accessibility tree

Any agent: partly. Over MCP, or in Xcode

Native apps: yes. Mac apps, the Simulator

CleanShot X, Shottr

You point: yes. A drag

One element: no. Pixels

Any agent: yes. An image, anywhere

Native apps: yes. Any Mac app

Locant

You point: yes. A click, with your note

One element: yes. Identifier, frame, and path

Any agent: yes. Clipboard and MCP

Native apps: yes. Any Mac app, and the Simulator

That research changed three things before any code. No window capture, because two platforms already send the window. A payload of plain text with an image path, because agents handled MCP image blocks unevenly. And field names with no Apple prefix, so another platform could write the same shape.

Appshots gives your agent the window. Locant gives it the element.

Decisions

A pixel is a guess. An identifier can be grepped.

Before any code, one sentence set the feel: Locant should read like something macOS grew, in the family of the ⌘⇧4 crosshair, Live Text, and Spotlight. It appears, is used, and is gone. Three rules followed from it.

· Borrow, don't brand. System accent, system type, system materials. The only mark of its own is the pointing hand.

· Nothing lingers. Every surface dismisses itself, and a confirmation lasts one second.

· Quiet until approached. Nothing pulses, bounces, or waits for you to notice it.

The Claude Design canvas titled Deixis Overlay States, showing Frame 1, Hover, over the iPhone Simulator, with the menu-bar timer reading Design Challenge 2:59:57
Claude Design: the overlay's states, drawn before any code, three seconds into the timer

The accessibility tree, not the pixels

Every native app already carries the answer in its accessibility tree: a role, a label, and often the exact identifier its code uses. A pixel is a guess. An identifier is something an agent can search the code for. The overlay shows it on hover, before the click, so you know what the agent will get.

The overlay over Calculator: the 8 key outlined in blue, with the label button · Eight beneath it
Hover: the element under the cursor, and its identifier
With Option held, the whole keypad is outlined and the label reads group · CalculatorKeypadView
Option: one level up, to the keypad that holds it
A dragged frame around the number keys with a 161 by 167 point readout
Drag: a frame, with every element inside it listed
After the click, a note field under the highlighted key reads make this key bigger, with Return to copy and Escape to cancel
Note: what should change, then Return

It leaves as plain Markdown with the image path first, because a terminal agent receives only the text on paste. This is the capture in the clips above, as Locant wrote it:

## Locant capture (fix)Image: /Users/malik/Pictures/Locant/locant-simulator-20260915-021609-zwec.pngApp: Simulator (com.inspireocean.app) · Window: iPhone 17 Pro – iOS 26.5Captured: 2026-09-15 02:16 · Image region: 139×183 pt @2x (element + 40 pt)Project: /Users/malik/Documents/inspire-ocean ### Target elementbutton "Product Ideas" · id=oceanCurrent.product ideasFrame: x=152.7 y=-1335.3 w=58 h=102Path: application > window > group > button#oceanCurrent.product ideas ### Notemake this orb bigger, with satellite nodes like Light And Color

A ladder when there is no element

Not every view has a name. Views drawn in SpriteKit, Canvas, or Metal expose pixels nobody labeled. So a capture steps down a ladder, and says which rung it reached.

  1. 01

    Identifier

    button · id=captureButton

    An element with a declared identifier. The best case.

  2. 02

    Label only

    button "Save changes" · no identifier

    The nearest labeled neighbors are named too.

  3. 03

    Drawn frame

    Elements in frame (6)

    Drag on the overlay, and everything inside is listed.

  4. 04

    Text and neighbors

    Text in image · Nearby

    Words in the image are recognized.

  5. 05

    Image

    No element information available

    The payload says so, and still carries your note.

The agent always gets something to grep, or an honest null.

One gesture instead of a screenshot tool

It had to replace my screenshot tool, or I would keep switching apps while polishing. Snap, Text, Color, and Cut ride the same gesture, and one rule decides what stays on disk: an action that makes an image keeps a file for 30 days, and one that makes text or a value keeps nothing.

A ball that wakes when you reach for it

A hotkey is invisible and the menu bar is far. So Locant has a glass disc that tucks into the screen edge and wakes as the cursor approaches. Click it to point. Hold it, and a ring unfolds.

The ball docked: a half disc tucked into the screen edge
DockedAfter two seconds without use, it tucks into the nearest edge.
The ball awake: a full glass disc on the desktop
AwakeMove toward it and it comes back out. Drag it anywhere.
The ball ready: the pointing hand appears on the disc
ReadyUnder the cursor the hand appears. Click to point.
The ring: Snap, Text, Color and Cut around the ball, revealed by pressing and holding
The ringPress and hold for Snap, Text, Color, and Cut.

On the whiteboard, hovering the ball opened a ring of six. Only press and hold shipped, with four segments at the compass points. Hover collided with docking at the edge and opened by accident, and 90 degrees per target is hard to miss. Settings moved to the menu bar: it was not ring material.

Whiteboard sketch: the ball at rest and docked at the screen edge, a six-segment ring labeled Hover, and a four-segment ring labeled Press & hold, release to pick/confirm
Whiteboard: the ring on hover, and the press and hold that shipped

Build

Four versions inside the three-hour window

The research, the PRD, and the whiteboard came first. The timer started at 15:27 on September 13, and from there AI came in everywhere except the decisions: the overlay drawn in Claude Design, one spec per version written with Claude, and the Swift written by Claude Code.

Inside the window, Sep 13

  1. v0.117:07

    The pointing flow: a hotkey, a click, a payload

  2. v0.2-region17:17

    The ladder: a drawn frame, text, and neighbors

  3. v0.317:45

    Usable by someone else: mode inferred, Settings, the ball

  4. v0.3-actions17:58

    Snap, Text, Color, Cut, and the ring

  5. v0.418:08

    Before and after, found again by identifier

After it, Sep 14 to 16

  1. v0.5Sep 14

    The first minute: hints that appear once and fade

  2. v0.6Sep 14

    A download that works, and a daily update check

  3. v0.7Sep 14

    Deixis renamed Locant

  4. v0.7.1Sep 15

    The agent fetches it: an MCP server inside the app

  5. v0.8Sep 16

    Web pages carry their DOM handles, and Shift picks several elements

v0.4 was tagged at 18:08, with about nineteen minutes left on the timer.

What fought back

With an hour and 42 minutes left, a capture of Oryne's orbs came back empty: no element information available. The orbs are drawn in SpriteKit, and nothing had named them.

The build recording with an hour and 42 minutes left: a Locant payload for the Oryne orb reads No element information available, beside the Library screen in the Simulator
The empty capture: Oryne's orbs, with an hour and 42 minutes left

Two things came out of that capture. The ladder, so a view with no name still gives the agent something to work from. And a fix to Oryne itself: at 17:02 its orbs were exposed to accessibility with identifiers, which is why every capture on this page can name the Product Ideas orb. Pointing at my own app showed me what it had been hiding from the accessibility tree.

The other fight was Locant seeing itself. Every capture leaves Locant's own windows out, so the overlay, the ball, and the note never land in the picture. Point at Locant's Settings, and it reads the window behind.

Locant on Locant: its own Settings reads as image only, then the window behind it

The product before the plumbing

With an hour and 12 minutes left, the plan said the MCP server came next. I moved it behind the product: Settings and the ball first.

The build recording with an hour and 12 minutes left: Malik's message asks to do the Settings window and the ball before the MCP connection, and finish the product first
The call: Settings and the ball first, the MCP server after

It shipped in 0.7.1, on September 15, and not as the TypeScript package the PRD had planned. The server lives inside the app, started with a flag, because the captures live on the Mac, the app is already signed and updated, and nobody has to install Node.

I used Locant to build Locant. Everything after 0.4 was the product earning a second user: a first minute with no tour, a download that works, and an agent that fetches the capture itself.

Final Design

One element, from pointed at to verified

Every clip below is a recording of the released app.

Hover: Calculator, a CalmMouse slider with no identifier, the orb, then a web page

The payload is plain Markdown, so any agent takes it on paste. Locant is also its own MCP server: connect Claude Code, Cursor, or Codex in Settings, say "fix what I just pointed at", and the agent fetches the capture itself.

Any agent: the same payload in the Codex app, then in Antigravity

After the edit, run the app again. Locant finds the same element by its identifier, captures it when it looks different, and keeps every iteration with the git diff beneath.

Before & After: the orb found again after the edit, 1 file changed and 28 insertions

The four other actions ride the same ball. Hold it, release on one, and the result is on the clipboard.

The ring: hold the ball, release on Color, and pick a value from the screen

Settings had to pass for an Apple app's preferences: system controls only, and every default works on first launch.

Locant Settings, General tab, in the dark appearance: Accessibility and Screen Recording granted, the floating ball and auto-hide on, and a daily update check

Point, note, paste, and see what changed, without once describing which button.

Measured

What Locant saves is the question

I measured one fix both ways. Same fix, same model, same repo, same note. The only difference was what got pasted: a ⌘⇧4 crop of the phone, or one Locant capture of the orb.

Asked which orb

0 of 12

With a screenshot, 17 of 18

Time to the edit

22 s

With a screenshot, 39 s. Medians, no build

Session tokens

253k

With a screenshot, 312k. Medians, no build

Correct edit

30 of 30

Every run on both sides, once the question was answered

One task, one app, one model: Claude Code on Opus 5 at medium effort, a fresh session per run, no MCP on either side, the repo reset between runs, and the order alternated. Method, transcripts, and statistics

Two panels, no build and build verified. With a screenshot the agent stopped to ask which orb in 6 of 6 and 11 of 12 runs; with Locant, 0 of 6 both times. Time from paste to the edit: screenshot medians 39 and 33 seconds, Locant 22 and 16 seconds.

Locant does not make the agent smarter. It removes the one thing the agent could not know from pixels.

Reflection

Every payload has two readers

Every capture is read twice: by me as I point, and by a model that sees only text. The label on the overlay is for the first reader. It says what the agent will get before I click. The image path leads the payload for the second, because a terminal agent can only open what the text tells it to.

Honest by default

The ladder names its rung instead of pretending. The agent table on the site says untested until someone has tested it. When the daily update check broke the promise of no network calls, every sentence that made the promise was rewritten to say what was true.

What is still open

Safari exposes the same web attributes as Chromium and has not been exercised yet. And because the payload's field names carry no Apple prefix, a port to another platform could write the same shape.

Next up

Oryne

0→1 Product Designer & Builder · 2026App Store
AI-NativeMotion DesignDesign Engineering

NeuraLyfe

Product Designer, Maker · 2026
Data VisualizationAI-NativePhysical Prototyping
Aura

Aura

Product Designer · 2025
Hardware UXUX ResearchPhysical Prototyping