@swarm.ing/pieui
v3.0.3
Published
A React component library featuring PieCard component
Readme
PieUI
PieUI (@swarm.ing/pieui) is a React library for rendering server-driven UIs. A backend returns a JSON description of a screen, and PieUI turns it into a React tree by looking up each node in a component registry. On top of rendering it ships real-time messaging (Socket.IO, Centrifuge, Mitt), server-driven AJAX updates, host integrations (Telegram Mini Apps, MAX, React Native), an OpenAI voice-agent bridge, a Storybook addon, and a pieui CLI for scaffolding apps, pages, and cards.
- Server-driven — screens are described by data (
UIConfigType), not code. - Registry-based — register components by name; the backend references them by that name.
- Real-time — cards can subscribe to Socket.IO, Centrifuge, or in-process Mitt events.
- Multi-platform — web, Telegram Mini Apps, MAX (VK), and React Native from one core.
- Tooling — a CLI that scaffolds Next.js apps, generates cards/pages, and syncs a component registry.
React and React DOM
>=19are peer dependencies. React Native>=0.74and@openai/agentsare optional peers (only needed for the/nativeand/agententries).
Table of contents
- Installation
- Core concepts
- Quick start
- Package entry points
- Root components
- Registering components
- Component prop shapes
- Built-in cards
- Real-time messaging
- Server-driven AJAX updates
depsNames— client-side submit sources- Configuration & environment
- Registry API
- Styling helpers
- Platform integrations
- OpenAI agent integration
- Storybook integration
- CLI reference
- API reference
- License
Installation
bun add @swarm.ing/pieui
# or
npm install @swarm.ing/pieui
# or
pnpm add @swarm.ing/pieui// peerDependencies
"react": ">=19",
"react-dom": ">=19",
"react-native": ">=0.74", // optional — only for @swarm.ing/pieui/native
"@openai/agents": "^0.4.5" // optional — only for @swarm.ing/pieui/agentCore concepts
Server-driven UI. The backend describes a screen as a tree of UIConfigType nodes:
interface UIConfigType {
card: string // registry name of the component to render
data: any // props bag passed to the component as `data`
content: UIConfigType | UIConfigType[] // nested child config(s), if any
}The registry. Every renderable component is registered under a name. The renderer (UI) reads config.card, looks it up in the registry, and renders it — passing data, content, and setUiAjaxConfiguration as appropriate.
Cards. A "card" is just a registered React component. PieUI ships a handful of built-in container/leaf cards (SequenceCard, BoxCard, HTMLEmbedCard, …) and you register your own for everything app-specific.
Side-effect registration. Importing from @swarm.ing/pieui (or @swarm.ing/pieui/components) registers the built-in cards automatically as a side effect — there is no initializePieComponents() call to make anymore. Your own cards register themselves when their module is imported (see Registering components).
Quick start
'use client'
import { PieRoot } from '@swarm.ing/pieui'
// Importing your registry module triggers side-effect registration of your cards.
import './piecomponents/registry'
export function App() {
return (
<PieRoot
location={{
pathname: window.location.pathname,
search: window.location.search,
}}
fallback={<>Loading…</>}
onError={() => window.location.reload()}
onNavigate={(url) => {
window.location.href = url
}}
config={{
apiServer: 'https://api.example.com/',
centrifugeServer:
'wss://realtime.example.com/connection/websocket',
enableRenderingLog: false,
}}
/>
)
}PieRoot fetches the screen config from `${config.apiServer}/api/content${pathname}?${search}` with React Query + Axios, sets up every provider PieUI needs, and renders the resulting tree.
- Telegram Mini App? Use
PieTelegramRootfrom@swarm.ing/pieui/telegram. - MAX mini-app? Use
PieMaxRootfrom@swarm.ing/pieui/max. - React Native? Use
PieRoot/PieNativeRootfrom@swarm.ing/pieui/native.
Package entry points
PieUI is split into focused entry points so apps only ship what they use:
| Import path | Contents |
| ---------------------------------- | ----------------------------------------------------------------------------------------- |
| @swarm.ing/pieui | Main barrel: roots, UI, PieCard, registry API, hooks, types, built-in card side-effect |
| @swarm.ing/pieui/components | Just the built-in cards + UI/PieCard (registers built-ins on import) |
| @swarm.ing/pieui/telegram | PieTelegramRoot, Telegram WebApp hooks, CloudStorageCard/SecureStorageCard |
| @swarm.ing/pieui/max | PieMaxRoot and MAX (VK) WebApp hooks |
| @swarm.ing/pieui/native | React Native core + native card variants + native source wiring |
| @swarm.ing/pieui/agent | OpenAI agent tools (getMittAgentTools, useOpenAIWebRTC) — pulls in @openai/agents |
| @swarm.ing/pieui/storybook | Storybook providers/decorators for testing cards in isolation |
| @swarm.ing/pieui/storybook/addon | Storybook addon (panel + manager) for firing PieCard methods from the Storybook UI |
The Telegram, MAX, and agent integrations are deliberately split out so apps that don't target those hosts never bundle their code.
Root components
All roots wrap children in the PieUI provider stack (React Query, Mitt, Socket.IO, Centrifuge, config, navigation, and — unless disabled — a global <form>).
PieRoot
Fetches a UIConfigType from api/content{pathname}{search} and renders it.
| Prop | Type | Description |
| ------------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| location | { pathname: string; search: string } | Current route. Used to build the request URL and as part of the React Query key. |
| config | PieConfig | Runtime configuration (API URLs, logging, page processor). Required. |
| fallback | ReactNode | Rendered while loading or on fetch failure. |
| piecache | Record<string, UIConfigType> | Optional per-pathname snapshot; a cached shell is shown via UILoading instead of the plain fallback. |
| onError | (error?: unknown) => void | Called on a render-blocking error (config fetch throws, or a lazy card chunk fails after retries). |
| onNavigate | (url: string) => void | Navigation handler forwarded through NavigateContext so cards route via the host router, not a full reload. |
| queryOptions | PieQueryOptions | React Query overrides (everything except queryKey/queryFn/enabled, which PieRoot manages). |
| queryClient | QueryClient | Supply a stable (module-singleton) client so the config cache survives remounts. Otherwise one is made per mount. |
| disableGlobalForm | boolean | When true, the implicit <form id="piedata_global_form"> wrapper is not rendered. |
PieBaseRoot
The provider stack without any fetch. Renders children inside the same providers and (optional) form shell. Use it when you already have a UIConfigType (render it with UI) or want PieUI context without a network round-trip.
PiePreviewRoot
A root tailored to the CLI preview harness (pieui registry dev/build) for previewing a single card.
Platform roots
PieTelegramRoot(/telegram) — likePieRoot, but appends Telegram WebAppinitDatato the request query string viauseWebApp. Throws ifapiServeris missing.PieMaxRoot(/max) — MAX (VK) equivalent.PieNativeRoot(/native) —PieBaseRootfor React Native (no HTML form).
Registering components
A card is any React component registered under a name. The CLI generates a per-card index.ts that calls registerPieComponent, and a piecomponents/registry.ts that side-effect-imports each card. Registering manually:
import { registerPieComponent } from '@swarm.ing/pieui'
import MyCard from './MyCard'
registerPieComponent({
name: 'MyCard',
component: MyCard,
metadata: { author: 'You', version: '1.0.0', description: 'A custom card' },
})Lazy / code-split cards — pass a loader instead of component; PieUI wraps it in React.lazy and renders it inside Suspense (with an optional fallback):
registerPieComponent({
name: 'HeavyChart',
loader: () => import('./HeavyChart'),
fallback: <Spinner />,
})ComponentRegistration shape:
interface ComponentRegistration<TProps> {
name: string
component?: ComponentType<TProps>
loader?: () => Promise<{ default: ComponentType<TProps> }>
fallback?: ComponentType<{}>
metadata?: { author?: string; version?: string; description?: string; tags?: string[] }
}A registration must provide a
nameand eithercomponentorloader, otherwiseregisterPieComponentthrows.
Component prop shapes
The renderer passes different props depending on a card's "kind". Pick the matching prop type for your component:
| Kind | Prop type | Props received | card add type |
| ------------------- | -------------------------------------- | -------------------------------------------------------- | ------------------- |
| Simple | PieSimpleComponentProps<T> | { data } | simple |
| Complex | PieComplexComponentProps<T> | { data, setUiAjaxConfiguration? } | complex |
| Container | PieContainerComponentProps<T> | { data, content: UIConfigType, setUiAjaxConfiguration? } | simple-container |
| Complex container | PieComplexContainerComponentProps<T> | { data, content: UIConfigType[], setUiAjaxConfiguration? } | complex-container |
Each has an InputPie…ComponentProps<T, TStored> variant that adds a typed stored prop (used when a card needs locally stored state — wired through <PieCard stored={…} />).
import type { PieContainerComponentProps } from '@swarm.ing/pieui'
interface MyData { title: string }
export default function Panel({
data,
content,
}: PieContainerComponentProps<MyData>) {
return (
<section>
<h2>{data.title}</h2>
<UI uiConfig={content} />
</section>
)
}Built-in cards
These register automatically when you import @swarm.ing/pieui (or /components).
Containers
| Card | Kind | Description |
| --------------- | ----------------- | ----------------------------------------------------------------------------- |
| SequenceCard | complex-container | Renders an array of children sequentially inside a styled wrapper. |
| UnionCard | complex-container | Renders an array of children with no wrapping element. |
| OneOfCard | complex-container | Renders children in a styled wrapper (typically for conditional selection). |
| BoxCard | container | Single-child wrapper with optional click-to-navigate and inline styling. |
| AjaxGroupCard | container | Single-child group that loads/replaces its content via AJAX and emits events. |
Common (leaf)
| Card | Kind | Description |
| -------------------- | ------ | ----------------------------------------------------------------------------------- |
| HiddenCard | simple | Renders a hidden input with state management and real-time value sync. |
| HTMLEmbedCard | simple | Parses and renders arbitrary HTML (optionally driven by OpenAI WebRTC generation). |
| AutoRedirectCard | simple | Redirects on mount (internal route or external URL). |
| IOEventsCard | simple | Handles toast notifications, alerts, push notifications, and redirect/reload events. |
| DeviceStorageCard | simple | Persists data to localStorage, mirrored into a hidden input for form submission. |
| SessionStorageCard | simple | Persists data to sessionStorage, mirrored into a hidden input. |
Telegram-only (register via @swarm.ing/pieui/telegram)
| Card | Kind | Description |
| ------------------- | ------ | ----------------------------------------------------------------- |
| CloudStorageCard | simple | Persists data to Telegram CloudStorage (async), hidden-input mirror. |
| SecureStorageCard | simple | Persists data to Telegram SecureStorage (async), hidden-input mirror. |
The storage cards pair with the AJAX depsNames prefixes: localStorage: ↔ DeviceStorageCard, sessionStorage: ↔ SessionStorageCard, telegram:cloud: ↔ CloudStorageCard, telegram:secure: ↔ SecureStorageCard (see depsNames).
Real-time messaging
PieCard wires a card into PieUI's real-time transports. It renders no UI of its own — it returns its children and, on mount, subscribes the supplied methods map to whichever transports are enabled:
| Transport | Event / channel format | Enable with |
| ------------- | --------------------------------------------------- | ----------------------- |
| Socket.IO | pie{methodName}_{data.name} | useSocketioSupport |
| Centrifuge| pie{methodName}_{data.name}_{centrifugeChannel} | useCentrifugeSupport + centrifugeChannel |
| Mitt | pie{methodName}_{data.name} | useMittSupport |
import { PieCard } from '@swarm.ing/pieui'
function LiveCounter({ data }) {
const [count, setCount] = useState(0)
return (
<PieCard
card="LiveCounter"
data={data}
useMittSupport
useSocketioSupport
methods={{
increment: (payload) => setCount((c) => c + (payload?.by ?? 1)),
reset: () => setCount(0),
}}
>
<span>{count}</span>
</PieCard>
)
}The methods map is held in a ref, so you can pass freshly-created closures every render without re-subscribing. Cleanup runs on unmount or when data.name/the support flags change.
Emitting Mitt events — usePieEmit
Trigger a card's Mitt-subscribed method from anywhere in the tree:
import { usePieEmit } from '@swarm.ing/pieui'
function Controls() {
const emit = usePieEmit('LiveCounter') // card name
return <button onClick={() => emit('increment', { by: 5 })}>+5</button>
}getEmitter() returns the shared emitter for imperative use outside React.
Server-driven AJAX updates
Cards that trigger updates receive a setUiAjaxConfiguration callback. Combined with useAjaxSubmit, a card can POST to the backend and swap part of the UI — or stream UI events — without a full reload.
import { useAjaxSubmit } from '@swarm.ing/pieui'
function SubmitButton({ data, setUiAjaxConfiguration }) {
const submit = useAjaxSubmit(
setUiAjaxConfiguration,
data.kwargs, // static key/values from the card config
data.deps_names, // client-side values resolved at submit time
data.pathname, // backend handler path
{ timeout: 10_000, retryPolicy: { maxRetries: 2, baseDelay: 1000 } }
)
return <button onClick={() => submit({ extra: 'value' })}>Submit</button>
}The request is POST {apiServer}/api/ajax_content{pathname}, and the response either replaces the UIConfigType subtree or streams JSON-line UI events through setUiAjaxConfiguration. The body is assembled from three sources:
kwargs— static key/value pairs from the card config.extraKwargs— values passed to the returned submit function at call time.depsNames— names whose current client-side value is read at submit time (see below).
RetryPolicy
type RetryPolicy = {
maxRetries?: number // default 0 (no retries)
baseDelay?: number // default 1000 ms; doubled each attempt
retryOn?: number[] // default [502, 503, 504]; timeouts/network errors always retry
}depsNames — client-side submit sources
Each entry in depsNames is a "magic name": an optional source prefix plus a key. The value is submitted under the bare key (the part after the prefix), so localStorage:token is sent as the field token.
| depsNames entry | Read from |
| ------------------------ | ------------------------------------------------------------- |
| email (no prefix) | DOM input named email (document.getElementsByName) |
| sid | window.sid — Socket.IO session id (awaits the socket) |
| localStorage:<key> | localStorage.getItem(key) |
| sessionStorage:<key> | sessionStorage.getItem(key) |
| cookie:<name> | matching cookie from document.cookie (URL-decoded) |
| url:<param> | ?<param> query params (repeated → multiple values) |
| telegram:cloud:<key> | Telegram.WebApp.CloudStorage.getItem (async) |
| telegram:secure:<key> | Telegram.WebApp.SecureStorage.getItem (async) |
depsNames: ['email', 'localStorage:token', 'url:ref', 'sid']
// POST body fields: email, token, ref, sidNotes:
- A missing value contributes nothing — the field is simply omitted (same as an absent DOM input).
- A single entry can yield multiple values (a multi-file
<input>, repeatedurl:params); each is appended under the same bare key. telegram:cloud:/telegram:secure:are async. The submit flow awaits them; the syncreadAjaxKeyreturns[]for them, so usereadAjaxKeyAsyncto read directly.parseDepName(name)returns{ source, key }.depsNamesis normally supplied by the backendUIConfigat runtime — it is data interpreted by the frontend, not a CLI surface.
Configuration & environment
PieConfig (passed to roots as config):
interface PieConfig {
apiServer: string // base URL of the PieUI API (build endpoints from it)
centrifugeServer?: string // Centrifuge websocket URL
enableRenderingLog?: boolean // verbose render/AJAX/realtime logging
pageProcessor?: string // page-processing configuration
}When PieUI is wired through the CLI scaffold, these are read from environment variables (multiple framework prefixes supported):
PIE_API_SERVER/VITE_PIE_API_SERVER/NEXT_PUBLIC_PIE_API_SERVERPIE_CENTRIFUGE_SERVER/VITE_PIE_CENTRIFUGE_SERVER/NEXT_PUBLIC_PIE_CENTRIFUGE_SERVERPIE_ENABLE_RENDERING_LOG(optional)PIE_PAGE_PROCESSOR(optional)
Use the literal value auto-api for a server URL to derive it automatically from the current hostname.
Registry API
All exported from @swarm.ing/pieui (and /native):
| Function | Description |
| --------------------------------- | ---------------------------------------------------------------------------- |
| registerPieComponent(reg) | Register a component or lazy loader. Returns the (possibly lazy) component. |
| registerMultipleComponents([…]) | Register an array of registrations at once. |
| unregisterComponent(name) | Remove a component from the registry. |
| hasComponent(name) | true if a name is registered. |
| getRegistryEntry(name) | Full ComponentRegistration (incl. fallback / isLazy). |
| getComponentMeta(name) | The metadata supplied at registration, if any. |
| getAllRegisteredComponents() | Array of all registered names. |
| getRegistrySize() | Number of registered components. |
| getLazyComponentNames() | Names of code-split (lazy) components. |
| preloadComponent(name) | Warm a single lazy chunk so the next render resolves without a fallback flash. |
| prefetchLazyComponents() | Idle-time, connection-aware background warm-up of every lazy chunk (idempotent). |
| registry | The underlying Map (shared singleton across all entry points). |
| trackLazy(loader, name) | Low-level React.lazy wrapper with named tracking. |
The registry is stored on
globalThisunder a sharedSymbol.forkey, so a card registered through one entry point (/components) is visible to a renderer loaded from another (/telegram). This avoids "[UI] Component not found in registry" across bundles.
Styling helpers
cn(...classes)— class-name merge built onclsx+tailwind-merge(dedupes conflicting Tailwind utilities).sx2radium(style)— converts a style object into Radium-friendlyCSSProperties, including turning an objectanimationNameinto Radium keyframes.PIEBREAK— the__piedemo__delimiter used internally to build form field names.pieName(...)— helper for composing Pie event/field names.submitGlobalForm()— programmatically submits the implicit global<form>.
Platform integrations
Telegram Mini Apps
import { PieTelegramRoot, useWebApp, useInitData } from '@swarm.ing/pieui/telegram'
function App() {
return <PieTelegramRoot location={location} config={config} />
}useWebApp()— the fullTelegram.WebAppobject (typed:MainButton,BackButton,HapticFeedback,CloudStorage,BiometricManager, theme params, safe-area insets, fullscreen/orientation, and the Bot API methods).useInitData()— the parsed init data.- Importing this entry also registers
CloudStorageCardandSecureStorageCard.
MAX (VK Messenger)
import {
PieMaxRoot,
useMaxWebApp,
useMaxInitData,
useMaxBackButton,
useMaxHapticFeedback,
} from '@swarm.ing/pieui/max'Typed access to the MAX bridge (BackButton, HapticFeedback, DeviceStorage/SecureStorage, BiometricManager, share/download helpers, screen capture, etc.).
React Native
The /native entry exposes the platform-agnostic core (registry, UI, PieCard, real-time contexts, AJAX helpers) plus native-specific wiring. It deliberately does not export the DOM container/leaf cards — on native the host registers its own React Native leaf components and renders them through UI/PieCard. Metro resolves the .native platform layer automatically.
Wire the host's storage / route / form sources once at startup:
import { configureNativeClientSources } from '@swarm.ing/pieui/native'
import { MMKV } from 'react-native-mmkv'
const mmkv = new MMKV()
configureNativeClientSources({
storage: {
getItem: (k) => mmkv.getString(k) ?? null,
setItem: (k, v) => mmkv.set(k, v),
removeItem: (k) => mmkv.delete(k),
},
getRouteParams: (key) => getCurrentRoute().params[key] ?? [],
getInput: (name) => readNativeField(name),
submitForm: () => {/* host submit */},
})NativeClientConfig supports sync (storage, sessionStorage) and async (asyncStorage, asyncSessionStorage) adapters, getCookie, getRouteParams, getInput, and submitForm. Unconfigured sources degrade to the same "missing value" semantics as web (null / []). Render with PieRoot/PieNativeRoot and pass disableGlobalForm (there is no HTML form on native). A small native form store is exposed via setNativeField / clearNativeField / readNativeField.
OpenAI agent integration
The /agent entry exposes PieCard methods as OpenAI function tools and provides a WebRTC voice hook. It pulls in @openai/agents, so it's a separate entry to keep it out of apps that don't need it.
import {
usePieMittAgentTools, // hook: build tools from the active Mitt emitter
getMittAgentTools, // imperative variant
useOpenAIWebRTC, // WebRTC voice session hook
} from '@swarm.ing/pieui/agent'getMittAgentTools(descriptors, options)/usePieMittAgentTools(...)— turn PieCard methods into agent tools. Options:filter,describe, andnameForto control which descriptors become tools, their descriptions, and tool names.useOpenAIWebRTC(audioElement?, onEvent?)— returns{ isSessionActive, startSession(ephemeralKey, useMicrophone?), stopSession(), sendTextMessage(text) }for an OpenAI Realtime WebRTC session (pass an<audio>element to play remote audio). Pairs withHTMLEmbedCard's AI-generation support.
Storybook integration
Test cards in isolation with the PieUI providers and fire their real-time methods from the Storybook UI.
// .storybook/preview.tsx
import { withPieCard } from '@swarm.ing/pieui/storybook'
export const decorators = [withPieCard]// MyCard.stories.tsx
import { PieStorybookProviders, PieMethodTrigger } from '@swarm.ing/pieui/storybook'
export const Live = () => (
<PieStorybookProviders>
<MyCard data={{ name: 'MyCard' }} />
<PieMethodTrigger card="MyCard" method="increment" payload={{ by: 1 }} />
</PieStorybookProviders>
)PieStorybookProviders— mounts the full provider stack with a stubbed config and a local Mitt emitter.withPieCard— decorator wrapping a story in those providers + a channel bridge.PieMethodTrigger— a button that fires apie{method}_{card}Mitt event.firePieMethod(emitter, card, method, payload)/usePieStorybookEmitter()— imperative helpers.- The addon (
@swarm.ing/pieui/storybook/addon) adds a Storybook panel to fire methods interactively; register it in.storybook/main(the CLI'scard add-storydoes this automatically).
CLI reference
The CLI ships as pieui (run via bunx pieui … or the installed bin). It mirrors the backend pie Python CLI — subcommand names, positionals, and flags are kept in sync.
Scaffolding
pieui create <AppName> # create a Next.js app and run `pieui init` inside it
pieui create-pie-app <AppName> # create a blank PieUI Next.js template (alias: create-pieui)
pieui init [--out-dir <dir>] # add piecomponents/, registry.ts, Tailwind & Next config to an existing appcreate scaffolds Next.js (next-app@latest), installs @swarm.ing/pieui, runs init, installs Storybook, then bun run dev. init also searches your home directory (depth ≤ 2) for a backend project with pages/ and components/ dirs and, in a TTY, offers to link them (saved to .pie/config.json).
Build
pieui postbuild [--src-dir <dir>] [--out-dir <dir>] [--append]Scans for registerPieComponent() calls, generates a JSON Schema per card data type, and writes pieui.components.json. Defaults: --src-dir src, --out-dir public. --append merges with the library manifest in node_modules.
pieui self-upgrade [--pm <bun|pnpm|yarn|npm>] # upgrade the global CLIAuth
pieui login # browser auth; writes .pie/config.json and PIE_* keys into .envPages (pieui page …)
pieui page add <path> # create app/<path>/page.tsx from the Pie page template
pieui page view <path> # print the page source
pieui page ajax <path> <add|remove> <handler> # add/remove an AJAX handler (delegates to the backend)add normalizes the path under app/, derives a PascalCasePage component name, and rejects traversal outside app/.
Cards (pieui card …)
pieui card add [<type>] <Name> [--io] [--ajax] [--input] [--from <ref>]Creates piecomponents/<Name>/ (index.ts, types/index.ts, ui/<Name>.tsx) and registers it in registry.ts. <type> is one of simple, complex, simple-container, complex-container (default). Flags:
--io— add real-time support fields (use_*_support) to the data interface.--ajax— add AJAX fields (pathname,deps_names,kwargs).--input— generate theInputPie…ComponentPropsvariant with a typedstoredprop.--from <ref>— port from backend Python metadata (a.py/JSON path, a card name, or auto-detected from the configuredbackendComponentsDir).
pieui card list [<filter>] [--src-dir <dir>] # list cards (filter: all|simple|complex|simple-container|complex-container)
pieui card view <Name> # print a card's props, ajax, io, and events
pieui card remove <Name> # delete a card directory
pieui card list-events <Name> # list a card's methods (event handlers)
pieui card add-event <Name> <EventName> # add a method to a card
pieui card pull <ref> # restore a card from a URL, local JSON file, or remote nameMetadata & Storybook:
pieui card dump-metadata <Name> [--out <file.json>] # emit full PieMetadata JSON (props/events/imports/ajax/io)
pieui card check-sync <Name> # compare TypeScript ↔ Python metadata, report mismatches
pieui card add-story <Name> [--force] # generate <Name>.stories.tsx wired to PieCard methods
pieui card generate-preview <Name> [--out <file.png>] # render the story via storycap to a PNGRemote card storage (pieui card remote …)
pieui card remote list [--user <id>] [--project <slug>]
pieui card remote push <Name>
pieui card remote pull <Name>[@rev]
pieui card remote remove <Name>
pieui card remote history <Name> [--page <n>] [--per-page <n>] [--from <r>] [--to <r>]
pieui card remote public <Name> # make readable as r/<user>/<Name>
pieui card remote private <Name> # revert to privateRegistry preview harness (pieui registry …)
pieui registry dev [--port <n>] [--api-server <url>] # run the PiePreviewRoot harness (default port 3210)
pieui registry build [--out <dir>] # static-export the harness (default .pie/registry/out)Configuration & environment
.pie/config.json (written by login / init) holds user_id, project, api_key, and optional backendPagesDir / backendComponentsDir. Notable env vars: PIE_USER_ID, PIE_PROJECT/PIE_PROJECT_SLUG, PIE_API_KEY; PIEUI_CREATE_NEXT_APP_SPEC, PIEUI_CREATE_PACKAGE_SPEC, PIEUI_CREATE_BUN_BIN, PIEUI_CREATE_SKIP_STORYBOOK; PIEUI_LOGIN_CONNECT_BASE, PIEUI_LOGIN_CREDENTIALS_API. Add --help/-h to any command for scoped help.
API reference
Runtime exports (@swarm.ing/pieui)
| Export | Description |
| ------ | ----------- |
| UI | Renders a UIConfigType by resolving uiConfig.card from the registry (Suspense for lazy cards; passes data, content, setUiAjaxConfiguration). |
| UILoading | Renders a cached/placeholder shell while a screen loads. |
| UIRendererContext | Context carrying renderer props (UIRendererProps). |
| PieRoot / PieBaseRoot / PiePreviewRoot | Root components (see Root components). |
| PieCard | Wires a card into Socket.IO / Centrifuge / Mitt messaging. |
| registerPieComponent & registry helpers | See Registry API. |
| trackLazy | Named React.lazy wrapper. |
| useAjaxSubmit / readAjaxKey / readAjaxKeyAsync / parseDepName | AJAX submit + dep-name resolution. |
| usePieEmit / getEmitter / MittContext | Mitt event emission. |
| SocketIOContext / CentrifugeIOContext / FallbackContext / PieConfigContext | PieUI contexts. |
| cn / sx2radium / pieName / submitGlobalForm / PIEBREAK | Utilities. |
Type exports
PieComponentProps, PieSimpleComponentProps, PieComplexComponentProps, PieContainerComponentProps, PieComplexContainerComponentProps, and their InputPie… variants; PieConfig, UIConfigType, SetUiAjaxConfigurationType, PieQueryOptions, RetryPolicy, DepSource. Telegram/MAX entries export their respective WebApp/init-data types; the /native entry adds PieRootProps, PieBaseRootProps, and the NativeClientConfig/adapter types.
License
MIT
