메뉴 관리 개편, 추천 사이트·1뎁스 네비, 우측 Recommended 연동(v1.1.13)
상단 네비는 평면 테이블·드래그로 편집하고 한 단계 하위만 허용한다. 추천 사이트 탭·location recommended·공개 API와 우측 사이드 카드·파비콘 URL 유틸을 추가한다. 문서·배포 마이그레이션 안내·관리자 레이아웃·설정 화면 등 누적 변경을 반영한다.
This commit is contained in:
32
lib/external-favicon-url.js
Normal file
32
lib/external-favicon-url.js
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 외부 사이트 파비콘을 브라우저에 표시하기 위한 프록시 URL을 만든다.
|
||||
* 호스트만 추출해 Google Favicon 서비스(`https://www.google.com/s2/favicons`) URL을 반환한다.
|
||||
* 내부 경로(`/…`)·`#`·파싱 실패 시 빈 문자열을 반환한다(이미지 생략).
|
||||
* @param {string} rawUrl - 링크 URL
|
||||
* @param {number} [sizePx] - 한 변(px), 기본 32, 최대 128
|
||||
* @returns {string} `https://www.google.com/s2/favicons?...` 또는 `''`
|
||||
*/
|
||||
export const getExternalFaviconUrl = (rawUrl, sizePx = 32) => {
|
||||
const trimmed = String(rawUrl || '').trim()
|
||||
if (!trimmed || trimmed === '#') {
|
||||
return ''
|
||||
}
|
||||
if (trimmed.startsWith('/') && !trimmed.startsWith('//')) {
|
||||
return ''
|
||||
}
|
||||
try {
|
||||
const withProto = trimmed.startsWith('//') ? `https:${trimmed}` : trimmed
|
||||
const u = new URL(withProto)
|
||||
if (u.protocol !== 'http:' && u.protocol !== 'https:') {
|
||||
return ''
|
||||
}
|
||||
const host = u.hostname
|
||||
if (!host) {
|
||||
return ''
|
||||
}
|
||||
const sz = Number.isFinite(sizePx) && sizePx > 0 ? Math.min(128, Math.round(sizePx)) : 32
|
||||
return `https://www.google.com/s2/favicons?domain=${encodeURIComponent(host)}&sz=${sz}`
|
||||
} catch {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
@@ -42,3 +42,26 @@ export const buildNavigationEditorTree = (flat, location) => {
|
||||
sortRec(roots)
|
||||
return roots
|
||||
}
|
||||
|
||||
/**
|
||||
* 관리자 상단 네비 트리 래퍼를 표시·드래그용 평면 행 배열로 만든다.
|
||||
* @param {Array<{ item: Object, children: any[] }>} wraps - `buildNavigationEditorTree` 결과
|
||||
* @param {number} [depth] - 들여쓰기 단계(0=루트)
|
||||
* @returns {Array<{ item: Object, depth: number }>}
|
||||
*/
|
||||
export const flattenNavigationEditorWrappers = (wraps, depth = 0) => {
|
||||
const out = []
|
||||
if (!Array.isArray(wraps)) {
|
||||
return out
|
||||
}
|
||||
for (const w of wraps) {
|
||||
if (!w?.item) {
|
||||
continue
|
||||
}
|
||||
out.push({ item: w.item, depth })
|
||||
if (w.children?.length) {
|
||||
out.push(...flattenNavigationEditorWrappers(w.children, depth + 1))
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user