경로 기반 파비콘 지원

- ncue.net 및 하위 도메인에서 /{첫경로}/favicon.ico 우선 사용
- index.html 폴백 모드에서도 파비콘 이미지 렌더링 및 실패 시 글자 폴백

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dsyoon
2026-02-08 12:26:12 +09:00
parent a72dcb154a
commit 39629006a7
2 changed files with 23 additions and 1 deletions

View File

@@ -516,6 +516,18 @@
const favTag = link.favorite ? `<span class="tag fav">★ 즐겨찾기</span>` : ""; const favTag = link.favorite ? `<span class="tag fav">★ 즐겨찾기</span>` : "";
const lockTag = accessible ? "" : `<span class="tag lock">접근 제한</span>`; const lockTag = accessible ? "" : `<span class="tag lock">접근 제한</span>`;
const letter = esc((link.title || d || "L").trim().slice(0, 1).toUpperCase()); const letter = esc((link.title || d || "L").trim().slice(0, 1).toUpperCase());
function faviconUrl(rawUrl) {
try {
const uu = new URL(String(rawUrl || ""));
const host = String(uu.hostname || "").toLowerCase();
const isNcue = host === "ncue.net" || host.endsWith(".ncue.net");
const parts = uu.pathname.split("/").filter(Boolean);
if (isNcue && parts.length) return `${uu.origin}/${parts[0]}/favicon.ico`;
return `${uu.origin}/favicon.ico`;
} catch {
return "";
}
}
function buildOpenUrl(rawUrl) { function buildOpenUrl(rawUrl) {
const url0 = String(rawUrl || "").trim(); const url0 = String(rawUrl || "").trim();
if (!url0) return ""; if (!url0) return "";
@@ -539,11 +551,15 @@
? `<a class="btn mini" href="${openHref}" target="_blank" rel="noopener noreferrer">열기</a>` ? `<a class="btn mini" href="${openHref}" target="_blank" rel="noopener noreferrer">열기</a>`
: `<button class="btn mini" type="button" disabled aria-disabled="true" title="이 링크는 현재 권한으로 접근할 수 없습니다.">열기</button>`; : `<button class="btn mini" type="button" disabled aria-disabled="true" title="이 링크는 현재 권한으로 접근할 수 없습니다.">열기</button>`;
const copyAttrs = accessible ? "" : ` disabled aria-disabled="true" title="이 링크는 현재 권한으로 접근할 수 없습니다."`; const copyAttrs = accessible ? "" : ` disabled aria-disabled="true" title="이 링크는 현재 권한으로 접근할 수 없습니다."`;
const fav = faviconUrl(link.url);
const faviconHtml = fav
? `<img src="${esc(fav)}" alt="" loading="lazy" decoding="async" referrerpolicy="no-referrer" onerror="const p=this.parentNode; this.remove(); if(p) p.insertAdjacentHTML('beforeend','<div class=&quot;letter&quot;>${letter}</div>');" />`
: `<div class="letter">${letter}</div>`;
return ` return `
<article class="card${accessible ? "" : " disabled"}" data-id="${esc(link.id)}" data-access="${accessible ? "1" : "0"}"> <article class="card${accessible ? "" : " disabled"}" data-id="${esc(link.id)}" data-access="${accessible ? "1" : "0"}">
<div class="card-head"> <div class="card-head">
<div class="card-title"> <div class="card-title">
<div class="favicon" aria-hidden="true"><div class="letter">${letter}</div></div> <div class="favicon" aria-hidden="true">${faviconHtml}</div>
<div class="title-wrap"> <div class="title-wrap">
<div class="title" title="${t}">${t}</div> <div class="title" title="${t}">${t}</div>
<div class="domain" title="${d}">${d}</div> <div class="domain" title="${d}">${d}</div>

View File

@@ -184,6 +184,12 @@
function faviconUrl(url) { function faviconUrl(url) {
try { try {
const u = new URL(url); const u = new URL(url);
// default: site root favicon
// special: allow per-path favicon like https://ncue.net/dsyoon/favicon.ico (internal only)
const host = String(u.hostname || "").toLowerCase();
const isNcue = host === "ncue.net" || host.endsWith(".ncue.net");
const parts = u.pathname.split("/").filter(Boolean);
if (isNcue && parts.length) return `${u.origin}/${parts[0]}/favicon.ico`;
return `${u.origin}/favicon.ico`; return `${u.origin}/favicon.ico`;
} catch { } catch {
return ""; return "";