### Setup and run development: bun install and dev commands Source: https://github.com/whitetowerai/ohmygame/blob/main/CONTRIBUTING.md Install dependencies and start the development environment after ensuring Bun 1.4.2+ and Node.js 22.19+. Use 'bun run dev' for the web renderer with a local daemon, or 'bun run dev:desktop' for the Electron app. ```bash bun install --frozen-lockfile bun run dev # web renderer + local daemon bun run dev:desktop # Electron app ``` -------------------------------- ### graph.json example Source: https://github.com/whitetowerai/ohmygame/blob/main/docs/playable-nodes/runtime.md A complete example of a graph.json file showing the runtime structure: version, title, viewport, entryNodeId, initialState, assets, nodes with signals, and edges. The example includes two nodes (main-menu and archive) and four edges with modes 'replace' and 'push'. ```json { "version": 1, "title": "Ash Club", "viewport": { "width": 1920, "height": 1080 }, "entryNodeId": "main-menu", "initialState": { "roundsCompleted": 0, "foundClues": [] }, "assets": { "club-background": { "type": "image", "source": { "kind": "library", "assetId": "lib_8f2c…" } }, "archive-desk": { "type": "image", "source": { "kind": "workspace", "path": "assets/archive-desk.webp" } } }, "nodes": [ { "id": "main-menu", "title": "Main menu", "source": { "html": "nodes/main-menu/index.html", "css": "nodes/main-menu/style.css", "javascript": "nodes/main-menu/node.js" }, "assets": ["club-background"], "signals": [ { "id": "enter-club", "label": "进入俱乐部" }, { "id": "open-archive", "label": "查看旧案" } ] }, { "id": "archive", "title": "Archive", "source": { "html": "nodes/archive/index.html", "css": "nodes/archive/style.css", "javascript": "nodes/archive/node.js" }, "assets": ["archive-desk"], "signals": [ { "id": "home", "label": "首页", "role": "navigation" }, { "id": "rules", "label": "规则" } ] } ], "edges": [ { "id": "e1", "source": { "nodeId": "main-menu", "signal": "enter-club" }, "targetNodeId": "game-lobby", "mode": "replace" }, { "id": "e2", "source": { "nodeId": "main-menu", "signal": "open-archive" }, "targetNodeId": "archive", "mode": "push" }, { "id": "e3", "source": { "nodeId": "archive", "signal": "home" }, "targetNodeId": "main-menu", "mode": "replace" }, { "id": "e4", "source": { "nodeId": "archive", "signal": "rules" }, "targetNodeId": "game-rules", "mode": "push" } ] } ``` -------------------------------- ### mount function example Source: https://github.com/whitetowerai/ohmygame/blob/main/docs/playable-nodes/runtime.md Shows the required export of a node: a mount function that receives a context. It uses context.root.querySelector, context.navigation.emit, context.state.get, and context.lifecycle.signal to handle a click event. ```javascript export function mount(context) { const open = context.root.querySelector("[data-open]"); const onClick = async () => { await context.navigation.emit( context.state.get("hasKey") ? "door-opened" : "door-locked", ); }; open.addEventListener("click", onClick, { signal: context.lifecycle.signal }); } ``` -------------------------------- ### playable_add_node JSON example Source: https://github.com/whitetowerai/ohmygame/blob/main/docs/playable-nodes/agent.md Example JSON payload for the playable_add_node tool, which creates a node from a preset. The preset 'choice' is used with id 'conductor' and title 'The conductor'. ```json { "preset": "choice", "id": "conductor", "title": "The conductor" } ``` -------------------------------- ### Run test ablation with npm commands Source: https://github.com/whitetowerai/ohmygame/blob/main/docs/experiments/test-ablation-2026-10-07.md Commands to run the test ablation script. The first command performs a dry run, the second runs the full ablation, the third runs a single repetition for specific mutants, and the fourth resumes from a checkpoint. Requires dependencies installed and the cloud repository prepared or OHMYGAME_CLOUD_ROOT set. ```bash npm run test:ablation -- --dry-run npm run test:ablation npm run test:ablation -- --runs=1 --mutant=D1,R3 # Resume a checkpoint with the same inputs, mutation selection and repetition count: npm run test:ablation -- --resume=.data/test-ablation//results.json ``` -------------------------------- ### reset() Source: https://github.com/whitetowerai/ohmygame/blob/main/docs/playable-nodes/runtime.md Restores the initial state and clears the back stack without leaving the current node. A menu starts a new game with reset() then its start Signal. ```APIDOC ## reset() ### Description Restores the initial state and clears the back stack without leaving the current node. A menu starts a new game with reset() then its start Signal. ### Method reset ### Parameters None ### Returns None ``` -------------------------------- ### playable_check JSON example Source: https://github.com/whitetowerai/ohmygame/blob/main/docs/playable-nodes/agent.md Example JSON payload for the playable_check tool, which validates the graph and compiles nodes. The mode 'draft' is used. ```json { "mode": "draft" } ``` -------------------------------- ### game_use Source: https://github.com/whitetowerai/ohmygame/blob/main/docs/playable-nodes/agent.md The existing game_use tool plays Playable Nodes projects. open builds the current sources into a draft of the Published Player, served on loopback under an unguessable path, and opens it in the Playtest window. Snapshots read text and interactive elements inside the Player's frames and node ShadowRoots, so the snapshot's text is what the screen shows. Its gameState is the Playtest debug record: current node, back stack, recent Signals and whether an edge followed them, State, State changes, errors, and save status. The bridge reset action starts a new game without the save. Only drafts expose the bridge; a published game does not. act drives real input; capture returns a screenshot. ```APIDOC ## game_use ### Description The existing `game_use` tool plays Playable Nodes projects. `open` builds the current sources into a draft of the Published Player, served on loopback under an unguessable path, and opens it in the Playtest window. Snapshots read text and interactive elements inside the Player's frames and node ShadowRoots, so the snapshot's text is what the screen shows. Its `gameState` is the Playtest debug record: current node, back stack, recent Signals and whether an edge followed them, State, State changes, errors, and save status. The bridge `reset` action starts a new game without the save. Only drafts expose the bridge; a published game does not. `act` drives real input; `capture` returns a screenshot. ### Actions - **open** - Builds the current sources into a draft of the Published Player and opens it in the Playtest window. - **reset** - Starts a new game without the save (bridge action, only available in drafts). - **act** - Drives real input. - **capture** - Returns a screenshot. ### Response Snapshots contain text and interactive elements inside the Player's frames and node ShadowRoots. `gameState` includes current node, back stack, recent Signals and whether an edge followed them, State, State changes, errors, and save status. ``` -------------------------------- ### Save slot JSON example Source: https://github.com/whitetowerai/ohmygame/blob/main/docs/playable-nodes/runtime.md Shows the structure of a single save slot, including version, graphSignature, savedAt, currentNodeId, backStack, and state. The Runtime checkpoints after navigation and state changes; session.save() flushes writes. ```json { "version": 1, "graphSignature": "…", "savedAt": "…", "currentNodeId": "archive", "backStack": ["main-menu"], "state": {} } ``` -------------------------------- ### Update feed URLs for desktop releases Source: https://github.com/whitetowerai/ohmygame/blob/main/docs/desktop-releases.md Example URLs for the update feed hosted on a custom domain with the prefix 'desktop'. These URLs must be publicly accessible without cookies or embedded credentials. ```text https://updates.ohmygame.ai/desktop/latest.yml https://updates.ohmygame.ai/desktop/latest-mac.yml https://updates.ohmygame.ai/desktop/OhMyGame-0.1.0-x64.exe ``` -------------------------------- ### mount(context) Source: https://github.com/whitetowerai/ohmygame/blob/main/docs/playable-nodes/runtime.md The mount function is the entry point for every node. It receives a context object and is called when the node is mounted. It may be async and can return a cleanup function. ```APIDOC ## mount(context) ### Description The `mount` function is the entry point for every node. It is called with a `RuntimeContext` (or `NodeContext`) and is responsible for setting up the node's behavior. It may be async and can return a cleanup function (sync or async) that is called when the node is unmounted. ### Method Function ### Signature ```js export function mount(context) { ... } ``` ### Parameters - **context** (RuntimeContext | NodeContext) - Required - The runtime context providing access to the DOM root, assets, state, session, story, lifecycle, and navigation. ### Returns - **cleanup** (function | Promise | void) - Optional - A cleanup function to be called when the node is unmounted. ### Example ```js export function mount(context) { const open = context.root.querySelector("[data-open]"); const onClick = async () => { await context.navigation.emit( context.state.get("hasKey") ? "door-opened" : "door-locked", ); }; open.addEventListener("click", onClick, { signal: context.lifecycle.signal }); } ``` ### Context Interface ```ts interface RuntimeContext { root: ShadowRoot; assets: { url(id: string): string }; state: { get(): Readonly; get(key: string): JsonValue; set(key: string, value: JsonValue): Promise; patch(values: JsonObject): Promise; subscribe(listener: (state: Readonly) => void): () => void; }; session: { hasSave(): boolean; save(): Promise; continue(): Promise; reset(): Promise; restart(): Promise; }; story: { map(): StoryMap }; lifecycle: { signal: AbortSignal }; } interface NodeContext extends RuntimeContext { navigation: { emit(signal: string): Promise; back(): Promise; connected(signal: string): boolean; }; } ``` ### Notes - Every request is validated. An undeclared asset, unknown state key, or undeclared Signal produces a visible runtime error. - A node emits only its own declared Signals or goes back, so story flow stays visible in the graph. ``` -------------------------------- ### Run pre-PR checks: test, typecheck, build, node-runtime, prettier Source: https://github.com/whitetowerai/ohmygame/blob/main/CONTRIBUTING.md Run the full check suite before opening a PR. Replace with the actual files you modified; the prettier command is a no-install check. ```bash bun run test bun run typecheck bun run build bun run test:node-runtime bunx --bun --no-install prettier --check ``` -------------------------------- ### restart() Source: https://github.com/whitetowerai/ohmygame/blob/main/docs/playable-nodes/runtime.md Resets the game and enters the entryNodeId. ```APIDOC ## restart() ### Description Resets the game and enters the entryNodeId. ### Method restart ### Parameters None ### Returns None ``` -------------------------------- ### playable_add_node Source: https://github.com/whitetowerai/ohmygame/blob/main/docs/playable-nodes/agent.md Creates a node from a Preset, exactly as the editor does. It copies the Preset source into nodes//, adds the node with the Preset's starter Signals to graph.json, and returns the created paths and the Preset brief. ```APIDOC ## playable_add_node ### Description Creates a node from a Preset, exactly as the editor does. It copies the Preset source into `nodes//`, adds the node with the Preset's starter Signals to `graph.json`, and returns the created paths and the Preset brief. ### Parameters #### Request Body - **preset** (string) - Required - The Preset identifier. Blank is available as `"blank"`. - **id** (string) - Required - The node ID. - **title** (string) - Required - The node title. ### Request Example ```json { "preset": "choice", "id": "conductor", "title": "The conductor" } ``` ### Response Returns the created paths and the Preset brief. ``` -------------------------------- ### Configure Supabase sign-in: VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY Source: https://github.com/whitetowerai/ohmygame/blob/main/CONTRIBUTING.md Set these public values in .env.local to enable real Supabase authentication and cloud publishing. If either is missing, localhost dev automatically uses a debug account; production builds never enable the fallback. ```dotenv VITE_SUPABASE_URL=https://your-project.supabase.co VITE_SUPABASE_PUBLISHABLE_KEY=sb_publishable_your-key ``` -------------------------------- ### Project directory layout Source: https://github.com/whitetowerai/ohmygame/blob/main/docs/runtime.md Shows the structure of a project directory under the daemon data directory, including project.json, cover images, workspace, and session folders. ```text projects// ├── project.json project identity and latest publication ├── cover.webp custom cover; its presence stops automatic replacement ├── cover-auto.webp last automatic cover, retained when a custom cover is applied ├── workspace/ the project files (or an existing folder in the desktop app) └── session/ Pi session files, one per conversation ``` -------------------------------- ### Run migration script Source: https://github.com/whitetowerai/ohmygame/blob/main/docs/canvas-agent.md Stops OhMyGame and runs the migration script against the data directory. The script backs up old Design directories and Asset Canvas files under /canvas-backups/, separates layout files, and materializes Library references. IDs, Markdown, media, and layout are preserved. Current projects are left alone. ```sh bun scripts/migrate-canvas-workspaces.ts .data ``` -------------------------------- ### Set local cloud API URL: CLOUD_API_URL Source: https://github.com/whitetowerai/ohmygame/blob/main/CONTRIBUTING.md Point the client at a local or preview deployment of the ohmygame-cloud Publish v1 service. Set this in .env.local to test against a local cloud. ```dotenv CLOUD_API_URL=http://127.0.0.1:43130 ``` -------------------------------- ### Mount shared top bar component in a node Source: https://github.com/whitetowerai/ohmygame/blob/main/docs/playable-nodes/runtime.md Imports the shared top-bar component and mounts it with the node's context and a list of signal names. The node must declare the component's Signals and route them with its own edges. ```javascript // nodes/archive/node.js import { mountTopBar } from "../../shared/components/top-bar.js"; export function mount(context) { return mountTopBar(context, ["home", "rules"]); } ``` -------------------------------- ### Sandbox hierarchy diagram Source: https://github.com/whitetowerai/ohmygame/blob/main/docs/playable-nodes/runtime.md Shows the structure of the Player host, iframe sandbox, and node layer. The iframe uses sandbox="allow-scripts" and an opaque origin with strict CSP. ```text Player host (editor, Playtest, or Published Player) └── iframe sandbox="allow-scripts" (opaque origin, strict CSP) └── sandbox document ├── Node Runtime └── Node layer → ShadowRoot → current node surface ``` -------------------------------- ### context.story.map() Source: https://github.com/whitetowerai/ohmygame/blob/main/docs/playable-nodes/runtime.md Returns the story laid out for a player to see, with what they have seen. The layout comes from the graph alone, never from editor positions, so it holds at any screen size and arranging the canvas does not change it. ```APIDOC ## context.story.map() ### Description Returns the story laid out for a player to see, with what they have seen. The layout comes from the graph alone, never from editor positions, so it holds at any screen size and arranging the canvas does not change it. ### Method map ### Parameters None ### Returns StoryMap ### Response Example ```ts interface StoryMap { nodes: { id: string; label: string; row: number; column: number; rowSize: number; ending: boolean; seen: boolean }[]; edges: { from: string; to: string; seen: boolean }[]; } ``` ``` -------------------------------- ### session.save() Source: https://github.com/whitetowerai/ohmygame/blob/main/docs/playable-nodes/runtime.md Flushes any debounced writes to the save slot. The Runtime checkpoints after every successful navigation and committed state change, but writes may be debounced; calling save() forces a write. ```APIDOC ## session.save() ### Description Flushes any debounced writes to the save slot. The Runtime checkpoints after every successful navigation and committed state change, but writes may be debounced; calling save() forces a write. ### Method save ### Parameters None ### Returns None ``` -------------------------------- ### playable_check Source: https://github.com/whitetowerai/ohmygame/blob/main/docs/playable-nodes/agent.md Validates the graph and compiles every node and the shared modules it imports. It returns the same issues as the validation endpoint, each with phase, code, path, message, and the surface when known, including compiler errors. ```APIDOC ## playable_check ### Description Validates the graph and compiles every node and the shared modules it imports. It returns the same issues as the validation endpoint, each with `phase`, `code`, `path`, `message`, and the surface when known, including compiler errors. The Agent fixes every issue before finishing. `publish` mode additionally requires every Signal other than navigation ones to be connected and every source file to exist. ### Parameters #### Request Body - **mode** (string) - Required - The validation mode. Can be `"draft"` or `"publish"`. ### Request Example ```json { "mode": "draft" } ``` ### Response Returns the same issues as the validation endpoint, each with `phase`, `code`, `path`, `message`, and the surface when known, including compiler errors. ``` -------------------------------- ### continue() Source: https://github.com/whitetowerai/ohmygame/blob/main/docs/playable-nodes/runtime.md Restores the save. The graphSignature hashes graph.json, so a code-only edit keeps the save. A save whose signature no longer matches is reported as incompatible (save.incompatible) rather than loaded partially. ```APIDOC ## continue() ### Description Restores the save. The graphSignature hashes graph.json, so a code-only edit keeps the save. A save whose signature no longer matches is reported as incompatible (save.incompatible) rather than loaded partially. ### Method continue ### Parameters None ### Returns None ``` -------------------------------- ### TypeScript declaration for the playtest bridge Source: https://github.com/whitetowerai/ohmygame/blob/main/docs/runtime.md Declares the global Window interface with optional playtest methods: snapshot, reset, setSeed, and step. The bridge is only active when the ohmygamePlaytest query parameter is present. Games must remain fully playable without this bridge. ```TypeScript declare global { interface Window { __OHMYGAME_PLAYTEST__?: { snapshot?: () => unknown | Promise; reset?: () => void | Promise; setSeed?: (seed: number) => void | Promise; step?: (milliseconds: number) => void | Promise; }; } } export {}; ``` -------------------------------- ### RuntimeContext and NodeContext interfaces Source: https://github.com/whitetowerai/ohmygame/blob/main/docs/playable-nodes/runtime.md TypeScript interfaces defining the runtime context available to nodes. RuntimeContext includes root, assets, state, session, story, and lifecycle. NodeContext extends RuntimeContext with navigation methods. ```typescript interface RuntimeContext { root: ShadowRoot; assets: { url(id: string): string }; state: { get(): Readonly; get(key: string): JsonValue; set(key: string, value: JsonValue): Promise; patch(values: JsonObject): Promise; subscribe(listener: (state: Readonly) => void): () => void; }; session: { hasSave(): boolean; save(): Promise; continue(): Promise; reset(): Promise; restart(): Promise; }; story: { map(): StoryMap }; lifecycle: { signal: AbortSignal }; } interface NodeContext extends RuntimeContext { navigation: { emit(signal: string): Promise; back(): Promise; connected(signal: string): boolean; }; } ``` -------------------------------- ### StoryMap interface Source: https://github.com/whitetowerai/ohmygame/blob/main/docs/playable-nodes/runtime.md Defines the structure returned by context.story.map(), listing nodes and edges with seen flags. The layout is derived from the graph, not editor positions. ```ts interface StoryMap { nodes: { id: string; label: string; row: number; column: number; rowSize: number; ending: boolean; seen: boolean }[]; edges: { from: string; to: string; seen: boolean }[]; } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.