@volter/editor-blender 0.1.151 → 0.1.153

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,173 +0,0 @@
1
- /**
2
- * THE MODEL DOCUMENT'S PLAY, which editor-blender owns and offers in its API (`registerModelPlay`,
3
- * `docs/adr/plugin-apis.md`). A Play tool owns its controls, state and runner; the model document
4
- * lends that runner its detached stage. play registers its tool from its own `activate`, through
5
- * the API; the host hears only whether a document plays (`@volter/sdk/document-play-extension`).
6
- */
7
- import { setDocumentPlaySignal } from '@volter/sdk/document-play-extension';
8
- import type { ComponentType } from 'react';
9
-
10
- export interface DocumentPlayControlProps {
11
- readonly documentId: string | undefined;
12
- readonly onClose: () => void;
13
- }
14
- /** Where the document's own area stands while its game runs (`DocumentPlayStage.edit`): a box is in fractions of the
15
- * stage. */
16
- export type DocumentPlayEditArea = 'live' | 'hidden' | { readonly left: number; readonly top: number; readonly width: number; readonly height: number };
17
- export interface DocumentPlayStage {
18
- readonly documentId: string;
19
- readonly sourcePath: string;
20
- readonly report: (title: string, detail: string) => void;
21
- /** Where the tool draws its game, over the document's own area. */
22
- readonly container: HTMLElement;
23
- /** The game's first frame stands. */
24
- readonly ready: () => void;
25
- /** THE DOCUMENT'S OWN AREA WHILE THE GAME RUNS ON: `live` brings the editing area back in front, live for editing
26
- * (its keys the editor's); a box shows it drawing in that part of the stage, taking no press (a tile among the
27
- * game's views); `hidden` puts the game in front again. */
28
- readonly edit?: (area: DocumentPlayEditArea) => void;
29
- /** A layer over the editing area and the play area alike, for the tool's view controls, which stand whichever area
30
- * is in front; it takes no press itself. */
31
- readonly overlay?: HTMLElement;
32
- }
33
-
34
- /**
35
- * A RUNNING GAME'S CLOCK, as the Play tool keeps it: the game's own seconds and frames, which stand still while paused
36
- * and run at `speed` times the page's own clock otherwise.
37
- */
38
- export interface DocumentPlayClock {
39
- /** The game's seconds since this run began. */
40
- readonly time: number;
41
- /** The frames the game has run since this run began. */
42
- readonly tick: number;
43
- readonly paused: boolean;
44
- /** Simulation seconds per real second; one of the transport's `speeds`. */
45
- readonly speed: number;
46
- /** While playing with no game running because the game failed to start or threw: why. The
47
- * run stays playing (a save of the game retries it); a running game clears it. */
48
- readonly failure?: string | null;
49
- /** A game has started in this run and is running (its first update ran). False while the run
50
- * is still preparing, and while it plays with a `failure`. A reader that must know whether Play
51
- * WORKED (the CLI's `play`) waits for this or for `failure`, never for "playing" alone. */
52
- readonly running: boolean;
53
- }
54
- /**
55
- * THE GAME'S TRANSPORT — what a Play tool offers beyond Play and Stop, for a layout that draws a
56
- * game panel (Cyclotron's Game mode, `@volter/editor-blender`). Optional: a tool without
57
- * one still plays and stops, and a layout draws only the controls a tool answers.
58
- */
59
- export interface DocumentPlayTransport {
60
- /** The speeds `setSpeed` accepts, slowest first. */
61
- readonly speeds: readonly number[];
62
- clock(documentId: string): DocumentPlayClock;
63
- /** The clock moves every played frame, so it has a subscription of its own: `subscribe` would
64
- * re-render everything that only asks whether a document plays. */
65
- subscribeClock(listener: () => void): () => void;
66
- /** Hold or release the game; the answer settles once the game has. */
67
- setPaused(documentId: string, paused: boolean): void | Promise<void>;
68
- /** `frames` frames (one when absent), pausing the game first. */
69
- step(documentId: string, frames?: number): void | Promise<void>;
70
- setSpeed(documentId: string, speed: number): void | Promise<void>;
71
- /** Begin the run again on a FRESH detached copy, the clock at zero. */
72
- restart(documentId: string): void;
73
- /** Changes on every restart. The document that lends the stage keys its detached copy on it,
74
- * so a new generation is a new copy; the change is announced through `subscribe`. */
75
- generation(documentId: string): number;
76
- /** Whether the running game's own bot drives the player in view, and the goals it offers; a change is announced
77
- * through `subscribeClock`. Absent, the tool has no autoplay. */
78
- autoplay?(documentId: string): DocumentPlayAutoplay;
79
- /** Switch the bot on (it must be offered, and the game playing) or off. On plays `goal` (the game's own when
80
- * absent); the answer settles once the goal has started. */
81
- setAutoplay?(documentId: string, on: boolean, by: 'panel' | 'cli', goal?: string): void | Promise<void>;
82
- /** While stopped, ask the next start to switch the bot on with `goal` as soon as its player joins. Absent, autoplay
83
- * can only be switched while running. */
84
- armAutoplay?(documentId: string, armed: boolean, goal?: string): void;
85
- }
86
- /**
87
- * AUTOPLAY, AS THE EDITOR OWNS IT: a game offers a bot and its goals, the editor decides whether it drives the player
88
- * in view. A person's own press in the game takes it back (`takeover`).
89
- */
90
- export interface DocumentPlayAutoplay {
91
- readonly on: boolean;
92
- /** The bot can be switched on for the player in view. */
93
- readonly available: boolean;
94
- /** Why the bot cannot be switched on now, in the tool's words (no goals, a view with no player, a player not yet
95
- * joined): the Game panel and `play state` say it. */
96
- readonly unavailable?: string | null;
97
- /** Who made the last change. */
98
- readonly by: 'panel' | 'cli' | 'takeover' | null;
99
- /** Armed while stopped (`armAutoplay`): the next start turns it on once its player joins. */
100
- readonly armed?: boolean;
101
- /** The goals the game's bot offers, and the one driving, armed, or the game's own. */
102
- readonly behaviors?: readonly string[];
103
- readonly behavior?: string | null;
104
- /** A goal played where the person is not looking (a std game's headless play), and how it stands: `<goal>:
105
- * playing`, `<goal>: achieved` or `<goal>: failed — why`; null before any. */
106
- readonly state?: string | null;
107
- }
108
- export interface DocumentPlayExtension {
109
- readonly Control: ComponentType<DocumentPlayControlProps>;
110
- readonly Menu: ComponentType<DocumentPlayControlProps>;
111
- /** The run's panel, which a document's layout shows in its bottom area while it plays, when the tool has one. */
112
- readonly Panel?: ComponentType;
113
- playing(documentId: string): boolean;
114
- /** Intended live frame shape; absent/null lets the document fill its area. */
115
- aspectRatio?(documentId: string): number | null;
116
- setPlaying(documentId: string, value: boolean): void;
117
- escape?(documentId: string): void;
118
- subscribe(listener: () => void): () => void;
119
- run(stage: DocumentPlayStage): () => void;
120
- /** Pause, step, speed, restart and the clock, when the tool keeps them. */
121
- readonly transport?: DocumentPlayTransport;
122
- /** What this tool runs to play a document's source file (a game's dev server command). */
123
- scriptPath?(sourcePath: string): string;
124
- /** Whether the document's source has a game to play: `null` while the tool has not looked yet. A change is announced
125
- * through `subscribe`. A layout uses it to open a document as a game or as a model. */
126
- hasScript?(sourcePath: string): boolean | null;
127
- }
128
-
129
- let registered: DocumentPlayExtension | null = null;
130
- const listeners = new Set<() => void>();
131
- function publish(): void { for (const listener of listeners) listener(); }
132
-
133
- /** The registered Play tool, or `null` while none is. */
134
- export function modelPlayExtension(): DocumentPlayExtension | null {
135
- return registered;
136
- }
137
- /** A registration, and every change the registered tool announces. */
138
- export function subscribeModelPlayExtension(listener: () => void): () => void {
139
- listeners.add(listener);
140
- return () => { listeners.delete(listener); };
141
- }
142
-
143
- /** Whether a document's Play runs and is paused, for the host. A pause is a clock change, and the
144
- * clock moves every played frame, so its subscribers are readers whose snapshot changes rarely. */
145
- function signal(extension: DocumentPlayExtension) {
146
- return {
147
- state(documentId: string): 'playing' | 'paused' | 'stopped' {
148
- if (!extension.playing(documentId)) return 'stopped';
149
- return extension.transport?.clock(documentId).paused ? 'paused' : 'playing';
150
- },
151
- subscribe(listener: () => void): () => void {
152
- const stopPlay = extension.subscribe(listener);
153
- const stopClock = extension.transport?.subscribeClock(listener);
154
- return () => { stopPlay(); stopClock?.(); };
155
- },
156
- };
157
- }
158
-
159
- /** The API's `registerModelPlay`: one Play tool for model documents at a time. */
160
- export function registerModelPlay(extension: DocumentPlayExtension): () => void {
161
- if (registered) throw new Error('A Play tool already plays model documents.');
162
- registered = extension;
163
- const stop = extension.subscribe(publish);
164
- setDocumentPlaySignal('model', signal(extension));
165
- publish();
166
- return () => {
167
- stop();
168
- if (registered !== extension) return;
169
- registered = null;
170
- setDocumentPlaySignal('model', null);
171
- publish();
172
- };
173
- }