// 뷰 헬퍼 — 빌드/프런트 프레임워크 없이 서버에서 SVG 를 직접 만들어 EJS 로 출력한다. // (디자인 레퍼런스의 Octicon 패스 / identicon 알고리즘 / 언어색을 그대로 포팅) // res.locals 에 실어 모든 뷰에서 icon()/identicon()/langColor() 로 사용. // GitHub Octicon 16x16 패스 모음. const OCTICONS = { code: '', search: '', sun: '', moon: '', caret: '', signout: '', lock: '', globe: '', repo: '', starFill: '', star: '', heart: '', heartFill: '', comment: '', plus: '', x: '', reply: '', check: '', ext: '', trash: '', }; // 인라인 SVG 아이콘 문자열. fill 기본은 currentColor(부모 색 상속). export function icon(name, opts = {}) { const size = opts.size || 16; const fill = opts.fill || "currentColor"; const path = OCTICONS[name] || OCTICONS.repo; return ( `` ); } // 언어 → 색(카드의 언어 점). const LANG_COLORS = { Python: "#3572A5", TypeScript: "#3178c6", JavaScript: "#f1e05a", Go: "#00ADD8", Rust: "#dea584", Shell: "#89e051", Java: "#b07219", }; export function langColor(lang) { return LANG_COLORS[lang] || "#8b949e"; } // 태그에서 대표 언어 추론(프로젝트 등록 시 lang 저장용). export function langFromTags(tags) { const t = (tags || []).map((x) => String(x).toLowerCase()); if (t.includes("fastapi") || t.includes("python") || t.includes("django")) return "Python"; if (t.includes("react") || t.includes("ts") || t.includes("typescript") || t.includes("next")) return "TypeScript"; if (t.includes("go") || t.includes("golang")) return "Go"; if (t.includes("rust")) return "Rust"; if (t.includes("node") || t.includes("js")) return "JavaScript"; return "TypeScript"; } // 문자열 → 결정적 해시(아바타/색 시드). function hashSeed(seed) { let h = 0; const s = String(seed); for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0; return h; } // 사번 기반 identicon 아바타(5x5 대칭 격자). 디자인과 동일한 시각. export function identicon(seed, size = 28) { const h = hashSeed(seed); const hue = h % 360; const sat = 48 + (h % 22); const color = `hsl(${hue},${sat}%,52%)`; const grid = 5; const on = new Array(25).fill(false); for (let col = 0; col < 3; col++) { for (let row = 0; row < 5; row++) { const bit = (h >> (col * 5 + row)) & 1; on[row * 5 + col] = !!bit; on[row * 5 + (4 - col)] = !!bit; } } const inner = Math.round(size * 0.74); const cell = inner / grid; let rects = ""; for (let r = 0; r < grid; r++) { for (let c = 0; c < grid; c++) { if (on[r * grid + c]) { rects += ``; } } } const radius = Math.max(5, Math.round(size * 0.2)); const svg = `${rects}`; return ( `${svg}` ); }