perf: speed up desktop startup and unify theme-aware branding (#596)

* fix: unify startup logo and loading theme behavior

Show desktop window immediately with animated splash logo
Align splash/logo colors with selected app theme and default themes
Keep auth loading state on full-screen logo without size jump
Make project SVG icons follow active app theme
Use the active theme foreground color for project icons discovered from favicons
Apply server-side SVG color overrides for currentColor icons via icon request params
Keep non-SVG project icons unchanged while preserving existing fallback behavior

* perf: speed up desktop startup and unify loading logo visuals

Desktop startup now shows UI sooner while backend boot continues in background
Startup host probing uses a faster local path with safer remote fallback retries
OpenChamber logo cube highlights now match splash screens consistently

* fix: keep macOS traffic-light buttons in the correct position on load

Stop native window title updates on macOS during app initialization
Prevent title bar relayout that reset custom traffic-light positioning

* fix: recover missing providers and agents after fast startup

Retries provider/agent loading when connection is up but core config is still empty
Prevents cold-start state where models/agents appear only after manual project switch
Keeps startup responsive with throttled background recovery in app bootstrap
This commit is contained in:
Bohdan Triapitsyn
2026-03-04 19:16:45 +02:00
committed by GitHub
parent 037a70f114
commit d1a41000ca
22 changed files with 763 additions and 190 deletions
+4
View File
@@ -41,6 +41,10 @@ export type DesktopSettings = {
themeVariant?: 'light' | 'dark';
lightThemeId?: string;
darkThemeId?: string;
splashBgLight?: string;
splashFgLight?: string;
splashBgDark?: string;
splashFgDark?: string;
lastDirectory?: string;
homeDirectory?: string;
// Optional absolute path to `opencode` binary.
+15 -2
View File
@@ -22,6 +22,8 @@ import {
} from '@remixicon/react';
import type { ProjectEntry } from '@/lib/api/types';
type ThemeVariant = 'light' | 'dark';
export const PROJECT_ICONS: Array<{ key: string; Icon: RemixiconComponentType; label: string }> = [
{ key: 'code', Icon: RiCodeBoxLine, label: 'Code' },
{ key: 'terminal', Icon: RiTerminalBoxLine, label: 'Terminal' },
@@ -64,10 +66,21 @@ export const PROJECT_COLOR_MAP: Record<string, string> = Object.fromEntries(
PROJECT_COLORS.map((c) => [c.key, c.cssVar])
);
export const getProjectIconImageUrl = (project: Pick<ProjectEntry, 'id' | 'iconImage'>): string | null => {
export const getProjectIconImageUrl = (
project: Pick<ProjectEntry, 'id' | 'iconImage'>,
options?: { themeVariant?: ThemeVariant; iconColor?: string },
): string | null => {
if (!project.iconImage || typeof project.iconImage.updatedAt !== 'number' || project.iconImage.updatedAt <= 0) {
return null;
}
return `/api/projects/${encodeURIComponent(project.id)}/icon?v=${project.iconImage.updatedAt}`;
const params = new URLSearchParams({ v: String(project.iconImage.updatedAt) });
if (typeof options?.iconColor === 'string' && options.iconColor.trim()) {
params.set('iconColor', options.iconColor.trim());
}
if (options?.themeVariant === 'light' || options?.themeVariant === 'dark') {
params.set('theme', options.themeVariant);
}
return `/api/projects/${encodeURIComponent(project.id)}/icon?${params.toString()}`;
};