gesso-framework 0.6.29 → 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.
- package/CHANGELOG.md +68 -0
- package/dist/{FunctionComponent-CHtojpp7.d.ts → FunctionComponent-Czkex8fp.d.ts} +358 -114
- package/dist/{serveChannels-BlEag7WU.js → WorkerPorts-m_hLRoPj.js} +8 -240
- package/dist/WorkerPorts-m_hLRoPj.js.map +1 -0
- package/dist/agent/index.d.ts +53 -2
- package/dist/agent/index.js +5 -4
- package/dist/agent/index.js.map +1 -1
- package/dist/app-De3z58x7.js +638 -0
- package/dist/app-De3z58x7.js.map +1 -0
- package/dist/computed-_cYcdczA.js +577 -0
- package/dist/computed-_cYcdczA.js.map +1 -0
- package/dist/createComponent-DJads88q.js +47 -0
- package/dist/createComponent-DJads88q.js.map +1 -0
- package/dist/{index-DZbSb8IN.d.ts → index-BENndtaH.d.ts} +222 -96
- package/dist/index.d.ts +4 -5
- package/dist/index.js +440 -6645
- package/dist/index.js.map +1 -1
- package/dist/jsx/jsx-runtime.d.ts +8 -5
- package/dist/jsx/jsx-runtime.js +15 -6
- package/dist/jsx/jsx-runtime.js.map +1 -1
- package/dist/{persisted-fUzIU8FZ.js → persisted-8meJmsOh.js} +4 -576
- package/dist/persisted-8meJmsOh.js.map +1 -0
- package/dist/remote/index.d.ts +2 -2
- package/dist/remote/index.js +2 -1
- package/dist/remote/index.js.map +1 -1
- package/dist/{remote-BoB8zh7z.js → remote-B4bjdTzg.js} +19 -4
- package/dist/remote-B4bjdTzg.js.map +1 -0
- package/dist/render-cydQ10Nd.js +6795 -0
- package/dist/render-cydQ10Nd.js.map +1 -0
- package/dist/serveChannels-DReCmRxO.js +241 -0
- package/dist/serveChannels-DReCmRxO.js.map +1 -0
- package/dist/{createComponent-3n1ZSql8.js → show-D0MevHaV.js} +2 -46
- package/dist/show-D0MevHaV.js.map +1 -0
- package/dist/{ui-CHtNccR8.d.ts → tools-BFBwuinE.d.ts} +351 -8
- package/dist/{webmcp-CdDq2nzS.js → webmcp-D7IlA0fu.js} +3 -3
- package/dist/webmcp-D7IlA0fu.js.map +1 -0
- package/dist/worker/index.d.ts +3 -4
- package/dist/worker/index.js +5 -3
- package/dist/worker/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/ChannelProtocol-ByNoHujM.d.ts +0 -247
- package/dist/app-Yig59Ygf.js +0 -326
- package/dist/app-Yig59Ygf.js.map +0 -1
- package/dist/createComponent-3n1ZSql8.js.map +0 -1
- package/dist/persisted-fUzIU8FZ.js.map +0 -1
- package/dist/remote-BoB8zh7z.js.map +0 -1
- package/dist/serveChannels-BlEag7WU.js.map +0 -1
- package/dist/webmcp-CdDq2nzS.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,73 @@
|
|
|
1
1
|
# gesso-framework
|
|
2
2
|
|
|
3
|
+
## 0.7.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- fe71a86: An application can offer an agent tools of its own, beside its channels' and its screen's: `renderRoot(App).useAgentTools([...])`, and `useAgentTools` on the app builder. A tool is a name, a description, an input schema and a `run` that may answer with text, data and pictures (`agentText`, `agentImages`, `agentError` from `gesso-framework/agent`); its arguments are checked against the schema first. In `gesso-vite-plugin`, `agentInstructions` adds an application's own words to what an agent is told when it connects, and `catalog.exclude` leaves folders of generated code out of the component catalog, so they are not counted as uses.
|
|
8
|
+
- 2034818: A tree drawn under transforms no longer costs a walk of all of it on every frame. A transformed node's subtree bounds are now its box and its children's carried out through its transform, where they were left unbounded and made every ancestor unbounded too; the hit tester passes such a subtree over when the point is outside it, and the accessibility mirror passes over what a panned, zoomed parent draws off screen (`LayoutEngine.screenBounds`). `InspectionService.box(node)` gives a node's box without building the rest of its report, for an editor that measures many nodes as its view moves. On a design tool's board of a thousand frames under one camera, a pan went from about one frame in three seconds to frames of about 25 milliseconds.
|
|
9
|
+
- c053bc3: A length that is not one (`width: '100%'`) is refused where it is written, naming the element, instead of throwing in layout: that throw abandoned every frame, so one element's mistake stopped the whole application drawing. One that arrives bound, from an Observable, is reported once and laid out as if unset. `renderRoot(...).onError(listener)` hears every error the render worker reports to the shell, for an application that shows its errors itself with the plugin's `overlay: false`.
|
|
10
|
+
- 6a7c397: `InspectionService.preview(node, property, value)` draws a node as though it declared a value, over its own and over its modifiers, until `endPreview` or the node leaves the tree: what a design editor shows while a colour is hovered or a padding dragged, before writing it to the source.
|
|
11
|
+
- 0307034: An inspector report's owners say what each component's parent supplied for its inputs, as `inputs`: a word, a number or a flag as it is, an element (a slot the parent filled) as `{ element: true }`, and handlers or data left out. A design tool can then write a component's use back as code, `<TextField label="Email" />`, and an inspector can show a component's props. `ComponentHost.suppliedInputs()` is what it reads.
|
|
12
|
+
- b85d643: `rasterize` keeps what a subtree paints as pixels and copies them while only what is above it moves or scales: a design tool's frames under a zooming camera are one `drawImage` each, not every photograph and path drawn again. Canvas2D draws a kept subtree again when anything in it changes, and once a zoom settles at another scale, sharp; while the zoom moves it stretches what it has, redrawing only what fits the frame. `UiRenderer.nextRepaint` asks the runtime for that settling frame. Culling also goes through a transform that only moves and scales, so a zoomed-in camera draws only what is on screen; a node moved by its own transform is culled where it is drawn, not where its box is.
|
|
13
|
+
- 5b66cbf: SVG files draw. An `Image` whose source ends in `.svg` or is a `data:image/svg+xml` URL reads the file in the worker and draws it as vectors through `PaintSurface`, on either renderer, at whatever size the node is laid out at, rather than failing because `createImageBitmap` refuses SVG. `objectFit` places it, the file's own size sizes the node when nothing else does, and `currentColor` is the theme's text colour. Drawn: paths and basic shapes, groups, `<use>`, nested `<svg>`, transforms, fills and strokes with opacities, dashes, linear and radial gradients, clip paths and simple `<style>` rules; text, images, masks, filters, patterns and markers are skipped with a console note. New in `gesso-core`: `parseSvg`, `drawSvg`, `svgPaint`, `DefaultSvgResolver`, `isSvgSource`, and `imageSource`'s `vectors` argument; in `gesso-framework`, `MediaService.vectors`, `setSvgResolver` and the `media.vectors` option. `Image` is now built on a paint node rather than a box.
|
|
14
|
+
- 9c411b1: An application's agent tool can say it waits on purpose, for a person or for its next task, with `waitsUpToMs`: it is listed under MCP's `_meta` as `gesso/waitsUpToMs` (`WAITS_UP_TO`), and the bridges between the agent and the thread, the dev server's and the render worker's, give a call of it that long, where they gave up on every call after two minutes.
|
|
15
|
+
- 803a189: Touch, apart from the screen's size. What the person points with is now in the environment (`UiEnvironmentKeys.pointer`, `fine` or `coarse`): the runtime reads it from the device (CSS's `pointer` feature), keeps it on `ShellService.pointer`, and provides it at the root, and a `pointer` prop says it for a subtree, as a design tool's phone frame does. A theme provided under a coarse pointer is turned for it (`withPointer`): its `touch` values (larger type, more room, token groups of its own) are laid over it, and a token group it does not carry resolves to the group's own `coarse` values (`defineThemeExtension({ coarse })`). The controls use that: on touch a medium `Button` and a `TextInput` are at least 44 high (`TOUCH_TARGET`), a small button 36 and a large one 52. With a mouse nothing moves.
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- 0e73baa: The dev server's agent endpoint now offers the project's own components, so an AI agent designing a screen uses what is there instead of drawing it again from boxes. `library_overview` lists every component in one line each, `library_search` finds the ones a few words describe, and `library_get` gives one in full: each input's type, default and meaning, examples, and the files that already use it. They are read from the files the project's tsconfig includes, a component saved in a new file is in the next answer, and they are answered on the dev server, so an agent can read them before a page is open. A library's components join only when named: `gesso({ catalog: { libraries: ['gesso-components'] } })`, and `catalog: false` leaves the tools out.
|
|
20
|
+
|
|
21
|
+
`tokens_view` joins the screen tools: the colours, type styles, corner radii, spacing scale and shadows of the theme in force, by name and value, at a control or at the application's outermost content. An agent told the names writes `backgroundColor="surface"` rather than a colour that will not follow the theme into dark mode. `GessoRuntime.themeAt(id)` gives the same theme to anything else that asks.
|
|
22
|
+
|
|
23
|
+
- cf57c23: `tokens_view` and `render_frame` default to the themes an application designs in, when it names them with `renderThemesWith`: a design editor's project's theme rather than the editor's own, which is what an agent designing that project needs to read and draw in. Both take `theme: 'app' | 'light' | 'dark'`, `app` being the running application's own. `formatThemeTokens`'s second argument is now the sentence that names the theme ("The theme in force at …"), not only the place.
|
|
24
|
+
- 407cefe: A desktop app's agents get `tokens_view` and `render_frame` from its windows, as a dev server's do. The relay from a window to the main process let through only tools named `ui_*`, so the two newer screen tools never crossed; it now lets through the screen's tools by name (`SCREEN_TOOL_NAMES`). `createScreenAgent({ windowChannels: true })` lets the window's channels through as well, for an application whose state lives in its window, and takes a `confirm` for their `@confirm` commands.
|
|
25
|
+
|
|
26
|
+
`renderRoot(App).loadModulesWith(load)` says how `render_frame` loads the module an agent names, for an application whose modules are served from somewhere other than its own root, such as an editor serving the project it designs from another folder.
|
|
27
|
+
|
|
28
|
+
- 17711c4: `InspectionService` gives a component the inspector's answers about what is on the screen: `nodesAt(x, y)` is every node under a point, topmost first, in the coordinates a pointer event carries, and `inspect(node)` is the inspector's report on one, with the line that wrote it and where each component around it was used. It is for tools built in Gesso that point at Gesso, such as a design editor whose canvas holds live frames and where a click picks what is under it instead of pressing it. Injected like any service: `ctx.inject(InspectionService)`.
|
|
29
|
+
- 4e0b6b7: Under `vite dev`, an element written in JSX remembers the file, line and column it was written on, and a component remembers where it was used. The inspector's report says so: a node's `source` is the line that made it, and each owner's `usedAt` is the line that used the component around it, so `formatNodeReport` now reads `written at src/Settings.tsx:12:7` and `rendered by Card (used at src/Settings.tsx:30:9)`. `UiGraphBuilder.sourceOf(node)` gives the same answer for any node. A canvas has no DOM for the browser's own inspector to point at, and this is the way back from a box on screen to the code. A production build compiles to `jsx`, which is given no location, so it costs nothing there.
|
|
30
|
+
- 4956e76: `renderFrame` waits for a picture still being fetched or decoded before it draws, rather than taking the tree's quiet for finished: a large image could arrive after the frame was drawn, leaving it out. `mediaLoadsInFlight()` says how many pictures the media modifiers in this thread are still loading.
|
|
31
|
+
- bf6cf01: `render_frame` joins the screen tools an agent gets: it draws a module of the project to a PNG, at the width asked for, and answers with it as an MCP image. It draws the module's default export, its only component, or the export named, with the inputs given, in the running application's theme unless `light` or `dark` is asked for. Without a height a frame is as tall as its content; given one, a screen fills it. The answer says when nothing was drawn, and what was thrown while it was drawn. A frame shares the application's channels and services, so a screen shows its real data.
|
|
32
|
+
|
|
33
|
+
`renderFrame(root, { width })` is the same drawing for any other use: a tree drawn once by a runtime of its own on an `OffscreenCanvas`, run until it stops asking for frames, sized to its content and read back as PNG bytes.
|
|
34
|
+
|
|
35
|
+
An error a frame causes is reported to the agent rather than over the person's screen. The render worker and the devtools overlay leave alone an error a frame has claimed (`claimErrors` in the framework, under a `Symbol.for` key the overlay reads).
|
|
36
|
+
|
|
37
|
+
`ServiceRegistry` takes a parent, which `get` falls back to and `has` does not consult, so an offscreen runtime finds the application's services and still registers its own.
|
|
38
|
+
|
|
39
|
+
A tool result's `content` can now hold an image (`AgentContent`), so code that read `content[0].text` without checking the part's type needs `resultText(result)`, which joins a result's text.
|
|
40
|
+
|
|
41
|
+
- cdca567: `renderRoot(App).renderThemesWith(() => ({ light, dark }))` says what `render_frame`'s `light` and `dark` draw in, asked on each call: an application's own themes rather than Gesso's, such as the design system of a project a design editor is designing, so an agent sees a mock in the product's colours and type.
|
|
42
|
+
- c35209d: A theme carries `units`, what an inch and a rem are worth in pixels: `{ pixelsPerInch: 96, rootFontSize: 16 }` on the stock themes, as in CSS. `lengths(theme.units)` gives `rem`, `inch`, `pt` and `mm` conversions to pixels, resolved where a value is written, so a theme can write its scales from the units it carries and a screen can write a print margin in millimetres or type in points. `themesEqual` compares the units. `UiTheme.units` is required, so a theme written without spreading a stock one has to add it. `tokens_view` reports the units, and tells an agent how to write a length in them.
|
|
43
|
+
- 688eaa2: A trackpad pinch in WebKit (Safari, and the macOS web view an Electrobun app runs in) reaches the application as the ctrl-wheel Chromium and Firefox send for one, `deltaY` being -100·ln of each step's scale. Before, WebKit's gesture events went unheard, so a canvas that zooms on a ctrl-wheel could not be pinched there and a web view that magnifies zoomed the whole page instead. The gesture is kept from the page by the same rule as a wheel: when the chain under the pointer would take it, which `overscrollBehavior="contain"` says of every wheel.
|
|
44
|
+
- Updated dependencies [2034818]
|
|
45
|
+
- Updated dependencies [c053bc3]
|
|
46
|
+
- Updated dependencies [85d58ae]
|
|
47
|
+
- Updated dependencies [ae6af9a]
|
|
48
|
+
- Updated dependencies [4e0b6b7]
|
|
49
|
+
- Updated dependencies [2c55872]
|
|
50
|
+
- Updated dependencies [6a7c397]
|
|
51
|
+
- Updated dependencies [3ababdf]
|
|
52
|
+
- Updated dependencies [b85d643]
|
|
53
|
+
- Updated dependencies [4956e76]
|
|
54
|
+
- Updated dependencies [6a7d6d4]
|
|
55
|
+
- Updated dependencies [29e3863]
|
|
56
|
+
- Updated dependencies [cf99e93]
|
|
57
|
+
- Updated dependencies [5b66cbf]
|
|
58
|
+
- Updated dependencies [c35209d]
|
|
59
|
+
- Updated dependencies [639be4a]
|
|
60
|
+
- Updated dependencies [9c8c730]
|
|
61
|
+
- Updated dependencies [803a189]
|
|
62
|
+
- gesso-core@0.7.0
|
|
63
|
+
|
|
64
|
+
## 0.6.30
|
|
65
|
+
|
|
66
|
+
### Patch Changes
|
|
67
|
+
|
|
68
|
+
- Updated dependencies [82104d1]
|
|
69
|
+
- gesso-core@0.6.30
|
|
70
|
+
|
|
3
71
|
## 0.6.29
|
|
4
72
|
|
|
5
73
|
### Patch Changes
|
|
@@ -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
2
|
import { LayoutBox, Reactive, UiChild, UiModifier } from "gesso-core";
|
|
4
|
-
//#region src/
|
|
3
|
+
//#region src/channel/ChannelToken.d.ts
|
|
5
4
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
16
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
90
|
+
readonly view: View;
|
|
23
91
|
/**
|
|
24
|
-
*
|
|
92
|
+
* The commands, as a type rather than as implementations.
|
|
25
93
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
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
|
-
|
|
98
|
+
readonly commands?: Commands;
|
|
35
99
|
}
|
|
36
|
-
//#endregion
|
|
37
|
-
//#region src/InternalState.d.ts
|
|
38
100
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
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
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
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
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
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
|
-
|
|
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
|
|
@@ -504,5 +748,5 @@ type ComponentArgs<C> = RequiredKeys<ComponentProps<C>> extends never ? [inputs?
|
|
|
504
748
|
*/
|
|
505
749
|
declare function isClassComponent(component: ComponentType): component is ClassComponent;
|
|
506
750
|
//#endregion
|
|
507
|
-
export {
|
|
508
|
-
//# sourceMappingURL=FunctionComponent-
|
|
751
|
+
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 };
|
|
752
|
+
//# sourceMappingURL=FunctionComponent-Czkex8fp.d.ts.map
|