feat: custom tool renderers
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user