fcfebe44f1
Settings → Dashboard gains an AI Tools section (installed tools with Enabled/Disabled and Set default, available ones with a homepage link, Refresh). useAiTools shares one catalog copy across the app and replaces LANE_TOOL_LABEL. The Add-lane modal picks Default (<tool>) or Other…; the router row reads Native (<tool>) and disables AI Router profiles for tools that cannot use one. The run form gives catalog tools resume and a red per-run Bypass permissions switch (Codex too). Claude Code / Codex settings tabs hide while that tool is disabled. The Workspace probes each lane tool's CLI fresh. Docs: CLAUDE.md tool/bypass rule, AGENTS.md, LANES.md, API.md, CLI.md, server/README.md, OpenAPI fragment + regenerated openapi.yaml.
91 lines
2.9 KiB
TypeScript
91 lines
2.9 KiB
TypeScript
/**
|
|
* @file SettingsHub.tsx
|
|
* @description Tabbed shell for /settings that hosts the dashboard's own settings plus one configuration panel per AI tool (Claude Code and Codex) plus the AI Router profiles they can be pointed at and the git logins dashboard fetch/pull/push use, with the active tab kept in the `?tool=` query param. A tool's tab is hidden while that tool is disabled in Settings → AI Tools.
|
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
|
*/
|
|
import type { ComponentType } from "react";
|
|
import { useSearchParams } from "react-router-dom";
|
|
import { useTranslation } from "react-i18next";
|
|
import { Boxes, KeyRound, Router, Settings as SettingsIcon, SquareTerminal } from "lucide-react";
|
|
import { PageTabs } from "../components/PageTabs";
|
|
import { Settings } from "./Settings";
|
|
import { CcConfig } from "./CcConfig";
|
|
import { CodexConfig } from "./CodexConfig";
|
|
import { AiRouterConfig } from "./AiRouterConfig";
|
|
import { GitCredentialsConfig } from "./GitCredentialsConfig";
|
|
import { useAiTools } from "../hooks/useAiTools";
|
|
|
|
interface ToolTab {
|
|
id: string;
|
|
labelKey: string;
|
|
Icon: ComponentType<{ className?: string }>;
|
|
Panel: ComponentType;
|
|
/** The AI Tools catalog id this tab configures; hidden while it is disabled. */
|
|
aiTool?: string;
|
|
}
|
|
|
|
const DASHBOARD_TAB: ToolTab = {
|
|
id: "dashboard",
|
|
labelKey: "tools.dashboard",
|
|
Icon: SettingsIcon,
|
|
Panel: Settings,
|
|
};
|
|
|
|
/** Add a tool here to give it a tab; that is the whole extension point. */
|
|
const TOOL_TABS: ToolTab[] = [
|
|
DASHBOARD_TAB,
|
|
{
|
|
id: "claude-code",
|
|
labelKey: "tools.claudeCode",
|
|
Icon: Boxes,
|
|
Panel: CcConfig,
|
|
aiTool: "claude",
|
|
},
|
|
{
|
|
id: "codex",
|
|
labelKey: "tools.codex",
|
|
Icon: SquareTerminal,
|
|
Panel: CodexConfig,
|
|
aiTool: "codex",
|
|
},
|
|
{ id: "ai-router", labelKey: "tools.aiRouter", Icon: Router, Panel: AiRouterConfig },
|
|
{
|
|
id: "git-credentials",
|
|
labelKey: "tools.gitCredentials",
|
|
Icon: KeyRound,
|
|
Panel: GitCredentialsConfig,
|
|
},
|
|
];
|
|
|
|
/**
|
|
* Settings page shell with one tab per configuration surface.
|
|
* @returns The tab bar plus the active tab's panel.
|
|
*/
|
|
export function SettingsHub() {
|
|
const { t } = useTranslation("settings");
|
|
const [params, setParams] = useSearchParams();
|
|
const requested = params.get("tool");
|
|
const aiTools = useAiTools();
|
|
const tabs = TOOL_TABS.filter(
|
|
(tab) => !tab.aiTool || aiTools.byId(tab.aiTool)?.enabled !== false
|
|
);
|
|
const active = tabs.find((tab) => tab.id === requested) ?? DASHBOARD_TAB;
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<PageTabs
|
|
ariaLabel={t("tools.label", "Tools")}
|
|
activeId={active.id}
|
|
onSelect={(id) => setParams(id === DASHBOARD_TAB.id ? {} : { tool: id }, { replace: true })}
|
|
tabs={tabs.map(({ id, labelKey, Icon }) => ({
|
|
id,
|
|
Icon,
|
|
label: t(labelKey),
|
|
}))}
|
|
/>
|
|
|
|
<active.Panel />
|
|
</div>
|
|
);
|
|
}
|