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.
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 |
|---|---|
| ⌥Space | Capture the focused window and open the palette. |
| ⌥⇧Space | Capture all windows into one merged, searchable list — each line tagged with its source app. |
| ⌥⌘S | Drag 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.
Keys in the palette
| Key | Action |
|---|---|
| type | Filter 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 ⇥. |
| esc | Dismiss. |
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
| Key | Does |
|---|---|
| ↩ | 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 / ⌘V | Cut / replace — clicks, selects the word, then sends ⌘X / ⌘V. |
| any other ⌘-shortcut | Click 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:
| Key | Does |
|---|---|
| ↩ | Copy the recognized text and dismiss. |
| ⇧↩ | Select the span in its app (drag-selects from the first word to the last). |
| ⌘X | Select it, then cut it. |
| ⌘V | Select 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.
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).
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 → the bright payload is the word Total; ↩ copies just that. The dim remainder is context.| Query | Copies |
|---|---|
| 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. |
.* chunks | The whole span — see below. |
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.*errdoes 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.*toworks — middle terms must match words between the ends, in order. - It composes with spaces.
err.*not npmrequires theerr…notchunk and a word matchingnpmwithin 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 plainerr.
Everyday recipes
| You want | Type |
|---|---|
| An ID, hash, or tracking number | a few of its characters |
| A whole error message | first word .* last word |
| The amount off an invoice | $ or the first digits |
| A URL from a slide | http (or htt.* for the full word) |
| The whole line, but found by keyword | keyword 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.
docker and press ↩ to watch it click, or err.*dep then ↩ to select the span (⌘V replace, ⌘X cut).
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.