chore: 精简代码
This commit is contained in:
parent
f2866df2ea
commit
95e771cb61
|
|
@ -132,11 +132,7 @@ function scrollToBottom(smooth = true) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => props.messages.length, () => {
|
watch([() => props.messages.length, () => props.streamingContent], () => {
|
||||||
scrollToBottom()
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(() => props.streamingContent, () => {
|
|
||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -298,52 +294,8 @@ watch(() => props.conversation?.id, () => {
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-bubble {
|
/* .message-bubble, .avatar, .message-body now in global.css */
|
||||||
display: flex;
|
|
||||||
gap: 12px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-bubble .message-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
max-width: 85%;
|
|
||||||
min-width: 200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-bubble.user .message-container {
|
|
||||||
align-items: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-bubble.assistant .message-container {
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-bubble .avatar {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
border-radius: 8px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 700;
|
|
||||||
letter-spacing: -0.3px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
background: var(--avatar-gradient);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-bubble .message-body {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
padding: 16px;
|
|
||||||
border: 1px solid var(--border-light);
|
|
||||||
border-radius: 12px;
|
|
||||||
background: var(--bg-primary);
|
|
||||||
transition: background 0.2s, border-color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
<template>
|
||||||
|
<button class="btn-close" @click="$emit('click')">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||||
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
defineEmits(['click'])
|
||||||
|
</script>
|
||||||
|
|
@ -90,35 +90,7 @@ function copyContent() {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.message-bubble {
|
/* .message-bubble, .avatar, .message-body now in global.css */
|
||||||
display: flex;
|
|
||||||
gap: 12px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-bubble.user {
|
|
||||||
flex-direction: row-reverse;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-width: 200px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-bubble.user .message-container {
|
|
||||||
align-items: flex-end;
|
|
||||||
width: fit-content;
|
|
||||||
max-width: 85%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-bubble.assistant .message-container {
|
|
||||||
align-items: flex-start;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.attachments-list {
|
.attachments-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -154,48 +126,6 @@ function copyContent() {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-bubble.assistant .message-body {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
border-radius: 8px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 700;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user .avatar {
|
|
||||||
background: linear-gradient(135deg, #2563eb, #3b82f6);
|
|
||||||
color: white;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 700;
|
|
||||||
letter-spacing: -0.3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.assistant .avatar {
|
|
||||||
background: var(--avatar-gradient);
|
|
||||||
color: white;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 700;
|
|
||||||
letter-spacing: -0.3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-body {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
padding: 16px;
|
|
||||||
border: 1px solid var(--border-light);
|
|
||||||
border-radius: 12px;
|
|
||||||
background: var(--bg-primary);
|
|
||||||
transition: background 0.2s, border-color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-footer {
|
.message-footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, watch } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
import { renderMarkdown } from '../utils/markdown'
|
import { renderMarkdown } from '../utils/markdown'
|
||||||
|
import { formatJson, truncate } from '../utils/format'
|
||||||
import { useCodeEnhancement } from '../composables/useCodeEnhancement'
|
import { useCodeEnhancement } from '../composables/useCodeEnhancement'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|
@ -97,18 +98,6 @@ function toggleItem(key) {
|
||||||
expandedKeys.value[key] = !expandedKeys.value[key]
|
expandedKeys.value[key] = !expandedKeys.value[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatJson(value) {
|
|
||||||
if (value == null) return ''
|
|
||||||
const str = typeof value === 'string' ? value : JSON.stringify(value)
|
|
||||||
try { return JSON.stringify(JSON.parse(str), null, 2) } catch { return str }
|
|
||||||
}
|
|
||||||
|
|
||||||
function truncate(text, max = 60) {
|
|
||||||
if (!text) return ''
|
|
||||||
const str = text.replace(/\s+/g, ' ').trim()
|
|
||||||
return str.length > max ? str.slice(0, max) + '…' : str
|
|
||||||
}
|
|
||||||
|
|
||||||
function getResultSummary(result) {
|
function getResultSummary(result) {
|
||||||
try {
|
try {
|
||||||
const parsed = typeof result === 'string' ? JSON.parse(result) : result
|
const parsed = typeof result === 'string' ? JSON.parse(result) : result
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@
|
||||||
<div class="modal">
|
<div class="modal">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h3>创建项目</h3>
|
<h3>创建项目</h3>
|
||||||
<button class="btn-close" @click="showCreateModal = false">×</button>
|
<CloseButton @click="showCreateModal = false" />
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
@ -79,7 +79,7 @@
|
||||||
<div class="modal">
|
<div class="modal">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h3>上传文件夹</h3>
|
<h3>上传文件夹</h3>
|
||||||
<button class="btn-close" @click="closeUploadModal">×</button>
|
<CloseButton @click="closeUploadModal" />
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
@ -124,7 +124,7 @@
|
||||||
<div class="modal">
|
<div class="modal">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h3>确认删除</h3>
|
<h3>确认删除</h3>
|
||||||
<button class="btn-close" @click="showDeleteModal = false">×</button>
|
<CloseButton @click="showDeleteModal = false" />
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>确定要删除项目 <strong>{{ projectToDelete?.name }}</strong> 吗?</p>
|
<p>确定要删除项目 <strong>{{ projectToDelete?.name }}</strong> 吗?</p>
|
||||||
|
|
@ -144,9 +144,10 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { projectApi } from '../api'
|
import { projectApi } from '../api'
|
||||||
|
import CloseButton from './CloseButton.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
currentProject: Object,
|
currentProject: { type: Object, default: null },
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['select', 'created', 'deleted'])
|
const emit = defineEmits(['select', 'created', 'deleted'])
|
||||||
|
|
@ -316,23 +317,7 @@ defineExpose({
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-icon {
|
/* .btn-icon is defined in global.css */
|
||||||
padding: 6px;
|
|
||||||
border: none;
|
|
||||||
background: transparent;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-icon:hover {
|
|
||||||
background: var(--bg-hover);
|
|
||||||
color: var(--accent-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-icon.danger:hover {
|
.btn-icon.danger:hover {
|
||||||
background: var(--danger-bg);
|
background: var(--danger-bg);
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,7 @@
|
||||||
{{ t.label }}
|
{{ t.label }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn-close" @click="$emit('close')">
|
<CloseButton @click="$emit('close')" />
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
||||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
||||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -132,6 +127,7 @@
|
||||||
import { reactive, ref, watch, onMounted } from 'vue'
|
import { reactive, ref, watch, onMounted } from 'vue'
|
||||||
import { modelApi, conversationApi } from '../api'
|
import { modelApi, conversationApi } from '../api'
|
||||||
import { useTheme } from '../composables/useTheme'
|
import { useTheme } from '../composables/useTheme'
|
||||||
|
import CloseButton from './CloseButton.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: { type: Boolean, default: false },
|
visible: { type: Boolean, default: false },
|
||||||
|
|
@ -251,34 +247,7 @@ onMounted(loadModels)
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.period-tabs {
|
/* tab styles now in global.css */
|
||||||
display: flex;
|
|
||||||
gap: 2px;
|
|
||||||
background: var(--bg-input);
|
|
||||||
padding: 3px;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab {
|
|
||||||
padding: 4px 12px;
|
|
||||||
border: none;
|
|
||||||
background: none;
|
|
||||||
color: var(--text-tertiary);
|
|
||||||
font-size: 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 6px;
|
|
||||||
transition: all 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab:hover {
|
|
||||||
color: var(--text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab.active {
|
|
||||||
background: var(--accent-primary);
|
|
||||||
color: white;
|
|
||||||
box-shadow: 0 1px 3px rgba(37, 99, 235, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-body {
|
.settings-body {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,8 @@ function selectProject(project) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onProjectCreated(project) {
|
function onProjectCreated(project) {
|
||||||
// Auto-select newly created project
|
// Auto-select newly created project and refresh list
|
||||||
|
projectManagerRef.value?.loadProjects()
|
||||||
emit('selectProject', project)
|
emit('selectProject', project)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,7 +133,6 @@ function onScroll(e) {
|
||||||
.sidebar {
|
.sidebar {
|
||||||
width: 20%;
|
width: 20%;
|
||||||
min-width: 220px;
|
min-width: 220px;
|
||||||
max-width: 320px;
|
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background: color-mix(in srgb, var(--bg-primary) 75%, transparent);
|
background: color-mix(in srgb, var(--bg-primary) 75%, transparent);
|
||||||
backdrop-filter: blur(40px);
|
backdrop-filter: blur(40px);
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,7 @@
|
||||||
{{ p.label }}
|
{{ p.label }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn-close" @click="$emit('close')">
|
<CloseButton @click="$emit('close')" />
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
||||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
||||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -224,6 +219,7 @@ import { ref, computed, onMounted } from 'vue'
|
||||||
import { statsApi } from '../api'
|
import { statsApi } from '../api'
|
||||||
import { useTheme } from '../composables/useTheme'
|
import { useTheme } from '../composables/useTheme'
|
||||||
import { formatNumber } from '../utils/format'
|
import { formatNumber } from '../utils/format'
|
||||||
|
import CloseButton from './CloseButton.vue'
|
||||||
|
|
||||||
const emit = defineEmits(['close'])
|
const emit = defineEmits(['close'])
|
||||||
|
|
||||||
|
|
@ -377,34 +373,7 @@ onMounted(loadStats)
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.period-tabs {
|
/* tab styles now in global.css */
|
||||||
display: flex;
|
|
||||||
gap: 2px;
|
|
||||||
background: var(--bg-input);
|
|
||||||
padding: 3px;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab {
|
|
||||||
padding: 4px 12px;
|
|
||||||
border: none;
|
|
||||||
background: none;
|
|
||||||
color: var(--text-tertiary);
|
|
||||||
font-size: 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 6px;
|
|
||||||
transition: all 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab:hover {
|
|
||||||
color: var(--text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab.active {
|
|
||||||
background: var(--accent-primary);
|
|
||||||
color: white;
|
|
||||||
box-shadow: 0 1px 3px rgba(37, 99, 235, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats-loading {
|
.stats-loading {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
|
|
@ -509,6 +509,107 @@ input[type="range"]::-moz-range-thumb {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============ Tab Navigation ============ */
|
||||||
|
.period-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 2px;
|
||||||
|
background: var(--bg-input);
|
||||||
|
padding: 3px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
padding: 4px 12px;
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab:hover {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab.active {
|
||||||
|
background: var(--accent-primary);
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 1px 3px rgba(37, 99, 235, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============ Message Bubble Shared ============ */
|
||||||
|
.message-bubble {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-bubble.user {
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 200px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-bubble.user .message-container {
|
||||||
|
align-items: flex-end;
|
||||||
|
width: fit-content;
|
||||||
|
max-width: 85%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-bubble.assistant .message-container {
|
||||||
|
align-items: flex-start;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-bubble.assistant .message-body {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: -0.3px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user .avatar {
|
||||||
|
background: linear-gradient(135deg, #2563eb, #3b82f6);
|
||||||
|
color: white;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant .avatar {
|
||||||
|
background: var(--avatar-gradient);
|
||||||
|
color: white;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-body {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 16px;
|
||||||
|
border: 1px solid var(--border-light);
|
||||||
|
border-radius: 12px;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
transition: background 0.2s, border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
/* ============ Form ============ */
|
/* ============ Form ============ */
|
||||||
.form-group {
|
.form-group {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
|
|
||||||
|
|
@ -23,3 +23,21 @@ export function formatNumber(num) {
|
||||||
if (num >= 1000) return (num / 1000).toFixed(1) + 'K'
|
if (num >= 1000) return (num / 1000).toFixed(1) + 'K'
|
||||||
return num.toString()
|
return num.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a value as pretty-printed JSON string.
|
||||||
|
*/
|
||||||
|
export function formatJson(value) {
|
||||||
|
if (value == null) return ''
|
||||||
|
const str = typeof value === 'string' ? value : JSON.stringify(value)
|
||||||
|
try { return JSON.stringify(JSON.parse(str), null, 2) } catch { return str }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Truncate text to max characters with ellipsis.
|
||||||
|
*/
|
||||||
|
export function truncate(text, max = 60) {
|
||||||
|
if (!text) return ''
|
||||||
|
const str = text.replace(/\s+/g, ' ').trim()
|
||||||
|
return str.length > max ? str.slice(0, max) + '\u2026' : str
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,9 @@ export function renderMarkdown(text) {
|
||||||
return marked.parse(text)
|
return marked.parse(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const COPY_SVG = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>'
|
||||||
|
const CHECK_SVG = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"></polyline></svg>'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 后处理 HTML:为所有代码块包裹 .code-block 容器,
|
* 后处理 HTML:为所有代码块包裹 .code-block 容器,
|
||||||
* 添加语言标签和复制按钮。在组件 onMounted / updated 中调用。
|
* 添加语言标签和复制按钮。在组件 onMounted / updated 中调用。
|
||||||
|
|
@ -101,9 +104,6 @@ export function enhanceCodeBlocks(container) {
|
||||||
langSpan.className = 'code-lang'
|
langSpan.className = 'code-lang'
|
||||||
langSpan.textContent = lang
|
langSpan.textContent = lang
|
||||||
|
|
||||||
const COPY_SVG = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>'
|
|
||||||
const CHECK_SVG = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"></polyline></svg>'
|
|
||||||
|
|
||||||
const copyBtn = document.createElement('button')
|
const copyBtn = document.createElement('button')
|
||||||
copyBtn.className = 'code-copy-btn'
|
copyBtn.className = 'code-copy-btn'
|
||||||
copyBtn.title = '复制'
|
copyBtn.title = '复制'
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue