Spaces:
Sleeping
Sleeping
File size: 3,930 Bytes
d3ab1f5 1302517 5c52644 1302517 8cc084b c4ae44d 5c52644 1302517 f60772d 5c52644 f60772d 8cc084b c4ae44d 8cc084b c4ae44d adb8afe c4ae44d d3ab1f5 f60772d 2961a5b 8cc084b c4ae44d 5c52644 c4ae44d 204c6ba 8b92de2 204c6ba c4ae44d 2d9278d c4ae44d 2961a5b d3ab1f5 5c52644 fe3e1db 5c52644 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | <script lang="ts">
import Button from '$lib/components/ui/button/button.svelte';
import { Check, Copy, Eye, X } from '@lucide/svelte';
import { HighlightAuto } from 'svelte-highlight';
import { mode } from 'mode-watcher';
import githubDarkUrl from 'svelte-highlight/styles/github-dark.css?url';
import githubUrl from 'svelte-highlight/styles/github.css?url';
import Switch from '$lib/components/ui/switch/switch.svelte';
import * as Dialog from '$lib/components/ui/dialog/index.js';
let {
lang,
text,
className = 'my-3 relative rounded-lg border border-border/60 bg-muted/50'
}: { lang?: string; text: string; className?: string } = $props();
let copiedCode = $state(false);
let open = $state.raw(false);
async function copy(text: string) {
await navigator.clipboard.writeText(text);
copiedCode = true;
setTimeout(() => (copiedCode = false), 2000);
}
function hasStrictHtml5Doctype(input: string): boolean {
if (!input) return false;
const withoutBOM = input.replace(/^\uFEFF/, '');
const trimmed = withoutBOM.trimStart();
// Strict HTML5 doctype: <!doctype html> with optional whitespace before >
return /^<!doctype\s+html\s*>/i.test(trimmed);
}
function isSvgDocument(input: string): boolean {
const trimmed = input.trimStart();
return /^(?:<\?xml[^>]*>\s*)?(?:<!doctype\s+svg[^>]*>\s*)?<svg[\s>]/i.test(trimmed);
}
$effect(() => {
const themeUrl = mode.current === 'dark' ? githubDarkUrl : githubUrl;
let link = document.getElementById('highlight-theme') as HTMLLinkElement | null;
if (!link) {
link = document.createElement('link');
link.id = 'highlight-theme';
link.rel = 'stylesheet';
document.head.appendChild(link);
}
link.href = themeUrl;
});
let canShowPreview = $derived(
hasStrictHtml5Doctype(text) || isSvgDocument(text) || lang == 'svg' || lang == 'xml'
);
// svelte-ignore state_referenced_locally
let showPreview = $state.raw(canShowPreview);
</script>
<div class="overflow-hidden {className}">
{#if lang}
<div
class="flex items-center justify-between border-b border-border/60 bg-muted px-3 py-1.5 dark:bg-accent/30"
>
<span class="font-mono text-xs text-muted-foreground">
{#if showPreview}
Live Preview
{:else}
{lang}
{/if}
</span>
{#if canShowPreview}
<div class="flex items-center gap-1.5">
{#if showPreview}
<Button variant="outline" size="2xs" onclick={() => (open = true)}>
<Eye class="size-3.5" />
Open
</Button>
{/if}
<p class="font-mono text-[10px] text-muted-foreground">
Show {showPreview ? 'Preview' : 'Code'}
</p>
<Switch bind:checked={showPreview} />
</div>
{/if}
</div>
{/if}
{#if showPreview}
<div class="group relative">
<iframe
srcDoc={text}
class="h-[500px] w-full"
title="Preview"
sandbox="allow-scripts allow-popups"
referrerpolicy="no-referrer"
></iframe>
</div>
{:else}
<div class="group relative">
<HighlightAuto code={text} class="font-mono text-xs! leading-relaxed" />
<Button
variant="outline"
class="absolute top-2 right-2 opacity-0 group-hover:opacity-100"
size="icon-xs"
onclick={() => copy(text)}
>
{#if copiedCode}
<Check class="size-3.5 text-green-500" />
{:else}
<Copy class="size-3.5" />
{/if}
</Button>
</div>
{/if}
</div>
<Dialog.Root bind:open>
<Dialog.Content
class="max-w-[90dvw]! gap-0! space-y-0! border-none! p-0!"
showCloseButton={false}
>
<Dialog.Description class="relative mt-0 pt-0!">
<Button
variant="outline"
class="absolute top-2 right-2"
size="icon-xs"
onclick={() => (open = false)}
>
<X class="size-3.5" />
</Button>
<iframe
srcDoc={text}
class="h-[90dvh] w-full"
title="Preview"
sandbox="allow-scripts allow-popups"
referrerpolicy="no-referrer"
></iframe>
</Dialog.Description>
</Dialog.Content>
</Dialog.Root>
|