Messages
Two actions on two canvases. One says something happened, and the other answers, without either knowing the other.
pnpm add @niscorp/nova @niscorp/prism @niscorp/strata zodThe kit
Nova draws nothing itself. It resolves a layout to a tree, and an adapter’s components draw the tree. This file uses the terminal kit, so it runs anywhere Node does; in an app the registry holds your own components.
import { createShell, shellSettled, shellView } from '@niscorp/nova';
import type { ActionDefinition } from '@niscorp/nova';
import { createTtyView } from '@niscorp/nova/adapters/tty';
import { defaultRegistry } from '@niscorp/nova/adapters/tty/components';
const registry = defaultRegistry();
registry.register('CanvasSlot', ({ children }) => ({ lines: children.flatMap((child) => child.lines) }));One action speaks, one listens
Actions do not call each other. emit publishes a message on a channel, with a payload, and any action with a message trigger on that channel answers it. The listener reads what was said as @event.payload.
The two actions here name the same channel and nothing else of each other.
const seats: ActionDefinition = {
id: 'seats',
data: { count: 2 },
layout: { component: 'Button', ref: 'book', children: 'Book {{$.count}} seats' },
triggers: [{ event: 'ui:click', ref: 'book', do: [{ emit: { channel: 'booked', payload: '$.count' } }] }],
};
const tally: ActionDefinition = {
id: 'tally',
data: { bookings: 0, seats: 0 },
layout: { component: 'Text', children: '{{$.bookings}} bookings, the last for {{$.seats}}' },
triggers: [{ message: 'booked', do: [{ increment: 'bookings' }, { set: 'seats', value: '@event.payload' }] }],
};Two canvases
Each action stands on a canvas of its own. A message reaches every action the shell holds, on any canvas.
const shell = createShell({ canvases: [{ id: 'main' }, { id: 'side' }], registry, actions: { seats, tally } });
shell.push('main', 'seats');
shell.push('side', 'tally');
await shellSettled(shell);
const draw = (): void => console.log(createTtyView(registry, shellView(shell).api).render().text);
draw();Book twice
Two presses on one canvas, and the other canvas counts them.
shell.dispatch({ type: 'ui:click', ref: 'book' });
shell.dispatch({ type: 'ui:click', ref: 'book' });
await shellSettled(shell);
draw();Run it.
Both canvases, before and after. The tally changed, and nothing in seats names it.
npx tsx messages.mts── main ──────────────────────────────────────────────────── [1] (Book 2 seats) ── side ──────────────────────────────────────────────────── 0 bookings, the last for 0 ── main ──────────────────────────────────────────────────── [1] (Book 2 seats) ── side ──────────────────────────────────────────────────── 2 bookings, the last for 2