gesso-framework 0.6.30 → 0.8.0

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.
Files changed (48) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/dist/{FunctionComponent-CHtojpp7.d.ts → FunctionComponent-BDVrANP0.d.ts} +371 -115
  3. package/dist/{serveChannels-BlEag7WU.js → WorkerPorts-m_hLRoPj.js} +8 -240
  4. package/dist/WorkerPorts-m_hLRoPj.js.map +1 -0
  5. package/dist/agent/index.d.ts +55 -2
  6. package/dist/agent/index.js +8 -4
  7. package/dist/agent/index.js.map +1 -1
  8. package/dist/app-De3z58x7.js +638 -0
  9. package/dist/app-De3z58x7.js.map +1 -0
  10. package/dist/computed-DyW9SPpq.js +587 -0
  11. package/dist/computed-DyW9SPpq.js.map +1 -0
  12. package/dist/createComponent-DJads88q.js +47 -0
  13. package/dist/createComponent-DJads88q.js.map +1 -0
  14. package/dist/{index-DZbSb8IN.d.ts → index-DocVauCS.d.ts} +593 -119
  15. package/dist/index.d.ts +4 -5
  16. package/dist/index.js +609 -6641
  17. package/dist/index.js.map +1 -1
  18. package/dist/jsx/jsx-runtime.d.ts +8 -5
  19. package/dist/jsx/jsx-runtime.js +15 -6
  20. package/dist/jsx/jsx-runtime.js.map +1 -1
  21. package/dist/{persisted-fUzIU8FZ.js → persisted-qEBOHDnj.js} +4 -576
  22. package/dist/persisted-qEBOHDnj.js.map +1 -0
  23. package/dist/remote/index.d.ts +2 -2
  24. package/dist/remote/index.js +2 -1
  25. package/dist/remote/index.js.map +1 -1
  26. package/dist/{remote-BoB8zh7z.js → remote-B4bjdTzg.js} +19 -4
  27. package/dist/remote-B4bjdTzg.js.map +1 -0
  28. package/dist/render-W2XO444K.js +7310 -0
  29. package/dist/render-W2XO444K.js.map +1 -0
  30. package/dist/serveChannels-DReCmRxO.js +241 -0
  31. package/dist/serveChannels-DReCmRxO.js.map +1 -0
  32. package/dist/{createComponent-3n1ZSql8.js → show-D0MevHaV.js} +2 -46
  33. package/dist/show-D0MevHaV.js.map +1 -0
  34. package/dist/{ui-CHtNccR8.d.ts → tools-Bl8LZ7LR.d.ts} +351 -8
  35. package/dist/{webmcp-CdDq2nzS.js → webmcp-D7IlA0fu.js} +3 -3
  36. package/dist/webmcp-D7IlA0fu.js.map +1 -0
  37. package/dist/worker/index.d.ts +3 -4
  38. package/dist/worker/index.js +5 -3
  39. package/dist/worker/index.js.map +1 -1
  40. package/package.json +2 -2
  41. package/dist/ChannelProtocol-ByNoHujM.d.ts +0 -247
  42. package/dist/app-Yig59Ygf.js +0 -326
  43. package/dist/app-Yig59Ygf.js.map +0 -1
  44. package/dist/createComponent-3n1ZSql8.js.map +0 -1
  45. package/dist/persisted-fUzIU8FZ.js.map +0 -1
  46. package/dist/remote-BoB8zh7z.js.map +0 -1
  47. package/dist/serveChannels-BlEag7WU.js.map +0 -1
  48. package/dist/webmcp-CdDq2nzS.js.map +0 -1
@@ -1,93 +1,249 @@
1
- import { f as ChannelToken, o as Patch, r as ChannelPort } from "./ChannelProtocol-ByNoHujM.js";
2
1
  import { BehaviorSubject, Observable, Subject, Subscription } from "rxjs";
3
- import { LayoutBox, Reactive, UiChild, UiModifier } from "gesso-core";
4
- //#region src/Component.d.ts
2
+ import { LayoutBox, Reactive, UiChild, UiEnvironmentKey, UiModifier } from "gesso-core";
3
+ //#region src/channel/ChannelToken.d.ts
5
4
  /**
6
- * Base class for all Gesso framework components.
7
- *
8
- * Components are class-based. They declare reactive state with
9
- * an `internalState()` cell, inputs with `@Input()`, and return a UiElement tree
10
- * from `render()`. The framework calls `render()` once per mount;
11
- * after that, observable emissions in the returned tree drive updates.
5
+ * The barrier between the application and the view, declared once.
6
+ *
7
+ * A token is a name, a shape and an initial value — and no
8
+ * implementation at all. Both threads import it, which is the point:
9
+ * the module holding it has nothing in it to bundle, so an app's api
10
+ * client and domain logic never reach the render worker the way a
11
+ * shared `Store` class dragged them there.
12
+ *
13
+ * What the framework knows about a channel ends here. It diffs plain
14
+ * data and ships patches; where the observables came from — a single
15
+ * subject, or an api → repository → domain → view-model stack — is the
16
+ * application's business and the framework cannot tell the difference.
17
+ *
18
+ * export interface CatalogView {
19
+ * products: ProductRow[];
20
+ * status: 'loading' | 'ready' | 'error';
21
+ * }
22
+ * export interface CatalogCommands {
23
+ * addToCart(id: string): void;
24
+ * }
25
+ * export const Catalog = channel<CatalogView, CatalogCommands>('catalog', {
26
+ * products: [],
27
+ * status: 'loading'
28
+ * });
12
29
  */
13
- declare abstract class Component {
30
+ /**
31
+ * A command a component can send back across the barrier.
32
+ *
33
+ * The arguments are what cross: each is structured-cloned onto the
34
+ * owning thread, so they must be plain data. There may be as many as
35
+ * the command needs, which is why `move(from, to)` is written the way
36
+ * anyone would write it rather than as `move({ from, to })`; a command
37
+ * used to carry one payload and the second argument was dropped on the
38
+ * floor with a warning.
39
+ *
40
+ * Commands return nothing: the effect comes back as a patch, never as
41
+ * a return value, since there is no synchronous answer to be had
42
+ * across a thread.
43
+ */
44
+ type Command = (...args: never[]) => void;
45
+ /**
46
+ * The internal view of a command set: a bag of callables by name.
47
+ *
48
+ * Public signatures constrain to `object`, not to this. An application
49
+ * declares its commands as an ordinary interface —
50
+ * `interface CatalogCommands { addToCart(id: string): void }` — and an
51
+ * interface has no index signature, so it does not satisfy a
52
+ * `Record<string, …>` constraint however well it fits in spirit.
53
+ * Constraining to `object` accepts what people actually write; this
54
+ * alias is what the implementation casts to when it looks a command up
55
+ * by name.
56
+ */
57
+ type CommandMap = Record<string, Command>;
58
+ /** The declared barrier for one area of an application. */
59
+ interface ChannelToken<View extends object, Commands extends object = Record<string, never>> {
60
+ readonly name: string;
14
61
  /**
15
- * Called once after the component's UiNode subtree has been created
16
- * and all observable bindings are connected.
62
+ * What every view key holds before the first patch arrives.
63
+ *
64
+ * Required rather than optional, so the render thread never observes
65
+ * `undefined` for a declared key. A channel that is genuinely still
66
+ * loading says so in its own shape — a `status` field — rather than
67
+ * leaving the view to infer it from an absence.
17
68
  */
18
- onMount?(): void;
69
+ readonly initial: View;
70
+ /** Phantom, carrying the command types to `send`. Never read. */
71
+ readonly commands?: Commands;
72
+ }
73
+ /**
74
+ * Declares a channel.
75
+ *
76
+ * The name identifies it across the thread boundary and must be
77
+ * stable; unlike a class name it survives minification, which is why
78
+ * it is written out rather than derived.
79
+ */
80
+ declare function channel<View extends object, Commands extends object = Record<string, never>>(name: string, initial: View): ChannelToken<View, Commands>;
81
+ /** A channel written as one object: the view with its values, and the commands. */
82
+ interface ChannelSpec<View extends object, Commands extends object> {
19
83
  /**
20
- * Called once when the component is being removed from the graph.
84
+ * Every view key with the value it holds before the first patch.
85
+ *
86
+ * The type of the channel's view is the type of this object, so the
87
+ * keys and their initial values are written once instead of in an
88
+ * interface and again in a literal that has to agree with it.
21
89
  */
22
- onUnmount?(): void;
90
+ readonly view: View;
23
91
  /**
24
- * Returns the component's UI definition.
92
+ * The commands, as a type rather than as implementations.
25
93
  *
26
- * Called exactly once per component instance. Dynamic content is
27
- * expressed through Observable props and children, never by
28
- * re-invoking render().
29
- *
30
- * Returning an Observable is allowed for structural changes that
31
- * cannot be expressed as observable children (routing, for example).
32
- * The framework reconciles the component's root on each emission.
94
+ * Written `{} as { addToCart(id: string, quantity: number): void }`:
95
+ * the handlers live on the thread that owns the data and are passed
96
+ * to `provide`, so what belongs in the token is only their shape.
33
97
  */
34
- abstract render(): UiChild;
98
+ readonly commands?: Commands;
35
99
  }
36
- //#endregion
37
- //#region src/InternalState.d.ts
38
100
  /**
39
- * A component's own state: originated here, and never crossing the
40
- * barrier.
41
- *
42
- * The writable counterpart to `InputCell`. The two are the same
43
- * `BehaviorSubject` and differ by one accessor — this one has a
44
- * `.value` setter — and that difference is the whole semantics:
45
- *
46
- * internalState() never crosses I write it
47
- * input() crosses inward someone else writes it
101
+ * Declares a channel from one object.
102
+ *
103
+ * export const Catalog = defineChannel('catalog', {
104
+ * view: {
105
+ * products: [] as readonly ProductRow[],
106
+ * status: 'loading' as ShelfStatus
107
+ * },
108
+ * commands: {} as {
109
+ * addToCart(id: string, quantity: number): void;
110
+ * move(from: number, to: number): void;
111
+ * }
112
+ * });
113
+ *
114
+ * export type CatalogView = ViewOf<typeof Catalog>;
115
+ *
116
+ * The same token `channel()` returns, declared once instead of three
117
+ * times. `channel<View, Commands>(name, initial)` wrote the view as an
118
+ * interface, then as an initial literal that had to agree with it, and
119
+ * a key added to one and forgotten in the other was a type error in a
120
+ * third file. Here the object is the type.
121
+ *
122
+ * A field whose initial value is narrower than the type it holds is
123
+ * given the type it holds: `[]` is `never[]` and `'loading'` is
124
+ * `string` unless it is said, which is what the `as` clauses above are
125
+ * for. `ViewOf` and `CommandsOf` name the resulting types wherever the
126
+ * application used to name its own interface.
127
+ *
128
+ * `channel()` is not deprecated and keeps working exactly as it did.
129
+ * An application with interfaces it wants to keep, because they are
130
+ * shared with something else or because the initial values are built
131
+ * elsewhere, has nothing to migrate.
132
+ */
133
+ declare function defineChannel<View extends object, Commands extends object = Record<string, never>>(name: string, spec: ChannelSpec<View, Commands>): ChannelToken<View, Commands>;
134
+ /** The view type of a channel token, for an application that wants to name it. */
135
+ type ViewOf<T> = T extends ChannelToken<infer View, object> ? View : never;
136
+ /** The command type of a channel token. */
137
+ type CommandsOf<T> = T extends ChannelToken<object, infer Commands> ? Commands : never;
138
+ /**
139
+ * The keys a channel publishes.
48
140
  *
49
- * Named on that axis deliberately. It used to be `state()`, which
50
- * described *what* a thing was while `input()` described *where it
51
- * came from*; two names on two axes made neither of them tell you
52
- * anything about the other. `internalState(products)` reads as a
53
- * mistake at the call site in a way `state(products)` never did.
141
+ * Structural in its parameter rather than generic over the token, so
142
+ * it does not have to agree with any particular command type to read
143
+ * what is only ever the initial value's shape.
144
+ */
145
+ declare function viewKeys(token: {
146
+ initial: object;
147
+ }): string[];
148
+ //#endregion
149
+ //#region src/channel/StorePatch.d.ts
150
+ type PatchPath = readonly (string | number)[];
151
+ /**
152
+ * A change to one projection, as sent from a data worker to a replica.
54
153
  *
55
- * For values that originate on this thread and die with the component:
56
- * a tooltip's open flag, a caret, a scroll offset, the active tab.
57
- * Anything that survives a reload, or that another screen cares about,
58
- * is application state and belongs on a channel. Anything derived from
59
- * other cells is a `computed`.
154
+ * Paths are relative to the projection's root value, so a patch is
155
+ * self-contained: the replica never needs the previous value to apply
156
+ * one, only the value it already holds.
157
+ */
158
+ type Patch = {
159
+ op: 'set';
160
+ projection: string;
161
+ path: PatchPath;
162
+ value: unknown;
163
+ } | {
164
+ op: 'delete';
165
+ projection: string;
166
+ path: PatchPath;
167
+ } | {
168
+ op: 'splice';
169
+ projection: string;
170
+ path: PatchPath;
171
+ index: number;
172
+ deleteCount: number;
173
+ items: readonly unknown[];
174
+ };
175
+ /**
176
+ * Describes how to turn `previous` into `current` for one projection.
60
177
  *
61
- * It is not only for components. The thread that owns a channel's data
62
- * writes cells too, and wrote them as a `BehaviorSubject` mirrored
63
- * into an `asObservable()` because this was reachable only through the
64
- * framework's main entry. `gesso-framework/worker` is the same cell
65
- * with none of the renderer behind it, so an application worker holds
66
- * one cell rather than a subject and a copy of it, and reads it with
67
- * `.value` in a `computed` rather than listing it in a
68
- * `combineLatest`. "Internal" still means what it says there: written
69
- * here, and crossing the barrier only as the plain data a view key
70
- * publishes.
178
+ * Returns an empty list when nothing changed, which is the common case
179
+ * and the reason this exists: the point of a projection is that most
180
+ * state changes do not alter it, and the ones that do usually alter a
181
+ * small part.
71
182
  */
72
- declare class InternalState<T> extends BehaviorSubject<T> {
73
- /** What to call this cell in a warning or the inspector; optional. */
74
- label: string | undefined;
75
- constructor(initialValue: T);
76
- get value(): T;
77
- set value(next: T);
183
+ declare function diffProjection(projection: string, previous: unknown, current: unknown): Patch[];
184
+ /**
185
+ * Applies patches to a projection value, sharing structure with the
186
+ * original everywhere the patch did not reach.
187
+ *
188
+ * Nothing handed in is mutated: bindings hold onto emitted values, so a
189
+ * replica that edited in place would change data a component already
190
+ * rendered.
191
+ *
192
+ * **Each container on a patched path is copied once per batch, not once
193
+ * per patch.** A batch of N patches into one K-key object used to cost
194
+ * N × K: every patch spread the whole object again to change one key.
195
+ * A snapshot keyed by id is exactly that shape — gessologic published
196
+ * `Record<netId, 0 | 1>` for ten thousand nets, about nine hundred
197
+ * patches a publish, and the render worker's patch phase fell minutes
198
+ * behind a 60 Hz stream it could never catch. So the batch remembers
199
+ * the containers it has copied, and writes into those in place: they
200
+ * are its own, made during this call and seen by nobody yet. Values
201
+ * that arrived inside a patch are never written into, because they are
202
+ * the patch's — a devtools log replays the same patch objects again.
203
+ */
204
+ declare function applyPatches(root: unknown, patches: readonly Patch[]): unknown;
205
+ declare function applyPatch(root: unknown, patch: Patch): unknown;
206
+ //#endregion
207
+ //#region src/channel/ChannelProtocol.d.ts
208
+ /**
209
+ * A two-way channel endpoint. `MessagePort` satisfies it, as does a
210
+ * test double, so nothing in the channel layer depends on Worker.
211
+ */
212
+ interface ChannelPort {
213
+ postMessage(message: unknown): void;
214
+ onmessage: ((event: {
215
+ data: unknown;
216
+ }) => void) | null;
78
217
  }
79
218
  /**
80
- * Creates a reactive state cell.
81
- *
82
- * Usage inside a component:
83
- *
84
- * private readonly count = state(0);
85
- *
86
- * increment() {
87
- * this.count.value++;
88
- * }
219
+ * Render thread → the thread that owns the channel.
220
+ *
221
+ * A command's first argument stayed `payload` when commands learned to
222
+ * take more than one, and the rest travel beside it. That is not
223
+ * tidiness: an older view talking to a newer application worker sends
224
+ * no `rest`, which reads as the one-argument call it is, and an older
225
+ * application worker ignores the field, which is exactly what it did
226
+ * before the field existed.
89
227
  */
90
- declare function internalState<T>(initialValue: T, label?: string): InternalState<T>;
228
+ type ChannelClientMessage = {
229
+ type: 'channel:sync';
230
+ } | {
231
+ type: 'channel:command';
232
+ command: string;
233
+ payload: unknown;
234
+ rest?: unknown[];
235
+ };
236
+ /** The owning thread → render thread. */
237
+ type ChannelHostMessage = {
238
+ type: 'channel:patch';
239
+ patches: Patch[];
240
+ } | {
241
+ type: 'channel:error';
242
+ message: string;
243
+ stack?: string;
244
+ };
245
+ declare function isChannelClientMessage(value: unknown): value is ChannelClientMessage;
246
+ declare function isChannelHostMessage(value: unknown): value is ChannelHostMessage;
91
247
  //#endregion
92
248
  //#region src/Input.d.ts
93
249
  /**
@@ -220,48 +376,6 @@ declare function into<V>(target: {
220
376
  }): OutputTarget<V>;
221
377
  declare function isOutputTarget(value: unknown): value is OutputTarget<unknown>;
222
378
  //#endregion
223
- //#region src/bounds.d.ts
224
- /**
225
- * A node's box, as a cell.
226
- *
227
- * `ctx.bounds()` hands one out and the modifier on it fills it, so the
228
- * arithmetic a pointer position needs starts from a value rather than
229
- * from `new BehaviorSubject<LayoutBox>({ x: 0, y: 0, width: 0, height: 0 })`
230
- * and a `measure(subject)` beside it. It is an ordinary cell: read it
231
- * with `.value` in a handler, bind it, or derive from it.
232
- *
233
- * const track = ctx.bounds();
234
- * <box modifiers={[track.modifier]} onPointerMove={e => at(e.x - track.value.x)} />
235
- *
236
- * **It reports a move, and only a move.** `measure` is built on
237
- * `LayoutNotifier`, which fires for every layout fact a node's listeners
238
- * could care about, and one of those is a scroll offset that changed
239
- * while the box stayed exactly where it was. A
240
- * cell that emitted for those would wake everything derived from it on
241
- * every frame of every scroll, for a box that did not move, which is
242
- * the defect this exists to avoid. So an equal box is dropped here,
243
- * where the comparison is four numbers, rather than by a
244
- * `distinctUntilChanged` at each of the places that read it.
245
- *
246
- * There is no `ctx.bounds(ref)`. A box is reported by a modifier, which
247
- * is how a node is reached from outside the layout engine everywhere
248
- * else in this framework, and a ref would be a second way of naming the
249
- * same node with nothing else built on it.
250
- */
251
- declare class BoundsCell extends InternalState<LayoutBox> {
252
- /** Put this on the element whose box the cell should hold. */
253
- readonly modifier: UiModifier<Subject<LayoutBox>>;
254
- constructor();
255
- /** Writes a box, unless it is the one already held. */
256
- next(box: LayoutBox): void;
257
- }
258
- /**
259
- * A bounds cell outside a component, for a class component or a test.
260
- * Inside a function component `ctx.bounds()` is the same thing, and it
261
- * completes with the component.
262
- */
263
- declare function bounds(label?: string): BoundsCell;
264
- //#endregion
265
379
  //#region src/channel/ChannelReplica.d.ts
266
380
  /**
267
381
  * The render thread's end of a channel.
@@ -326,6 +440,136 @@ declare class ChannelReplica<View extends object, Commands extends object> {
326
440
  dispose(): void;
327
441
  }
328
442
  //#endregion
443
+ //#region src/InternalState.d.ts
444
+ /**
445
+ * A component's own state: originated here, and never crossing the
446
+ * barrier.
447
+ *
448
+ * The writable counterpart to `InputCell`. The two are the same
449
+ * `BehaviorSubject` and differ by one accessor — this one has a
450
+ * `.value` setter — and that difference is the whole semantics:
451
+ *
452
+ * internalState() never crosses I write it
453
+ * input() crosses inward someone else writes it
454
+ *
455
+ * Named on that axis deliberately. It used to be `state()`, which
456
+ * described *what* a thing was while `input()` described *where it
457
+ * came from*; two names on two axes made neither of them tell you
458
+ * anything about the other. `internalState(products)` reads as a
459
+ * mistake at the call site in a way `state(products)` never did.
460
+ *
461
+ * For values that originate on this thread and die with the component:
462
+ * a tooltip's open flag, a caret, a scroll offset, the active tab.
463
+ * Anything that survives a reload, or that another screen cares about,
464
+ * is application state and belongs on a channel. Anything derived from
465
+ * other cells is a `computed`.
466
+ *
467
+ * It is not only for components. The thread that owns a channel's data
468
+ * writes cells too, and wrote them as a `BehaviorSubject` mirrored
469
+ * into an `asObservable()` because this was reachable only through the
470
+ * framework's main entry. `gesso-framework/worker` is the same cell
471
+ * with none of the renderer behind it, so an application worker holds
472
+ * one cell rather than a subject and a copy of it, and reads it with
473
+ * `.value` in a `computed` rather than listing it in a
474
+ * `combineLatest`. "Internal" still means what it says there: written
475
+ * here, and crossing the barrier only as the plain data a view key
476
+ * publishes.
477
+ */
478
+ declare class InternalState<T> extends BehaviorSubject<T> {
479
+ /** What to call this cell in a warning or the inspector; optional. */
480
+ label: string | undefined;
481
+ constructor(initialValue: T);
482
+ get value(): T;
483
+ set value(next: T);
484
+ }
485
+ /**
486
+ * Creates a reactive state cell.
487
+ *
488
+ * Usage inside a component:
489
+ *
490
+ * private readonly count = state(0);
491
+ *
492
+ * increment() {
493
+ * this.count.value++;
494
+ * }
495
+ */
496
+ declare function internalState<T>(initialValue: T, label?: string): InternalState<T>;
497
+ //#endregion
498
+ //#region src/bounds.d.ts
499
+ /**
500
+ * A node's box, as a cell.
501
+ *
502
+ * `ctx.bounds()` hands one out and the modifier on it fills it, so the
503
+ * arithmetic a pointer position needs starts from a value rather than
504
+ * from `new BehaviorSubject<LayoutBox>({ x: 0, y: 0, width: 0, height: 0 })`
505
+ * and a `measure(subject)` beside it. It is an ordinary cell: read it
506
+ * with `.value` in a handler, bind it, or derive from it.
507
+ *
508
+ * const track = ctx.bounds();
509
+ * <box modifiers={[track.modifier]} onPointerMove={e => at(e.x - track.value.x)} />
510
+ *
511
+ * **It reports a move, and only a move.** `measure` is built on
512
+ * `LayoutNotifier`, which fires for every layout fact a node's listeners
513
+ * could care about, and one of those is a scroll offset that changed
514
+ * while the box stayed exactly where it was. A
515
+ * cell that emitted for those would wake everything derived from it on
516
+ * every frame of every scroll, for a box that did not move, which is
517
+ * the defect this exists to avoid. So an equal box is dropped here,
518
+ * where the comparison is four numbers, rather than by a
519
+ * `distinctUntilChanged` at each of the places that read it.
520
+ *
521
+ * There is no `ctx.bounds(ref)`. A box is reported by a modifier, which
522
+ * is how a node is reached from outside the layout engine everywhere
523
+ * else in this framework, and a ref would be a second way of naming the
524
+ * same node with nothing else built on it.
525
+ */
526
+ declare class BoundsCell extends InternalState<LayoutBox> {
527
+ /** Put this on the element whose box the cell should hold. */
528
+ readonly modifier: UiModifier<Subject<LayoutBox>>;
529
+ constructor();
530
+ /** Writes a box, unless it is the one already held. */
531
+ next(box: LayoutBox): void;
532
+ }
533
+ /**
534
+ * A bounds cell outside a component, for a class component or a test.
535
+ * Inside a function component `ctx.bounds()` is the same thing, and it
536
+ * completes with the component.
537
+ */
538
+ declare function bounds(label?: string): BoundsCell;
539
+ //#endregion
540
+ //#region src/Component.d.ts
541
+ /**
542
+ * Base class for all Gesso framework components.
543
+ *
544
+ * Components are class-based. They declare reactive state with
545
+ * an `internalState()` cell, inputs with `@Input()`, and return a UiElement tree
546
+ * from `render()`. The framework calls `render()` once per mount;
547
+ * after that, observable emissions in the returned tree drive updates.
548
+ */
549
+ declare abstract class Component {
550
+ /**
551
+ * Called once after the component's UiNode subtree has been created
552
+ * and all observable bindings are connected.
553
+ */
554
+ onMount?(): void;
555
+ /**
556
+ * Called once when the component is being removed from the graph.
557
+ */
558
+ onUnmount?(): void;
559
+ /**
560
+ * Returns the component's UI definition.
561
+ *
562
+ * Called exactly once per component instance. Dynamic content is
563
+ * expressed through Observable props and children, never by
564
+ * re-invoking render().
565
+ *
566
+ * Returning an Observable is allowed for structural changes that
567
+ * cannot be expressed as observable children (routing, for example).
568
+ * The framework reconciles the component's root on each emission.
569
+ */
570
+ abstract render(): UiChild;
571
+ }
572
+ //#endregion
329
573
  //#region src/FunctionComponent.d.ts
330
574
  /**
331
575
  * What a functional component can ask of the framework while its body
@@ -392,6 +636,18 @@ interface ComponentContext {
392
636
  * <box modifiers={[track.modifier]} onPointerDown={e => seek(e.x - track.value.x)} />
393
637
  */
394
638
  bounds(label?: string): BoundsCell;
639
+ /**
640
+ * What the component is placed under for `key`: the nearest value a
641
+ * node above it provides (`provide={[provided(key, value)]}`), else
642
+ * the key's default.
643
+ *
644
+ * Read as the body runs, from where the component is placed, so it
645
+ * is a value rather than a stream: a component that has to follow a
646
+ * change is given a source to follow (a cell) rather than each value.
647
+ * A design tool tells the screen it plays from the same screen drawn
648
+ * on its board this way.
649
+ */
650
+ environment<T>(key: UiEnvironmentKey<T>): T;
395
651
  }
396
652
  /**
397
653
  * The inputs a functional component receives: one host-owned cell per
@@ -504,5 +760,5 @@ type ComponentArgs<C> = RequiredKeys<ComponentProps<C>> extends never ? [inputs?
504
760
  */
505
761
  declare function isClassComponent(component: ComponentType): component is ClassComponent;
506
762
  //#endregion
507
- export { InternalState as C, output as S, Component as T, OutputTarget as _, ComponentType as a, into as b, Inputs as c, BoundsCell as d, bounds as f, OutputCell as g, InputCell as h, ComponentProps as i, isClassComponent as l, EmitValue as m, ComponentArgs as n, FunctionComponent as o, EmitArgs as p, ComponentContext as r, InputValue as s, ClassComponent as t, ChannelReplica as u, ReadableCell as v, internalState as w, isOutputTarget as x, input as y };
508
- //# sourceMappingURL=FunctionComponent-CHtojpp7.d.ts.map
763
+ export { isChannelHostMessage as A, CommandsOf as B, into as C, ChannelHostMessage as D, ChannelClientMessage as E, diffProjection as F, channel as H, ChannelSpec as I, ChannelToken as L, PatchPath as M, applyPatch as N, ChannelPort as O, applyPatches as P, Command as R, input as S, output as T, defineChannel as U, ViewOf as V, viewKeys as W, EmitValue as _, ComponentType as a, OutputTarget as b, Inputs as c, BoundsCell as d, bounds as f, EmitArgs as g, ChannelReplica as h, ComponentProps as i, Patch as j, isChannelClientMessage as k, isClassComponent as l, internalState as m, ComponentArgs as n, FunctionComponent as o, InternalState as p, ComponentContext as r, InputValue as s, ClassComponent as t, Component as u, InputCell as v, isOutputTarget as w, ReadableCell as x, OutputCell as y, CommandMap as z };
764
+ //# sourceMappingURL=FunctionComponent-BDVrANP0.d.ts.map