feat(easter-egg): footer'da Tâhâ 25–26 âyeti, konsolda piksel kum saati + Asr Sûresi

- Footer: "Rabbi'şrah lî sadrî ve yessir lî emrî" (Tâhâ 25–26) Arapça +
  Diyanet İşleri meali; Arapça Amiri Quran ile (önceden yüklenmez, ~45 KB).
  Altında PixelMark'lı kaynak satırı.
- Konsol: ConsoleAsr artık kök layout'ta. Logodaki renklerle dot-matrix kum
  saati, Asr Sûresi (Diyanet mushafı + Diyanet meali, kaynakla bayt bayt
  aynı), İmam Şâfiî'nin sûfîlerden aktardığı vakit sözü, /meraklisina linki.
  Tarayıcı boşa çıkınca bir kez basar.
- DESIGN.md §3: Arapça yazı tipinin tek işi (yalnız Kur'an metni) yazıldı.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-10-03 19:27:08 +03:00
parent 70423853f3
commit 26626ae449
4 changed files with 165 additions and 19 deletions

View File

@@ -2,31 +2,136 @@
import { useEffect } from "react";
let hasPlayed = false;
/**
* Konsol sürprizi: DevTools'u açan meraklıya sitenin piksel dilinde bir kum
* saati, Asr Sûresi (Arapça + meal) ve tasavvuftan bir vakit sözü. "Asr"
* zaman demek; kum saati de onu anlatır. Görünür arayüzde iz bırakmaz; kök
* layout'ta bir kez çalışır, sayfa geçişlerinde tekrar basmaz.
*
* DevTools açık ya da koyu temada olabilir: düz metne renk verilmez
* (konsolun kendi rengi kalır), yalnız iki zeminde de okunan marka renkleri
* kullanılır.
*
* Arapça metin Diyanet'in Kur'an-ı Kerim mushafından, meal Diyanet İşleri
* mealinden harfi harfine alındı (kuran.diyanet.gov.tr, âyet gruplaması
* aynen). Elle düzeltme yapılmaz; değişecekse kaynaktan yeniden kopyalanır.
*/
const consoleMessage = [
"%cبِسْمِ اللَّهِ الرَّحْمٰنِ الرَّحِيمِ",
"",
"وَالْعَصْرِۙ اِنَّ الْاِنْسَانَ لَف۪ي خُسْرٍۙ",
"اِلَّا الَّذ۪ينَ اٰمَنُوا وَعَمِلُوا الصَّالِحَاتِ",
"وَتَوَاصَوْا بِالْحَقِّ وَتَوَاصَوْا بِالصَّبْرِ",
"",
let basildi = false;
// Logodaki üç kare (public/kolay-tercih-mark.svg)
const MAVI = "#3b82f6";
const SEFTALI = "#fdc7a7";
const TURUNCU = "#ff5a00";
// slate-400: açık ve koyu konsolda okunan silik metin
const SILIK = "#94a3b8";
// Hero'daki dot-matrix ızgarasıyla kum saati: # çerçeve, g boş cam,
// o yığılmış kum, p akan kum. Üst hazne boşalıyor, alt hazne doluyor.
const KUM_SAATI = [
"###########",
" #ggggggg# ",
" #ooooooo# ",
" #ooooo# ",
" #ooo# ",
" #p# ",
" #p# ",
" #gpg# ",
" #ggpgg# ",
" #ggooogg# ",
" #ooooooo# ",
"###########",
];
const PIKSEL_RENK: Record<string, string> = {
"#": MAVI,
g: "rgba(59,130,246,0.16)",
o: TURUNCU,
p: SEFTALI,
};
// Piksel = arka planı boyanmış iki boşluk; satır aralığı ve sağ boşluk
// hero'daki gibi kareler arasında eşit nefes bırakır.
const HUCRE = "font-size:8px;line-height:14px;margin-right:4px;border-radius:2px";
// Kum saatinin sağında, boğaz hizasında duran satırlar
const YAN_YAZI: Record<number, [string, string]> = {
5: ["KolayTercih", `font-size:13px;font-weight:700;color:${MAVI}`],
6: ["Asr Sûresi", `font-size:12px;color:${SILIK}`],
};
const BESMELE = "بِسْمِ اللّٰهِ الرَّحْمٰنِ الرَّح۪يمِ";
const ASR = [
"وَالْعَصْرِۙ",
"اِنَّ الْاِنْسَانَ لَفٖي خُسْرٍۙ",
"اِلَّا الَّذٖينَ اٰمَنُوا وَعَمِلُوا الصَّالِحَاتِ وَتَوَاصَوْا بِالْحَقِّ وَتَوَاصَوْا بِالصَّبْرِ",
];
const MEAL = [
"Rahmân ve Rahîm olan Allah’ın adıyla.",
"",
"Asra yemîn olsun ki,",
"İnsan gerçekten ziyândadır.",
"Ancak iman edip sâlih ameller yapanlar, birbirlerine hakkı tavsiye",
"edenler ve birbirlerine sabretmeyi öğütleyenler müstesnâ!",
].join("\n");
"1-2. Andolsun zamana ki, insan gerçekten ziyan içindedir.",
"3. Ancak, iman edip de sâlih ameller işleyenler, birbirlerine hakkı tavsiye edenler, birbirlerine sabrı tavsiye edenler başka (Onlar ziyanda değillerdir).",
];
// Konsol sayfanın web fontlarını görmez; sistemde ne varsa o seçilir.
const ARAPCA_YAZI_TIPI =
"'Amiri Quran','Amiri','Scheherazade New','Noto Naskh Arabic','Geeza Pro','Traditional Arabic',serif";
function mesaj(): string[] {
let bicim = "";
const stiller: string[] = [];
const ekle = (metin: string, stil: string) => {
bicim += `%c${metin}`;
stiller.push(stil);
};
KUM_SAATI.forEach((satir, i) => {
for (const k of satir) {
const renk = PIKSEL_RENK[k];
ekle(" ", renk ? `${HUCRE};background:${renk}` : HUCRE);
}
const yan = YAN_YAZI[i];
if (yan) ekle(` ${yan[0]}`, `line-height:14px;${yan[1]}`);
bicim += "\n";
});
ekle(
`\n${BESMELE}\n${ASR.join("\n")}\n`,
`font-size:22px;line-height:2;font-family:${ARAPCA_YAZI_TIPI}`,
);
ekle(`\n${MEAL.join("\n")}\n`, "font-size:13px;line-height:1.7");
ekle("Asr Sûresi (103) · Diyanet İşleri meali\n\n", `font-size:11px;color:${SILIK}`);
ekle(
"«Vakit bir kılıçtır; sen onu kesmezsen o seni keser.»\n",
`font-size:13px;font-weight:600;line-height:1.8;color:${TURUNCU}`,
);
ekle(
"İmam Şâfiî’nin sûfîlerden öğrendiğini söylediği söz\n\n",
`font-size:11px;color:${SILIK}`,
);
ekle(
`Meraklısına: ${window.location.origin}/meraklisina`,
`font-size:11px;color:${SILIK}`,
);
return [bicim, ...stiller];
}
export function ConsoleAsr() {
useEffect(() => {
if (hasPlayed) return;
hasPlayed = true;
if (basildi) return;
basildi = true;
window.setTimeout(() => {
console.log(consoleMessage, "font-size:16px");
}, 250);
// Hidrasyon ve ilk etkileşimle yarışmasın: tarayıcı boşa çıkınca bas.
// Safari'de requestIdleCallback yok, orada kısa bir gecikme yeter.
const bas = () => console.log(...mesaj());
if (typeof window.requestIdleCallback === "function") {
window.requestIdleCallback(bas, { timeout: 3000 });
} else {
window.setTimeout(bas, 1000);
}
}, []);
return null;