From 4e1750a7926a21f24c15ef3426b9e68f8f0dc37e Mon Sep 17 00:00:00 2001 From: taoxu Date: Sun, 17 May 2026 23:13:20 +0800 Subject: [PATCH] feat: configurable thinking animation with file upload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new display setting that lets users upload a custom GIF or video to replace the default thinking animation shown while the AI is responding. Changes: - config.ts: add thinking_video_url to DisplayConfig interface - MessageList.vue: use custom URL when set (img for GIF, video for mp4/webm), fall back to bundled thinking-light/dark.mp4 based on theme - DisplaySettings.vue: replace URL text input with upload button; uploads file to server and auto-saves the resulting URL - upload.ts (controller): add handleThinkingAnimationUpload — validates ext (gif/mp4/webm/png/jpg/webp), saves to uploadDir with random filename, returns /user-uploads/ URL - upload.ts (routes): register POST /api/hermes/upload/thinking-animation - index.ts: serve /user-uploads/* from config.uploadDir (persists across builds) - i18n en/zh: add thinkingVideoUrl, thinkingVideoUrlHint, thinkingVideoUrlUpload, thinkingVideoUrlReplace, thinkingVideoUrlClear keys --- packages/client/src/api/hermes/config.ts | 1 + .../components/hermes/chat/MessageList.vue | 22 +++++- .../hermes/settings/DisplaySettings.vue | 67 ++++++++++++++++++- packages/client/src/i18n/locales/en.ts | 5 ++ packages/client/src/i18n/locales/zh.ts | 5 ++ packages/server/src/controllers/upload.ts | 48 +++++++++++++ packages/server/src/index.ts | 10 +++ packages/server/src/routes/upload.ts | 1 + 8 files changed, 157 insertions(+), 2 deletions(-) diff --git a/packages/client/src/api/hermes/config.ts b/packages/client/src/api/hermes/config.ts index 82be95050..9928839e0 100644 --- a/packages/client/src/api/hermes/config.ts +++ b/packages/client/src/api/hermes/config.ts @@ -11,6 +11,7 @@ export interface DisplayConfig { inline_diffs?: boolean show_cost?: boolean skin?: string + thinking_video_url?: string } export interface AgentConfig { diff --git a/packages/client/src/components/hermes/chat/MessageList.vue b/packages/client/src/components/hermes/chat/MessageList.vue index 8442e8676..846a7b376 100644 --- a/packages/client/src/components/hermes/chat/MessageList.vue +++ b/packages/client/src/components/hermes/chat/MessageList.vue @@ -6,12 +6,25 @@ import { useChatStore } from "@/stores/hermes/chat"; import thinkingVideoLight from "@/assets/thinking-light.mp4"; import thinkingVideoDark from "@/assets/thinking-dark.mp4"; import { useTheme } from "@/composables/useTheme"; +import { useSettingsStore } from "@/stores/hermes/settings"; import { useToolTraceVisibility } from "@/composables/useToolTraceVisibility"; const chatStore = useChatStore(); const { t } = useI18n(); const { isDark } = useTheme(); +const settingsStore = useSettingsStore(); const { toolTraceVisible } = useToolTraceVisibility(); + +const thinkingIsGif = computed(() => { + const url = settingsStore.display?.thinking_video_url?.trim() + return !!url && /\.gif(\?.*)?$/i.test(url) +}) + +const thinkingVideoSrc = computed(() => { + const custom = settingsStore.display.thinking_video_url; + if (custom && custom.trim()) return custom.trim(); + return isDark.value ? thinkingVideoDark : thinkingVideoLight; +}); const listRef = ref(); function formatTokens(n: number): string { @@ -172,8 +185,15 @@ watch(currentToolCalls, () => { />
+ thinking