Fix syntax highlighting by upgrading to Shiki v3 with all languages
- Upgrade shiki from ^1.0.0 to ^3.13.0 - Use shiki/bundle/full with all bundled languages (200+) - Change getHighlighter to createHighlighter (v3 API) - Fix CodeBlockInline to track reactive dependencies (theme, code, language) - Add markdown code block detection in tool call outputs - Render tool outputs with Markdown component when they contain code blocks - Support syntax highlighting for bash, webfetch, and default tool outputs
This commit is contained in:
@@ -21,7 +21,7 @@
|
|||||||
"electron": "38.4.0",
|
"electron": "38.4.0",
|
||||||
"lucide-solid": "^0.300.0",
|
"lucide-solid": "^0.300.0",
|
||||||
"marked": "^12.0.0",
|
"marked": "^12.0.0",
|
||||||
"shiki": "^1.0.0",
|
"shiki": "^3.13.0",
|
||||||
"solid-js": "^1.8.0"
|
"solid-js": "^1.8.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createSignal, onMount, Show, createEffect } from "solid-js"
|
import { createSignal, onMount, Show, createEffect } from "solid-js"
|
||||||
import type { Highlighter } from "shiki"
|
import type { Highlighter } from "shiki/bundle/full"
|
||||||
import { useTheme } from "../lib/theme"
|
import { useTheme } from "../lib/theme"
|
||||||
import { getSharedHighlighter, escapeHtml } from "../lib/markdown"
|
import { getSharedHighlighter, escapeHtml } from "../lib/markdown"
|
||||||
|
|
||||||
@@ -23,6 +23,9 @@ export function CodeBlockInline(props: CodeBlockInlineProps) {
|
|||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (ready()) {
|
if (ready()) {
|
||||||
|
isDark()
|
||||||
|
props.code
|
||||||
|
props.language
|
||||||
updateHighlight()
|
updateHighlight()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { createSignal, Show, For, createEffect } from "solid-js"
|
import { createSignal, Show, For, createEffect } from "solid-js"
|
||||||
import { isToolCallExpanded, toggleToolCallExpanded } from "../stores/tool-call-state"
|
import { isToolCallExpanded, toggleToolCallExpanded } from "../stores/tool-call-state"
|
||||||
import { CodeBlockInline } from "./code-block-inline"
|
import { CodeBlockInline } from "./code-block-inline"
|
||||||
|
import { Markdown } from "./markdown"
|
||||||
|
import { useTheme } from "../lib/theme"
|
||||||
|
|
||||||
interface ToolCallProps {
|
interface ToolCallProps {
|
||||||
toolCall: any
|
toolCall: any
|
||||||
@@ -96,7 +98,12 @@ function getLanguageFromPath(path: string): string | undefined {
|
|||||||
return ext ? langMap[ext] : undefined
|
return ext ? langMap[ext] : undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasMarkdownCodeBlocks(text: string): boolean {
|
||||||
|
return /```[\s\S]*?```/.test(text)
|
||||||
|
}
|
||||||
|
|
||||||
export default function ToolCall(props: ToolCallProps) {
|
export default function ToolCall(props: ToolCallProps) {
|
||||||
|
const { isDark } = useTheme()
|
||||||
const toolCallId = () => props.toolCallId || props.toolCall?.id || ""
|
const toolCallId = () => props.toolCallId || props.toolCall?.id || ""
|
||||||
const expanded = () => isToolCallExpanded(toolCallId())
|
const expanded = () => isToolCallExpanded(toolCallId())
|
||||||
|
|
||||||
@@ -345,6 +352,17 @@ export default function ToolCall(props: ToolCallProps) {
|
|||||||
|
|
||||||
if (input.command) {
|
if (input.command) {
|
||||||
const fullOutput = `$ ${input.command}${output ? "\n" + output : ""}`
|
const fullOutput = `$ ${input.command}${output ? "\n" + output : ""}`
|
||||||
|
|
||||||
|
if (output && hasMarkdownCodeBlocks(output)) {
|
||||||
|
return (
|
||||||
|
<div class="tool-call-bash">
|
||||||
|
<div class="message-text">
|
||||||
|
<Markdown content={fullOutput} isDark={isDark()} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="tool-call-bash">
|
<div class="tool-call-bash">
|
||||||
<CodeBlockInline code={fullOutput} language="bash" />
|
<CodeBlockInline code={fullOutput} language="bash" />
|
||||||
@@ -362,6 +380,15 @@ export default function ToolCall(props: ToolCallProps) {
|
|||||||
if (output) {
|
if (output) {
|
||||||
const lines = output.split("\n")
|
const lines = output.split("\n")
|
||||||
const truncated = lines.slice(0, 10).join("\n")
|
const truncated = lines.slice(0, 10).join("\n")
|
||||||
|
|
||||||
|
if (hasMarkdownCodeBlocks(truncated)) {
|
||||||
|
return (
|
||||||
|
<div class="message-text">
|
||||||
|
<Markdown content={truncated} isDark={isDark()} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return <CodeBlockInline code={truncated} language="markdown" />
|
return <CodeBlockInline code={truncated} language="markdown" />
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,6 +475,15 @@ export default function ToolCall(props: ToolCallProps) {
|
|||||||
if (output) {
|
if (output) {
|
||||||
const lines = output.split("\n")
|
const lines = output.split("\n")
|
||||||
const truncated = lines.slice(0, 10).join("\n")
|
const truncated = lines.slice(0, 10).join("\n")
|
||||||
|
|
||||||
|
if (hasMarkdownCodeBlocks(truncated)) {
|
||||||
|
return (
|
||||||
|
<div class="message-text">
|
||||||
|
<Markdown content={truncated} isDark={isDark()} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return <CodeBlockInline code={truncated} />
|
return <CodeBlockInline code={truncated} />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { marked } from "marked"
|
import { marked } from "marked"
|
||||||
import { getHighlighter, type Highlighter } from "shiki"
|
import { createHighlighter, type Highlighter, bundledLanguages } from "shiki/bundle/full"
|
||||||
|
|
||||||
let highlighter: Highlighter | null = null
|
let highlighter: Highlighter | null = null
|
||||||
let highlighterPromise: Promise<Highlighter> | null = null
|
let highlighterPromise: Promise<Highlighter> | null = null
|
||||||
@@ -15,9 +15,9 @@ async function getOrCreateHighlighter() {
|
|||||||
return highlighterPromise
|
return highlighterPromise
|
||||||
}
|
}
|
||||||
|
|
||||||
highlighterPromise = getHighlighter({
|
highlighterPromise = createHighlighter({
|
||||||
themes: ["github-light", "github-dark"],
|
themes: ["github-light", "github-dark"],
|
||||||
langs: [],
|
langs: Object.keys(bundledLanguages),
|
||||||
})
|
})
|
||||||
|
|
||||||
highlighter = await highlighterPromise
|
highlighter = await highlighterPromise
|
||||||
|
|||||||
Reference in New Issue
Block a user