feat(projects): 등록 폼·카드 미리보기·README 가져오기 개선

- 등록 모달: 저장소 URL 필드를 제목 아래(설명 위)로 이동 — README 가져오기 흐름 자연화
- URL 미입력 상태로 README 가져오기 시 입력란에 빨간 인라인 안내 표시(토스트 대신)
- 피드 카드: 설명 미리보기 2줄 클램프 + flex:1 로 태그·푸터를 카드 하단에 정렬
- fetchReadme: CRLF→LF·줄끝공백·연속빈줄 정리로 불필요한 개행 유입 방지
- mdPlain: 개행을 살려(빈 줄만 제거) 카드 미리보기가 README 줄 구조로 보이게

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-07 13:15:20 +09:00
parent e21d622ba9
commit 1f5179cd04
6 changed files with 52 additions and 12 deletions

View File

@@ -49,6 +49,20 @@ export async function fetchMainLang(repoUrl) {
}
}
// raw README 의 개행을 정리한다. Gitea 화면(렌더링)에는 없지만 raw 원문에 남아 있는
// 군더더기 개행이 설명란에 "없던 엔터"처럼 들어오는 문제를 막는다.
// - CRLF/CR → LF 로 통일(Windows 개행의 \r 제거)
// - 줄 끝 공백 제거
// - 3줄 이상 연속 빈 줄 → 빈 줄 1개(2개 개행)로 축소 (마크다운상 동일)
// - 문서 앞뒤 공백/개행 제거
function normalizeReadme(text) {
return String(text)
.replace(/\r\n?/g, "\n")
.replace(/[ \t]+$/gm, "")
.replace(/\n{3,}/g, "\n\n")
.trim();
}
// 레포의 README 원문(마크다운)을 반환. 실패/없음이면 "".
// Gitea raw 엔드포인트로 기본 브랜치의 README 파일을 후보 순서대로 시도한다.
export async function fetchReadme(repoUrl) {
@@ -65,7 +79,7 @@ export async function fetchReadme(repoUrl) {
for (const name of candidates) {
const res = await fetch(`${base}/${name}`, { headers, signal: ctrl.signal });
if (!res.ok) continue;
const text = await res.text();
const text = normalizeReadme(await res.text());
// 너무 큰 README 는 앞부분만(설명란이 감당할 범위). 20KB 컷.
return text.length > 20000 ? text.slice(0, 20000) : text;
}