@solidrt/core 0.0.51 → 0.0.52
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 +89 -20
- 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 +204 -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 +68 -32
- package/src/index.ts +8 -2
- package/src/logo.tsx +92 -0
- package/src/renderer.ts +181 -29
- 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,145 @@
|
|
|
1
|
+
// `points` turns a line into a polyline: a flat [x0, y0, x1, y1, ...] array in
|
|
2
|
+
// the element's local space, so geometry that changes every frame is one
|
|
3
|
+
// array write - no `d` string to format and re-parse. Three uses below:
|
|
4
|
+
// 1. A live trace: a Float32Array rebuilt each frame in onFrame and set as
|
|
5
|
+
// `points`. Typed arrays marshal like number[]; nothing is parsed.
|
|
6
|
+
// 2. A closed outline: `closed` strokes the segment back to the first point
|
|
7
|
+
// and joins there instead of capping both ends; the join style shows at
|
|
8
|
+
// the apex. A line's paint defaults to stroke; drawStyle="fill" fills the
|
|
9
|
+
// polygon instead (implicitly closed), "stroke-and-fill" does both.
|
|
10
|
+
// Dashing runs along the whole stroke, through the vertices.
|
|
11
|
+
// 3. Marching ants: `dashOffset` slides the dash pattern, so writing it every
|
|
12
|
+
// frame animates the dashes. The ring is dense (6 px segments under
|
|
13
|
+
// 12 px dashes), which only works because the phase carries across
|
|
14
|
+
// vertices; the two-point d-line and the d-path go through the same
|
|
15
|
+
// walker (a path's dashes are pieces of its curves, stroked as curves).
|
|
16
|
+
// 4. Partial draw: `pathLength={1}` makes the dash units fractions of the
|
|
17
|
+
// geometry's length, so `onLength={0.77} offLength={1}` draws the first
|
|
18
|
+
// 77% and an `onLength` written from 0 to 1 draws the geometry on, without
|
|
19
|
+
// knowing its length.
|
|
20
|
+
// 5. A laid-out <line points>: the points are content (like a path's `d`), so
|
|
21
|
+
// the box measures from their extent and takes part in the row.
|
|
22
|
+
// The two-endpoint form (x1..y2, on d-line only) is unchanged; while `points`
|
|
23
|
+
// is set it takes precedence over the endpoints.
|
|
24
|
+
import { render, onFrame, createSignal } from "@solidrt/core"
|
|
25
|
+
|
|
26
|
+
const SAMPLES = 200
|
|
27
|
+
const TRACE_W = 560
|
|
28
|
+
const TRACE_H = 160
|
|
29
|
+
const TRIANGLE = [20, 100, 70, 20, 120, 100]
|
|
30
|
+
const ZIGZAG = [0, 0, 30, 24, 60, 0, 90, 24, 120, 0]
|
|
31
|
+
const RING = ring(70, 60, 45, 48)
|
|
32
|
+
const CURVE = "M20 60 C 60 0, 100 120, 140 60 S 200 20, 200 60"
|
|
33
|
+
const ANTS_SPEED = 40 // local units per second
|
|
34
|
+
const DRAW_PERIOD = 3 // seconds per draw-on cycle
|
|
35
|
+
|
|
36
|
+
function ring(cx: number, cy: number, r: number, n: number): number[] {
|
|
37
|
+
let pts: number[] = []
|
|
38
|
+
for (let i = 0; i < n; i++) {
|
|
39
|
+
let a = (i / n) * Math.PI * 2
|
|
40
|
+
pts.push(cx + Math.cos(a) * r, cy + Math.sin(a) * r)
|
|
41
|
+
}
|
|
42
|
+
return pts
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// A travelling wave inside a sine envelope, sampled into x, y pairs.
|
|
46
|
+
function wave(t: number): Float32Array {
|
|
47
|
+
let pts = new Float32Array(SAMPLES * 2)
|
|
48
|
+
for (let i = 0; i < SAMPLES; i++) {
|
|
49
|
+
let u = i / (SAMPLES - 1)
|
|
50
|
+
pts[2 * i] = u * TRACE_W
|
|
51
|
+
pts[2 * i + 1] = TRACE_H / 2 + Math.sin(u * 14 - t * 4) * Math.sin(u * Math.PI) * (TRACE_H / 2 - 8)
|
|
52
|
+
}
|
|
53
|
+
return pts
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function App() {
|
|
57
|
+
let [trace, setTrace] = createSignal<Float32Array>(wave(0))
|
|
58
|
+
let [ants, setAnts] = createSignal(0)
|
|
59
|
+
let [drawn, setDrawn] = createSignal(0)
|
|
60
|
+
onFrame((tick) => {
|
|
61
|
+
setTrace(wave(tick / 1000))
|
|
62
|
+
setAnts((tick / 1000) * ANTS_SPEED)
|
|
63
|
+
setDrawn(((tick / 1000) % DRAW_PERIOD) / DRAW_PERIOD)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<window padding={24} gap={20}>
|
|
68
|
+
<d-rect color="#0b0f17" />
|
|
69
|
+
|
|
70
|
+
<text fontSize={16} color="#8b949e">
|
|
71
|
+
live trace: a Float32Array of {SAMPLES} points written every frame
|
|
72
|
+
</text>
|
|
73
|
+
<view width={TRACE_W} height={TRACE_H} flexShrink={0}>
|
|
74
|
+
<d-rect radius={8} color="#151b28" />
|
|
75
|
+
<d-line points={trace()} color="#3fb950" strokeWidth={3} strokeJoin="round" />
|
|
76
|
+
</view>
|
|
77
|
+
|
|
78
|
+
<text fontSize={16} color="#8b949e">
|
|
79
|
+
closed (round join), open (round caps), filled, stroke-and-fill dashed
|
|
80
|
+
</text>
|
|
81
|
+
<view flexDirection="row" flexWrap="wrap" gap={20}>
|
|
82
|
+
<view width={140} height={120}>
|
|
83
|
+
<d-rect radius={8} color="#151b28" />
|
|
84
|
+
<d-line points={TRIANGLE} closed color="#e3b341" strokeWidth={8} strokeJoin="round" />
|
|
85
|
+
</view>
|
|
86
|
+
<view width={140} height={120}>
|
|
87
|
+
<d-rect radius={8} color="#151b28" />
|
|
88
|
+
<d-line points={TRIANGLE} color="#e3b341" strokeWidth={8} strokeCap="round" />
|
|
89
|
+
</view>
|
|
90
|
+
<view width={140} height={120}>
|
|
91
|
+
<d-rect radius={8} color="#151b28" />
|
|
92
|
+
<d-line points={TRIANGLE} drawStyle="fill" color="#a371f7" />
|
|
93
|
+
</view>
|
|
94
|
+
<view width={140} height={120}>
|
|
95
|
+
<d-rect radius={8} color="#151b28" />
|
|
96
|
+
<d-line points={TRIANGLE} closed drawStyle="stroke-and-fill" onLength={12} offLength={8} color="#f85149" strokeWidth={3} />
|
|
97
|
+
</view>
|
|
98
|
+
</view>
|
|
99
|
+
|
|
100
|
+
<text fontSize={16} color="#8b949e">
|
|
101
|
+
marching ants: dashOffset written every frame, on a dense ring, a segment and a path
|
|
102
|
+
</text>
|
|
103
|
+
<view flexDirection="row" flexWrap="wrap" gap={20}>
|
|
104
|
+
<view width={140} height={120}>
|
|
105
|
+
<d-rect radius={8} color="#151b28" />
|
|
106
|
+
<d-line points={RING} closed onLength={12} offLength={8} dashOffset={ants()} color="#e3b341" strokeWidth={3} />
|
|
107
|
+
</view>
|
|
108
|
+
<view width={300} height={120}>
|
|
109
|
+
<d-rect radius={8} color="#151b28" />
|
|
110
|
+
<d-line x1={20} y1={60} x2={280} y2={60} onLength={0} offLength={14} dashOffset={-ants()} color="#79c0ff" strokeWidth={6} strokeCap="round" />
|
|
111
|
+
</view>
|
|
112
|
+
<view width={220} height={120}>
|
|
113
|
+
<d-rect radius={8} color="#151b28" />
|
|
114
|
+
<d-path d={CURVE} drawStyle="stroke" onLength={10} offLength={6} dashOffset={ants()} color="#f778ba" strokeWidth={3} strokeCap="round" />
|
|
115
|
+
</view>
|
|
116
|
+
</view>
|
|
117
|
+
|
|
118
|
+
<text fontSize={16} color="#8b949e">
|
|
119
|
+
partial draw: pathLength=1 makes the pattern fractional, 77% of the curve and a triangle drawing on
|
|
120
|
+
</text>
|
|
121
|
+
<view flexDirection="row" flexWrap="wrap" gap={20}>
|
|
122
|
+
<view width={220} height={120}>
|
|
123
|
+
<d-rect radius={8} color="#151b28" />
|
|
124
|
+
<d-path d={CURVE} drawStyle="stroke" pathLength={1} onLength={0.77} offLength={1} color="#3fb950" strokeWidth={4} strokeCap="round" />
|
|
125
|
+
</view>
|
|
126
|
+
<view width={140} height={120}>
|
|
127
|
+
<d-rect radius={8} color="#151b28" />
|
|
128
|
+
<d-line points={TRIANGLE} closed pathLength={1} onLength={drawn()} offLength={1} color="#79c0ff" strokeWidth={4} strokeCap="round" strokeJoin="round" />
|
|
129
|
+
</view>
|
|
130
|
+
</view>
|
|
131
|
+
|
|
132
|
+
<view flexDirection="row" alignItems="center" gap={12}>
|
|
133
|
+
<text fontSize={16} color="#8b949e">
|
|
134
|
+
laid out:
|
|
135
|
+
</text>
|
|
136
|
+
<line points={ZIGZAG} color="#1f6feb" strokeWidth={3} strokeJoin="round" />
|
|
137
|
+
<text fontSize={16} color="#8b949e">
|
|
138
|
+
the box measures from the points
|
|
139
|
+
</text>
|
|
140
|
+
</view>
|
|
141
|
+
</window>
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
render(() => <App />)
|
package/examples/parse-svg.tsx
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// it the source text (a string you import, fetch, or inline) and get back the
|
|
4
4
|
// document's intrinsic size plus a flat list of draws whose keys match the
|
|
5
5
|
// path element's props - so rendering is a map to <d-path>, wrapped in a view
|
|
6
|
-
// whose `
|
|
6
|
+
// whose `designSize` fits the document's coordinate space into the box.
|
|
7
7
|
//
|
|
8
8
|
// The point of draws-as-data over an opaque document element: every shape is
|
|
9
9
|
// a real node you own. Below, the house highlights the shape under the
|
|
@@ -28,7 +28,7 @@ import { render, parseSvg, svg, createMemo, createSignal, For } from "@solidrt/c
|
|
|
28
28
|
// the string unchanged; it exists so editors highlight the markup (like `glsl`
|
|
29
29
|
// for shader sources).
|
|
30
30
|
const HOUSE = svg`
|
|
31
|
-
<svg
|
|
31
|
+
<svg designSize="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
|
32
32
|
<rect x="20" y="45" width="60" height="45" fill="#457b9d"/>
|
|
33
33
|
<path d="M10 50 L50 15 L90 50 Z" fill="#e63946"/>
|
|
34
34
|
<rect x="42" y="62" width="16" height="28" fill="#f1faee"/>
|
|
@@ -37,7 +37,7 @@ const HOUSE = svg`
|
|
|
37
37
|
|
|
38
38
|
// Monochrome icon (Lucide arrow-right) drawn with currentColor, recolored below.
|
|
39
39
|
const ARROW = svg`
|
|
40
|
-
<svg
|
|
40
|
+
<svg designSize="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
|
41
41
|
stroke-linecap="round" stroke-linejoin="round">
|
|
42
42
|
<path d="M5 12h14"/>
|
|
43
43
|
<path d="M12 5l7 7-7 7"/>
|
|
@@ -54,7 +54,7 @@ function InteractiveHouse() {
|
|
|
54
54
|
// re-records only when a draw inside it changes (hover), never because a
|
|
55
55
|
// sibling elsewhere on the screen did.
|
|
56
56
|
return (
|
|
57
|
-
<view repaintBoundary width={240} height={240}
|
|
57
|
+
<view repaintBoundary width={240} height={240} designSize={[doc().width, doc().height]}>
|
|
58
58
|
<For each={doc().draws}>
|
|
59
59
|
{(draw, i) => (
|
|
60
60
|
<d-path
|
|
@@ -69,14 +69,14 @@ function InteractiveHouse() {
|
|
|
69
69
|
)
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
// The plain pattern: memoized parse,
|
|
72
|
+
// The plain pattern: memoized parse, designSize-fitted box, draws mapped to
|
|
73
73
|
// <d-path>, and a plain repaintBoundary (the DL-reuse tier, not "snapshot")
|
|
74
74
|
// so the static subtree never re-records alongside animating siblings. The
|
|
75
75
|
// components-package Icon is this plus theming.
|
|
76
76
|
function Svg(props: { src: string; size: number; color?: string }) {
|
|
77
77
|
let doc = createMemo(() => parseSvg(props.src, { color: props.color }))
|
|
78
78
|
return (
|
|
79
|
-
<view repaintBoundary width={props.size} height={props.size}
|
|
79
|
+
<view repaintBoundary width={props.size} height={props.size} designSize={[doc().width, doc().height]}>
|
|
80
80
|
<For each={doc().draws}>{(draw) => <d-path {...draw} />}</For>
|
|
81
81
|
</view>
|
|
82
82
|
)
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// This is the REFLOW answer, for layouts that genuinely rearrange across form
|
|
13
13
|
// factors. For content with fixed internal geometry (diagrams, slides,
|
|
14
14
|
// dashboards, game boards) do not branch on window size at all: author one
|
|
15
|
-
// design space and let `
|
|
15
|
+
// design space and let `designSize` scale it to fit - see view-design-size.tsx.
|
|
16
16
|
import { render, capabilities, windowSize, createMemo, For } from "@solidrt/core"
|
|
17
17
|
|
|
18
18
|
const GAP = 16
|
package/examples/scroll.tsx
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// Capture both with refs, pass their accessors to createScroll, then apply the
|
|
12
12
|
// returned offset to the viewport's scrollX/scrollY. createScroll does no input,
|
|
13
13
|
// so wire an event (here onWheel) to scroll.scrollBy - positive dy moves content
|
|
14
|
-
// up. scrollTo(x, y) jumps to an absolute, clamped offset.
|
|
14
|
+
// up. scrollTo({ x, y }) jumps to an absolute, clamped offset.
|
|
15
15
|
import { render, For, createScroll } from "@solidrt/core"
|
|
16
16
|
import type { WheelEvent } from "@solidrt/core"
|
|
17
17
|
|
|
@@ -22,7 +22,7 @@ function App() {
|
|
|
22
22
|
// Default axis is "vertical"; pass { axis: "horizontal" } or "both" for others.
|
|
23
23
|
let scroll = createScroll(() => viewport, () => content)
|
|
24
24
|
|
|
25
|
-
let onWheel = (e: WheelEvent) => scroll.scrollBy(e.deltaX, e.deltaY)
|
|
25
|
+
let onWheel = (e: WheelEvent) => scroll.scrollBy({ x: e.deltaX, y: e.deltaY })
|
|
26
26
|
|
|
27
27
|
let rows = Array.from({ length: 30 }, (_, i) => i)
|
|
28
28
|
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// A UI subtree as a live texture. snapshotTexture(ref) returns the texture id
|
|
2
|
+
// behind a repaintBoundary="snapshot" view: its rasterized pixels, as an
|
|
3
|
+
// ordinary texture id any GPU consumer samples. Here a shader texture binds
|
|
4
|
+
// it as uPanel and a sibling <texture> shows the result, so the panel on the
|
|
5
|
+
// left and its warped twin on the right are the same pixels. The id is
|
|
6
|
+
// stable; the runtime re-points it after every re-rasterization, and the
|
|
7
|
+
// boundary only re-rasterizes when its subtree changes - so the warp
|
|
8
|
+
// animates every frame while the panel's text is painted once per edit.
|
|
9
|
+
//
|
|
10
|
+
// Tap the panel to count. Both copies update, the mirror through the GPU.
|
|
11
|
+
import { render, onFrame, createSignal, snapshotTexture, Show } from "@solidrt/core"
|
|
12
|
+
import { createShaderTexture, glsl } from "@solidrt/core/gpu"
|
|
13
|
+
|
|
14
|
+
let MIRROR = glsl`
|
|
15
|
+
uniform sampler2D uPanel;
|
|
16
|
+
uniform float uTime;
|
|
17
|
+
void main() {
|
|
18
|
+
vec2 uv = vUV;
|
|
19
|
+
uv.x += sin(uv.y * 20.0 + uTime * 3.0) * 0.02;
|
|
20
|
+
vec4 panel = texture(uPanel, uv);
|
|
21
|
+
// Premultiplied source: tint the color, keep the alpha.
|
|
22
|
+
float glow = 0.5 + 0.5 * sin(uTime * 2.0 + uv.y * 6.0);
|
|
23
|
+
fragColor = vec4(panel.rgb * vec3(1.0, 0.7 + 0.3 * glow, 0.6), panel.a);
|
|
24
|
+
}
|
|
25
|
+
`
|
|
26
|
+
|
|
27
|
+
function App() {
|
|
28
|
+
let [count, setCount] = createSignal(0)
|
|
29
|
+
let [time, setTime] = createSignal(0)
|
|
30
|
+
onFrame(tick => setTime(tick / 1000))
|
|
31
|
+
|
|
32
|
+
let [panel, setPanel] = createSignal<{ id: number }>()
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<window alignItems="center" justifyContent="center" gap={40} flexDirection="row">
|
|
36
|
+
<view
|
|
37
|
+
ref={(n: { id: number }) => setPanel(n)}
|
|
38
|
+
repaintBoundary="snapshot"
|
|
39
|
+
onPointerDown={() => setCount(c => c + 1)}
|
|
40
|
+
width={240}
|
|
41
|
+
height={160}
|
|
42
|
+
padding={20}
|
|
43
|
+
gap={12}
|
|
44
|
+
flexDirection="column"
|
|
45
|
+
>
|
|
46
|
+
<rect position="absolute" width="100%" height="100%" radius={12} color="#1e2a44" />
|
|
47
|
+
<text fontSize={20} color="#ffffff">
|
|
48
|
+
Live panel
|
|
49
|
+
</text>
|
|
50
|
+
<text fontSize={40} color="#ffd166">
|
|
51
|
+
{String(count())}
|
|
52
|
+
</text>
|
|
53
|
+
<text fontSize={14} color="#9fb3d9">
|
|
54
|
+
tap to count
|
|
55
|
+
</text>
|
|
56
|
+
</view>
|
|
57
|
+
<Show when={panel()} keyed>
|
|
58
|
+
{p => {
|
|
59
|
+
// The id is valid as soon as the boundary exists; the texture is
|
|
60
|
+
// empty until its first paint, then live.
|
|
61
|
+
let mirror = createShaderTexture(MIRROR, 480, 320, { uTime: 0 }, {
|
|
62
|
+
textures: { uPanel: snapshotTexture(p) },
|
|
63
|
+
label: "panel-mirror",
|
|
64
|
+
})
|
|
65
|
+
return <texture src={mirror} width={240} height={160} params={{ uTime: time() }} />
|
|
66
|
+
}}
|
|
67
|
+
</Show>
|
|
68
|
+
</window>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
render(() => <App />)
|
|
@@ -1,21 +1,26 @@
|
|
|
1
|
-
// `
|
|
2
|
-
// author the whole scene once, in your own made-up design units, and the
|
|
3
|
-
// scales that space to fit its box. It is SVG's viewBox generalized
|
|
4
|
-
//
|
|
1
|
+
// `designSize` on a <view> is the fixed-aspect answer to "many screen sizes":
|
|
2
|
+
// you author the whole scene once, in your own made-up design units, and the
|
|
3
|
+
// view scales that space to fit its box. It is SVG's viewBox generalized off
|
|
4
|
+
// the graphics format and onto a layout element - the design size is the only
|
|
5
|
+
// irreducible half of it, since a transform already does what min-x/min-y did.
|
|
5
6
|
//
|
|
6
7
|
// Four facts, each demonstrated below:
|
|
7
|
-
// 1.
|
|
8
|
-
//
|
|
9
|
-
//
|
|
8
|
+
// 1. The view sizes like a REPLACED element (think <img>): its intrinsic size
|
|
9
|
+
// is the design size, one sized axis derives the other from the design
|
|
10
|
+
// aspect, and layout props override it as usual - here `flex={1}` takes
|
|
11
|
+
// the whole window. It never refuses to shrink: a design has no size it
|
|
12
|
+
// cannot scale below.
|
|
10
13
|
// 2. It fits UNIFORMLY and centers - one scale for both axes, SVG's default
|
|
11
14
|
// preserveAspectRatio. Content never stretches; the leftover on the loose
|
|
12
15
|
// axis is letterbox, showing the window background through it.
|
|
13
|
-
// 3. Children live in DESIGN space.
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
16
|
+
// 3. Children live in DESIGN space, laid-out ones included. x/y, w/h,
|
|
17
|
+
// fontSize, stroke widths, flex, percentages, text wrapping - everything
|
|
18
|
+
// resolves against the design size, not the box. The box a child inherits
|
|
19
|
+
// IS the design size, so a bare `d-rect` fills the design space, detached
|
|
20
|
+
// text wraps at its width, and a flex row is laid out at the design width
|
|
21
|
+
// whatever the window - nothing reflows on resize, the fit does the work.
|
|
17
22
|
// 4. Pointer coordinates arrive in design space too. localX/localY on the
|
|
18
|
-
//
|
|
23
|
+
// design-size view (and on anything under it) read in design units, so no
|
|
19
24
|
// scale factor is threaded through the app's hit math.
|
|
20
25
|
//
|
|
21
26
|
// The payoff: no `windowSizeClass` branching, no per-breakpoint sizes, no
|
|
@@ -44,14 +49,15 @@ function App() {
|
|
|
44
49
|
<window>
|
|
45
50
|
<d-rect color="#0b0f17" />
|
|
46
51
|
|
|
47
|
-
{/* flex={1}
|
|
52
|
+
{/* flex={1} overrides the intrinsic design size: the box is the whole
|
|
53
|
+
window, and the fit maps the design into it (fact 1). */}
|
|
48
54
|
<view
|
|
49
55
|
flex={1}
|
|
50
|
-
|
|
56
|
+
designSize={[DESIGN_W, DESIGN_H]}
|
|
51
57
|
onPointerMove={(e) => setAt({ x: e.localX, y: e.localY })}
|
|
52
58
|
onPointerLeave={() => setAt(null)}
|
|
53
59
|
>
|
|
54
|
-
{/* No w/h, so it fills the box it inherits - which under a
|
|
60
|
+
{/* No w/h, so it fills the box it inherits - which under a design size is
|
|
55
61
|
the design space (fact 3). Its edges are the letterbox edges. */}
|
|
56
62
|
<d-rect color="#151b28" />
|
|
57
63
|
|
|
@@ -78,6 +84,18 @@ function App() {
|
|
|
78
84
|
<Show when={at()}>
|
|
79
85
|
{(a) => <d-oval x={a().x - 8} y={a().y - 8} w={16} h={16} color="#f85149" />}
|
|
80
86
|
</Show>
|
|
87
|
+
|
|
88
|
+
{/* A laid-out row under the design size (fact 3): positioned and sized in
|
|
89
|
+
design units, the bar flexing against the design width. Resize the
|
|
90
|
+
window: it scales with the scene instead of reflowing. */}
|
|
91
|
+
<view position="absolute" left={40} right={40} top={364} flexDirection="row" alignItems="center" gap={12}>
|
|
92
|
+
<text fontSize={16} color="#8b949e">
|
|
93
|
+
flex row, laid out in design units
|
|
94
|
+
</text>
|
|
95
|
+
<view flex={1} height={10}>
|
|
96
|
+
<d-rect radius={5} color="#1f6feb" />
|
|
97
|
+
</view>
|
|
98
|
+
</view>
|
|
81
99
|
</view>
|
|
82
100
|
</window>
|
|
83
101
|
)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidrt/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.52",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"funding": "https://github.com/sponsors/wellawaretech",
|
|
6
6
|
"author": "Antoine van Wel",
|
|
@@ -25,14 +25,15 @@
|
|
|
25
25
|
"examples/",
|
|
26
26
|
"jsx-runtime.d.ts",
|
|
27
27
|
"agents/",
|
|
28
|
+
"docs/",
|
|
28
29
|
"AGENTS.md"
|
|
29
30
|
],
|
|
30
31
|
"devDependencies": {
|
|
31
|
-
"@solidrt/flux-types": "0.0.
|
|
32
|
+
"@solidrt/flux-types": "0.0.52"
|
|
32
33
|
},
|
|
33
34
|
"peerDependencies": {
|
|
34
|
-
"@solidjs/signals": "2.0.0-rc.
|
|
35
|
-
"@solidjs/universal": "2.0.0-rc.
|
|
36
|
-
"solid-js": "2.0.0-rc.
|
|
35
|
+
"@solidjs/signals": "2.0.0-rc.1",
|
|
36
|
+
"@solidjs/universal": "2.0.0-rc.1",
|
|
37
|
+
"solid-js": "2.0.0-rc.1"
|
|
37
38
|
}
|
|
38
39
|
}
|
package/src/core.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as tree from "flux:rendertree"
|
|
2
|
+
import type { TextureId } from "flux:gpu"
|
|
2
3
|
import { createSignal, onCleanup } from "@solidjs/signals"
|
|
3
4
|
import { on } from "srt:events"
|
|
4
5
|
|
|
@@ -310,7 +311,8 @@ export interface BoundingBox {
|
|
|
310
311
|
* reactive: call it inside `onLayout` (or an event handler) to get values for
|
|
311
312
|
* the current frame. Transforms anywhere in the chain (including the node's
|
|
312
313
|
* own) compose fully; the box is the axis-aligned bounds of the transformed
|
|
313
|
-
* quad.
|
|
314
|
+
* quad. A detached node reports its painted box: its own w/h (or the
|
|
315
|
+
* inherited box), or for `d-line` and `d-path` the geometry plus its stroke.
|
|
314
316
|
*/
|
|
315
317
|
export function getBoundingBox(node: { id: number }): BoundingBox | null {
|
|
316
318
|
return tree.getBoundingBox(node.id)
|
|
@@ -324,6 +326,22 @@ export function getBoundingBoxViewport(node: { id: number }): BoundingBox | null
|
|
|
324
326
|
return tree.getBoundingBoxViewport(node.id)
|
|
325
327
|
}
|
|
326
328
|
|
|
329
|
+
/**
|
|
330
|
+
* The texture id of a snapshot boundary's retained rasterization
|
|
331
|
+
* (`repaintBoundary="snapshot"`): the subtree's pixels as a live texture any
|
|
332
|
+
* GPU consumer - a `<texture>`, a shader or draw target binding, a 3d
|
|
333
|
+
* material - samples by id. Allocated on the first call and stable for the
|
|
334
|
+
* node's lifetime; the runtime re-points it at the current pixels after every
|
|
335
|
+
* rasterization, and only when the subtree changes (a static panel on an
|
|
336
|
+
* animated consumer costs no repaint). Empty until the first paint. Owned by
|
|
337
|
+
* the boundary: `destroyTexture` throws on it, and unmounting the boundary
|
|
338
|
+
* releases it. Throws if the node is not a snapshot boundary. Not a readback:
|
|
339
|
+
* for pixels on the CPU use `captureSnapshot`.
|
|
340
|
+
*/
|
|
341
|
+
export function snapshotTexture(node: { id: number }): TextureId {
|
|
342
|
+
return tree.snapshotTexture(node.id)
|
|
343
|
+
}
|
|
344
|
+
|
|
327
345
|
/**
|
|
328
346
|
* Measures the rendered size of `text` in layout pixels under the given font
|
|
329
347
|
* options (family, size, weight, style, maxLines), without adding it to the
|