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 }}
+
             {{ 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)));