fix: improve startup readiness performance
This commit is contained in:
@@ -49,14 +49,15 @@ export const AgentMentionAutocomplete = React.forwardRef<AgentMentionAutocomplet
|
||||
const itemRefs = React.useRef<(HTMLDivElement | null)[]>([]);
|
||||
const ignoreTabClickRef = React.useRef(false);
|
||||
const getVisibleAgents = useConfigStore((state) => state.getVisibleAgents);
|
||||
const configAgentsCount = useConfigStore((state) => state.agents.length);
|
||||
const agentsWithMetadata = useAgentsStore((state) => state.agents);
|
||||
const loadAgents = useAgentsStore((state) => state.loadAgents);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (agentsWithMetadata.length === 0) {
|
||||
if (agentsWithMetadata.length === 0 && configAgentsCount === 0) {
|
||||
void loadAgents();
|
||||
}
|
||||
}, [loadAgents, agentsWithMetadata.length]);
|
||||
}, [loadAgents, agentsWithMetadata.length, configAgentsCount]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const visibleAgents = getVisibleAgents();
|
||||
|
||||
@@ -38,6 +38,7 @@ import { formatEffortLabel, getCycledPrimaryAgentName, type MobileControlsPanel
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
import { useOpenCodeReadiness } from '@/hooks/useOpenCodeReadiness';
|
||||
import { eventMatchesShortcut, getEffectiveShortcutCombo, normalizeCombo } from '@/lib/shortcuts';
|
||||
import { markStartupTrace } from '@/lib/startupTrace';
|
||||
|
||||
type IconComponent = IconName;
|
||||
|
||||
@@ -312,6 +313,19 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
|
||||
// Use visible agents (excludes hidden internal agents)
|
||||
const agents = getVisibleAgents();
|
||||
const primaryAgents = React.useMemo(() => agents.filter((agent) => agent.mode === 'primary'), [agents]);
|
||||
const tracedReadyRef = React.useRef(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (tracedReadyRef.current || !isReady) return;
|
||||
tracedReadyRef.current = true;
|
||||
markStartupTrace('ModelControls:ready', {
|
||||
providers: providers.length,
|
||||
agents: agents.length,
|
||||
currentProviderId,
|
||||
currentModelId,
|
||||
currentAgentName,
|
||||
});
|
||||
}, [agents.length, currentAgentName, currentModelId, currentProviderId, isReady, providers.length]);
|
||||
|
||||
const currentSessionId = useSessionUIStore((s) => s.currentSessionId);
|
||||
const getDirectoryForSession = useSessionUIStore((s) => s.getDirectoryForSession);
|
||||
|
||||
@@ -320,10 +320,10 @@ export const VSCodeLayout: React.FC = () => {
|
||||
// Keep trying to fetch core datasets on cold starts.
|
||||
if (configStore.isConnected) {
|
||||
if (configStore.providers.length === 0) {
|
||||
await configStore.loadProviders();
|
||||
await configStore.loadProviders({ source: 'vscodeLayout:bootstrap' });
|
||||
}
|
||||
if (configStore.agents.length === 0) {
|
||||
await configStore.loadAgents();
|
||||
await configStore.loadAgents({ source: 'vscodeLayout:bootstrap' });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,8 +50,8 @@ export const AgentSelector: React.FC<AgentSelectorProps> = ({
|
||||
|
||||
// Load agents on mount
|
||||
React.useEffect(() => {
|
||||
loadAgents();
|
||||
}, [loadAgents]);
|
||||
if (agents.length === 0) void loadAgents();
|
||||
}, [agents.length, loadAgents]);
|
||||
|
||||
// Ensure we always have a valid selection (defaults to current default agent, then first selectable agent).
|
||||
React.useEffect(() => {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { cn } from '@/lib/utils';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
import { runtimeFetch } from '@/lib/runtime-fetch';
|
||||
|
||||
const GITHUB_URL = 'https://github.com/btriapitsyn/openchamber';
|
||||
|
||||
@@ -17,6 +18,7 @@ export const AboutSettings: React.FC = () => {
|
||||
const { t } = useI18n();
|
||||
const [updateDialogOpen, setUpdateDialogOpen] = React.useState(false);
|
||||
const [showChecking, setShowChecking] = React.useState(false);
|
||||
const [openCodeVersion, setOpenCodeVersion] = React.useState<string | null>(null);
|
||||
const updateStore = useUpdateStore(useShallow((s) => ({
|
||||
info: s.info,
|
||||
checking: s.checking,
|
||||
@@ -34,6 +36,33 @@ export const AboutSettings: React.FC = () => {
|
||||
|
||||
const currentVersion = updateStore.info?.currentVersion || 'unknown';
|
||||
|
||||
React.useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
||||
const loadOpenCodeVersion = async () => {
|
||||
try {
|
||||
const response = await runtimeFetch('/api/opencode/version', {
|
||||
method: 'GET',
|
||||
headers: { Accept: 'application/json' },
|
||||
});
|
||||
if (!response.ok) return;
|
||||
const data = await response.json().catch(() => null) as { version?: unknown } | null;
|
||||
const version = typeof data?.version === 'string' && data.version.trim().length > 0
|
||||
? data.version.trim()
|
||||
: null;
|
||||
if (!cancelled) setOpenCodeVersion(version);
|
||||
} catch {
|
||||
if (!cancelled) setOpenCodeVersion(null);
|
||||
}
|
||||
};
|
||||
|
||||
void loadOpenCodeVersion();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Track if we initiated a check to show toast on completion
|
||||
const didInitiateCheck = React.useRef(false);
|
||||
|
||||
@@ -91,6 +120,11 @@ export const AboutSettings: React.FC = () => {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="typography-meta text-muted-foreground">{t('settings.openchamber.about.field.openCodeVersion')}</span>
|
||||
<span className="typography-meta text-muted-foreground font-mono">{openCodeVersion || t('settings.openchamber.about.state.unknown')}</span>
|
||||
</div>
|
||||
|
||||
{updateStore.error && (
|
||||
<p className="typography-micro text-[var(--status-error)] truncate">{updateStore.error}</p>
|
||||
)}
|
||||
@@ -159,6 +193,10 @@ export const AboutSettings: React.FC = () => {
|
||||
<span className="typography-ui-label text-foreground">{t('settings.openchamber.about.field.version')}</span>
|
||||
<span className="typography-meta text-muted-foreground font-mono">{currentVersion}</span>
|
||||
</div>
|
||||
<div className="flex min-w-0 flex-col">
|
||||
<span className="typography-ui-label text-foreground">{t('settings.openchamber.about.field.openCodeVersion')}</span>
|
||||
<span className="typography-meta text-muted-foreground font-mono">{openCodeVersion || t('settings.openchamber.about.state.unknown')}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
{updateStore.checking && (
|
||||
|
||||
@@ -53,7 +53,7 @@ export function ForkSessionDialog(props: ForkSessionDialogProps) {
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!open) return;
|
||||
void loadProviders({ directory: projectDirectory });
|
||||
void loadProviders({ directory: projectDirectory, source: 'forkSessionDialog' });
|
||||
void loadConfigAgents({ directory: projectDirectory });
|
||||
void loadAgentsStoreAgents();
|
||||
}, [open, loadProviders, loadConfigAgents, loadAgentsStoreAgents, projectDirectory]);
|
||||
|
||||
@@ -662,8 +662,8 @@ export function ScheduledTaskEditorDialog(props: {
|
||||
if (!open) {
|
||||
return;
|
||||
}
|
||||
void loadProviders();
|
||||
void loadAgents();
|
||||
void loadProviders({ source: 'scheduledTaskEditor' });
|
||||
void loadAgents({ source: 'scheduledTaskEditor' });
|
||||
}, [open, loadProviders, loadAgents]);
|
||||
|
||||
React.useEffect(() => {
|
||||
|
||||
@@ -66,7 +66,7 @@ export function TodoSendDialog(props: TodoSendDialogProps) {
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!open) return;
|
||||
void loadProviders({ directory: projectDirectory });
|
||||
void loadProviders({ directory: projectDirectory, source: 'todoSendDialog' });
|
||||
void loadConfigAgents({ directory: projectDirectory });
|
||||
void loadAgentsStoreAgents();
|
||||
}, [open, loadProviders, loadConfigAgents, loadAgentsStoreAgents, projectDirectory]);
|
||||
|
||||
Reference in New Issue
Block a user