From f0c9fda209c15e718025b7da77c696dca90f89e3 Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Thu, 10 Sep 2026 22:56:50 +0800 Subject: [PATCH] feat(chatui): reuse Shiki for workspace code previews --- .../components/chat/WorkspaceFilesPanel.vue | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/dashboard/src/components/chat/WorkspaceFilesPanel.vue b/dashboard/src/components/chat/WorkspaceFilesPanel.vue index baeafb1286..c133e9aece 100644 --- a/dashboard/src/components/chat/WorkspaceFilesPanel.vue +++ b/dashboard/src/components/chat/WorkspaceFilesPanel.vue @@ -152,6 +152,11 @@
{{ fileError }}
+
import "@/components/chat/chatPanelTransition.css";
import { computed, ref, watch } from "vue";
+import { useTheme } from "vuetify";
import {
ChevronDown,
ChevronRight,
@@ -244,6 +255,7 @@ const emit = defineEmits<{
}>();
const { tm } = useModuleI18n("features/chat");
+const theme = useTheme();
const rootEntries = ref([]);
const loadedProjectId = ref("");
const rootLoading = ref(false);
@@ -251,6 +263,7 @@ const treeError = ref("");
const filterQuery = ref("");
const selectedFilePath = ref("");
const fileContent = ref("");
+const highlightedContent = ref("");
const fileLoading = ref(false);
const fileError = ref("");
const fileDownloading = ref(false);
@@ -297,6 +310,39 @@ watch(
{ immediate: true },
);
+watch(
+ [fileContent, selectedFilePath, () => theme.global.current.value.dark],
+ async ([content, path, dark], _, onCleanup) => {
+ let cancelled = false;
+ onCleanup(() => {
+ cancelled = true;
+ });
+ highlightedContent.value = "";
+ if (!content || !path) return;
+
+ try {
+ const { getShikiHighlighter, renderShikiCode } = await import(
+ "@/utils/shiki"
+ );
+ const highlighter = await getShikiHighlighter();
+ if (cancelled) return;
+
+ const name = path.split("/").pop()?.toLowerCase() || "";
+ const language = name.startsWith("dockerfile.")
+ ? "dockerfile"
+ : name.split(".").pop();
+ highlightedContent.value = renderShikiCode(
+ highlighter,
+ content,
+ language,
+ dark ? "dark" : "light",
+ );
+ } catch (error) {
+ if (!cancelled) console.warn("Failed to highlight workspace file", error);
+ }
+ },
+);
+
function close() {
emit("update:modelValue", false);
}
@@ -682,6 +728,15 @@ function formatSize(size: number) {
white-space: pre;
}
+.workspace-preview-content :deep(pre),
+.workspace-preview-content :deep(code) {
+ margin: 0;
+ padding: 0;
+ font: inherit;
+ tab-size: inherit;
+ background: transparent !important;
+}
+
.workspace-dialog-preview {
height: min(78vh, 760px);
background: var(--chat-page-bg, rgb(var(--v-theme-surface)));