Scenario system
A scenario is a JSON script that makes the desktop react to the player. For example, when the player opens a file, you can unlock a folder, show a notification, or write a new file — no React code required.
Hello world trigger
import { WindowsXP } from '@caoergou/windows-xp';
const scenario = {
id: 'hello',
triggers: [
{
id: 'open-readme',
on: 'file:open',
when: { event: { name: 'ReadMe.txt' } },
do: [{ notify: { title: 'ReadMe', body: 'You opened it!' } }],
},
],
};
<WindowsXP scenario={scenario} autoLogin />;A trigger has three parts:
on— the event to listen for (here, a file was opened).when— optional condition (here, the file name must beReadMe.txt).do— the list of actions to run.
A fuller example
Once you are comfortable with the basic shape, you can combine flags, conditions, and multiple actions to build a puzzle beat:
import { WindowsXP } from '@caoergou/windows-xp';
const scenario = {
id: 'prologue-v1',
initialFlags: { readLog: false },
triggers: [
{
id: 'read-chat-log',
on: 'file:open',
when: { event: { name: '聊天记录.txt' } },
once: true,
do: [
{ setFlag: 'readLog', value: true },
{ unlock: ['我的电脑', '本地磁盘 (C:)', 'WINDOWS'] },
{ qqOnline: 'crystal' },
],
},
],
};
<WindowsXP scenario={scenario} autoLogin />;Progress (flags, a bounded event journal, per-trigger fire counts, pending delayed after actions) persists per instance and resets when scenario.id changes; flags feed the snapshot flags slot. Full schema reference — every condition and action, once/max semantics, happened/count predicates, delayed actions, and a worked example — lives in docs/SCENARIOS.md.
Scenario DevTools
onEvent={console.log} already shows you what happened — so this panel doesn't duplicate an event stream. It surfaces the two things that live inside the engine and never reach the console: why a trigger didn't fire, and the current flags. Set devtools to mount an XP-styled overlay:
<WindowsXP scenario={scenario} devtools autoLogin />Two tabs:
- Triggers — for the most recent event, each registered trigger's outcome:
fired,no match(event type didn't matchon), or a skip reason. When a trigger matched but itswhenwas false, the condition tree is shown annotated ✓/✗ so the exact false predicate is obvious (e.g.✗ flag door_open (undefined) is truthy— the runtime only ever computes a single boolean, so this is otherwise invisible). - Flags — every current flag with its value and who last changed it (which event → which trigger).
It reads the trace the runtime publishes, is opt-in, and tree-shakes out of a production build that never sets devtools. Advanced hosts can mount <DevToolsPanel/> themselves or subscribe to subscribeTrace(prefix, …) to feed their own console logging or UI.