@glowbox/svelte 1.0.0-rc.2
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/LICENSE +21 -0
- package/README.md +83 -0
- package/dist/LedGrid.svelte +110 -0
- package/dist/LedGrid.svelte.d.ts +16 -0
- package/dist/NixieTube.svelte +92 -0
- package/dist/NixieTube.svelte.d.ts +18 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Eetu Sutinen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# @glowbox/svelte
|
|
2
|
+
|
|
3
|
+
glowbox components for **Svelte 5**: `<LedGrid>` — the 3D WebGL LED-grid display
|
|
4
|
+
(over **[@glowbox/led-grid](../led-grid)**) — and `<NixieTube>` — a glowing nixie-tube numeral
|
|
5
|
+
(over **[@glowbox/nixie](../nixie)**).
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
yarn add @glowbox/svelte
|
|
9
|
+
# peer: svelte ^5 (@glowbox/led-grid + @glowbox/nixie come along as dependencies)
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## `<LedGrid>`
|
|
13
|
+
|
|
14
|
+
```svelte
|
|
15
|
+
<script lang="ts">
|
|
16
|
+
import { LedGrid, type LedDisplay } from '@glowbox/svelte';
|
|
17
|
+
|
|
18
|
+
const draw = (d: LedDisplay, dt: number) => {
|
|
19
|
+
d.clear();
|
|
20
|
+
d.sphere([4, 4, 4], 3, '#00aaff');
|
|
21
|
+
};
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<LedGrid
|
|
25
|
+
size={[8, 8, 8]}
|
|
26
|
+
{draw}
|
|
27
|
+
led={{ glow: 3, offColor: '#0a0a12' }}
|
|
28
|
+
camera={{ autoOrbit: true, projection: 'perspective' }}
|
|
29
|
+
color={{ background: '#000', gain: 1.1 }}
|
|
30
|
+
interaction={{ zoom: true }}
|
|
31
|
+
/>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
| prop | type | notes |
|
|
35
|
+
| ------------- | ----------------------------------- | -------------------------------------------------------------------------------------- |
|
|
36
|
+
| `size` | `[number, number, number]` | grid dims `[nx, ny, nz]` (changing it resizes in place — no remount) |
|
|
37
|
+
| `draw` | `(d: LedDisplay, dt: number)=>void` | called every frame; write voxels here |
|
|
38
|
+
| `led` | `LedOptions` | `style` `shape` `stagger` `rgb` `rgbLayout` `vivid` `outline` `size` `glow` `offColor` |
|
|
39
|
+
| `color` | `ColorOptions` | `background` `gain` `tint` |
|
|
40
|
+
| `camera` | `CameraOptions` | `yaw` `pitch` `distance` `fov` `projection` `autoOrbit` `orbitSpeed` `pitchLimits` |
|
|
41
|
+
| `interaction` | `InteractionOptions` | `drag` `dragSpeed` `zoom` `zoomLimits` |
|
|
42
|
+
| `quality` | `QualityOptions` | `pixelRatio` `antialias` `paused` `fps` (frame-rate cap) |
|
|
43
|
+
| `oncreate` | `(d: LedDisplay \| null)=>void` | imperative handle — called with the display on create, `null` on teardown |
|
|
44
|
+
|
|
45
|
+
The grouped props mirror `@glowbox/led-grid`'s options 1:1 and update **live** — even `size`
|
|
46
|
+
resizes the grid in place (no remount / context loss). Colours accept a `Color` (`[r,g,b]`
|
|
47
|
+
0..1, `>1` blooms, or any CSS string). See **@glowbox/led-grid** for every field's default,
|
|
48
|
+
the full voxel API, and colour semantics.
|
|
49
|
+
|
|
50
|
+
## `<NixieTube>`
|
|
51
|
+
|
|
52
|
+
```svelte
|
|
53
|
+
<script lang="ts">
|
|
54
|
+
import { NixieTube } from '@glowbox/svelte';
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<!-- the tube fills its parent — size the parent -->
|
|
58
|
+
<div style="width: 80px; height: 150px">
|
|
59
|
+
<NixieTube value="7" tubeStyle="classic" color="#ff6a12" />
|
|
60
|
+
</div>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
| prop | type | notes |
|
|
64
|
+
| ------------ | --------------------------------- | ---------------------------------------------------------- |
|
|
65
|
+
| `value` | `string \| number \| null` | the lit symbol: `0`–`9`, `:`, `-`, or `null`/`''` for dark |
|
|
66
|
+
| `tubeStyle` | `'classic' \| 'slim' \| 'tall'` | physical tube style (maps to the core `style` option) |
|
|
67
|
+
| `color` | `Color` | glow colour (default warm nixie orange) |
|
|
68
|
+
| `glow` | `number` | glow strength 0..1 |
|
|
69
|
+
| `background` | `Color` | tube glass colour |
|
|
70
|
+
| `mesh` | `boolean` | draw the honeycomb anode mesh (default `true`) |
|
|
71
|
+
| `ghost` | `boolean` | show the unlit cathode stack (default `true`) |
|
|
72
|
+
| `pixelRatio` | `number` | cap on `devicePixelRatio` |
|
|
73
|
+
| `oncreate` | `(tube: NixieTube \| null)=>void` | imperative handle — the tube on create, `null` on teardown |
|
|
74
|
+
|
|
75
|
+
Props update **live** (`value` → `setValue`, the rest → `setOptions`). A clock is just a
|
|
76
|
+
row of `<NixieTube>`s. See **@glowbox/nixie** for defaults + the size-adaptive rendering.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
Sibling packages with the same components: **[@glowbox/react](../react)** and
|
|
81
|
+
**[@glowbox/vue](../vue)**; content helpers in **[@glowbox/extras](../extras)**. Each
|
|
82
|
+
component fills its parent (`width/height: 100%`); give the parent a size.
|
|
83
|
+
Live demos: <https://eetu.github.io/glowbox/>.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// Svelte wrapper around the plain-JS LED display. Give it a `size` and an
|
|
3
|
+
// optional `draw(d, dt)` callback; the grouped option props (led/color/camera/
|
|
4
|
+
// interaction/quality) mirror @glowbox/led-grid's options 1:1 and update live. All
|
|
5
|
+
// content is the client's — this ships no programs. (React/Vue wrappers can
|
|
6
|
+
// mirror this over the same core.)
|
|
7
|
+
import {
|
|
8
|
+
type CameraOptions,
|
|
9
|
+
type ColorOptions,
|
|
10
|
+
createLedDisplay,
|
|
11
|
+
type InteractionOptions,
|
|
12
|
+
type LedDisplay,
|
|
13
|
+
type LedOptions,
|
|
14
|
+
type QualityOptions
|
|
15
|
+
} from '@glowbox/led-grid';
|
|
16
|
+
import { untrack } from 'svelte';
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
size,
|
|
20
|
+
draw,
|
|
21
|
+
led,
|
|
22
|
+
color,
|
|
23
|
+
camera,
|
|
24
|
+
interaction,
|
|
25
|
+
quality,
|
|
26
|
+
oncreate
|
|
27
|
+
}: {
|
|
28
|
+
size: [number, number, number];
|
|
29
|
+
draw?: (d: LedDisplay, dt: number) => void;
|
|
30
|
+
led?: LedOptions;
|
|
31
|
+
color?: ColorOptions;
|
|
32
|
+
camera?: CameraOptions;
|
|
33
|
+
interaction?: InteractionOptions;
|
|
34
|
+
quality?: QualityOptions;
|
|
35
|
+
/** Called with the display when (re)created, and with null on teardown —
|
|
36
|
+
* an escape hatch for imperative access (snapshot(), stats, setCamera…). */
|
|
37
|
+
oncreate?: (display: LedDisplay | null) => void;
|
|
38
|
+
} = $props();
|
|
39
|
+
|
|
40
|
+
let canvas = $state<HTMLCanvasElement | null>(null);
|
|
41
|
+
// $state.raw: the display is an opaque handle (owns a Float32Array + methods),
|
|
42
|
+
// not reactive data — deep-proxying it would break identity checks and mutate
|
|
43
|
+
// its buffer through a proxy.
|
|
44
|
+
let display = $state.raw<LedDisplay | null>(null);
|
|
45
|
+
|
|
46
|
+
// Create the display once for the canvas. Depends only on `canvas` — the option
|
|
47
|
+
// groups and size are read untracked so changing them never re-creates (size
|
|
48
|
+
// changes go through display.resize() below, which keeps the same canvas/context).
|
|
49
|
+
$effect(() => {
|
|
50
|
+
const el = canvas;
|
|
51
|
+
if (!el) return;
|
|
52
|
+
const d = untrack(() =>
|
|
53
|
+
createLedDisplay(el, { size, led, color, camera, interaction, quality })
|
|
54
|
+
);
|
|
55
|
+
if (!d) {
|
|
56
|
+
console.warn('LedGrid: WebGL unavailable');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
display = d;
|
|
60
|
+
untrack(() => oncreate?.(d));
|
|
61
|
+
return () => {
|
|
62
|
+
d.dispose();
|
|
63
|
+
if (display === d) display = null;
|
|
64
|
+
untrack(() => oncreate?.(null));
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Resize the grid in place when the dimensions change (no remount / context loss).
|
|
69
|
+
$effect(() => {
|
|
70
|
+
const [x, y, z] = size;
|
|
71
|
+
display?.resize([x, y, z]);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Live-update each option group *independently*. Patching all groups on any one change
|
|
75
|
+
// would re-send `camera` (yaw/pitch/distance) on, say, a colour tweak — snapping the
|
|
76
|
+
// view back and fighting drag / auto-orbit. One effect per group patches only what changed.
|
|
77
|
+
$effect(() => {
|
|
78
|
+
display?.setOptions({ led });
|
|
79
|
+
});
|
|
80
|
+
$effect(() => {
|
|
81
|
+
display?.setOptions({ color });
|
|
82
|
+
});
|
|
83
|
+
$effect(() => {
|
|
84
|
+
display?.setOptions({ camera });
|
|
85
|
+
});
|
|
86
|
+
$effect(() => {
|
|
87
|
+
display?.setOptions({ interaction });
|
|
88
|
+
});
|
|
89
|
+
$effect(() => {
|
|
90
|
+
display?.setOptions({ quality });
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// (Re)bind the per-frame draw callback.
|
|
94
|
+
$effect(() => {
|
|
95
|
+
const d = display;
|
|
96
|
+
if (!d || !draw) return;
|
|
97
|
+
return d.onFrame(draw);
|
|
98
|
+
});
|
|
99
|
+
</script>
|
|
100
|
+
|
|
101
|
+
<canvas bind:this={canvas}></canvas>
|
|
102
|
+
|
|
103
|
+
<style>
|
|
104
|
+
canvas {
|
|
105
|
+
display: block;
|
|
106
|
+
width: 100%;
|
|
107
|
+
height: 100%;
|
|
108
|
+
touch-action: none; /* let drag-orbit work without the page panning */
|
|
109
|
+
}
|
|
110
|
+
</style>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type CameraOptions, type ColorOptions, type InteractionOptions, type LedDisplay, type LedOptions, type QualityOptions } from '@glowbox/led-grid';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
size: [number, number, number];
|
|
4
|
+
draw?: (d: LedDisplay, dt: number) => void;
|
|
5
|
+
led?: LedOptions;
|
|
6
|
+
color?: ColorOptions;
|
|
7
|
+
camera?: CameraOptions;
|
|
8
|
+
interaction?: InteractionOptions;
|
|
9
|
+
quality?: QualityOptions;
|
|
10
|
+
/** Called with the display when (re)created, and with null on teardown —
|
|
11
|
+
* an escape hatch for imperative access (snapshot(), stats, setCamera…). */
|
|
12
|
+
oncreate?: (display: LedDisplay | null) => void;
|
|
13
|
+
};
|
|
14
|
+
declare const LedGrid: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
15
|
+
type LedGrid = ReturnType<typeof LedGrid>;
|
|
16
|
+
export default LedGrid;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// Svelte wrapper around @glowbox/nixie's canvas tube. Give it a `value` (the lit
|
|
3
|
+
// symbol) plus optional appearance props that mirror the core NixieOptions and update
|
|
4
|
+
// live. The canvas fills its parent — size the parent to size the tube. Ships in
|
|
5
|
+
// @glowbox/svelte alongside <LedGrid>, over the sibling @glowbox/nixie core.
|
|
6
|
+
import {
|
|
7
|
+
createNixieTube,
|
|
8
|
+
type NixieOptions,
|
|
9
|
+
type NixieStyle,
|
|
10
|
+
type NixieTube
|
|
11
|
+
} from '@glowbox/nixie';
|
|
12
|
+
import { untrack } from 'svelte';
|
|
13
|
+
|
|
14
|
+
let {
|
|
15
|
+
value = null,
|
|
16
|
+
tubeStyle = 'classic',
|
|
17
|
+
color,
|
|
18
|
+
glow,
|
|
19
|
+
background,
|
|
20
|
+
mesh,
|
|
21
|
+
ghost,
|
|
22
|
+
pixelRatio,
|
|
23
|
+
oncreate
|
|
24
|
+
}: {
|
|
25
|
+
/** The lit symbol: a char `0`–`9`, `:`, `-`, or null/'' for all-cathodes-dark. */
|
|
26
|
+
value?: string | number | null;
|
|
27
|
+
/** Physical tube style — maps to the core `style` option (renamed to avoid the DOM `style`). */
|
|
28
|
+
tubeStyle?: NixieStyle;
|
|
29
|
+
color?: NixieOptions['color'];
|
|
30
|
+
glow?: number;
|
|
31
|
+
background?: NixieOptions['background'];
|
|
32
|
+
mesh?: boolean;
|
|
33
|
+
ghost?: boolean;
|
|
34
|
+
pixelRatio?: number;
|
|
35
|
+
/** Called with the tube when created, and null on teardown — imperative escape hatch. */
|
|
36
|
+
oncreate?: (tube: NixieTube | null) => void;
|
|
37
|
+
} = $props();
|
|
38
|
+
|
|
39
|
+
let canvas = $state<HTMLCanvasElement | null>(null);
|
|
40
|
+
// $state.raw: the tube is an opaque handle (owns a 2D context + methods), not reactive data.
|
|
41
|
+
let tube = $state.raw<NixieTube | null>(null);
|
|
42
|
+
|
|
43
|
+
// Create the tube once for the canvas — options are read untracked so changing them
|
|
44
|
+
// never re-creates (value goes through setValue, appearance through setOptions below).
|
|
45
|
+
$effect(() => {
|
|
46
|
+
const el = canvas;
|
|
47
|
+
if (!el) return;
|
|
48
|
+
const t = untrack(() =>
|
|
49
|
+
createNixieTube(el, {
|
|
50
|
+
value,
|
|
51
|
+
style: tubeStyle,
|
|
52
|
+
color,
|
|
53
|
+
glow,
|
|
54
|
+
background,
|
|
55
|
+
mesh,
|
|
56
|
+
ghost,
|
|
57
|
+
pixelRatio
|
|
58
|
+
})
|
|
59
|
+
);
|
|
60
|
+
if (!t) {
|
|
61
|
+
console.warn('NixieTube: 2D canvas unavailable');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
tube = t;
|
|
65
|
+
untrack(() => oncreate?.(t));
|
|
66
|
+
return () => {
|
|
67
|
+
t.dispose();
|
|
68
|
+
if (tube === t) tube = null;
|
|
69
|
+
untrack(() => oncreate?.(null));
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Live-update the lit symbol.
|
|
74
|
+
$effect(() => {
|
|
75
|
+
tube?.setValue(value);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Live-update appearance when any option changes.
|
|
79
|
+
$effect(() => {
|
|
80
|
+
tube?.setOptions({ style: tubeStyle, color, glow, background, mesh, ghost, pixelRatio });
|
|
81
|
+
});
|
|
82
|
+
</script>
|
|
83
|
+
|
|
84
|
+
<canvas bind:this={canvas}></canvas>
|
|
85
|
+
|
|
86
|
+
<style>
|
|
87
|
+
canvas {
|
|
88
|
+
display: block;
|
|
89
|
+
width: 100%;
|
|
90
|
+
height: 100%;
|
|
91
|
+
}
|
|
92
|
+
</style>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type NixieOptions, type NixieStyle, type NixieTube } from '@glowbox/nixie';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
/** The lit symbol: a char `0`–`9`, `:`, `-`, or null/'' for all-cathodes-dark. */
|
|
4
|
+
value?: string | number | null;
|
|
5
|
+
/** Physical tube style — maps to the core `style` option (renamed to avoid the DOM `style`). */
|
|
6
|
+
tubeStyle?: NixieStyle;
|
|
7
|
+
color?: NixieOptions['color'];
|
|
8
|
+
glow?: number;
|
|
9
|
+
background?: NixieOptions['background'];
|
|
10
|
+
mesh?: boolean;
|
|
11
|
+
ghost?: boolean;
|
|
12
|
+
pixelRatio?: number;
|
|
13
|
+
/** Called with the tube when created, and null on teardown — imperative escape hatch. */
|
|
14
|
+
oncreate?: (tube: NixieTube | null) => void;
|
|
15
|
+
};
|
|
16
|
+
declare const NixieTube: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
17
|
+
type NixieTube = ReturnType<typeof NixieTube>;
|
|
18
|
+
export default NixieTube;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@glowbox/svelte",
|
|
3
|
+
"version": "1.0.0-rc.2",
|
|
4
|
+
"description": "glowbox components for Svelte 5 — <LedGrid> (3D WebGL LED grid) + <NixieTube> (nixie-tube display).",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"svelte",
|
|
7
|
+
"led",
|
|
8
|
+
"led-cube",
|
|
9
|
+
"webgl",
|
|
10
|
+
"voxel",
|
|
11
|
+
"3d",
|
|
12
|
+
"nixie",
|
|
13
|
+
"component"
|
|
14
|
+
],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"type": "module",
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"svelte": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"svelte": "./dist/index.js",
|
|
27
|
+
"default": "./dist/index.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/eetu/glowbox.git",
|
|
36
|
+
"directory": "packages/svelte"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://eetu.github.io/glowbox/",
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "svelte-package",
|
|
41
|
+
"prepack": "svelte-package",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"lint": "eslint .",
|
|
44
|
+
"lint:fix": "eslint . --fix",
|
|
45
|
+
"typecheck": "svelte-check --tsconfig ./tsconfig.json"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"svelte": "^5"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@glowbox/led-grid": "^1.0.0-rc.2",
|
|
52
|
+
"@glowbox/nixie": "^1.0.0-rc.2"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@anarkisti/eslint-config": "^1",
|
|
56
|
+
"@sveltejs/package": "^2.5",
|
|
57
|
+
"@sveltejs/vite-plugin-svelte": "^7",
|
|
58
|
+
"@vitest/browser": "^4.1",
|
|
59
|
+
"@vitest/browser-playwright": "^4.1",
|
|
60
|
+
"eslint": "^10",
|
|
61
|
+
"playwright": "^1.61",
|
|
62
|
+
"svelte": "^5.56",
|
|
63
|
+
"svelte-check": "^4.7",
|
|
64
|
+
"typescript": "^6",
|
|
65
|
+
"vite": "^8.1",
|
|
66
|
+
"vitest": "^4.1",
|
|
67
|
+
"vitest-browser-svelte": "^2.2"
|
|
68
|
+
}
|
|
69
|
+
}
|