gesso-framework 0.2.0 → 0.3.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 +100 -0
- package/README.md +1 -1
- package/dist/{index-C8yYHFKz.d.ts → index-D3SBTLAB.d.ts} +2990 -2608
- package/dist/index.d.ts +2 -2
- package/dist/index.js +6418 -5899
- package/dist/index.js.map +1 -1
- package/dist/{persisted-Ddb51avc.js → persisted-r3XmooBP.js} +2 -2
- package/dist/{persisted-Ddb51avc.js.map → persisted-r3XmooBP.js.map} +1 -1
- package/dist/worker/index.d.ts +1 -1
- package/dist/worker/index.js +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,105 @@
|
|
|
1
1
|
# gesso-framework
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 025321a: **Breaking: the single-thread configuration is `createSyncApp`.** `createApp` now
|
|
8
|
+
builds the worker configuration and nothing else.
|
|
9
|
+
|
|
10
|
+
```diff
|
|
11
|
+
-createApp(AppRoot).useChannel(Catalog, { source }).mountSync('#app');
|
|
12
|
+
+createSyncApp(AppRoot).useChannel(Catalog, { source }).mountSync('#app');
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Nothing else changes: same builder, same methods, same `mountSync`. The worker
|
|
16
|
+
form, `createApp({ renderWorker })` and the form `gesso-vite-plugin` writes, is
|
|
17
|
+
untouched, so an application that mounts into a render worker needs no edit.
|
|
18
|
+
`createApp` given a component throws and names `createSyncApp`, because the fix
|
|
19
|
+
is one identifier and a message that does not say which one turns a rename into
|
|
20
|
+
an afternoon.
|
|
21
|
+
|
|
22
|
+
The reason is what it costs on the wire. `createApp` took either the options or
|
|
23
|
+
a root component, and the overload that took a component reached
|
|
24
|
+
`GessoAppBuilder` → `GessoApp` → `GessoRuntime`: the layout engine, both
|
|
25
|
+
renderers and the hit-tester, statically, in every shell. A bundler cannot see
|
|
26
|
+
which half of one function a given call reaches, so every worker application
|
|
27
|
+
shipped the whole engine to the thread whose entire job is to create a canvas
|
|
28
|
+
and forward input.
|
|
29
|
+
|
|
30
|
+
Measured on the smallest honest shell, built from source:
|
|
31
|
+
|
|
32
|
+
| | raw | gzipped |
|
|
33
|
+
| ------ | -------- | -------- |
|
|
34
|
+
| before | 662.9 kB | 169.3 kB |
|
|
35
|
+
| after | 46.2 kB | 12.3 kB |
|
|
36
|
+
|
|
37
|
+
And on a real application, gessosheet's shell: 524.9 kB to 152.9 kB raw, 152.7 kB
|
|
38
|
+
to 45.0 kB gzipped. `check-bundle-size.ts` holds the line in CI with a budget and
|
|
39
|
+
with a look for Canvas2D calls in the shell's bytes, because the number alone
|
|
40
|
+
would pass a build that kept the rasterizer and got lucky.
|
|
41
|
+
|
|
42
|
+
Two names cost one line in the configuration that was always the exception, and
|
|
43
|
+
take 120 kB off the main thread of every other kind.
|
|
44
|
+
|
|
45
|
+
- 025321a: **`fanOut`, for when every node wants its own slice.** One source, N things on
|
|
46
|
+
screen, each reading one part of it: a grid, a timeline, a log viewer all arrive
|
|
47
|
+
at this shape, and the natural spelling does not scale. A pipe per slice runs N
|
|
48
|
+
pipelines on every emission whatever changed, and RxJS removes an observer from a
|
|
49
|
+
Subject by scanning its list, so tearing down a window of N is quadratic.
|
|
50
|
+
|
|
51
|
+
`fanOut(source, read, { initial })` holds one subscription for the whole registry
|
|
52
|
+
and hands out a stable cell per key. Its `changed` hint is where a frame is won: a
|
|
53
|
+
source that knows which keys a patch touched reads only those. Ten thousand live
|
|
54
|
+
keys, measured against a pipe per key: mount 29.8 ms to 10.7 ms, teardown 9.7 ms
|
|
55
|
+
to 3.7 ms, and one key changing 2.2 ms to 0.1 ms.
|
|
56
|
+
|
|
57
|
+
Reach for it when N is large _and each emission touches few of them_. A source
|
|
58
|
+
that republishes its whole window on every scroll gets the cheaper mount and
|
|
59
|
+
teardown and nothing from `changed`, because every key really did change.
|
|
60
|
+
|
|
61
|
+
It compares by reference where `select` and `derive` compare by content, which is
|
|
62
|
+
the opposite default for the opposite reason: those run once per emission and this
|
|
63
|
+
runs once per live key per emission. And a registry that has grown past a couple
|
|
64
|
+
of thousand keys having released none of them says so once, because `release` is
|
|
65
|
+
the caller's and forgetting it is the one thing here that goes wrong silently.
|
|
66
|
+
|
|
67
|
+
**`tabStop`, which is `tabindex="-1"`.** Focusable, reachable by a press and by
|
|
68
|
+
`focus()`, skipped by the Tab cycle. `focusable` only ever answered "may this node
|
|
69
|
+
hold focus", which is the wrong question for a container: `UiFocusManager.settleScope`
|
|
70
|
+
blurred when a scope held nothing focusable, so a `Dialog` whose content is a
|
|
71
|
+
sentence handed the keyboard to nothing and could not be dismissed with Escape.
|
|
72
|
+
`settleScope` now falls back to the scope root before blurring, and `Dialog` sets
|
|
73
|
+
`focusable: true, tabStop: false` on its body. Both halves are needed and neither
|
|
74
|
+
is enough alone.
|
|
75
|
+
|
|
76
|
+
**`borders()`, a border per edge, as paint.** `borderWidth` is one number and
|
|
77
|
+
`borderColor` one colour, so a node could not have a heavy bottom edge and a
|
|
78
|
+
hairline top. A border here is paint-only and a decoration is already a coloured
|
|
79
|
+
rectangle in the node's own paint pass, so four edges are four draw instances and
|
|
80
|
+
no extra nodes. `DecorationBox` gains `right` and `bottom` to put them: any two of
|
|
81
|
+
near edge, size and far edge fix an axis, which is CSS's rule for an absolutely
|
|
82
|
+
positioned box, and the only one that can express a side edge spanning between two
|
|
83
|
+
horizontal ones.
|
|
84
|
+
|
|
85
|
+
**`menuBarStep`, a menu bar's keyboard, as a peer of `Menu`.** `Menu` traps focus,
|
|
86
|
+
which is right for a popup opened by a button and wrong for a bar: with focus in
|
|
87
|
+
the panel, ArrowLeft cannot reach the bar to move to the menu next door, and that
|
|
88
|
+
is most of what makes a bar a bar. A pure function, generic in the command type,
|
|
89
|
+
that returns null for a key it does not claim, so a bar can still be tabbed out of.
|
|
90
|
+
|
|
91
|
+
### Patch Changes
|
|
92
|
+
|
|
93
|
+
- Updated dependencies [025321a]
|
|
94
|
+
- Updated dependencies [025321a]
|
|
95
|
+
- gesso-core@0.3.0
|
|
96
|
+
|
|
97
|
+
## 0.2.1
|
|
98
|
+
|
|
99
|
+
### Patch Changes
|
|
100
|
+
|
|
101
|
+
- gesso-core@0.2.1
|
|
102
|
+
|
|
3
103
|
## 0.2.0
|
|
4
104
|
|
|
5
105
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -70,7 +70,7 @@ The shell spawns both workers, joins them with one `MessageChannel`, hands each
|
|
|
70
70
|
|
|
71
71
|
A component body runs **once**, and an `Observable` binds straight into the retained graph. So there is no re-render pass, no virtual DOM diff, no `useEffect`, and no base class to extend for your state. Component identity _is_ node identity, so there is no second reconciler.
|
|
72
72
|
|
|
73
|
-
Single-thread mode exists for tests, headless rendering and environments without `OffscreenCanvas`: `
|
|
73
|
+
Single-thread mode exists for tests, headless rendering and environments without `OffscreenCanvas`: `createSyncApp(NotesApp).useChannel(Notes, { source }).mountSync('#app')`. Channels resolve in-process there, so the same contract runs with no ports.
|
|
74
74
|
|
|
75
75
|
## Entry points
|
|
76
76
|
|