diff --git a/src/pages/launcher/LauncherSourcePage.tsx b/src/pages/launcher/LauncherSourcePage.tsx index 08d077a..04dc8a6 100644 --- a/src/pages/launcher/LauncherSourcePage.tsx +++ b/src/pages/launcher/LauncherSourcePage.tsx @@ -1,5 +1,5 @@ import { Copy, FileText, Link2, RotateCcw } from 'lucide-react'; -import { useEffect, useMemo, useState } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; import { Badge, Button, Card, CardHeader, Modal } from '../../components/ui'; import { useToast } from '../../components/toast'; import { PageTitle } from '../../layout/AppLayout'; @@ -58,6 +58,13 @@ type DiffFile = { removed: number; }; +type DiffTreeNode = { + name: string; + path: string; + children: DiffTreeNode[]; + file?: DiffFile; +}; + function repoFullName(repo: GiteaRepo) { return (repo as GiteaRepo & { fullName?: string }).fullName ?? repo.full_name ?? normalizeRepository(repo.name); } @@ -222,8 +229,96 @@ function diffLineClass(line: string) { return 'text-slate-300'; } +function buildDiffTree(files: DiffFile[]) { + const root: DiffTreeNode = { name: '', path: '', children: [] }; + + files.forEach((file) => { + const parts = file.path.split('/').filter(Boolean); + if (parts.length === 0) parts.push(file.path); + + let current = root; + parts.forEach((part, index) => { + const path = parts.slice(0, index + 1).join('/'); + let next = current.children.find((child) => child.name === part); + if (!next) { + next = { name: part, path, children: [] }; + current.children.push(next); + } + if (index === parts.length - 1) next.file = file; + current = next; + }); + }); + + const sortNodes = (nodes: DiffTreeNode[]) => { + nodes.sort((a, b) => { + if (Boolean(a.file) !== Boolean(b.file)) return a.file ? 1 : -1; + return a.name.localeCompare(b.name); + }); + nodes.forEach((node) => sortNodes(node.children)); + }; + + sortNodes(root.children); + return root.children; +} + +function DiffTree({ + nodes, + depth = 0, + onSelect +}: { + nodes: DiffTreeNode[]; + depth?: number; + onSelect: (file: DiffFile) => void; +}) { + return ( +
총 {files.length}개
{file.path}