From e5962166e0d564bbddf1032bae14f10c135b74d8 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Thu, 30 Apr 2026 12:04:37 +0200 Subject: [PATCH] bug-fixes --- assets/styles/layout.css | 7 ++- src/Twig/ArticleCardCoverExtension.php | 58 +++++++++++++++++++ .../Organisms/HomeHighlightsAside.html.twig | 2 +- templates/pages/article.html.twig | 16 ++--- 4 files changed, 67 insertions(+), 16 deletions(-) diff --git a/assets/styles/layout.css b/assets/styles/layout.css index 5b22917..ccb88e3 100644 --- a/assets/styles/layout.css +++ b/assets/styles/layout.css @@ -748,17 +748,18 @@ aside { .home-aside-highlights__meta { display: block; - font-size: 0.7rem; + font-size: 0.78rem; font-style: normal; + font-weight: 500; font-family: var(--font-family), system-ui, sans-serif; - color: color-mix(in srgb, var(--color-text-mid) 80%, var(--color-bg) 20%); + color: var(--color-primary); letter-spacing: 0.02em; transition: color 0.18s ease; } .home-aside-highlights__item-inner:hover .home-aside-highlights__meta, .home-aside-highlights__item-inner:has(.home-aside-highlights__hit:focus-visible) .home-aside-highlights__meta { - color: color-mix(in srgb, var(--color-primary) 45%, var(--color-text-mid) 55%); + color: color-mix(in srgb, var(--color-secondary) 55%, var(--color-primary)); } table { diff --git a/src/Twig/ArticleCardCoverExtension.php b/src/Twig/ArticleCardCoverExtension.php index e6e7d5f..d2abd44 100644 --- a/src/Twig/ArticleCardCoverExtension.php +++ b/src/Twig/ArticleCardCoverExtension.php @@ -7,6 +7,7 @@ namespace App\Twig; use App\Service\CacheService; use App\Service\NostrPathHelper; use Symfony\Component\Asset\Packages; +use Symfony\Component\HttpFoundation\RequestStack; use Throwable; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; @@ -22,6 +23,8 @@ final class ArticleCardCoverExtension extends AbstractExtension */ private const DEFAULT_PACKAGE_IMAGE = 'icons/favicon-96x96.png'; + private const OG_FALLBACK_PACKAGE_IMAGE = 'og-image.jpg'; + /** * @var array lowercase 64-hex pubkey → resolved cover URL (author picture or site default) */ @@ -31,6 +34,7 @@ final class ArticleCardCoverExtension extends AbstractExtension private readonly CacheService $cacheService, private readonly NostrPathHelper $nostrPathHelper, private readonly Packages $packages, + private readonly RequestStack $requestStack, ) { } @@ -38,9 +42,63 @@ final class ArticleCardCoverExtension extends AbstractExtension { return [ new TwigFunction('article_card_cover', $this->articleCardCover(...)), + new TwigFunction('article_og_image', $this->articleOgImage(...)), + ]; + } + + /** + * Absolute URL + whether to emit og:image:width/height (1200×630) for the site default OG JPEG only. + * + * @return array{href: string, use_default_dimensions: bool} + */ + public function articleOgImage(?string $articleImage, ?string $pubkeyHex): array + { + $cover = $this->articleCardCover($articleImage, $pubkeyHex); + $defaultCover = $this->defaultSiteImageUrl(); + $useDefaultDimensions = false; + $chosen = $cover; + if ($chosen === $defaultCover) { + try { + $chosen = $this->packages->getUrl(self::OG_FALLBACK_PACKAGE_IMAGE); + $useDefaultDimensions = true; + } catch (Throwable) { + $useDefaultDimensions = false; + } + } + + return [ + 'href' => $this->absolutizeForOpenGraph($chosen), + 'use_default_dimensions' => $useDefaultDimensions, ]; } + /** + * Crawlers require absolute https URLs; article/profile images are often https, //, or site-relative. + */ + private function absolutizeForOpenGraph(string $urlOrPath): string + { + $u = trim($urlOrPath); + if ($u === '') { + return ''; + } + if (preg_match('#^https?://#i', $u) === 1) { + return $u; + } + if (str_starts_with($u, '//')) { + return 'https:'.$u; + } + $request = $this->requestStack->getMainRequest(); + if ($request === null) { + return $u; + } + $base = $request->getSchemeAndHttpHost().$request->getBaseUrl(); + if (str_starts_with($u, '/')) { + return $base.$u; + } + + return $base.'/'.ltrim($u, '/'); + } + /** * @param string|null $articleImage Cover URL stored on the article, if any * @param string|null $pubkeyHex 64-char hex (lowercase) Nostr public key, if any diff --git a/templates/components/Organisms/HomeHighlightsAside.html.twig b/templates/components/Organisms/HomeHighlightsAside.html.twig index 62fb93f..a56913f 100644 --- a/templates/components/Organisms/HomeHighlightsAside.html.twig +++ b/templates/components/Organisms/HomeHighlightsAside.html.twig @@ -25,7 +25,7 @@ {% if _prew == '' %}{% set _prew = h.quoteExcerpt|default('')|trim %}{% endif %}
{{ _prew|u.truncate(200, '…') }}
{% endif %} - {{ art.title|default('')|u.truncate(52, '…') }} + {{ art.title|default('')|u.truncate(52, '…') }} {% endif %} diff --git a/templates/pages/article.html.twig b/templates/pages/article.html.twig index 9aaeb9a..e08b4e2 100644 --- a/templates/pages/article.html.twig +++ b/templates/pages/article.html.twig @@ -8,18 +8,10 @@ {% endblock %} {% block ogtags %} - {# Upstream main only outputs og:image when article.image is set — unfurlers often show a blank card. Always set an absolute image + JSON-LD. #} - {% set _img = article.image|default('')|trim %} - {% if _img == '' %} - {% set _og_image = absolute_url(asset('og-image.jpg')) %} - {% set _og_default_dims = true %} - {% elseif _img starts with '//' %} - {% set _og_image = 'https:' ~ _img %} - {% set _og_default_dims = false %} - {% else %} - {% set _og_image = absolute_url(_img) %} - {% set _og_default_dims = false %} - {% endif %} + {# Same resolution as card heroes (article image → kind-0 picture → site OG JPEG); absolute URL for unfurlers. #} + {% set _og = article_og_image(article.image, article.pubkey) %} + {% set _og_image = _og.href %} + {% set _og_default_dims = _og.use_default_dimensions %} {% set _desc = article.summary|default('')|striptags|u.truncate(159, '…') %} {% set _canonical = url('article', { npub: npub|default(npub_from_hex(article.pubkey)), slug: article.slug }) %} {% set _author_name = '' %}