feat(desktop): unify canary with stable app name/icon, add channel tag in About (#12881)

- Use same app name (LobeHub) and icon as stable for canary builds
- Add build channel tag in Settings > About for non-stable channels (Canary, Nightly, Beta)
- Add getBuildChannel IPC to expose build-time channel for display

Made-with: Cursor
This commit is contained in:
Innei
2026-03-10 16:41:56 +08:00
committed by GitHub
parent 5e468cd850
commit c087134953
8 changed files with 37 additions and 6 deletions

View File

@@ -90,8 +90,8 @@ const protocolScheme = getProtocolScheme();
// Determine icon file based on version type
const getIconFileName = () => {
if (isStable) return 'Icon';
// nightly, canary share pre-release icon
if (isStable || isCanary) return 'Icon';
// nightly uses pre-release icon
return 'Icon-nightly';
};

View File

@@ -49,6 +49,16 @@ export default class UpdaterCtr extends ControllerModule {
return this.app.storeManager.get('updateChannel') ?? 'stable';
}
/**
* Get the build-time channel (stable, nightly, canary, beta).
* Used for display in About page to distinguish pre-release builds.
*/
@IpcMethod()
async getBuildChannel(): Promise<string> {
const { BUILD_CHANNEL } = await import('@/modules/updater/configs');
return BUILD_CHANNEL;
}
@IpcMethod()
async setUpdateChannel(channel: UpdateChannel): Promise<void> {
const validChannels = new Set(['stable', 'nightly', 'canary']);

View File

@@ -6,6 +6,8 @@ import { getDesktopEnv } from '@/env';
// Build-time default channel, can be overridden at runtime via store
const rawChannel = getDesktopEnv().UPDATE_CHANNEL || 'stable';
const VALID_CHANNELS = new Set<UpdateChannel>(['stable', 'nightly', 'canary']);
/** Raw build channel for display (stable, nightly, canary, beta) */
export const BUILD_CHANNEL: string = rawChannel;
export const UPDATE_CHANNEL: UpdateChannel = VALID_CHANNELS.has(rawChannel as UpdateChannel)
? (rawChannel as UpdateChannel)
: rawChannel === 'beta'

View File

@@ -700,6 +700,7 @@
"tab.all": "全部",
"tab.apikey": "API Key 管理",
"tab.beta": "Beta",
"tab.beta.updateChannel.beta": "Beta",
"tab.beta.updateChannel.canary": "Canary",
"tab.beta.updateChannel.canaryDesc": "每次 PR 合并触发构建,一天可能多次。最不稳定。",
"tab.beta.updateChannel.desc": "默认接收稳定版更新通知。Nightly 和 Canary 通道将接收预发布版本,可能不适合生产使用。",

View File

@@ -96,10 +96,9 @@ function updatePackageJson() {
break;
}
case 'canary': {
packageJson.productName = 'LobeHub-Canary';
packageJson.productName = 'LobeHub';
packageJson.name = 'lobehub-desktop-canary';
console.log('🐤 Setting as Canary version.');
updateAppIcon('nightly');
console.log('🐤 Setting as Canary version (same app name and icon as stable).');
break;
}
}

View File

@@ -802,6 +802,7 @@ When I am ___, I need ___
'tab.advanced': 'Advanced',
'tab.addAgentSkill': 'Add Agent Skill',
'tab.beta': 'Beta',
'tab.beta.updateChannel.beta': 'Beta',
'tab.beta.updateChannel.canary': 'Canary',
'tab.beta.updateChannel.canaryDesc':
'Triggered on every PR merge, multiple builds per day. Most unstable.',

View File

@@ -29,7 +29,7 @@ const Version = memo<{ mobile?: boolean }>(({ mobile }) => {
s.serverVersion,
s.useCheckServerVersion,
]);
const { t } = useTranslation('common');
const { t } = useTranslation(['common', 'setting']);
useCheckServerVersion();
@@ -37,12 +37,18 @@ const Version = memo<{ mobile?: boolean }>(({ mobile }) => {
const isDesktop = useMemo(() => !!getElectronIpc(), []);
const [updaterState, setUpdaterState] = useState<UpdaterState>({ stage: 'idle' });
const [buildChannel, setBuildChannel] = useState<string | null>(null);
useEffect(() => {
if (!isDesktop) return;
autoUpdateService.getUpdaterState().then(setUpdaterState);
}, [isDesktop]);
useEffect(() => {
if (!isDesktop) return;
autoUpdateService.getBuildChannel().then(setBuildChannel);
}, [isDesktop]);
useWatchBroadcast('updaterStateChanged', (state: UpdaterState) => {
setUpdaterState(state);
});
@@ -128,6 +134,14 @@ const Version = memo<{ mobile?: boolean }>(({ mobile }) => {
<div style={{ fontSize: 18, fontWeight: 'bolder' }}>{BRANDING_NAME}</div>
<Flexbox gap={6} horizontal={!mobile}>
<Tag>v{CURRENT_VERSION}</Tag>
{buildChannel && buildChannel !== 'stable' && (
<Tag color={'gold'}>
{t(`setting:tab.beta.updateChannel.${buildChannel}`, {
defaultValue: buildChannel.charAt(0).toUpperCase() + buildChannel.slice(1),
})}
</Tag>
)}
{showServerVersion && (
<Tag>{t('upgradeVersion.serverVersion', { version: `v${serverVersion}` })}</Tag>
)}

View File

@@ -23,6 +23,10 @@ class AutoUpdateService {
return ensureElectronIpc().autoUpdate.getUpdateChannel();
};
getBuildChannel = async (): Promise<string> => {
return ensureElectronIpc().autoUpdate.getBuildChannel();
};
setUpdateChannel = async (channel: UpdateChannel): Promise<void> => {
return ensureElectronIpc().autoUpdate.setUpdateChannel(channel);
};