@lapikit/repl 0.0.1 → 0.0.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/dist/Button.svelte +32 -0
- package/dist/Button.svelte.d.ts +5 -0
- package/dist/Files.svelte +67 -0
- package/dist/Files.svelte.d.ts +4 -0
- package/dist/Repl.svelte +61 -205
- package/dist/Repl.svelte.d.ts +2 -5
- package/dist/Toolbar.svelte +93 -0
- package/dist/Toolbar.svelte.d.ts +4 -0
- package/dist/types.d.ts +25 -0
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +6 -0
- package/dist/utils.js +31 -0
- package/package.json +1 -1
- package/dist/assets/icons/check.svg +0 -1
- package/dist/assets/icons/code.svg +0 -1
- package/dist/assets/icons/codesandbox.svg +0 -1
- package/dist/assets/icons/copy.svg +0 -1
- /package/dist/{assets/languages → languages}/css.svg +0 -0
- /package/dist/{assets/languages → languages}/html.svg +0 -0
- /package/dist/{assets/languages → languages}/javascript.svg +0 -0
- /package/dist/{assets/languages/code.svg → languages/shell.svg} +0 -0
- /package/dist/{assets/languages → languages}/svelte.svg +0 -0
- /package/dist/{assets/languages → languages}/typescript.svg +0 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let { children, ...rest } = $props();
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<button {...rest}>
|
|
6
|
+
{@render children?.()}
|
|
7
|
+
</button>
|
|
8
|
+
|
|
9
|
+
<style>
|
|
10
|
+
button {
|
|
11
|
+
background: none;
|
|
12
|
+
border: none;
|
|
13
|
+
cursor: pointer;
|
|
14
|
+
display: flex;
|
|
15
|
+
align-self: center;
|
|
16
|
+
justify-content: center;
|
|
17
|
+
font-size: 0.875rem;
|
|
18
|
+
border-radius: 0.375rem;
|
|
19
|
+
transition: background-color 0.2s ease;
|
|
20
|
+
padding: 8px;
|
|
21
|
+
color: var(--repl-secondary);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
button:hover {
|
|
25
|
+
color: var(--repl-primary);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
button :global(svg) {
|
|
29
|
+
height: 20px;
|
|
30
|
+
width: 20px;
|
|
31
|
+
}
|
|
32
|
+
</style>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { FilesProps } from './types.js';
|
|
3
|
+
import { dictionaryIcons } from './utils.js';
|
|
4
|
+
|
|
5
|
+
let { files, activeIndex = $bindable() }: FilesProps = $props();
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
{#if files && files.length > 1}
|
|
9
|
+
<div>
|
|
10
|
+
{#each files as file, index (index)}
|
|
11
|
+
<button class:active={activeIndex === index} onclick={() => (activeIndex = index)}>
|
|
12
|
+
{#if file.lang && dictionaryIcons[file.lang]}
|
|
13
|
+
<img src={dictionaryIcons[file.lang]} alt="{file.lang} icon" />
|
|
14
|
+
{/if}
|
|
15
|
+
<span>{file.name}</span>
|
|
16
|
+
</button>
|
|
17
|
+
{/each}
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<hr />
|
|
21
|
+
{/if}
|
|
22
|
+
|
|
23
|
+
<style>
|
|
24
|
+
div {
|
|
25
|
+
display: flex;
|
|
26
|
+
gap: calc(var(--repl-spacing) * 2);
|
|
27
|
+
padding-left: calc(5 * var(--repl-spacing));
|
|
28
|
+
padding-right: calc(5 * var(--repl-spacing));
|
|
29
|
+
padding-block: calc(var(--repl-spacing) * 2);
|
|
30
|
+
overflow-x: auto;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
button {
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
gap: calc(var(--repl-spacing) * 2);
|
|
37
|
+
padding: calc(var(--repl-spacing) * 2) calc(var(--repl-spacing) * 3);
|
|
38
|
+
border-radius: 0.375rem;
|
|
39
|
+
font-size: 0.875rem;
|
|
40
|
+
transition: all 0.2s ease;
|
|
41
|
+
border: 1px solid transparent;
|
|
42
|
+
white-space: nowrap;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
button img {
|
|
46
|
+
width: 16px;
|
|
47
|
+
height: 16px;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
button:hover {
|
|
51
|
+
background-color: #f5f5f5;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
button.active {
|
|
55
|
+
background-color: #f0f0f0;
|
|
56
|
+
border-color: #d0d0d0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
hr {
|
|
60
|
+
max-width: calc(100% - 4.5rem);
|
|
61
|
+
margin-inline-start: calc(4.5rem / 2);
|
|
62
|
+
display: block;
|
|
63
|
+
border: thin solid var(--repl-border-color);
|
|
64
|
+
margin-top: 0;
|
|
65
|
+
margin-bottom: 0;
|
|
66
|
+
}
|
|
67
|
+
</style>
|
package/dist/Repl.svelte
CHANGED
|
@@ -1,32 +1,28 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { copyToClipboard } from './utils.js';
|
|
3
|
+
import { getHighlighterSingleton } from './shiki.js';
|
|
4
|
+
import type { FileItem, ReplProps } from './types.js';
|
|
3
5
|
|
|
4
|
-
|
|
6
|
+
// components
|
|
7
|
+
import Toolbar from './Toolbar.svelte';
|
|
8
|
+
import Files from './Files.svelte';
|
|
5
9
|
|
|
6
|
-
|
|
7
|
-
import JavaScriptIcon from './assets/languages/javascript.svg';
|
|
8
|
-
import TypeScriptIcon from './assets/languages/typescript.svg';
|
|
9
|
-
import SvelteIcon from './assets/languages/svelte.svg';
|
|
10
|
-
import CssIcon from './assets/languages/css.svg';
|
|
11
|
-
import HtmlIcon from './assets/languages/html.svg';
|
|
12
|
-
import { getHighlighterSingleton } from './shiki.js';
|
|
10
|
+
let { title, content, children, presentation }: ReplProps = $props();
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
content: string;
|
|
17
|
-
lang?: string;
|
|
18
|
-
}
|
|
12
|
+
// refs
|
|
13
|
+
let ref: null | HTMLElement = $state(null);
|
|
19
14
|
|
|
20
|
-
|
|
21
|
-
let
|
|
15
|
+
// states
|
|
16
|
+
let language = $state('javascript');
|
|
17
|
+
|
|
18
|
+
let modeState: 'code' | 'playground' | 'mixed' = $state('code');
|
|
22
19
|
let copyState = $state(false);
|
|
23
|
-
let
|
|
24
|
-
let
|
|
20
|
+
let viewState: 'code' | 'preview' = $state('code');
|
|
21
|
+
let themeState: 'light' | 'dark' = $state('light');
|
|
25
22
|
|
|
26
|
-
let
|
|
27
|
-
let
|
|
23
|
+
let codeHTML = $state('');
|
|
24
|
+
let activeFileIndex = $state(0);
|
|
28
25
|
|
|
29
|
-
// multiple content types
|
|
30
26
|
let files = $derived.by<FileItem[]>(() => {
|
|
31
27
|
if (typeof content === 'object' && content !== null && 'code' in content) {
|
|
32
28
|
return [
|
|
@@ -41,8 +37,14 @@
|
|
|
41
37
|
if (typeof content === 'object' && content !== null && !Array.isArray(content)) {
|
|
42
38
|
return Object.entries(content).map(([name, fileContent]) => ({
|
|
43
39
|
name,
|
|
44
|
-
content:
|
|
45
|
-
|
|
40
|
+
content:
|
|
41
|
+
typeof fileContent === 'string'
|
|
42
|
+
? fileContent
|
|
43
|
+
: (fileContent as Record<string, unknown>).code || '',
|
|
44
|
+
lang:
|
|
45
|
+
typeof fileContent === 'object'
|
|
46
|
+
? ((fileContent as Record<string, unknown>).lang as string)
|
|
47
|
+
: 'javascript'
|
|
46
48
|
}));
|
|
47
49
|
}
|
|
48
50
|
|
|
@@ -56,21 +58,23 @@
|
|
|
56
58
|
|
|
57
59
|
return [{ name: 'code', content: content || '', lang: 'javascript' }];
|
|
58
60
|
});
|
|
59
|
-
|
|
60
|
-
let activeFileIndex = $state(0);
|
|
61
61
|
let activeFile = $derived(files[activeFileIndex]);
|
|
62
|
-
let hasMultipleFiles = $derived(files.length > 1);
|
|
63
|
-
|
|
64
|
-
let iconMap = {
|
|
65
|
-
code: CodeIcon,
|
|
66
|
-
javascript: JavaScriptIcon,
|
|
67
|
-
typescript: TypeScriptIcon,
|
|
68
|
-
svelte: SvelteIcon,
|
|
69
|
-
css: CssIcon,
|
|
70
|
-
html: HtmlIcon
|
|
71
|
-
} as const;
|
|
72
62
|
|
|
73
|
-
|
|
63
|
+
$effect.pre(() => {
|
|
64
|
+
if (children && content && !presentation) {
|
|
65
|
+
modeState = 'mixed';
|
|
66
|
+
viewState = 'preview';
|
|
67
|
+
} else if (presentation) {
|
|
68
|
+
modeState = 'mixed';
|
|
69
|
+
viewState = 'code';
|
|
70
|
+
} else if (children && !content) {
|
|
71
|
+
modeState = 'playground';
|
|
72
|
+
viewState = 'preview';
|
|
73
|
+
} else {
|
|
74
|
+
modeState = 'code';
|
|
75
|
+
viewState = 'code';
|
|
76
|
+
}
|
|
77
|
+
});
|
|
74
78
|
|
|
75
79
|
$effect(() => {
|
|
76
80
|
if (copyState) {
|
|
@@ -87,16 +91,16 @@
|
|
|
87
91
|
|
|
88
92
|
$effect(() => {
|
|
89
93
|
const file = activeFile;
|
|
90
|
-
const theme =
|
|
94
|
+
const theme = themeState;
|
|
91
95
|
|
|
92
96
|
if (file?.content) {
|
|
93
97
|
(async () => {
|
|
94
|
-
console.log('Rendering file:', file);
|
|
95
98
|
const highlighter = await getHighlighterSingleton();
|
|
96
99
|
|
|
100
|
+
language = file.lang || 'javascript';
|
|
97
101
|
const html = highlighter.codeToHtml(file.content, {
|
|
98
102
|
theme: theme === 'light' ? 'github-light' : 'github-dark',
|
|
99
|
-
lang: file.lang ||
|
|
103
|
+
lang: file.lang || language
|
|
100
104
|
});
|
|
101
105
|
|
|
102
106
|
codeHTML = html;
|
|
@@ -106,76 +110,32 @@
|
|
|
106
110
|
</script>
|
|
107
111
|
|
|
108
112
|
<div class="repl-container">
|
|
109
|
-
<
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
113
|
+
<Toolbar
|
|
114
|
+
{title}
|
|
115
|
+
{language}
|
|
116
|
+
{presentation}
|
|
117
|
+
bind:copyState
|
|
118
|
+
bind:viewState
|
|
119
|
+
bind:themeState
|
|
120
|
+
bind:modeState
|
|
121
|
+
/>
|
|
122
|
+
|
|
123
|
+
{#if modeState !== 'code'}
|
|
124
|
+
<hr />
|
|
125
|
+
{/if}
|
|
114
126
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
class="repl-btn--icon"
|
|
119
|
-
onclick={() => (themeMode = themeMode === 'light' ? 'dark' : 'light')}
|
|
120
|
-
title="Toggle Theme"
|
|
121
|
-
>
|
|
122
|
-
{#if themeMode === 'light'}
|
|
123
|
-
<Moon class="toolbar-icon" />
|
|
124
|
-
{:else}
|
|
125
|
-
<Sun class="toolbar-icon" />
|
|
126
|
-
{/if}
|
|
127
|
-
</button>
|
|
128
|
-
{/if}
|
|
129
|
-
<button
|
|
130
|
-
class="repl-btn--icon"
|
|
131
|
-
title={viewMode === 'editor' ? 'Code' : 'Playground'}
|
|
132
|
-
onclick={() => (viewMode = viewMode === 'editor' ? 'playground' : 'editor')}
|
|
133
|
-
>
|
|
134
|
-
{#if viewMode === 'editor'}
|
|
135
|
-
<Code class="toolbar-icon" />
|
|
136
|
-
{:else}
|
|
137
|
-
<Codesandbox class="toolbar-icon" />
|
|
138
|
-
{/if}
|
|
139
|
-
</button>
|
|
140
|
-
<button
|
|
141
|
-
class="repl-btn--icon"
|
|
142
|
-
onclick={() => (copyState = true)}
|
|
143
|
-
title={copyState ? 'Copied!' : 'Copy'}
|
|
144
|
-
>
|
|
145
|
-
{#if copyState}
|
|
146
|
-
<Check class="toolbar-icon" />
|
|
147
|
-
{:else}
|
|
148
|
-
<Copy class="toolbar-icon" />
|
|
149
|
-
{/if}
|
|
150
|
-
</button>
|
|
127
|
+
{#if presentation}
|
|
128
|
+
<div class="repl-content">
|
|
129
|
+
<div>{@render children?.()}</div>
|
|
151
130
|
</div>
|
|
152
|
-
</div>
|
|
153
|
-
|
|
154
|
-
<hr />
|
|
155
131
|
|
|
156
|
-
{#if hasMultipleFiles && viewMode === 'editor'}
|
|
157
|
-
<div class="sub-toolbar">
|
|
158
|
-
{#each files as file, index (index)}
|
|
159
|
-
<button
|
|
160
|
-
class="file-tab"
|
|
161
|
-
class:active={activeFileIndex === index}
|
|
162
|
-
onclick={() => (activeFileIndex = index)}
|
|
163
|
-
title={file.name}
|
|
164
|
-
>
|
|
165
|
-
<img
|
|
166
|
-
src={iconMap[file.lang as keyof typeof iconMap] || CodeIcon}
|
|
167
|
-
alt="{file.lang} icon"
|
|
168
|
-
class="file-icon"
|
|
169
|
-
/>
|
|
170
|
-
<span>{file.name}</span>
|
|
171
|
-
</button>
|
|
172
|
-
{/each}
|
|
173
|
-
</div>
|
|
174
132
|
<hr />
|
|
175
133
|
{/if}
|
|
176
134
|
|
|
135
|
+
<Files {files} bind:activeIndex={activeFileIndex} />
|
|
136
|
+
|
|
177
137
|
<div class="repl-content">
|
|
178
|
-
{#if
|
|
138
|
+
{#if viewState === 'code'}
|
|
179
139
|
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
180
140
|
<div class="wrapper-highlight" bind:this={ref}>{@html codeHTML}</div>
|
|
181
141
|
{:else}
|
|
@@ -206,74 +166,6 @@
|
|
|
206
166
|
background-color: var(--repl-background) !important;
|
|
207
167
|
}
|
|
208
168
|
|
|
209
|
-
div.repl-toolbar {
|
|
210
|
-
display: flex;
|
|
211
|
-
align-items: center;
|
|
212
|
-
justify-content: space-between;
|
|
213
|
-
gap: calc(var(--repl-spacing) * 3);
|
|
214
|
-
padding-left: calc(5 * var(--repl-spacing));
|
|
215
|
-
padding-right: calc(var(--repl-spacing) * 2);
|
|
216
|
-
padding-block: calc(var(--repl-spacing) * 1.5);
|
|
217
|
-
border-top-left-radius: var(--repl-radius);
|
|
218
|
-
border-top-right-radius: var(--repl-radius);
|
|
219
|
-
min-height: 36px;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
div.repl-toolbar--actions {
|
|
223
|
-
display: flex;
|
|
224
|
-
align-items: center;
|
|
225
|
-
gap: calc(var(--repl-spacing) * 2);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
.sub-toolbar button {
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
div button.repl-btn--icon {
|
|
232
|
-
background: none;
|
|
233
|
-
border: none;
|
|
234
|
-
cursor: pointer;
|
|
235
|
-
display: flex;
|
|
236
|
-
align-self: center;
|
|
237
|
-
justify-content: center;
|
|
238
|
-
font-size: 0.875rem;
|
|
239
|
-
border-radius: 0.375rem;
|
|
240
|
-
transition: background-color 0.2s ease;
|
|
241
|
-
padding: 8px;
|
|
242
|
-
color: var(--repl-secondary);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
div button.repl-btn--icon:hover {
|
|
246
|
-
color: var(--repl-primary);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
div button.repl-btn--icon :global(svg) {
|
|
250
|
-
height: 20px;
|
|
251
|
-
width: 20px;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
.repl-toolbar--title {
|
|
255
|
-
display: flex;
|
|
256
|
-
align-items: center;
|
|
257
|
-
gap: calc(var(--repl-spacing) * 2);
|
|
258
|
-
font-weight: 600;
|
|
259
|
-
color: var(--repl-secondary);
|
|
260
|
-
max-width: 80%;
|
|
261
|
-
min-width: 0;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
.repl-toolbar--title span {
|
|
265
|
-
overflow: hidden;
|
|
266
|
-
text-overflow: ellipsis;
|
|
267
|
-
white-space: nowrap;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
.repl-toolbar--title .toolbar-icon {
|
|
271
|
-
width: 20px;
|
|
272
|
-
height: 20px;
|
|
273
|
-
object-fit: contain;
|
|
274
|
-
flex-shrink: 0;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
169
|
.repl-content {
|
|
278
170
|
display: flow-root;
|
|
279
171
|
margin-top: calc(var(--repl-spacing) * 0);
|
|
@@ -300,40 +192,4 @@
|
|
|
300
192
|
white-space: pre-wrap;
|
|
301
193
|
word-break: break-word;
|
|
302
194
|
}
|
|
303
|
-
|
|
304
|
-
.sub-toolbar {
|
|
305
|
-
display: flex;
|
|
306
|
-
gap: calc(var(--repl-spacing) * 2);
|
|
307
|
-
padding-left: calc(5 * var(--repl-spacing));
|
|
308
|
-
padding-right: calc(5 * var(--repl-spacing));
|
|
309
|
-
padding-block: calc(var(--repl-spacing) * 2);
|
|
310
|
-
overflow-x: auto;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
.file-tab {
|
|
314
|
-
display: flex;
|
|
315
|
-
align-items: center;
|
|
316
|
-
gap: calc(var(--repl-spacing) * 2);
|
|
317
|
-
padding: calc(var(--repl-spacing) * 2) calc(var(--repl-spacing) * 3);
|
|
318
|
-
border-radius: 0.375rem;
|
|
319
|
-
font-size: 0.875rem;
|
|
320
|
-
transition: all 0.2s ease;
|
|
321
|
-
border: 1px solid transparent;
|
|
322
|
-
white-space: nowrap;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
.file-tab:hover {
|
|
326
|
-
background-color: #f5f5f5;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
.file-tab.active {
|
|
330
|
-
background-color: #f0f0f0;
|
|
331
|
-
border-color: #d0d0d0;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
.file-icon {
|
|
335
|
-
width: 16px;
|
|
336
|
-
height: 16px;
|
|
337
|
-
object-fit: contain;
|
|
338
|
-
}
|
|
339
195
|
</style>
|
package/dist/Repl.svelte.d.ts
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
content: any;
|
|
4
|
-
children: any;
|
|
5
|
-
}, {}, "">;
|
|
1
|
+
import type { ReplProps } from './types.js';
|
|
2
|
+
declare const Repl: import("svelte").Component<ReplProps, {}, "">;
|
|
6
3
|
type Repl = ReturnType<typeof Repl>;
|
|
7
4
|
export default Repl;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ToolbarProps } from './types.js';
|
|
3
|
+
import Button from './Button.svelte';
|
|
4
|
+
import { dictionary } from './utils.js';
|
|
5
|
+
import { Copy, Check, Code, Codesandbox, Moon, Sun } from '@lucide/svelte';
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
title,
|
|
9
|
+
language,
|
|
10
|
+
presentation,
|
|
11
|
+
copyState = $bindable(),
|
|
12
|
+
viewState = $bindable(),
|
|
13
|
+
themeState = $bindable(),
|
|
14
|
+
modeState = $bindable()
|
|
15
|
+
}: ToolbarProps = $props();
|
|
16
|
+
|
|
17
|
+
let languageKey = $derived(
|
|
18
|
+
Object.entries(dictionary).find(([, values]) => values.includes(language))?.[0] || language
|
|
19
|
+
);
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<div class="lpk-repl--toolbar">
|
|
23
|
+
<div class="lpk-repl--toolbar-title">
|
|
24
|
+
{#if title}
|
|
25
|
+
<span>{title}</span>
|
|
26
|
+
{:else if language}
|
|
27
|
+
<span>{languageKey}</span>
|
|
28
|
+
{/if}
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<div class="lpk-repl--toolbar-actions">
|
|
32
|
+
{#if (modeState !== 'code' && viewState === 'preview') || presentation}
|
|
33
|
+
<Button onclick={() => (themeState = themeState === 'light' ? 'dark' : 'light')}>
|
|
34
|
+
{#if themeState === 'light'}
|
|
35
|
+
<Moon />
|
|
36
|
+
{:else}
|
|
37
|
+
<Sun />
|
|
38
|
+
{/if}
|
|
39
|
+
</Button>
|
|
40
|
+
{/if}
|
|
41
|
+
|
|
42
|
+
{#if modeState === 'mixed' && !presentation}
|
|
43
|
+
<Button onclick={() => (viewState = viewState === 'code' ? 'preview' : 'code')}>
|
|
44
|
+
{#if viewState === 'code'}
|
|
45
|
+
<Code />
|
|
46
|
+
{:else}
|
|
47
|
+
<Codesandbox />
|
|
48
|
+
{/if}
|
|
49
|
+
</Button>
|
|
50
|
+
{/if}
|
|
51
|
+
|
|
52
|
+
{#if modeState !== 'playground'}
|
|
53
|
+
<Button onclick={() => (copyState = true)}>
|
|
54
|
+
{#if copyState}
|
|
55
|
+
<Check />
|
|
56
|
+
{:else}
|
|
57
|
+
<Copy />
|
|
58
|
+
{/if}
|
|
59
|
+
</Button>
|
|
60
|
+
{/if}
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<style>
|
|
65
|
+
.lpk-repl--toolbar {
|
|
66
|
+
display: flex;
|
|
67
|
+
align-items: center;
|
|
68
|
+
justify-content: space-between;
|
|
69
|
+
gap: calc(var(--repl-spacing) * 3);
|
|
70
|
+
padding-left: calc(5 * var(--repl-spacing));
|
|
71
|
+
padding-right: calc(var(--repl-spacing) * 2);
|
|
72
|
+
padding-block: calc(var(--repl-spacing) * 1.5);
|
|
73
|
+
border-top-left-radius: var(--repl-radius);
|
|
74
|
+
border-top-right-radius: var(--repl-radius);
|
|
75
|
+
min-height: 36px;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.lpk-repl--toolbar .lpk-repl--toolbar-title {
|
|
79
|
+
display: flex;
|
|
80
|
+
align-items: center;
|
|
81
|
+
gap: calc(var(--repl-spacing) * 2);
|
|
82
|
+
font-weight: 600;
|
|
83
|
+
color: var(--repl-secondary);
|
|
84
|
+
max-width: 80%;
|
|
85
|
+
min-width: 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.lpk-repl--toolbar .lpk-repl--toolbar-actions {
|
|
89
|
+
display: flex;
|
|
90
|
+
align-items: center;
|
|
91
|
+
gap: calc(var(--repl-spacing) * 2);
|
|
92
|
+
}
|
|
93
|
+
</style>
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
export interface ReplProps {
|
|
3
|
+
title?: string;
|
|
4
|
+
content?: string | Record<string, string | Record<string, unknown>>;
|
|
5
|
+
children?: Snippet;
|
|
6
|
+
presentation?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface ToolbarProps {
|
|
9
|
+
title?: string;
|
|
10
|
+
language: string;
|
|
11
|
+
copyState?: boolean;
|
|
12
|
+
presentation?: boolean;
|
|
13
|
+
viewState?: 'code' | 'preview';
|
|
14
|
+
themeState?: 'light' | 'dark';
|
|
15
|
+
modeState?: 'code' | 'playground' | 'mixed';
|
|
16
|
+
}
|
|
17
|
+
export interface FilesProps {
|
|
18
|
+
files?: FileItem[];
|
|
19
|
+
activeIndex: number;
|
|
20
|
+
}
|
|
21
|
+
export interface FileItem {
|
|
22
|
+
name: string;
|
|
23
|
+
content: string;
|
|
24
|
+
lang?: string;
|
|
25
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/utils.d.ts
CHANGED
package/dist/utils.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import javascript from './languages/javascript.svg';
|
|
2
|
+
import typescript from './languages/typescript.svg';
|
|
3
|
+
import svelte from './languages/svelte.svg';
|
|
4
|
+
import css from './languages/css.svg';
|
|
5
|
+
import html from './languages/html.svg';
|
|
6
|
+
import shell from './languages/shell.svg';
|
|
1
7
|
export const copyToClipboard = (value) => {
|
|
2
8
|
if (navigator.clipboard && window.isSecureContext) {
|
|
3
9
|
navigator.clipboard
|
|
@@ -28,3 +34,28 @@ export const copyToClipboard = (value) => {
|
|
|
28
34
|
document.body.removeChild(zoneTexte);
|
|
29
35
|
}
|
|
30
36
|
};
|
|
37
|
+
export const dictionary = {
|
|
38
|
+
javascript: ['js', 'javascript'],
|
|
39
|
+
typescript: ['ts', 'typescript'],
|
|
40
|
+
svelte: ['svelte', 'svelte.ts', 'svelte.js'],
|
|
41
|
+
css: ['css', 'scss', 'less'],
|
|
42
|
+
html: ['html', 'htm'],
|
|
43
|
+
json: ['json'],
|
|
44
|
+
shell: ['bash', 'sh', 'shell']
|
|
45
|
+
};
|
|
46
|
+
export const dictionaryIcons = {
|
|
47
|
+
js: javascript,
|
|
48
|
+
ts: typescript,
|
|
49
|
+
'svelte.ts': svelte,
|
|
50
|
+
'svelte.js': svelte,
|
|
51
|
+
svelte: svelte,
|
|
52
|
+
shell: shell,
|
|
53
|
+
bash: shell,
|
|
54
|
+
sh: shell,
|
|
55
|
+
css: css,
|
|
56
|
+
scss: css,
|
|
57
|
+
less: css,
|
|
58
|
+
html: html,
|
|
59
|
+
htm: html,
|
|
60
|
+
json: shell
|
|
61
|
+
};
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check-icon lucide-check"><path d="M20 6 9 17l-5-5"/></svg>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-code-icon lucide-code"><path d="m16 18 6-6-6-6"/><path d="m8 6-6 6 6 6"/></svg>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-codesandbox-icon lucide-codesandbox"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="7.5 4.21 12 6.81 16.5 4.21"/><polyline points="7.5 19.79 7.5 14.6 3 12"/><polyline points="21 12 16.5 14.6 16.5 19.79"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" x2="12" y1="22.08" y2="12"/></svg>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy-icon lucide-copy"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|