type IdleCallbackHandle = number; type IdleCallbackOptions = { timeout?: number; }; type WindowWithIdleCallback = Window & { requestIdleCallback?: ( callback: () => void, options?: IdleCallbackOptions, ) => IdleCallbackHandle; }; type NavigatorWithConnection = Navigator & { connection?: { effectiveType?: string; saveData?: boolean; }; deviceMemory?: number; }; const prefetchedGroups = new Set(); const scheduledGroups = new Set(); type PrefetchTask = { key: string; load: () => Promise; }; const corePrefetchTasks: PrefetchTask[] = [ { key: 'system-user', load: () => import('#/views/_core/user/index.vue') }, { key: 'system-menu', load: () => import('#/views/_core/menu/index.vue') }, { key: 'system-role', load: () => import('#/views/_core/role/index.vue') }, ]; const aiPrefetchTasks = { agent: { key: 'ai-agent', load: () => import('#/views/ai-platform/agent/index.vue'), }, agentChat: { key: 'agent-chat', load: () => import('#/views/_core/agent-chat/index.vue'), }, aiChatPanel: { key: 'ai-chat-panel', load: () => import('#/components/ai-chat-panel/AiChatPanel.vue'), }, knowledge: { key: 'ai-knowledge', load: () => import('#/views/ai-platform/knowledge/index.vue'), }, model: { key: 'ai-model', load: () => import('#/views/ai-platform/model/index.vue'), }, workflow: { key: 'ai-workflow', load: () => import('#/views/ai-platform/workflow/index.vue'), }, workflowRuns: { key: 'ai-workflow-runs', load: () => import('#/views/ai-platform/workflow-runs/index.vue'), }, } as const; function scheduleIdle(callback: () => void) { const browserWindow = window as WindowWithIdleCallback; if (browserWindow.requestIdleCallback) { browserWindow.requestIdleCallback(callback, { timeout: 8000 }); return; } window.setTimeout(callback, 8000); } function canPrefetch() { if (typeof document !== 'undefined' && document.visibilityState !== 'visible') { return false; } const connection = (navigator as NavigatorWithConnection).connection; if (connection?.saveData) { return false; } const deviceMemory = (navigator as NavigatorWithConnection).deviceMemory; if (deviceMemory && deviceMemory < 4) { return false; } return !/(^|-)2g$/.test(connection?.effectiveType || ''); } async function runTasksSequentially(tasks: PrefetchTask[]) { for (const task of tasks) { if (!canPrefetch()) { return; } if (prefetchedGroups.has(task.key)) { continue; } prefetchedGroups.add(task.key); await task.load().catch(() => undefined); await new Promise((resolve) => window.setTimeout(resolve, 300)); } } function delayedPrefetch(groupKey: string, tasks: PrefetchTask[], delay: number) { if (!tasks.length || typeof window === 'undefined') { return; } if (scheduledGroups.has(groupKey)) { return; } scheduledGroups.add(groupKey); window.setTimeout(() => { scheduleIdle(() => { void runTasksSequentially(tasks); }); }, delay); } function isSystemRoute(path: string) { return path.startsWith('/system/') || path.startsWith('/message/'); } function isAiPlatformRoute(path: string) { return path.startsWith('/ai-platform/'); } function isAgentChatRoute(path: string) { return path.startsWith('/agent-chat/'); } function getAiRoutePrefetchTasks(path: string): PrefetchTask[] { if (path.startsWith('/ai-platform/agent')) { return [aiPrefetchTasks.workflow, aiPrefetchTasks.model]; } if (path.startsWith('/ai-platform/workflow-runs')) { return [aiPrefetchTasks.workflow]; } if (path.startsWith('/ai-platform/workflow')) { return [aiPrefetchTasks.workflowRuns, aiPrefetchTasks.agent]; } if (path.startsWith('/ai-platform/model')) { return [aiPrefetchTasks.agent, aiPrefetchTasks.workflow]; } if (path.startsWith('/ai-platform/knowledge')) { return [aiPrefetchTasks.agent]; } return [aiPrefetchTasks.agent, aiPrefetchTasks.workflow]; } function prefetchAiPlatformPages(currentPath = '') { if (typeof window === 'undefined') { return; } if (isSystemRoute(currentPath)) { delayedPrefetch(`core:${currentPath}`, corePrefetchTasks, 18_000); return; } if (isAgentChatRoute(currentPath)) { delayedPrefetch( `agent-chat:${currentPath}`, [aiPrefetchTasks.agentChat, aiPrefetchTasks.aiChatPanel], 14_000, ); return; } if (isAiPlatformRoute(currentPath)) { delayedPrefetch( `ai:${currentPath}`, getAiRoutePrefetchTasks(currentPath), 14_000, ); return; } delayedPrefetch('core:home', corePrefetchTasks, 22_000); } export { prefetchAiPlatformPages };