feat: 添加仓库管理页面和 AI 功能,优化路由和导航

- 新增仓库列表页面,支持查看和管理 CNB 仓库
- 添加 AI 代理系统和状态管理
- 新增 tags-input、popover、textarea、tooltip 等 UI 组件
- 更新依赖:@kevisual/cnb 升级至 0.0.22,添加 idb-keyval
- 改进路由守卫:未配置 API Key 时自动跳转配置页
- 优化 Dialog 遮罩层样式和整体布局

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-09 04:44:28 +08:00
parent 0ced574b8b
commit a2629fec7b
22 changed files with 1606 additions and 62 deletions

93
src/app/repo/page.tsx Normal file
View File

@@ -0,0 +1,93 @@
import { useEffect } from 'react'
import { useRepoStore } from './store/index'
import { RepoCard } from './components/RepoCard'
import { EditRepoDialog } from './modules/EditRepoDialog'
import { WorkspaceDetailDialog } from './modules/WorkspaceDetailDialog'
import { SyncRepoDialog } from './modules/SyncRepoDialog'
export const App = () => {
const { list, getList, loading, editRepo, setEditRepo, showEditDialog, setShowEditDialog, startWorkspace, getWorkspaceList, deleteItem, setSelectedSyncRepo, setSyncDialogOpen } = useRepoStore()
useEffect(() => {
getList()
getWorkspaceList()
}, [])
const handleEdit = (repo: any) => {
setEditRepo(repo)
setShowEditDialog(true)
}
const handleIssue = (repo: any) => {
window.open(`https://cnb.cool/${repo.path}/-/issues`)
}
const handleSettings = (repo: any) => {
window.open(`https://cnb.cool/${repo.path}/-/settings`)
}
const handleDelete = (repo: any) => {
if (repo.path)
deleteItem(repo.path)
}
const handleSync = (repo: any) => {
setSelectedSyncRepo(repo)
setSyncDialogOpen(true)
}
return (
<div className="min-h-screen bg-neutral-50 flex flex-col">
<div className="container mx-auto p-6 max-w-7xl flex-1">
<div className="mb-8">
<h1 className="text-4xl font-bold text-neutral-900 mb-2"></h1>
<p className="text-neutral-600"> {list.length} </p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{list.map((repo) => (
<RepoCard
key={repo.id}
repo={repo}
onStartWorkspace={startWorkspace}
onEdit={handleEdit}
onIssue={handleIssue}
onSettings={handleSettings}
onDelete={handleDelete}
onSync={handleSync}
/>
))}
</div>
{list.length === 0 && !loading && (
<div className="text-center py-20">
<div className="text-neutral-400 text-lg"></div>
</div>
)}
</div>
<footer className="border-t border-neutral-200 bg-white py-6 mt-auto">
<div className="container mx-auto px-6 max-w-7xl">
<div className="flex items-center justify-between text-sm text-neutral-500">
<div>© 2026 </div>
<div className="flex items-center gap-4">
<a href="#" className="hover:text-neutral-900 transition-colors"></a>
<a href="#" className="hover:text-neutral-900 transition-colors"></a>
<a href="#" className="hover:text-neutral-900 transition-colors"></a>
</div>
</div>
</div>
</footer>
<EditRepoDialog
open={showEditDialog}
onOpenChange={setShowEditDialog}
repo={editRepo}
/>
<WorkspaceDetailDialog />
<SyncRepoDialog />
</div>
)
}
export default App;