Writing Automations
A browser automation is a small .js file that drives a browser tab. This page is the reference
for writing your own — the tranquil API you script against, and the patterns the shipped examples
use. If you haven’t run one yet, start with Running Automations and Your First Automation; this page goes deeper.
Browser automations vs. workflows. This page is about browser automations — scripts you run inside the desktop app against a live page. Tranquil also has workflows:
@tranquil/sdkprograms that run headless on the Engine, on a schedule or webhook. Those are a separate topic — see Engine.
Dev preview — not stable. Tranquil is in early, active development. Everything here — APIs, interfaces, and behavior — is provisional and will change before a stable release, often without notice or backward compatibility. Don’t build anything you depend on against it.
Where automations live
Scripts live as .js files under Automations/browser/ in the guided sample project (open it with File → New Default Window). Any .js file works, though — put yours wherever you like in your
project and run it the same way.
How a script runs
Your script runs in Tranquil (a Node/Atom context), not in the web page. The runner injects five globals into every script:
| Global | What it is |
|---|---|
tranquil | The automation API (tabs, files, notifications, …) — documented below. |
atom | The full Atom/Tranquil API, for anything tranquil doesn’t wrap. |
require | Node require, for built-ins (path, fs, …) and installed modules. |
__dirname | The directory containing the running script (files resolve against it). |
__filename | The running script’s absolute path. |
To touch the page itself — its document and window — you pass a function to tab.evaluate().
That function is serialized and runs inside the page; everything outside it runs in Tranquil:
const tab = await tranquil.getActiveTab(); // in Tranquil
const title = await tab.evaluate(() => { // this callback runs IN THE PAGE
return document.title;
});
tranquil.notify(title); // back in Tranquil Each tab is a real Puppeteer Page, so beyond evaluate() you also get click(), type(), waitForSelector(), goto(), screenshot(), and the rest.
Running: with the .js editor focused (not the browser tab), press Cmd-Shift-R (tranquil-automations:run-in-webview). It runs the whole file, or — if you have text selected —
just the selection, which makes it a quick REPL for experimenting a line at a time.
The tranquil API
Tabs
Every tab method returns a Puppeteer Page.
| Method | Returns | Description |
|---|---|---|
getActiveTab() | Promise<Page> | The most recently viewed browser tab. Throws if none has been visited. |
getTab(urlOrPattern) | Promise<Page> | An open tab matching an exact URL string or RegExp (waits up to 5 s). |
getTabs() | Promise<Page[]> | All open http(s) tabs (excludes Tranquil’s own UI). |
openTab(url) | Promise<Page> | Opens a new visible tab and resolves once it’s loaded (waits up to 10 s). |
openBackgroundTab(url) | Promise<Page> | Loads a page in an off-screen webview (no visible tab) — for scraping or auth. Always closeTab() it when done. |
closeTab(page) | Promise<void> | Closes a visible tab or destroys a background one. |
Notifications
notify(message, level?) — shows a notification. level defaults to 'info'; valid levels are 'info', 'success', 'warning', 'error'.
tranquil.notify('Done!', 'success'); Files
Paths resolve against __dirname (the script’s own directory).
| Method | Returns | Description |
|---|---|---|
writeFile(name, content) | string | Writes the file; returns its absolute path. |
readFile(name) | string | Reads a file as UTF-8. |
openFile(path, options?) | Promise<TextEditor> | Opens a file in the editor. options passes through to atom.workspace.open — most usefully split: 'left' \| 'right' \| 'up' \| 'down'. |
Everything else
| Method | Description |
|---|---|
clipboard.read() / clipboard.write(text) | Tranquil’s clipboard (distinct from the page clipboard inside evaluate). |
getProjectDir() | Absolute path of the first open project root, or ''. |
exec(command) | Runs a shell command (cwd = script dir); resolves with trimmed stdout. |
config.get(key) / config.set(key, value) | Persistent config that survives runs and restarts. Namespace keys (e.g. price-checker.lastPrice) to avoid collisions. |
autoInject.register(urlPattern) / unregister() | Re-run this script automatically whenever a matching URL finishes loading. Run once to register; persists across restarts. |
Example catalog
These ship in Automations/browser/ — open one, focus a browser tab, and run it. Each shows a
different shape.
Read the page — count-elements.js:
// Notify how many of a tag are on the page (edit the selector as needed).
const tab = await tranquil.getActiveTab();
const count = await tab.evaluate(() => document.querySelectorAll('img').length);
tranquil.notify(`This page has ${count} image(s).`, 'info'); Restyle the page — reader-mode.js hides clutter and widens the main column:
// Rough "reader mode": hide common clutter and widen the main column.
const tab = await tranquil.getActiveTab();
await tab.evaluate(() => {
document.querySelectorAll('header, footer, aside, nav, [role="banner"]').forEach((el) => {
el.style.display = 'none';
});
const main = document.querySelector('main, article') || document.body;
main.style.maxWidth = '720px';
main.style.margin = '0 auto';
main.style.fontSize = '18px';
main.style.lineHeight = '1.6';
}); highlight-links.js is the same shape — it outlines every external link.
Toggle something — page-banner.js adds a banner, or removes it if it’s already there
(run it again to toggle):
// Toggle a small "enhanced by Tranquil" banner on the active page.
const tab = await tranquil.getActiveTab();
await tab.evaluate(() => {
const existing = document.getElementById('tranquil-banner');
if (existing) { existing.remove(); return; }
const el = document.createElement('p');
el.id = 'tranquil-banner';
el.textContent = 'This page was enhanced by a Tranquil automation.';
el.style.cssText =
'margin:1rem 0;padding:0.75rem 1rem;background:#f0f4ff;border:1px solid #c7d8ff;' +
'border-radius:6px;font:14px Inter,sans-serif;color:#333';
(document.querySelector('main') || document.body).prepend(el);
}); Scrape to a file — page-info.js reads stats from the page, writes them, and opens the result
in the editor (writeFile + openFile):
// Scrape a few page stats and open them as a text file in the editor.
const tab = await tranquil.getActiveTab();
const stats = await tab.evaluate(() => {
const links = document.querySelectorAll('a').length;
const images = document.querySelectorAll('img').length;
const headings = document.querySelectorAll('h1,h2,h3').length;
return `${document.title}\n\nLinks: ${links} | Images: ${images} | Headings: ${headings}`;
});
await tranquil.openFile(tranquil.writeFile('page-info.txt', stats)); Patterns beyond one tab
Background scraping — pull data without a visible tab, then present it:
const bg = await tranquil.openBackgroundTab('https://api.example.com/data'); const data = await bg.evaluate(() => JSON.parse(document.body.innerText)); await tranquil.closeTab(bg);Multiple tabs —
getTabs()for every open tab, orgetTab(/pattern/)to target a specific one.Auto-injection — call
tranquil.autoInject.register(/example\.com/)once, and the script re-runs itself on every matching page load (same as pressingCmd-Shift-R, but on the load event).
Debugging
console.log in the script body prints to the renderer DevTools (the app’s own dev tools). Logs inside tab.evaluate(() => { … }) run in the page, so they appear in that tab’s DevTools
instead. Setup problems (like “no active browser tab”) surface as warning notifications; any other
thrown error shows a red notification titled Script error in <file>.
Save it as a command
Once a script is useful, run “Automations: Register Current File” from the command palette
(Cmd-Shift-P) with it focused. It becomes a permanent palette command named automatically from the
filename — count-paragraphs.js → Automations: Count Paragraphs — that targets the active tab
and persists across restarts, no longer needing the file open. See Your First Automation for the walkthrough.
Next: Engine — the headless, scheduled kind of automation that runs in the cloud.