@solidrt/core 0.0.51 → 0.0.53
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/AGENTS.md +105 -21
- package/README.md +1 -1
- package/docs/index.md +154 -0
- package/docs/reference/detached.md +85 -0
- package/docs/reference/drawing.md +95 -0
- package/docs/reference/elements.md +56 -0
- package/docs/reference/gpu.md +207 -0
- package/docs/reference/index.md +50 -0
- package/docs/reference/input.md +58 -0
- package/docs/reference/layout.md +44 -0
- package/docs/reference/shaders.md +46 -0
- package/docs/reference/text.md +46 -0
- package/docs/reference/transforms.md +35 -0
- package/docs/reference/types.md +34 -0
- package/examples/README.md +5 -3
- package/examples/gpu-pipeline.tsx +2 -2
- package/examples/line-points.tsx +145 -0
- package/examples/parse-svg.tsx +6 -6
- package/examples/responsive-grid.tsx +1 -1
- package/examples/scroll.tsx +2 -2
- package/examples/snapshot-texture.tsx +72 -0
- package/examples/{view-viewbox.tsx → view-design-size.tsx} +33 -15
- package/package.json +6 -5
- package/src/core.ts +19 -1
- package/src/gpu.ts +86 -34
- package/src/index.ts +8 -2
- package/src/logo.tsx +92 -0
- package/src/renderer.ts +189 -34
- package/src/runtime-modules.d.ts +4 -0
- package/src/scroll.ts +50 -14
- package/src/svg.ts +1 -1
- package/src/text-input.ts +0 -1
- package/src/types.d.ts +121 -29
- package/src/window.ts +52 -7
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# GPU
|
|
2
|
+
|
|
3
|
+
`@solidrt/core/gpu` is the programmable half of rendering: textures you
|
|
4
|
+
upload, shader passes that render into textures, and pipelines that draw
|
|
5
|
+
your own geometry into them. Everything it produces is a texture id, and a
|
|
6
|
+
texture id goes anywhere a texture goes: `<texture src={id} />` to display
|
|
7
|
+
it, a `textures` binding to sample it from another pass, `readTexture` to
|
|
8
|
+
bake it.
|
|
9
|
+
|
|
10
|
+
The imperative primitives (`uploadTexture`, `setTargetParams`, `addDraw`,
|
|
11
|
+
`destroyTexture`, ...) are the `flux:gpu` module and are documented in the
|
|
12
|
+
[runtime reference](/runtime/gui/gpu/). This page is the reactive layer
|
|
13
|
+
over them and the contracts that hold across both.
|
|
14
|
+
|
|
15
|
+
## The model
|
|
16
|
+
|
|
17
|
+
A target is retained, not redrawn. Creating one renders it once; after that
|
|
18
|
+
it re-renders exactly when something it depends on changes - a param, a
|
|
19
|
+
bound texture, a buffer it draws from, its size - and never otherwise. A
|
|
20
|
+
static shader costs zero passes per frame.
|
|
21
|
+
|
|
22
|
+
Sampler bindings are live dependencies. A target bound as another's
|
|
23
|
+
`textures` input re-renders its consumer whenever it re-renders itself, in
|
|
24
|
+
topological order, so a chain (a plasma pass feeding a mesh pipeline) is
|
|
25
|
+
driven by writing only the first target's uniforms. A cycle throws.
|
|
26
|
+
|
|
27
|
+
Targets whose pass is state rather than a function of its inputs
|
|
28
|
+
(accumulation, feedback, simulation) opt out with `render: "manual"`: the
|
|
29
|
+
runtime never renders them, only `renderTarget(id)` does, in call order,
|
|
30
|
+
normally from `onFrame`.
|
|
31
|
+
|
|
32
|
+
## Three layers
|
|
33
|
+
|
|
34
|
+
- **Fused.** `createShaderTexture` compiles a fragment source and renders it
|
|
35
|
+
fullscreen; `createPipelineTexture` does the same over your own vertex
|
|
36
|
+
buffer. One call, one texture, program and target sharing a lifetime. The
|
|
37
|
+
shader-toy shape.
|
|
38
|
+
- **Raw.** `compileShader`, `linkProgram`, `createRenderPipeline`, then
|
|
39
|
+
`createShaderTarget` per target. A pipeline (program plus draw state:
|
|
40
|
+
attribute layout, topology, blend, cull, depth) backs any number of targets
|
|
41
|
+
and compiles nothing per target. Reach for it when programs are shared or
|
|
42
|
+
lifetimes differ.
|
|
43
|
+
- **Draw list.** `createDrawTarget` renders many entries in one pass, each
|
|
44
|
+
entry its own pipeline, buffer, params and textures, added and removed with
|
|
45
|
+
`addDraw` / `removeDraw` and sorted with `setDrawOrder`. A scene, in one
|
|
46
|
+
texture.
|
|
47
|
+
|
|
48
|
+
`@solidrt/3d` builds its scene graph on the third layer; nothing there is
|
|
49
|
+
hidden from this one.
|
|
50
|
+
|
|
51
|
+
## Creating
|
|
52
|
+
|
|
53
|
+
Every `create*` helper frees its resource when the reactive owner that
|
|
54
|
+
created it is disposed. Created outside a reactive scope (after an `await`,
|
|
55
|
+
in an event handler with no owner) nothing is registered and the matching
|
|
56
|
+
`destroy*` is yours to call; `{ autoFree: false }` opts out of the auto-free
|
|
57
|
+
inside a scope for resources rebuilt by hand. `label` names the resource in
|
|
58
|
+
the dev tooling's GPU inventory and in engine log messages.
|
|
59
|
+
|
|
60
|
+
{{ decl packages/core/src/gpu.ts createTexture }}
|
|
61
|
+
|
|
62
|
+
{{ decl packages/core/src/gpu.ts createMutableTexture }}
|
|
63
|
+
|
|
64
|
+
{{ decl packages/core/src/gpu.ts createShaderTexture }}
|
|
65
|
+
|
|
66
|
+
{{ decl packages/core/src/gpu.ts createPipelineTexture }}
|
|
67
|
+
|
|
68
|
+
{{ decl packages/core/src/gpu.ts createShaderTarget }}
|
|
69
|
+
|
|
70
|
+
{{ decl packages/core/src/gpu.ts createDrawTarget }}
|
|
71
|
+
|
|
72
|
+
{{ decl packages/core/src/gpu.ts createShaderTextureMemo }}
|
|
73
|
+
|
|
74
|
+
{{ decl packages/core/src/gpu.ts ShaderSpec }}
|
|
75
|
+
|
|
76
|
+
## Buffers
|
|
77
|
+
|
|
78
|
+
{{ decl packages/core/src/gpu.ts createBuffer }}
|
|
79
|
+
|
|
80
|
+
{{ decl packages/core/src/gpu.ts beginBufferWrite }}
|
|
81
|
+
|
|
82
|
+
{{ decl packages/core/src/gpu.ts writeBuffer }}
|
|
83
|
+
|
|
84
|
+
A buffer's size is fixed at creation, an entry's buffers are not: `setDraw`
|
|
85
|
+
(single-draw targets) and `setDrawBuffers` (draw-list entries) re-point a
|
|
86
|
+
role the entry already fills - `buffer`, `indexBuffer` + `indexFormat`,
|
|
87
|
+
`instanceBuffer` - at another buffer. That is how a population grows past
|
|
88
|
+
its reservation: create a larger buffer, write it, swap, destroy the old.
|
|
89
|
+
The swap is replace-only (roles are pipeline layout state) and keeps the
|
|
90
|
+
entry's draw range, rechecked against the new sizes.
|
|
91
|
+
|
|
92
|
+
## Uniforms
|
|
93
|
+
|
|
94
|
+
Uniforms are driven by name. Declaratively, `<texture src={id}
|
|
95
|
+
params={{ uTime: t() }} />` writes the target's params paced to the next real
|
|
96
|
+
repaint, which is the preferred form; `setTargetParams` and `setDrawParams`
|
|
97
|
+
are the imperative forms for targets no element holds. A number drives a
|
|
98
|
+
scalar, a flat number array drives the declared type (2, 3, 4 for `vec2`,
|
|
99
|
+
`vec3`, `vec4`, 16 column-major for `mat4`, element size times length for
|
|
100
|
+
arrays). `textures` binds `sampler2D` uniforms to texture ids the same way.
|
|
101
|
+
|
|
102
|
+
Every write is validated at the call site against the linked program:
|
|
103
|
+
|
|
104
|
+
- a name the program never declares throws, listing the active uniforms;
|
|
105
|
+
- a value whose length does not fit the declared type throws;
|
|
106
|
+
- a `sampler2D` named in `params` (or a non-sampler named in `textures`)
|
|
107
|
+
throws;
|
|
108
|
+
- a name the source declares but the compiler optimized out is accepted
|
|
109
|
+
with a warning and skipped, so one param object can drive several shader
|
|
110
|
+
variants that do not all read every uniform.
|
|
111
|
+
|
|
112
|
+
On a draw target, target-level `params` and `textures` are the shared set
|
|
113
|
+
every entry reads (a camera's view-projection written once per move), applied
|
|
114
|
+
before each entry's own, and a shared name only some entries declare applies
|
|
115
|
+
where declared.
|
|
116
|
+
|
|
117
|
+
The fused preamble declares exactly what the runtime provides: `#version
|
|
118
|
+
300 es`, precision, `vUV` (fragment path), `fragColor`, and `iResolution`,
|
|
119
|
+
filled with the target size in physical pixels. Anything app-driven - a time
|
|
120
|
+
uniform - is the source's own declaration, driven like any other uniform, so
|
|
121
|
+
forgetting to drive it is a compile error rather than a value stuck at zero.
|
|
122
|
+
A source starting with its own `#version` line gets no preamble and compiles
|
|
123
|
+
as written.
|
|
124
|
+
|
|
125
|
+
## Sampling
|
|
126
|
+
|
|
127
|
+
`filter` (`"linear"` default, `"nearest"`), `wrap` (`"clamp"` default,
|
|
128
|
+
`"repeat"`) and `mipmap` (`false` default) are declared at creation and are
|
|
129
|
+
a property of the texture id: `<texture>` display and shader sampling follow
|
|
130
|
+
the same state, so a nearest texture upscales with hard pixels everywhere.
|
|
131
|
+
Changing any of them means a new id.
|
|
132
|
+
|
|
133
|
+
Without a mip chain, shader sampling of a minified texture skips texels and
|
|
134
|
+
aliases (3d surfaces at distance, a target sampled at a fraction of its
|
|
135
|
+
size). `mipmap: true` keeps the chain on the id and the runtime rebuilds it
|
|
136
|
+
after every upload (data textures) and every render (targets) - there is
|
|
137
|
+
nothing to schedule. Shader sampling then minifies through it (trilinear
|
|
138
|
+
for `"linear"`, per-level nearest for `"nearest"`). The `<texture>` display
|
|
139
|
+
draw samples the full-size level only, so a supersampled target shown
|
|
140
|
+
through `<texture>` should stay at 2x. Rebuilding is one GPU pass per
|
|
141
|
+
upload or render; a per-frame texture pays it per frame.
|
|
142
|
+
|
|
143
|
+
One binding can deviate: a `textures` value may be `{ id, filter?, wrap? }`
|
|
144
|
+
instead of a bare id, sampling that texture with a different filter or
|
|
145
|
+
wrap in this binding only - blur a `"nearest"` atlas linearly, tile a
|
|
146
|
+
clamped target in one consumer. The texture's own state stays what
|
|
147
|
+
`<texture>` paints and what every other binding uses. `mipmap` is not
|
|
148
|
+
overridable: the chain either exists on the id or it does not.
|
|
149
|
+
|
|
150
|
+
## Blending
|
|
151
|
+
|
|
152
|
+
Combining passes is a render-tree job: stack `<texture>` elements and set
|
|
153
|
+
`blendMode` on them. Within one pipeline draw, `blend` on the pipeline
|
|
154
|
+
decides how overlapping geometry combines: `"add"` and `"multiply"` are
|
|
155
|
+
order-independent (glows, shadows); `"alpha"` composites over in draw-list
|
|
156
|
+
order with premultiplied output, normally after the opaques with
|
|
157
|
+
`depthWrite: false`, and nothing sorts for you (`setDrawOrder` does, or a
|
|
158
|
+
scene layer above). Anything else overwrites.
|
|
159
|
+
|
|
160
|
+
## The pixel contract
|
|
161
|
+
|
|
162
|
+
Three facts hold for every texture and target:
|
|
163
|
+
|
|
164
|
+
- **Clip space is y-down.** `gl_Position` y = -1 is the top of the target.
|
|
165
|
+
A vertex stage carrying camera-up geometry negates y or folds the flip into
|
|
166
|
+
its projection. The fragment path absorbs the flip, so `vUV` is 0..1 with
|
|
167
|
+
a top-left origin.
|
|
168
|
+
- **Color is premultiplied alpha.** A target's RGB is already multiplied by
|
|
169
|
+
its A: write `vec4(rgb * a, a)`. `vec4(rgb, a)` composites as opaque.
|
|
170
|
+
`clearColor` is premultiplied too, and so are uploaded pixels:
|
|
171
|
+
`decodeImage` premultiplies at the codec boundary (image files store
|
|
172
|
+
straight alpha) and `encodeImage` converts back, so pixels inside the app
|
|
173
|
+
are premultiplied everywhere.
|
|
174
|
+
- **Values are non-linear RGBA8** (or `"r8"` for single-channel data), with
|
|
175
|
+
no color-space conversion anywhere. Filtering and blending operate on the
|
|
176
|
+
stored values.
|
|
177
|
+
|
|
178
|
+
## UI as a texture
|
|
179
|
+
|
|
180
|
+
`snapshotTexture(ref)` (from `@solidrt/core`) is the texture id behind a
|
|
181
|
+
`repaintBoundary="snapshot"` view: the subtree's rasterized pixels, usable
|
|
182
|
+
wherever a texture id is - a `<texture>`, a shader or draw target binding,
|
|
183
|
+
a 3d material. The id is allocated on first call and stable for the node's
|
|
184
|
+
lifetime; the runtime re-points it after every re-rasterization, which
|
|
185
|
+
happens only when the subtree changes, so a static panel sampled by an
|
|
186
|
+
animated consumer costs no repaint. Premultiplied, top-left origin, cropped
|
|
187
|
+
to the layout box. Empty until the boundary's first paint. The boundary owns
|
|
188
|
+
it: `destroyTexture` on it throws, and unmounting the boundary releases it
|
|
189
|
+
on the deferred-destroy path (a consumer still sampling it keeps the last
|
|
190
|
+
pixels). A boundary showing its own texture is not a feedback loop: its
|
|
191
|
+
rasterization is the change, so it does not re-invalidate itself.
|
|
192
|
+
|
|
193
|
+
## Readback
|
|
194
|
+
|
|
195
|
+
`captureSnapshot` renders a render-tree node to pixels and `readTexture`
|
|
196
|
+
reads any texture back; both resolve `{ width, height, data }`. This is the
|
|
197
|
+
one-shot bake path (a glyph atlas, a processed image), paid for with a
|
|
198
|
+
readback stall and a paint pass of latency per call. Never per frame; to
|
|
199
|
+
feed live content into a shader, bind a texture that updates in place
|
|
200
|
+
instead - for a UI subtree, `snapshotTexture`.
|
|
201
|
+
|
|
202
|
+
## Limits
|
|
203
|
+
|
|
204
|
+
`limits` holds the device ceilings queried at startup: maximum texture and
|
|
205
|
+
target size, sampler inputs per pass, vertex attributes per pipeline. Creates
|
|
206
|
+
and binds validate against them and throw naming the limit; read them to
|
|
207
|
+
size within the device instead.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Reference
|
|
2
|
+
|
|
3
|
+
Core's API is its types. `@solidrt/core` ships `types.d.ts` and
|
|
4
|
+
`jsx-runtime.d.ts` as sources, with a doc comment on every prop that deviates
|
|
5
|
+
from the CSS or DOM meaning you already expect, so these pages show those
|
|
6
|
+
declarations directly rather than paraphrasing them.
|
|
7
|
+
|
|
8
|
+
They are grouped by subject, not by element. Most props live on several
|
|
9
|
+
elements at once (every element takes pointer handlers, every drawing
|
|
10
|
+
primitive takes paint props), so a page per element would print the same
|
|
11
|
+
interface a dozen times and still not say where a prop comes from.
|
|
12
|
+
|
|
13
|
+
- [Elements](/core/reference/elements/) - `window` and `view`, the structural
|
|
14
|
+
vocabulary
|
|
15
|
+
- [Drawing](/core/reference/drawing/) - `rect`, `oval`, `line`, `path`,
|
|
16
|
+
`texture`, and the paint props they share
|
|
17
|
+
- [Text](/core/reference/text/) - `text` and `span`
|
|
18
|
+
- [Detached elements](/core/reference/detached/) - the `d-*` forms and their
|
|
19
|
+
paint-space geometry
|
|
20
|
+
- [Layout](/core/reference/layout/) - flexbox, the grid subset, and the box
|
|
21
|
+
props
|
|
22
|
+
- [Transforms](/core/reference/transforms/) - post-layout transform, opacity,
|
|
23
|
+
and scroll offsets
|
|
24
|
+
- [Input](/core/reference/input/) - handlers and the event objects they receive
|
|
25
|
+
- [Shaders](/core/reference/shaders/) - the `shader` prop on `window` and
|
|
26
|
+
`view`
|
|
27
|
+
- [GPU](/core/reference/gpu/) - textures, shader targets, pipelines and the
|
|
28
|
+
draw list from `@solidrt/core/gpu`
|
|
29
|
+
- [Types](/core/reference/types/) - the shared aliases
|
|
30
|
+
|
|
31
|
+
## The element vocabulary
|
|
32
|
+
|
|
33
|
+
Every JSX intrinsic and the prop interfaces it composes. `ref` is available on
|
|
34
|
+
all of them and is left out here.
|
|
35
|
+
|
|
36
|
+
{{ intrinsics packages/core/jsx-runtime.d.ts }}
|
|
37
|
+
|
|
38
|
+
Read a row as the sum of its parts: `<rect>` takes paint props, pointer props
|
|
39
|
+
and layout props, while `<d-rect>` swaps the layout props for detached
|
|
40
|
+
geometry. The pages above cover the interfaces in that table.
|
|
41
|
+
|
|
42
|
+
## Clearing a prop
|
|
43
|
+
|
|
44
|
+
Writing `undefined` (or `null`) to a prop resets it to its default - the
|
|
45
|
+
value it had before anything was set, per element kind. So the reactive
|
|
46
|
+
pattern `scale={style()?.scale}` is safe: when the binding clears, the
|
|
47
|
+
element returns to its unset state instead of erroring. On a `span`,
|
|
48
|
+
clearing a prop returns the run to inheriting from its paragraph. Content
|
|
49
|
+
props are the exception: `text` on a span and `d` on a path require a value.
|
|
50
|
+
A wrong-typed value is an error either way.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Input
|
|
2
|
+
|
|
3
|
+
Pointer, wheel, key and text events are props on any element. There is no
|
|
4
|
+
`addEventListener` and no event registry: a handler prop is the subscription.
|
|
5
|
+
|
|
6
|
+
Events travel from the hit leaf up to the root, and `stopPropagation()` ends
|
|
7
|
+
the walk.
|
|
8
|
+
|
|
9
|
+
## Handlers
|
|
10
|
+
|
|
11
|
+
{{ decl packages/core/src/types.d.ts PointerProps }}
|
|
12
|
+
|
|
13
|
+
`pointerEvents="none"` takes an element out of hit testing, and the walk skips
|
|
14
|
+
it as an ancestor too, so the `parentX`/`parentY` of an event stay in the frame
|
|
15
|
+
you would expect.
|
|
16
|
+
|
|
17
|
+
Focus is explicit: `setFocus(node)` moves it, `onFocus`/`onBlur` report it,
|
|
18
|
+
and `focusable` only marks an element as a candidate for `getFocusables()`,
|
|
19
|
+
so a focus-navigation scheme (a component set's, or your own) can enumerate
|
|
20
|
+
targets without the runtime imposing one.
|
|
21
|
+
|
|
22
|
+
## Pointer events
|
|
23
|
+
|
|
24
|
+
Coordinates are logical points, so a handler reads the same numbers on a
|
|
25
|
+
high-density phone screen as on a desktop monitor. Three frames are reported,
|
|
26
|
+
which is what makes drag idioms short:
|
|
27
|
+
|
|
28
|
+
{{ decl packages/core/src/types.d.ts PointerEvent }}
|
|
29
|
+
|
|
30
|
+
The drag idiom is `x = parentX - grab offset`, taking the grab offset from
|
|
31
|
+
`localX`/`localY` at pointer down.
|
|
32
|
+
|
|
33
|
+
{{ decl packages/core/src/types.d.ts WheelEvent }}
|
|
34
|
+
|
|
35
|
+
## Key events
|
|
36
|
+
|
|
37
|
+
Key events use the W3C UI Events vocabulary: `key` is the logical,
|
|
38
|
+
layout-dependent value (`"a"`, `"!"`, `"Enter"`, `"ArrowLeft"`), and `code` is
|
|
39
|
+
the physical key position (`"KeyA"`, `"Digit1"`).
|
|
40
|
+
|
|
41
|
+
{{ decl packages/core/src/types.d.ts KeyEvent }}
|
|
42
|
+
|
|
43
|
+
Routing differs from pointer events in where the walk starts: keydown and
|
|
44
|
+
keyup dispatch along the focused node's ancestor chain and always end at the
|
|
45
|
+
window root, and with nothing focused they go to the window root alone.
|
|
46
|
+
`<window onKeyDown>` is therefore the app-global shortcut point.
|
|
47
|
+
|
|
48
|
+
## Text entry
|
|
49
|
+
|
|
50
|
+
Printable characters do not arrive as key events. Text entry goes to the
|
|
51
|
+
focused node's `onTextInput`:
|
|
52
|
+
|
|
53
|
+
{{ decl packages/core/src/types.d.ts TextEvent }}
|
|
54
|
+
|
|
55
|
+
Focusing a field never raises the on-screen keyboard by itself. A tap on the
|
|
56
|
+
field does, or an explicit `startTextInput()`, and never while a physical
|
|
57
|
+
keyboard is attached. `textInputHints` on the node is read when a session
|
|
58
|
+
starts.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Layout
|
|
2
|
+
|
|
3
|
+
Layout is flexbox plus a line-based subset of CSS grid, over the whole element
|
|
4
|
+
tree. Prop names match CSS, so `flexDirection`, `alignItems`,
|
|
5
|
+
`justifyContent`, `gap`, `padding` and `width` mean what you expect.
|
|
6
|
+
|
|
7
|
+
These props exist on `window`, `view`, and the layout form of every drawing
|
|
8
|
+
and text primitive. They do not exist on the
|
|
9
|
+
[detached](/core/reference/detached/) forms, which own their geometry instead.
|
|
10
|
+
|
|
11
|
+
## Units
|
|
12
|
+
|
|
13
|
+
A bare number is pixels. A percentage is `pct(50)`, a branded value rather
|
|
14
|
+
than a parsed string, though the `"50%"` string form is accepted so pasted CSS
|
|
15
|
+
keeps working.
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
<view flexDirection="row" gap={8} padding={16}>
|
|
19
|
+
<view width={pct(50)} />
|
|
20
|
+
</view>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
{{ decl packages/core/src/types.d.ts Dimension }}
|
|
24
|
+
|
|
25
|
+
{{ decl packages/core/src/types.d.ts LengthPercentage }}
|
|
26
|
+
|
|
27
|
+
## The box
|
|
28
|
+
|
|
29
|
+
{{ decl packages/core/src/types.d.ts LayoutProps }}
|
|
30
|
+
|
|
31
|
+
Two divergences from CSS are worth reading twice. `position` has `relative`
|
|
32
|
+
and `absolute` only, and an absolute element does not itself become a
|
|
33
|
+
containing block: it resolves against the nearest ancestor with
|
|
34
|
+
`position="relative"`. And `float` / `clear` are not page layout; they apply
|
|
35
|
+
to an element child of a `<text>`, where it becomes an inline atom the lines
|
|
36
|
+
wrap around.
|
|
37
|
+
|
|
38
|
+
## Flexbox
|
|
39
|
+
|
|
40
|
+
{{ decl packages/core/src/types.d.ts FlexboxProps }}
|
|
41
|
+
|
|
42
|
+
## Grid
|
|
43
|
+
|
|
44
|
+
{{ decl packages/core/src/types.d.ts GridProps }}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Shaders
|
|
2
|
+
|
|
3
|
+
Two elements take a `shader` prop: `window` runs a program over the finished
|
|
4
|
+
frame, and `view` runs one over its own rasterized subtree. Both are
|
|
5
|
+
declarative - the pass exists while the prop is declared and the resources go
|
|
6
|
+
away when it is removed.
|
|
7
|
+
|
|
8
|
+
Everything about the program itself (compiling, linking, lifetime) belongs to
|
|
9
|
+
the raw shading layer: `compileShader` and `linkProgram` from
|
|
10
|
+
`@solidrt/core/gpu`.
|
|
11
|
+
|
|
12
|
+
## The shared contract
|
|
13
|
+
|
|
14
|
+
Both passes bind their input as `uniform sampler2D uSource` with a top-left
|
|
15
|
+
origin, like every sampled texture in the engine, and fill `iResolution` by
|
|
16
|
+
name with the pass size in physical pixels. Both fill `params` uniforms by
|
|
17
|
+
name, paced to the next real repaint: a number drives a scalar, and a flat
|
|
18
|
+
number array drives the declared GLSL type (2, 3 or 4 for `vec2`, `vec3`,
|
|
19
|
+
`vec4`, and 16 column-major for `mat4`).
|
|
20
|
+
|
|
21
|
+
## Window
|
|
22
|
+
|
|
23
|
+
{{ decl packages/core/src/types.d.ts WindowShaderProps }}
|
|
24
|
+
|
|
25
|
+
The window pass draws attributeless triangles with `vertexCount` vertices
|
|
26
|
+
fetched via `gl_VertexID`, defaulting to a single covering triangle.
|
|
27
|
+
|
|
28
|
+
## View
|
|
29
|
+
|
|
30
|
+
A boundary shader requires a snapshot boundary (`repaintBoundary="snapshot"`
|
|
31
|
+
or `"snapshot-no-aa"`). The cost is snapshot semantics, and it is kept
|
|
32
|
+
explicit: declared without one, the shader is ignored with a warning.
|
|
33
|
+
|
|
34
|
+
{{ decl packages/core/src/types.d.ts ViewShaderProps }}
|
|
35
|
+
|
|
36
|
+
The pass is split from content invalidation, so animating `params` over a
|
|
37
|
+
static subtree re-runs only the pass against the cached snapshot and never
|
|
38
|
+
re-rasterizes it.
|
|
39
|
+
|
|
40
|
+
The effect samples the subtree's own pixels and nothing else. Grading,
|
|
41
|
+
warping and dissolving a panel work; anything that needs what is *behind* the
|
|
42
|
+
panel does not. Hit testing stays on layout geometry, so a distortion moves
|
|
43
|
+
pixels, not hit targets.
|
|
44
|
+
|
|
45
|
+
`outset` grows the rasterized canvas by a transparent margin on every side,
|
|
46
|
+
for effects that write past the edge such as a glow or a drop shadow.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Text
|
|
2
|
+
|
|
3
|
+
`<text>` is a shaped paragraph, and `<span>` is a styled run inside it. Text is
|
|
4
|
+
laid out by the engine's own layout and shaping, not by a browser: a paragraph
|
|
5
|
+
is one element that wraps, aligns and truncates as a unit.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
<text fontSize={16} maxLines={2} textOverflow="ellipsis">
|
|
9
|
+
Weather for <span fontWeight={700}>Tuesday</span>
|
|
10
|
+
</text>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Run style
|
|
14
|
+
|
|
15
|
+
The style props of a run: paragraph defaults on `<text>`, overrides on
|
|
16
|
+
`<span>`.
|
|
17
|
+
|
|
18
|
+
{{ decl packages/core/src/types.d.ts TextRunProps }}
|
|
19
|
+
|
|
20
|
+
The cascade is intra-paragraph only. A span inherits from its enclosing span
|
|
21
|
+
and then from the `<text>`; nothing inherits across the element tree, so there
|
|
22
|
+
is no ambient font size to chase.
|
|
23
|
+
|
|
24
|
+
`lineHeight` is the one prop with a CSS reflex worth unlearning: it is a
|
|
25
|
+
multiplier of `fontSize`, not a pixel value.
|
|
26
|
+
|
|
27
|
+
## text
|
|
28
|
+
|
|
29
|
+
{{ decl packages/core/src/types.d.ts TextProps }}
|
|
30
|
+
|
|
31
|
+
Paragraph-level behavior lives here: `textAlign`, `maxLines` with
|
|
32
|
+
`textOverflow`, `textIndent`, and `textWrap` for line-breaking quality
|
|
33
|
+
(`"balance"` for headings, `"pretty"` to avoid a lone last word).
|
|
34
|
+
|
|
35
|
+
An element child of a `<text>` is an inline atom, which is where the
|
|
36
|
+
[layout](/core/reference/layout/) props `float` and `clear` apply: a floated
|
|
37
|
+
atom leaves the flow and the lines it overlaps wrap around it.
|
|
38
|
+
|
|
39
|
+
## span
|
|
40
|
+
|
|
41
|
+
{{ decl packages/core/src/types.d.ts SpanProps }}
|
|
42
|
+
|
|
43
|
+
Inline only: its children are text and other spans, and it has no layout box,
|
|
44
|
+
which is why it is the one element with no detached form. Pointer handlers on
|
|
45
|
+
a span fire for the boxes its text occupies on each line it spans, and bubble
|
|
46
|
+
to the enclosing spans and the text.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Transforms
|
|
2
|
+
|
|
3
|
+
Transform props apply after layout, at composite time. Nothing re-records and
|
|
4
|
+
nothing reflows, on a laid-out `view` as much as on a `d-view`, so these are
|
|
5
|
+
the props to animate.
|
|
6
|
+
|
|
7
|
+
They live on `ViewOwnProps`, which means both `view` and `d-view` have them;
|
|
8
|
+
see [elements](/core/reference/elements/).
|
|
9
|
+
|
|
10
|
+
{{ decl packages/core/src/types.d.ts TransformProps }}
|
|
11
|
+
|
|
12
|
+
## Origin
|
|
13
|
+
|
|
14
|
+
`originX` and `originY` are the point rotation and scale pivot around, split
|
|
15
|
+
per axis to match the engine's `x`/`y` prop convention.
|
|
16
|
+
|
|
17
|
+
{{ decl packages/core/src/types.d.ts OriginX }}
|
|
18
|
+
|
|
19
|
+
{{ decl packages/core/src/types.d.ts OriginY }}
|
|
20
|
+
|
|
21
|
+
A percentage origin tracks the layout size with no reactive wiring of your
|
|
22
|
+
own. On a `d-view` there is no box, so the origin defaults to the view's local
|
|
23
|
+
`(0,0)` - the origin its children's coordinates are authored against - and
|
|
24
|
+
`pct()` or keyword origins resolve against the inherited box, which is rarely
|
|
25
|
+
what you want. Pivot a `d-view` around its content by setting the origin in
|
|
26
|
+
pixels.
|
|
27
|
+
|
|
28
|
+
## Opacity
|
|
29
|
+
|
|
30
|
+
`opacity` is group opacity: the children composite together and then fade as a
|
|
31
|
+
whole, like CSS. It costs a compositing layer while below 1, unless the view
|
|
32
|
+
is a `repaintBoundary`, where it is hoisted to composite time for free.
|
|
33
|
+
|
|
34
|
+
To fade a single primitive, put the alpha in its `color` instead. Paint alpha
|
|
35
|
+
costs nothing.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Types
|
|
2
|
+
|
|
3
|
+
The shared aliases the element props refer to.
|
|
4
|
+
|
|
5
|
+
## Color
|
|
6
|
+
|
|
7
|
+
{{ decl packages/core/src/types.d.ts Color }}
|
|
8
|
+
|
|
9
|
+
Anywhere a `color` prop is accepted, a `Gradient` from
|
|
10
|
+
`createLinearGradient` or `createRadialGradient` is accepted too. Gradient
|
|
11
|
+
stops are positions in 0..1, not percentages.
|
|
12
|
+
|
|
13
|
+
## Percentages
|
|
14
|
+
|
|
15
|
+
{{ decl packages/core/src/types.d.ts Pct }}
|
|
16
|
+
|
|
17
|
+
`pct(50)` is the only way to write a percentage as a value. It is branded, so
|
|
18
|
+
a percentage cannot be confused with a pixel count by accident, and it
|
|
19
|
+
resolves against the element box wherever it is used - layout dimensions,
|
|
20
|
+
gaps, and transform origins alike.
|
|
21
|
+
|
|
22
|
+
## Children
|
|
23
|
+
|
|
24
|
+
{{ decl packages/core/src/types.d.ts Children }}
|
|
25
|
+
|
|
26
|
+
The element type is SolidJS's own, so the control-flow components (`For`,
|
|
27
|
+
`Show`, `Switch`) return something the JSX types accept.
|
|
28
|
+
|
|
29
|
+
## JSX plumbing
|
|
30
|
+
|
|
31
|
+
One declaration exists purely to tell TypeScript which prop receives JSX
|
|
32
|
+
children. It is not something an app refers to:
|
|
33
|
+
|
|
34
|
+
{{ decl packages/core/src/types.d.ts ElementChildrenAttribute }}
|
package/examples/README.md
CHANGED
|
@@ -8,7 +8,7 @@ for the element/prop model see `@solidrt/core/AGENTS.md`.
|
|
|
8
8
|
## Host elements and layout
|
|
9
9
|
- `window-root.tsx` - the minimal app; the root must be `<window>`.
|
|
10
10
|
- `view-layout.tsx` - `<view>` as a flex container; containers do not paint.
|
|
11
|
-
- `view-
|
|
11
|
+
- `view-design-size.tsx` - `designSize` on a `<view>`: author a scene once in fixed design units and let the view uniformly scale-and-center (letterbox) that space into its box. Children live in design space for layout as well as paint (the box they inherit IS the design size, so a bare `d-rect` fills it and a flex row lays out against the design width, never reflowing on resize); the view itself sizes like a replaced element whose intrinsic size is the design size; pointer `localX`/`localY` arrive in design units. The fixed-aspect alternative to `windowSizeClass` reflow for diagrams, slides, dashboards, game boards, scaled panels.
|
|
12
12
|
- `background-rect.tsx` - a `d-rect` filling its parent as a background.
|
|
13
13
|
- `detached-positioning.tsx` - the `d-` prefix: x/y placement, no reflow, detached-only children.
|
|
14
14
|
- `text-paint-styling.tsx` - the uniform `color` prop; `drawStyle="stroke"` vs fill.
|
|
@@ -21,6 +21,7 @@ for the element/prop model see `@solidrt/core/AGENTS.md`.
|
|
|
21
21
|
- `pointer-local-coords.tsx` - the three pointer coordinate frames (`clientX` window, `localX` the handling node's own frame, `parentX` its path-parent's frame - where the node's x/y live) and the transform-proof drag idiom: grab offset from `localX` at down, place with `parentX - offset` on moves. Exact inside rotated/scaled ancestors and when the pointer leaves the node mid-drag.
|
|
22
22
|
|
|
23
23
|
## Performance
|
|
24
|
+
- `snapshot-texture.tsx` - `snapshotTexture(ref)`: a `repaintBoundary="snapshot"` view's rasterized pixels as a live texture id; a shader texture samples the panel and a sibling `<texture>` shows the warped copy. The boundary re-rasterizes only when its content changes.
|
|
24
25
|
- `repaint-boundary.tsx` - `repaintBoundary` on a `<view>` to keep static content from rebuilding while a neighbor animates: `{true}` retains the recorded draw list, `"snapshot"` also retains the rasterized pixels as a GPU texture (for raster-expensive, screen-aligned, static subtrees). `"snapshot-no-aa"` rasterizes without anti-aliasing: cheaper, fine for text and axis-aligned rects, hard-edged on vector content.
|
|
25
26
|
|
|
26
27
|
## Scrolling
|
|
@@ -28,7 +29,7 @@ for the element/prop model see `@solidrt/core/AGENTS.md`.
|
|
|
28
29
|
|
|
29
30
|
## Window state
|
|
30
31
|
- `window-signals.tsx` - reactive `windowSize()` / `safeArea()` accessors (prefer over `onResize`).
|
|
31
|
-
- `responsive-grid.tsx` - one app across phone/tablet/desktop: `capabilities.windowSizeClass` (Material 3 breakpoints, a reactive getter) drives the column count and `windowSize()` sizes each card; reflows on resize. The reflow answer; for fixed-aspect content use `view-
|
|
32
|
+
- `responsive-grid.tsx` - one app across phone/tablet/desktop: `capabilities.windowSizeClass` (Material 3 breakpoints, a reactive getter) drives the column count and `windowSize()` sizes each card; reflows on resize. The reflow answer; for fixed-aspect content use `view-design-size.tsx` instead.
|
|
32
33
|
|
|
33
34
|
## Overlays
|
|
34
35
|
- `portal.tsx` - `createPortal` relocating content to the window root to escape clipping.
|
|
@@ -51,7 +52,8 @@ for the element/prop model see `@solidrt/core/AGENTS.md`.
|
|
|
51
52
|
- `audio.tsx` - `createSound`: decode a clip once from bytes (here a binary import), replay cheaply; `overlap` stacking vs single-voice, `playing()` signal, release on unmount. `createPcmSound` for a synthesised clip from raw samples (a generated sine sweep). Points to `createSoundStream` for long tracks streamed from a path.
|
|
52
53
|
|
|
53
54
|
## Vector graphics
|
|
54
|
-
- `
|
|
55
|
+
- `line-points.tsx` - `points` on `line`/`d-line`: a flat `[x0, y0, x1, y1, ...]` array (or `Float32Array`) makes the polyline whose geometry is numbers - a live trace rewritten every frame with no `d` string to parse, `closed` outlines, `drawStyle="fill"` polygons (a line's paint defaults to stroke), per-segment dashing, and a laid-out `<line points>` measuring its box from the points.
|
|
56
|
+
- `parse-svg.tsx` - `parseSvg` turns a whole SVG *document string* (not HTML/JSX children) into plain draw data mapped to `<d-path>` inside a `designSize`-fitted view; per-shape hover highlighting shows the payoff (exact-outline hit testing, recolor without re-parse), plus a `currentColor` icon recolored via the `color` option. This is how to use existing icon libraries (Lucide, Heroicons, etc.) - hand their SVG source to `parseSvg`.
|
|
55
57
|
|
|
56
58
|
## Bundling assets
|
|
57
59
|
- `binary-import.tsx` - `import bytes from "./file" with { type: "binary" }` inlines a file's bytes into the bundle as a `Uint8Array` (the bytes are in memory, so `inline-image.tsx` displays them with the synchronous `decodeImage` + `createTexture` path).
|
|
@@ -101,11 +101,11 @@ function App() {
|
|
|
101
101
|
let [time, setTime] = createSignal(0)
|
|
102
102
|
onFrame((tick) => setTime(tick / 1000))
|
|
103
103
|
|
|
104
|
-
// Fill the window: the
|
|
104
|
+
// Fill the window: the design-size fits and centers the square content into
|
|
105
105
|
// the full-window view, so the projection is never stretched.
|
|
106
106
|
return (
|
|
107
107
|
<window>
|
|
108
|
-
<view width={pct(100)} height={pct(100)}
|
|
108
|
+
<view width={pct(100)} height={pct(100)} designSize={[1024, 1024]}>
|
|
109
109
|
<texture src={id} params={{ uTime: time() }} width={1024} height={1024} />
|
|
110
110
|
</view>
|
|
111
111
|
</window>
|