Files
menav/templates/components/site-card.hbs
rbetree 3d9363a550 fix: 重命名 favicon helper 避免与 sites.faviconUrl 同名冲突
将 Handlebars helper `faviconUrl(url)` 更名为 `faviconV2Url(url)`,解决自定义字段 `sites[].faviconUrl`
在模板中被误解析为 helper(无参调用)导致的渲染崩溃。

- helper:faviconUrl -> faviconV2Url
- 模板:site-card 中调用同步更新

BREAKING CHANGE:
自定义模板如使用 `{{faviconUrl url}}` 生成 faviconV2 地址,需要改为 `{{faviconV2Url url}}`。

Fixes: #32
2026-01-04 19:18:14 +08:00

174 lines
8.5 KiB
Handlebars
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{#if url}}
<a href="{{safeUrl url}}" class="site-card{{#if style}} site-card-{{style}}{{/if}}" {{#if external}}target="_blank"
rel="noopener" {{/if}} data-type="{{#if type}}{{type}}{{else}}site{{/if}}" data-name="{{name}}" data-url="{{url}}"
data-icon="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}}" {{#if (lookup . "faviconUrl")}}data-favicon-url="{{lookup . "faviconUrl"}}"
{{/if}} {{#if forceIconMode}}data-force-icon-mode="{{forceIconMode}}" {{/if}}
data-description="{{#if description}}{{description}}{{else}}{{extractDomain url}}{{/if}}"
data-tooltip="{{#if name}}{{name}}{{else}}未命名站点{{/if}}{{#if description}} - {{description}}{{else}} - {{extractDomain url}}{{/if}}"
{{#if publishedAt}}data-published-at="{{publishedAt}}" {{/if}} {{#if source}}data-source="{{source}}" {{/if}}>
{{!-- articles首行图标+标题;下方“时间/来源 + 简介”全宽对齐,不被图标列缩进 --}}
{{#ifEquals type "article"}}
<div class="article-card-header">
<div class="site-card-icon" aria-hidden="true">
{{!-- 站点图标优先级faviconUrl > forceIconMode > 全局 icons.mode --}}
{{#if (lookup . "faviconUrl")}}
<div class="icon-container">
<i class="fas fa-circle-notch fa-spin icon-placeholder" aria-hidden="true"></i>
<img class="favicon-icon" src="{{lookup . "faviconUrl"}}" alt="{{name}} favicon" loading="lazy"
onload="this.classList.add('loaded'); this.previousElementSibling.classList.add('hidden');"
onerror="this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');" />
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} icon-fallback" aria-hidden="true"></i>
</div>
{{else}}
{{#ifEquals forceIconMode "manual"}}
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} site-icon" aria-hidden="true"></i>
{{else}}
{{#ifEquals forceIconMode "favicon"}}
{{#ifHttpUrl url}}
<div class="icon-container">
<i class="fas fa-circle-notch fa-spin icon-placeholder" aria-hidden="true"></i>
<img class="favicon-icon" src="{{faviconV2Url url}}" alt="{{name}} favicon" loading="lazy"
onload="this.classList.add('loaded'); this.previousElementSibling.classList.add('hidden');"
onerror="if (!this.dataset.faviconFallbackTried) { this.dataset.faviconFallbackTried = '1'; this.src = '{{faviconFallbackUrl url}}'; return; } this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');" />
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} icon-fallback" aria-hidden="true"></i>
</div>
{{else}}
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} site-icon" aria-hidden="true"></i>
{{/ifHttpUrl}}
{{else}}
{{#ifEquals @root.icons.mode "favicon"}}
{{#ifHttpUrl url}}
<div class="icon-container">
<i class="fas fa-circle-notch fa-spin icon-placeholder" aria-hidden="true"></i>
<img class="favicon-icon" src="{{faviconV2Url url}}" alt="{{name}} favicon" loading="lazy"
onload="this.classList.add('loaded'); this.previousElementSibling.classList.add('hidden');"
onerror="if (!this.dataset.faviconFallbackTried) { this.dataset.faviconFallbackTried = '1'; this.src = '{{faviconFallbackUrl url}}'; return; } this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');" />
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} icon-fallback" aria-hidden="true"></i>
</div>
{{else}}
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} site-icon" aria-hidden="true"></i>
{{/ifHttpUrl}}
{{else}}
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} site-icon" aria-hidden="true"></i>
{{/ifEquals}}
{{/ifEquals}}
{{/ifEquals}}
{{/if}}
</div>
<div class="article-card-title">
<h3>{{#if name}}{{name}}{{else}}未命名站点{{/if}}</h3>
</div>
</div>
<div class="article-card-body">
{{#ifCond publishedAt '||' source}}
<div class="site-card-meta">
{{#if publishedAt}}
<span class="site-card-meta-date">{{formatDate publishedAt "YYYY-MM-DD"}}</span>
{{/if}}
{{#ifCond publishedAt '&&' source}}
<span class="site-card-meta-sep">·</span>
{{/ifCond}}
{{#if source}}
<span class="site-card-meta-source">{{source}}</span>
{{/if}}
</div>
{{/ifCond}}
<p>{{#if
description}}{{description}}{{else}}{{extractDomain url}}{{/if}}</p>
</div>
{{else}}
{{!-- projects代码仓库风格卡片保留 data-* 结构,便于扩展识别与写回) --}}
{{#ifEquals style "repo"}}
<div class="repo-header">
<i class="{{#if icon}}{{icon}}{{else}}fas fa-code{{/if}} repo-icon" aria-hidden="true"></i>
<div class="repo-title">{{#if name}}{{name}}{{else}}未命名项目{{/if}}</div>
</div>
<div class="repo-desc">{{#if
description}}{{description}}{{else}}{{extractDomain url}}{{/if}}</div>
{{#ifCond language '||' stars}}
<div class="repo-stats">
{{#if language}}
<div class="stat-item">
<span class="lang-dot"
style="background-color: {{#if languageColor}}{{languageColor}}{{else}}#909296{{/if}};"></span>
{{language}}
</div>
{{/if}}
{{#if stars}}
<div class="stat-item">
<i class="far fa-star" aria-hidden="true"></i> {{stars}}
</div>
{{/if}}
{{#if forks}}
<div class="stat-item">
<i class="fas fa-code-branch" aria-hidden="true"></i> {{forks}}
</div>
{{/if}}
{{#if issues}}
<div class="stat-item">
<i class="fas fa-exclamation-circle" aria-hidden="true"></i> {{issues}}
</div>
{{/if}}
</div>
{{/ifCond}}
{{else}}
<div class="site-card-icon" aria-hidden="true">
{{!-- 站点图标优先级faviconUrl > forceIconMode > 全局 icons.mode --}}
{{#if (lookup . "faviconUrl")}}
<div class="icon-container">
<i class="fas fa-circle-notch fa-spin icon-placeholder" aria-hidden="true"></i>
<img class="favicon-icon" src="{{lookup . "faviconUrl"}}" alt="{{name}} favicon" loading="lazy"
onload="this.classList.add('loaded'); this.previousElementSibling.classList.add('hidden');"
onerror="this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');" />
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} icon-fallback" aria-hidden="true"></i>
</div>
{{else}}
{{#ifEquals forceIconMode "manual"}}
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} site-icon" aria-hidden="true"></i>
{{else}}
{{#ifEquals forceIconMode "favicon"}}
{{#ifHttpUrl url}}
<div class="icon-container">
<i class="fas fa-circle-notch fa-spin icon-placeholder" aria-hidden="true"></i>
<img class="favicon-icon" src="{{faviconV2Url url}}" alt="{{name}} favicon" loading="lazy"
onload="this.classList.add('loaded'); this.previousElementSibling.classList.add('hidden');"
onerror="if (!this.dataset.faviconFallbackTried) { this.dataset.faviconFallbackTried = '1'; this.src = '{{faviconFallbackUrl url}}'; return; } this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');" />
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} icon-fallback" aria-hidden="true"></i>
</div>
{{else}}
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} site-icon" aria-hidden="true"></i>
{{/ifHttpUrl}}
{{else}}
{{#ifEquals @root.icons.mode "favicon"}}
{{#ifHttpUrl url}}
<div class="icon-container">
<i class="fas fa-circle-notch fa-spin icon-placeholder" aria-hidden="true"></i>
<img class="favicon-icon" src="{{faviconV2Url url}}" alt="{{name}} favicon" loading="lazy"
onload="this.classList.add('loaded'); this.previousElementSibling.classList.add('hidden');"
onerror="if (!this.dataset.faviconFallbackTried) { this.dataset.faviconFallbackTried = '1'; this.src = '{{faviconFallbackUrl url}}'; return; } this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');" />
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} icon-fallback" aria-hidden="true"></i>
</div>
{{else}}
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} site-icon" aria-hidden="true"></i>
{{/ifHttpUrl}}
{{else}}
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} site-icon" aria-hidden="true"></i>
{{/ifEquals}}
{{/ifEquals}}
{{/ifEquals}}
{{/if}}
</div>
<div class="site-card-content">
<h3>{{#if name}}{{name}}{{else}}未命名站点{{/if}}</h3>
<p>{{#if
description}}{{description}}{{else}}{{extractDomain url}}{{/if}}</p>
</div>
{{/ifEquals}}
{{/ifEquals}}
</a>
{{/if}}