109 lines
3.9 KiB
TypeScript
109 lines
3.9 KiB
TypeScript
import { SettingsLayoutClient } from "@/components/settings/settings-layout-client";
|
|
import { StateHydrator } from "@/components/state-hydrator";
|
|
import {
|
|
fetchUserSettings,
|
|
listAgentDiscovery,
|
|
listAgents,
|
|
listAvailableAgents,
|
|
listExecutors,
|
|
listWorkspaces,
|
|
} from "@/lib/api";
|
|
import {
|
|
ACTIVE_WORKSPACE_COOKIE,
|
|
mapWorkspaceItem,
|
|
resolveSettingsActiveWorkspaceId,
|
|
} from "@/lib/routing/route-bootstrap";
|
|
import { mapUserSettingsResponse } from "@/lib/ssr/user-settings";
|
|
import { readCookies } from "@/lib/server/cookies";
|
|
import { toAgentProfileOption } from "@/lib/state/slices/settings/types";
|
|
|
|
export default function SettingsLayout({ children }: { children: React.ReactNode }) {
|
|
return <SettingsLayoutServer>{children}</SettingsLayoutServer>;
|
|
}
|
|
|
|
async function SettingsLayoutServer({ children }: { children: React.ReactNode }) {
|
|
let initialState = {};
|
|
try {
|
|
// Fetch discovery + available agents alongside the DB-backed list so a
|
|
// hard refresh of /settings/agents/[name] (where no profile exists yet)
|
|
// can still render the agent from the discovered set.
|
|
const [workspaces, executors, agents, discovery, available, userSettingsResponse, cookieStore] =
|
|
await Promise.all([
|
|
listWorkspaces({ cache: "no-store" }),
|
|
listExecutors({ cache: "no-store" }),
|
|
listAgents({ cache: "no-store" }),
|
|
listAgentDiscovery({ cache: "no-store" }),
|
|
listAvailableAgents({ cache: "no-store" }),
|
|
fetchUserSettings({ cache: "no-store" }).catch(() => null),
|
|
readCookies().catch(() => null),
|
|
]);
|
|
// Hydrate userSettings into the ROOT store so app-global, override-driven
|
|
// shortcuts (TOGGLE_SIDEBAR, Quick Chat) work on settings routes too. The
|
|
// settings/general page mounts its own nested store for editing; that store
|
|
// is invisible to the root-mounted GlobalCommands/useAppShortcuts.
|
|
const mappedUserSettings = mapUserSettingsResponse(userSettingsResponse);
|
|
const workspaceItems = workspaces.workspaces.map(mapWorkspaceItem);
|
|
const activeWorkspaceId = resolveSettingsActiveWorkspaceId(
|
|
workspaceItems,
|
|
// `readCookies()` is client-only in this path; during SSR this is empty.
|
|
// Settings active workspace selection is completed on first client render by
|
|
// spa-routes via `readActiveWorkspaceCookie()`.
|
|
cookieStore?.get(ACTIVE_WORKSPACE_COOKIE)?.value ?? null,
|
|
userSettingsResponse?.settings?.workspace_id ?? null,
|
|
);
|
|
initialState = {
|
|
workspaces: {
|
|
items: workspaceItems,
|
|
activeId: activeWorkspaceId,
|
|
},
|
|
executors: {
|
|
items: executors.executors,
|
|
},
|
|
agentProfiles: {
|
|
items: agents.agents.flatMap((agent) =>
|
|
agent.profiles.map((profile) => toAgentProfileOption(agent, profile)),
|
|
),
|
|
},
|
|
settingsAgents: {
|
|
items: agents.agents,
|
|
},
|
|
agentDiscovery: {
|
|
items: discovery.agents,
|
|
loading: false,
|
|
loaded: true,
|
|
},
|
|
availableAgents: {
|
|
items: available.agents,
|
|
tools: available.tools ?? [],
|
|
loading: false,
|
|
loaded: true,
|
|
},
|
|
settingsData: {
|
|
executorsLoaded: true,
|
|
agentsLoaded: true,
|
|
},
|
|
...(mappedUserSettings.loaded
|
|
? {
|
|
userSettings: {
|
|
...mappedUserSettings,
|
|
workspaceId: activeWorkspaceId,
|
|
},
|
|
}
|
|
: {}),
|
|
};
|
|
} catch {
|
|
// If any non-settings fetch (workspaces, executors, agents, …) throws, we
|
|
// render with empty initial state — losing the userSettings overrides too.
|
|
// That's acceptable: the page is already degraded (no executors/agents), so
|
|
// override-driven shortcuts being inactive until client hydration is fine.
|
|
initialState = {};
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<StateHydrator initialState={initialState} />
|
|
<SettingsLayoutClient>{children}</SettingsLayoutClient>
|
|
</>
|
|
);
|
|
}
|