@potoru/web
v0.1.1
Published
Potoru on the web: the <potoru-player>, <potoru-snapshot> and <potoru-editor> elements and compile() for .poto stories, in browsers and Node. Heavy parts load on first use.
Maintainers
Readme
@potoru/web
Potoru on the web, in one package: the <potoru-player> element with a control bar, the
<potoru-snapshot> element for one still frame, the <potoru-editor> element, and compile(),
which turns Potoru source into .poto packages in browsers and in Node. Frames are drawn with
Canvas 2D. Pages stay light: engines (interaction, music, path guides), library code, the compiler,
the editor UI and the Script sandbox each load only when they are first needed.
From a CDN
<script type="module" src="https://cdn.jsdelivr.net/npm/@potoru/[email protected]/dist/potoru.js"></script>
<potoru-player src="https://example.com/stories/sunrise.poto" controls autoplay muted loop
style="width: 640px"></potoru-player>
<!-- one frame, no player: thumbnails, storyboards -->
<potoru-snapshot src="https://example.com/stories/sunrise.poto" time="1.5" style="width: 240px"></potoru-snapshot>
<potoru-editor mode="yaml" style="height: 560px"></potoru-editor>potoru.js registers all three elements. A page with only players fetches no compiler, editor or
sandbox code; a page with only snapshots fetches no action or score engine either.
From npm
npm install @potoru/webimport { compile } from "@potoru/web"; // registers the three elements (browsers)
const player = document.querySelector("potoru-player");
player.addEventListener("potoru:ready", (event) => console.log(event.detail.mode, event.detail.duration));
player.addEventListener("potoru:report", (event) => console.log(event.detail.event, event.detail.payload));
player.addEventListener("potoru:error", (event) => console.warn(event.detail.code, event.detail.message));
player.play();
player.seek(1.5);
player.fire("story-door", "open"); // send an event into an interactive story
player.set("story-hero", "speed", 2); // set a host input
const still = document.querySelector("potoru-snapshot");
still.seek(3); // draw another moment| Entry | What |
|---|---|
| @potoru/web | registers <potoru-player>, <potoru-snapshot> and <potoru-editor> in browsers and exports compile(), the element classes and the controller; under Node it resolves to core (no registration) |
| @potoru/web/player | registers only <potoru-player> and <potoru-snapshot>: no compiler or editor code, even for an app bundler |
| @potoru/web/snapshot | registers only <potoru-snapshot>: no controller, clock, sound or controls, and never the action or score engines |
| @potoru/web/core | compile, PlayerController, PotoruPlayerElement, PotoruSnapshotElement, PotoruEditorElement, definePotoruPlayer, definePotoruSnapshot, definePotoruEditor, PLAYER_VERSION, PLAYER_CAPABILITIES, types; importing it registers nothing (SSR, Node, frameworks, tests) |
| @potoru/web/experimental | the Canvas renderer, surfaces and host: may change in a minor release |
<potoru-player> attributes: src, libraries, autoplay, muted, loop, controls,
captions, mode, fit (contain, cover, fill), renderer (canvas, canvas-exact, svg),
motion, font-base, camera, still + time (show the frame at time seconds and do not play).
Methods: load, play, pause, seek, restart, next, prev, goto, set, fire,
setCamera (experimental).
Events: potoru:ready, potoru:play, potoru:pause, potoru:ended, potoru:slide,
potoru:scene, potoru:report, potoru:caption, potoru:status, potoru:error,
potoru:camera.
<potoru-snapshot>: attributes src, time (seconds), libraries, camera, fit,
font-base; methods seek(t) and load(src, { libraries }); currentTime, duration; events
potoru:ready ({ kind: "snapshot", time, duration, name, width, height, capabilities }) and
potoru:error. It draws again when an attribute changes or its box resizes. For many stills,
prefer it to <potoru-player still>.
All three elements can be created with document.createElement. The names and events are shared
with @potoru/react and other hosts: see docs/COMPONENTS.md in the repository.
compile()
Two source modes:
- YAML: a format v4 source folder (path → text). Validated and compiled with the same format
code as the
potoruCLI: the bytes equal whatpotoru exportwrites. Browsers and Node. - Script: JavaScript that uses the authoring API and returns a Project, run in a sandboxed iframe (opaque origin, no network, hard timeout). Browsers only, JavaScript only in 1.0.
import { compile } from "@potoru/web";
const result = await compile({
yaml: {
"potoru.project.yml": "format: potoru\nkind: story\n…",
"scenes/scene-hill/scene.yml": "…"
}
});
if (result.ok) await save(result.poto); // Uint8Array
else for (const d of result.diagnostics) console.log(`${d.file}:${d.line} ${d.code}: ${d.message}`);
// Script mode (browser):
await compile({ script: `
import { Project } from "@potoru/authoring";
const project = Project.create({ id: "dawn", name: "Dawn", format: 4 });
project.scene("field", { name: "Field", stage: { width: 320, height: 180 }, duration: 1, fps: 30 }, (scene) => {
scene.rect("sky", { at: [0, 0], size: [320, 120], fill: "#3366cc" });
});
export default project;
` });compile(input) → { ok, poto?, diagnostics, files?, scenes?, sizes }. Options: libraries
(.potolib bytes), scene, state, locale, profile: "presentation" (every root State a
slide), timeoutMs (Script, default 10 s). It never rejects for bad source; in Node, Script mode
returns the diagnostic script-browser-only.
<potoru-editor>
<script type="module">
import "@potoru/web"; // registers <potoru-editor> (and the player elements)
const editor = document.querySelector("potoru-editor");
editor.yaml = files; // compiles once, then on Compile
editor.addEventListener("potoru:compiled", (event) => console.log(event.detail.sizes));
</script>
<potoru-editor mode="yaml" style="height: 560px"></potoru-editor>A YAML / Script toggle, the file list, a text editor, diagnostics linked to file:line, a live
preview (<potoru-player>) and Compile / Download .poto. Attribute mode; properties yaml,
script, lastResult; methods getFiles(), setFiles(), getScript(), setScript(),
setMode(), compile(), download(); events potoru:compiled and potoru:error (the
compile() result). React: PotoEditor and compile in @potoru/react.
Loading
Everything heavy loads on first use from next to the entry: the action and score engines and
library code when a story needs them, the compiler on the first compile(), the editor UI when the
first editor connects, the authoring sandbox on the first Script-mode run. The compiler and the
player share their chunks, so a page that uses both loads the common runtime once.
Compatibility
Each release lists the capabilities it plays (PLAYER_CAPABILITIES). A package that declares a
capability or a container version the player does not know is refused before the first frame with
potoru:error code needs-newer-player (also shown in the player): update @potoru/web to
the version of the authoring tools that exported the package. Versions move in lockstep with
@potoru/authoring, @potoru/render and @potoru/react.
Hosting notes
- Chunks, wasm and fonts load next to
potoru.js(new URL(…, import.meta.url)): serve the wholedist/folder, with.wasmasapplication/wasm. - With an app bundler (Vite, webpack), the chunks, wasm and the music Worker are copied into your
build, but
dist/fonts/is not: copy it tofonts/next to your page (the elements andcompile()look there next), or setfont-baseto where you serve it. - Content Security Policy:
script-src 'self' 'wasm-unsafe-eval'(plus the CDN),worker-src 'self'for the music Worker (from another origin, music falls back to the main thread),connect-srcfor the hosts of your.potofiles, andfont-src/connect-srcforfonts/. Script mode inlines its sandbox into asrcdocframe, which inherits the page's policy: it needsscript-src 'unsafe-inline'there. - Browsers with ES2022, Canvas 2D,
OffscreenCanvasand WebAssembly (current Chrome, Edge, Firefox, Safari).compile({ yaml })also runs in Node 20 or newer.
Licence
Apache-2.0 (LICENSE, NOTICE). The wasm engines contain third-party Rust crates (tiny-skia under
BSD-3-Clause, rustybuzz and others) and dist/fonts/ holds three fonts under the SIL Open Font
License 1.1: see THIRD-PARTY-NOTICES.txt. "Potoru" is a trademark of The Potoru Authors.
