sutram docs
All pages

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:

CommandDoesAnnotations
nav statusWhere this page is in the menusread-only
nav menu [menu]The menus, or one menu's links with their path and descriptionread-only, untrustedContent
nav search <query>Links whose title or description contains all the wordsread-only, untrustedContent
nav go <target>Goes to a link by path, title or breadcrumb, or to any path on the sitenavigates 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 keyRequiredWhat it is
menus()yesThe 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)yesGoes 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.
reloadsyestrue if going loads a new page, false for a client-side router.
here()noThe 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.

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 go is marked navigates. The page is left, and Sutram starts again on the new one.
  • reloads: false: nav go is marked reinitializes. Your router shows the new route, and then your app calls Sutram.initialized() (or registry.initialized()). Until then, runs are refused with exit 130, so no command acts on the route that was left. Call it even when go() 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:

  1. A link's path or id: nav go /admin/content.
  2. A link's title, in any case: nav go "content types".
  3. 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.
  4. Any path that starts with a single /, like following a link to it. //host and 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.

This page as MarkdownEdit on GitHub