💄 style: mount DynamicFavicon for agent operation favicon switching (#13416)

*  feat: mount DynamicFavicon to enable favicon state switching during agent operations

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* 🐛 fix: add favicon link tags to SPA HTML templates and handle missing links in updateFaviconDOM

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Arvin Xu 2026-03-31 09:25:18 +08:00 committed by GitHub
parent c59c066330
commit e76ab1f990
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 42 additions and 0 deletions

View file

@ -3,6 +3,8 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
<link rel="shortcut icon" href="/favicon-32x32.ico" />
<link rel="manifest" href="/manifest.webmanifest" />
<!--SEO_META-->
<style>

View file

@ -3,6 +3,8 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
<link rel="shortcut icon" href="/favicon-32x32.ico" />
<!--SEO_META-->
<style>
html body {

View file

@ -0,0 +1,22 @@
'use client';
import { memo, useEffect } from 'react';
import { useFaviconSetters } from '@/layout/GlobalProvider/FaviconProvider';
import { useChatStore } from '@/store/chat';
import { operationSelectors } from '@/store/chat/slices/operation/selectors';
const DynamicFavicon = memo(() => {
const isRunning = useChatStore(operationSelectors.isAgentRuntimeRunning);
const { setFavicon } = useFaviconSetters();
useEffect(() => {
setFavicon(isRunning ? 'progress' : 'default');
}, [isRunning, setFavicon]);
return null;
});
DynamicFavicon.displayName = 'DynamicFavicon';
export default DynamicFavicon;

View file

@ -56,6 +56,20 @@ const updateFaviconDOM = (state: FaviconState, isDev: boolean) => {
'link[rel="icon"], link[rel="shortcut icon"]',
);
if (existingLinks.length === 0) {
// No favicon links found — create them
const iconLink = document.createElement('link');
iconLink.rel = 'icon';
iconLink.href = `${getFaviconPath(state, isDev)}?v=${Date.now()}`;
head.append(iconLink);
const shortcutLink = document.createElement('link');
shortcutLink.rel = 'shortcut icon';
shortcutLink.href = `${getFaviconPath(state, isDev, '32x32')}?v=${Date.now()}`;
head.append(shortcutLink);
return;
}
// Remove existing favicon links and create new ones to bust cache
existingLinks.forEach((link) => {
const oldHref = link.href;

View file

@ -10,6 +10,7 @@ import { DragUploadProvider } from '@/components/DragUploadZone/DragUploadProvid
import { isDesktop } from '@/const/version';
import AuthProvider from '@/layout/AuthProvider';
import AppTheme from '@/layout/GlobalProvider/AppTheme';
import DynamicFavicon from '@/layout/GlobalProvider/DynamicFavicon';
import { FaviconProvider } from '@/layout/GlobalProvider/FaviconProvider';
import { GroupWizardProvider } from '@/layout/GlobalProvider/GroupWizardProvider';
import ImportSettings from '@/layout/GlobalProvider/ImportSettings';
@ -54,6 +55,7 @@ const SPAGlobalProvider = memo<PropsWithChildren>(({ children }) => {
{isDesktop && <ServerVersionOutdatedAlert />}
<FaviconProvider>
<DynamicFavicon />
<GroupWizardProvider>
<DragUploadProvider>
<LazyMotion features={domMax}>