gesso-framework 0.6.30 → 0.7.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 +61 -0
  2. package/dist/{FunctionComponent-CHtojpp7.d.ts → FunctionComponent-Czkex8fp.d.ts} +358 -114
  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 +53 -2
  6. package/dist/agent/index.js +5 -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-_cYcdczA.js +577 -0
  11. package/dist/computed-_cYcdczA.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-BENndtaH.d.ts} +222 -96
  15. package/dist/index.d.ts +4 -5
  16. package/dist/index.js +440 -6645
  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-8meJmsOh.js} +4 -576
  22. package/dist/persisted-8meJmsOh.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-cydQ10Nd.js +6795 -0
  29. package/dist/render-cydQ10Nd.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-BFBwuinE.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,247 +0,0 @@
1
- //#region src/channel/ChannelToken.d.ts
2
- /**
3
- * The barrier between the application and the view, declared once.
4
- *
5
- * A token is a name, a shape and an initial value — and no
6
- * implementation at all. Both threads import it, which is the point:
7
- * the module holding it has nothing in it to bundle, so an app's api
8
- * client and domain logic never reach the render worker the way a
9
- * shared `Store` class dragged them there.
10
- *
11
- * What the framework knows about a channel ends here. It diffs plain
12
- * data and ships patches; where the observables came from — a single
13
- * subject, or an api → repository → domain → view-model stack — is the
14
- * application's business and the framework cannot tell the difference.
15
- *
16
- * export interface CatalogView {
17
- * products: ProductRow[];
18
- * status: 'loading' | 'ready' | 'error';
19
- * }
20
- * export interface CatalogCommands {
21
- * addToCart(id: string): void;
22
- * }
23
- * export const Catalog = channel<CatalogView, CatalogCommands>('catalog', {
24
- * products: [],
25
- * status: 'loading'
26
- * });
27
- */
28
- /**
29
- * A command a component can send back across the barrier.
30
- *
31
- * The arguments are what cross: each is structured-cloned onto the
32
- * owning thread, so they must be plain data. There may be as many as
33
- * the command needs, which is why `move(from, to)` is written the way
34
- * anyone would write it rather than as `move({ from, to })`; a command
35
- * used to carry one payload and the second argument was dropped on the
36
- * floor with a warning.
37
- *
38
- * Commands return nothing: the effect comes back as a patch, never as
39
- * a return value, since there is no synchronous answer to be had
40
- * across a thread.
41
- */
42
- type Command = (...args: never[]) => void;
43
- /**
44
- * The internal view of a command set: a bag of callables by name.
45
- *
46
- * Public signatures constrain to `object`, not to this. An application
47
- * declares its commands as an ordinary interface —
48
- * `interface CatalogCommands { addToCart(id: string): void }` — and an
49
- * interface has no index signature, so it does not satisfy a
50
- * `Record<string, …>` constraint however well it fits in spirit.
51
- * Constraining to `object` accepts what people actually write; this
52
- * alias is what the implementation casts to when it looks a command up
53
- * by name.
54
- */
55
- type CommandMap = Record<string, Command>;
56
- /** The declared barrier for one area of an application. */
57
- interface ChannelToken<View extends object, Commands extends object = Record<string, never>> {
58
- readonly name: string;
59
- /**
60
- * What every view key holds before the first patch arrives.
61
- *
62
- * Required rather than optional, so the render thread never observes
63
- * `undefined` for a declared key. A channel that is genuinely still
64
- * loading says so in its own shape — a `status` field — rather than
65
- * leaving the view to infer it from an absence.
66
- */
67
- readonly initial: View;
68
- /** Phantom, carrying the command types to `send`. Never read. */
69
- readonly commands?: Commands;
70
- }
71
- /**
72
- * Declares a channel.
73
- *
74
- * The name identifies it across the thread boundary and must be
75
- * stable; unlike a class name it survives minification, which is why
76
- * it is written out rather than derived.
77
- */
78
- declare function channel<View extends object, Commands extends object = Record<string, never>>(name: string, initial: View): ChannelToken<View, Commands>;
79
- /** A channel written as one object: the view with its values, and the commands. */
80
- interface ChannelSpec<View extends object, Commands extends object> {
81
- /**
82
- * Every view key with the value it holds before the first patch.
83
- *
84
- * The type of the channel's view is the type of this object, so the
85
- * keys and their initial values are written once instead of in an
86
- * interface and again in a literal that has to agree with it.
87
- */
88
- readonly view: View;
89
- /**
90
- * The commands, as a type rather than as implementations.
91
- *
92
- * Written `{} as { addToCart(id: string, quantity: number): void }`:
93
- * the handlers live on the thread that owns the data and are passed
94
- * to `provide`, so what belongs in the token is only their shape.
95
- */
96
- readonly commands?: Commands;
97
- }
98
- /**
99
- * Declares a channel from one object.
100
- *
101
- * export const Catalog = defineChannel('catalog', {
102
- * view: {
103
- * products: [] as readonly ProductRow[],
104
- * status: 'loading' as ShelfStatus
105
- * },
106
- * commands: {} as {
107
- * addToCart(id: string, quantity: number): void;
108
- * move(from: number, to: number): void;
109
- * }
110
- * });
111
- *
112
- * export type CatalogView = ViewOf<typeof Catalog>;
113
- *
114
- * The same token `channel()` returns, declared once instead of three
115
- * times. `channel<View, Commands>(name, initial)` wrote the view as an
116
- * interface, then as an initial literal that had to agree with it, and
117
- * a key added to one and forgotten in the other was a type error in a
118
- * third file. Here the object is the type.
119
- *
120
- * A field whose initial value is narrower than the type it holds is
121
- * given the type it holds: `[]` is `never[]` and `'loading'` is
122
- * `string` unless it is said, which is what the `as` clauses above are
123
- * for. `ViewOf` and `CommandsOf` name the resulting types wherever the
124
- * application used to name its own interface.
125
- *
126
- * `channel()` is not deprecated and keeps working exactly as it did.
127
- * An application with interfaces it wants to keep, because they are
128
- * shared with something else or because the initial values are built
129
- * elsewhere, has nothing to migrate.
130
- */
131
- declare function defineChannel<View extends object, Commands extends object = Record<string, never>>(name: string, spec: ChannelSpec<View, Commands>): ChannelToken<View, Commands>;
132
- /** The view type of a channel token, for an application that wants to name it. */
133
- type ViewOf<T> = T extends ChannelToken<infer View, object> ? View : never;
134
- /** The command type of a channel token. */
135
- type CommandsOf<T> = T extends ChannelToken<object, infer Commands> ? Commands : never;
136
- /**
137
- * The keys a channel publishes.
138
- *
139
- * Structural in its parameter rather than generic over the token, so
140
- * it does not have to agree with any particular command type to read
141
- * what is only ever the initial value's shape.
142
- */
143
- declare function viewKeys(token: {
144
- initial: object;
145
- }): string[];
146
- //#endregion
147
- //#region src/channel/StorePatch.d.ts
148
- type PatchPath = readonly (string | number)[];
149
- /**
150
- * A change to one projection, as sent from a data worker to a replica.
151
- *
152
- * Paths are relative to the projection's root value, so a patch is
153
- * self-contained: the replica never needs the previous value to apply
154
- * one, only the value it already holds.
155
- */
156
- type Patch = {
157
- op: 'set';
158
- projection: string;
159
- path: PatchPath;
160
- value: unknown;
161
- } | {
162
- op: 'delete';
163
- projection: string;
164
- path: PatchPath;
165
- } | {
166
- op: 'splice';
167
- projection: string;
168
- path: PatchPath;
169
- index: number;
170
- deleteCount: number;
171
- items: readonly unknown[];
172
- };
173
- /**
174
- * Describes how to turn `previous` into `current` for one projection.
175
- *
176
- * Returns an empty list when nothing changed, which is the common case
177
- * and the reason this exists: the point of a projection is that most
178
- * state changes do not alter it, and the ones that do usually alter a
179
- * small part.
180
- */
181
- declare function diffProjection(projection: string, previous: unknown, current: unknown): Patch[];
182
- /**
183
- * Applies patches to a projection value, sharing structure with the
184
- * original everywhere the patch did not reach.
185
- *
186
- * Nothing handed in is mutated: bindings hold onto emitted values, so a
187
- * replica that edited in place would change data a component already
188
- * rendered.
189
- *
190
- * **Each container on a patched path is copied once per batch, not once
191
- * per patch.** A batch of N patches into one K-key object used to cost
192
- * N × K: every patch spread the whole object again to change one key.
193
- * A snapshot keyed by id is exactly that shape — gessologic published
194
- * `Record<netId, 0 | 1>` for ten thousand nets, about nine hundred
195
- * patches a publish, and the render worker's patch phase fell minutes
196
- * behind a 60 Hz stream it could never catch. So the batch remembers
197
- * the containers it has copied, and writes into those in place: they
198
- * are its own, made during this call and seen by nobody yet. Values
199
- * that arrived inside a patch are never written into, because they are
200
- * the patch's — a devtools log replays the same patch objects again.
201
- */
202
- declare function applyPatches(root: unknown, patches: readonly Patch[]): unknown;
203
- declare function applyPatch(root: unknown, patch: Patch): unknown;
204
- //#endregion
205
- //#region src/channel/ChannelProtocol.d.ts
206
- /**
207
- * A two-way channel endpoint. `MessagePort` satisfies it, as does a
208
- * test double, so nothing in the channel layer depends on Worker.
209
- */
210
- interface ChannelPort {
211
- postMessage(message: unknown): void;
212
- onmessage: ((event: {
213
- data: unknown;
214
- }) => void) | null;
215
- }
216
- /**
217
- * Render thread → the thread that owns the channel.
218
- *
219
- * A command's first argument stayed `payload` when commands learned to
220
- * take more than one, and the rest travel beside it. That is not
221
- * tidiness: an older view talking to a newer application worker sends
222
- * no `rest`, which reads as the one-argument call it is, and an older
223
- * application worker ignores the field, which is exactly what it did
224
- * before the field existed.
225
- */
226
- type ChannelClientMessage = {
227
- type: 'channel:sync';
228
- } | {
229
- type: 'channel:command';
230
- command: string;
231
- payload: unknown;
232
- rest?: unknown[];
233
- };
234
- /** The owning thread → render thread. */
235
- type ChannelHostMessage = {
236
- type: 'channel:patch';
237
- patches: Patch[];
238
- } | {
239
- type: 'channel:error';
240
- message: string;
241
- stack?: string;
242
- };
243
- declare function isChannelClientMessage(value: unknown): value is ChannelClientMessage;
244
- declare function isChannelHostMessage(value: unknown): value is ChannelHostMessage;
245
- //#endregion
246
- export { channel as _, isChannelHostMessage as a, applyPatch as c, ChannelSpec as d, ChannelToken as f, ViewOf as g, CommandsOf as h, isChannelClientMessage as i, applyPatches as l, CommandMap as m, ChannelHostMessage as n, Patch as o, Command as p, ChannelPort as r, PatchPath as s, ChannelClientMessage as t, diffProjection as u, defineChannel as v, viewKeys as y };
247
- //# sourceMappingURL=ChannelProtocol-ByNoHujM.d.ts.map
@@ -1,326 +0,0 @@
1
- import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { a as agentSurface, i as serveAgentPort, n as combineSurfaces, r as remoteSurface, t as AGENT_PORT } from "./remote-BoB8zh7z.js";
3
- //#region src/agent/ui.ts
4
- const TARGET = {
5
- ref: {
6
- type: "string",
7
- description: "The ref ui_snapshot gave the control, such as \"e4\"."
8
- },
9
- role: {
10
- type: "string",
11
- description: "The control's role, such as \"button\" or \"textbox\"."
12
- },
13
- name: {
14
- type: "string",
15
- description: "The control's accessible name, as ui_snapshot shows it in quotes."
16
- }
17
- };
18
- const TOOLS = [
19
- {
20
- name: "ui_snapshot",
21
- title: "Read the screen",
22
- description: "The screen as an outline of what a screen reader would announce: each control with its role, name, state, value and a ref to act on it by. Read it before acting, and again whenever an action did not do what you expected. Prefer a channel tool when one covers what you want; these act on the interface.",
23
- inputSchema: {
24
- type: "object",
25
- properties: {},
26
- additionalProperties: false
27
- },
28
- annotations: {
29
- readOnlyHint: true,
30
- openWorldHint: false
31
- }
32
- },
33
- {
34
- name: "ui_press",
35
- title: "Press a control",
36
- description: "Presses a button, link, checkbox, tab or any other control, as a click would. Name it by ref, or by role and name. Returns the screen afterwards.",
37
- inputSchema: {
38
- type: "object",
39
- properties: TARGET,
40
- additionalProperties: false
41
- },
42
- annotations: {
43
- readOnlyHint: false,
44
- destructiveHint: false,
45
- openWorldHint: false
46
- }
47
- },
48
- {
49
- name: "ui_type",
50
- title: "Fill a field",
51
- description: "Replaces a text field's whole text, as if the person had selected it all and typed. Name the field by ref, or by role and name. Returns the screen afterwards; press Enter with ui_key to submit.",
52
- inputSchema: {
53
- type: "object",
54
- properties: {
55
- ...TARGET,
56
- text: {
57
- type: "string",
58
- description: "The text the field should hold."
59
- }
60
- },
61
- required: ["text"],
62
- additionalProperties: false
63
- },
64
- annotations: {
65
- readOnlyHint: false,
66
- destructiveHint: false,
67
- openWorldHint: false
68
- }
69
- },
70
- {
71
- name: "ui_focus",
72
- title: "Focus a control",
73
- description: "Moves keyboard focus to a control, by ref or by role and name. Returns the screen afterwards.",
74
- inputSchema: {
75
- type: "object",
76
- properties: TARGET,
77
- additionalProperties: false
78
- },
79
- annotations: {
80
- readOnlyHint: false,
81
- destructiveHint: false,
82
- idempotentHint: true,
83
- openWorldHint: false
84
- }
85
- },
86
- {
87
- name: "ui_key",
88
- title: "Press a key",
89
- description: "Presses a key where focus is, as the keyboard would: Enter, Escape, Tab, ArrowDown, a letter. Modifiers are optional. Returns the screen afterwards.",
90
- inputSchema: {
91
- type: "object",
92
- properties: {
93
- key: {
94
- type: "string",
95
- description: "A KeyboardEvent key name, such as \"Enter\", \"Escape\" or \"a\"."
96
- },
97
- shift: { type: "boolean" },
98
- ctrl: { type: "boolean" },
99
- alt: { type: "boolean" },
100
- meta: { type: "boolean" }
101
- },
102
- required: ["key"],
103
- additionalProperties: false
104
- },
105
- annotations: {
106
- readOnlyHint: false,
107
- destructiveHint: false,
108
- openWorldHint: false
109
- }
110
- }
111
- ];
112
- /**
113
- * Short names for record ids.
114
- *
115
- * A record's id is its path through the tree, `root:0:0:component:1:0:1`,
116
- * which is exact and long, and an outline repeats one on every line an
117
- * agent reads. A ref is `e` and a number, given the first time a record
118
- * is seen and kept for as long as the surface lives, so the ref an agent
119
- * read in one snapshot still names the same control in the next.
120
- */
121
- var UiRefs = class {
122
- byId = /* @__PURE__ */ new Map();
123
- byRef = /* @__PURE__ */ new Map();
124
- refOf(id) {
125
- let ref = this.byId.get(id);
126
- if (ref === void 0) {
127
- ref = `e${this.byId.size + 1}`;
128
- this.byId.set(id, ref);
129
- this.byRef.set(ref, id);
130
- }
131
- return ref;
132
- }
133
- /** The record id a ref names, or the input itself when it is not a ref this surface gave. */
134
- idOf(ref) {
135
- return this.byRef.get(ref) ?? ref;
136
- }
137
- };
138
- /**
139
- * The tools, over a host. `host` returns undefined until the
140
- * application has started, and a tool called before then says so.
141
- */
142
- function uiSurface(host, options = {}) {
143
- const quietMs = options.quietMs ?? 50;
144
- const settleMs = options.settleMs ?? 1e3;
145
- const refs = new UiRefs();
146
- /** Waits until two readings of the screen agree, then returns the last. */
147
- const settled = async (ui) => {
148
- const start = Date.now();
149
- let previous = "";
150
- for (;;) {
151
- ui.flush();
152
- const current = outline(ui.semanticsTree(), ui.focusedNodeId(), refs);
153
- if (current === previous || Date.now() - start >= settleMs) return current;
154
- previous = current;
155
- await new Promise((resolve) => setTimeout(resolve, quietMs));
156
- }
157
- };
158
- const act = async (args, perform) => {
159
- const ui = host();
160
- if (ui === void 0) return failure("The application has not started yet.");
161
- ui.flush();
162
- const found = resolveTarget(ui.semanticsTree(), args, refs);
163
- if (typeof found === "string") return failure(found);
164
- if (found.disabled === true) return failure(`${describe(found)} is disabled.`);
165
- const problem = perform(ui, found);
166
- if (problem !== null) return failure(problem);
167
- return success(await settled(ui));
168
- };
169
- return {
170
- tools: () => TOOLS,
171
- call: async (name, args = {}) => {
172
- switch (name) {
173
- case "ui_snapshot": {
174
- const ui = host();
175
- return ui === void 0 ? failure("The application has not started yet.") : success(await settled(ui));
176
- }
177
- case "ui_press": return act(args, (ui, record) => (ui.applySemanticsAction({
178
- id: record.id,
179
- action: "click"
180
- }), null));
181
- case "ui_focus": return act(args, (ui, record) => (ui.applySemanticsAction({
182
- id: record.id,
183
- action: "focus"
184
- }), null));
185
- case "ui_type":
186
- if (typeof args.text !== "string") return failure("ui_type needs text: the text the field should hold.");
187
- return act(args, (ui, record) => {
188
- if (record.states?.includes("readonly")) return `${describe(record)} is read-only.`;
189
- ui.applySemanticsAction({
190
- id: record.id,
191
- action: "setValue",
192
- value: args.text
193
- });
194
- return null;
195
- });
196
- case "ui_key": {
197
- const ui = host();
198
- if (ui === void 0) return failure("The application has not started yet.");
199
- if (typeof args.key !== "string" || args.key === "") return failure("ui_key needs key: a KeyboardEvent key name, such as \"Enter\".");
200
- ui.key(args.key, {
201
- shift: args.shift === true,
202
- ctrl: args.ctrl === true,
203
- alt: args.alt === true,
204
- meta: args.meta === true
205
- });
206
- return success(await settled(ui));
207
- }
208
- default: return failure(`There is no tool called ${name}.`);
209
- }
210
- },
211
- resources: () => [],
212
- read: () => void 0,
213
- dispose: () => {}
214
- };
215
- }
216
- /**
217
- * The tree as an indented outline, one control per line:
218
- *
219
- * - button "Add one" [e3]
220
- * - textbox "Title" value="Groceries" [e4] (focused)
221
- *
222
- * Text, because an agent reads it, and an outline is a third the size
223
- * of the same tree as JSON. A record with neither a role nor a name is
224
- * structure, and its children are lifted to its depth.
225
- */
226
- function outline(tree, focused, refs = new UiRefs()) {
227
- const depths = /* @__PURE__ */ new Map();
228
- const lines = [];
229
- for (const record of tree.values()) {
230
- const parentDepth = record.parent === null ? -1 : depths.get(record.parent) ?? -1;
231
- const shown = record.role !== void 0 || record.label !== void 0 && record.label !== "";
232
- const depth = shown ? parentDepth + 1 : parentDepth;
233
- depths.set(record.id, depth);
234
- if (!shown) continue;
235
- const parts = [`${" ".repeat(depth)}- ${describe(record)}`];
236
- if (record.states !== void 0 && record.states.length > 0) parts.push(`[${record.states.join(", ")}]`);
237
- const value = valueOf(record);
238
- if (value !== null) parts.push(`value=${JSON.stringify(value)}`);
239
- if (record.disabled === true) parts.push("(disabled)");
240
- parts.push(`[${refs.refOf(record.id)}]`);
241
- if (record.id === focused) parts.push("(focused)");
242
- lines.push(parts.join(" "));
243
- }
244
- return lines.length === 0 ? "(the screen announces nothing)" : lines.join("\n");
245
- }
246
- function describe(record) {
247
- const name = record.label === void 0 || record.label === "" ? "" : ` ${JSON.stringify(record.label)}`;
248
- return `${record.role ?? "text"}${name}`;
249
- }
250
- function valueOf(record) {
251
- if (record.valueText !== void 0 && record.valueText !== record.label) return record.valueText;
252
- if (record.valueNow !== void 0) return String(record.valueNow);
253
- return null;
254
- }
255
- /**
256
- * The record an action names: by ref, or by role and name. A name
257
- * matches exactly before it matches as a part, ignoring case, so
258
- * "Save" finds the button called Save rather than "Save as". More than
259
- * one match is an error listing them, because guessing which of two
260
- * buttons an agent meant is how the wrong one gets pressed.
261
- */
262
- function resolveTarget(tree, args, refs = new UiRefs()) {
263
- const ref = typeof args.ref === "string" ? args.ref : void 0;
264
- const role = typeof args.role === "string" ? args.role : void 0;
265
- const name = typeof args.name === "string" ? args.name : void 0;
266
- if (ref !== void 0) return tree.get(refs.idOf(ref)) ?? `Nothing on the screen has the ref ${ref} now. Read ui_snapshot again for current refs.`;
267
- if (role === void 0 && name === void 0) return "Say which control: a ref from ui_snapshot, or a role and a name.";
268
- const candidates = [...tree.values()].filter((record) => role === void 0 || record.role === role);
269
- let matches = candidates;
270
- if (name !== void 0) {
271
- const wanted = name.toLowerCase();
272
- const exact = candidates.filter((record) => record.label?.toLowerCase() === wanted);
273
- matches = exact.length > 0 ? exact : candidates.filter((record) => record.label?.toLowerCase().includes(wanted));
274
- }
275
- const wanted = [role, name === void 0 ? void 0 : JSON.stringify(name)].filter(Boolean).join(" ");
276
- if (matches.length === 1) return matches[0];
277
- if (matches.length === 0) {
278
- const nearby = candidates.filter((record) => record.role !== void 0).slice(0, 10).map((record) => `${describe(record)} [${refs.refOf(record.id)}]`);
279
- return `Nothing on the screen is ${wanted}.${nearby.length > 0 ? ` Some that are: ${nearby.join("; ")}.` : ""}`;
280
- }
281
- return `${matches.length} controls are ${wanted}: ${matches.slice(0, 10).map((record) => `${describe(record)} [${refs.refOf(record.id)}]`).join("; ")}. Use a ref.`;
282
- }
283
- function success(text) {
284
- return {
285
- content: [{
286
- type: "text",
287
- text
288
- }],
289
- isError: false
290
- };
291
- }
292
- function failure(text) {
293
- return {
294
- content: [{
295
- type: "text",
296
- text
297
- }],
298
- isError: true
299
- };
300
- }
301
- //#endregion
302
- //#region src/agent/app.ts
303
- var app_exports = /* @__PURE__ */ __exportAll({ serveApplicationAgent: () => serveApplicationAgent });
304
- /**
305
- * Answers an agent port with the whole application: the channels this
306
- * thread feeds, whatever each worker behind it serves, and the screen.
307
- *
308
- * The thread that draws is the one that knows all three, which is the
309
- * render worker in the worker configuration and the page in the single
310
- * thread one; both answer with this. The screen comes last, so a
311
- * channel tool keeps its name.
312
- */
313
- function serveApplicationAgent(port, parts) {
314
- return serveAgentPort(port, (confirm) => {
315
- const workers = [...parts.workers()].filter((worker) => worker.spawned).map((worker) => remoteSurface(worker.open(AGENT_PORT), { confirm }));
316
- return combineSurfaces([
317
- agentSurface(parts.channels(), { confirm }),
318
- ...workers,
319
- uiSurface(parts.ui)
320
- ]);
321
- });
322
- }
323
- //#endregion
324
- export { resolveTarget as a, outline as i, serveApplicationAgent as n, uiSurface as o, UiRefs as r, app_exports as t };
325
-
326
- //# sourceMappingURL=app-Yig59Ygf.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"app-Yig59Ygf.js","names":[],"sources":["../src/agent/ui.ts","../src/agent/app.ts"],"sourcesContent":["import type { UiKeyModifiers, UiSemanticsAction, UiSemanticsMap, UiSemanticsRecord } from 'gesso-core';\n\nimport type { JsonSchema } from '../channel/ChannelSchema';\nimport type { AgentSurface, AgentTool, AgentToolResult } from './AgentSurface';\n\n/**\n * The screen, as tools an agent can use.\n *\n * Channels are the application's own vocabulary, and the right way in\n * for anything they cover. Not everything is covered: a dialog's\n * buttons, a tab, a field the person is halfway through filling. For\n * those an agent has to do what a person does, and a canvas gives it\n * nothing to do that with: no DOM to query, no element to click.\n *\n * The semantics tree is the answer a screen reader already gets. Every\n * control has a role and an accessible name, a state, a value, and an\n * id, so an agent can read the screen as an outline and act on a\n * control by naming it. Acting goes through `applySemanticsAction`,\n * the path the accessibility mirror uses, which turns a press into the\n * same click a pointer makes and a value into the same edit a keyboard\n * makes: an agent can do exactly what a person could, no more, and an\n * open focus trap holds it as it holds Tab.\n *\n * ui_snapshot the screen as an outline, each control with a ref\n * ui_press press a control, by ref or by role and name\n * ui_type replace a field's text\n * ui_focus move focus to a control\n * ui_key press a key where focus is: Enter, Escape, Tab, ArrowDown\n *\n * Each action answers with the outline as it is afterwards.\n */\n\n/** What the tools need of the running application. */\nexport interface UiHost {\n semanticsTree(): UiSemanticsMap;\n focusedNodeId(): string | null;\n applySemanticsAction(action: UiSemanticsAction): void;\n key(key: string, modifiers: UiKeyModifiers): void;\n /**\n * Runs a pending frame now. A tab in the background is sent no\n * frames, and without one an action's effect never reaches the tree\n * this reads back.\n */\n flush(): void;\n}\n\nexport interface UiSurfaceOptions {\n /** How long the screen must hold still after an action before it is read back (default 50 ms). */\n quietMs?: number;\n /** The longest an action waits for that (default 1000 ms). */\n settleMs?: number;\n}\n\nconst TARGET: Record<string, JsonSchema> = {\n ref: { type: 'string', description: 'The ref ui_snapshot gave the control, such as \"e4\".' },\n role: { type: 'string', description: 'The control\\'s role, such as \"button\" or \"textbox\".' },\n name: { type: 'string', description: \"The control's accessible name, as ui_snapshot shows it in quotes.\" }\n};\n\nconst TOOLS: readonly AgentTool[] = [\n {\n name: 'ui_snapshot',\n title: 'Read the screen',\n description:\n 'The screen as an outline of what a screen reader would announce: each control with its role, name, state, ' +\n 'value and a ref to act on it by. Read it before acting, and again whenever an action did not do what you ' +\n 'expected. Prefer a channel tool when one covers what you want; these act on the interface.',\n inputSchema: { type: 'object', properties: {}, additionalProperties: false },\n annotations: { readOnlyHint: true, openWorldHint: false }\n },\n {\n name: 'ui_press',\n title: 'Press a control',\n description:\n 'Presses a button, link, checkbox, tab or any other control, as a click would. Name it by ref, or by role and ' +\n 'name. Returns the screen afterwards.',\n inputSchema: { type: 'object', properties: TARGET, additionalProperties: false },\n annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }\n },\n {\n name: 'ui_type',\n title: 'Fill a field',\n description:\n \"Replaces a text field's whole text, as if the person had selected it all and typed. Name the field by ref, or \" +\n 'by role and name. Returns the screen afterwards; press Enter with ui_key to submit.',\n inputSchema: {\n type: 'object',\n properties: { ...TARGET, text: { type: 'string', description: 'The text the field should hold.' } },\n required: ['text'],\n additionalProperties: false\n },\n annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }\n },\n {\n name: 'ui_focus',\n title: 'Focus a control',\n description: 'Moves keyboard focus to a control, by ref or by role and name. Returns the screen afterwards.',\n inputSchema: { type: 'object', properties: TARGET, additionalProperties: false },\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }\n },\n {\n name: 'ui_key',\n title: 'Press a key',\n description:\n 'Presses a key where focus is, as the keyboard would: Enter, Escape, Tab, ArrowDown, a letter. Modifiers are ' +\n 'optional. Returns the screen afterwards.',\n inputSchema: {\n type: 'object',\n properties: {\n key: { type: 'string', description: 'A KeyboardEvent key name, such as \"Enter\", \"Escape\" or \"a\".' },\n shift: { type: 'boolean' },\n ctrl: { type: 'boolean' },\n alt: { type: 'boolean' },\n meta: { type: 'boolean' }\n },\n required: ['key'],\n additionalProperties: false\n },\n annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }\n }\n];\n\n/**\n * Short names for record ids.\n *\n * A record's id is its path through the tree, `root:0:0:component:1:0:1`,\n * which is exact and long, and an outline repeats one on every line an\n * agent reads. A ref is `e` and a number, given the first time a record\n * is seen and kept for as long as the surface lives, so the ref an agent\n * read in one snapshot still names the same control in the next.\n */\nexport class UiRefs {\n private readonly byId = new Map<string, string>();\n private readonly byRef = new Map<string, string>();\n\n refOf(id: string): string {\n let ref = this.byId.get(id);\n if (ref === undefined) {\n ref = `e${this.byId.size + 1}`;\n this.byId.set(id, ref);\n this.byRef.set(ref, id);\n }\n return ref;\n }\n\n /** The record id a ref names, or the input itself when it is not a ref this surface gave. */\n idOf(ref: string): string {\n return this.byRef.get(ref) ?? ref;\n }\n}\n\n/**\n * The tools, over a host. `host` returns undefined until the\n * application has started, and a tool called before then says so.\n */\nexport function uiSurface(host: () => UiHost | undefined, options: UiSurfaceOptions = {}): AgentSurface {\n const quietMs = options.quietMs ?? 50;\n const settleMs = options.settleMs ?? 1000;\n const refs = new UiRefs();\n\n /** Waits until two readings of the screen agree, then returns the last. */\n const settled = async (ui: UiHost): Promise<string> => {\n const start = Date.now();\n let previous = '';\n for (;;) {\n ui.flush();\n const current = outline(ui.semanticsTree(), ui.focusedNodeId(), refs);\n if (current === previous || Date.now() - start >= settleMs) {\n return current;\n }\n previous = current;\n await new Promise(resolve => setTimeout(resolve, quietMs));\n }\n };\n\n const act = async (\n args: Readonly<Record<string, unknown>>,\n perform: (ui: UiHost, record: UiSemanticsRecord) => string | null\n ): Promise<AgentToolResult> => {\n const ui = host();\n if (ui === undefined) {\n return failure('The application has not started yet.');\n }\n ui.flush();\n const found = resolveTarget(ui.semanticsTree(), args, refs);\n if (typeof found === 'string') {\n return failure(found);\n }\n if (found.disabled === true) {\n return failure(`${describe(found)} is disabled.`);\n }\n const problem = perform(ui, found);\n if (problem !== null) {\n return failure(problem);\n }\n return success(await settled(ui));\n };\n\n return {\n tools: () => TOOLS,\n call: async (name, args = {}) => {\n switch (name) {\n case 'ui_snapshot': {\n const ui = host();\n return ui === undefined ? failure('The application has not started yet.') : success(await settled(ui));\n }\n case 'ui_press':\n return act(args, (ui, record) => (ui.applySemanticsAction({ id: record.id, action: 'click' }), null));\n case 'ui_focus':\n return act(args, (ui, record) => (ui.applySemanticsAction({ id: record.id, action: 'focus' }), null));\n case 'ui_type':\n if (typeof args.text !== 'string') {\n return failure('ui_type needs text: the text the field should hold.');\n }\n return act(args, (ui, record) => {\n if (record.states?.includes('readonly')) {\n return `${describe(record)} is read-only.`;\n }\n ui.applySemanticsAction({ id: record.id, action: 'setValue', value: args.text as string });\n return null;\n });\n case 'ui_key': {\n const ui = host();\n if (ui === undefined) {\n return failure('The application has not started yet.');\n }\n if (typeof args.key !== 'string' || args.key === '') {\n return failure('ui_key needs key: a KeyboardEvent key name, such as \"Enter\".');\n }\n ui.key(args.key, {\n shift: args.shift === true,\n ctrl: args.ctrl === true,\n alt: args.alt === true,\n meta: args.meta === true\n });\n return success(await settled(ui));\n }\n default:\n return failure(`There is no tool called ${name}.`);\n }\n },\n resources: () => [],\n read: () => undefined,\n dispose: () => {}\n };\n}\n\n/**\n * The tree as an indented outline, one control per line:\n *\n * - button \"Add one\" [e3]\n * - textbox \"Title\" value=\"Groceries\" [e4] (focused)\n *\n * Text, because an agent reads it, and an outline is a third the size\n * of the same tree as JSON. A record with neither a role nor a name is\n * structure, and its children are lifted to its depth.\n */\nexport function outline(tree: UiSemanticsMap, focused: string | null, refs: UiRefs = new UiRefs()): string {\n const depths = new Map<string, number>();\n const lines: string[] = [];\n for (const record of tree.values()) {\n const parentDepth = record.parent === null ? -1 : (depths.get(record.parent) ?? -1);\n const shown = record.role !== undefined || (record.label !== undefined && record.label !== '');\n const depth = shown ? parentDepth + 1 : parentDepth;\n depths.set(record.id, depth);\n if (!shown) {\n continue;\n }\n const parts = [`${' '.repeat(depth)}- ${describe(record)}`];\n if (record.states !== undefined && record.states.length > 0) {\n parts.push(`[${record.states.join(', ')}]`);\n }\n const value = valueOf(record);\n if (value !== null) {\n parts.push(`value=${JSON.stringify(value)}`);\n }\n if (record.disabled === true) {\n parts.push('(disabled)');\n }\n parts.push(`[${refs.refOf(record.id)}]`);\n if (record.id === focused) {\n parts.push('(focused)');\n }\n lines.push(parts.join(' '));\n }\n return lines.length === 0 ? '(the screen announces nothing)' : lines.join('\\n');\n}\n\nfunction describe(record: UiSemanticsRecord): string {\n const name = record.label === undefined || record.label === '' ? '' : ` ${JSON.stringify(record.label)}`;\n return `${record.role ?? 'text'}${name}`;\n}\n\nfunction valueOf(record: UiSemanticsRecord): string | null {\n if (record.valueText !== undefined && record.valueText !== record.label) {\n return record.valueText;\n }\n if (record.valueNow !== undefined) {\n return String(record.valueNow);\n }\n return null;\n}\n\n/**\n * The record an action names: by ref, or by role and name. A name\n * matches exactly before it matches as a part, ignoring case, so\n * \"Save\" finds the button called Save rather than \"Save as\". More than\n * one match is an error listing them, because guessing which of two\n * buttons an agent meant is how the wrong one gets pressed.\n */\nexport function resolveTarget(\n tree: UiSemanticsMap,\n args: Readonly<Record<string, unknown>>,\n refs: UiRefs = new UiRefs()\n): UiSemanticsRecord | string {\n const ref = typeof args.ref === 'string' ? args.ref : undefined;\n const role = typeof args.role === 'string' ? args.role : undefined;\n const name = typeof args.name === 'string' ? args.name : undefined;\n if (ref !== undefined) {\n return (\n tree.get(refs.idOf(ref)) ??\n `Nothing on the screen has the ref ${ref} now. Read ui_snapshot again for current refs.`\n );\n }\n if (role === undefined && name === undefined) {\n return 'Say which control: a ref from ui_snapshot, or a role and a name.';\n }\n const candidates = [...tree.values()].filter(record => role === undefined || record.role === role);\n let matches = candidates;\n if (name !== undefined) {\n const wanted = name.toLowerCase();\n const exact = candidates.filter(record => record.label?.toLowerCase() === wanted);\n matches = exact.length > 0 ? exact : candidates.filter(record => record.label?.toLowerCase().includes(wanted));\n }\n const wanted = [role, name === undefined ? undefined : JSON.stringify(name)].filter(Boolean).join(' ');\n if (matches.length === 1) {\n return matches[0];\n }\n if (matches.length === 0) {\n const nearby = candidates\n .filter(record => record.role !== undefined)\n .slice(0, 10)\n .map(record => `${describe(record)} [${refs.refOf(record.id)}]`);\n return `Nothing on the screen is ${wanted}.${nearby.length > 0 ? ` Some that are: ${nearby.join('; ')}.` : ''}`;\n }\n return `${matches.length} controls are ${wanted}: ${matches\n .slice(0, 10)\n .map(record => `${describe(record)} [${refs.refOf(record.id)}]`)\n .join('; ')}. Use a ref.`;\n}\n\nfunction success(text: string): AgentToolResult {\n return { content: [{ type: 'text', text }], isError: false };\n}\n\nfunction failure(text: string): AgentToolResult {\n return { content: [{ type: 'text', text }], isError: true };\n}\n","import type { ServedChannel } from '../channel/serveChannels';\nimport type { WorkerHandle } from '../worker/WorkerPorts';\nimport { agentSurface } from './AgentSurface';\nimport { AGENT_PORT, combineSurfaces, remoteSurface, serveAgentPort } from './remote';\nimport { uiSurface, type UiHost } from './ui';\n\n/** What a thread that draws the application can offer an agent. */\nexport interface ApplicationAgentParts {\n /** The channels fed from this thread. */\n readonly channels: () => readonly ServedChannel[];\n /** The workers behind it, each asked over an agent port of its own. */\n readonly workers: () => Iterable<WorkerHandle>;\n /** The screen, once the application has started. */\n readonly ui: () => UiHost | undefined;\n}\n\n/**\n * Answers an agent port with the whole application: the channels this\n * thread feeds, whatever each worker behind it serves, and the screen.\n *\n * The thread that draws is the one that knows all three, which is the\n * render worker in the worker configuration and the page in the single\n * thread one; both answer with this. The screen comes last, so a\n * channel tool keeps its name.\n */\nexport function serveApplicationAgent(port: MessagePort, parts: ApplicationAgentParts): () => void {\n return serveAgentPort(port, confirm => {\n const workers = [...parts.workers()]\n .filter(worker => worker.spawned)\n .map(worker => remoteSurface(worker.open(AGENT_PORT), { confirm }));\n return combineSurfaces([agentSurface(parts.channels(), { confirm }), ...workers, uiSurface(parts.ui)]);\n });\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"createComponent-3n1ZSql8.js","names":["keyed","EMPTY","EMPTY"],"sources":["../src/each.ts","../src/show.ts","../src/metadata.ts","../src/createComponent.ts"],"sourcesContent":["import { Observable, defer, distinctUntilChanged, map, of } from 'rxjs';\n\nimport { isComponentLikeElement, isObservable, type UiChild, type UiElement } from 'gesso-core';\n\n/**\n * What gives a row its identity: the name of a field holding it, or a\n * function returning it. Omitting it keys by index, which is what the\n * builder falls back to and what `each` warns about.\n */\nexport type EachKey<T> = (keyof T & string) | ((item: T, index: number) => string | number);\n\nexport interface EachProps<T> {\n /** The list: a cell, any Observable of one, or a plain array. */\n readonly of: Observable<readonly T[]> | readonly T[];\n /** Identity per row. See `EachKey`. */\n readonly by?: EachKey<T>;\n /** What one row draws as. */\n readonly children: (item: T, index: number) => UiChild;\n}\n\n/**\n * A keyed list.\n *\n * <Each of={rows} by=\"id\">{row => <Row row={row} />}</Each>\n *\n * The `map`, the change check and the keying, which every screen in\n * both applications wrote by hand: forty blocks of\n * `map(items => items.map(item => <Row key={item.id} />))`, each of\n * which rebuilt every row's element on every emission of the list.\n *\n * A row's element is built once and kept while the row is in the list\n * and its value has not changed, so a list that re-emits equal data\n * builds nothing and reconciles nothing: the result is the same array,\n * and the change check downstream ends the emission there. A row whose\n * value changed is rebuilt alone; the key keeps its node and its\n * component instance, so only what changed is written.\n *\n * `key` is set from `by` on the element the function returns, so a row\n * component is written without one. An element that carries its own key\n * keeps it.\n *\n * `LazyColumn` is the same shape for a list long enough that only the\n * visible rows should exist. This is the short-list case, and the two\n * read alike on purpose.\n */\nexport function Each<T>(props: EachProps<T>): Observable<readonly UiChild[]> {\n return each(props.of, props.by, props.children);\n}\n\n/**\n * The function form of `Each`, for a list built outside a tag:\n *\n * const rows = each(tracks, 'id', track => <ItemRow item={track} />);\n */\nexport function each<T>(\n source: Observable<readonly T[]> | readonly T[],\n by: EachKey<T> | undefined,\n render: (item: T, index: number) => UiChild\n): Observable<readonly UiChild[]> {\n const keyOf = keyFunction(by);\n const dynamic = isObservable(source);\n const items = dynamic ? (source as Observable<readonly T[]>) : of(source as readonly T[]);\n if (by === undefined && dynamic) {\n warnIndexKeyed(render);\n }\n // Whether the row function took the index. One that did not is\n // unaffected by its row moving, so a reorder or a removal reuses\n // every element it already built; one that did has to be asked again\n // for a row that moved, since the index is part of what it drew.\n const positional = render.length >= 2;\n return defer(() => {\n const held = new Map<string | number, Row<T>>();\n let previous: readonly UiChild[] | null = null;\n let generation = 0;\n return items.pipe(\n map(list => {\n previous = reconcile(held, previous, ++generation, list, keyOf, render, positional);\n return previous;\n }),\n distinctUntilChanged()\n );\n });\n}\n\n/** One row's element, and what it was built from. */\ninterface Row<T> {\n item: T;\n index: number;\n child: UiChild;\n /** The pass that last saw this key; anything older has left the list. */\n generation: number;\n}\n\n/**\n * Builds the children for one emission, reusing every row it can.\n *\n * The result is the previous array itself when nothing about the list\n * changed, so the `distinctUntilChanged` above it ends the emission and\n * the builder is never entered. A new array is allocated at the first\n * row that differs and not before, which is what keeps an unchanged\n * emission free rather than merely cheap.\n */\nfunction reconcile<T>(\n held: Map<string | number, Row<T>>,\n previous: readonly UiChild[] | null,\n generation: number,\n list: readonly T[],\n keyOf: (item: T, index: number) => string | number,\n render: (item: T, index: number) => UiChild,\n positional: boolean\n): readonly UiChild[] {\n let next: UiChild[] | null = null;\n for (let index = 0; index < list.length; index++) {\n const item = list[index] as T;\n const key = keyOf(item, index);\n const row = held.get(key);\n if (row !== undefined && row.generation === generation) {\n throw new Error(`Duplicate key '${String(key)}' in a list: two rows claim the same identity.`);\n }\n let child: UiChild;\n if (row !== undefined && Object.is(row.item, item) && (!positional || row.index === index)) {\n child = row.child;\n row.index = index;\n row.generation = generation;\n } else {\n child = keyed(render(item, index), key);\n if (row === undefined) {\n held.set(key, { item, index, child, generation });\n } else {\n row.item = item;\n row.index = index;\n row.child = child;\n row.generation = generation;\n }\n }\n if (next === null && (previous === null || index >= previous.length || previous[index] !== child)) {\n next = previous === null ? [] : previous.slice(0, index);\n }\n next?.push(child);\n }\n for (const [key, row] of held) {\n if (row.generation !== generation) {\n held.delete(key);\n }\n }\n if (next === null && previous !== null && previous.length !== list.length) {\n // Nothing before the end changed and the list got shorter: the\n // rows that remain are the ones already in `previous`.\n next = previous.slice(0, list.length);\n }\n return next ?? previous ?? EMPTY;\n}\n\nconst EMPTY: readonly UiChild[] = [];\n\nfunction keyFunction<T>(by: EachKey<T> | undefined): (item: T, index: number) => string | number {\n if (by === undefined) {\n return (_item, index) => index;\n }\n if (typeof by === 'function') {\n return by;\n }\n return item => (item as Record<string, unknown>)[by] as string | number;\n}\n\n/**\n * Puts the row's key on the element the render function returned.\n *\n * An element that named its own key keeps it, since the author meant\n * something by it. An observable row cannot carry one at all: its\n * fragment anchor is identified by position, so a list of them\n * reorders wrongly, and it is worth saying so rather than leaving a\n * reorder to look like corruption.\n */\nfunction keyed(child: UiChild, key: string | number): UiChild {\n if (isComponentLikeElement(child)) {\n return child.key === undefined || child.key === null ? { ...child, key } : child;\n }\n if (isObservable(child)) {\n throw new Error(\n `A row of a list is an observable, which cannot carry the key '${String(key)}'. ` +\n `Return an element or a component from the row function and bind the observable inside it.`\n );\n }\n const element = child as UiElement;\n const existing = element.props.key;\n if (existing !== undefined && existing !== null) {\n return element;\n }\n return { ...element, props: { ...element.props, key } };\n}\n\n/** Said once: every unkeyed list has the same problem and the same fix. */\nlet warnedAboutIndexKeys = false;\n\nfunction warnIndexKeyed(render: (item: never, index: number) => UiChild): void {\n if (warnedAboutIndexKeys) {\n return;\n }\n warnedAboutIndexKeys = true;\n const what = render.name === '' ? 'a list' : `the list drawn by '${render.name}'`;\n console.warn(\n `${what} is keyed by index, because \\`each\\` was given no \\`by\\`. A row inserted or removed ` +\n `anywhere but the end then shifts every row after it onto a different node: state moves between rows, ` +\n `a text field keeps the wrong value, and an animation runs on the wrong element. ` +\n `Pass \\`by\\` naming what identifies a row: by=\"id\", or by={row => row.id}.`\n );\n}\n","import { Observable, defer, distinctUntilChanged, map, of } from 'rxjs';\n\nimport { isComponentLikeElement, isObservable, type UiChild, type UiElement } from 'gesso-core';\n\nexport interface ShowProps {\n /** The condition: a cell, any Observable, or a plain value. */\n readonly when: Observable<unknown> | unknown;\n /** What to draw while it is truthy. */\n readonly children: () => UiChild;\n /** What to draw while it is not. Nothing, by default. */\n readonly otherwise?: () => UiChild;\n}\n\n/**\n * One child, while a condition holds.\n *\n * <Show when={liked}>{() => <Icon path={ICONS.heart} />}</Show>\n *\n * `{cond && <x />}` already works when `cond` is a plain value, because\n * JSX drops a `false` child. A reactive `cond` had to become\n * `cond.pipe(map(on => (on ? [<x key=\"x\" />] : [])))`, which is the\n * fifty-six-block idiom in the two applications, and the discontinuity\n * between the two forms is the surprise this removes.\n *\n * The child is built once, on the first frame it is shown, and kept\n * while it is shown: a condition that emits `true` again draws nothing\n * and reconciles nothing. It carries a stable key, so the node it\n * built is the node it gets back when the condition returns, and it\n * adds no node of its own: what a `Show` puts under its parent is the\n * child, or nothing, and never a wrapper that layout would have to\n * account for.\n *\n * Anything inside it that changes is bound as usual. The function is\n * not re-run when the *data* changes, only when the condition does, so\n * a `Show` whose child shows a value binds that value rather than\n * reading it.\n */\nexport function Show(props: ShowProps): Observable<readonly UiChild[]> {\n return show(props.when, props.children, props.otherwise);\n}\n\n/**\n * The function form of `Show`:\n *\n * {show(liked, () => <Icon path={ICONS.heart} />)}\n */\nexport function show(\n when: Observable<unknown> | unknown,\n children: () => UiChild,\n otherwise?: () => UiChild\n): Observable<readonly UiChild[]> {\n const condition = isObservable(when) ? (when as Observable<unknown>) : of(when);\n return defer(() => {\n let shown: readonly UiChild[] | null = null;\n let hidden: readonly UiChild[] | null = null;\n return condition.pipe(\n map(value => Boolean(value)),\n distinctUntilChanged(),\n map(on => {\n if (on) {\n shown ??= [keyed(children(), 'when')];\n return shown;\n }\n hidden ??= otherwise === undefined ? EMPTY : [keyed(otherwise(), 'otherwise')];\n return hidden;\n })\n );\n });\n}\n\nconst EMPTY: readonly UiChild[] = [];\n\n/**\n * Gives the branch its stable key, so returning to it returns to the\n * node it built. An element that named its own key keeps it.\n */\nfunction keyed(child: UiChild, key: string): UiChild {\n if (isComponentLikeElement(child)) {\n return child.key === undefined || child.key === null ? { ...child, key } : child;\n }\n if (isObservable(child)) {\n return child;\n }\n const element = child as UiElement;\n const existing = element.props.key;\n if (existing !== undefined && existing !== null) {\n return element;\n }\n return { ...element, props: { ...element.props, key } };\n}\n","export const COMPONENT_METADATA_KEY = Symbol('gesso:component:metadata');\n\nexport interface ComponentMetadata {\n tag: string;\n inputs: Set<string>;\n states: Set<string>;\n injects: Map<string, Function>;\n /** Property name → the channel token it resolves to (`@Channel`). */\n channels: Map<string, { name: string }>;\n}\n\nconst metadataStore = new WeakMap<Function, ComponentMetadata>();\n\nexport function getComponentMetadata(constructor: Function): ComponentMetadata {\n let metadata = metadataStore.get(constructor);\n if (metadata === undefined) {\n metadata = { tag: constructor.name, inputs: new Set(), states: new Set(), injects: new Map(), channels: new Map() };\n metadataStore.set(constructor, metadata);\n }\n return metadata;\n}\n\nexport function hasComponentMetadata(constructor: Function): boolean {\n return metadataStore.has(constructor);\n}\n","import type { ComponentElement } from './ComponentElement';\nimport type { ComponentArgs, ComponentProps, ComponentType } from './FunctionComponent';\nimport { getComponentMetadata } from './metadata';\n\n/**\n * Creates a ComponentElement for use as a child of a UiElement.\n *\n * Column(\n * createComponent(Counter, { label: 'Count' }),\n * createComponent(TodoItem, { todo }, todo.id)\n * )\n *\n * The component may be a class extending Component or a function\n * `(props, ctx) => UiChild`. Props are typed from the component: a\n * class's `input()` fields, or a function's `Inputs<P>` parameter. A\n * misspelled prop, a value of the wrong type, and a missing required\n * prop are compile errors; props may be omitted only when none is\n * required.\n */\nexport function createComponent<C extends ComponentType>(\n component: C,\n ...args: ComponentArgs<C>\n): ComponentElement<ComponentProps<C>> {\n const [props, key] = args as [ComponentProps<C> | undefined, string | number | undefined];\n const metadata = getComponentMetadata(component);\n return {\n kind: 'component',\n tag: metadata.tag,\n component,\n props: (props ?? {}) as ComponentProps<C>,\n key\n };\n}\n"],"mappings":"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"}