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.

Sandstone lets you define entirely custom pages that are served from memory rather than the network. You register them on frame.special_pages, an object that maps sandstone:// URL strings to HTML strings. When navigate_to() receives a sandstone:// URL, it looks up the stored HTML and renders it directly inside the sandboxed iframe, skipping libcurl and the Wisp server entirely.

Registering a special page

Assign an object to frame.special_pages where each key is the full sandstone:// URL and each value is either an HTML string or null. Setting a value to null reserves the URL while you build the content asynchronously.
// Reserve the URL before the HTML is ready
main_frame.special_pages = {
  "sandstone://home": null,
};

// Later, once you've built the HTML string, fill it in
main_frame.special_pages["sandstone://home"] = homepageHtml;
In the example application, the homepage HTML is constructed dynamically — the app icon is inlined as a data URI and the version string is injected — before being stored in special_pages:
main_frame.special_pages = {
  "sandstone://home": null,
};

async function create_homepage() {
  let parser = new DOMParser();
  let html = parser.parseFromString(resources["home.html"], "text/html");

  let icon_element = document.querySelector("link[rel='icon']");
  let icon_url = icon_element.href;

  if (!icon_url.startsWith("data:")) {
    let response = await fetch(icon_url);
    let icon_blob = await response.blob();
    icon_url = await new Promise((resolve) => {
      var reader = new FileReader();
      reader.onload = (event) => { resolve(event.target.result); };
      reader.readAsDataURL(icon_blob);
    });
  }

  html.querySelector("link[rel='icon']").href = icon_url;
  html.getElementById("main_img").src = icon_url;
  html.getElementById("sandstone_version").textContent = version_text.textContent;

  let homepage_html = "<!DOCTYPE html>" + html.documentElement.outerHTML;
  main_frame.special_pages["sandstone://home"] = homepage_html;
}

await create_homepage();
Pass the sandstone:// URL directly to navigate_to(). Sandstone detects that special_pages has a string value for that key and renders it without making any network request.
await main_frame.navigate_to("sandstone://home");
You can also set the initial value of your URL input to a sandstone:// address so the frame opens on your custom page when the application first loads:
<input id="url_box" type="text" value="sandstone://home">

How special pages are resolved

Inside navigate_to(), Sandstone checks special_pages before attempting a network fetch:
if (typeof this.special_pages[url] === "string") {
  html = this.special_pages[url];
} else {
  // ...perform a normal network request
}
If the value is null or the key does not exist, Sandstone falls through to the normal fetch path. If the value is a string (including an empty string), that string is used as the page HTML and no request is made.
Special pages are ideal for a custom home or new-tab screen. Because the HTML is served from memory and rendered inside the sandbox, you can embed images as data URIs, inject live data (like a version number or recent history), and fully style the page — all without hosting any additional files.

Build docs developers (and LLMs) love