feat: enhance iPython tool rendering with Shiki syntax highlighting

This commit is contained in:
Soulter
2026-01-15 14:01:43 +08:00
parent ac427af3c8
commit c78ac6acd7
2 changed files with 119 additions and 20 deletions

View File

@@ -28,7 +28,7 @@
"katex": "^0.16.27",
"lodash": "4.17.21",
"markdown-it": "^14.1.0",
"markstream-vue": "0.0.3-beta.7",
"markstream-vue": "^0.0.6-beta.1",
"mermaid": "^11.12.2",
"pinia": "2.1.6",
"pinyin-pro": "^3.26.0",

View File

@@ -147,24 +147,44 @@
borderTopColor: 'rgba(100, 140, 200, 0.3)',
backgroundColor: 'rgba(30, 45, 70, 0.5)'
} : {}">
<div class="tool-call-detail-row">
<span class="detail-label">ID:</span>
<code class="detail-value"
:style="isDark ? { backgroundColor: 'transparent' } : {}">{{ toolCall.id
}}</code>
</div>
<div class="tool-call-detail-row">
<span class="detail-label">Args:</span>
<pre class="detail-value detail-json"
:style="isDark ? { backgroundColor: 'transparent' } : {}">{{
JSON.stringify(toolCall.args, null, 2) }}</pre>
</div>
<div v-if="toolCall.result" class="tool-call-detail-row">
<span class="detail-label">Result:</span>
<pre class="detail-value detail-json detail-result"
:style="isDark ? { backgroundColor: 'transparent' } : {}">{{ formatToolResult(toolCall.result) }}
<!-- Special rendering for iPython tool -->
<template v-if="isIPythonTool(toolCall)">
<div class="ipython-code-container">
<!-- <div class="detail-label ipython-label">Code:</div> -->
<div v-if="shikiReady && getIPythonCode(toolCall)"
class="ipython-code-highlighted"
v-html="highlightIPythonCode(getIPythonCode(toolCall))"></div>
<pre v-else class="detail-value detail-json"
:style="isDark ? { backgroundColor: 'transparent' } : {}">{{ getIPythonCode(toolCall) || 'No code available' }}</pre>
</div>
<div v-if="toolCall.result" class="tool-call-detail-row">
<span class="detail-label">Result:</span>
<pre class="detail-value detail-json detail-result"
:style="isDark ? { backgroundColor: 'transparent' } : {}">{{ formatToolResult(toolCall.result) }}</pre>
</div>
</template>
<!-- Default rendering for other tools -->
<template v-else>
<div class="tool-call-detail-row">
<span class="detail-label">ID:</span>
<code class="detail-value"
:style="isDark ? { backgroundColor: 'transparent' } : {}">{{ toolCall.id
}}</code>
</div>
<div class="tool-call-detail-row">
<span class="detail-label">Args:</span>
<pre class="detail-value detail-json"
:style="isDark ? { backgroundColor: 'transparent' } : {}">{{
JSON.stringify(toolCall.args, null, 2) }}</pre>
</div>
<div v-if="toolCall.result" class="tool-call-detail-row">
<span class="detail-label">Result:</span>
<pre class="detail-value detail-json detail-result"
:style="isDark ? { backgroundColor: 'transparent' } : {}">{{ formatToolResult(toolCall.result) }}
</pre>
</div>
</div>
</template>
</div>
</div>
</div>
@@ -305,6 +325,7 @@ import 'markstream-vue/index.css'
import 'katex/dist/katex.min.css'
import 'highlight.js/styles/github.css';
import axios from 'axios';
import { createHighlighter } from 'shiki';
enableKatex();
enableMermaid();
@@ -363,15 +384,19 @@ export default {
imagePreview: {
show: false,
url: ''
}
},
// Shiki highlighter
shikiHighlighter: null,
shikiReady: false
};
},
mounted() {
async mounted() {
this.initCodeCopyButtons();
this.initImageClickEvents();
this.addScrollListener();
this.scrollToBottom();
this.startElapsedTimeTimer();
await this.initShiki();
},
updated() {
this.initCodeCopyButtons();
@@ -903,6 +928,52 @@ export default {
setTimeout(() => {
this.imagePreview.url = '';
}, 300);
},
// Initialize Shiki highlighter
async initShiki() {
try {
this.shikiHighlighter = await createHighlighter({
themes: ['nord', 'github-light'],
langs: ['python']
});
this.shikiReady = true;
} catch (err) {
console.error('Failed to initialize Shiki:', err);
}
},
// Check if tool is iPython executor
isIPythonTool(toolCall) {
return toolCall.name === 'astrbot_execute_ipython';
},
// Get iPython code from tool args
getIPythonCode(toolCall) {
try {
if (toolCall.args && toolCall.args.code) {
return toolCall.args.code;
}
} catch (err) {
console.error('Failed to get iPython code:', err);
}
return null;
},
// Highlight iPython code with Shiki
highlightIPythonCode(code) {
if (!this.shikiReady || !this.shikiHighlighter || !code) {
return '';
}
try {
return this.shikiHighlighter.codeToHtml(code, {
lang: 'python',
theme: this.isDark ? 'nord' : 'github-light'
});
} catch (err) {
console.error('Failed to highlight code:', err);
return `<pre><code>${code}</code></pre>`;
}
}
}
}
@@ -1623,6 +1694,34 @@ export default {
max-height: 300px;
background-color: transparent;
}
/* iPython Tool Special Styles */
.ipython-code-container {
margin-bottom: 12px;
}
.ipython-label {
margin-bottom: 8px;
}
.ipython-code-highlighted {
border-radius: 6px;
overflow-x: auto;
font-size: 13px;
line-height: 1.5;
}
.ipython-code-highlighted :deep(pre) {
margin: 0;
padding: 12px;
border-radius: 6px;
overflow-x: auto;
}
.ipython-code-highlighted :deep(code) {
font-family: 'Fira Code', 'Consolas', monospace;
font-size: 13px;
}
</style>
<style>