Getting started
Unfiled opens to an empty tab with a caret already in it. There is no project to create and no file to name. Type, paste, close the lid — it is already saved.
- Open Unfiled. A tab called Untitled 1 is waiting.
- Paste anything. The bar along the bottom names what it recognized and offers the actions that apply.
- Press ⌘T for another tab whenever a new thought arrives.
- Quit whenever you like. You will never be asked to save.
The one habit to unlearn
Do not name things. A tab earns a filename only when you decide it should live on disk; until then Untitled 7 is a perfectly good name for a stack trace you will read twice and forget.
Tabs and autosave
Every tab is written to a local recovery database as you type. That database — not a file on disk — is the source of truth for a scratch tab, which is why nothing prompts you on quit.
The tab strip. A dot means a file-backed tab has unsaved changes; scratch tabs never show one, because there is nothing to save them to.
What gets restored
- Every tab, in the order you left them
- Which tab was active
- The caret position and scroll offset in each tab
- Pins, groups and the collapsed state of each group
Copying a whole tab
The Copy button in the toolbar puts the entire tab on the clipboard. Not the selection — ⌘C already does that, and the reason to reach for this is precisely that you do not want to select two hundred thousand lines first. The button shows a checkmark for a moment to confirm it landed.
Closing and reopening
⌘W closes a tab. If it was file-backed and had unsaved edits, Unfiled asks; if it was a scratch tab, it closes silently and goes onto the reopen stack. ⇧⌘T brings back the last one you closed, and keeps going back if you press it again.
Files, encoding and line endings
Unfiled is happy to work with real files; it just does not insist on it. ⌘O opens one into a new tab, ⌘S saves the current tab (asking for a location the first time), and ⇧⌘S is Save As.
Reading is lossless. Unfiled detects the encoding, remembers it, and writes the file back in the same encoding with the same line endings unless you ask it to change them. Both are shown in the status bar at all times.
Conversion is always explicit
Changing UTF-16 to UTF-8, or CRLF to LF, happens only when you pick it from the Format menu. Opening and saving a file never silently rewrites either one.
Smart actions
The bar along the bottom of the window is the heart of the app. Unfiled runs eleven detectors over the content of the active tab and shows only the actions that the detected format supports — so the bar is short, and everything on it is relevant.
2
3
4
{
"service": "orders-api",
"retries": 3
}
A locked action is never hidden. You can always see what Pro would offer for the text in front of you.
What Unfiled recognizes
Two rules that never bend
- Detection is a suggestion, never a conversion. Nothing is reformatted because Unfiled guessed a format.
- Every transformation is exactly one undo step. ⌘Z puts the text back the way it was, once.
The editor
A real AppKit text view, not a web view. System text behaviors work the way you expect them to: word movement, emoji, right-to-left text, the services menu, dictation, and macOS's own undo coalescing.
- Line numbers in a gutter that stays aligned as text wraps.
- Syntax highlighting for twenty languages, applied as a display attribute so it never enters your text or your undo history.
- Log colouring — open a log and its severities colour themselves in the editor, so
ERRORandWARNdo not look alike and the timestamps step back out of the way. - Current line highlight, subtle enough to ignore and clear enough to find.
- Large file mode — past a size threshold, expensive passes back off automatically so typing stays instant.
Logs are coloured in the editor, not only in the workbench
A log opened in Unfiled colours its own severities, timestamps and quoted strings while you scroll and edit it — no mode to enter, and nothing to pay for. It reads the same formats the Log workbench does, so WARNING, SEVERE, CRITICAL and FINE land on the right colour, and both views take that colour from the same palette. Stack frames are deliberately left plain: a frame carries no severity of its own, and colouring every one puts the loudest colour on the least informative lines.
Why highlighting never breaks undo
Colours are applied as temporary display attributes on the layout manager rather than as attributes on the string itself. The document you undo, save and copy is exactly the plain text you typed.
Find and replace
⌘F opens the find bar over the editor. Matches are counted and highlighted live; ⌘G and ⇧⌘G step through them without touching the mouse.
2
3
{
"retries": 3, "retries_max": 9
}
- Aa toggles case sensitivity; the match count updates as you type.
- .* switches to regular expressions, with capture groups available in the replacement as $1, $2 and so on.
- Replace All is a single undo step no matter how many matches it changes.
Split view and multi-cursor
Put two tabs side by side with ⌥⌘\, or stacked, and pick what goes in the second pane from its own tab menu. Each pane scrolls, searches and selects on its own.
2
3
ALTER TABLE orders ADD COLUMN tls_mode TEXT DEFAULT 'require';
2
3
ALTER TABLE orders DROP COLUMN tls_mode; /* verify first */
Column selection and multiple carets
- Hold ⌥ and drag to select a rectangular block — ideal for a column of a fixed-width log.
- ⌃⇧↓ adds a caret on the line below; type once and every caret types.
- Every multi-caret edit collapses into one undo step.
Validators
The active tab is checked as you type. The status bar carries the verdict, so you never have to ask for it; the panel carries the detail when the verdict is bad.
6
7
8
9
replicas: 3 image: orders-api:2.4 replicas: 5 ports: - 8080
Clicking an issue moves the caret to its line and column.
The three places it shows up
- The status bar chip. Pass or fail for the active tab, updated about a quarter of a second after you stop typing. Click it to open the panel.
- The Validation panel. One panel for every format. Each issue carries a message, a line and column, and a hint when there is something concrete to suggest.
- Tools ▸ Validate (⌥⌘V). Runs the check and opens the panel.
When detection guesses wrong
Tools ▸ Validate As pins a format for the current tab, which is what you want when a file has no extension, or when JSON Lines arrives looking like broken JSON. The choice sticks while you edit and clears when you switch tabs, because it was about that document.
What each validator checks
| Format | Engine | Catches |
|---|---|---|
| JSON Free | Strict RFC 8259 parser | Any syntax error, with the line and column. No comments, trailing commas or single quotes — those are not JSON. |
| JSON Lines | The same parser, per line | Which records are broken, by record number, so one bad line in a thousand is findable. |
| XML | Foundation’s XML parser | Well-formedness: tags that do not nest or close, bad entities, malformed declarations. |
| YAML | libyaml, the reference implementation | Real parse errors — unclosed flow collections, bad anchors and aliases, tab indentation, invalid tags — plus duplicate mapping keys, and every document in a multi-document file. |
| CSV / TSV | RFC 4180 parser | Unterminated quotes as errors; rows whose field count disagrees with the table as warnings. |
Errors and warnings are different things
An error means the document does not parse. A warning means it parses but something is probably wrong — a CSV row with an extra field is the usual case. A file with only warnings still counts as valid, because it is.
Three things it deliberately does not claim
XML is checked for well-formedness, not validity. No DTD or schema is applied, so the app says “Well-formed” rather than “Valid” — the stronger word would promise a check nobody ran.
There is no JSON Schema validation. Checking a document against a schema is a different feature, and this is not it.
SQL, HTML and Markdown have no validator. SQL validity depends on the dialect and would be guesswork; browsers accept nearly any HTML; Markdown has no meaningful notion of invalid. A validator that is confidently wrong is worse than none.
Limits
- Documents above 5 MB are not parsed; the chip says so rather than freezing.
- At most 200 issues are listed, with a count of how many were left out.
- In a CSV containing any double-quote character, warnings carry no line number and are not clickable. A quoted value may span lines, so a row cannot be mapped to a line without re-implementing the parser — and no jump is better than a wrong one.
JSON
Format and Minify are free and unlimited. Validation reports the line and column of the first problem instead of a bare "invalid JSON".
2
3
4
5
{
"service": "orders-api",
"retries": 3,
"hosts": ["db-01", "db-02"]
}
The tree is view-only. Selecting a node highlights the matching range in the editor; it never rewrites it.
- Paste JSON into a tab.
- Press ⌥⌘J, or click Tree in the action bar. Pro
- Expand what you need. Copy Path gives you $.hosts[1]; Copy Value gives you the value alone.
JSON Lines files are recognized separately: each line is validated on its own, and the entry number of the first bad line is reported.
XML, YAML, SQL and CSV
- XML — pretty-print with a chosen indent, minify, and well-formedness checking that names the offending element.
- YAML — structural validation, including the indentation mistakes that produce valid-but-wrong documents.
- SQL — keyword-aware formatting that breaks clauses onto their own lines without reordering anything.
- CSV and TSV — open the table view to sort by any column, with quoted fields and embedded newlines handled correctly. Pro
| host ▲ | port | tls | latency_ms |
|---|---|---|---|
| db-01.internal | 1521 | true | 12 |
| db-02.internal | 1521 | true | 19 |
| db-03.internal | 1522 | false | 7 |
| cache-01.internal | 6379 | false | 2 |
Sorting the table never rewrites the file. Close the panel and your text is byte-for-byte what it was.
Converting between formats Pro
Tools ▸ Convert to… reads the current document and writes the result into a new tab. JSON, YAML, XML, CSV and TSV convert to each other. The tab you started from is never rewritten, never reformatted and never closed, so a conversion cannot cost you the original.
2
3
4
5
service: orders-api retries: 3 hosts: - db-01 - db-02
Nothing is dropped quietly
Formats do not carry the same things. XML has attributes; JSON does not. YAML has comments and anchors; CSV has neither, and no nesting at all. Rather than silently discarding what will not fit, every conversion reports it in two kinds of note:
- Convention — the output is faithful, and this explains a decision the converter made, such as how an XML attribute was named.
- Lossy — the output is usable, but something in the source is not represented, and converting back will not return the original.
A conversion that cannot be done at all does not produce a half-finished tab. It refuses, and says why — a document too large for the limit, or a structure the target format has no way to express.
Schema Pro
Tools ▸ Schema… does two jobs. Validate checks the current document against a JSON Schema you supply. Infer reads the document and writes a schema that describes it.
2
3
4
5
{
"service": "orders-api",
"retries": "3", "tls": true
}
- Open Tools ▸ Schema… on the document you want to check.
- Choose where the schema comes from: the clipboard, or a file.
- Press Validate. Each violation names the keyword that failed and the path in your document that failed it.
- Switch to Infer and press Infer Schema to go the other way. Open in New Tab or Copy takes the result with you.
An inferred schema describes what is there
Inference reads one document, so it can only describe the shape of that document — not every shape that would be valid. A field that happens to be absent from your sample is not marked optional, and a field that happens to hold null is not widened to allow anything. Treat the result as a first draft to edit, which is why it opens in a tab rather than being applied to anything.
Generate types Pro
Tools ▸ Types… reads the shape of the current document and writes type declarations for it in TypeScript, Swift, Python, Java, C# or Go. It is the fastest way to stop hand-typing a model from an API response you already have in front of you.
2
3
4
5
{
"service": "orders-api",
"retries": 3,
"hosts": ["db-01"]
}
struct Payload: Codable { let service: String let retries: Int let hosts: [String] }
Nested objects become nested types, arrays become the element type, and a name is only ever given to something that needs one. Generation is deterministic: the same document and the same settings produce byte-identical output every time, so the result can go into a repository without churning on every regeneration.
JWT, Base64, URL and timestamps
JWT
Paste a token and Unfiled splits it into header, payload and signature, decodes the first two locally, and renders the claims as JSON. Registered time claims — exp, iat, nbf — are shown as readable dates alongside their raw values.
Signatures are decoded, never verified
Verifying a signature needs the signing key, which Unfiled does not have and will not ask you for. The signature segment is shown as-is and is never described as valid. Nothing about the token leaves your Mac.
Base64
Encode and decode, with standard and URL-safe alphabets and optional padding. Binary results that are not valid text are reported as such rather than being pasted back as replacement characters.
URL
Percent-encode and decode are free. Pro adds a component breakdown — scheme, host, port, path, fragment — and a table of query parameters, each of which can be copied on its own.
Timestamps
Unix seconds and milliseconds convert to and from ISO 8601 in your local zone or UTC. Ten-digit values are read as seconds and thirteen-digit values as milliseconds, so a number is never silently interpreted a thousand times off.
List tools
When a tab is line-oriented, the action bar offers the operations you would otherwise reach for a shell to do.
Each one is a single undo step. Natural sort orders item2 before item10, which is almost always what you meant.
Extract Pro
Tools ▸ Extract… pulls the useful strings out of text that has no structure worth parsing — a stack trace, an email thread, a wall of console output. It reads characters rather than structure, so it works on any tab, including one Unfiled has not detected as anything.
12
13
14
POST https://api.example.com/v2/orders from 10.0.4.19 req 7c9e6679-7425-40de retry -> https://api.example.com/v2/orders from 10.0.4.22
Nine categories are recognized: URLs, IPv4 and IPv6 addresses, email addresses, UUIDs, embedded JSON objects and arrays, file paths and timestamps. Turn on only the ones you want, or all of them at once. Unique collapses repeats, which is usually what you want when the same URL appears forty times in a retry loop.
Clicking a match selects it in the editor. Open in New Tab writes the whole list into a tab of its own. The document you extracted from is only ever read.
Regex workbench Pro
Tools ▸ Regex Workbench… runs a pattern over the current tab and shows you what it matches while you type it. Like Extract, it reads characters rather than structure, so it is available on any tab with text in it.
4
5
6
2026-09-08 ORA-28860: Fatal SSL error 2026-09-08 ORA-12514: Listener refused 2026-09-09 ORA-28860: Fatal SSL error 2026-09-09 INFO Listener started
Three switches, and what they mean
- Aa — case sensitivity, the same switch the find bar uses.
- ^$ — anchors match at every line boundary rather than only at the start and end of the whole document.
- . — the dot also matches a line break, so a pattern can span lines.
The run is debounced and happens off the main thread, because a pattern that is half typed usually matches far too much. Extract lifts one capture group — or the whole match — into a new tab, one per line. Replace is only offered while the result still describes the document in front of you: edit the text and the preview is withdrawn rather than applied to a document it no longer matches.
Log workbench Pro
Paste or open a log and press ⌥⌘L. Unfiled parses each line into a timestamp, a severity and a message, attaches Java and Python stack traces to the entry they belong to, and gives you a filtered view over the whole thing.
Colour alone is free — a log is already colour-coded in the editor. What the workbench adds is everything you cannot do by reading: filtering by severity, collapsing repeats, hiding timestamps, and highlight terms across the whole file.
- Open the log in a tab and press ⌥⌘L.
- Click severity chips to include or exclude levels. They are independent — you can watch Warn and Fatal and nothing else.
- Turn on Collapse duplicates to fold consecutive identical messages into one row with a count.
- Type into Highlight to mark every occurrence of a request ID, an error code or a service name.
- Press ⌥⌘L again to go back to the raw text, which has not changed.
Formats recognized include ISO 8601 timestamps, syslog, Apache and nginx access logs, Java and Python application logs, and bare LEVEL: message lines. Anything unparsed is shown verbatim rather than dropped.
Follow a file Pro
Tools ▸ Follow watches a file and appends new lines as they are written — tail -f with the log workbench still on top of it. Point it at a file with Choose File…, or at the file the current tab was opened from.
- Open Tools ▸ Follow. If the tab was opened from a file, that file is already offered as the target.
- Choose Start at End to watch only what happens from now, or Start at Beginning to read the file in first.
- Leave Follow the end on to stay pinned to the newest line; turn it off to read back through what has arrived without being dragged forward.
- Pause holds the view still while the file keeps growing. Resume catches up. Stop ends the session and releases the file.
Rotation, truncation and disappearance
Log files do not simply grow. They get rotated out from under you, truncated in place, or deleted and recreated. Follow handles each explicitly rather than showing you a corrupted mixture: it notices the file it is reading is no longer the file at that path, starts again from the beginning of the new one, and says so. A path that is not there yet is waited for and retried rather than treated as an error, so you can start following before the process that writes it has started.
It never writes
Follow only ever reads. It does not create, move, truncate or write to the file, and it only ever reads the bytes between where it got to and where the file now ends — it does not re-read from the beginning when a line is appended. Very long sessions drop the oldest lines from the view rather than growing without limit, and say how many were dropped; the file itself is untouched and still has them.
Compare
⌥⌘D puts the current tab on the left and lets you choose the right side: another tab, a file, or the clipboard. Neither side is ever modified.
- Ignore whitespace and Ignore case are independent toggles and re-run the comparison immediately.
- ⌘' and ⇧⌘' jump to the next and previous difference.
- Added, removed and changed lines are coloured differently, and the summary counts each kind separately.
Compare structurally Pro
Ordinary Compare works on lines, which is the right answer for prose, code and config. It is the wrong answer for two JSON documents that a formatter has been over: reindent one and every line differs, though nothing about the data changed.
Tools ▸ Compare Structurally… compares the data. Key order, indentation and whitespace are not differences. What is left is what actually changed.
2
3
4
5
{
"retries": 5,
"service": "orders-api",
"region": "eu-west-1"
}
The other side can be another tab, a file, or the clipboard — the same three choices line-based Compare offers. Each change is reported at its path, so $.hosts[1] tells you where to look without counting lines, and Copy Path puts it on the clipboard. Neither document is modified.
Arrays, and what counts as the same item
Two arrays are hard to compare when something has been inserted near the front: by position, everything after it has moved and every item looks changed. Match array items by id pairs items by an identity key instead, so an object that kept its id is recognized as the same item that moved, and only the fields that really changed are reported.
Global search
⇧⌘F searches every open tab at once — the thing you need when you know you pasted that connection string somewhere last Tuesday.
Results are grouped by tab with a count each. Select one and Unfiled switches to that tab with the match selected. Searching never modifies anything, and results update as you keep typing.
Pinned tabs and groups
A pinned tab moves to the left of the tab strip, shows a pin glyph, and cannot be closed by accident with ⌘W. It is for the scratch tab you actually live in.
A group gathers related tabs behind a named chip. Every group has a colour, and the tabs in it are underlined in that colour — so you can see what belongs to what without opening anything.
A group's colour is picked for you from nine, always one no other group is using, so two groups on screen never look alike. Change it whenever you like from the chip's context menu.
Putting tabs in a group
Drag, or use the menu — both do the same thing.
- Drop a tab on a chip and it joins that group, wherever in the strip it came from.
- Drop a tab between two tabs of the same group and it joins them. Drop it at either edge of a group and it stays out. That one rule lets a single drag reorder inside a group and pull a tab out of one, without a modifier key.
- Drag the chip to move the whole group somewhere else. Its tabs go with it, in order.
- Or right-click a tab: Move to Group, New Group…, Remove from Group.
A group is always one unbroken run. Joining a group moves the tab next to that group's other tabs; leaving one lands it immediately after the run it left, where you last saw it.
Collapsing
Click a chip to collapse the group, and click it again to open it. A collapsed group keeps its place in the strip rather than moving to the end, and it hides all of its tabs — including the one you were looking at, which is why collapsing moves you to the nearest tab outside the group first. A group holding every open tab will not collapse; there would be nothing left in the row.
Two rules worth knowing
A tab is either pinned or in a group, never both — pinned tabs are drawn ahead of everything, so a pinned member would split its own group in half. Pinning a grouped tab takes it out of the group, and adding a pinned tab to a group unpins it.
Taking the last tab out of a group ends the group; there is nothing left to name. Closing the last tab does not, because a closed tab keeps its membership and ⇧⌘T puts it back where it was.
- Groups can be renamed, recoloured and deleted from the chip's context menu; deleting one leaves its tabs open and ungrouped.
- Pins, groups, colours and collapsed state all survive a relaunch.
- Pins and groups are ordering metadata only — they never change a tab's content.
Revision history Pro
Unfiled periodically snapshots each tab, so you can look at what a tab said an hour ago even after undo history is long gone. Open it from the tab's context menu.
- Pick a revision from the list to see it beside the current text, using the same diff view as Compare.
- Restore replaces the tab's content — as one undo step, so ⌘Z takes you straight back.
- Copy takes the old version to the clipboard without touching the tab at all.
What is not snapshotted
Snapshots are skipped when a tab is unchanged since the last one, when one was taken very recently, and when the content is above the snapshot size limit — so a large log does not quietly grow your database.
Custom actions Pro
Build your own entry in the action bar out of the transformations Unfiled already ships: a find-and-replace, a regular expression, a case change, a trim, or a chain of several applied in order.
- Open Settings → Actions and add one.
- Choose which detected formats it should appear for, so it stays out of the way the rest of the time.
- Give it a name and, if you like, a keyboard shortcut.
By design, no shell
Custom actions cannot run scripts, shell commands or arbitrary code, and they cannot reach the network. They compose the transformations already built into the app — which is what keeps "I pasted a config from a colleague" from ever being a security question.
Keyboard shortcuts
Tabs and files
Editing
Tools
Tools menu only
View
Privacy and your data
Unfiled has no networking code that touches your content — the only thing it sends is a license key, an identifier for the installation and your Mac's model, when you activate Pro. There is no account, no sync and no cloud, and every tool described on this page runs entirely on your Mac.
- Tabs live in a local SQLite database in Application Support. Back it up like any other file.
- Diagnostics record operation identifiers and error categories only — never text, file paths, URLs or tokens.
- JWTs, Base64 payloads and logs are decoded in-process and never transmitted.
Frequently asked
Where does Unfiled store my tabs?
In a local SQLite database inside Application Support. Nothing is uploaded, and you can back the file up like any other.
Do I have to save my tabs?
No. Every keystroke is written to the local recovery database automatically. Saving to a real file with ⌘S is optional, for when a scratch tab becomes something you want on disk.
Does Unfiled modify my text when it analyses it?
No. The log workbench, compare, the JSON tree and the CSV table are all view-only. Transformations such as Format or Minify do change the text, and each is exactly one undo step.
Does Unfiled verify JWT signatures?
No. It decodes the header and payload locally so you can read the claims, and it never claims a signature is valid, because verifying one would require the signing key.
What happens to my work if I quit without saving?
Nothing is lost. Unfiled never shows a save prompt on quit; every tab reopens in the same order with the same active tab and caret position.
How many tabs can I keep open?
There is no cap. Large individual documents switch into a mode that backs off expensive work so typing stays instant.
Can I try Pro features before buying?
Yes. There is a 14-day trial of everything in Pro, and it starts when you start it — from Settings, when you are ready to use it. Installing Unfiled does not begin a countdown, and closing the app does not pause one. Pro actions are also always visible and always labelled, so you can see what you would get for the text in front of you before you decide whether to start the trial at all.
Ready to stop naming files?
Free forever, and Pro is one payment.
Unfiled