Creator guide
This guide takes a PlayerVox OverCrow widget from an empty directory to a submission: create a project, edit its view, style and logic, run it in OverCrow, test it, package it and submit it. Each step links to the page that covers it in full.
To work with an AI assistant instead, add PlayerVox OverCrow's MCP server to it: see MCP.
1. Install the tools
overcrow-widget, the widget CLI, one binary for Linux and Windows. See installing the CLI.- Node.js 22 or later and npm, so that
checkcan type-check your logic with the project's own TypeScript and the SDK from npm. The CLI checks and packages without it, but warns that types were not checked. - PlayerVox OverCrow, to run the widget while you write it.
overcrow-widget doctor tells what your setup has and lacks.
2. Create a project
overcrow-widget init my-counter --template counter
cd my-counter
npm installnpm install brings TypeScript and @overcrow/sdk 1.0.0, named in the
project's package.json.
The templates are blank, counter, list and chart. The ID defaults to
yourhandle.<dir>: pass --id with your widget's ID, your publisher handle
and a name (nova.my-counter), reserved in the creator
space. Files of a project
lists every file and says which ones are packaged; the SDK
types says how to add the SDK to another project.
3. The view
view.ocml is markup: elements, attributes, {…} expressions bound to the
state, and on:<event> handlers that call functions exported by the logic.
<box class="counter">
<text class="value">{state.count}</text>
<box class="actions">
<button class="step" label={t("decrement")} on:activate={decrement}>
<icon name="minus"/>
</button>
<button class="step" label={t("increment")} on:activate={increment}>
<icon name="plus"/>
</button>
</box>
</box>The view also has if, for with a mandatory key, and local components:
see the view. Every element and attribute is in
elements and attributes.
4. The style
style.ocss is a bounded subset of CSS: class, element, descendant and
child selectors, the :hover, :active, :focus, :disabled and
:checked states, flex and grid layout, and the theme's design tokens as
var(--…), which follow the user's light or dark theme.
.step {
width: var(--icon-button-size);
height: var(--icon-button-size);
border-radius: var(--radius-md);
background: var(--color-surface-raised);
}.step:hover {
background: var(--color-surface-hover);
}There is no @media, @import, url() or calc(): an unknown
property or selector is an error, not a silent no-op. See
the style and style properties and tokens.
5. The logic
logic.ts owns the state. Declare its type once, mutate it in your
handlers, and the view follows: after each turn the VM evaluates the view
again and sends only what changed.
import { initState } from "@overcrow/sdk";
declare module "@overcrow/sdk" {
interface WidgetState {
count: number;
}
}export function increment(): void {
state.count += 1;
}The logic runs in a sandboxed VM, not in a browser: there is no DOM,
fetch, setTimeout, console or Intl. The SDK provides timers,
services, formatting helpers for times and numbers in the user's regional
format, messages and drawing. See the logic and the
SDK reference.
6. Messages
t("key") in the view or the logic reads locales/en.json or
locales/fr.json for the user's language, and fills {name} parameters.
{
"decrement": "Decrease",
"increment": "Increase"
}Provide both files or neither, with the same keys. See messages.
7. Check
overcrow-widget checkcheck runs the same validators as PlayerVox OverCrow: manifest, view,
style, locales, a lint of the logic against what the VM runs, the project's
TypeScript, and a package built in memory and read back. Each problem has a
stable code, a position and a help line; see
diagnostics.
8. Run it in OverCrow
overcrow-widget devdev sends the widget to the PlayerVox OverCrow overlay running on your
machine and reloads it each time you save. OverCrow offers this only when it
was started with development installs allowed
(OVERCROW_WIDGET_DEVELOPMENT=1; overcrow-widget doctor prints the
commands). The widget shows an Unverified · development package marker;
its declared permissions are granted for the session only, and nothing is
stored. log.info(…) from your logic prints in the terminal. See
the development channel.
9. Test it
Two kinds of tests, both described in testing a widget:
- unit tests of the logic with
@overcrow/sdk/testing, which runs your module with virtual time and simulated services; - scenarios played by
overcrow-widget testin PlayerVox OverCrow's headless runtime: the real VM and renderer, services answered by fixtures, and images compared with references.
"host": { "mode": "interactive" },
"steps": [
{ "expect": { "state": "running", "text": ["0"], "image": "initial" } },
{ "pointer": { "click": { "label": "Increase" } } },
{ "expect": { "text": ["1"], "noText": ["0"], "image": "increased", "fault": "none" } }
]overcrow-widget test10. Ask for what you need
A widget gets nothing by default. To call an API, keep data or read game
information, declare it in manifest.json; the user consents in the
Control Center before the widget gets it. A network rule names one HTTPS origin, one method and one
complete path, with typed parameters:
"permissions": {
"network": [
{
"origin": "https://api.example.com",
"method": "GET",
"path": "/v1/forecast/{city}",
"pathParams": { "city": { "type": "slug", "maxLength": 32 } },
"queryParams": {
"units": { "type": "enum", "values": ["metric", "imperial"], "required": true }
}
}
],
"storage": true
}, const url = `https://api.example.com/v1/forecast/${state.city}?units=${state.units}`;
const { status, body } = await http.fetch(url, { as: "json" });Game and system data come from subscriptions (fps.subscribe,
media.subscribe…), which update your state until you cancel them. See
services and permissions for what each permission allows,
and security for what a widget can never do. The complete
example is in
docs/content/examples/weather.
11. Package
overcrow-widget package
overcrow-widget inspect dist/yourhandle.my-counter-0.1.0.ocpkgpackage writes a deterministic .ocpkg to dist/: the same sources and
CLI version give the same bytes on every system. inspect shows what a
package holds and asks for, as a reviewer sees it. A package installed
locally stays unverified; users get widgets from the signed catalog. See
the package.
12. Submit
Run the checks of the creator space on your computer:
overcrow-widget admit --listing optionalThen create your publisher and reserve the widget's ID
(getting started), and send the version from the
creator space (publish a widget), or from a terminal
or a CI job with submit. A person at PlayerVox reviews
every version before players get it: see review.