97 lines
2.3 KiB
Svelte
97 lines
2.3 KiB
Svelte
<script lang="ts">
|
|
import hljs from 'highlight.js';
|
|
import { browser } from '$app/environment';
|
|
import { mode } from 'mode-watcher';
|
|
|
|
import githubDarkCss from 'highlight.js/styles/github-dark.css?inline';
|
|
import githubLightCss from 'highlight.js/styles/github.css?inline';
|
|
|
|
interface Props {
|
|
code: string;
|
|
language?: string;
|
|
class?: string;
|
|
maxHeight?: string;
|
|
maxWidth?: string;
|
|
}
|
|
|
|
let {
|
|
code,
|
|
language = 'text',
|
|
class: className = '',
|
|
maxHeight = '60vh',
|
|
maxWidth = ''
|
|
}: Props = $props();
|
|
|
|
let highlightedHtml = $state('');
|
|
|
|
function loadHighlightTheme(isDark: boolean) {
|
|
if (!browser) return;
|
|
|
|
const existingThemes = document.querySelectorAll('style[data-highlight-theme-preview]');
|
|
existingThemes.forEach((style) => style.remove());
|
|
|
|
const style = document.createElement('style');
|
|
style.setAttribute('data-highlight-theme-preview', 'true');
|
|
style.textContent = isDark ? githubDarkCss : githubLightCss;
|
|
|
|
document.head.appendChild(style);
|
|
}
|
|
|
|
$effect(() => {
|
|
const currentMode = mode.current;
|
|
const isDark = currentMode === 'dark';
|
|
|
|
loadHighlightTheme(isDark);
|
|
});
|
|
|
|
$effect(() => {
|
|
if (!code) {
|
|
highlightedHtml = '';
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Check if the language is supported
|
|
const lang = language.toLowerCase();
|
|
const isSupported = hljs.getLanguage(lang);
|
|
|
|
if (isSupported) {
|
|
const result = hljs.highlight(code, { language: lang });
|
|
highlightedHtml = result.value;
|
|
} else {
|
|
// Try auto-detection or fallback to plain text
|
|
const result = hljs.highlightAuto(code);
|
|
highlightedHtml = result.value;
|
|
}
|
|
} catch {
|
|
// Fallback to escaped plain text
|
|
highlightedHtml = code.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<div
|
|
class="code-preview-wrapper overflow-auto rounded-lg border border-border bg-muted {className}"
|
|
style="max-height: {maxHeight};"
|
|
>
|
|
<pre class="m-0 overflow-x-auto p-4 max-w-[{maxWidth}]"><code class="hljs text-sm leading-relaxed"
|
|
>{@html highlightedHtml}</code
|
|
></pre>
|
|
</div>
|
|
|
|
<style>
|
|
.code-preview-wrapper {
|
|
font-family:
|
|
ui-monospace, SFMono-Regular, 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas,
|
|
'Liberation Mono', Menlo, monospace;
|
|
}
|
|
|
|
.code-preview-wrapper pre {
|
|
background: transparent;
|
|
}
|
|
|
|
.code-preview-wrapper code {
|
|
background: transparent;
|
|
}
|
|
</style>
|