From 5574edf3255a93a53bc11704476aa1778e806b8b Mon Sep 17 00:00:00 2001 From: chyoo Date: Tue, 7 Jul 2026 10:18:10 +0900 Subject: [PATCH] modal error change.. --- src/pages/launcher/LauncherSourcePage.tsx | 124 ++++++++++++++++++---- 1 file changed, 106 insertions(+), 18 deletions(-) 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 ( +
+ {nodes.map((node) => ( +
+ {node.file ? ( + + ) : ( +
+ {node.name} +
+ )} + {node.children.length ? : null} +
+ ))} +
+ ); +} + function DiffViewer({ diff }: { diff: string }) { const files = parseDiffFiles(diff); + const tree = useMemo(() => buildDiffTree(files), [files]); + const scrollerRef = useRef(null); + const sectionRefs = useRef>({}); + + const scrollToFile = (file: DiffFile) => { + const scroller = scrollerRef.current; + const section = sectionRefs.current[file.id]; + if (!scroller || !section) return; + scroller.scrollTo({ + top: section.offsetTop - scroller.offsetTop - 12, + behavior: 'smooth' + }); + }; return (
@@ -233,27 +328,20 @@ function DiffViewer({ diff }: { diff: string }) {

총 {files.length}개

- {files.map((file) => ( - - ))} +
-
+
{files.map((file) => ( -
+
{ + sectionRefs.current[file.id] = element; + }} + className="min-w-max scroll-mt-3 overflow-hidden rounded-md border border-slate-700/80 bg-[#0b0f14]" + >

{file.path}