Initial commit

This commit is contained in:
fawney19
2025-12-10 20:52:44 +08:00
commit f784106826
485 changed files with 110993 additions and 0 deletions

View File

@@ -0,0 +1,400 @@
<template>
<div :class="wrapperClass">
<pre><code :class="`language-${language}`" v-html="highlightedCode"></code></pre>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import hljs from 'highlight.js/lib/core'
import bash from 'highlight.js/lib/languages/bash'
import json from 'highlight.js/lib/languages/json'
import ini from 'highlight.js/lib/languages/ini'
import javascript from 'highlight.js/lib/languages/javascript'
// 注册需要的语言
hljs.registerLanguage('bash', bash)
hljs.registerLanguage('sh', bash)
hljs.registerLanguage('json', json)
hljs.registerLanguage('toml', ini)
hljs.registerLanguage('ini', ini)
hljs.registerLanguage('javascript', javascript)
const props = defineProps<{
code: string
language: string
dense?: boolean
}>()
const wrapperClass = computed(() =>
['code-highlight', props.dense ? 'code-highlight--dense' : '']
.filter(Boolean)
.join(' ')
)
// 自定义 bash 高亮增强
function enhanceBashHighlight(html: string, code: string): string {
// 如果 highlight.js 已经识别了 token直接返回
if (html.includes('hljs-')) {
return html
}
// 手动添加高亮
const escaped = code
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;')
// 使用占位符保护URL
const urlPlaceholders: string[] = []
let result = escaped.replace(/(https?:\/\/[^\s]+)/g, (match) => {
const index = urlPlaceholders.length
urlPlaceholders.push(match)
return `__URL_PLACEHOLDER_${index}__`
})
// 高亮命令关键字
result = result
.replace(/\b(curl|npm|npx|git|bash|sh|powershell|iex|irm|wget|apt|yum|brew|pip|python|node|docker|kubectl)\b/g, '<span class="hljs-built_in">$1</span>')
.replace(/(^|\s)(-[a-zA-Z]+)/gm, '$1<span class="hljs-meta">$2</span>')
.replace(/(\|)/g, '<span class="hljs-keyword">$1</span>')
// 恢复URL并添加高亮
result = result.replace(/__URL_PLACEHOLDER_(\d+)__/g, (_, index) => {
return `<span class="hljs-string">${urlPlaceholders[parseInt(index)]}</span>`
})
return result
}
// 自定义 dotenv 高亮
function highlightDotenv(code: string): string {
const escaped = code
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;')
return escaped
// 注释行
.replace(/^(#.*)$/gm, '<span class="hljs-comment">$1</span>')
// 环境变量 KEY=VALUE
.replace(/^([A-Z_][A-Z0-9_]*)(=)(.*)$/gm, (_, key, eq, value) => {
return `<span class="hljs-attr">${key}</span>${eq}<span class="hljs-string">${value}</span>`
})
}
const highlightedCode = computed(() => {
const lang = props.language.trim().toLowerCase()
const code = props.code ?? ''
let result: string
try {
if (lang === 'bash' || lang === 'sh') {
const highlighted = hljs.highlight(code, { language: 'bash' }).value
result = enhanceBashHighlight(highlighted, code)
} else if (lang === 'dotenv' || lang === 'env') {
result = highlightDotenv(code)
} else {
const language = hljs.getLanguage(lang) ? lang : 'plaintext'
result = hljs.highlight(code, { language }).value
}
} catch (e) {
console.error('Highlight error:', e)
result = code
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
}
// Highlight placeholders that need user modification (e.g., your-api-key, latest-model-name)
result = highlightPlaceholders(result)
return result
})
// Highlight placeholder values that users need to modify
function highlightPlaceholders(html: string): string {
// Match common placeholder patterns (already HTML-escaped)
const placeholderPatterns = [
/your-api-key/gi,
/latest-model-name/gi,
/your-[a-z-]+/gi,
]
for (const pattern of placeholderPatterns) {
html = html.replace(pattern, (match) => {
// Avoid double-wrapping if already in a span
return `<span class="hljs-placeholder">${match}</span>`
})
}
return html
}
</script>
<style scoped>
.code-highlight {
width: 100%;
}
.code-highlight pre {
margin: 0;
padding: 0.9rem 1.1rem;
border-radius: 0.85rem;
border: 1px solid var(--color-border);
background-color: var(--color-code-background);
font-family: var(--font-mono, 'Cascadia Code', monospace);
font-size: 0.875rem;
line-height: 1.6;
color: var(--color-code-text);
overflow-x: auto;
transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
letter-spacing: 0.01em;
}
.code-highlight code {
font-family: inherit;
font-size: inherit;
font-weight: 400;
}
.code-highlight--dense pre {
padding: 0.6rem 0.9rem;
font-size: 0.9375rem;
border-radius: 0.75rem;
line-height: 1.5;
/* Dense mode: transparent background for embedding in panels */
background-color: transparent;
border: 1px solid var(--color-border);
}
/* Highlight.js 浅色主题 */
.code-highlight :deep(.hljs-string) {
color: #24292e;
font-weight: 450;
}
.code-highlight :deep(.hljs-attr),
.code-highlight :deep(.hljs-attribute) {
color: #c96442;
font-weight: 500;
}
.code-highlight :deep(.hljs-keyword),
.code-highlight :deep(.hljs-selector-tag),
.code-highlight :deep(.hljs-built_in) {
color: #0066cc;
font-weight: 500;
}
.code-highlight :deep(.hljs-comment) {
color: #6a737d;
font-style: italic;
opacity: 0.8;
}
.code-highlight :deep(.hljs-function),
.code-highlight :deep(.hljs-title) {
color: #6f42c1;
font-weight: 500;
}
.code-highlight :deep(.hljs-number) {
color: #005cc5;
}
.code-highlight :deep(.hljs-literal),
.code-highlight :deep(.language-json .hljs-literal),
.code-highlight :deep(.language-json .hljs-literal .hljs-keyword) {
color: #24292e;
font-weight: normal;
}
.code-highlight :deep(.hljs-variable),
.code-highlight :deep(.hljs-property) {
color: #cc785c;
}
.code-highlight :deep(.hljs-punctuation) {
color: #24292e;
opacity: 0.7;
}
/* Placeholder values that need user modification */
.code-highlight :deep(.hljs-placeholder) {
color: #d73a49;
font-weight: 500;
font-style: italic;
}
/* Bash 命令样式 - 浅色模式 */
.code-highlight :deep(.hljs-built_in),
.code-highlight :deep(.hljs-name),
.code-highlight :deep(.language-bash .hljs-built_in),
.code-highlight :deep(.language-bash .hljs-name) {
color: #d73a49;
font-weight: 500;
}
.code-highlight :deep(.hljs-meta),
.code-highlight :deep(.language-bash .hljs-meta) {
color: #d73a49;
font-weight: 500;
}
.code-highlight :deep(.hljs-params),
.code-highlight :deep(.language-bash .hljs-params) {
color: #0366d6;
font-weight: 500;
}
.code-highlight :deep(.language-bash .hljs-keyword),
.code-highlight :deep(.language-bash .hljs-literal) {
color: #0366d6;
font-weight: 500;
}
.code-highlight :deep(.language-bash) {
color: #24292e;
}
.code-highlight :deep(.language-bash .hljs-string) {
color: #24292e;
font-weight: 400;
}
.code-highlight :deep(pre code.language-bash) {
color: #24292e;
}
.code-highlight :deep(pre code.language-bash .hljs-subst) {
color: #24292e;
}
/* Highlight.js 深色主题 */
.dark .code-highlight :deep(.hljs-string),
body[theme-mode='dark'] .code-highlight :deep(.hljs-string) {
color: #f1ead8;
font-weight: 450;
}
.dark .code-highlight :deep(.hljs-attr),
.dark .code-highlight :deep(.hljs-attribute),
body[theme-mode='dark'] .code-highlight :deep(.hljs-attr),
body[theme-mode='dark'] .code-highlight :deep(.hljs-attribute) {
color: #d4a27f;
font-weight: 500;
}
.dark .code-highlight :deep(.hljs-keyword),
.dark .code-highlight :deep(.hljs-selector-tag),
.dark .code-highlight :deep(.hljs-built_in),
body[theme-mode='dark'] .code-highlight :deep(.hljs-keyword),
body[theme-mode='dark'] .code-highlight :deep(.hljs-selector-tag),
body[theme-mode='dark'] .code-highlight :deep(.hljs-built_in) {
color: #569cd6;
font-weight: 500;
}
.dark .code-highlight :deep(.hljs-comment),
body[theme-mode='dark'] .code-highlight :deep(.hljs-comment) {
color: #6a9955;
font-style: italic;
opacity: 0.85;
}
.dark .code-highlight :deep(.hljs-function),
.dark .code-highlight :deep(.hljs-title),
body[theme-mode='dark'] .code-highlight :deep(.hljs-function),
body[theme-mode='dark'] .code-highlight :deep(.hljs-title) {
color: #dcdcaa;
font-weight: 500;
}
.dark .code-highlight :deep(.hljs-number),
body[theme-mode='dark'] .code-highlight :deep(.hljs-number) {
color: #b5cea8;
}
.dark .code-highlight :deep(.hljs-literal),
.dark .code-highlight :deep(.language-json .hljs-literal),
.dark .code-highlight :deep(.language-json .hljs-literal .hljs-keyword),
body[theme-mode='dark'] .code-highlight :deep(.hljs-literal),
body[theme-mode='dark'] .code-highlight :deep(.language-json .hljs-literal),
body[theme-mode='dark'] .code-highlight :deep(.language-json .hljs-literal .hljs-keyword) {
color: #e1e4e8;
font-weight: normal;
}
.dark .code-highlight :deep(.hljs-variable),
.dark .code-highlight :deep(.hljs-property),
body[theme-mode='dark'] .code-highlight :deep(.hljs-variable),
body[theme-mode='dark'] .code-highlight :deep(.hljs-property) {
color: #9cdcfe;
}
.dark .code-highlight :deep(.hljs-punctuation),
body[theme-mode='dark'] .code-highlight :deep(.hljs-punctuation) {
color: #d4d4d4;
opacity: 0.7;
}
/* Placeholder values - dark mode */
.dark .code-highlight :deep(.hljs-placeholder),
body[theme-mode='dark'] .code-highlight :deep(.hljs-placeholder) {
color: #f97583;
font-weight: 500;
font-style: italic;
}
/* Bash 深色主题 */
.dark .code-highlight :deep(.hljs-built_in),
.dark .code-highlight :deep(.hljs-name),
.dark .code-highlight :deep(.language-bash .hljs-built_in),
.dark .code-highlight :deep(.language-bash .hljs-name),
body[theme-mode='dark'] .code-highlight :deep(.hljs-built_in),
body[theme-mode='dark'] .code-highlight :deep(.hljs-name),
body[theme-mode='dark'] .code-highlight :deep(.language-bash .hljs-built_in),
body[theme-mode='dark'] .code-highlight :deep(.language-bash .hljs-name) {
color: #e67764;
font-weight: 500;
}
.dark .code-highlight :deep(.hljs-meta),
.dark .code-highlight :deep(.language-bash .hljs-meta),
body[theme-mode='dark'] .code-highlight :deep(.hljs-meta),
body[theme-mode='dark'] .code-highlight :deep(.language-bash .hljs-meta) {
color: #e67764;
}
.dark .code-highlight :deep(.hljs-params),
.dark .code-highlight :deep(.language-bash .hljs-params),
body[theme-mode='dark'] .code-highlight :deep(.hljs-params),
body[theme-mode='dark'] .code-highlight :deep(.language-bash .hljs-params) {
color: #6fa9e6;
font-weight: 500;
}
.dark .code-highlight :deep(.language-bash .hljs-keyword),
.dark .code-highlight :deep(.language-bash .hljs-literal),
body[theme-mode='dark'] .code-highlight :deep(.language-bash .hljs-keyword),
body[theme-mode='dark'] .code-highlight :deep(.language-bash .hljs-literal) {
color: #6fa9e6;
font-weight: 500;
}
.dark .code-highlight :deep(.language-bash),
body[theme-mode='dark'] .code-highlight :deep(.language-bash) {
color: #e1e4e8;
}
.dark .code-highlight :deep(.language-bash .hljs-string),
body[theme-mode='dark'] .code-highlight :deep(.language-bash .hljs-string) {
color: #e1e4e8;
font-weight: 400;
}
</style>