The web UI
tcm serve has a web UI for the codebase it serves, at /forge/ (and at / on a server with no
--site). It has the project's front page, its code by namespace and definition,
search, its contributions and their review, its history, branches, releases and people. Anyone
can browse a --public server; on one that isn't, it takes a token.
Pages
The front page says what the project is (named for its package, its one top namespace, or its directory) and how to get it (
tcm init && tcm remote add … && tcm pull). It lists what you can use (its packages and their latest releases, its apps, its pages), maps its namespaces with a bar of where their code runs (server, anywhere, browser), and shows its README.A namespace lists the namespaces in it (each with its definition count, its README's first paragraph and a where-it-runs bar), then its definitions grouped by what they're for (apps, types, data, jobs, server, browser and shared code, tests, files), each with its type and the first line of its doc. Its README follows, if it has one.
A definition shows:
- its doc, with
@since,@category,@stabilityand flags like@internalas chips under its name (documentation); - its code, every reference a link (pointing at one shows its type);
- where it runs and why, and what uses it (what a change to it re-hashes);
- beside them: what it uses, the tests that reach it (passing or failing), the history entries that changed it, its other names, and the releases it's in.
Its address is its name:
/forge/code/lib/math/square.- its doc, with
Code and files have line numbers. A line's number links to it (
/forge/code/lib/math/square#L3); shift-clicking another makes a range (#L3-L8). An address naming lines marks them and scrolls to them. A Markdown file (its images linking to their files' pages) or an image shows rendered, with a switch to its text; an address naming lines shows the text.A package's manifest shows the package beside its source: its namespace and releases, its commands, what it exports, the apps, files and end-to-end tests it ships, and what else it installs. Each links to the name it means (a path like
../README.mdto the file under the namespace it's written from).Search is the box in every page's header (⌘K or /). It finds names by their letters, words in docs, and types (
:(xs: T[]) => T), astcm finddoes, with results as you type. Enter opens the chosen result, or the search page if the results aren't in yet.History lists a branch's entries by day. Each is called by its message (with the command under it) or by its command, with what it edited, added, removed and renamed, and how many definitions it only re-hashed. It can be narrowed to entries changing a namespace. An entry's page has its whole message and its changes, shown as a contribution's are: each definition's lines (added, removed, or a diff), what only re-hashed folded away. Its address is the start of its hash, as
tcm historyshows it (/forge/history/6iewhvaq).Branches shows when each last changed, how many names it differs from the default by, and the contribution proposing it, if any.
Releases has each package's versions and their notes.
People has the maintainer and the contributors, what they've proposed and merged, and how to become one.
A page that doesn't load (the server failed or is unreachable) says why, with a button to reload. A tab opened before the UI was updated reloads onto the new version by itself (production).
Reviewing a contribution
A contribution's page has its conversation (the description, comments, closing and reopening, and whether it merges) and its changes:
- The changes follow a review path: what's new before what uses it, then tests, then what was removed and renamed. What only re-hashed because something it uses changed comes last, folded: its code is the same.
- What it touches sums up the change in a row: what it added and edited, what re-hashed because of it, and the tests that reach it.
- Each definition's change is its code before and after, with two lines of context, highlighted, numbered on
both sides, with its comments.
- "Unified" or "Split" shows every diff on the page inline or side by side. The browser remembers which; a phone gets them inline.
- "Viewed" folds a change away, remembered for that version of it.
- Checks say whether it merges (as a fast-forward, cleanly, or with conflicts in which names), that its definitions typechecked when pushed, how many tests reach it, and whether docs changed with it.
- Commenting, closing, reopening and merging are what the viewer may do (below). A merge is
tcm contribution merge: by history, refused on conflicts.
Signing in
Who you are is the token the browser signed in with, kept as a cookie (HttpOnly, SameSite=Strict):
- The link
tcm contributeprints (/forge/contributions/<id>#token=…) signs you in by itself. The token is in the fragment, which browsers never send to a server; the page signs in with it and removes it from the address. - Sign in, in the header, takes a token: a contributor's (
tcm user add) or the server's. Signed in, your avatar opens a menu with who you are, links to your work, and sign out. - You may do what your token may do, as the server allows it. A contributor comments (signed as them) and closes or reopens their own contributions. The maintainer (the server's token) merges, and signs with the name they give. With no token, a public server's pages are read-only.
A page runs no code
The UI reads the codebase; it never runs what's in it, and doesn't show how it runs (that's the studio's, on your own machine). A doc's examples are shown as written, not evaluated, and packages are read from their manifests' source. So a public server can show anyone its code, contributions included, without running any of it.
How it runs
The UI is an app of tcm's own (tcmts.share.App), shipped in tcm's package and run by tcm start
(apps). Its controls are React Aria's, so the finder, tabs, menus, selects and folds work by
keyboard and with a screen reader.
tcm servestarts it the first time/forge/is asked for, or, on a--publicserver, as soon as it's listening. It's kept in the served codebase's.tcm/share/, and stops whentcm servestops or is killed.- The first start, and the first after tcm is updated, builds it (installing React and the rest), which takes a few seconds. A page asked for meanwhile says it's starting and reloads by itself.
- Its output is in
tcm serve's, each line markedui: starting, stopping, failures (with a stack), and any page that took a second or more to render. Each page's time is in itsServer-Timingheader (React). /favicon.icois the UI's icon, unless the--sitehas one.
Pages are quick, the first time too
- Pages are small. They're gzipped, and a diff sends only each change with two lines of context. A history entry's page comes with its first twenty changes and loads the rest ten at a time as you scroll; what only re-hashed lists the first hundred.
- What a page reads is kept. What it reads of the branch's head is read once per head, until the head moves. What never changes (each history entry's changes, each release's contents) is kept for good. A history entry's changes are read from what differs from its parent, so one that re-hashed hundreds of definitions reads hundreds, not every name.
- When the head moves (a push, a merge), pages are made ahead, before anyone asks: the UI checks every two seconds, and when it starts. It works out where each definition runs and what it's for, the front page's map, and the history's first ten pages.
- One page doesn't hold up the others. Bulk work is done in small steps with other pages' work in between, and the work ahead waits while a page is being made.
- A busy UI doesn't hold
tcm serve's requests. A page or file the UI hasn't begun to answer in 30 seconds gets a 503 saying it's busy, and a page reloads by itself five seconds later. A UI that has answered nothing for a minute while asked is stopped (ui answered nothing for 60s: stopped, to start again) and started at the next request, unless an action it's making (a merge, a comment) is still going: those are never cut short.
Other codebases
tcm serve --forge /code/notes=../notes shows another codebase under a path, in a web UI of its own
(/code/notes/, its pages laid out as /forge/'s are: /code/notes/code/notes/model). tcm's server shows
its examples this way. Separate several with commas.
- It's read-only. Anyone can read it, whether or not the server is
--public, and no one signs in. There's nothing to pull from it (it isn't served), so its pages leave out how to get it, its contributions and its people. - It's started when first asked for, as
/forge/is, its app kept in the served codebase's.tcm/share-<path>/. Until the directory holds a codebase, its pages say there's none yet (a 503). - A directory that's a link is followed. When the link moves to another codebase (a new build of it), the next page asked for starts the UI again on that one.