9–10 Ekim oturumlarının commit'lenmemiş işi tek parça alındı (raporlar: docs/ekip-raporlari/2026-10-09, 2026-10-10): - Landing deneyim dili 1. dalga: sayfa geçişi (ViewTransition), piksel morf/şerit, sorun sahneleri, adım akışı, sayan sayı, mobil menü, adım göstergesi, logoya tepki; DESIGN.md §11 ve AGENTS.md kuralları, tasarım denetimine yeni kurallar - Tema: tema ikonu, D kısayolu, tema geçişi - PWA: manifest.ts ve ikonlar - Danışman: revizyon niyet modeli (OPENROUTER_NIYET_MODEL), sohbet geçmişi, soru API'si - /listem ve /sonuc tadımlığı bileşen denetimi düzeltmeleri - eslint: .venv-tts/ yok sayılır tsc, eslint ve tasarım denetimi sıfır hata. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
76 lines
2.6 KiB
TypeScript
76 lines
2.6 KiB
TypeScript
"use client";
|
||
|
||
// Piksel şeridi (DESIGN.md §4.38): hero haritasından dökülen serpintinin tek
|
||
// sıraya inmiş hâli. Dikey bir piksel sütunudur ve kaydırmaya bağlıdır:
|
||
// ekranın okuma çizgisini (varsayılan: yüksekliğin %65'i) geçen her piksel
|
||
// turuncuya ısınır, geri kaydırınca yavaşça soğur. Animasyon yoktur; şeridi
|
||
// kullanıcının parmağı çizer (Parallax gibi kaydırmayla 1:1).
|
||
//
|
||
// Renk geçişi CSS'tir (globals.css `.kt-serit-px`): çabuk ısınma, yavaş soğuma
|
||
// — ısı fırçasıyla aynı asimetri. Hareketi azalt'ta geçiş anlıktır.
|
||
|
||
import { useRef, useState, type CSSProperties } from "react";
|
||
import { useMotionValueEvent, useScroll } from "motion/react";
|
||
import { PIKSEL, dokuOpakligi } from "@/components/pixel-decor";
|
||
import { cn } from "@/lib/utils";
|
||
|
||
const { hucre: CELL, nokta: DOT, bosluk: INSET } = PIKSEL;
|
||
|
||
export function PikselSerit({
|
||
uzunluk,
|
||
okuma = 0.65,
|
||
tohum = 0,
|
||
hucreSinifi,
|
||
className,
|
||
style,
|
||
}: {
|
||
/** Şeridin boyu (hücre) */
|
||
uzunluk: number;
|
||
/** Okuma çizgisi: ekran yüksekliğinin oranı (0 üst, 1 alt) */
|
||
okuma?: number;
|
||
/** Doku deseni; yan yana şeritler aynı görünmesin */
|
||
tohum?: number;
|
||
/** Sıra başına ek sınıf: dar boşlukta içeriğe değecek pikseli gizlemek
|
||
* için (ör. `max-sm:hidden`). */
|
||
hucreSinifi?: readonly (string | undefined)[];
|
||
className?: string;
|
||
style?: CSSProperties;
|
||
}) {
|
||
const kapRef = useRef<HTMLDivElement>(null);
|
||
const [yanan, setYanan] = useState(0);
|
||
// 0: şeridin tepesi okuma çizgisinde; 1: dibi okuma çizgisinde.
|
||
const { scrollYProgress } = useScroll({
|
||
target: kapRef,
|
||
offset: [`start ${okuma}`, `end ${okuma}`],
|
||
});
|
||
useMotionValueEvent(scrollYProgress, "change", (ilerleme) => {
|
||
setYanan(Math.round(ilerleme * uzunluk));
|
||
});
|
||
|
||
return (
|
||
<div ref={kapRef} aria-hidden="true" className={className} style={style}>
|
||
<svg
|
||
viewBox={`0 0 ${CELL} ${uzunluk * CELL}`}
|
||
focusable="false"
|
||
className="block h-auto w-full text-primary"
|
||
>
|
||
{Array.from({ length: uzunluk }, (_, i) => (
|
||
<rect
|
||
key={i}
|
||
x={INSET}
|
||
y={i * CELL + INSET}
|
||
width={DOT}
|
||
height={DOT}
|
||
rx={2}
|
||
data-yandi={i < yanan ? "" : undefined}
|
||
// Şeridin ucu: en son yanan piksel tam turuncu
|
||
data-uc={i === yanan - 1 ? "" : undefined}
|
||
className={cn("kt-serit-px", hucreSinifi?.[i])}
|
||
style={{ "--op": dokuOpakligi(tohum, i) } as CSSProperties}
|
||
/>
|
||
))}
|
||
</svg>
|
||
</div>
|
||
);
|
||
}
|