Textracto_
The 90-second guide

Find any text. Then click it, edit it, or copy it.

Press a shortcut, type a few characters to find a line, and press a key. Plain copies its text, ⇧↩ clicks it where it sits, and a .* query makes ⇧↩ select it instead — ready to cut or replace. Every one of those keys is yours to change in Settings ▸ Shortcuts. This page covers the capture shortcuts, every key in the palette, the pointer actions, and — the part worth learning — how your query matches and what it acts on.

Step one

Capture shortcuts

A capture takes what's on screen and runs on-device OCR. The first two open the palette with every recognized line; the third skips the palette and drops a region straight onto your clipboard. Three flavors:

Shortcut (default)Action
⌥SpaceCapture the focused window and open the palette.
⌥⇧SpaceCapture all windows into one merged, searchable list — each line tagged with its source app.
⌥⌘SDrag a box over any region; the text inside is copied straight to your clipboard as a single line — no palette.

All three are rebindable: menu-bar icon ▸ Settings… (the same window this guide opens from). Shortcuts must include , , or .

Drag-to-copy, no palette

⌥⌘S is the odd one out. Instead of opening the palette, the screen dims to a crosshair — drag a rectangle around the text you want and, on release, every recognized line inside it is joined into one line (line breaks removed) and placed on your clipboard. Focus returns to where you were, so you can paste right away. esc — or a click without a drag — cancels. There's nothing to type and no ranking: it's the quickest path when you already know exactly what you want and it sits together on screen, like a code snippet, an address, or an error string.

Step two

Keys in the palette

KeyAction
typeFilter and re-rank as you type.
/ Move the selection.
Copy the recognized text and dismiss.
⇧↩Click the highlighted result where it sits on screen (in a .* search, select it instead). See below.
⌃↩Right-click it. ⇧↩⇧↩ double-clicks (or, in a .* search, clicks the selection).
(empty box)Recall your previous searches, newest first. Only when the box is empty and the selection is at the top — otherwise moves the selection as usual.
⌘⌘Double-tap to switch to selection mode — ⇧↩ selects instead of clicking; tap again to widen to the enclosing pair of delimiters.
Hop the target to a nearby control (button, checkbox…); ⇧⇥ steps back.
⇧↑ ⇧↓ ⇧← ⇧→Hop the target to the nearest control in that direction⇧← for the checkbox beside a line, ⇧→ back to it. Mixes freely with .
escDismiss.
Tip — linger to read the whole thing. Rows that are longer than they can show truncate with an ellipsis. Rest the pointer on one (or arrow onto it) for a second and it expands in place at a smaller font, so you can read the full line before you copy it.
Beyond copying

Click, select, edit

Press a key and Textracto brings that text's window to the front and acts right where the text sits on screen — no mouse. always copies; ⇧↩ is what acts on screen, and what it does depends on whether your query used a .* span.

Normal search — you're pointing at something

KeyDoes
Copy the recognized text and dismiss.
⇧↩Click it — a button, link, tab, cell, or menu found by its label.
⇧↩⇧↩Double-click it (a quick second ⇧↩).
⌃↩Right-click it — open the context menu there.
⌥↩ / ⌘↩Click with that modifier held — e.g. ⌘↩ to open a link in a new tab.
⌘X / ⌘VCut / replace — clicks, selects the word, then sends ⌘X / ⌘V.
any other -shortcutClick it, then send that shortcut to the app (e.g. ⌘F).

Selection search — your query has a .*

Add a .* span (see below) and ⇧↩ works with the text itself instead of clicking:

KeyDoes
Copy the recognized text and dismiss.
⇧↩Select the span in its app (drag-selects from the first word to the last).
⌘XSelect it, then cut it.
⌘VSelect it, then replace it with whatever's on your clipboard.
⇧↩⇧↩ / ⌃↩ / ⌘↩Click / right-click / modified-click the selection, same as a normal search.
  • Selecting is best-effort. It works by moving the real pointer, so results vary a little between apps — most text fields, editors, and web pages behave; a few custom controls won't.
One-time permission. The first time you use a pointer action, macOS asks for Accessibility access — separate from Screen Recording. It is what lets Textracto move the pointer and type for you, and it also lets Textracto read exact text from apps that publish it, including text scrolled out of view. Turn on Textracto under System Settings ▸ Privacy & Security ▸ Accessibility, then try again. Without it, Textracto still reads whatever is on screen from the screen image.
Where clicks won't work. macOS ignores automated clicks on its own privacy and security prompts — the permission dialogs for local network access, camera, microphone, or screen recording. Textracto clicks by synthesizing events, so the system discards them on those windows no matter what permissions Textracto has. It's a deliberate protection against software approving its own permissions, and it applies to every app that automates input. Press those buttons by hand, or with to move the focus ring and Space to press. Ordinary app dialogs — Save, Don't Save, Cancel — are unaffected.
The important part

How matching works

Matching is word-confined

A word is a run of letters, digits, underscores and hyphens — or a run of punctuation. The two never mix, so "legalEntityId": is three words: ", legalEntityId, ":. Your query fuzzy-matches inside a single word — the characters you type can be non-contiguous (inv matches Invoice, dcu matches document) — but a match never spans two words: it will not match Is there by borrowing one letter from each.

Hyphens join, they don't separate. --team-id, app-specific-password and well-known are each one word, so you can type them the way you read them: app-sp finds app-specific-password, and --password finds the flag. A .* still reaches across a hyphen — team.*id matches --team-id — because chunk terms only have to come one after the other, not in different words.

Punctuation forming words of its own is what keeps '.*' and {.*} working: the quotes and braces are matchable words, not invisible separators.

Selecting and widening with ⌘⌘

Normally clicks what you found. Double-tap and the palette switches to selection mode instead: now drag-selects the text on screen, exactly as if your query had contained a .*. Each further double-tap widens the selection to the next enclosing matching pair of delimiters:

line:   -d '{ "type": "adyenForPlatformsAdvanced" }'
query:  type

        type                     ← ↩ clicks it
tap 1   type                     ← ↩ now selects it
tap 2   "type"
tap 3   { "type": "adyenForPlatformsAdvanced" }
tap 4   '{ "type": "adyenForPlatformsAdvanced" }'
tap 5   -d '{ … }'               (whole line)

The first tap doesn't widen — it just starts selecting, so the matched word itself is selectable. If the query already has a .* you're in selection mode already, so the first tap widens straight away.

The pairs are "…", '…', `…`, |…|, $…$, {…}, […], (…) and <…>. Only delimiters that genuinely pair up are crossed, so a trailing : or , is never picked up on its own. When nothing encloses the selection any more, the next double-tap takes the whole line.

The current range is what the row highlights, what a copy takes, and where a click or text-selection lands — they never disagree. Moving the selection or editing the query drops you back to clicking. It has to be alone: ⌘X, ⌘V, ⌘↩ and friends are ordinary shortcuts and never count as a tap.

Spaces mean AND

inv 44 matches lines that have a word matching inv and a word matching 44 — in any order, anywhere on the line.

Ranking

Ranking favors matches at word starts, contiguous runs of your typed characters, and camelCase boundaries. The best match is always on top, so the fastest workflow is: type until the thing you want is #1, then act on it ( to copy, ⇧↩ to click).

The clever bit

What gets copied

The line you see is context; the copy is the payload — shown at full brightness while the rest of the line dims.

Total due: $1,204.00 Preview — invoice.pdf
Query total → the bright payload is the word Total; copies just that. The dim remainder is context.
QueryCopies
Empty (browsing)The whole line.
Plain term(s)Just the matched word(s). total vs Total due: $1,204.00 copies Total. Attached punctuation comes along: 44 vs Invoice #4471 copies #4471. Multiple terms copy each matched word, space-separated.
.* chunksThe whole span — see below.
Power move

Selecting a span with .*

word1.*word2 selects a chunk: it matches when a word fuzzy-matches word1 and a later word on the same line fuzzy-matches word2, and the copy is everything from the first word through the last, verbatim. A .* query is also what switches the pointer actions into select / replace / cut mode — the chunk is exactly what gets selected.

line:   npm ERR! Could not resolve dependency
query:  err.*dep
copies: ERR! Could not resolve dependency

Rules of thumb

  • It crosses lines. A chunk can start on one line and end on a later one (within the same window; spans never mix windows). The copy keeps the line breaks, and the row shows a where each break is. Multi-line rows truncate in the middle so you see both ends; linger to expand the whole thing.
  • Order matters. Like a regex, .* reads left to right (and top to bottom). dep.*err does not match the line above.
  • Nearest end wins. Each place the first word matches starts a chunk, completed by the nearest following match of the last word. Overlapping chunks ending at the same word collapse to the tightest one.
  • The ends are two different words. A chunk needs a first word and a last word, so both terms landing in the same word doesn't count.
  • It chains. from.*via.*to works — middle terms must match words between the ends, in order.
  • It composes with spaces. err.*not npm requires the err…not chunk and a word matching npm within the chunk's lines; both end up in the copy.
  • While you're still typing. err. may momentarily match nothing (the dot is literal until the * arrives); err.* behaves like plain err.
Cheat sheet

Everyday recipes

You wantType
An ID, hash, or tracking numbera few of its characters
A whole error messagefirst word .* last word
The amount off an invoice$ or the first digits
A URL from a slidehttp (or htt.* for the full word)
The whole line, but found by keywordkeyword to find it, esc, then arrow to it with an empty query

That last one is the honest workaround for “I searched to find the line but want all of it” — with a query active, copies take the payload, not the line.

Want to try it live? The home page has a working, miniaturized palette — type docker and press to watch it click, or err.*dep then to select the span (⌘V replace, ⌘X cut).
Say hello

Feedback & bug reports

Textracto is a tiny tool made by one person, and real feedback shapes it. Found a bug, hit a window it couldn't read, or wish it did one more thing? Email hello@textracto.app — it goes straight to me.

  • Bugs: what you captured, what you expected, and what happened. A screenshot of the palette helps.
  • Ideas: the thing you wanted to grab and couldn't — those requests drive the roadmap.
  • Your setup: your macOS version, so I can reproduce it.