diff --git a/.blueprint/manifest.yaml b/.blueprint/manifest.yaml index 8ea1660..d718c1d 100644 --- a/.blueprint/manifest.yaml +++ b/.blueprint/manifest.yaml @@ -397,6 +397,14 @@ modules: updated: 2026-07-02 position: [3750, 150] + - id: ui-restructure + name: 功能区 UI 重构 + area: frontend + status: in_progress + progress: 40 + note: "阶段一完成(N1 六组导航 + N2 卡片分层);阶段二 N3/N4/N5 待用一周后启动" + position: [3750, 300] + edges: # 前端依赖 - from: workspace-mgmt diff --git a/.blueprint/modules/ui-restructure.md b/.blueprint/modules/ui-restructure.md new file mode 100644 index 0000000..37042b2 --- /dev/null +++ b/.blueprint/modules/ui-restructure.md @@ -0,0 +1,77 @@ +# 功能区 UI 信息架构重构 + +对照宪章"名实一致"做的一次 UI 自洽重构。核心问题:资源类功能寄生在"项目管理"页、 +BlueprintModal 巨型容器混装全局/跨项目/单项目三层内容、ProjectCard 十个操作无分层、 +Gitea 全局实例配置错位在单项目弹窗内。 + +## 三条公理 + +1. **名实一致**:页面叫什么就只放什么 +2. **层级对齐**:全局配置 → 设置页;跨项目 → 一级导航;单项目 → 项目弹窗/卡片 +3. **频率分层**:高频直达,低频收进"更多" + +## 目标信息架构(六组一级导航) + +``` +🏠 总览 行动项:跨项目进行中、可派发、Buff 项目、启动警报 +📁 项目 产品组看板 / 独立项目 / 管理(项目·产品组·模板·标签·仓库注册表) +🖥️ 资源 服务器 / 软件仓库 / 云产品 / API 密钥 / 源码库 +♻️ 洞察 飞轮(梦核·归档·git 质量)/ 健康中心 / CONVENTIONS 全局治理 +🔧 工具 开发环境扫描 / WSL 代理 +⚙️ 设置 编辑器·更新·MCP + Gitea 实例管理 +``` + +设计文档全文见 2026-07-02 会话 /architect 输出(方案 A,零后端变更,纯前端挂载点迁移,分两阶段)。 + +## 任务卡(2026-07-02 /splitter 拆解) + +### ✅ N1. 资源/工具一级页 + Sidebar 六组重组 + Dashboard 瘦身 +- status: done +- complexity: M +- files: src/components/resources/ResourcesPage.tsx(新增), src/components/tools/ToolsPage.tsx(新增), src/components/layout/Sidebar.tsx, src/components/dashboard/Dashboard.tsx, src/App.tsx +- acceptance: Sidebar 显示六组导航(总览/项目/资源/洞察/工具/设置);资源页 5 tab(服务器/软件仓库/云产品/API密钥/源码库)复用现有面板组件且 queryKey 不变;工具页 2 tab(开发环境/WSL代理);洞察暂挂现健康中心内容(N3 再扩充);Dashboard 看板只剩产品组/独立项目两个 tab;typecheck 通过 + +### ✅ N2. ProjectCard 操作分层(4 明按钮 + ⋯ 更多下拉) +- status: done +- complexity: M +- files: src/components/dashboard/ProjectCard.tsx +- acceptance: 底部操作区只显示 🗺️ 蓝图 / 🎓 导师 / 编辑 / 动态 四个明按钮 + ⋯ 更多下拉(收纳 🤖 Claude 配置、📝 个人备忘、🖥️ 关联服务器、🚀 CI/CD、🔬 环境扫描、🧰 工具管理,带文字标签);下拉点击外部自动关闭;所有原功能可达;typecheck 通过 + +### 📋 N3. 洞察页:飞轮跨项目内容迁出 BlueprintModal + 健康中心并入 +- status: todo +- complexity: M +- depends: N1 +- files: src/components/insight/InsightPage.tsx(新增), src/components/dashboard/BlueprintModal.tsx, src/components/health/HealthPage.tsx +- acceptance: 洞察页 3 tab:飞轮 / 健康 / 全局治理;FlywheelPanel 的跨项目内容(梦核提示词、归档候选、全项目 Git 质量表、全量导入按钮)迁至飞轮 tab;CONVENTIONS 版本管理与应用梦核建议迁至全局治理 tab;BlueprintModal 侧板只保留单项目内容;typecheck 通过 + +### 📋 N4. Gitea 实例管理迁设置页(GiteaPanel 拆全局/项目级两半) +- status: todo +- complexity: S +- depends: N1 +- files: src/components/dashboard/GiteaPanel.tsx, src/components/settings/SettingsPage.tsx +- acceptance: 实例管理区(添加/测试/删除)拆为独立组件挂到设置页;BlueprintModal 治理面板只保留项目级绑定与 webhook 区(无实例时提示去设置页添加);两处共用 ["gitea-instances"] queryKey;typecheck 通过 + +### 📋 N5. 仓库注册表并入项目页管理 tab + 总览收敛为行动项 +- status: todo +- complexity: S +- depends: N1 +- files: src/components/manage/ManagePage.tsx, src/components/layout/Sidebar.tsx, src/components/overview/OverviewPage.tsx, src/components/dashboard/RepoRegistryModal.tsx +- acceptance: ManagePage 新增"仓库"tab 复用 RepoRegistry 列表内容;Sidebar 底部"仓库管理"按钮移除;总览页标题与文案对齐"行动项"定位(进行中任务/Buff 项目/警报,不新增统计类内容);typecheck 通过 + +## 决策记录 + +- 选六组导航全面重组而非最小纠偏:资源拆分只解决问题 1,弹窗过载(问题 2)必须有"洞察页"作为跨项目内容的去处才能解 +- 零后端变更是硬约束:所有迁移只挪组件挂载点,queryKey 保持不变(React Query 缓存天然共享,避免状态重复) +- 分两阶段落地(N1+N2 先行):给肌肉记忆留适应期,也让阶段二可以基于阶段一的真实使用反馈微调 + +## 复盘笔记 + +【复盘】N1+N2 阶段一:导航重组 + 卡片分层(2026-07-02) +任务类型: 前端组件 +复杂度: M×2 +实际轮数: 1轮(预估 1 轮) +是否返工: 否 +有效策略: 零后端约束使迁移退化为纯挂载点搬运——新容器页只做 tab 壳,面板组件与 queryKey 原样复用,typecheck 一次通过 +遗留风险: ① 洞察页暂挂健康中心(N3 未做前名实略不符,导航先行占位);② ProjectCard 更多菜单向上弹出,卡片在视口顶部时可能被裁剪(overflow 场景待实际使用观察) +auto_applied_rules: R01 +rule_effectiveness: R01=未触发(无新 command,纯前端) diff --git a/src/App.tsx b/src/App.tsx index b46e287..261ecd2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,11 +2,10 @@ import { useState, useEffect } from "react"; import { OverviewPage } from "./components/overview/OverviewPage"; import { Dashboard } from "./components/dashboard/Dashboard"; import { ProjectModal } from "./components/dashboard/ProjectModal"; -import { DevToolsPage } from "./components/devtools/DevToolsPage"; -import { ProxyPage } from "./components/proxy/ProxyPage"; +import { ResourcesPage } from "./components/resources/ResourcesPage"; +import { ToolsPage } from "./components/tools/ToolsPage"; import { SettingsPage } from "./components/settings/SettingsPage"; import { HealthPage } from "./components/health/HealthPage"; -import { SourceLibraryPage } from "./components/source-library/SourceLibraryPage"; import { Sidebar, type Page } from "./components/layout/Sidebar"; import { Toast } from "./components/ui/Toast"; import { getStartupAlerts, type StartupAlert } from "./lib/commands"; @@ -66,13 +65,13 @@ export default function App() { )}
- {page === "overview" && } - {page === "dashboard" && } - {page === "source-library" && } - {page === "devtools" && } - {page === "proxy" && } - {page === "settings" && } - {page === "health" && } + {page === "overview" && } + {page === "dashboard" && } + {page === "resources" && } + {/* 洞察页阶段一暂挂健康中心,N3 扩充为 飞轮/健康/全局治理 三 tab */} + {page === "insight" && } + {page === "tools" && } + {page === "settings" && }
diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index cb3736b..3597fbd 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -6,10 +6,6 @@ import { ProjectCard } from "./ProjectCard"; import { DeployModal } from "./DeployModal"; import { GroupResourceCanvas } from "./GroupResourceCanvas"; import { ManagePage } from "../manage/ManagePage"; -import { ServerManagerPanel } from "./ServerManagerPanel"; -import { ServerSoftwarePanel } from "./ServerSoftwareBlock"; -import { ApiKeysPanel } from "./ApiKeysPanel"; -import { CloudProductsPanel } from "./CloudProductsPanel"; import { BatchAddModal } from "./BatchAddModal"; import { MountRepoModal } from "./MountRepoModal"; import { ProjectToolsModal } from "./ProjectToolsModal"; @@ -18,7 +14,7 @@ import { openUrl } from "@tauri-apps/plugin-opener"; import { useUIStore } from "../../store/ui"; type DashView = "board" | "manage"; -type BoardTab = "groups" | "standalone" | "servers" | "software" | "apikeys" | "cloud"; +type BoardTab = "groups" | "standalone"; type GroupView = "default" | "resource"; export function Dashboard() { @@ -130,46 +126,6 @@ export function Dashboard() { )} - - - - @@ -219,22 +175,6 @@ export function Dashboard() { )} - {/* ── 服务器管理标签 ── */} - {boardTab === "servers" && } - - {/* ── 软件仓库标签 ── */} - {boardTab === "software" && ( -
- -
- )} - - {/* ── API 管理标签 ── */} - {boardTab === "apikeys" && } - - {/* ── 云产品标签 ── */} - {boardTab === "cloud" && } - diff --git a/src/components/dashboard/ProjectCard.tsx b/src/components/dashboard/ProjectCard.tsx index 5cf7ded..9d29d6d 100644 --- a/src/components/dashboard/ProjectCard.tsx +++ b/src/components/dashboard/ProjectCard.tsx @@ -72,6 +72,8 @@ export function ProjectCard({ project: p }: Props) { const [newBranchName, setNewBranchName] = useState(""); const [creatingBranch, setCreatingBranch] = useState(false); const branchMenuRef = useRef(null); + const [showMoreMenu, setShowMoreMenu] = useState(false); + const moreMenuRef = useRef(null); const showToast = useUIStore((s) => s.showToast); const openEdit = useUIStore((s) => s.openEdit); const queryClient = useQueryClient(); @@ -107,6 +109,17 @@ export function ProjectCard({ project: p }: Props) { return () => document.removeEventListener("mousedown", handler); }, [showBranchMenu]); + useEffect(() => { + if (!showMoreMenu) return; + const handler = (e: MouseEvent) => { + if (moreMenuRef.current && !moreMenuRef.current.contains(e.target as Node)) { + setShowMoreMenu(false); + } + }; + document.addEventListener("mousedown", handler); + return () => document.removeEventListener("mousedown", handler); + }, [showMoreMenu]); + const pullMutation = useMutation({ mutationFn: () => gitPull(localPath!), onSuccess: (msg) => { @@ -573,44 +586,52 @@ export function ProjectCard({ project: p }: Props) {
{(p.updated_at ?? "").slice(0, 10)}
- - - - - - - + + {/* 低频操作收进更多菜单 */} +
+ + {showMoreMenu && ( +
+ {([ + { icon: "🤖", label: "Claude 自主性", action: () => setShowClaudeConfig(true) }, + { icon: "📝", label: showTodos ? "收起备忘" : "个人备忘", action: () => setShowTodos((v) => !v) }, + { icon: "🖥️", label: showServers ? "收起服务器" : "关联服务器", action: () => setShowServers((v) => !v) }, + { icon: "🚀", label: "CI/CD 控制台", action: () => setShowCicd(true) }, + { icon: "🔬", label: "环境扫描", action: () => setShowEnvScan(true) }, + { icon: "🧰", label: "工具管理", action: () => setShowToolsMgr(true) }, + ]).map(({ icon, label, action }) => ( + + ))} +
+ )} +
+
+ ))} +
+ +
+ {/* 源码库自带页面级布局,其余面板沿用看板时代的容器内边距 */} + {tab === "source" ? ( + + ) : ( +
+ {tab === "servers" && } + {tab === "software" && } + {tab === "cloud" && } + {tab === "apikeys" && } +
+ )} +
+
+ ); +} diff --git a/src/components/tools/ToolsPage.tsx b/src/components/tools/ToolsPage.tsx new file mode 100644 index 0000000..85680b6 --- /dev/null +++ b/src/components/tools/ToolsPage.tsx @@ -0,0 +1,39 @@ +// 工具页:开发环境扫描 / WSL 代理 +// 两个低频独立页合并为 tabs(ui-restructure N1),页面组件原样复用 +import { useState } from "react"; +import { DevToolsPage } from "../devtools/DevToolsPage"; +import { ProxyPage } from "../proxy/ProxyPage"; + +type ToolTab = "devtools" | "proxy"; + +export function ToolsPage() { + const [tab, setTab] = useState("devtools"); + + return ( +
+
+ {([ + { id: "devtools" as ToolTab, label: "开发环境" }, + { id: "proxy" as ToolTab, label: "WSL 代理" }, + ]).map(({ id, label }) => ( + + ))} +
+ +
+ {tab === "devtools" && } + {tab === "proxy" && } +
+
+ ); +}