Moss · guide
A screen by grant
Two people open the same screen and one sees more on it. The richer layout is granted like a screen is.
pnpm add @niscorp/moss @niscorp/charter @niscorp/nova @niscorp/prism @niscorp/strata @niscorp/tide @niscorp/vex zod @electric-sql/pgliteStep 1 of 3
One screen, and a richer layout of it
The programme screen has a plain layout. The app also ships a second layout of the same screen, with the takings on it, under a name of its own.
The plain layout is what everybody gets. A variant only ever adds.
import { PGlite } from '@electric-sql/pglite';
import { createServer, defineApp, mintDevToken } from '@niscorp/moss';
import type { RenderNode } from '@niscorp/nova';
import { createPglitePool } from '@niscorp/vex/pglite';
const db = new PGlite();
const runtime = { db, pool: createPglitePool(db), session: 'dev-open' as const };
const text = (says: string) => ({ component: 'Text', children: says });
const actions = { programme: { id: 'programme', layout: text('This week: The Tempest') } };
const layouts = {
'programme.with-takings': {
action: 'programme',
layout: { component: 'Stack', children: [text('This week: The Tempest'), text('Saturday took 4,180')] },
},
};Step 2 of 3
Grant the variant
In the charter, a role’s layouts selects variants by name. The manager is granted this one. A member is granted none, so a member gets the plain layout.
const charter = {
public: [],
member: { actions: ['programme'] },
manager: { extends: ['member'], layouts: ['programme.with-takings'] },
};
const assignments = { grace: ['member'], ruth: ['manager'] };
const shell = { canvases: [{ id: 'main', initial: 'programme' }] };
const server = await createServer(defineApp({ charter, assignments, actions, layouts, shell }), runtime);Step 3 of 3
The same screen, for each
Moss swaps the variant in when it builds the manager’s shell, before anything is drawn. Grace’s browser is never sent the takings, and her screen has no hidden part.
const words = (nodes: readonly RenderNode[]): string[] =>
nodes.flatMap((node) => (node.type === 'text' ? [node.value] : 'children' in node ? words(node.children) : []));
for (const who of ['grace', 'ruth']) {
const snapshot = await server.shells!.snapshot(mintDevToken(who), who);
console.log(who + ':', words(snapshot.trees['main'] ?? []));
}
server.close();Then
Run it.
The programme as Grace is sent it, and as Ruth is.
npx tsx variant.mtsgrace: [ 'This week: The Tempest' ] ruth: [ 'This week: The Tempest', 'Saturday took 4,180' ]