@solidrt/core 0.0.39 → 0.0.41

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.
@@ -8,9 +8,25 @@ declare module "*.svg" {
8
8
  export default content
9
9
  }
10
10
 
11
+ // Text asset imports: `import src from "./effect.glsl" with { type: "text" }`.
12
+ // The bundler inlines the file's UTF-8 contents as a string literal, so a
13
+ // shader source travels in the bundle and needs no runtime read.
14
+ declare module "*.glsl" {
15
+ const content: string
16
+ export default content
17
+ }
18
+ declare module "*.vert" {
19
+ const content: string
20
+ export default content
21
+ }
22
+ declare module "*.frag" {
23
+ const content: string
24
+ export default content
25
+ }
26
+
11
27
  // Binary asset imports: `import data from "./pic.png" with { type: "binary" }`.
12
28
  // The bundler inlines the file's bytes as a Uint8Array (see packages/cli
13
- // bundler `binaryImport`); feed it straight into createImage/decodeImage.
29
+ // bundler `inlineImport`); feed it straight into createImage/decodeImage.
14
30
  declare module "*.png" {
15
31
  const bytes: Uint8Array
16
32
  export default bytes
@@ -89,8 +105,8 @@ declare module "srt:apps" {
89
105
  * installs nothing and leaves it alone. `size` is the version's
90
106
  * manifest-declared size (bundle plus assets) - claimed rather than walked,
91
107
  * so that listing stays cheap; `info()` reports what is actually on disk.
92
- * `icon` is the manifest-declared icon's SVG source, ready for an `<svg>`
93
- * src; absent when the app declares none (or the file is unreadable).
108
+ * `icon` is the manifest-declared icon's SVG source, ready for `parseSvg`;
109
+ * absent when the app declares none (or the file is unreadable).
94
110
  */
95
111
  export type InstalledApp = {
96
112
  id: string
@@ -3,7 +3,8 @@
3
3
  // final transcripts through onResult. With wakeWord the session starts
4
4
  // asleep behind an efficient wake word detector (livekit-wakeword) and only
5
5
  // transcribes after the wake word. startRecognition resolves once the models
6
- // are loaded and listening has begun; it rejects when loading fails.
6
+ // are loaded and listening has begun; it rejects when the microphone cannot be
7
+ // opened or the models fail to load.
7
8
  // Models are passed as bytes so any source composes: flux:fs file(), fetch
8
9
  // (incl. the dev-server file proxy), or a download cache layered on top.
9
10
  // Requires a runtime built with speech support.
package/src/svg.ts ADDED
@@ -0,0 +1,71 @@
1
+ // SVG documents as data: parseSvg turns a document string into a flat list of
2
+ // draws that map straight onto <d-path> elements. Vector currency is path
3
+ // data, the same way raster currency is a texture id - there is no document
4
+ // element that swallows the source; the app owns the parsed data and composes
5
+ // ordinary primitives from it.
6
+
7
+ import { parseSvg as fluxParseSvg } from "flux:svg"
8
+ import { parseColor, type Gradient } from "./color"
9
+
10
+ /**
11
+ * Tags an inline SVG source, returning it unchanged. Documents small enough to
12
+ * belong beside the code that uses them stay in the file; the tag is what
13
+ * makes them legible there, because editors highlight markup inside a template
14
+ * literal only when a known tag marks it (the name matters - `svg` is one the
15
+ * grammars look for). Raw semantics, like `glsl`.
16
+ */
17
+ export let svg = String.raw
18
+
19
+ /**
20
+ * One resolved draw in document coordinates. The keys match `PathProps`, so a
21
+ * draw spreads onto a `<d-path>` unchanged: `<d-path {...draw} />`. A source
22
+ * path with both a fill and a stroke yields two draws (fill first).
23
+ */
24
+ export type SvgDraw = {
25
+ d: string
26
+ color: string | Gradient
27
+ drawStyle: "fill" | "stroke"
28
+ fillRule?: "nonzero" | "evenodd"
29
+ strokeWidth?: number
30
+ strokeCap?: "butt" | "round" | "square"
31
+ strokeJoin?: "miter" | "round" | "bevel"
32
+ }
33
+
34
+ /** A parsed document: intrinsic size (viewBox/width-height) plus the flat draw list. */
35
+ export type SvgDocument = {
36
+ width: number
37
+ height: number
38
+ draws: SvgDraw[]
39
+ }
40
+
41
+ /**
42
+ * Parses an SVG document string (an imported `.svg` asset, an icon library's
43
+ * string export, or a template literal) into plain draw data: geometry
44
+ * flattened to absolute path data with every transform baked in, paints
45
+ * resolved to colors or gradients. Render it by wrapping the draws in a view
46
+ * that fits the document's coordinate space into its box:
47
+ *
48
+ * let doc = createMemo(() => parseSvg(src))
49
+ * <view repaintBoundary viewBox={[doc().width, doc().height]} width={48} height={48}>
50
+ * {doc().draws.map((draw) => <d-path {...draw} />)}
51
+ * </view>
52
+ *
53
+ * The plain repaintBoundary is the recommended default: the parsed subtree is
54
+ * static, so it never re-records alongside changing siblings. Each `<d-path>`
55
+ * hit-tests its exact outline; when the document should act as ONE hit target
56
+ * (the usual icon case), add `pointerEvents="all"` to the wrapper - the box
57
+ * then matches as a whole and the per-path outline tests are skipped.
58
+ *
59
+ * `opts.color` drives `currentColor` in the document (any CSS color string),
60
+ * which is how monochrome icon sets (Lucide, Feather, Heroicons, ...) get
61
+ * recolored; explicit fills/strokes still win. Parsing is synchronous and
62
+ * sandboxed (no network, file, or data-URI access) and throws on an invalid
63
+ * document. Parse once per document under a memo, not per instance.
64
+ *
65
+ * Unsupported and skipped: clipPath, masks, filters, patterns, embedded
66
+ * images, and SVG text.
67
+ */
68
+ export function parseSvg(src: string, opts?: { color?: string }): SvgDocument {
69
+ if (opts?.color != null) return fluxParseSvg(src, { color: parseColor(opts.color) })
70
+ return fluxParseSvg(src)
71
+ }
package/src/types.d.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  /// <reference path="./runtime-modules.d.ts" />
3
3
 
4
4
  import type { Gradient } from "./color"
5
+ import type { ProgramId, TextureId } from "flux:gpu"
5
6
  import type { Element } from "solid-js"
6
7
 
7
8
  // The "srt:*" lattice runner modules are declared in ./runtime-modules.d.ts
@@ -173,6 +174,9 @@ export interface TransformProps {
173
174
  // Perspective viewing distance in pixels (CSS `perspective`). Enables the 3D
174
175
  // depth for rotateY; larger values give a shallower effect.
175
176
  perspective?: number
177
+ // Subtree translation in pixels, composited post-layout (no re-record, no
178
+ // layout). Unlike the draw primitives' detached-only x/y, these exist on
179
+ // layout views too - the drag/thumb idiom animates them freely.
176
180
  x?: number
177
181
  y?: number
178
182
  originX?: OriginX
@@ -264,11 +268,53 @@ export interface PointerProps {
264
268
  pointerEvents?: "auto" | "none" | "all"
265
269
  }
266
270
 
267
- interface Position {
271
+ /**
272
+ * Detached-only geometry, in paint-space pixels. Never affects layout: these
273
+ * props exist only on the d-* forms, where there is no layout box and the
274
+ * element owns its geometry. The layout forms of the draw primitives derive
275
+ * their geometry from the layout box instead (size it with width/height).
276
+ */
277
+ export interface PositionProps {
278
+ /** Horizontal offset of the drawn geometry; defaults to 0. */
268
279
  x?: number
280
+ /** Vertical offset of the drawn geometry; defaults to 0. */
269
281
  y?: number
270
282
  }
271
283
 
284
+ /** See {@link PositionProps}: detached-only, never affects layout. */
285
+ export interface GeometryProps extends PositionProps {
286
+ /** Drawn width; defaults to the inherited box width. */
287
+ w?: number
288
+ /** Drawn height; defaults to the inherited box height. */
289
+ h?: number
290
+ }
291
+
292
+ /** See {@link PositionProps}: detached-only, never affects layout. */
293
+ export interface OvalGeometryProps extends PositionProps {
294
+ /** Bounding box width of the ellipse (not a radius); defaults to the inherited box. */
295
+ w?: number
296
+ /** Bounding box height of the ellipse (not a radius); defaults to the inherited box. */
297
+ h?: number
298
+ }
299
+
300
+ /** See {@link PositionProps}: detached-only, never affects layout. */
301
+ export interface TextGeometryProps extends PositionProps {
302
+ // Shaping (wrap) width. Detached text wraps at the inherited ancestor size
303
+ // by default; set w for an unwrapped natural line or an explicit wrap width.
304
+ w?: number
305
+ // Reported-bounds height only; paragraph height always falls out of the text.
306
+ h?: number
307
+ }
308
+
309
+ /** See {@link PositionProps}: detached-only, never affects layout. */
310
+ export interface LineGeometryProps {
311
+ /** Endpoints default to spanning the box: (0,0) to (box width, box height). */
312
+ x1?: number
313
+ y1?: number
314
+ x2?: number
315
+ y2?: number
316
+ }
317
+
272
318
  // Primitives
273
319
 
274
320
  export interface WindowProps extends LayoutProps, PointerProps {
@@ -298,11 +344,15 @@ export interface WindowProps extends LayoutProps, PointerProps {
298
344
  */
299
345
  export interface WindowShaderProps {
300
346
  /** Linked program handle from linkProgram. */
301
- program: number
302
- /** Float uniforms filled by name, paced to the next real repaint. */
303
- params?: Record<string, number>
347
+ program: ProgramId
348
+ /**
349
+ * Uniforms filled by name, paced to the next real repaint. A number drives
350
+ * a scalar (`float`/`int`); a flat number array drives the declared GLSL
351
+ * type: 2/3/4 for `vec2`/`vec3`/`vec4`, 16 (column-major) for `mat4`.
352
+ */
353
+ params?: Record<string, number | number[]>
304
354
  /** Extra sampler2D inputs: uniform name to texture id. */
305
- textures?: Record<string, number>
355
+ textures?: Record<string, TextureId>
306
356
  /** Vertices drawn (attributeless triangles). Default 3, the covering triangle. */
307
357
  vertexCount?: number
308
358
  /**
@@ -316,9 +366,23 @@ export interface WindowShaderProps {
316
366
  previous?: boolean
317
367
  }
318
368
 
319
- export interface ViewProps extends LayoutProps, TransformProps, PointerProps {
369
+ // Everything a view offers besides layout: d-view uses this directly (a
370
+ // detached view has no taffy presence, so layout props would be rejected at
371
+ // runtime); the layout `view` adds LayoutProps below.
372
+ export interface ViewOwnProps extends TransformProps, PointerProps {
320
373
  children?: Children
321
374
  trace?: boolean
375
+ /**
376
+ * Design-space size `[w, h]` for the children: content drawn in that
377
+ * coordinate space is uniformly scaled to fit and centered in the element's
378
+ * box (SVG's default preserveAspectRatio, generalized). A pure fit
379
+ * transform - it never sizes the element, so give the box its size with
380
+ * layout props. Composed innermost: the transform props still operate in
381
+ * box space, and pointer events on children arrive in design coordinates.
382
+ * The natural wrapper for parseSvg draws, or any d-* subtree authored in
383
+ * fixed design units.
384
+ */
385
+ viewBox?: [number, number]
322
386
  /**
323
387
  * Corner radii for the clip applied when overflow is non-visible (hidden,
324
388
  * clip, scroll on both axes). A single number rounds all four corners; an
@@ -345,54 +409,32 @@ export interface ViewProps extends LayoutProps, TransformProps, PointerProps {
345
409
  repaintBoundary?: boolean | "snapshot" | "snapshot-no-aa"
346
410
  }
347
411
 
412
+ export interface ViewProps extends ViewOwnProps, LayoutProps {}
413
+
348
414
  // draw primitives
349
415
 
350
- export interface RectProps extends Position, PaintProps, PointerProps {
351
- w?: number
352
- h?: number
416
+ export interface RectProps extends PaintProps, PointerProps {
353
417
  // Corner radius. A single number applies to all four corners; an array is
354
418
  // [top-left, top-right, bottom-right, bottom-left] (CSS border-radius order).
355
419
  radius?: number | [number, number, number, number]
356
420
  }
357
421
 
358
- export interface OvalProps extends Position, PaintProps, PointerProps {
359
- /** Bounding box width of the ellipse (not a radius); defaults to the layout box. */
360
- w?: number
361
- /** Bounding box height of the ellipse (not a radius); defaults to the layout box. */
362
- h?: number
363
- }
422
+ export interface OvalProps extends PaintProps, PointerProps {}
364
423
 
365
424
  export interface LineProps extends PaintProps, PointerProps {
366
- x1?: number
367
- y1?: number
368
- x2?: number
369
- y2?: number
370
425
  /** Dash pattern in local units: the drawn segment length. Both onLength and offLength must be set to dash; with either unset the line is solid. */
371
426
  onLength?: number
372
427
  /** Dash pattern in local units: the gap length. Both onLength and offLength must be set to dash; with either unset the line is solid. */
373
428
  offLength?: number
374
429
  }
375
430
 
376
- export interface PathProps extends Position, PaintProps, PointerProps {
431
+ export interface PathProps extends PaintProps, PointerProps {
377
432
  d?: string
378
433
  fillRule?: "nonzero" | "evenodd"
379
434
  }
380
435
 
381
- export interface SvgProps extends Position, PointerProps {
382
- // A whole SVG document as a string (an imported asset, a fetched string, or a
383
- // template literal). Parsed and rendered as one unit; takes no JSX children.
384
- src?: string
385
- // Drives currentColor in the document. Explicit fills/strokes still win.
386
- color?: Color
387
- }
388
-
389
- export interface TextProps extends Position, PaintProps, PointerProps {
436
+ export interface TextProps extends PaintProps, PointerProps {
390
437
  children?: Children
391
- // Shaping (wrap) width. Detached text wraps at the inherited ancestor size
392
- // by default; set w for an unwrapped natural line or an explicit wrap width.
393
- w?: number
394
- // Reported-bounds height only; paragraph height always falls out of the text.
395
- h?: number
396
438
  fontFamily?: "sans" | "serif" | "mono" | (string & {})
397
439
  fontSize?: number
398
440
  /**
@@ -416,8 +458,8 @@ export interface TextProps extends Position, PaintProps, PointerProps {
416
458
  * Texture alpha is premultiplied, so additive modes need no manual
417
459
  * premultiplication.
418
460
  */
419
- export interface TextureProps extends Position, PaintProps, PointerProps {
420
- src?: number
461
+ export interface TextureProps extends PaintProps, PointerProps {
462
+ src?: TextureId
421
463
  /**
422
464
  * How the texture's pixels map to the element box (CSS object-fit).
423
465
  * "fill" (default) stretches; "cover" and "none" crop; "contain" and
@@ -426,14 +468,15 @@ export interface TextureProps extends Position, PaintProps, PointerProps {
426
468
  * bars and "cover" cropped edges still hit-test as part of the element.
427
469
  */
428
470
  fit?: "fill" | "cover" | "contain" | "none" | "scale-down"
429
- w?: number
430
- h?: number
431
471
  srcX?: number
432
472
  srcY?: number
433
473
  srcW?: number
434
474
  srcH?: number
435
475
  // Shader uniform values, when src names a shader texture. Applied at the
436
476
  // next repaint (not synchronously), so a fast-changing signal stays paced
437
- // to real frames rather than triggering a GL render pass per write.
438
- params?: Record<string, number>
477
+ // to real frames rather than triggering a GL render pass per write. A
478
+ // number drives a scalar (`float`/`int`); a flat number array drives the
479
+ // declared GLSL type: 2/3/4 for `vec2`/`vec3`/`vec4`, 16 (column-major)
480
+ // for `mat4`.
481
+ params?: Record<string, number | number[]>
439
482
  }
package/examples/svg.tsx DELETED
@@ -1,49 +0,0 @@
1
- // <svg> draws a whole SVG *document* passed as a STRING in the `src` prop. This
2
- // is not HTML: there are no per-element <rect>/<circle>/<path> JSX children to
3
- // nest. You hand it the SVG source text and usvg parses it (CSS, transforms,
4
- // defs/use, gradients, clips) into a flat path tree the element renders. So an
5
- // SVG is a value (a string you import, fetch, or inline), not markup you author
6
- // inline with JSX.
7
- //
8
- // width/height set the drawn box (percentages work too); a multi-color document
9
- // keeps each shape's own fill, while a monochrome icon using stroke/fill
10
- // "currentColor" is recolored by the host `color` prop. For per-shape authored
11
- // or animated vector art, compose <d-path> instead of this document layer.
12
- //
13
- // Being a vector, an <svg> is resolution-independent: it stays crisp at any
14
- // drawn size x displayScale(). Prefer it over a raster <texture> (image.tsx)
15
- // whenever the render size is fluid or the display DPI varies.
16
- //
17
- // This is how you use an existing icon library (Lucide, Heroicons, Feather,
18
- // Material, etc.): those ship SVG source, so import/inline the icon string and
19
- // hand it to `src`. The `currentColor` convention they follow means the `color`
20
- // prop recolors them, exactly as `currentColor` would in a browser.
21
- import { render } from "@solidrt/core"
22
-
23
- // Multi-color document: each shape carries its own fill, no host color needed.
24
- const HOUSE = `
25
- <svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
26
- <rect x="20" y="45" width="60" height="45" fill="#457b9d"/>
27
- <path d="M10 50 L50 15 L90 50 Z" fill="#e63946"/>
28
- <rect x="42" y="62" width="16" height="28" fill="#f1faee"/>
29
- <circle cx="50" cy="35" r="6" fill="#ffd166"/>
30
- </svg>`
31
-
32
- // Monochrome icon (Lucide arrow-right) drawn with currentColor, recolored below.
33
- const ARROW = `
34
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
35
- stroke-linecap="round" stroke-linejoin="round">
36
- <path d="M5 12h14"/>
37
- <path d="M12 5l7 7-7 7"/>
38
- </svg>`
39
-
40
- function App() {
41
- return (
42
- <window justifyContent="center" alignItems="center" flexDirection="row" gap={32}>
43
- <svg width={120} height={120} src={HOUSE} />
44
- <svg width={120} height={120} src={ARROW} color="#4f8cff" />
45
- </window>
46
- )
47
- }
48
-
49
- render(() => <App />)