feat: 添加返回按钮和优化仓库卡片,移除未使用的 RepoInfoCard 组件

This commit is contained in:
2026-02-26 01:39:52 +08:00
parent a54597c65e
commit 245bbb33b0
5 changed files with 74 additions and 305 deletions

View File

@@ -1,13 +1,12 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useRepoStore } from "../store"; import { useRepoStore } from "../store";
import { useShallow } from "zustand/shallow"; import { useShallow } from "zustand/shallow";
import { toast } from "sonner";
import CodeMirror from "@uiw/react-codemirror"; import CodeMirror from "@uiw/react-codemirror";
import { yaml } from "@codemirror/lang-yaml"; import { yaml } from "@codemirror/lang-yaml";
import { useLayoutStore } from "@/pages/auth/store"; import { useLayoutStore } from "@/pages/auth/store";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { ArrowLeft, Workflow } from "lucide-react";
import { Workflow } from "lucide-react"; import { useNavigate } from "@tanstack/react-router";
export const BuildConfig = () => { export const BuildConfig = () => {
const repoStore = useRepoStore(useShallow((state) => ({ const repoStore = useRepoStore(useShallow((state) => ({
@@ -21,6 +20,7 @@ export const BuildConfig = () => {
buildWorkspace: state.buildWorkspace, buildWorkspace: state.buildWorkspace,
}))); })));
const repo = repoStore.editRepo!; const repo = repoStore.editRepo!;
const navigate = useNavigate();
const me = useLayoutStore((state) => state.me); const me = useLayoutStore((state) => state.me);
const [localConfig, setLocalConfig] = useState(repoStore.buildConfig?.config || ""); const [localConfig, setLocalConfig] = useState(repoStore.buildConfig?.config || "");
@@ -59,6 +59,12 @@ export const BuildConfig = () => {
{/* 左侧边栏 - 配置信息 */} {/* 左侧边栏 - 配置信息 */}
<div className="w-64 shrink-0 space-y-4"> <div className="w-64 shrink-0 space-y-4">
<div className="text-xl font-bold border-b pb-2 mb-4 flex"> <div className="text-xl font-bold border-b pb-2 mb-4 flex">
<button
onClick={() => navigate({ to: '/' })}
className="cursor-pointer flex items-center justify-center w-8 h-8 rounded-md hover:bg-neutral-100 transition-colors"
>
<ArrowLeft className="w-4 h-4 text-neutral-600" />
</button>
<span className="text-lg font-semibold"></span> <span className="text-lg font-semibold"></span>
<button <button
onClick={repoStore.buildWorkspace} onClick={repoStore.buildWorkspace}

View File

@@ -13,7 +13,7 @@ import {
PopoverContent, PopoverContent,
PopoverTrigger, PopoverTrigger,
} from '@/components/ui/popover' } from '@/components/ui/popover'
import { Star, GitFork, FileText, Edit, FolderGit2, MoreVertical, FileText as IssueIcon, Settings, Play, Trash2, RefreshCw, BookOpen, Copy, Clock, Info, Eye, Square, LinkIcon, ExternalLink } from 'lucide-react' import { Star, GitFork, FileText, Edit, FolderGit2, MoreVertical, FileText as IssueIcon, Settings, Play, Trash2, RefreshCw, BookOpen, Copy, Clock, Info, Eye, Square, LinkIcon, ExternalLink, ArrowLeft } from 'lucide-react'
import { useRepoStore } from '../store' import { useRepoStore } from '../store'
import { useMemo, useState } from 'react' import { useMemo, useState } from 'react'
import { useShallow } from 'zustand/shallow' import { useShallow } from 'zustand/shallow'
@@ -24,26 +24,27 @@ import { useNavigate } from '@tanstack/react-router'
interface RepoCardProps { interface RepoCardProps {
repo: any repo: any
onStartWorkspace: (repo: any) => void showReturn?: boolean
onEdit: (repo: any) => void
onIssue: (repo: any) => void
onSettings: (repo: any) => void
onDelete: (repo: any) => void
onSync?: (repo: any) => void
} }
export function RepoCard({ repo, onStartWorkspace, onEdit, onIssue, onSettings, onDelete, onSync }: RepoCardProps) { export function RepoCard({ showReturn = false, repo }: RepoCardProps) {
const [deletePopoverOpen, setDeletePopoverOpen] = useState(false) const [deletePopoverOpen, setDeletePopoverOpen] = useState(false)
const { workspaceList, getWorkspaceDetail, getList, buildUpdate, stopWorkspace } = useRepoStore(useShallow((state) => ({ const store = useRepoStore(useShallow((state) => ({
workspaceList: state.workspaceList, workspaceList: state.workspaceList,
getWorkspaceDetail: state.getWorkspaceDetail, getWorkspaceDetail: state.getWorkspaceDetail,
getList: state.getList, getList: state.getList,
buildUpdate: state.buildUpdate, buildUpdate: state.buildUpdate,
stopWorkspace: state.stopWorkspace, stopWorkspace: state.stopWorkspace,
setSelectedSyncRepo: state.setSelectedSyncRepo,
setSyncDialogOpen: state.setSyncDialogOpen,
startWorkspace: state.startWorkspace,
setEditRepo: state.setEditRepo,
setShowEditDialog: state.setShowEditDialog,
deleteItem: state.deleteItem,
}))); })));
const workspace = useMemo(() => { const workspace = useMemo(() => {
return workspaceList.find(ws => ws.slug === repo.path) return store.workspaceList.find(ws => ws.slug === repo.path)
}, [workspaceList, repo.path]) }, [store.workspaceList, repo.path])
const isWorkspaceActive = !!workspace const isWorkspaceActive = !!workspace
const owner = repo.path.split('/')[0] const owner = repo.path.split('/')[0]
const isMine = myOrgs.includes(owner) const isMine = myOrgs.includes(owner)
@@ -53,7 +54,7 @@ export function RepoCard({ repo, onStartWorkspace, onEdit, onIssue, onSettings,
const res = await app.run({ path: 'cnb', key: 'build-knowledge-base', payload: { repo: repo.path } }) const res = await app.run({ path: 'cnb', key: 'build-knowledge-base', payload: { repo: repo.path } })
if (res.code === 200) { if (res.code === 200) {
toast.success("知识库创建中") toast.success("知识库创建中")
getList({}, true) store.getList({}, true)
} }
} }
const onClone = async () => { const onClone = async () => {
@@ -65,18 +66,38 @@ export function RepoCard({ repo, onStartWorkspace, onEdit, onIssue, onSettings,
}) })
} }
const onUpdate = async () => { const onUpdate = async () => {
await buildUpdate({ path: repo.path }); await store.buildUpdate({ path: repo.path });
} }
const handleIssue = (repo: any) => {
window.open(`https://cnb.cool/${repo.path}/-/issues`)
}
const handleSettings = (repo: any) => {
window.open(`https://cnb.cool/${repo.path}/-/settings`)
}
return ( return (
<> <>
<Card className="relative p-0 overflow-hidden border border-neutral-200 bg-white hover:shadow-xl hover:border-neutral-300 transition-all duration-300 group pb-14"> <Card className="relative p-0 overflow-hidden border border-neutral-200 bg-white hover:shadow-xl hover:border-neutral-300 transition-all duration-300 group pb-14">
<div className="p-6 space-y-4"> <div className="p-6 space-y-4">
<div className="flex items-start justify-between gap-3"> <div className="flex items-start justify-between gap-3">
<div className="flex items-center gap-2 flex-1 min-w-0"> <div className="flex items-center gap-2 flex-1 min-w-0">
{showReturn && (
<button
onClick={() => navigate({ to: '/' })}
className="cursor-pointer flex items-center justify-center w-8 h-8 rounded-md hover:bg-neutral-100 transition-colors"
>
<ArrowLeft className="w-4 h-4 text-neutral-600" />
</button>
)}
<div <div
className="text-lg font-bold text-neutral-900 hover:text-neutral-600 transition-colors line-clamp-1 group-hover:underline" className="text-lg font-bold text-neutral-900 hover:text-neutral-600 transition-colors line-clamp-1 group-hover:underline"
onClick={() => { onClick={() => {
if (!showReturn) {
navigate({ to: `/repo?repo=${repo.path}` }) navigate({ to: `/repo?repo=${repo.path}` })
} else {
window.open(`https://cnb.cool/${repo.path}`, '_blank')
}
}} }}
> >
{repo.path} {repo.path}
@@ -114,7 +135,7 @@ export function RepoCard({ repo, onStartWorkspace, onEdit, onIssue, onSettings,
size="sm" size="sm"
variant="outline" variant="outline"
onClick={() => { onClick={() => {
stopWorkspace(workspace) store.stopWorkspace(workspace)
}} }}
className="h-8 w-8 p-0 border-neutral-200 hover:border-red-600 hover:bg-red-600 hover:text-white transition-all cursor-pointer" className="h-8 w-8 p-0 border-neutral-200 hover:border-red-600 hover:bg-red-600 hover:text-white transition-all cursor-pointer"
> >
@@ -137,9 +158,9 @@ export function RepoCard({ repo, onStartWorkspace, onEdit, onIssue, onSettings,
variant="outline" variant="outline"
onClick={() => { onClick={() => {
if (!isWorkspaceActive) { if (!isWorkspaceActive) {
onStartWorkspace(repo) store.startWorkspace(repo)
} else { } else {
getWorkspaceDetail(workspace) store.getWorkspaceDetail(workspace)
} }
}} }}
@@ -167,7 +188,10 @@ export function RepoCard({ repo, onStartWorkspace, onEdit, onIssue, onSettings,
} }
/> />
<DropdownMenuContent align="end" className="w-40"> <DropdownMenuContent align="end" className="w-40">
<DropdownMenuItem onClick={() => onEdit(repo)} className="cursor-pointer"> <DropdownMenuItem onClick={() => {
store.setEditRepo(repo)
store.setShowEditDialog(true)
}} className="cursor-pointer">
<Edit className="w-4 h-4 mr-2" /> <Edit className="w-4 h-4 mr-2" />
</DropdownMenuItem> </DropdownMenuItem>
@@ -202,11 +226,11 @@ export function RepoCard({ repo, onStartWorkspace, onEdit, onIssue, onSettings,
<ExternalLink className="w-4 h-4 mr-2" /> <ExternalLink className="w-4 h-4 mr-2" />
访 访
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem onClick={() => onIssue(repo)} className="cursor-pointer"> <DropdownMenuItem onClick={() => handleIssue(repo)} className="cursor-pointer">
<IssueIcon className="w-4 h-4 mr-2" /> <IssueIcon className="w-4 h-4 mr-2" />
访 访
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem onClick={() => onSettings(repo)} className="cursor-pointer"> <DropdownMenuItem onClick={() => handleSettings(repo)} className="cursor-pointer">
<Settings className="w-4 h-4 mr-2" /> <Settings className="w-4 h-4 mr-2" />
访 访
</DropdownMenuItem> </DropdownMenuItem>
@@ -247,7 +271,8 @@ export function RepoCard({ repo, onStartWorkspace, onEdit, onIssue, onSettings,
variant="outline" variant="outline"
className="bg-red-600 text-white border-red-600 hover:bg-red-700 hover:border-red-700" className="bg-red-600 text-white border-red-600 hover:bg-red-700 hover:border-red-700"
onClick={() => { onClick={() => {
onDelete(repo) if (repo.path)
store.deleteItem(repo.path)
setDeletePopoverOpen(false) setDeletePopoverOpen(false)
}} }}
> >
@@ -309,7 +334,7 @@ export function RepoCard({ repo, onStartWorkspace, onEdit, onIssue, onSettings,
</span> </span>
{isWorkspaceActive && <span className="flex items-center gap-1.5 hover:text-neutral-900 transition-colors cursor-pointer" {isWorkspaceActive && <span className="flex items-center gap-1.5 hover:text-neutral-900 transition-colors cursor-pointer"
onClick={() => { onClick={() => {
getWorkspaceDetail(workspace) store.getWorkspaceDetail(workspace)
}}> }}>
<Play className="w-3.5 h-3.5" /> <Play className="w-3.5 h-3.5" />
<span className="font-medium"></span> <span className="font-medium"></span>
@@ -317,7 +342,10 @@ export function RepoCard({ repo, onStartWorkspace, onEdit, onIssue, onSettings,
{isMine && ( {isMine && (
<span <span
className="flex items-center gap-1.5 hover:text-neutral-900 transition-colors cursor-pointer" className="flex items-center gap-1.5 hover:text-neutral-900 transition-colors cursor-pointer"
onClick={() => onSync?.(repo)} onClick={() => {
store.setSelectedSyncRepo(repo)
store.setSyncDialogOpen(true)
}}
> >
<RefreshCw className="w-3.5 h-3.5" /> <RefreshCw className="w-3.5 h-3.5" />
<span className="font-medium"></span> <span className="font-medium"></span>

View File

@@ -1,231 +0,0 @@
import { useNavigate } from "@tanstack/react-router";
import { useMemo } from "react";
import { useRepoStore } from "../store";
import { useShallow } from "zustand/shallow";
import { toast } from "sonner";
import { Card } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { Star, GitFork, FileText, ExternalLink, Calendar, User, Copy, ArrowLeft, Play, Square, Eye, BookOpen, RefreshCw } from "lucide-react";
import { myOrgs } from "../store/build";
export const RepoInfoCard = () => {
const navigate = useNavigate();
const { workspaceList, getWorkspaceDetail, stopWorkspace, editRepo, setSelectedSyncRepo, setSyncDialogOpen } = useRepoStore(useShallow((state) => ({
workspaceList: state.workspaceList,
getWorkspaceDetail: state.getWorkspaceDetail,
stopWorkspace: state.stopWorkspace,
editRepo: state.editRepo,
setSelectedSyncRepo: state.setSelectedSyncRepo,
setSyncDialogOpen: state.setSyncDialogOpen,
})));
const repo = editRepo!;
const workspace = useMemo(() => {
return workspaceList.find(ws => ws.slug === repo.path)
}, [workspaceList, repo.path])
const isWorkspaceActive = !!workspace
const owner = repo.path.split('/')[0]
const isMine = myOrgs.includes(owner)
const isKnowledge = repo?.flags === "KnowledgeBase"
const onClone = () => {
const url = `git clone https://cnb.cool/${repo.path}`
navigator.clipboard.writeText(url).then(() => {
toast.success('克隆地址已复制到剪贴板')
}).catch(() => {
toast.error('复制失败')
})
}
if (!repo) {
return <div>Loading...</div>
}
return (
<div className="space-y-6">
{/* 顶部仓库信息卡片 */}
<Card className="p-6 border border-neutral-200 bg-white">
<div className="space-y-4">
{/* 标题行 */}
<div className="flex items-start justify-between gap-4">
<div className="flex items-center gap-3 flex-1 min-w-0">
<button
onClick={() => navigate({ to: '/' })}
className="cursor-pointer flex items-center justify-center w-8 h-8 rounded-md hover:bg-neutral-100 transition-colors"
>
<ArrowLeft className="w-4 h-4 text-neutral-600" />
</button>
<span className="text-sm text-neutral-500 font-mono">
{repo.path}
</span>
<button
onClick={onClone}
className="flex items-center gap-1 text-xs text-neutral-500 hover:text-neutral-900 transition-colors"
>
<Copy className="w-3.5 h-3.5" />
</button>
<Badge variant="outline" className="shrink-0">
{repo.visibility_level === 'Public' ? '公开' : repo.visibility_level === 'Private' ? '私有' : repo.visibility_level}
</Badge>
{isKnowledge && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger
render={
<div className="shrink-0">
<BookOpen className="w-5 h-5 text-neutral-700" />
</div>
}
/>
<TooltipContent>
<p></p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{isWorkspaceActive && (
<span className="relative flex h-2.5 w-2.5 shrink-0">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
<span className="relative inline-flex rounded-full h-2.5 w-2.5 bg-green-500"></span>
</span>
)}
</div>
<div className="flex items-center gap-2 shrink-0">
{isWorkspaceActive && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger
render={
<Button
size="sm"
variant="outline"
onClick={() => {
stopWorkspace(workspace)
}}
className="h-8 w-8 p-0 border-neutral-200 hover:border-red-600 hover:bg-red-600 hover:text-white transition-all cursor-pointer"
>
<Square className="w-4 h-4" />
</Button>
}
/>
<TooltipContent>
<p></p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
<TooltipProvider>
<Tooltip>
<TooltipTrigger
render={
<Button
size="sm"
variant="outline"
onClick={() => {
if (!isWorkspaceActive) {
// TODO: 启动工作区
} else {
getWorkspaceDetail(workspace)
}
}}
className="h-8 w-8 p-0 border-neutral-200 hover:border-neutral-900 hover:bg-neutral-900 hover:text-white transition-all cursor-pointer"
>
{isWorkspaceActive ? <Eye className="w-4 h-4" /> : <Play className="w-4 h-4" />}
</Button>
}
/>
<TooltipContent>
<p>{isWorkspaceActive ? '查看工作区' : '启动工作区'}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<a
href={repo.web_url}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1.5 text-sm text-neutral-600 hover:text-neutral-900 transition-colors shrink-0"
>
<ExternalLink className="w-4 h-4" />
CNB
</a>
</div>
</div>
{/* 描述 */}
{repo.description && (
<p className="text-sm text-neutral-600 h-12 overflow-hidden truncate">
{repo.description}
</p>
)}
{/* 主题标签和知识库 */}
<div className="flex items-center gap-2">
{/* 主题标签 */}
{repo.topics && (
<div className="flex flex-wrap gap-2">
{repo.topics.split(',').map((topic: string, idx: number) => (
<Badge key={idx} variant="outline" className="text-xs border-neutral-300 text-neutral-700">
{topic.trim()}
</Badge>
))}
</div>
)}
</div>
{/* 统计信息 */}
<div className="flex items-center gap-6 text-xs text-neutral-500">
<span className="flex items-center gap-1.5 hover:text-neutral-900 transition-colors">
<Star className="w-3.5 h-3.5" />
<span className="font-medium">{repo.star_count}</span>
</span>
<span className="flex items-center gap-1.5 hover:text-neutral-900 transition-colors">
<GitFork className="w-3.5 h-3.5" />
<span className="font-medium">{repo.fork_count}</span>
</span>
<span className="flex items-center gap-1.5 hover:text-neutral-900 transition-colors">
<FileText className="w-3.5 h-3.5" />
<span className="font-medium">{repo.open_issue_count}</span>
</span>
{isWorkspaceActive && (
<span className="flex items-center gap-1.5 hover:text-neutral-900 transition-colors cursor-pointer">
<Play className="w-3.5 h-3.5" />
<span className="font-medium"></span>
</span>
)}
{isMine && (
<span
className="flex items-center gap-1.5 hover:text-neutral-900 transition-colors cursor-pointer"
onClick={() => {
setSelectedSyncRepo(repo)
setSyncDialogOpen(true)
}}
>
<RefreshCw className="w-3.5 h-3.5" />
<span className="font-medium"></span>
</span>
)}
</div>
{/* 更新信息 */}
<div className="flex items-center gap-6 text-xs text-neutral-500">
{repo.last_update_nickname && (
<span className="flex items-center gap-1">
<User className="w-3.5 h-3.5" />
{repo.last_update_nickname}
</span>
)}
{repo.last_updated_at && (
<span className="flex items-center gap-1">
<Calendar className="w-3.5 h-3.5" />
{new Date(repo.last_updated_at).toLocaleDateString('zh-CN')}
</span>
)}
</div>
</div>
</Card>
</div>
)
}

View File

@@ -13,21 +13,12 @@ import Fuse from 'fuse.js'
import { useNavigate } from '@tanstack/react-router' import { useNavigate } from '@tanstack/react-router'
export const App = () => { export const App = () => {
const { list, refresh, loading, editRepo, setEditRepo, workspaceList, showEditDialog, setShowEditDialog, showCreateDialog, setShowCreateDialog, startWorkspace, deleteItem, setSelectedSyncRepo, setSyncDialogOpen } = useRepoStore(useShallow((state) => ({ const { list, refresh, loading, workspaceList, setShowCreateDialog } = useRepoStore(useShallow((state) => ({
list: state.list, list: state.list,
refresh: state.refresh, refresh: state.refresh,
loading: state.loading, loading: state.loading,
editRepo: state.editRepo,
setEditRepo: state.setEditRepo,
workspaceList: state.workspaceList, workspaceList: state.workspaceList,
showEditDialog: state.showEditDialog,
setShowEditDialog: state.setShowEditDialog,
showCreateDialog: state.showCreateDialog,
setShowCreateDialog: state.setShowCreateDialog, setShowCreateDialog: state.setShowCreateDialog,
startWorkspace: state.startWorkspace,
deleteItem: state.deleteItem,
setSelectedSyncRepo: state.setSelectedSyncRepo,
setSyncDialogOpen: state.setSyncDialogOpen,
}))) })))
const [searchQuery, setSearchQuery] = useState('') const [searchQuery, setSearchQuery] = useState('')
const navigate = useNavigate(); const navigate = useNavigate();
@@ -35,28 +26,6 @@ export const App = () => {
refresh({ showTips: false }) refresh({ showTips: false })
}, []) }, [])
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)
}
const appList = useMemo(() => { const appList = useMemo(() => {
// 首先按活动状态排序 // 首先按活动状态排序
const sortedList = [...list].sort((a, b) => { const sortedList = [...list].sort((a, b) => {
@@ -133,12 +102,6 @@ export const App = () => {
<RepoCard <RepoCard
key={repo.id} key={repo.id}
repo={repo} repo={repo}
onStartWorkspace={startWorkspace}
onEdit={handleEdit}
onIssue={handleIssue}
onSettings={handleSettings}
onDelete={handleDelete}
onSync={handleSync}
/> />
))} ))}
</div> </div>

View File

@@ -2,18 +2,18 @@ import { useSearch } from "@tanstack/react-router";
import { useRepoStore } from "../store"; import { useRepoStore } from "../store";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useShallow } from "zustand/shallow"; import { useShallow } from "zustand/shallow";
import { RepoInfoCard } from "../components/RepoInfoCard";
import BuildConfig from "../components/BuildConfig"; import BuildConfig from "../components/BuildConfig";
import { CommonRepoDialog } from "../page"; import { CommonRepoDialog } from "../page";
import { RepoCard } from "../components/RepoCard";
export const App = () => { export const App = () => {
const params = useSearch({ strict: false }) as { repo?: string }; const params = useSearch({ strict: false }) as { repo?: string, tab?: string };
const repoStore = useRepoStore(useShallow((state) => ({ const repoStore = useRepoStore(useShallow((state) => ({
getItem: state.getItem, getItem: state.getItem,
editRepo: state.editRepo, editRepo: state.editRepo,
refresH: state.refresh, refresH: state.refresh,
}))); })));
const [activeTab, setActiveTab] = useState("build"); const [activeTab, setActiveTab] = useState(params.tab || "build");
const tabs = [ const tabs = [
{ key: "build", label: "构建配置" }, { key: "build", label: "构建配置" },
{ key: "info", label: "基本信息" }, { key: "info", label: "基本信息" },
@@ -31,16 +31,16 @@ export const App = () => {
} }
return ( return (
<div className="p-2 flex-col flex gap-2 h-full"> <div className="p-2 flex-col flex gap-2 h-full">
<div className="px-4"> <div className="px-4 h-full scrollbar flex-col flex gap-4 overflow-hidden">
<RepoInfoCard />
</div>
<div className="px-4 h-[calc(100%-200px)] scrollbar flex-col flex gap-4 overflow-hidden">
<div className="flex border-b mb-4"> <div className="flex border-b mb-4">
{tabs.map(tab => ( {tabs.map(tab => (
<div <div
key={tab.key} key={tab.key}
className={`px-4 py-2 cursor-pointer ${activeTab === tab.key ? 'border-b-2 border-gray-500' : ''}`} className={`px-4 py-2 cursor-pointer ${activeTab === tab.key ? 'border-b-2 border-gray-500' : ''}`}
onClick={() => setActiveTab(tab.key)} onClick={() => {
setActiveTab(tab.key)
history.replaceState(null, '', `?repo=${params.repo}&tab=${tab.key}`)
}}
> >
{tab.label} {tab.label}
</div> </div>
@@ -48,9 +48,12 @@ export const App = () => {
</div> </div>
{activeTab === 'build' && <BuildConfig />} {activeTab === 'build' && <BuildConfig />}
{activeTab === 'info' && ( {activeTab === 'info' && (
<div className="flex flex-col gap-4 h-full">
<RepoCard repo={repoStore.editRepo} showReturn />
<div className="p-4 border rounded bg-white h-full overflow-auto scrollbar"> <div className="p-4 border rounded bg-white h-full overflow-auto scrollbar">
<pre className="whitespace-pre-wrap break-all">{JSON.stringify(repoStore.editRepo, null, 2)}</pre> <pre className="whitespace-pre-wrap break-all">{JSON.stringify(repoStore.editRepo, null, 2)}</pre>
</div> </div>
</div>
)} )}
</div> </div>
<CommonRepoDialog /> <CommonRepoDialog />