@solidrt/core 0.0.52 → 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 CHANGED
@@ -111,7 +111,7 @@ tsconfig.json - the two load-bearing lines are jsx + jsxImportSource:
111
111
  ```
112
112
 
113
113
  Peer deps @solidjs/signals and @solidjs/universal must match (currently
114
- 2.0.0-rc.1); bun resolves them from peerDependencies.
114
+ 2.0.0-rc.3); bun resolves them from peerDependencies.
115
115
 
116
116
  ## Element model (the parts that are easy to get wrong)
117
117
 
@@ -147,6 +147,13 @@ Peer deps @solidjs/signals and @solidjs/universal must match (currently
147
147
  endpoint props and spans its layout box corner to corner. `points` (a flat
148
148
  `[x0, y0, x1, y1, ...]` array, plus `closed`) turns either form into a
149
149
  polyline and wins over the endpoints while set.
150
+ - Nesting rule: d-* elements go under anything; laid-out elements only under
151
+ laid-out parents. A detached subtree is entirely detached, and the insert
152
+ throws otherwise (`<view> cannot be a child of <d-view>: ...`). So a
153
+ component that renders any laid-out element is a LAYOUT component and
154
+ cannot be used inside a d-* subtree, and a component meant for d-* trees
155
+ renders d-* only. Say which in the component's doc comment: the types
156
+ cannot tell (every JSX expression is the same `Element`).
150
157
 
151
158
  - Plain vs `d-` variant (the `d-` prefix means "detached" - detached from the
152
159
  layout engine, Taffy): a plain element (e.g. `rect`) is `RectProps &
@@ -267,6 +274,11 @@ Peer deps @solidjs/signals and @solidjs/universal must match (currently
267
274
  `children()` helper (re-exported from @solidrt/core) and probe the resolved
268
275
  memo - never `typeof props.children` on the raw prop.
269
276
 
277
+ - Keep the simulation out of the renderer: app or game state as plain
278
+ TypeScript with no @solidrt imports, with the JSX reading it and the
279
+ per-frame hook doing one state step plus a handful of property writes.
280
+ That shape runs under bun with no window (tests, audits, replaying a whole
281
+ session in seconds) and it is what keeps per-frame work cheap.
270
282
  - Animation is target-shaped first: declare `transition` on the element and
271
283
  write targets, and the runtime animates natively with no per-frame JS.
272
284
  Reach for per-frame work only for genuinely procedural motion:
@@ -294,7 +306,10 @@ Peer deps @solidjs/signals and @solidjs/universal must match (currently
294
306
 
295
307
  - Device/GPU access via subpath imports: @solidrt/core/camera, /microphone,
296
308
  /speech, /gpu. Image flow: `decodeImage(bytes)` ->
297
- `createTexture(data,w,h)` -> `<texture src={id} />`.
309
+ `createTexture(data,w,h)` -> `<texture src={id} />`. Pixels are
310
+ premultiplied alpha from decode onward (the GPU contract); `decodeImage(bytes,
311
+ { alpha: "straight" })` keeps the file's color under transparent pixels for
312
+ CPU work, and `encodeImage` converts back to straight for the file.
298
313
 
299
314
  ## Minimal app, core primitives only (verified to render)
300
315
 
@@ -167,7 +167,10 @@ Three facts hold for every texture and target:
167
167
  a top-left origin.
168
168
  - **Color is premultiplied alpha.** A target's RGB is already multiplied by
169
169
  its A: write `vec4(rgb * a, a)`. `vec4(rgb, a)` composites as opaque.
170
- `clearColor` is premultiplied too.
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.
171
174
  - **Values are non-linear RGBA8** (or `"r8"` for single-channel data), with
172
175
  no color-space conversion anywhere. Filtering and blending operate on the
173
176
  stored values.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidrt/core",
3
- "version": "0.0.52",
3
+ "version": "0.0.53",
4
4
  "license": "MIT",
5
5
  "funding": "https://github.com/sponsors/wellawaretech",
6
6
  "author": "Antoine van Wel",
@@ -29,11 +29,11 @@
29
29
  "AGENTS.md"
30
30
  ],
31
31
  "devDependencies": {
32
- "@solidrt/flux-types": "0.0.52"
32
+ "@solidrt/flux-types": "0.0.53"
33
33
  },
34
34
  "peerDependencies": {
35
- "@solidjs/signals": "2.0.0-rc.1",
36
- "@solidjs/universal": "2.0.0-rc.1",
37
- "solid-js": "2.0.0-rc.1"
35
+ "@solidjs/signals": "2.0.0-rc.3",
36
+ "@solidjs/universal": "2.0.0-rc.3",
37
+ "solid-js": "2.0.0-rc.3"
38
38
  }
39
39
  }
package/src/gpu.ts CHANGED
@@ -42,7 +42,10 @@
42
42
  // multiplied by its A - `vec4(rgb * a, a)`, not `vec4(rgb, a)`, which
43
43
  // composites as opaque. That is what Impeller composites and what
44
44
  // `<texture blendMode>` blends; `clearColor` is premultiplied too, so the
45
- // default transparent black needs no thought.
45
+ // default transparent black needs no thought. Uploaded pixels follow the
46
+ // same rule: `decodeImage` premultiplies at the codec boundary (image
47
+ // files store straight alpha) and `encodeImage` undoes it, so pixels
48
+ // inside the app are premultiplied everywhere.
46
49
  // - Values are non-linear RGBA8, with no color-space concept. Every texture
47
50
  // and target holds 8-bit RGBA UNORM exactly as written; nothing converts to
48
51
  // or from linear light. `filter: "linear"` averages and the `blend` modes
@@ -110,6 +113,7 @@ export {
110
113
  endBufferWrite,
111
114
  resizeTexture,
112
115
  setTargetParams,
116
+ setTargetRect,
113
117
  setTargetSize,
114
118
  setTargetTextures,
115
119
  uploadTexture,
@@ -213,7 +217,9 @@ export { captureSnapshot, readTexture } from "flux:gpu"
213
217
  * Uploads raw pixels to an immutable GPU texture and returns its id (use it
214
218
  * as `<texture src={id} />`). `data` must be exactly `width * height` pixels
215
219
  * at the declared format's size (`* 4` bytes for the default "rgba8", `* 1`
216
- * for "r8"); a mismatch throws. For pixels you intend to mutate and
220
+ * for "r8"); a mismatch throws. RGBA data is uploaded verbatim and composited
221
+ * as premultiplied alpha, like every texture: `decodeImage` and the readback
222
+ * calls already deliver that, and hand-built pixels must too (`rgb * a`). For pixels you intend to mutate and
217
223
  * re-upload, use `createMutableTexture` instead. When called inside a
218
224
  * reactive scope the texture is freed automatically once that owner is
219
225
  * disposed; when called outside one (e.g. after an `await`, where the owner
@@ -389,6 +395,13 @@ export function createShaderTarget(
389
395
  * driven later with `setTargetTextures`, same precedence and coverage
390
396
  * rules.
391
397
  *
398
+ * `into` makes a sub-target: a draw target rendering into the rectangle at
399
+ * `x`/`y` (top-left origin) of draw target `into`'s storage, so N views or
400
+ * N shadow maps share one texture and ONE pass; the id is a draw target to
401
+ * every verb but not a texture (display and sample the parent, with
402
+ * `srcX`/`srcY` on the leaf), and `setTargetRect` moves it. Auto-free works
403
+ * the same way; destroying the parent takes its tiles.
404
+ *
392
405
  * The render contract is unchanged: the list is input data, so an ordinary
393
406
  * (`render: "auto"`) draw target re-renders exactly when its entries or
394
407
  * their inputs change - a static scene costs zero passes, and one render is
@@ -408,6 +421,9 @@ export function createDrawTarget(
408
421
  render?: "auto" | "manual"
409
422
  loadOp?: "clear" | "load"
410
423
  samples?: 1 | 2 | 4 | 8
424
+ into?: gpu.TextureId
425
+ x?: number
426
+ y?: number
411
427
  } & CreateOptions &
412
428
  SamplerOptions,
413
429
  ): gpu.TextureId {
package/src/renderer.ts CHANGED
@@ -266,6 +266,14 @@ let renderer = createRenderer<ProxyNode>({
266
266
  pendingDestroy.delete(node.id)
267
267
 
268
268
  if (parent) {
269
+ // console.debug("[srt] insertNode", parent.id, node.id, anchor?.id ?? "")
270
+
271
+ // Native first: the tree refuses a laid-out element under a d-* parent
272
+ // (it throws, naming both tags), and the mirror must not record a child
273
+ // the tree does not have.
274
+ if (anchor) tree.insertNode(parent.id, node.id, anchor.id)
275
+ else tree.insertNode(parent.id, node.id)
276
+
269
277
  node.parent = parent
270
278
 
271
279
  if (!anchor) {
@@ -278,11 +286,6 @@ let renderer = createRenderer<ProxyNode>({
278
286
  parent.children.splice(index, 0, node)
279
287
  }
280
288
  }
281
-
282
- // console.debug("[srt] insertNode", parent.id, node.id, anchor?.id ?? "")
283
-
284
- if (anchor) tree.insertNode(parent.id, node.id, anchor.id)
285
- else tree.insertNode(parent.id, node.id)
286
289
  }
287
290
  },
288
291