|
| 1 | +<script module> |
| 2 | + import { createHighlighter } from 'shiki'; |
| 3 | +
|
| 4 | + const highlighter = createHighlighter({ |
| 5 | + themes: ['github-light-default', 'github-dark-default'], |
| 6 | + langs: ['svelte', 'javascript', 'ts', 'typescript', 'json', 'sh'], |
| 7 | + }); |
| 8 | +</script> |
| 9 | + |
1 | 10 | <script lang="ts"> |
2 | | - import Prism from 'prismjs'; |
3 | | - import 'prism-svelte'; |
4 | 11 | import { CopyButton } from 'svelte-ux'; |
5 | 12 | import { cls } from '@layerstack/tailwind'; |
| 13 | + import type { HTMLAttributes } from 'svelte/elements'; |
| 14 | +
|
| 15 | + interface Props { |
| 16 | + source?: string | null; |
| 17 | + language?: string; |
| 18 | + classes?: { root?: string; pre?: string; code?: string }; |
| 19 | + } |
6 | 20 |
|
7 | | - const { |
| 21 | + let { |
8 | 22 | source = null, |
9 | 23 | language = 'svelte', |
10 | | - highlightedSource = source |
11 | | - ? Prism.highlight(source, Prism.languages[language] ?? Prism.languages.text, language) |
12 | | - : '', |
13 | 24 | classes = {}, |
14 | 25 | class: className, |
15 | | - }: { |
16 | | - source: string | null; |
17 | | - language?: string; |
18 | | - highlightedSource?: string; |
19 | | - classes?: { |
20 | | - root?: string; |
21 | | - pre?: string; |
22 | | - code?: string; |
23 | | - }; |
24 | | - class?: string; |
25 | | - } = $props(); |
| 26 | + }: Props & HTMLAttributes<HTMLDivElement> = $props(); |
26 | 27 | </script> |
27 | 28 |
|
28 | | -<div class={cls('Code', 'rounded-sm', classes.root, className)}> |
| 29 | +<div |
| 30 | + class={cls( |
| 31 | + 'Code', |
| 32 | + 'relative bg-surface-200 dark:bg-surface-300 p-4 overflow-auto not-prose', |
| 33 | + classes.root, |
| 34 | + className |
| 35 | + )} |
| 36 | +> |
29 | 37 | {#if source} |
30 | | - <div class="relative"> |
31 | | - <pre |
32 | | - class={cls('language-{language} rounded-sm', classes.pre)} |
33 | | - style="margin: 0; white-space: normal;"> |
34 | | - <code class={cls('language-{language}', classes.code)}>{@html highlightedSource}</code> |
35 | | - </pre> |
36 | | - |
37 | | - <div class="absolute top-0 right-0 p-2 z-10"> |
38 | | - <CopyButton |
39 | | - value={source ?? ''} |
40 | | - class="text-white/70 hover:bg-surface-100/20 py-1 backdrop-blur-md" |
41 | | - size="sm" |
42 | | - /> |
43 | | - </div> |
| 38 | + <pre class={cls('whitespace-normal overflow-auto', classes.pre)}> |
| 39 | + <code class={cls('text-xs', classes.code)}> |
| 40 | + {#await highlighter} |
| 41 | + <div>Loading...</div> |
| 42 | + {:then h} |
| 43 | + {@html h.codeToHtml(source, { |
| 44 | + lang: language, |
| 45 | + themes: { |
| 46 | + light: 'github-light-default', |
| 47 | + dark: 'github-dark-default', |
| 48 | + }, |
| 49 | + })} |
| 50 | + {:catch error} |
| 51 | + <div class="text-red-500">Error loading code highlighting: {error.message}</div> |
| 52 | + {/await} |
| 53 | + |
| 54 | + </code> |
| 55 | + </pre> |
| 56 | + |
| 57 | + <div class="absolute top-0 right-0 p-2 z-10"> |
| 58 | + <CopyButton |
| 59 | + value={source ?? ''} |
| 60 | + class="text-surface-content/70 hover:bg-surface-100/20 py-1 backdrop-blur-md" |
| 61 | + size="sm" |
| 62 | + /> |
44 | 63 | </div> |
45 | 64 | {/if} |
46 | 65 | </div> |
| 66 | + |
| 67 | +<style> |
| 68 | + :global(.shiki) { |
| 69 | + background-color: transparent !important; |
| 70 | + } |
| 71 | +
|
| 72 | + :global(html.dark .shiki), |
| 73 | + :global(html.dark .shiki span) { |
| 74 | + color: var(--shiki-dark) !important; |
| 75 | + /* background-color: var(--shiki-dark-bg) !important; */ |
| 76 | + font-style: var(--shiki-dark-font-style) !important; |
| 77 | + font-weight: var(--shiki-dark-font-weight) !important; |
| 78 | + text-decoration: var(--shiki-dark-text-decoration) !important; |
| 79 | + } |
| 80 | +</style> |
0 commit comments