@particle-academy/fancy-screens
v0.4.1
Published
Cross-surface coordination layer for Human+ apps — a screen registry, agent-presence rendering, schema-driven mode, and Zustand-store registration so agents can introspect and operate any surface.
Maintainers
Readme
@particle-academy/fancy-screens
Cross-surface coordination for Human+ apps. A <Screen> is a scoped, addressable application surface — bigger than a tab, smaller than a route. It registers with a global system so agents and presence layers can enumerate and target it, owns its own state (via Zustand stores you bring), and can render either as JSX or from an agent-emitted JSON schema.
Why
In a Human+ app, multiple agents and the user can be working in parallel across different surfaces. The runtime needs:
- Addressability — agents reference surfaces by stable id (
screen_focus("sheet")). - Cross-surface presence — the user looking at one screen sees a glimpse of agent activity on another.
- State introspection — agents enumerate the Zustand stores attached to each surface and read or mutate them without DOM scraping.
- Schema-driven mode — an LLM emits a JSON page description; the client renders it.
That's what fancy-screens provides. It's deliberately small (~400 lines) and does not implement its own state primitive — state is Zustand, registered with the screen system so it's discoverable.
Installation
npm install @particle-academy/fancy-screens zustandPeer dependencies (all optional):
react >= 18,react-dom >= 18zustand >= 4.5— bring your own state primitive@particle-academy/react-fancy >= 3— only if you render its components inside screens or schemas
Quick start
import { create } from "zustand";
import {
Screen,
useRegisterStore,
useScreens,
} from "@particle-academy/fancy-screens";
const useUserStore = create<{ name: string; setName: (n: string) => void }>((set) => ({
name: "",
setName: (name) => set({ name }),
}));
function App() {
return (
<Screen.System>
<Screen id="profile" title="Profile">
<Screen.Body><Form /></Screen.Body>
</Screen>
<DebugPanel />
</Screen.System>
);
}
function Form() {
useRegisterStore("user", useUserStore);
const { name, setName } = useUserStore();
return <input value={name} onChange={(e) => setName(e.target.value)} />;
}
function DebugPanel() {
// Lives outside the Screen but still sees the registry.
const screens = useScreens();
return <pre>{JSON.stringify(screens, null, 2)}</pre>;
}What you get
<Screen.System>— root provider; owns the registry + the store map.<Screen id title>— addressable surface. Self-registers; participates in presence (CSS class +--agent-colorvar whenagentActivityis set).useRegisterStore(name, store)— attach a Zustand store to the enclosing<Screen>under${screen.id}.${name}.useScreens()— agent-introspectable snapshot of every mounted Screen and the state of its registered stores.<Screen schema={...}>+registerSchemaComponent— render an entire surface from an LLM-emitted JSON page description.
Inertia.js
Inside an Inertia app, mount <Screen.System> at app-shell level (the @particle-academy/fancy-inertia adapter's <FancyAppRoot> does this for you). The schema-driven mode pairs with Inertia props out of the box via <InertiaSchemaScreen>. See docs/Inertia.md.
Documentation
| Topic | Description |
|-------|-------------|
| Screen | Root component + lifecycle |
| Stores | Zustand stores + useRegisterStore |
| SchemaMode | Agent-emitted JSON UI |
| Registry | useScreens() introspection |
| Inertia | Inertia.js patterns |
| Migration | Migrating from 0.3.x Ports |
Status
v0.4.x — Zustand-based state + addressable screens + schema-driven mode + cross-surface presence. Breaking change from 0.3.x: the Port system was removed; see Migration.md for the mapping. The <Screen>, <Screen.System>, useScreen(), useScreens() APIs are stable across the 0.x series.
License
MIT
