feat: add sidebar brand customization

The primary sidebar brand was fixed to the built-in name, subtitle, and logo. Appearance settings now let users preview and save a custom brand name, localized Chinese and English subtitles, and a validated local PNG, JPEG, or WebP logo, with default-logo and full-reset actions.

Customization remains device-local and limited to the top-left brand area; installer, update, Agent, and protocol identities remain GoodBuddy. The desktop package version is now 0.11.6.

Release note: 新增“外观 → 品牌定制”,可修改左上角品牌名称、中英文副标题和本地 Logo,并支持实时预览、恢复默认及本机持久化。
This commit is contained in:
mesalogo
2026-08-27 12:42:26 +08:00
parent 618e505b8b
commit 12463ad593
22 changed files with 1125 additions and 117 deletions
+25
View File
@@ -76,6 +76,11 @@ import { EmbeddingSettingsSection } from './EmbeddingSettingsSection'
import { useUiLocale } from './i18n/UiLocaleProvider'
import type { GlobalShortcutSettingsSnapshot } from '../../shared/shortcut'
import { findPreferredCompatibleModelProfile } from './model-profile-selection'
import { BrandingSettingsSection } from './BrandingSettingsSection'
import {
defaultBrandingPreferences,
type BrandingPreferences
} from './branding'
type ModelType = 'llm' | 'embedding' | 'rerank' | 'speech'
type AgentRuntimeType =
@@ -218,6 +223,11 @@ type SettingsPanelProps = {
onClearLocalData: () => Promise<void>
appearanceTheme?: AppearanceTheme
onAppearanceThemeChange?: (theme: AppearanceTheme) => void
brandingPreferences?: BrandingPreferences
brandingFallbackLogo?: string
onBrandingPreferencesChange?: (
preferences: BrandingPreferences
) => boolean
magicNotesEnabled?: boolean
remoteProjectsEnabled?: boolean
onMagicNotesEnabledChange?: (enabled: boolean) => void
@@ -584,6 +594,9 @@ export function SettingsPanel({
onExpertsChanged = () => {},
appearanceTheme = 'system',
onAppearanceThemeChange = () => {},
brandingPreferences = defaultBrandingPreferences,
brandingFallbackLogo,
onBrandingPreferencesChange = () => false,
magicNotesEnabled = false,
remoteProjectsEnabled = false,
onMagicNotesEnabledChange = () => {},
@@ -598,6 +611,7 @@ export function SettingsPanel({
setPreference: setUiLocalePreference
} = useUiLocale()
const [settings, setSettings] = useState<RuntimeSettings>()
const [brandingDirty, setBrandingDirty] = useState(false)
const [provider, setProvider] =
useState<RuntimeSettingsInput['provider']>(
defaultRuntimeSettings.provider
@@ -927,11 +941,13 @@ export function SettingsPanel({
runtimeDraftDirty ||
speechModelSelectionDirty ||
runtimeCustomizationDirty ||
brandingDirty ||
platformFeaturesDirty ||
documentParsingDirty ||
sshHostsDirty
const navigationWouldLoseDraft =
runtimeCustomizationDirty ||
brandingDirty ||
platformFeaturesDirty ||
documentParsingDirty ||
sshHostsDirty
@@ -939,6 +955,7 @@ export function SettingsPanel({
runtimeCustomizationDirty &&
!runtimeDraftDirty &&
!speechModelSelectionDirty &&
!brandingDirty &&
!platformFeaturesDirty &&
!documentParsingDirty &&
!sshHostsDirty
@@ -1188,6 +1205,7 @@ export function SettingsPanel({
setSpeechModelDraftId(undefined)
setPersistedSpeechModelId(undefined)
setSpeechModelSelectionDirty(false)
setBrandingDirty(false)
setPlatformFeaturesDirty(false)
setDocumentParsingDirty(false)
setSshHostsDirty(false)
@@ -1213,6 +1231,7 @@ export function SettingsPanel({
setSpeechModelSelectionDirty(false)
runtimeCustomizationRef.current?.discard()
setRuntimeCustomizationDirty(false)
setBrandingDirty(false)
setPlatformFeaturesDirty(false)
setDocumentParsingDirty(false)
setSshHostsDirty(false)
@@ -2110,6 +2129,12 @@ export function SettingsPanel({
))}
</div>
</div>
<BrandingSettingsSection
fallbackLogo={brandingFallbackLogo}
onChange={onBrandingPreferencesChange}
onDirtyChange={setBrandingDirty}
preferences={brandingPreferences}
/>
</>
)}
{activeTab === 'platform-features' && (