← Back to blog

Writing Your First g.* Script

· 5 min read ·
tutorial

You’ve installed Guida, clicked around a few pages, maybe poked at the settings. Now what? This post walks you through writing your first real script — one that navigates to a page, pulls data out of it, and saves it somewhere useful.

No prior scripting experience required. If you’ve written any JavaScript before, you’ll feel right at home. If not, the API is small enough to pick up as we go.

Open the script editor

Press Ctrl+N or go to File > New Script. You’ll get a blank editor tab with IntelliSense already wired up. Type g. and you’ll see the full API surface — 160+ methods organized into namespaces like g.store, g.queue, g.search, and more.

Every script in Guida has the same shape:

async function main() {
// your code here
}

The async keyword matters — most g.* methods are asynchronous because they talk to a live browser. The runtime calls main() for you; just define it and press F5 to run.

Let’s start simple. We’ll go to Hacker News and wait for the page to load:

async function main() {
await g.nav.navigateToUrl("https://news.ycombinator.com");
await g.dom.waitForDomStable();
}

g.nav.navigateToUrl() tells the active browser tab to load a URL. g.dom.waitForDomStable() waits until the DOM stops changing — no more elements appearing, no spinners, no lazy-loaded content still streaming in. Together, they’re the bread and butter of any automation script.

Run it with F5. You’ll see the browser tab navigate to Hacker News. The console pane at the bottom will show a success message.

How g.* methods work

Before we go further, there’s one convention you need to know. Every g.* method returns its result directly and throws on error.

await g.nav.navigateToUrl("https://example.com");

If the operation succeeds, the return value contains the result. If it fails, an exception is thrown. Use try/catch for error handling:

try {
const elements = await g.dom.getElements(".item", ["innerText"]);
g.log("Found " + elements.length + " items");
} catch (e) {
g.log("Couldn't find items: " + e.message);
return;
}

g.log() writes a message to the Console pane.

Extract data from the page

Now let’s do something useful. We’ll grab the titles and links from Hacker News:

async function main() {
await g.nav.navigateToUrl("https://news.ycombinator.com");
await g.dom.waitForDomStable();
const items = await g.dom.getElements(
".titleline > a",
["innerText", "href"]
);
g.log("Found " + items.length + " stories");
for (const item of items) {
g.log(item.innerText);
}
}

g.dom.getElements(selector, attributes) queries the DOM and returns an array of objects, each containing the attributes you asked for. It’s like document.querySelectorAll() but returns structured data instead of DOM nodes.

Run this and you’ll see 30 story titles logged to the console.

Store results in a workspace

Logging is fine for debugging, but what if you want to keep the data? That’s what workspaces and g.store are for.

First, open a workspace: File > Open Workspace, then pick or create a folder. This gives your scripts a home directory and unlocks persistent storage.

Now update the script to save each story:

async function main() {
await g.nav.navigateToUrl("https://news.ycombinator.com");
await g.dom.waitForDomStable();
const items = await g.dom.getElements(
".titleline > a",
["innerText", "href"]
);
for (const item of items) {
const key = item.innerText.substring(0, 80);
g.store.put("stories", key, {
title: item.innerText,
url: item.href,
scrapedAt: new Date().toISOString()
});
}
const count = g.store.count("stories");
g.log("Stored " + count + " stories");
}

g.store.put(collection, key, data) saves a JSON document into a named collection. The key is unique within the collection — if you run the script again, existing stories get updated rather than duplicated. The data is stored in a store.db file in your workspace folder.

After running, open the Store pane to browse your saved stories. You can also query them from another script:

const results = g.store.search("stories", "rust");

Process the queue with workers

What if you want to visit each story’s URL and extract more data? First, enqueue the URLs:

async function main() {
const stories = g.store.list("stories");
for (const doc of stories) {
g.queue.enqueue("to-visit", {
url: doc.data.url,
title: doc.data.title
});
}
const count = g.queue.count("to-visit");
g.log("Queued " + count + " URLs to visit");
}

Now, instead of writing a manual dequeue loop, use workers. Workers open multiple browser tabs and process queue items in parallel — each tab runs your script independently.

Create a per-item script in your workspace at scripts/visit-story.js:

async function main() {
const ctx = g.worker.getContext();
await g.nav.navigateToUrl(ctx.item.data.url);
await g.dom.waitForDomStable();
const title = await g.dom.extractContent("h1");
g.log("Visited: " + (title || ctx.item.data.title));
g.queue.commit(ctx.item.id);
}

g.worker.getContext() gives each worker its current queue item and tab ID. The script navigates, extracts, and calls commit() to mark the item as done. If the script fails before committing, the item goes back into the queue automatically.

Then launch the workers from any script (or the console):

async function main() {
g.workers.start({
queue: "to-visit",
script: "scripts/visit-story.js",
concurrency: 3,
layout: "horizontal"
});
}

That’s it. Guida opens 3 tabs side by side, and each one processes items from the queue independently. The Workers pane shows live progress — items processed, errors, queue depth — and lets you pause or stop the pool without touching code.

What’s next

You’ve now seen the core workflow: navigate, extract, store, queue, workers. From here you can explore:

The script editor has full IntelliSense — type g. anywhere and let autocomplete guide you. Happy scripting.