@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.
- package/AGENTS.md +3 -2
- package/README.md +1 -1
- package/examples/README.md +9 -4
- package/examples/gpu-instancing.tsx +70 -0
- package/examples/gpu-particles.tsx +96 -0
- package/examples/gpu-pipeline.tsx +40 -37
- package/examples/gpu-raw-program.tsx +95 -0
- package/examples/gpu-shader.tsx +66 -24
- package/examples/gpu-texture-blend.tsx +79 -0
- package/examples/inline-image.tsx +1 -1
- package/examples/parse-svg.tsx +94 -0
- package/examples/text-import.tsx +29 -0
- package/examples/wave.glsl +15 -0
- package/examples/window-shader-history.tsx +23 -23
- package/examples/window-shader.tsx +30 -28
- package/jsx-runtime.d.ts +17 -9
- package/package.json +2 -2
- package/src/camera.ts +8 -4
- package/src/color.ts +14 -2
- package/src/gpu.ts +289 -90
- package/src/image.ts +12 -11
- package/src/index.ts +3 -0
- package/src/renderer.ts +9 -8
- package/src/runtime-modules.d.ts +19 -3
- package/src/speech-recognition.ts +2 -1
- package/src/svg.ts +71 -0
- package/src/types.d.ts +83 -40
- package/examples/svg.tsx +0 -49
package/src/runtime-modules.d.ts
CHANGED
|
@@ -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 `
|
|
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
|
|
93
|
-
*
|
|
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
|
|
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
|
-
|
|
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:
|
|
302
|
-
/**
|
|
303
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
431
|
+
export interface PathProps extends PaintProps, PointerProps {
|
|
377
432
|
d?: string
|
|
378
433
|
fillRule?: "nonzero" | "evenodd"
|
|
379
434
|
}
|
|
380
435
|
|
|
381
|
-
export interface
|
|
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
|
|
420
|
-
src?:
|
|
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
|
-
|
|
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 />)
|