Spaces:
Sleeping
Sleeping
| /** | |
| * useBootEffects.ts — S-BE1: Tutti gli effetti di bootstrap dell'app | |
| * | |
| * Estratto da MainWorkspace.tsx (S-BE1): | |
| * - hydrateKnowledgeFromCloud (cloud context) | |
| * - prewarmProviders (TCP/TLS pre-connect S316) | |
| * - autoRecoverTasks (S696 task recovery) | |
| * - agentLoop chunk preload (S439 requestIdleCallback) | |
| * - agent:stream-chunk SSE listener (S422) | |
| * - useStorageAutoPrune / useSafariLifecycle / useSafariMemory | |
| * - selfDiag (autodiagnosi critica) | |
| * - VFS → previewHtml + setTab("preview") | |
| * - agente:preview_open event listener | |
| * - sessionStorage cleanup on unload | |
| * - ghListCommits fire-and-forget | |
| * | |
| * Nessun parametro, nessun valore di ritorno — puri side-effects. | |
| * Usa getState() per setPreviewHtml/setActiveTab → zero stale-closure deps. | |
| */ | |
| import { useEffect } from "react"; | |
| import { useStorageAutoPrune } from "@/hooks/useStorageQuota"; | |
| import { useSafariLifecycle } from "@/hooks/useSafariLifecycle"; | |
| import { useSafariMemory } from "@/hooks/useSafariMemory"; | |
| import { hydrateKnowledgeFromCloud } from "@/lib/contextBridge"; | |
| import { prewarmProviders } from "@/lib/providerChain"; | |
| import { ghListCommits, getRepoConfig } from "@/lib/github"; | |
| import { vfsAsync, onVfsChanged } from "@/lib/vfsDb"; | |
| import { selfDiag } from "@/lib/selfDiag"; | |
| import { type Message } from "@/lib/storage"; | |
| import { useConversationStore } from "@/store/conversationStore"; | |
| import { useWorkspaceStore } from "@/store/workspaceStore"; | |
| import { useUIStore } from "@/store/uiStore"; | |
| import { useChatStore } from "@/store/chatStore"; | |
| // ── agentLoop lazy cache (modulo-level, condiviso con gli effetti del hook) ── | |
| let _agentLoopPromise: Promise<typeof import("@/lib/agentLoop")> | null = null; | |
| const _loadAgentLoop = (): Promise<typeof import("@/lib/agentLoop")> => | |
| (_agentLoopPromise ??= import("@/lib/agentLoop")); | |
| // ───────────────────────────────────────────────────────────────────────────── | |
| export function useBootEffects(): void { | |
| // ── Cloud context hydration ─────────────────────────────────────────────── | |
| useEffect(() => { hydrateKnowledgeFromCloud(); }, []); | |
| // ── S316: prewarm TCP/TLS verso Groq — riduce cold-start ~300ms su mobile/Safari | |
| useEffect(() => { prewarmProviders(); }, []); | |
| // ── S696: auto-recovery — drena task interrotti < 10min fa (max 3 task) ── | |
| useEffect(() => { | |
| import("@/lib/taskRecovery").then(m => m.autoRecoverTasks()).catch(() => {}); | |
| }, []); // eslint-disable-line react-hooks/exhaustive-deps | |
| // ── S439: preload agentLoop chunk via requestIdleCallback ───────────────── | |
| // Garantisce chunk in memoria prima del primo invio messaggio. | |
| // Fallback setTimeout 2s se rIC non supportato. | |
| useEffect(() => { | |
| const load = () => { _loadAgentLoop().catch(() => {}); }; | |
| if (typeof requestIdleCallback !== "undefined") { | |
| const id = requestIdleCallback(load, { timeout: 3_000 }); | |
| return () => cancelIdleCallback(id); | |
| } | |
| const t = setTimeout(load, 2_000); | |
| return () => clearTimeout(t); | |
| }, []); | |
| // ── S422: SSE token listener → chatStore (live stream senza re-render completo) | |
| useEffect(() => { | |
| const handler = (e: Event) => { | |
| const ev = e as CustomEvent<{ taskId: string; token: string }>; | |
| useChatStore.getState().appendStreamChunk(ev.detail.taskId, ev.detail.token); | |
| }; | |
| window.addEventListener("agent:stream-chunk", handler); | |
| return () => window.removeEventListener("agent:stream-chunk", handler); | |
| }, []); | |
| // ── Storage / Safari lifecycle hooks ───────────────────────────────────── | |
| useStorageAutoPrune(); | |
| useSafariLifecycle(); | |
| useSafariMemory(); | |
| // ── selfDiag — inietta messaggio critico in conversazione attiva ────────── | |
| useEffect(() => { | |
| selfDiag.start(); | |
| return selfDiag.onCritical(report => { | |
| const sysMsg: Message = { | |
| id: crypto.randomUUID(), role: "assistant", | |
| content: `🔍 **Autodiagnosi:** ${report}`, | |
| createdAt: Date.now(), | |
| }; | |
| useConversationStore.getState().setMessages(prev => [...prev, sysMsg]); | |
| }); | |
| // eslint-disable-next-line react-hooks/exhaustive-deps | |
| }, []); | |
| // ── VFS → previewHtml: apre tab preview quando VFS produce un .html ─────── | |
| // Usa getState() — i setter Zustand sono stabili, zero stale-closure deps. | |
| useEffect(() => { | |
| const check = () => { | |
| vfsAsync.list() | |
| .then(files => { | |
| const html = files | |
| .filter(f => f.name.endsWith(".html") || f.path.endsWith(".html")) | |
| .sort((a, b) => b.updatedAt - a.updatedAt)[0]; | |
| if (html?.content) { | |
| useWorkspaceStore.getState().setPreviewHtml({ content: html.content, name: html.name }); | |
| useUIStore.getState().setActiveTab("preview"); | |
| } | |
| }) | |
| .catch(() => {}); | |
| }; | |
| return onVfsChanged(check); | |
| }, []); | |
| // ── agente:preview_open — preview HTML inline da tool output ───────────── | |
| useEffect(() => { | |
| const handler = (e: Event) => { | |
| const detail = (e as CustomEvent<{ path?: string; html?: string; name?: string }>).detail; | |
| if (detail?.html) { | |
| const fname = detail.name ?? detail.path?.split("/").pop() ?? "preview.html"; | |
| useWorkspaceStore.getState().setPreviewHtml({ content: detail.html, name: fname }); | |
| useUIStore.getState().setActiveTab("preview"); | |
| } | |
| }; | |
| window.addEventListener("agente:preview_open", handler); | |
| return () => window.removeEventListener("agente:preview_open", handler); | |
| }, []); | |
| // ── sessionStorage cleanup on unload ───────────────────────────────────── | |
| useEffect(() => { | |
| const cleanup = () => { try { sessionStorage.removeItem("agente_active_id"); } catch { /**/ } }; | |
| window.addEventListener("beforeunload", cleanup); | |
| return () => window.removeEventListener("beforeunload", cleanup); | |
| }, []); | |
| // ── GH recent commit (fire-and-forget) ─────────────────────────────────── | |
| useEffect(() => { | |
| const { owner, repo } = getRepoConfig(); | |
| ghListCommits(owner, repo, 1).catch(() => {}); | |
| }, []); | |
| } | |