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>
This commit is contained in:
75
src/components/piksel-serit.tsx
Normal file
75
src/components/piksel-serit.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user