import type { ReactNode } from "react"; import { useEffect, useRef } from "react"; import { App } from "antd"; import { createModelChannel, useConfigStore } from "@/stores/use-config-store"; import { navigateToSettings } from "@/lib/settings-navigation"; import { useLocalDreaminaModelBootstrap } from "@/stores/use-local-dreamina-model-store"; import { useLocalRuntimeBootstrap } from "@/stores/use-local-runtime-store"; import { initializeClientDiagnostics, setDiagnosticUserScope } from "@/services/diagnostics/client-diagnostics"; import { useUserStore } from "@/stores/use-user-store"; export function ClientRootInit({ children }: { children: ReactNode }) { const config = useConfigStore((state) => state.config); const userId = useUserStore((state) => state.user?.id || ""); const localRuntimeConfigured = config.channels.some((channel) => channel.transport === "local-runtime" && channel.enabled !== false); useLocalRuntimeBootstrap(localRuntimeConfigured); useLocalDreaminaModelBootstrap(); const { message } = App.useApp(); const handledConfigParams = useRef(false); const updateConfig = useConfigStore((state) => state.updateConfig); useEffect(() => { initializeClientDiagnostics(); }, []); useEffect(() => { setDiagnosticUserScope(userId); }, [userId]); useEffect(() => { const interactiveSelector = 'button, [role="button"], a, [class*="card"], [class*="Card"]'; const blurPointerFocus = (event: PointerEvent) => { if (event.pointerType === "mouse" && event.button !== 0) return; const target = event.target instanceof Element ? event.target.closest(interactiveSelector) : null; if (!target || target.hasAttribute("disabled") || target.getAttribute("aria-disabled") === "true") return; // 浏览器可能把鼠标点击误判为 :focus-visible;下一帧只清掉这次指针点击产生的焦点。 window.requestAnimationFrame(() => { if (document.activeElement === target) target.blur(); }); }; document.addEventListener("pointerdown", blurPointerFocus, true); return () => document.removeEventListener("pointerdown", blurPointerFocus, true); }, []); useEffect(() => { if (handledConfigParams.current) return; const searchParams = new URLSearchParams(window.location.search); const baseUrl = searchParams.get("baseUrl") || searchParams.get("baseurl"); const ignoredApiKey = searchParams.has("apiKey") || searchParams.has("apikey"); if (!baseUrl && !ignoredApiKey) return; handledConfigParams.current = true; searchParams.delete("baseUrl"); searchParams.delete("baseurl"); searchParams.delete("apiKey"); searchParams.delete("apikey"); window.history.replaceState(null, "", `${window.location.pathname}${searchParams.size ? `?${searchParams}` : ""}${window.location.hash}`); const firstChannel = config.channels[0]; updateConfig( "channels", firstChannel ? config.channels.map((channel, index) => index === 0 ? { ...channel, ...(baseUrl ? { baseUrl } : {}), } : channel, ) : [createModelChannel({ id: "default", name: "默认渠道", baseUrl: baseUrl || undefined })], ); if (baseUrl) updateConfig("baseUrl", baseUrl); navigateToSettings({ section: "channels" }); if (ignoredApiKey) message.warning("出于安全考虑,链接中的 API Key 已忽略,请在配置中手动填写"); else message.success("已导入本地直连地址"); }, [config.channels, message, updateConfig]); return <>{children}; }