Files
kolaytercih/src/components/piksel-serit.tsx
bilalgursen aec8a2ad76 feat(v2): 9–10 Ekim çalışma ağacı — deneyim dili 1. dalga, PWA manifest, revizyon niyet modeli, listem/tadımlık bileşen denetimi
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>
2026-10-11 00:34:29 +03:00

76 lines
2.6 KiB
TypeScript
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.
"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>
);
}