diff --git a/src/app/opengraph-image.tsx b/src/app/opengraph-image.tsx index d6776c2..9be0977 100644 --- a/src/app/opengraph-image.tsx +++ b/src/app/opengraph-image.tsx @@ -1,5 +1,5 @@ import { ImageResponse } from "next/og"; -import { loadSiteOgFonts } from "@/lib/og-fonts"; +import { loadSiteOgFonts, OG_AILE } from "@/lib/og-fonts"; export const alt = "KolayTercih — YKS tercihin, gerçek YÖK Atlas verisiyle. Sıralamanı gir, dengeli 24 tercihlik listeni al."; @@ -10,83 +10,81 @@ export default async function OpenGraphImage() { const fonts = await loadSiteOgFonts(); return new ImageResponse( - ( +
-
- - - - - - - Kolay - Tercih - -
+ + + + + + Kolay + Tercih + +
-
- YKS tercihin, gerçek YÖK Atlas verisiyle -
+
+ YKS tercihin, gerçek YÖK Atlas verisiyle +
-
- Sıralamanı gir · Dengeli 24 tercihlik listeni al -
+
+ Sıralamanı gir · Dengeli 24 tercihlik listeni al
- ), +
, { ...size, fonts }, ); } diff --git a/src/app/rehber/[slug]/opengraph-image.tsx b/src/app/rehber/[slug]/opengraph-image.tsx index 62f4553..7445208 100644 --- a/src/app/rehber/[slug]/opengraph-image.tsx +++ b/src/app/rehber/[slug]/opengraph-image.tsx @@ -1,6 +1,6 @@ import { ImageResponse } from "next/og"; import { getRehber } from "@/lib/rehber"; -import { loadRehberOgFonts } from "@/lib/og-fonts"; +import { loadRehberOgFonts, OG_AILE } from "@/lib/og-fonts"; import { rehberBasliginiBol, rehberMetniniParcala, @@ -19,137 +19,180 @@ export default async function RehberOpenGraphImage({ const yazi = getRehber(slug); const baslik = yazi?.baslik ?? "Tercih Rehberi"; const { vurgu, kalan } = rehberBasliginiBol(baslik); + const kelimeler = [ + ...vurgu + .split(/\s+/) + .filter(Boolean) + .map((metin) => ({ metin, vurgu: true })), + ...kalan + .split(/\s+/) + .filter(Boolean) + .map((metin) => ({ metin, vurgu: false })), + ]; + // Kademeler sayfadaki RehberKapak ile aynı mantık; 90+ karakter için bir + // kademe daha var — 1000px genişlikte üç satırı aşmasın. + const baslikBoyutu = + baslik.length > 90 + ? 44 + : baslik.length > 70 + ? 48 + : baslik.length > 52 + ? 54 + : 62; + const kelimeAraligi = Math.round(baslikBoyutu * 0.26); const fonts = await loadRehberOgFonts(); return new ImageResponse( - ( +
+ {[ + [960, 50, 0.16], + [980, 50, 0.28], + [1020, 50, 0.12], + [940, 70, 0.24], + [980, 70, 0.12], + [1000, 70, 0.3], + [1060, 70, 0.18], + [960, 90, 0.12], + [1000, 90, 0.2], + [1040, 90, 0.1], + [1080, 90, 0.26], + [1080, 110, 0.14], + [1120, 110, 0.22], + [1140, 110, 0.12], + [60, 490, 0.12], + [100, 490, 0.25], + [120, 490, 0.14], + [40, 510, 0.24], + [80, 510, 0.12], + [120, 510, 0.3], + [160, 510, 0.16], + [60, 530, 0.14], + [100, 530, 0.22], + [140, 530, 0.1], + [180, 530, 0.27], + ].map(([left, top, opacity], i) => ( + + ))} + + +
+
- {[ - [960, 50, 0.16], [980, 50, 0.28], [1020, 50, 0.12], - [940, 70, 0.24], [980, 70, 0.12], [1000, 70, 0.3], - [1060, 70, 0.18], [960, 90, 0.12], [1000, 90, 0.2], - [1040, 90, 0.1], [1080, 90, 0.26], [1080, 110, 0.14], - [1120, 110, 0.22], [1140, 110, 0.12], [60, 490, 0.12], - [100, 490, 0.25], [120, 490, 0.14], [40, 510, 0.24], - [80, 510, 0.12], [120, 510, 0.3], [160, 510, 0.16], - [60, 530, 0.14], [100, 530, 0.22], [140, 530, 0.1], - [180, 530, 0.27], - ].map(([left, top, opacity], i) => ( - - ))} - -
- -
-
- Tercih Rehberi -
+ Tercih Rehberi +
-
70 ? 48 : baslik.length > 52 ? 54 : 62, - fontWeight: 400, - lineHeight: 1.02, - letterSpacing: "-0.025em", - color: "#2563eb", - }} - > + {/* + Satori satır kaydırmayı yalnızca düz metin düğümlerinde yapar; + rakam+metin karışık bir flex span tek satırda kalır ve parçalar + üst üste biner (2026'lı başlıklarda görülen bozulma). Bu yüzden + başlık kelime kelime ayrı kutulara bölünür: kaydırma kelime + sınırında olur, her kelime kendi rengini/yazı tipini taşır. + */} +
+ {kelimeler.map((kelime, i) => ( - {rehberMetniniParcala(vurgu).map((parca, i) => + {rehberMetniniParcala(kelime.metin).map((parca, j) => /^\d/.test(parca) ? ( - {kalan ? ( - - {rehberMetniniParcala(kalan.trimStart()).map((parca, i) => - /^\d/.test(parca) ? ( - - {parca} - - ) : ( - parca - ), - )} - - ) : null} -
+ ))} +
-
+ + + + + + - - - - - - - Kolay - Tercih - -
+ Kolay + Tercih +
- ), +
, { ...size, fonts }, ); } diff --git a/src/lib/og-fonts.ts b/src/lib/og-fonts.ts index 543a49d..494cb9a 100644 --- a/src/lib/og-fonts.ts +++ b/src/lib/og-fonts.ts @@ -21,7 +21,24 @@ type OgFont = { style: "normal" | "italic"; }; -/** latin + latin-ext çiftini aynı aile/ağırlıkta kaydeder (Türkçe glifler için). */ +/** + * Satori aynı aile adı + ağırlık için TEK dosya seçer (`get()` en iyi eşleşen + * tek kaydı döndürür); iki alt-kümeyi aynı adla kaydetmek latin-ext dosyasını + * fiilen devre dışı bırakıyordu — İ/ş/ğ gibi glifler başka bir aileden + * (kayıt sırasına göre Clarity City) geliyor, ölçüm ile çizim ayrışıyor ve + * "İ" ile başlayan kelimeden sonraki boşluk yutuluyordu. Bu yüzden ext + * alt-kümesi ` Ext` adıyla ayrı kaydedilir; şablonlar `fontFamily`'ye + * aşağıdaki OG_AILE değerlerini verir (Satori CSS gibi virgüllü METİN ister, + * dizi verilirse `split` hatasıyla çöker); glif ilk dosyada yoksa ikinciye + * geçer. + */ +export const OG_AILE = { + calSans: "Cal Sans, Cal Sans Ext", + bricolage: "Bricolage Grotesque, Bricolage Grotesque Ext", + clarity: "Clarity City", +} as const; + +/** latin + latin-ext çiftini kaydeder; ext alt-kümesi ` Ext` adını alır. */ async function latinPair( name: string, weight: OgFont["weight"], @@ -35,7 +52,7 @@ async function latinPair( ]); return [ { name, data: latin, weight, style }, - { name, data: latinExt, weight, style }, + { name: `${name} Ext`, data: latinExt, weight, style }, ]; } diff --git a/src/lib/og-sayfa.tsx b/src/lib/og-sayfa.tsx index 2362dd7..a2de983 100644 --- a/src/lib/og-sayfa.tsx +++ b/src/lib/og-sayfa.tsx @@ -1,5 +1,5 @@ import { ImageResponse } from "next/og"; -import { loadRehberOgFonts } from "@/lib/og-fonts"; +import { loadRehberOgFonts, OG_AILE } from "@/lib/og-fonts"; export const OG_SIZE = { width: 1200, height: 630 }; export const OG_CONTENT_TYPE = "image/png"; @@ -51,187 +51,185 @@ export async function createOgSayfaImage(opts: { const { eyebrow, title, description, logo } = opts; return new ImageResponse( - ( +
+ {PIXELS.map(([left, top, opacity], i) => ( + + ))} + + +
+
- {PIXELS.map(([left, top, opacity], i) => ( - - ))} - -
+ > + {eyebrow} + {logo ? ( + + {/* eslint-disable-next-line @next/next/no-img-element */} + + + ) : null} +
- {eyebrow} - {logo ? ( - - {/* eslint-disable-next-line @next/next/no-img-element */} - - - ) : null} + {title}
- -
+ {description ? (
- {title} + {description}
- {description ? ( -
- {description} -
- ) : null} -
+ ) : null} +
-
+ + + + + + - - - - - - - Kolay - Tercih - -
+ Kolay + Tercih +
- ), +
, { ...OG_SIZE, fonts }, ); }