@shiguri/cavalry-jsx
v0.1.3
Published
JSX runtime and small helpers for building Cavalry script UIs.
Maintainers
Readme
cavalry-jsx
JSX runtime and small helpers for building Cavalry script UIs.
Cavalry Scripts
Cavalry script UIs are JavaScript files run by Cavalry. They use Cavalry's global ui module to create widgets, add them to a window, and call ui.show().
Official Cavalry documentation:
This package only provides a JSX layer over that scripting API. It does not add a renderer, lifecycle, or reactive update model.
Install Package
Install this package and Cavalry's TypeScript definitions:
pnpm add @shiguri/cavalry-jsx
pnpm add -D @scenery/cavalry-typesConfigure JSX
JSX must be compiled with the automatic JSX runtime and @shiguri/cavalry-jsx as the import source. Prefer configuring this once in tsconfig.json:
{
"compilerOptions": {
"types": ["@scenery/cavalry-types"],
"jsx": "react-jsx",
"jsxImportSource": "@shiguri/cavalry-jsx"
}
}If your bundler performs the JSX transform without reading that TypeScript setting, configure the same runtime there instead. For example:
// rolldown.config.ts
import { defineConfig } from "rolldown";
export default defineConfig({
transform: {
jsx: {
runtime: "automatic",
importSource: "@shiguri/cavalry-jsx",
},
},
});With automatic JSX runtime, the compiler imports @shiguri/cavalry-jsx/jsx-runtime. Development JSX mode may import @shiguri/cavalry-jsx/jsx-dev-runtime. Those are compiler entry points; user code should normally import from @shiguri/cavalry-jsx.
Write Scripts
A JSX expression creates Cavalry widgets immediately. A script file must execute the UI once at top level. The Window element configures Cavalry's script window, adds its children, and calls ui.show().
import { Button, HLayout, Label, Window } from "@shiguri/cavalry-jsx";
function CounterPanel() {
return (
<Window title="Counter" fixedSize={[280, 120]} margins={[12, 12, 12, 12]}>
<HLayout spaceBetween={8}>
<Label text="Count: 0" />
<Button text="Reset" onClick={() => {}} />
</HLayout>
</Window>
);
}
CounterPanel();The function is just a way to organize the script. It is not a component instance with state or lifecycle. Calling it runs the JSX and creates the UI; calling it again creates another UI.
Props
Props configure widgets at creation time. They are not reactive.
Use initial-value props for creation-time value:
<LineEdit defaultText="Untitled" />
<NumericField defaultValue={10} />
<ColorChip defaultColor="#4ffd7a" />
<FilePath defaultFilePath="/tmp/output.txt" />
<ProgressBar maximum={10} defaultValue={0} />Value callbacks receive the current widget value as an argument.
<LineEdit onValueChanged={(text) => console.log(text)} />
<NumericField onValueChanged={(value) => console.log(value)} />
<Checkbox onValueChanged={(checked) => console.log(checked)} />
<DropDown entries={["One", "Two"]} onValueChanged={(index, text) => console.log(index, text)} />
<ColorWheel onColorChanged={(color) => console.log(color)} />Refs
Use refs when script code needs to read or update a widget after it has been created. In practice, this is most common for input widgets.
Value refs are helpers for common input widgets. They attach to a widget and expose value() and set(value) so callers do not need to remember the widget-specific getter and setter names.
import { Button, LineEdit, textRef, Window } from "@shiguri/cavalry-jsx";
const message = textRef();
function Panel() {
return (
<Window title="Input Ref">
<LineEdit ref={message.attach} defaultText="" />
<Button
text="Log"
onClick={() => {
console.log(message.value());
message.set("");
}}
/>
</Window>
);
}
Panel();Use widgetRef<T>() when you need the widget object itself rather than a value helper.
import { Button, List, widgetRef, Window } from "@shiguri/cavalry-jsx";
const list = widgetRef<ui.List>();
function Panel() {
return (
<Window title="Widget Ref">
<List ref={list.attach} model={[]} />
<Button
text="Add Row"
onClick={() => {
list()?.addRow({ uuid: String(Date.now()), label: "New Row" });
}}
/>
</Window>
);
}
Panel();Build Script
Cavalry runs JavaScript, so TypeScript/JSX must be compiled before installation. Any bundler that can emit a JavaScript file for Cavalry can be used.
One Rolldown setup is:
// rolldown.config.ts
import { defineConfig } from "rolldown";
export default defineConfig({
input: "src/panel.tsx",
output: {
file: "dist/panel.js",
format: "esm",
},
platform: "neutral",
treeshake: {
moduleSideEffects: true,
},
});This example relies on the JSX runtime setting from tsconfig.json. If your bundler does not read it, set the JSX runtime in the bundler config as shown above.
Build the script, then put the generated .js file in Cavalry's Scripts folder. In Cavalry, use Scripts > Show Scripts Folder to open that location. Installed scripts appear in Scripts.
The installed file should be a JavaScript file Cavalry can run directly. Do not leave unresolved package imports in the generated script unless your Cavalry script environment can resolve them.
Examples
The examples/ workspace contains small Cavalry UI scripts built with this package.
Included scripts:
demo-hello-panel: minimal panel with basic widgets and refs.layer-batch-renamer: rename selected layers with preview.time-marker-generator: create timeline markers from a frame range.
Build the examples:
cd examples
pnpm install
pnpm buildThe generated files are written to examples/dist/*.js. Copy them into Cavalry's Scripts folder (Scripts > Show Scripts Folder) to install them.
