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:
bilalgursen
2026-10-11 00:34:29 +03:00
parent da0e9dff7a
commit aec8a2ad76
80 changed files with 4134 additions and 654 deletions

View 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>
);
}