Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ading2210/sandstone/llms.txt

Use this file to discover all available pages before exploring further.

ProxyFrame is the core class in Sandstone’s host module. It creates a sandboxed <iframe> element, handles navigating to URLs by fetching their HTML through libcurl.js, and communicates with the frame’s JavaScript runtime via an internal RPC channel. Each instance manages its own per-site settings and persisted localStorage namespace. Note that HTTP cookies are stored in the shared network.session (HTTPSession) and are therefore shared across all ProxyFrame instances on the same page.

Import

import * as sandstone from "./dist/sandstone.mjs";
const { ProxyFrame } = sandstone.controller;

Constructor

new ProxyFrame()
Creates a new ProxyFrame instance. Calling the constructor immediately creates an HTMLIFrameElement with the required sandbox attributes, assigns it a unique internal ID, and registers it in the global frame registry. You must append frame.iframe to the DOM before calling navigate_to.

Properties

iframe
HTMLIFrameElement
The underlying sandboxed iframe element. The sandbox attribute is set to "allow-scripts allow-forms allow-modals allow-pointer-lock" and allowFullscreen is true. Append this element to your DOM to display the proxied page.
url
URL | null
The current proxied URL as a URL object, or null before the first navigation. This value is updated at the start of each navigation and again when the final response URL is known (after redirects).
on_navigate
() => void
Callback fired when a navigation begins, before the page finishes loading. Use this to update UI elements such as a URL bar or loading indicator.
on_load
() => void
Callback fired after the page finishes loading and the RPC channel is established. Use this to fetch the favicon, read the final URL, or update other UI state.
on_url_change
() => void
Callback fired when the URL changes without a full page reload — for example, hash fragment changes or same-page history.pushState navigations. frame.url is already updated when this fires.
special_pages
Object<string, string | null>
A map of sandstone:// scheme URLs to raw HTML strings. When you call navigate_to with a URL that matches a key in this map, Sandstone serves the mapped HTML directly instead of making a network request. Set the value to null to reserve a key without providing HTML yet (useful for building a homepage asynchronously).
site_settings
Array<{hostname: RegExp, ...settings}>
An array of per-hostname settings overrides. Each entry must include a hostname property whose value is a RegExp tested against the current page’s hostname. The first matching entry is merged on top of default_settings before the page loads.
default_settings
Object
Default settings applied to every page. Merged with any matching entry from site_settings. Defaults to { allow_js: true }.

Methods

navigate_to
(url: string, form_data?: { enctype: string, body: string }) => Promise<void>
Navigate the frame to the given URL. Sandstone fetches the URL through libcurl.js and renders the HTML inside the sandboxed iframe. If form_data is provided, the request is sent as an HTTP POST with the given Content-Type (enctype) and body — used for proxying HTML form submissions. Throws a TypeError if url is not a valid URL.
get_favicon
() => Promise<string>
Returns the favicon URL of the currently loaded page as a string. The URL may be an absolute https:// URL or a data: URI, depending on what the page declares. Call this inside on_load to update a favicon element.
eval_js
(js: string) => Promise<any>
Evaluate a JavaScript expression in the proxied page’s context via the RPC channel. Returns a promise that resolves with the serialized result. Use this for debugging or for programmatic interaction with the proxied page.

Usage example

import * as sandstone from "./dist/sandstone.mjs";
const { ProxyFrame } = sandstone.controller;

const frame = new ProxyFrame();

// Register a custom sandstone:// home page
frame.special_pages["sandstone://home"] = "<h1>Welcome</h1>";

// Update the URL bar when navigation starts
frame.on_navigate = () => {
  urlBar.value = frame.url.href;
};

// Fetch and display the favicon after the page loads
frame.on_load = async () => {
  urlBar.value = frame.url.href;
  const faviconUrl = await frame.get_favicon();
  faviconImg.src = faviconUrl;
};

// Sync the URL bar on in-page navigation
frame.on_url_change = () => {
  urlBar.value = frame.url.href;
};

// Set the Wisp server and point the iframe at it
sandstone.network.set_websocket("wss://wisp.mercurywork.shop/");

// Append the iframe to the DOM, then navigate
document.getElementById("frame-container").append(frame.iframe);
await frame.navigate_to("https://example.com");

// Evaluate JavaScript in the proxied page
const title = await frame.eval_js("document.title");
console.log("Page title:", title);
Sandstone persists the proxied page’s localStorage to the host page’s localStorage under the key "proxy_local_storage" (exported as sandstone.controller.persist_storage_key). Each origin’s data is stored as a JSON-serialized object nested under that key. You can pre-populate or inspect this value directly in the host page’s localStorage if you need to seed or migrate storage.

Build docs developers (and LLMs) love