@lapikit/repl 0.0.0-insiders.7635da2 → 0.0.0-insiders.83ebe3a
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/README.md +92 -0
- package/dist/Button.svelte +13 -4
- package/dist/Button.svelte.d.ts +6 -2
- package/dist/Files.svelte +15 -8
- package/dist/Repl.svelte +43 -40
- package/dist/Toolbar.svelte +32 -21
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1 +1,93 @@
|
|
|
1
1
|
# @Lapikit/repl
|
|
2
|
+
|
|
3
|
+
@lapikit/repl is a Svelte component for Lapikit. It's a add-on package for Lapikit library.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm install -D @lapikit/repl
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
// svelte.config.js
|
|
15
|
+
|
|
16
|
+
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
|
17
|
+
|
|
18
|
+
const config = {
|
|
19
|
+
preprocess: [vitePreprocess(), lapikitPreprocess({ plugins: ['repl'] })],
|
|
20
|
+
|
|
21
|
+
...
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default config;
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```svelte
|
|
30
|
+
<kit:repl content="console.log('hello')" />
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
```svelte
|
|
35
|
+
<script lang="ts">
|
|
36
|
+
import sampleCounter from './samples/counter.svelte?raw';
|
|
37
|
+
import Counter from './samples/counter.svelte';
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<kit:repl presentation content={sampleCounter}>
|
|
41
|
+
<Counter />
|
|
42
|
+
</kit:repl>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
```svelte
|
|
47
|
+
<script lang="ts">
|
|
48
|
+
import sampleJson from './samples/json.json?raw';
|
|
49
|
+
import sampleCounter from './samples/counter.svelte?raw';
|
|
50
|
+
import sampleCss from './samples/css.css?raw';
|
|
51
|
+
import Counter from './samples/counter.svelte';
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<kit:repl
|
|
55
|
+
presentation
|
|
56
|
+
content={{
|
|
57
|
+
'Counter.svelte': { code: sampleCounter, lang: 'svelte' },
|
|
58
|
+
'file.json': { code: sampleJson, lang: 'json' },
|
|
59
|
+
'Styles.css': { code: sampleCss, lang: 'css' },
|
|
60
|
+
}}
|
|
61
|
+
>
|
|
62
|
+
<Counter />
|
|
63
|
+
</kit:repl>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
```svelte
|
|
68
|
+
<script lang="ts">
|
|
69
|
+
import sampleJson from './samples/json.json?raw';
|
|
70
|
+
import sampleCounter from './samples/counter.svelte?raw';
|
|
71
|
+
import sampleCss from './samples/css.css?raw';
|
|
72
|
+
import Counter from './samples/counter.svelte';
|
|
73
|
+
</script>
|
|
74
|
+
|
|
75
|
+
<kit:repl
|
|
76
|
+
content={{
|
|
77
|
+
'Counter.svelte': { code: sampleCounter, lang: 'svelte' },
|
|
78
|
+
'file.json': { code: sampleJson, lang: 'json' },
|
|
79
|
+
'Styles.css': { code: sampleCss, lang: 'css' },
|
|
80
|
+
}}
|
|
81
|
+
>
|
|
82
|
+
<Counter />
|
|
83
|
+
</kit:repl>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
## Props
|
|
88
|
+
|
|
89
|
+
| Prop | Type | Default | Description |
|
|
90
|
+
| ----------- | --------------------------- | --------- | ------------------------------------------------------------ |
|
|
91
|
+
| content | string \| Record<string, { code: string; lang?: string }> | '' | The code content to be displayed in the REPL. It can be a single string or an object representing multiple files. |
|
|
92
|
+
| presentation | boolean | false | If true, the REPL will be in presentation mode, showing only the output without the code editor. |
|
|
93
|
+
| tiltle | string | '' | The title displayed on the REPL toolbar. |
|
package/dist/Button.svelte
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
let { children, type = 'button', disabled = false, ...rest }: PropsTypeButton = $props();
|
|
5
|
+
|
|
6
|
+
type PropsTypeButton = {
|
|
7
|
+
children?: Snippet;
|
|
8
|
+
type?: 'button' | 'submit' | 'reset';
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
[rest: string]: unknown;
|
|
11
|
+
};
|
|
3
12
|
</script>
|
|
4
13
|
|
|
5
|
-
<button {...rest}>
|
|
14
|
+
<button {type} {disabled} {...rest}>
|
|
6
15
|
{@render children?.()}
|
|
7
16
|
</button>
|
|
8
17
|
|
|
@@ -18,11 +27,11 @@
|
|
|
18
27
|
border-radius: 0.375rem;
|
|
19
28
|
transition: background-color 0.2s ease;
|
|
20
29
|
padding: 8px;
|
|
21
|
-
color: var(--repl-secondary);
|
|
30
|
+
color: var(--kit-repl-secondary);
|
|
22
31
|
}
|
|
23
32
|
|
|
24
33
|
button:hover {
|
|
25
|
-
color: var(--repl-primary);
|
|
34
|
+
color: var(--kit-repl-primary);
|
|
26
35
|
}
|
|
27
36
|
|
|
28
37
|
button :global(svg) {
|
package/dist/Button.svelte.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
1
2
|
declare const Button: import("svelte").Component<{
|
|
2
|
-
|
|
3
|
-
|
|
3
|
+
[rest: string]: unknown;
|
|
4
|
+
children?: Snippet;
|
|
5
|
+
type?: "button" | "submit" | "reset";
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
}, {}, "">;
|
|
4
8
|
type Button = ReturnType<typeof Button>;
|
|
5
9
|
export default Button;
|
package/dist/Files.svelte
CHANGED
|
@@ -6,9 +6,16 @@
|
|
|
6
6
|
</script>
|
|
7
7
|
|
|
8
8
|
{#if modeState !== 'playground' && viewState === 'code' && files && files.length > 1}
|
|
9
|
-
<div>
|
|
9
|
+
<div role="tablist" aria-label="Files">
|
|
10
10
|
{#each files as file, index (index)}
|
|
11
|
-
<button
|
|
11
|
+
<button
|
|
12
|
+
type="button"
|
|
13
|
+
role="tab"
|
|
14
|
+
aria-selected={activeIndex === index}
|
|
15
|
+
aria-label="Select {file.name}"
|
|
16
|
+
class:active={activeIndex === index}
|
|
17
|
+
onclick={() => (activeIndex = index)}
|
|
18
|
+
>
|
|
12
19
|
{#if file.lang && dictionaryIcons[file.lang]}
|
|
13
20
|
<img src={dictionaryIcons[file.lang]} alt="{file.lang} icon" />
|
|
14
21
|
{/if}
|
|
@@ -21,18 +28,18 @@
|
|
|
21
28
|
<style>
|
|
22
29
|
div {
|
|
23
30
|
display: flex;
|
|
24
|
-
gap: calc(var(--repl-spacing) * 2);
|
|
25
|
-
padding-left: calc(5 * var(--repl-spacing));
|
|
26
|
-
padding-right: calc(5 * var(--repl-spacing));
|
|
27
|
-
padding-block: calc(var(--repl-spacing) * 2);
|
|
31
|
+
gap: calc(var(--kit-repl-spacing) * 2);
|
|
32
|
+
padding-left: calc(5 * var(--kit-repl-spacing));
|
|
33
|
+
padding-right: calc(5 * var(--kit-repl-spacing));
|
|
34
|
+
padding-block: calc(var(--kit-repl-spacing) * 2);
|
|
28
35
|
overflow-x: auto;
|
|
29
36
|
}
|
|
30
37
|
|
|
31
38
|
button {
|
|
32
39
|
display: flex;
|
|
33
40
|
align-items: center;
|
|
34
|
-
gap: calc(var(--repl-spacing) * 2);
|
|
35
|
-
padding: calc(var(--repl-spacing) * 2) calc(var(--repl-spacing) * 3);
|
|
41
|
+
gap: calc(var(--kit-repl-spacing) * 2);
|
|
42
|
+
padding: calc(var(--kit-repl-spacing) * 2) calc(var(--kit-repl-spacing) * 3);
|
|
36
43
|
font-size: 0.875rem;
|
|
37
44
|
transition: all 0.2s ease;
|
|
38
45
|
border: 0;
|
package/dist/Repl.svelte
CHANGED
|
@@ -109,9 +109,9 @@
|
|
|
109
109
|
});
|
|
110
110
|
</script>
|
|
111
111
|
|
|
112
|
-
<div class="
|
|
112
|
+
<div class="kit-repl">
|
|
113
113
|
{#if presentation}
|
|
114
|
-
<div class="repl-content" class:repl-content--playground={presentation}>
|
|
114
|
+
<div class="kit-repl-content" class:kit-repl-content--playground={presentation}>
|
|
115
115
|
<div
|
|
116
116
|
class="wrapper-playground"
|
|
117
117
|
class:dark={themeState === 'dark'}
|
|
@@ -122,7 +122,7 @@
|
|
|
122
122
|
</div>
|
|
123
123
|
{/if}
|
|
124
124
|
|
|
125
|
-
<div class="repl-container">
|
|
125
|
+
<div class="kit-repl-container">
|
|
126
126
|
<Toolbar
|
|
127
127
|
{title}
|
|
128
128
|
{language}
|
|
@@ -139,13 +139,16 @@
|
|
|
139
139
|
|
|
140
140
|
<Files {files} bind:activeIndex={activeFileIndex} {modeState} {viewState} />
|
|
141
141
|
|
|
142
|
-
<div
|
|
142
|
+
<div
|
|
143
|
+
class="kit-repl-content"
|
|
144
|
+
class:kit-repl-content--code={viewState === 'code' && !presentation}
|
|
145
|
+
>
|
|
143
146
|
{#if viewState === 'code'}
|
|
144
147
|
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
145
|
-
<div class="wrapper-highlight" bind:this={ref}>{@html codeHTML}</div>
|
|
148
|
+
<div class="kit-repl-wrapper-highlight" bind:this={ref}>{@html codeHTML}</div>
|
|
146
149
|
{:else}
|
|
147
150
|
<div
|
|
148
|
-
class="wrapper-playground"
|
|
151
|
+
class="kit-repl-wrapper-playground"
|
|
149
152
|
class:dark={themeState === 'dark'}
|
|
150
153
|
class:light={themeState === 'light'}
|
|
151
154
|
>
|
|
@@ -157,70 +160,70 @@
|
|
|
157
160
|
</div>
|
|
158
161
|
|
|
159
162
|
<style>
|
|
160
|
-
.
|
|
163
|
+
.kit-repl {
|
|
161
164
|
/* ui */
|
|
162
|
-
--repl-spacing: 0.25rem;
|
|
163
|
-
--repl-radius: 1rem;
|
|
165
|
+
--kit-repl-spacing: 0.25rem;
|
|
166
|
+
--kit-repl-radius: 1rem;
|
|
164
167
|
|
|
165
168
|
/* shiki override */
|
|
166
|
-
--repl-shiki-size: 0.875rem;
|
|
167
|
-
--repl-shiki-tab-size: 2;
|
|
169
|
+
--kit-repl-shiki-size: 0.875rem;
|
|
170
|
+
--kit-repl-shiki-tab-size: 2;
|
|
168
171
|
|
|
169
172
|
/* colors */
|
|
170
|
-
--repl-background: #f9f9f9;
|
|
171
|
-
--repl-border-color: #ebebeb;
|
|
172
|
-
--repl-primary: #0d0d34;
|
|
173
|
-
--repl-secondary: #8f8f8f;
|
|
173
|
+
--kit-repl-background: #f9f9f9;
|
|
174
|
+
--kit-repl-border-color: #ebebeb;
|
|
175
|
+
--kit-repl-primary: #0d0d34;
|
|
176
|
+
--kit-repl-secondary: #8f8f8f;
|
|
174
177
|
}
|
|
175
|
-
.repl-container {
|
|
176
|
-
background-color: var(--repl-background);
|
|
177
|
-
border-radius: var(--repl-radius);
|
|
178
|
-
border: 2px solid var(--repl-border-color);
|
|
178
|
+
.kit-repl-container {
|
|
179
|
+
background-color: var(--kit-repl-background);
|
|
180
|
+
border-radius: var(--kit-repl-radius);
|
|
181
|
+
border: 2px solid var(--kit-repl-border-color);
|
|
179
182
|
}
|
|
180
183
|
|
|
181
|
-
.repl-container :global(pre) {
|
|
182
|
-
background-color: var(--repl-background) !important;
|
|
184
|
+
.kit-repl-container :global(pre) {
|
|
185
|
+
background-color: var(--kit-repl-background) !important;
|
|
183
186
|
}
|
|
184
187
|
|
|
185
|
-
.repl-content {
|
|
188
|
+
.kit-repl-content {
|
|
186
189
|
display: flow-root;
|
|
187
|
-
margin-top: calc(var(--repl-spacing) * 0);
|
|
188
|
-
padding-right: calc(10 * var(--repl-spacing));
|
|
189
|
-
padding-left: calc(5 * var(--repl-spacing));
|
|
190
|
-
padding-bottom: calc(4 * var(--repl-spacing));
|
|
191
|
-
padding-top: calc(3 * var(--repl-spacing));
|
|
190
|
+
margin-top: calc(var(--kit-repl-spacing) * 0);
|
|
191
|
+
padding-right: calc(10 * var(--kit-repl-spacing));
|
|
192
|
+
padding-left: calc(5 * var(--kit-repl-spacing));
|
|
193
|
+
padding-bottom: calc(4 * var(--kit-repl-spacing));
|
|
194
|
+
padding-top: calc(3 * var(--kit-repl-spacing));
|
|
192
195
|
position: relative;
|
|
193
196
|
}
|
|
194
197
|
|
|
195
|
-
.repl-content--code {
|
|
198
|
+
.kit-repl-content--code {
|
|
196
199
|
padding-top: 0;
|
|
197
200
|
}
|
|
198
201
|
|
|
199
|
-
.repl-content--playground {
|
|
200
|
-
padding-top: calc(4 * var(--repl-spacing));
|
|
201
|
-
padding-bottom: calc(10 * var(--repl-spacing));
|
|
202
|
+
.kit-repl-content--playground {
|
|
203
|
+
padding-top: calc(4 * var(--kit-repl-spacing));
|
|
204
|
+
padding-bottom: calc(10 * var(--kit-repl-spacing));
|
|
202
205
|
}
|
|
203
206
|
|
|
204
207
|
hr {
|
|
205
208
|
max-width: calc(100% - 2.5rem);
|
|
206
209
|
margin-inline-start: calc(2.5rem / 2);
|
|
207
210
|
display: block;
|
|
208
|
-
border: thin solid var(--repl-border-color);
|
|
211
|
+
border: thin solid var(--kit-repl-border-color);
|
|
209
212
|
margin-top: 0;
|
|
210
213
|
margin-bottom: 0;
|
|
211
214
|
}
|
|
212
215
|
|
|
213
|
-
div.repl-container .wrapper-highlight :global(pre code) {
|
|
214
|
-
font-size: var(--repl-shiki-size);
|
|
215
|
-
-moz-tab-size: var(--repl-shiki-tab-size);
|
|
216
|
-
tab-size: var(--repl-shiki-tab-size);
|
|
216
|
+
div.kit-repl-container .kit-repl-wrapper-highlight :global(pre code) {
|
|
217
|
+
font-size: var(--kit-repl-shiki-size);
|
|
218
|
+
-moz-tab-size: var(--kit-repl-shiki-tab-size);
|
|
219
|
+
tab-size: var(--kit-repl-shiki-tab-size);
|
|
217
220
|
white-space: pre-wrap;
|
|
218
221
|
word-break: break-word;
|
|
219
222
|
}
|
|
220
223
|
|
|
221
|
-
div.repl-container .wrapper-playground {
|
|
222
|
-
background-color: var(--repl-background);
|
|
223
|
-
border-radius: var(--repl-radius);
|
|
224
|
-
padding: calc(4 * var(--repl-spacing));
|
|
224
|
+
div.kit-repl-container .kit-repl-wrapper-playground {
|
|
225
|
+
background-color: var(--kit-repl-background);
|
|
226
|
+
border-radius: var(--kit-repl-radius);
|
|
227
|
+
padding: calc(4 * var(--kit-repl-spacing));
|
|
225
228
|
}
|
|
226
229
|
</style>
|
package/dist/Toolbar.svelte
CHANGED
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
);
|
|
20
20
|
</script>
|
|
21
21
|
|
|
22
|
-
<div class="
|
|
22
|
+
<div class="kit-repl--toolbar">
|
|
23
23
|
<div
|
|
24
|
-
class="
|
|
25
|
-
class:
|
|
26
|
-
class:
|
|
24
|
+
class="kit-repl--toolbar-title"
|
|
25
|
+
class:kit-repl--toolbar-title--language={!title && language}
|
|
26
|
+
class:kit-repl--toolbar-title--title={title}
|
|
27
27
|
>
|
|
28
28
|
{#if title}
|
|
29
29
|
<span>{title}</span>
|
|
@@ -32,9 +32,13 @@
|
|
|
32
32
|
{/if}
|
|
33
33
|
</div>
|
|
34
34
|
|
|
35
|
-
<div class="
|
|
35
|
+
<div class="kit-repl--toolbar-actions">
|
|
36
36
|
{#if (modeState !== 'code' && viewState === 'preview') || presentation}
|
|
37
|
-
<Button
|
|
37
|
+
<Button
|
|
38
|
+
aria-label={themeState === 'light' ? 'Switch to dark theme' : 'Switch to light theme'}
|
|
39
|
+
aria-pressed={themeState === 'dark'}
|
|
40
|
+
onclick={() => (themeState = themeState === 'light' ? 'dark' : 'light')}
|
|
41
|
+
>
|
|
38
42
|
{#if themeState === 'light'}
|
|
39
43
|
<Moon />
|
|
40
44
|
{:else}
|
|
@@ -44,7 +48,11 @@
|
|
|
44
48
|
{/if}
|
|
45
49
|
|
|
46
50
|
{#if modeState === 'mixed' && !presentation}
|
|
47
|
-
<Button
|
|
51
|
+
<Button
|
|
52
|
+
aria-label={viewState === 'code' ? 'Show preview' : 'Show code'}
|
|
53
|
+
aria-pressed={viewState === 'preview'}
|
|
54
|
+
onclick={() => (viewState = viewState === 'code' ? 'preview' : 'code')}
|
|
55
|
+
>
|
|
48
56
|
{#if viewState === 'code'}
|
|
49
57
|
<Code />
|
|
50
58
|
{:else}
|
|
@@ -54,7 +62,10 @@
|
|
|
54
62
|
{/if}
|
|
55
63
|
|
|
56
64
|
{#if modeState !== 'playground'}
|
|
57
|
-
<Button
|
|
65
|
+
<Button
|
|
66
|
+
aria-label={copyState ? 'Code copied' : 'Copy code'}
|
|
67
|
+
onclick={() => (copyState = true)}
|
|
68
|
+
>
|
|
58
69
|
{#if copyState}
|
|
59
70
|
<Check />
|
|
60
71
|
{:else}
|
|
@@ -66,44 +77,44 @@
|
|
|
66
77
|
</div>
|
|
67
78
|
|
|
68
79
|
<style>
|
|
69
|
-
.
|
|
80
|
+
.kit-repl--toolbar {
|
|
70
81
|
display: flex;
|
|
71
82
|
align-items: center;
|
|
72
83
|
justify-content: space-between;
|
|
73
|
-
gap: calc(var(--repl-spacing) * 3);
|
|
74
|
-
padding-left: calc(5 * var(--repl-spacing));
|
|
75
|
-
padding-right: calc(var(--repl-spacing) * 2);
|
|
76
|
-
padding-block: calc(var(--repl-spacing) * 1.5);
|
|
77
|
-
border-top-left-radius: var(--repl-radius);
|
|
78
|
-
border-top-right-radius: var(--repl-radius);
|
|
84
|
+
gap: calc(var(--kit-repl-spacing) * 3);
|
|
85
|
+
padding-left: calc(5 * var(--kit-repl-spacing));
|
|
86
|
+
padding-right: calc(var(--kit-repl-spacing) * 2);
|
|
87
|
+
padding-block: calc(var(--kit-repl-spacing) * 1.5);
|
|
88
|
+
border-top-left-radius: var(--kit-repl-radius);
|
|
89
|
+
border-top-right-radius: var(--kit-repl-radius);
|
|
79
90
|
min-height: 36px;
|
|
80
91
|
}
|
|
81
92
|
|
|
82
|
-
.
|
|
93
|
+
.kit-repl--toolbar .kit-repl--toolbar-title {
|
|
83
94
|
display: flex;
|
|
84
95
|
align-items: center;
|
|
85
|
-
gap: calc(var(--repl-spacing) * 2);
|
|
96
|
+
gap: calc(var(--kit-repl-spacing) * 2);
|
|
86
97
|
max-width: 80%;
|
|
87
98
|
min-width: 0;
|
|
88
99
|
}
|
|
89
100
|
|
|
90
|
-
.
|
|
101
|
+
.kit-repl--toolbar-title--language {
|
|
91
102
|
font-size: 0.875rem;
|
|
92
103
|
line-height: 16px;
|
|
93
104
|
font-weight: 400;
|
|
94
105
|
color: #5d5d5d;
|
|
95
106
|
}
|
|
96
107
|
|
|
97
|
-
.
|
|
108
|
+
.kit-repl--toolbar-title--title {
|
|
98
109
|
font-weight: 500;
|
|
99
110
|
font-size: 1rem;
|
|
100
111
|
line-height: 20px;
|
|
101
112
|
color: #8f8f8f;
|
|
102
113
|
}
|
|
103
114
|
|
|
104
|
-
.
|
|
115
|
+
.kit-repl--toolbar .kit-repl--toolbar-actions {
|
|
105
116
|
display: flex;
|
|
106
117
|
align-items: center;
|
|
107
|
-
gap: calc(var(--repl-spacing) * 2);
|
|
118
|
+
gap: calc(var(--kit-repl-spacing) * 2);
|
|
108
119
|
}
|
|
109
120
|
</style>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lapikit/repl",
|
|
3
|
-
"version": "0.0.0-insiders.
|
|
3
|
+
"version": "0.0.0-insiders.83ebe3a",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"repository": {
|
|
13
13
|
"type": "git",
|
|
14
|
-
"url": "https://github.com/Nycolaide/lapikit.git"
|
|
14
|
+
"url": "git+https://github.com/Nycolaide/lapikit.git"
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
17
|
"dev": "vite dev",
|