Textulon is the one-purpose macOS app at the end of the
acquire → transform → return text pipeline.
It does not select text on its own. Some other tool — a Stream Deck
button, a robo keyboard shortcut, a Raycast script — supplies the input.
Textulon shows it, lets you reshape it, and hands the result back via the clipboard
or stdout.
That separation means the same transform engine works whether the front-end is a Stream Deck key, an editor menu, or just a piped CLI command.
{grab}{insert before …}{paste}), Textulon is for cases that need a glance,
a tweak, or a preview before paste-back.
Textulon is distributed as a signed macOS app. Install
Textulon.app in /Applications so it is available from Finder,
Stream Deck actions, and command-line aliases.
Three ways to put text in front of you:
# From a file
Textulon --input-file ~/notes/messy.json --mode json
# From a pipe
pbpaste | Textulon --input-stdin
# From within the UI: ⌘O opens a file picker
# (Textulon will ask before discarding non-empty editor contents)
Shell alias. The repo includes
install_textulon_alias.sh, which appends an
alias tx='…/Textulon' line to ~/.zprofile (zsh) or
~/.bash_profile (bash). Run it once after Textulon.app is in
/Applications — it's idempotent, and falls back to Spotlight if
Textulon lives elsewhere. After that:
tx ~/some-file.json # autodetect mode by extension
cat foo.xml | tx --input-stdin # pipe stdin
tx --help # full CLI surface
$ Textulon --version
Textulon v0.1.0.0 (built 2026-05-14)
Built against robo v0.1.1.125 (built 2026-02-04)
Copyright © 2025-2026 RoboMac
Textulon reports its signed-app build and the RoboCore version it was linked against.
The Mode dropdown determines how the editor and preview pane behave. In Auto, Textulon inspects the editor contents on every change and rotates the preview into the right shape.
| Mode | Preview pane | What it’s for |
|---|---|---|
| Auto | Inferred | Pasted-in text of unknown shape. |
| Plain | None | Just the editor; transforms still available. |
| JSON | Tree (collapsible value tree), Query (JSONPath expression → matching nodes), or Pretty (via json.beautify) | Validate & reformat, click through a parsed structure, or jump to nodes via path expressions. The status row shows the selected node's JSONPath (e.g. $.documents[0].title). |
| XML | Tree (element / attribute / text nodes with positional indexes), Query (XPath expression → matching nodes), or Pretty (via xml.beautify) | Validate well-formed XML, navigate the parsed tree, or query with XPath. The status row shows positional XPath (e.g. /catalog/book[2]/title). |
| Markdown | Rendered Markdown (HTML in a sandboxed WKWebView) with inline Mermaid fence support | Source on top, rendered preview below. Fenced mermaid / mmd blocks render as diagrams inside the Markdown document. |
| Mermaid | Rendered Mermaid diagram (flowchart, sequence, ER, etc) | Preview diagrams from .mmd or .mermaid files. |
| SVG | Rendered SVG preview with transparent-background controls | Auto mode recognises <svg. The preview bar offers System/Dark/Light backgrounds and a green 1 px frame around the SVG canvas. |
| HTML | Rendered HTML in WKWebView | Source on top; rendered preview below. Network is blocked. |
| JavaScript / TypeScript / Shell / YAML / TOML / INI / Python / Go / Swift | Source syntax colouring | Lightweight source editing and inspection; Shell mode also provides rc/profile snippets. |
| RoboKeys | Scratch pane that receives live robo keys output. | Test key chains before binding them to a Stream Deck button. |
| ASCII | Live string-extraction results with inline filters. | Extract readable ASCII / UTF-8 / UTF-16 BE strings from binary noise. |
| Hex (toggle) | Byte grid above mode-native preview | Inspect/edit bytes for any file. .bin, .dat, .raw open with Hex view on by default. |
<svg), code/settings files, RoboKeys-looking
text, and Parseo-recognised binaries.
The source pane is an NSTextView-backed editor with Textulon-specific controls
for transforms, snippets, matching, and session restore.
| Feature | Details |
|---|---|
| Tabs | The tab bar appears when more than one document is open. Use ⌃Tab and ⌃⇧Tab to switch documents. |
| File breadcrumb | Click the final file name to copy the full path. Click a folder segment for Open File, Reveal in Finder, and path-copy options including home-relative and current-directory-relative paths when available. |
| Session restore | Tabs marked Keep restore after quit. Stream Deck-originated tabs default to Ephemeral and are not saved. Memory buffers marked Keep are stored as compressed session data. |
| Find / Replace | ⌘F opens Textulon's source find panel with literal or regex search, case and whole-word options, context rows, jump, replace current, and replace all. |
| Word wrap / line numbers | Use the source status-row switches or ⌘⌥W / ⌘⌥L. |
| Current line | The cursor line can be highlighted with a configurable background from Settings. |
| Rectangular selection | ⌥⇧ drag selects a source rectangle. Textulon-to-Textulon copy/cut/paste preserves columns by using a private pasteboard payload plus normal plain text. |
| Match actions | ⌘M jumps to the mate; ⇧⌘M selects the block. Matching covers brackets, XML/HTML/SVG tags, Markdown heading sections, Mermaid subgraph/end, and settings sections, while skipping common strings/comments lexically. |
| Undo history | ⌘⌥Z shows Textulon's visible edit history. Typing groups close after about four seconds of inactivity; transforms, snippets, paste, cut/delete, and line edits become separate records. |
| Snippets and colour | Markdown/HTML toolbar buttons wrap selected text. SVG snippets include document scaffolds, shapes, and transform stubs; Shell snippets cover common rc/profile patterns. The shared colour picker inserts colour text in HTML, Mermaid, and SVG. |
| Image data URIs | HTML, Markdown, and Mermaid modes can import an image file as Base64 data:image/... source text. Select an HTML image tag, Markdown image, or selected URI text and choose Export Selected Data URI Image to write it back to a file. Mermaid support is source-level because diagram rendering of data-URI images depends on Mermaid syntax and settings. |
JSON and XML modes are not just pretty-printers. Textulon parses the source into a tree whose rows map back to source ranges. Click a node to select the matching text in the editor, or use the Query tab to run a JSONPath / XPath expression.
| Feature | Details |
|---|---|
| Tree | Collapsible JSON objects/arrays and XML elements/text nodes. Parse errors show the failing offset. |
| Query | JSONPath supports exact paths, quoted keys, array indexes, wildcards, and descendant searches. XPath supports absolute paths, positional predicates, attribute equality, and namespace-prefixed names. |
| Path row | The source header shows a clickable Path: chain. Click Path: or press ⌘⌥F to jump by expression; click a segment to jump to that ancestor. |
| Titlebar shortcut | The fx glyph opens the same JSONPath / XPath prompt without leaving the mouse on the editor. |
Textulon can export rendered views as standalone HTML. The commands live in the File menu and are intentionally limited to modes where the rendered output adds something beyond saving source text.
| Command | Use | Output |
|---|---|---|
| Export Rendered HTML (SVG)… | Preferred for Markdown and Mermaid portability. | Textulon renders the preview, waits for Mermaid, strips renderer scripts, and saves HTML with Mermaid diagrams embedded as inline SVG. |
| Export Rendered HTML (PNG Images)… | Compatibility path for apps that do not import inline SVG reliably, such as Microsoft Word's HTML importer. | Rendered Mermaid SVGs are rasterized to 3x PNG data-URI images while keeping their normal display size. |
| JSON / XML tree export | Share a navigable structured view instead of just pretty source. | A standalone collapsible HTML tree with Expand All / Collapse All controls. |
| Export Pageless PDF… | Save rendered output without artificial page cuts. | One continuous PDF page for Markdown, Mermaid, HTML, JSON tree, or XML tree output. Textulon asks for a remembered export width for wide Mermaid diagrams before saving. |
Textulon does not export Pretty JSON/XML as HTML; use the
json.beautify or xml.beautify transforms when you want
formatted source text.
Hex view is a source-pane toggle, not a mode. Turn it on with ⌘H or the header switch to inspect bytes while the Render pane stays mode-native. A JSON file can show raw bytes on top and the JSON tree underneath.
| Hex capability | Details |
|---|---|
| Byte grid | 8-digit address column, grouped hex bytes, printable ASCII column, and 0x10 / 0x20 / 0x30 / 0x40 byte row widths. |
| Editing | ⇧⌘E edits a byte at an offset, ⌃⌥I inserts a 0x00 byte, and ⌃⌥⌫ deletes the selected byte. |
| Find | ⌘F accepts addresses (0x100), hex patterns (48 65 6C), quoted text, or plain UTF-8 text. |
| Binary files | .bin, .dat, and .raw open as bytes. Files above 32 MiB warn before loading; files above 256 MiB are refused. |
| Saving | When Hex view is active, Save writes bytes verbatim instead of round-tripping through UTF-8. |
Textulon embeds Parseo to inspect binary file metadata — MP3/ID3 tags, MP4 atoms, PNG chunks, PDF info, MPEG-TS packets, OneNote sections, and other bundled formats. Open a supported file from Finder or ⌘O; Parseo auto-detects the format when possible.
.one) shows an HTML reader view instead.
Parseo is a peer checkout next to mac-a-tron; from Textulon's package
directory the dependency path is ../../Parseo. Its specs are compiled into
the Textulon binary at build time.
ASCII mode runs RoboCore's string extractor against the current source. For binary
files it scans the loaded bytes; for normal text it scans the editor's UTF-8 bytes.
The same engine powers the ascii transform, the Stream Deck action, and
the {ascii} robo keys token.
| Control | Use |
|---|---|
| Min length | Drop strings shorter than the chosen length. Default is 6. |
| Alpha % | Require a percentage of letters, digits, and common punctuation. Set to 0 to disable. |
| UTF-8 / UTF-16 | Accept multi-byte UTF-8 and ASCII-range UTF-16 BE pairs. |
| Offsets | Prefix each result with the starting byte offset. |
| Suppress / Ignore / Search | Strip matching tokens, reject chosen characters as ASCII, or keep only strings containing a case-insensitive search term. |
Every transform is implemented once, in RoboCore’s TransformEngine, and exposed via the
shared TransformRegistry. The Transform dropdown is ranked: in
Auto mode, the most applicable transform is offered first.
| id | What it does |
|---|---|
rot13 | ROT-13 — reciprocal letter substitution. |
rot18 | ROT-13 for letters + ROT-5 for digits (reciprocal). |
json.beautify | JSON beautify (sorted keys). |
json.minify | JSON minify. |
xml.beautify | XML beautify via XMLDocument. |
html.escape | Encode &, <, >, ", '. |
html.unescape | Decode named & numeric HTML entities. |
url.encode | RFC 3986 percent-encoding. |
url.decode | Percent-decoding. |
base64.encode | Base64 encode (UTF-8 input). |
base64.decode | Base64 decode → UTF-8. |
lines.trim | Trim each line. |
lines.sort | Sort lines (localized standard compare). |
lines.unique | Unique lines, preserving first-seen order. |
lines.sortUnique | Sort + unique. |
text.uppercase | Uppercase the whole text. |
text.lowercase | Lowercase the whole text. |
text.trim | Trim leading/trailing whitespace from the whole text. |
url.removeQuery | Strip everything from ? onward (URL host+path). |
ascii | Extract ASCII / UTF-8 / UTF-16 BE strings from binary noise. |
auto | Detect the content kind and apply the best-fit transform. |
robo keys and the Keyboard Shortcut action by its {token} form —
for example {json beautify}, {url decode}, {uppercase},
{remove query}, and {ascii}. The complete list lives in cmds.txt next to
Textulon, and the alias map is the single source of truth in
TransformRegistry.keysCommandAliases.
Textulon accepts a small CLI surface so that any front-end can launch it. The simplest form:
Textulon [<path> | --file <path> | -i <path>] # autodetect mode by extension+contents
[--input-file <path> | --input-stdin] # plain text in, --mode controls mode
[--payload-file <path>| --payload-stdin] # full JSON payload (§5)
[--mode auto|plain|json|xml|markdown|mermaid|svg|html|robokeys|hex|ascii]
[--output-behavior copyOnly|pasteBack|viewOnly]
[--preferred-transform <id>]
[--on-top yes|no]
[--version | -v]
[--help | -h]
Input vs. payload. The --input-* flags pass just the text;
everything else (mode, output behaviour, etc.) comes from other CLI flags.
The --payload-* flags pass an entire §5 JSON document —
with sourceText, mode, outputBehavior,
originatingAppBundleId, originFrame, etc. — which is what an
automation front-end (Stream Deck, Raycast, robo) sends when it wants to carry origin metadata.
A bare <path>, --file, and -i are the friendly forms:
load a file and let Textulon pick the mode.
In the running app:
| Shortcut | Action |
|---|---|
| ⌘O | Open File — asks before discarding unsaved changes. |
| ⌘S | Save back to the originating file (Save As if none). |
| ⇧⌘S | Save As — extension defaults from the current Mode. |
| ⌘N | New untitled tab. |
| ⌃Tab / ⌃⇧Tab | Switch to next / previous document tab. |
| ⌘P / ⇧⌘P | Print rendered/default view / Print Source. The standard macOS print dialog includes PDF export. |
| ⌘W | In the editor: dismiss the find panel or Query tab first; close the window only when no overlay is up. In Help/Settings: close that window. |
| Esc | Closes the Help window. In the editor: dismisses the find panel or Query tab; otherwise no-op. |
| ⌘Q | Quit Textulon, regardless of which overlay is up. |
| ⌘F | Find / Replace. In source text: literal or regex search with context rows, jump, replace current, and replace all. In Hex view: byte-aware find. |
| ⌘G | Find next in the active find target. |
| ⌘J | Jump to line. |
| ⌘⌥F | JSONPath / XPath jump prompt in JSON or XML mode. |
| ⌘M / ⇧⌘M | Jump to match / select matching block. |
| ⌘⏎ | Apply Transform — operates on the selection if any; in RoboKeys mode, runs the keys command into the scratch pane. |
| ⌘B / ⌘I | In Markdown and HTML modes, insert Bold / Italic at the cursor (or wrap the selection). |
| ⌘] / ⌘[ | Indent / outdent selected or current source lines. |
| ⌘⌥Z | Show visible undo history and roll back to a selected record. |
| ⌥⇧ drag | Rectangular source selection; copy/cut/paste preserves columns inside Textulon. |
| ⌘? / F1 / ⇧⌘? | Open the Help window. |
| ⌥⌘P | Toggle Stay on Top (Pin). |
| ⌘⌥M | Focus the Mode picker (then Space / arrows to change). |
| ⌘⌥T | Focus the Transform picker (then Space / arrows to change). |
| ⌘⌥I | Open the Snippets menu (Markdown, HTML, and other snippet-capable modes only); then ↓ ↩ to insert, or ↓ → ↩ for a sub-group item. |
| ⌘H / ⌘T | Enter Hex view / return to Text view. |
| ⇧⌘E | Hex: Edit Byte at Offset. |
| ⌃⌥I / ⌃⌥⌫ | Hex: Insert Byte / Delete Byte. |
| ⇧⌘R / ⇧⌘0 | Toggle Hide Raw / force-show the source pane. |
| ⌘⌥W / ⌘⌥L | Toggle source word wrap / line numbers. |
| ⌘+ / ⌘= / ⌘- / ⌘0 | Zoom in, zoom out, or reset to actual size. |
A richer JSON payload mirrors §5 of the original spec:
{
"sourceText": "<p>Hi</p>",
"mode": "html",
"preferredTransform": "html.escape",
"outputBehavior": "copyOnly",
"originatingAppBundleId": "com.apple.Safari",
"originatingWindowTitle": "Some tab",
"originFrame": { "x": 100, "y": 200, "width": 500, "height": 30 }
}
On Copy or Paste Back, the final text is placed on the system pasteboard and printed to stdout (newline-terminated). On Cancel, Textulon exits with code 1 and leaves the clipboard alone.
The Textulon/Samples/ directory ships files designed to exercise transforms,
preview modes, and structured parsing. The same files drive TextulonTests,
so transform regressions fail the suite.
| File | Try |
|---|---|
messy.json | JSON Beautify → JSON Minify (round-trips). |
compact.xml | XML Beautify — adds line breaks and indentation. |
example.html | See the rendered HTML preview; HTML Escape ↔ HTML Unescape. |
example.md | See the rendered Markdown preview. |
AdSignaler-Spec.md | Markdown with inline Mermaid diagrams; try rendered HTML export as SVG or PNG images. |
example.mmd | See a Mermaid flowchart diagram rendered in the preview. |
example-sequence.mmd | See a Mermaid sequence diagram rendered in the preview. |
example-er.mmd | See a Mermaid ER diagram rendered in the preview. |
*.svg | Use SVG mode to inspect transparent backgrounds, frame the canvas, insert SVG snippets, and tune transforms. |
encoded.url | URL Decode reveals query params and JSON. |
secret.base64 | Base64 Decode reveals plaintext. |
unsorted.txt | Sort Lines / Unique Lines / Sort Unique Lines. |
rot13.txt | ROT-13 — apply twice to verify round-trip. |
complex.json | Explore the JSON tree, path row, JSONPath query, and collapsible HTML tree export. |
complex.xml | Explore the XML tree, XPath query, positional paths, and collapsible HTML tree export. |
legacy-word-binary.doc | Synthetic legacy-DOC-like binary fixture; opens in Hex by extension and provides readable strings for ASCII extraction. |
broken.json / broken.xml | Confirm parser error display and source offsets. |
Drop captures into docs/img/ with the filenames below to fill these slots.
Textulon is designed to handle secrets safely:
WKWebView blocks all navigation after the initial load; clicked links open in your default browser via NSWorkspace, never inside the preview.Textulon depends on the same RoboCore library as Mac-A-Tron and SnapMatic. Three useful pairings:
The Transform Text action can paste back, copy to clipboard, or open the acquired text in Textulon. It has separate single-click and double-click transform blocks; when double-click is enabled, the key graphic shows the primary title at top and the double-click title at bottom.
# Conceptually:
robo keys "{cmd+c}{pause 100}"
pbpaste | Textulon --input-stdin --mode auto --output-behavior copyOnly
curl -s https://example.org/api/notes | Textulon --input-stdin --mode json
The TransformRegistry in RoboCore can be used directly from any Swift project
that depends on it — Textulon is just one front-end:
import RoboCore
let beautify = TransformRegistry.shared.byId("json.beautify")!
let pretty = try beautify.transform(messy)