Nav kit
A kit is a set of commands that Sutram ships ready-made. It gives every app the same command names, so an agent that has found its way around one site can find its way around the next. The kit registers the commands, matches what was typed, writes the errors and offers completion. Your app supplies an adapter: the data, and the one action the kit cannot do itself.
The nav kit gives you four commands:
| Command | Does | Annotations |
|---|---|---|
nav status | Where this page is in the menus | read-only |
nav menu [menu] | The menus, or one menu's links with their path and description | read-only, untrustedContent |
nav search <query> | Links whose title or description contains all the words | read-only, untrustedContent |
nav go <target> | Goes to a link by path, title or breadcrumb, or to any path on the site | navigates or reinitializes |
nav go takes a path only when it leads to this site: it resolves the path the way the browser would and refuses one that ends up elsewhere, such as /\example.com. The adapter's go() gets the resolved path.
The adapter
import { nav } from "@d34dman/sutram/nav";
nav({
// The menus, keyed by name. Called each time a command runs.
menus: () => ({
main: {
label: "Main",
items: [
{ title: "Home", url: "/" },
{
title: "Docs", url: "/docs", description: "How to use the app",
children: [{ title: "Install", url: "/docs/install" }],
},
],
},
footer: { label: "Footer", items: [{ title: "Contact", url: "/contact" }] },
}),
// Goes to a URL. Called after nav go has answered.
go: (url) => location.assign(url),
// Does going load a new page?
reloads: true,
});| Adapter key | Required | What it is |
|---|---|---|
menus() | yes | The menus as { [name]: { label, items } }. Each item has a title and a url, and may have an id, a description and children. It may return a promise. |
go(url) | yes | Goes to the URL. The kit calls it after nav go has delivered its result, so the caller always gets an answer. Return false (or a promise of it, or reject) when the page did not leave, such as when a router handled it in place or the person stayed; Sutram then resumes on this page at once. |
reloads | yes | true if going loads a new page, false for a client-side router. |
here() | no | The current path, such as /admin?tab=2. The default is location.pathname + location.search. |
nav() returns a function that removes the four commands again. It refuses an adapter without one of the required keys, and it registers nothing if a nav command already belongs to another provider.
Only the links the user may open
The kit shows whatever menus() returns. Leave out links that the current user cannot open, the way your app's menus do, so that people and agents see the same site.
Ask, don't cache
The kit calls menus() every time a command runs, and again for completion. Keep it cheap: return data the page already holds. Because the kit never keeps a copy, a menu that changes (after a login, say, or a route change) shows up in the next command without any work on your side.
Full page loads or a router
reloads decides how nav go is marked, so that agents know what happens to the page:
reloads: true:nav gois markednavigates. The page is left, and Sutram starts again on the new one.reloads: false:nav gois markedreinitializes. Your router shows the new route, and then your app callsSutram.initialized()(orregistry.initialized()). Until then, runs are refused with exit 130, so no command acts on the route that was left. Call it even whengo()is asked for the route that is already showing:
const here = () => location.hash.slice(1) || "/";
addEventListener("hashchange", () => { render(); Sutram.initialized(); });
nav({
menus: () => menus,
go: (url) => { if (url === here()) Sutram.initialized(); else location.hash = url; },
reloads: false,
here,
});The example page uses this adapter, with two static menus.
What nav go accepts
In this order:
- A link's path or
id:nav go /admin/content. - A link's title, in any case:
nav go "content types". - A breadcrumb, which may leave out the top of the trail or start with the menu's label or name:
nav go Structure › Menus,nav go Footer › About. - Any path that starts with a single
/, like following a link to it.//hostand full URLs to other sites are refused.
If a title matches links with different paths, nav go fails and lists them, so the caller can pick the breadcrumb.
nav go is not consequential: it does what following a link does. So any same-site URL that changes something when it is opened (a GET logout link without a confirmation form, a "delete" link, a one-click action) can be reached through it without asking, even where the command for it, such as user logout, is consequential. Keep such actions behind a form or a POST, as the web expects. (go() cannot refuse them: it runs after nav go has answered.)
Without a bundler
dist/nav.js is the kit as a file of its own. Load it after dist/sutram.js, which it takes the registry from, and call Sutram.kits.nav():
<script type="module" src="sutram.js"></script>
<script type="module" src="nav.js"></script>
<script type="module">
Sutram.kits.nav({ menus: () => MENUS, go: (url) => location.assign(url), reloads: true });
</script>In Drupal, drupal/sutram ships it as the library sutram/nav, and sutram_demo_nav is the adapter. See Drupal.