From 341197aac01ff5b0e4a00a28ac83d4a56f07fa00 Mon Sep 17 00:00:00 2001 From: nntrivi2001 Date: Wed, 26 Aug 2026 09:01:00 +0700 Subject: [PATCH] feat(settings): host Claude config as a tab in Settings Settings becomes a tabbed shell (SettingsHub) with one tab per AI tool: Dashboard (existing settings), Claude Code (CcConfig), plus Codex and Cursor placeholders. Active tab lives in the ?tool= query param, and the old /cc-config route redirects to /settings?tool=claude-code. The standalone sidebar entry is dropped. Adding a tool is one entry in TOOL_TABS plus its i18n label. --- client/README.md | 9 +-- client/src/App.tsx | 13 ++-- client/src/components/Sidebar.tsx | 2 - client/src/i18n/locales/en/settings.json | 12 +++- client/src/i18n/locales/vi/settings.json | 12 +++- client/src/pages/SettingsHub.tsx | 92 ++++++++++++++++++++++++ docs/API.md | 2 +- 7 files changed, 127 insertions(+), 15 deletions(-) create mode 100644 client/src/pages/SettingsHub.tsx diff --git a/client/README.md b/client/README.md index 93c6d930..f12d0fab 100644 --- a/client/README.md +++ b/client/README.md @@ -448,9 +448,9 @@ graph TB Detail["/sessions/:id"] Feed["/activity"] Workflows["/workflows"] - CcConfig["/cc-config"] + CcConfig["/cc-config (redirect)"] Run["/run"] - Settings["/settings"] + Settings["/settings (tabs: dashboard | claude-code | ...)"] NF["/* (NotFound)"] Root --> Dashboard & Sessions & Detail & Feed & Workflows & CcConfig & Run & Settings & NF @@ -476,9 +476,10 @@ function App() { } /> } /> } /> - } /> + {/* legacy path - Claude config now lives in a Settings tab */} + } /> } /> - } /> + } /> } /> diff --git a/client/src/App.tsx b/client/src/App.tsx index d867c8e3..999d0a36 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -43,8 +43,7 @@ * - `./pages/SessionDetail` * - `./pages/ActivityFeed` * - `./pages/Workflows` - * - `./pages/Settings` - * - `./pages/CcConfig` + * - `./pages/SettingsHub` * - `./pages/Workspace` * * ## Testing pointers @@ -68,8 +67,7 @@ import { Sessions } from "./pages/Sessions"; import { SessionDetail } from "./pages/SessionDetail"; import { ActivityFeed } from "./pages/ActivityFeed"; import { Workflows } from "./pages/Workflows"; -import { Settings } from "./pages/Settings"; -import { CcConfig } from "./pages/CcConfig"; +import { SettingsHub } from "./pages/SettingsHub"; import { Workspace } from "./pages/Workspace"; import { NotFound } from "./pages/NotFound"; import { useWebSocket } from "./hooks/useWebSocket"; @@ -102,9 +100,12 @@ export default function App() { } /> } /> } /> - } /> + } + /> } /> - } /> + } /> } /> diff --git a/client/src/components/Sidebar.tsx b/client/src/components/Sidebar.tsx index 121a7c9d..0b0ba3ba 100644 --- a/client/src/components/Sidebar.tsx +++ b/client/src/components/Sidebar.tsx @@ -58,7 +58,6 @@ import { Activity, BarChart3, Workflow, - Boxes, Play, Settings, Wifi, @@ -91,7 +90,6 @@ const NAV_KEYS = [ { to: "/sessions", icon: FolderOpen, key: "nav:sessions" }, { to: "/activity", icon: Activity, key: "nav:activityFeed" }, { to: "/workflows", icon: Workflow, key: "nav:workflows" }, - { to: "/cc-config", icon: Boxes, key: "nav:ccConfig" }, { to: "/run", icon: Play, key: "nav:workspace" }, { to: "/settings", icon: Settings, key: "nav:settings" }, ] as const; diff --git a/client/src/i18n/locales/en/settings.json b/client/src/i18n/locales/en/settings.json index 1aa27d95..52f981ab 100644 --- a/client/src/i18n/locales/en/settings.json +++ b/client/src/i18n/locales/en/settings.json @@ -440,5 +440,15 @@ "removeKeep": "Remove (keep data)", "remoteSourceBadgeTitle": "Collected from a remote machine" }, - "cursorPathsNote": "Informational: Cursor sessions count here too — Cursor happens to use the same ~/.claude paths as Claude Code (locally and on synced remotes)." + "cursorPathsNote": "Informational: Cursor sessions count here too — Cursor happens to use the same ~/.claude paths as Claude Code (locally and on synced remotes).", + "tools": { + "label": "Tools", + "dashboard": "Dashboard", + "claudeCode": "Claude Code", + "codex": "Codex", + "cursor": "Cursor", + "soonBadge": "soon", + "comingSoon": "{{tool}} is not integrated yet", + "comingSoonDesc": "Its configuration will show up here once support lands." + } } diff --git a/client/src/i18n/locales/vi/settings.json b/client/src/i18n/locales/vi/settings.json index 6a25b601..977b037e 100644 --- a/client/src/i18n/locales/vi/settings.json +++ b/client/src/i18n/locales/vi/settings.json @@ -440,5 +440,15 @@ "removeKeep": "Xóa (giữ dữ liệu)", "remoteSourceBadgeTitle": "Được thu thập từ một máy từ xa" }, - "cursorPathsNote": "Chỉ mang tính thông tin: phiên Cursor cũng được tính — Cursor tình cờ dùng cùng đường dẫn ~/.claude với Claude Code (cục bộ và trên máy từ xa đã đồng bộ)." + "cursorPathsNote": "Chỉ mang tính thông tin: phiên Cursor cũng được tính — Cursor tình cờ dùng cùng đường dẫn ~/.claude với Claude Code (cục bộ và trên máy từ xa đã đồng bộ).", + "tools": { + "label": "Công cụ", + "dashboard": "Dashboard", + "claudeCode": "Claude Code", + "codex": "Codex", + "cursor": "Cursor", + "soonBadge": "sắp có", + "comingSoon": "{{tool}} chưa được tích hợp", + "comingSoonDesc": "Cấu hình của công cụ này sẽ hiện ở đây khi có hỗ trợ." + } } diff --git a/client/src/pages/SettingsHub.tsx b/client/src/pages/SettingsHub.tsx new file mode 100644 index 00000000..e39b533b --- /dev/null +++ b/client/src/pages/SettingsHub.tsx @@ -0,0 +1,92 @@ +/** + * @file SettingsHub.tsx + * @description Tabbed shell for /settings that hosts the dashboard's own settings plus per-AI-tool configuration panels (Claude Code today, Codex/Cursor reserved), with the active tab kept in the `?tool=` query param. + * @author Nguyễn Ngọc Trí Vĩ + */ +import type { ComponentType } from "react"; +import { useSearchParams } from "react-router-dom"; +import { useTranslation } from "react-i18next"; +import { Boxes, Settings as SettingsIcon, SquareTerminal, MousePointerClick } from "lucide-react"; +import { Settings } from "./Settings"; +import { CcConfig } from "./CcConfig"; + +interface ToolTab { + id: string; + labelKey: string; + Icon: ComponentType<{ className?: string }>; + /** Omit for tools that are not integrated yet - the tab renders a placeholder. */ + Panel?: ComponentType; +} + +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 }, + { id: "codex", labelKey: "tools.codex", Icon: SquareTerminal }, + { id: "cursor", labelKey: "tools.cursor", Icon: MousePointerClick }, +]; + +/** + * 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 active = TOOL_TABS.find((tab) => tab.id === requested) ?? DASHBOARD_TAB; + + return ( +
+
+ {TOOL_TABS.map(({ id, labelKey, Icon, Panel }) => { + const isActive = id === active.id; + return ( + + ); + })} +
+ + {active.Panel ? ( + + ) : ( +
+

+ {t("tools.comingSoon", { tool: t(active.labelKey) })} +

+

{t("tools.comingSoonDesc")}

+
+ )} +
+ ); +} diff --git a/docs/API.md b/docs/API.md index 1ee0ea4c..18230d7b 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1441,7 +1441,7 @@ curl "http://localhost:4820/api/sessions?sources=local,4d1f0e2a-7b9c-4c33-8a21-9 ### Claude Config Explorer -The `/api/cc-config/*` namespace powers the Claude Config Explorer page. All read endpoints are pure file reads under `CLAUDE_HOME` and the project's `.claude/` dir. File mutations cover the low-risk text-file artifacts (skills, subagents, slash commands, output styles, memory), `keybindings.json`, and `settings.json` layers **via a per-key merge only** — each always creates a timestamped backup before writing. Plugin and marketplace management is delegated to the `claude plugin …` CLI. MCP server config stays read-only, because it is written concurrently by the running Claude Code CLI. +The `/api/cc-config/*` namespace powers the Claude Config Explorer, now the **Claude Code** tab of Settings (`/settings?tool=claude-code`). All read endpoints are pure file reads under `CLAUDE_HOME` and the project's `.claude/` dir. File mutations cover the low-risk text-file artifacts (skills, subagents, slash commands, output styles, memory), `keybindings.json`, and `settings.json` layers **via a per-key merge only** — each always creates a timestamped backup before writing. Plugin and marketplace management is delegated to the `claude plugin …` CLI. MCP server config stays read-only, because it is written concurrently by the running Claude Code CLI. ```http GET /api/cc-config/overview