@fusion.dev/helix-headless 0.4.2 → 0.4.4
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/custom-elements.json +39 -39
- package/dist/app-header/root/app-header-root.d.ts +8 -28
- package/dist/avatar/group/avatar-group.d.ts +5 -30
- package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +9 -28
- package/dist/chat/root/chat-root.d.ts +9 -28
- package/dist/color-picker/root/color-picker-root.d.ts +10 -28
- package/dist/combobox/root/combobox-root.d.ts +18 -60
- package/dist/copy-button/root/copy-button-root.d.ts +6 -21
- package/dist/data-table/root/data-table-root.d.ts +9 -32
- package/dist/date-input/root/date-input-root.d.ts +5 -21
- package/dist/date-picker/root/date-picker-root.d.ts +10 -35
- package/dist/dialog/root/dialog-root.d.ts +7 -29
- package/dist/donut-chart/root/donut-chart-root.d.ts +5 -25
- package/dist/file-input/root/file-input-root.d.ts +5 -22
- package/dist/heatmap/root/heatmap-root.d.ts +13 -36
- package/dist/image/root/image-root.d.ts +5 -22
- package/dist/input/root/input-root.d.ts +11 -29
- package/dist/kbd/root/kbd-root.d.ts +8 -26
- package/dist/legend/root/legend-root.d.ts +6 -24
- package/dist/listbox/root/listbox-root.d.ts +4 -26
- package/dist/megamenu/root/megamenu-root.d.ts +7 -39
- package/dist/meter/root/meter-root.d.ts +6 -24
- package/dist/number-input/root/number-input-root.d.ts +11 -20
- package/dist/outline/root/outline-root.d.ts +8 -27
- package/dist/pagination/root/pagination-root.d.ts +21 -54
- package/dist/pagination/summary/pagination-summary.d.ts +10 -31
- package/dist/progress/root/progress-root.d.ts +4 -21
- package/dist/rating/root/rating-root.d.ts +8 -31
- package/dist/slider/root/slider-root.d.ts +9 -32
- package/dist/splitter/root/splitter-root.d.ts +17 -85
- package/dist/steps/root/steps-root.d.ts +8 -26
- package/dist/steps/step/step.d.ts +11 -41
- package/dist/toast/item/toast-item.d.ts +6 -30
- package/dist/toggle/group/toggle-group.d.ts +7 -25
- package/dist/tooltip/root/tooltip-root.d.ts +14 -36
- package/dist/tour/root/tour-root.d.ts +6 -29
- package/dist/tree/item/tree-item.d.ts +13 -35
- package/dist/video/root/video-root.d.ts +12 -40
- package/dist/video-playlist/menu/video-playlist-menu.d.ts +3 -25
- package/dist/virtual-list/root/virtual-list-root.d.ts +5 -44
- package/package.json +17 -18
- package/{jsx.d.ts → types/jsx.d.ts} +724 -2537
- package/{vue.d.ts → types/vue.d.ts} +39 -39
- /package/{svelte.d.ts → types/svelte.d.ts} +0 -0
|
@@ -11,36 +11,13 @@ import type { HelixHeadlessTourStop } from '../stop/tour-stop.js';
|
|
|
11
11
|
*
|
|
12
12
|
* **The spotlight is a shadow, not a hole.** The scrim is a box the size of the
|
|
13
13
|
* current target with an enormous spread on its `box-shadow`, so the darkness
|
|
14
|
-
* is what the shadow paints
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* stops being true the moment anything between them is transformed.
|
|
14
|
+
* is what the shadow paints. A `clip-path` on a full-screen scrim needs the
|
|
15
|
+
* viewport in the same coordinate space as the cut, which stops being true the
|
|
16
|
+
* moment anything between them is transformed.
|
|
18
17
|
*
|
|
19
|
-
* **It measures, and
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* design rule was expressible and the measurement was answering a question
|
|
23
|
-
* nobody had asked. Here the question is "where is that button", and there is
|
|
24
|
-
* no other way to ask it.
|
|
25
|
-
*
|
|
26
|
-
* **Nothing is measured on the server and nothing needs to be.** A tour starts
|
|
27
|
-
* closed and a closed tour draws nothing, so the server renders the stops as
|
|
28
|
-
* inert markup and the first frame is correct. This is also what makes it
|
|
29
|
-
* acceptable for the root to write `current` onto stops it does not own: there
|
|
30
|
-
* is no server state for the write to disagree with.
|
|
31
|
-
*
|
|
32
|
-
* **It cannot point inside a modal, and nothing can fix that here.**
|
|
33
|
-
* `showModal()` makes everything outside the dialog's own subtree inert, so a
|
|
34
|
-
* panel anywhere else is unpressable however the top layer is ordered: this is
|
|
35
|
-
* what modal means rather than a stacking accident. Measured with a modal
|
|
36
|
-
* `Drawer` open, `elementsFromPoint` over the tour's own Done button returned
|
|
37
|
-
* the drawer, the press light-dismissed the drawer, and the tour heard nothing
|
|
38
|
-
* at all. Anything a tour points into has to stay reachable, which for `Drawer`
|
|
39
|
-
* and `Dialog` means not modal while the tour is running.
|
|
40
|
-
*
|
|
41
|
-
* **Focus is not trapped.** A reader has to be able to look at, and reach, the
|
|
42
|
-
* thing being pointed at. `Escape` leaves, which is the one binding a tour
|
|
43
|
-
* needs and the one every reader already tries.
|
|
18
|
+
* **It measures, and the target is an element elsewhere on the page whose
|
|
19
|
+
* position nothing can know statically.** Nothing is measured on the server and
|
|
20
|
+
* nothing needs to be: a tour starts when a reader asks for it.
|
|
44
21
|
*
|
|
45
22
|
* @since 0.1.0
|
|
46
23
|
* @status experimental
|
|
@@ -5,45 +5,23 @@ declare const HelixHeadlessTreeItem_base: (new (...args: any[]) => import("@fusi
|
|
|
5
5
|
* @summary One node in a tree.
|
|
6
6
|
*
|
|
7
7
|
* @description
|
|
8
|
-
* **`open`, on `MixinControllerOpen`, rather than a bare boolean of its own.**
|
|
9
|
-
* branch showing its children is the same state a disclosure holds
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* tree uses; the property is what differs, not the semantics.
|
|
8
|
+
* **`open`, on `MixinControllerOpen`, rather than a bare boolean of its own.**
|
|
9
|
+
* A branch showing its children is the same state a disclosure holds. A
|
|
10
|
+
* hand-written toggle announces nothing, so a consumer could neither know a
|
|
11
|
+
* branch opened nor own the state. `aria-expanded` is still what the row
|
|
12
|
+
* renders: the property differs, not the semantics.
|
|
14
13
|
*
|
|
15
|
-
* An element rather than a slot because it owns state: whether it is open,
|
|
16
|
-
*
|
|
14
|
+
* An element rather than a slot, because it owns state: whether it is open,
|
|
15
|
+
* selected, and whether it holds focus.
|
|
17
16
|
*
|
|
18
17
|
* A branch is any item with something slotted into `children`, detected in
|
|
19
|
-
* `firstUpdated`
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* appears rather than disappearing, which is the harmless direction.
|
|
18
|
+
* `firstUpdated` because the server cannot inspect children. The server renders
|
|
19
|
+
* the node as a leaf and the twisty appears on hydration, which is the harmless
|
|
20
|
+
* direction.
|
|
23
21
|
*
|
|
24
|
-
* **`href` makes the row itself the link.**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* and there is no middle-click, no status bar and no open-in-new-tab. Given an
|
|
28
|
-
* `href` the row *is* an `<a>`, so the platform provides all of that and the
|
|
29
|
-
* keyboard needs no handler at all.
|
|
30
|
-
*
|
|
31
|
-
* It changes the shape of the ARIA, which is why it is a property and not the
|
|
32
|
-
* default. An anchor cannot contain the group, since the group holds more
|
|
33
|
-
* anchors and nesting them is invalid, so the row carries `treeitem` and the
|
|
34
|
-
* group becomes its sibling under a presentational wrapper. That is exactly the
|
|
35
|
-
* navigation flavor of the tree pattern, and the arrangement without an `href`
|
|
36
|
-
* stays as it was: `treeitem` containing `group`, which is what an `<li>`
|
|
37
|
-
* holding a `<ul>` is.
|
|
38
|
-
*
|
|
39
|
-
* A consumer whose framework owns routing slots its own link instead and leaves
|
|
40
|
-
* `href` unset. The row follows that one too, so the whole row works either
|
|
41
|
-
* way; what the anchor adds is what only a real anchor can give.
|
|
42
|
-
*
|
|
43
|
-
* The properties below are written by this component and read by whoever draws
|
|
44
|
-
* it. A consumer sets none of them: they carry a number this element computed
|
|
45
|
-
* and CSS cannot, which is what lets a stylesheet on this tier draw the thing
|
|
46
|
-
* without measuring anything itself.
|
|
22
|
+
* **`href` makes the row itself the link.** With the anchor slotted into the
|
|
23
|
+
* name instead, everything outside the words is dead: no Enter, no middle
|
|
24
|
+
* click, no status bar. Given an `href` the row *is* an `<a>`.
|
|
47
25
|
*
|
|
48
26
|
* @since 0.1.0
|
|
49
27
|
* @status experimental
|
|
@@ -7,57 +7,29 @@ import '../../menu/index.js';
|
|
|
7
7
|
*
|
|
8
8
|
* @description
|
|
9
9
|
* **The picture is a real `<video>`.** Decoding, buffering, captions, Picture
|
|
10
|
-
* in Picture, AirPlay and
|
|
11
|
-
*
|
|
12
|
-
* is the control bar, which is the one part of a video element that cannot be
|
|
13
|
-
* styled and looks different in every browser.
|
|
10
|
+
* in Picture, AirPlay and whatever codecs a device has are the platform's. What
|
|
11
|
+
* is replaced is the control bar, which is the one part that cannot be styled.
|
|
14
12
|
*
|
|
15
13
|
* **Every control is a real control.** The scrubber and the volume are
|
|
16
|
-
* `<input type="range">`, so
|
|
17
|
-
*
|
|
18
|
-
* element implementing any of it. Drawing a div and listening for pointer moves
|
|
19
|
-
* is how a media player ends up unusable from a keyboard.
|
|
14
|
+
* `<input type="range">`, so arrow keys, Home and End, the platform's touch
|
|
15
|
+
* target and its screen reader announcement all come for free.
|
|
20
16
|
*
|
|
21
17
|
* **Native controls are the fallback, not the default.** Where a consumer sets
|
|
22
|
-
* `controls
|
|
23
|
-
*
|
|
24
|
-
* for the native bar has a reason.
|
|
18
|
+
* `controls` the browser's bar is used and this one is not drawn, and the keys
|
|
19
|
+
* below are not bound: two handlers on one press is a video that seeks twice.
|
|
25
20
|
*
|
|
26
21
|
* The keys are the ones every player uses: Space and `k` to play or pause, the
|
|
27
|
-
* arrows to seek and to change
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* `controls` is set they are not bound at all, since two handlers on one press
|
|
31
|
-
* is a video that seeks twice, and the focus stop goes with them.
|
|
22
|
+
* arrows to seek and to change volume, `m` to mute, `f` for fullscreen. They
|
|
23
|
+
* are bound on this element rather than on the document, because a video is not
|
|
24
|
+
* a palette.
|
|
32
25
|
*
|
|
33
26
|
* **`<source>` and `<track>` are copied into the video, never slotted into it.**
|
|
34
27
|
* A media element runs resource selection over its own child *nodes*, and a slot
|
|
35
28
|
* is one node, so slotted sources reach it as nothing: no text tracks, no
|
|
36
29
|
* current source, `readyState` zero. A video given sources that way does not
|
|
37
|
-
* merely lack captions, it does not play.
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* and a framework that re-renders it would find its children gone. The copies
|
|
41
|
-
* are rebuilt whenever the slot changes and the video is asked to load again,
|
|
42
|
-
* which is what makes a `<track>` added later take effect.
|
|
43
|
-
*
|
|
44
|
-
* **The lock screen and a headset's buttons work, and one player owns them.**
|
|
45
|
-
* There is one `navigator.mediaSession` per page, so the rule is stated rather
|
|
46
|
-
* than left to render order: whichever player was started last owns it, which
|
|
47
|
-
* is what a reader means by "this one" when they press play on their
|
|
48
|
-
* headphones. The title comes from `label` and the artwork from `poster`.
|
|
49
|
-
*
|
|
50
|
-
* **`remote` sends the picture somewhere else.** The Remote Playback API is
|
|
51
|
-
* what Safari implements for AirPlay and Chrome for Cast, so one control covers
|
|
52
|
-
* both and the platform supplies the device list. It is offered only once
|
|
53
|
-
* something has answered `watchAvailability`: a button that opens an empty list
|
|
54
|
-
* is worse than no button, and a consumer who wants none can set
|
|
55
|
-
* `disableremoteplayback` on this element.
|
|
56
|
-
*
|
|
57
|
-
* The properties below are written by this component and read by whoever draws
|
|
58
|
-
* it. A consumer sets none of them: they carry a number this element computed
|
|
59
|
-
* and CSS cannot, which is what lets a stylesheet on this tier draw the thing
|
|
60
|
-
* without measuring anything itself.
|
|
30
|
+
* merely lack captions, it does not play. They are copied rather than moved,
|
|
31
|
+
* because the consumer's light DOM is theirs, and rebuilt whenever the slot
|
|
32
|
+
* changes.
|
|
61
33
|
*
|
|
62
34
|
* @since 0.1.0
|
|
63
35
|
* @status experimental
|
|
@@ -7,35 +7,13 @@ import '../../menu/index.js';
|
|
|
7
7
|
* @description
|
|
8
8
|
* The entries again, reachable without leaving the picture. A playlist beside
|
|
9
9
|
* the player is a list a reader scrolls to; the same list in the control bar is
|
|
10
|
-
* one they open over the video
|
|
11
|
-
* a phone has room for.
|
|
10
|
+
* one they open over the video.
|
|
12
11
|
*
|
|
13
12
|
* **It is placed by the consumer, not rendered by the playlist**, and that is
|
|
14
|
-
* forced rather than chosen
|
|
13
|
+
* forced rather than chosen: a shadow root cannot project into a sibling's
|
|
15
14
|
* light DOM, so nothing the playlist renders can land inside the player's
|
|
16
15
|
* control bar. What can is an element the consumer writes there, which is what
|
|
17
|
-
* `Video`'s `controls` slot is for
|
|
18
|
-
*
|
|
19
|
-
* ```html
|
|
20
|
-
* <helix-video-playlist>
|
|
21
|
-
* <helix-video slot="player" controls>
|
|
22
|
-
* <helix-video-playlist-menu slot="controls"></helix-video-playlist-menu>
|
|
23
|
-
* </helix-video>
|
|
24
|
-
* <helix-video-playlist-item value="one" src="/one.mp4">One</helix-video-playlist-item>
|
|
25
|
-
* </helix-video-playlist>
|
|
26
|
-
* ```
|
|
27
|
-
*
|
|
28
|
-
* So it finds its playlist upward rather than being handed one, and that chain
|
|
29
|
-
* crosses a shadow boundary in the ordinary arrangement, which is why it is
|
|
30
|
-
* `closestInstance` and not `closest`.
|
|
31
|
-
*
|
|
32
|
-
* **Choosing dispatches `helix-select`, the same event an entry dispatches.**
|
|
33
|
-
* It never writes `active` on the playlist, which looks like the shorter path
|
|
34
|
-
* and skips the cancelable request: an assignment applies the change with no
|
|
35
|
-
* `helix-before-activate` for anyone to refuse. Announcing is what an entry
|
|
36
|
-
* does, the playlist already listens for it, and a consumer refusing a
|
|
37
|
-
* selection gets `preventDefault()` on one event rather than two paths that
|
|
38
|
-
* behave differently.
|
|
16
|
+
* `Video`'s `controls` slot is for.
|
|
39
17
|
*
|
|
40
18
|
* @since 0.1.0
|
|
41
19
|
* @status experimental
|
|
@@ -10,58 +10,19 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
10
10
|
* `<template>` child says what one row looks like: elements carrying
|
|
11
11
|
* `data-field="name"` are filled with that field of the item.
|
|
12
12
|
*
|
|
13
|
-
* Both halves are markup, and that is the
|
|
14
|
-
* `VirtualizeController` says why the obvious alternatives are shut: an `items`
|
|
13
|
+
* Both halves are markup, and that is the reason for the shape. An `items`
|
|
15
14
|
* property cannot survive React hydration, which skips every property it would
|
|
16
15
|
* have to apply imperatively, and light-DOM children exist by the time anything
|
|
17
|
-
* could decide not to create them. Text in the document is neither
|
|
18
|
-
*
|
|
19
|
-
* node whatever the row count.
|
|
16
|
+
* could decide not to create them. Text in the document is neither, and it is
|
|
17
|
+
* one node whatever the row count.
|
|
20
18
|
*
|
|
21
19
|
* **The accessible version is not a `<table>`**, the way it is for the charts
|
|
22
20
|
* that share this `for="<id>"` shape: ten thousand rows in one is the cost this
|
|
23
|
-
* exists to avoid. It is the rendered window plus `aria-setsize
|
|
24
|
-
* `aria-posinset`, which is what says "four thousand of ten thousand" rather
|
|
25
|
-
* than "four of twenty".
|
|
21
|
+
* exists to avoid. It is the rendered window plus `aria-setsize`.
|
|
26
22
|
*
|
|
27
|
-
* **Rows are light DOM, deliberately.** They are cloned from the consumer's
|
|
28
|
-
* template and appended to the host, so a document rule reaches them, the base
|
|
29
|
-
* layer applies, and a class in the template means what it says. Cloned into
|
|
30
|
-
* the shadow root they would be markup the consumer wrote and could not style,
|
|
31
|
-
* which is the one thing a row template must not be.
|
|
32
|
-
*
|
|
33
|
-
* **Reading the source is client-only**, because a server has no document to
|
|
34
|
-
* query. The window renders empty from the server and fills on the first update,
|
|
35
|
-
* which is the same client-side enhancement rule every tree-reading component
|
|
36
|
-
* here follows.
|
|
37
|
-
*
|
|
38
|
-
* **On this tier the consumer gives the viewport its box**, and the component
|
|
39
|
-
* cannot do it for them: `helix/headless` ships no stylesheet, and a height is a
|
|
40
|
-
* decision rather than a fact this could publish. So a base list wants two rules
|
|
41
|
-
* before it is a list at all:
|
|
42
|
-
*
|
|
43
|
-
* ```css
|
|
44
|
-
* helix-headless-virtual-list::part(viewport) {
|
|
45
|
-
* block-size: 12rem;
|
|
46
|
-
* overflow-y: auto;
|
|
47
|
-
* }
|
|
48
|
-
* ```
|
|
49
|
-
*
|
|
50
|
-
* **Without them it is not a scroller**, and the failure is worse than it
|
|
51
|
-
* sounds. `scrollTop` is always zero, so nothing ever windows; and the box has
|
|
52
|
-
* no height of its own, so `clientHeight` is a function of the rows just drawn.
|
|
53
|
-
* Measuring then moves what is being measured, and the two need not converge:
|
|
54
|
-
* measured in Chromium at 500 rows, it settled into a two-cycle between 378 and
|
|
55
|
-
* 379 rows and stayed there, at two and a half thousand passes in eight seconds.
|
|
56
|
-
* `VirtualizeController` gives up after ten passes and says so, so this now
|
|
57
|
-
* draws a bad window rather than stopping the page, but a bad window is still
|
|
58
|
-
* the answer until the rules are written.
|
|
59
|
-
*
|
|
60
|
-
* The styled tier writes them for you, with `--virtual-list-block-size`.
|
|
61
|
-
*
|
|
62
|
-
* @category layout
|
|
63
23
|
* @since 0.1.0
|
|
64
24
|
* @status experimental
|
|
25
|
+
* @category layout
|
|
65
26
|
* @documentation https://helix.fusion.dev/components/virtual-list
|
|
66
27
|
*
|
|
67
28
|
* @csspart after - The space standing in for rows below the window.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fusion.dev/helix-headless",
|
|
3
3
|
"description": "Headless component tier. Structure, accessibility, state, and form participation. No styling.",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.4",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ross Sheppard <ross.sheppard@fusion.dev>",
|
|
7
7
|
"repository": {
|
|
@@ -21,41 +21,40 @@
|
|
|
21
21
|
"types": "./dist/*/index.d.ts",
|
|
22
22
|
"default": "./dist/*/index.js"
|
|
23
23
|
},
|
|
24
|
-
"./
|
|
25
|
-
"./
|
|
26
|
-
"./
|
|
24
|
+
"./custom-elements.json": "./custom-elements.json",
|
|
25
|
+
"./jsx": "./types/jsx.d.ts",
|
|
26
|
+
"./svelte": "./types/svelte.d.ts",
|
|
27
|
+
"./vue": "./types/vue.d.ts"
|
|
27
28
|
},
|
|
28
29
|
"files": [
|
|
29
30
|
"custom-elements.json",
|
|
30
31
|
"dist",
|
|
31
|
-
"
|
|
32
|
-
"svelte.d.ts",
|
|
33
|
-
"vue.d.ts"
|
|
32
|
+
"types"
|
|
34
33
|
],
|
|
35
34
|
"customElements": "custom-elements.json",
|
|
36
|
-
"sideEffects":
|
|
37
|
-
"./dist/**/*.js"
|
|
38
|
-
],
|
|
35
|
+
"sideEffects": true,
|
|
39
36
|
"scripts": {
|
|
40
|
-
"analyze": "cem analyze --config
|
|
41
|
-
"build": "tsc -p tsconfig.json && tsc -p tsconfig.types.json",
|
|
42
|
-
"
|
|
37
|
+
"analyze": "bun --bun cem analyze --config cem.config.ts",
|
|
38
|
+
"build": "bun run element-events && tsc -p tsconfig.build.json && tsc -p tsconfig.types.json",
|
|
39
|
+
"element-events": "build-element-events --package headless",
|
|
40
|
+
"test": "bun run element-events && bun run test:pure && bun run test:dom && bun run test:ssr",
|
|
43
41
|
"test:dom": "bun test dom.spec",
|
|
44
42
|
"test:pure": "bun test pure.spec",
|
|
45
43
|
"test:ssr": "bun test ssr.spec",
|
|
46
|
-
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
44
|
+
"typecheck": "bun run element-events && tsc -p tsconfig.json --noEmit"
|
|
47
45
|
},
|
|
48
46
|
"bugs": "https://github.com/project-fusion/fusion/issues",
|
|
49
47
|
"dependencies": {
|
|
50
|
-
"@fusion.dev/helix-core": "0.4.
|
|
51
|
-
"@fusion.dev/helix-icons": "0.4.
|
|
48
|
+
"@fusion.dev/helix-core": "0.4.4",
|
|
49
|
+
"@fusion.dev/helix-icons": "0.4.4",
|
|
52
50
|
"uqr": "^0.1.3"
|
|
53
51
|
},
|
|
54
52
|
"devDependencies": {
|
|
55
53
|
"@custom-elements-manifest/analyzer": "^0.11.0",
|
|
54
|
+
"@fusion.dev/helix-cem": "0.4.4",
|
|
55
|
+
"@fusion.dev/helix-testing": "0.4.4",
|
|
56
|
+
"@fusion.dev/scripts": "0.0.0-dev",
|
|
56
57
|
"@happy-dom/global-registrator": "^20.0.0",
|
|
57
|
-
"@fusion.dev/helix-cem": "0.4.2",
|
|
58
|
-
"@fusion.dev/helix-testing": "0.4.2",
|
|
59
58
|
"@lit-labs/ssr": "^3.3.0",
|
|
60
59
|
"@types/bun": "^1.3.0",
|
|
61
60
|
"lit": "^3.2.1"
|