104 lines
2.6 KiB
Vue
104 lines
2.6 KiB
Vue
<script setup lang="ts">
|
|
import { computed, ref, watch } from "vue";
|
|
import { useNuxtApp } from "#app";
|
|
|
|
const props = defineProps<{
|
|
code: string;
|
|
lang?: string;
|
|
}>();
|
|
|
|
const { $shiki } = useNuxtApp();
|
|
const html = ref("");
|
|
|
|
const targetLang = computed(() => props.lang || "json");
|
|
|
|
const lineNumberWidth = computed(() => {
|
|
const lines = props.code ? props.code.split("\n").length : 1;
|
|
return lines.toString().length;
|
|
});
|
|
|
|
watch(
|
|
[() => props.code, targetLang],
|
|
async ([newCode, newLang]) => {
|
|
if (!newCode) {
|
|
html.value = "";
|
|
return;
|
|
}
|
|
|
|
let resolvedLang = newLang;
|
|
try {
|
|
if ($shiki?.getLanguage) {
|
|
await $shiki.getLanguage(resolvedLang);
|
|
}
|
|
} catch {
|
|
resolvedLang = "text";
|
|
}
|
|
|
|
try {
|
|
if ($shiki?.codeToHtml) {
|
|
html.value = await $shiki.codeToHtml(newCode, {
|
|
lang: resolvedLang,
|
|
themes: {
|
|
dark: "vitesse-dark",
|
|
light: "vitesse-light",
|
|
},
|
|
});
|
|
} else {
|
|
html.value = "";
|
|
}
|
|
} catch {
|
|
html.value = "";
|
|
}
|
|
},
|
|
{ immediate: true },
|
|
);
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
class="tool-code-container overflow-auto max-h-80 scrollbar-thin text-xs font-mono"
|
|
:style="`--line-number-width: ${lineNumberWidth}ch;`"
|
|
>
|
|
<div v-if="html" class="tool-code-highlight" v-html="html"></div>
|
|
<pre v-else class="tool-code-fallback m-0 p-3.5 text-[var(--text-secondary)] whitespace-pre-wrap break-words leading-relaxed">{{ code }}</pre>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.tool-code-container {
|
|
background-color: var(--shiki-dark-bg, #121019);
|
|
}
|
|
|
|
:deep(.tool-code-highlight > pre) {
|
|
margin: 0;
|
|
padding: 0.875rem 1rem;
|
|
font-size: 0.75rem;
|
|
line-height: 1.625;
|
|
scrollbar-width: thin;
|
|
overflow-x: auto;
|
|
counter-reset: lines;
|
|
min-height: 100%;
|
|
box-sizing: border-box;
|
|
font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
}
|
|
|
|
:deep(.tool-code-highlight > pre > code .line::before) {
|
|
counter-increment: lines;
|
|
content: counter(lines);
|
|
width: var(--line-number-width, 2ch);
|
|
margin-right: 1.25rem;
|
|
display: inline-block;
|
|
text-align: right;
|
|
color: rgba(255, 255, 255, 0.25);
|
|
user-select: none;
|
|
}
|
|
|
|
:deep(html.light .tool-code-container) {
|
|
background-color: #ffffff;
|
|
}
|
|
|
|
:deep(html.light .tool-code-highlight > pre > code .line::before) {
|
|
color: rgba(0, 0, 0, 0.25);
|
|
}
|
|
</style>
|