İçeriğe geç

Mimari Genel Bakış

Uygulama Sürümü: v0.1.1 (fork: DarrellThomas/en-parlant) Teknoloji Yığını: Tauri v2 (Rust) + React 19 (TypeScript) + Vite


Tauri, masaüstü uygulamaları oluşturmak için kullanılan bir çerçevedir. Electron’un yaptığı gibi tam bir tarayıcı paketlemek yerine, Tauri kullanıcı arayüzü için işletim sisteminin yerleşik webview’ını ve arka uç için bir Rust sürecini kullanır. Sonuç olarak küçük ve hızlı bir ikili dosya elde edilir.

İki yarım, IPC (süreçler arası iletişim) üzerinden haberleşir:

+---------------------------+ IPC +---------------------------+
| Rust Backend | <--------------> | React/TS Frontend |
| | (commands + | |
| - Chess engines (UCI) | events) | - Chessboard UI |
| - SQLite database | | - Analysis panels |
| - File I/O | | - Settings |
| - PGN parsing | | - Game tree navigation |
| - Position search index | | - TTS narration |
+---------------------------+ +---------------------------+
src-tauri/src/ src/

Rust, hızlı olması gereken veya sistem erişimi gerektiren her şeyi yönetir.

Ön yüzün çağırabileceği yaklaşık 50 komutu kaydeder, eklentileri (dosya sistemi, diyalog, HTTP, kabuk, günlükleme, güncelleyici) başlatır ve uygulama penceresini açar.

Komutlar bir makro ile tanımlanır:

#[tauri::command]
async fn get_best_moves(id: String, engine: String, ...) -> Result<...> {
// spawn UCI engine, return analysis
}

specta crate’i bu Rust fonksiyonlarından otomatik olarak TypeScript tür tanımlamaları üretir, böylece ön yüz hiçbir manuel çaba gerektirmeden tam tür güvenliği elde eder.

ModülNe Yapar
db/mod.rsDiesel ORM aracılığıyla SQLite veritabanı — oyun sorguları, oyuncu istatistikleri, içe aktarmalar, konum arama
game.rsCanlı oyun motoru — motor-insan ve motor-motor oyunlarını, zaman kontrollerini, hamle doğrulamayı yönetir
chess.rsMotor analizi — UCI motorlarını başlatır, en iyi hamle sonuçlarını olaylar aracılığıyla ön yüze aktarır
engine/UCI protokol uygulaması — süreç başlatma, stdin/stdout kanalları, çoklu PV desteği
pgn.rsPGN dosyası okuma/yazma/tokenize etme
opening.rsFEN’den açılış adı arama (uygulamaya gömülü ikili veri)
puzzle.rsLichess bulmaca veritabanı — bellek eşlemeli rastgele erişim
fs.rsDevam ettirilebilir dosya indirmeleri, çalıştırılabilir dosya izni ayarlama
sound.rsSes akışı için yerel HTTP sunucusu (Linux ses geçici çözümü)
tts.rsspeech-dispatcher (Linux) / yerel işletim sistemi konuşma API’leri aracılığıyla Sistem TTS’i ve KittenTTS sunucu yönetimi
oauth.rsLichess/Chess.com hesap bağlama için OAuth2 akışı
  • Her yerde asenkron: Tokio çalışma zamanı, engellemeyen G/Ç
  • Eşzamanlı durum: Motor süreçleri, veritabanı bağlantıları ve önbellekler için DashMap (eşzamanlı HashMap)
  • Bağlantı havuzu: r2d2, SQLite bağlantı havuzlarını yönetir
  • Bellek eşlemeli arama: Anlık sonuçlar için mmap’lenmiş ikili indeks üzerinden konum arama
  • Olay akışı: Rust, React’ın gerçek zamanlı olarak dinlediği olayları (en iyi hamleler, saat tikleri, oyun sonu) yayar

vite.config.ts yapılandırır:

  • Babel derleyicili React eklentisi
  • TanStack Router eklentisiroutes/ klasöründen otomatik olarak rota ağacı oluşturur
  • Vanilla Extract — sıfır çalışma zamanı CSS-in-JS
  • Yol takma adı: @, ./src ile eşleştirilir
  • 1420 portunda geliştirme sunucusu

Derleme akışı:

pnpm dev → Vite on :1420 + Tauri opens webview pointing to it
pnpm build → tsc (typecheck) → vite build (bundle to dist/) → tauri build (native binary)

Kök bileşen:

  • Tauri eklentilerini (günlük, süreç, güncelleyici) başlatır
  • Kalıcı atom’lardan kullanıcı tercihlerini yükler
  • Mantine kullanıcı arayüzü temasını ayarlar
  • Yönlendiriciyi kaydeder
  • Uygulama güncellemelerini kontrol eder

Jotai atom’ları (src/state/atoms.ts) — hafif reaktif durum:

KategoriÖrnekler
SekmelertabsAtom, activeTabAtom (çoklu belge arayüzü)
DizinlerstoredDocumentDirAtom, storedDatabasesDirAtom
Arayüz tercihleriprimaryColorAtom, fontSizeAtom, pieceSetAtom
MotorengineMovesFamily, engineProgressFamily (atomFamily ile sekme bazlı)
TTSttsEnabledAtom, ttsProviderAtom, ttsVoiceIdAtom, ttsVolumeAtom, ttsSpeedAtom, ttsLanguageAtom

atomWithStorage() ile tanımlanan atom’lar otomatik olarak localStorage’a kalıcı hale getirilir.

Karmaşık alan durumu için Zustand depoları:

  • src/state/store/tree.ts — oyun ağacı navigasyonu, hamle dallanması, notasyonlar, yorumlar. Değişmez güncellemeler için Immer kullanır.
  • src/state/store/database.ts — veritabanı görünümü filtreleri, seçili oyun, sayfalama

src/routes/ içinde dosya tabanlı yönlendirme:

routes/
__root.tsx # Root layout (AppShell, menu bar)
index.tsx # Home/dashboard
databases/ # Database browsing
accounts.tsx # Lichess/Chess.com accounts
settings.tsx # App preferences
engines.tsx # Engine management
GrupAmaç
boards/Satranç tahtası (chessground), hamle girişi, değerlendirme çubuğu, analiz gösterimi, terfi modalı, ok çizimi
panels/Yan paneller: motor analizi (BestMoves), veritabanı konum arama, notasyon düzenleme, oyun bilgisi, pratik modu
databases/Veritabanı arayüzü: oyun tablosu, oyuncu tablosu, detay kartları, filtreleme
settings/Tercih formları, motor yolları, TTS ayarları
home/Hesap kartları, içe aktarma arayüzü
common/Paylaşılan: TreeStateContext, malzeme gösterimi, yorum hoparlör simgesi
tabs/Çoklu sekme çubuğu

Specta, src/bindings/generated.ts içinde TypeScript bağlamları oluşturur:

// Auto-generated from Rust #[tauri::command] functions
export const commands = {
async getBestMoves(id, engine, tab, goMode, options) {
return await TAURI_INVOKE("get_best_moves", { id, engine, tab, goMode, options });
},
// ~50 more commands...
}

React bileşenleri bunları normal asenkron fonksiyonlar gibi çağırır:

import { commands } from "@/bindings";
const result = await commands.getBestMoves(id, engine, tab, goMode, options);

Gerçek zamanlı veriler için (motor analizi, saat tikleri, oyun hamleleri):

Rust: app.emit("best_moves_payload", BestMovesPayload { depth: 24, ... })
React: listen("best_moves_payload", (event) => updateBestMoves(event.payload))

Uygulama, sistem erişimi için birçok resmi eklenti kullanır:

EklentiAmaç
@tauri-apps/plugin-fsDosya okuma/yazma
@tauri-apps/plugin-dialogDosya seçiciler, mesaj kutuları
@tauri-apps/plugin-httpHTTP istemcisi (motor indirmeleri, bulut TTS)
@tauri-apps/plugin-shellUCI motorlarını çalıştırma
@tauri-apps/plugin-updaterOtomatik güncelleme kontrolleri
@tauri-apps/plugin-logYapılandırılmış günlükleme
@tauri-apps/plugin-osCPU/RAM algılama

En Parlant~, bir oyunun hamlelerini adım adım ilerlerken satranç hamlelerini ve yorumları sesli olarak okuyabilir. Bu bölüm, TTS sisteminin nasıl oluşturulduğunu — ön işleme hattını, sağlayıcı mimarisini ve önbellekleme stratejisini — açıklamaktadır. Kurulum talimatları için TTS menüsündeki TTS kılavuzlarına bakın.

Metinden konuşmaya, yazılı metni sesli sese dönüştürür. Modern TTS sistemleri, binlerce saatlik insan konuşması üzerinde eğitilmiş derin sinir ağları üzerine kuruludur. Model, metin (harfler, kelimeler, noktalama işaretleri) ile konuşmanın akustik özellikleri (perde, zamanlama, vurgu, nefes duraklamaları) arasındaki ilişkiyi öğrenir. Çıkarım zamanında metin gönderirsiniz ve bir ses dalga formu alırsınız.

İki geniş yaklaşım vardır:

  • Bulut TTS — metin uzak bir sunucuya (Google, ElevenLabs, vb.) gönderilir; sunucu GPU donanımında büyük bir sinir ağı çalıştırır ve ses döndürür. Mükemmel kalite, ancak internet bağlantısı gerektirir ve istek başına maliyeti vardır (çoğu sağlayıcı ücretsiz katmanlar sunsa da).

  • Yerel TTS — model doğrudan makinenizde çalışır. İnternet gerekmez, istek başına maliyet yoktur ve metniniz bilgisayarınızdan asla çıkmaz. Son dönem açık kaynak modeller (Kokoro ve Piper gibi) kalite farkını önemli ölçüde kapatmıştır.

TTS modellerinin perde arkasında nasıl çalıştığını merak ediyorsanız, HuggingFace (huggingface.co) keşfedebileceğiniz, indirebileceğiniz ve yerel olarak çalıştırabileceğiniz yüzlerce açık kaynak konuşma sentezi modeli barındırmaktadır. Hafif CPU dostu seçeneklerden en son teknoloji araştırma modellerine kadar geniş bir yelpazede modeller bulmak için “text-to-speech” araması yapın.

Temel TTS uygulaması src/utils/tts.ts dosyasında bulunur. Tek bir genel arayüz (speakText()) ve değiştirilebilir arka uçlar etrafında tasarlanmıştır. Uygulamanın geri kalanı hangi sağlayıcının aktif olduğunu bilmez veya umursamaz — sadece speakText() çağırır ve ses çıkar.

Beş sağlayıcı desteklenmektedir:

SağlayıcıTürArka Uç
ElevenLabsBulutREST API aracılığıyla sinir ağı sesleri. MP3 döndürür.
Google Cloud TTSBulutREST API aracılığıyla WaveNet sesleri. Base64 kodlu MP3 döndürür.
KittenTTSYerelRust arka ucu tarafından otomatik başlatılan paketlenmiş TTS sunucusu. Localhost üzerinden HTTP ile iletişim kurar.
OpenTTSYerelKendi sunucunuzda barındırılan TTS sunucusu. Birçok motoru destekler (espeak, MaryTTS, Piper, vb.).
System TTSYerelRust/Tauri komutları aracılığıyla işletim sistemi yerel konuşma motoru (Linux’ta speech-dispatcher, Windows’ta SAPI, macOS’ta AVSpeechSynthesizer).

Sağlayıcı seçimi tek bir Jotai atom’unda (ttsProviderAtom) saklanır. Sağlayıcı değiştirmek anlıktır — atom’u değiştirin ve bir sonraki speakText() çağrısı yeni arka uca yönlendirilir.

Zorluk: Satranç Notasyonu İngilizce Değildir

Section titled “Zorluk: Satranç Notasyonu İngilizce Değildir”

Satranç hamleleri Standart Cebirsel Notasyon (SAN) ile yazılır: Nf3, Bxe5+, O-O-O, e8=Q#. Bunu doğrudan bir TTS motoruna verirseniz anlamsız sonuçlar alırsınız — “Nf3”ü bir kelime gibi telaffuz etmeye çalışabilir veya “O-O-O”yu “oh oh oh” olarak okuyabilir.

Çözüm, satranç notasyonunu TTS motoruna ulaşmadan önce doğal dile çeviren bir ön işleme hattıdır:

SAN Input → Preprocessing → Spoken Output
─────────────────────────────────────────────────────
"Nf3" → sanToSpoken() → "Knight f3"
"Bxe5+" → sanToSpoken() → "Bishop takes e5, check"
"O-O-O" → sanToSpoken() → "castles queenside"
"e8=Q#" → sanToSpoken() → "e8 promotes to Queen, checkmate"

sanToSpoken() fonksiyonu, herhangi bir SAN dizesini bileşenlerine (taş, belirsizlik giderme, alma, hedef kare, terfi, şah/mat) ayırmak için regex kalıp eşleştirmesi kullanır ve bunları bir kelime dağarcığı tablosundan doğal dil kullanarak yeniden birleştirir.

Satranç kelime dağarcığı birçok dile çevrilmiştir (İngilizce, Fransızca, İspanyolca, Almanca, Japonca, Rusça, Çince, Korece ve daha fazlası). CHESS_VOCAB tablosu her terimi eşleştirir:

English: "Knight takes e5, check"
French: "Cavalier prend e5, échec"
German: "Springer schlägt e5, Schach"
Japanese: "ナイト テイクス e5, チェック"
Russian: "Конь берёт e5, шах"

Dil ayarı, ön işleme için hangi kelime dağarcığı tablosunun kullanılacağını ve TTS motorunun sentez için hangi sesi/aksanı kullandığını belirler.

Oyun notasyonları genellikle sesli okunduğunda çok kötü duyulacak PGN’e özgü işaretlemeler içerir:

Raw comment: "BLUNDER. 7.Nf3 was better [%eval -2.3] [%cal Gg1f3]"
After cleaning: "7, Knight f3 was better"

cleanCommentForTTS() fonksiyonu:

  1. PGN etiketlerini kaldırır: [%eval ...], [%csl ...], [%cal ...], [%clk ...]
  2. Yinelenen notasyon kelimelerini kaldırır (”??” zaten “Blunder” dediyse)
  3. Düzyazı içindeki satır içi SAN’ı genişletir: "7.Nf3 controls e5""7, Knight f3 controls e5"
  4. TTS motorlarının yanlış telaffuz ettiği satranç terimlerini düzeltir (ör. “en prise” → “on preez”)
  5. Düzyazıdaki taş kısaltmalarını genişletir: "R vs R""Rook versus Rook"

Yeni bir hamleye geçtiğinizde, buildNarration() üç kaynaktan tam sözlü metni bir araya getirir:

Move: "12, Knight f3, check." ← from sanToSpoken()
Annotation: "Good move." ← from annotation symbol (!)
Comment: "Developing with tempo." ← from cleanCommentForTTS()
Full narration: "12, Knight f3, check. Good move. Developing with tempo."

Bölümler arasındaki çift boşluk, TTS motorlarına doğal bir nefes duraklaması verir.

Bulut TTS çağrıları para ve zaman maliyetine sahiptir (yaklaşık 200-500ms gidiş-dönüş). Aynı sesi yeniden getirmekten kaçınmak için, üretilen her ses klibi bellekte blob URL olarak önbelleğe alınır:

Cache key: "elevenlabs:pNInz6obpgDQGcFmaJgB:en:12, Knight f3, check."
Cache value: blob:http://localhost/abc123 (the MP3 audio in browser memory)

Önbellek isabet ettiğinde oynatma anlıktır. Önbellek provider:voice:language:text ile anahtarlanır, bu nedenle ses veya dil değiştirmek ayrı girdiler oluşturur.

Çok sayıda notasyon içeren oyunlar için, tüm oyun ağacını arka planda önceden önbelleğe alabilirsiniz. Uygulama her düğümü gezer, anlatım metnini oluşturur ve siz gezinmeye başlamadan önce önbelleği doldurmak için sıralı API çağrıları yapar.

Hızlı ok tuşu navigasyonu bir sorun yaratır: kullanıcı hızla 5 kez ileri giderse, birbirleriyle çakışan 5 ses klibi istemezsiniz. Çözüm bir nesil sayacıdır:

const thisGeneration = ++requestGeneration;
// ... fetch audio ...
if (thisGeneration !== requestGeneration) return; // stale — discard

Her yeni speakText() çağrısı sayacı artırır ve AbortController aracılığıyla devam eden HTTP isteğini iptal eder. Ses geldiğinde, kendi neslinin hâlâ güncel olup olmadığını kontrol eder. Kullanıcı zaten ilerlemiş durumdaysa, yanıt sessizce atılır. Bu, hamleler arasında hızla tıklarken bile temiz, kesintisiz ses sağlar.

TTS’in Uygulamaya Entegrasyon Noktaları

Section titled “TTS’in Uygulamaya Entegrasyon Noktaları”

Entegrasyon noktaları asgaridir:

DosyaNe Olur
src/state/store/tree.tsHer navigasyon fonksiyonu (goToNext, goToPrevious, vb.) stopSpeaking() çağırır. Otomatik anlatım açık olduğunda, goToNext ayrıca speakMoveNarration() çağırır.
src/components/common/Comment.tsxHer yorumun yanındaki hoparlör simgesi, o yorum için TTS’i manuel olarak tetiklemenizi sağlar.
src/components/settings/TTSSettings.tsxSağlayıcı, ses, dil, ses düzeyi, hız seçimi ve API anahtarı girişi için ayarlar arayüzü.

TTS kapalı olduğunda bu kodların hiçbiri çalışmaz. Uygulama, üst akış En Croissant ile aynı şekilde davranır.


User clicks "Analyze"
→ React calls commands.getBestMoves(position, engine, settings)
→ Rust spawns UCI engine process, sends position via stdin
→ Engine writes "info depth 18 score cp 45 pv e2e4 ..." to stdout
→ Rust parses UCI output, emits BestMovesPayload event
→ React's EvalListener receives event, updates atoms
→ UI re-renders: eval bar moves, best move arrows appear
→ User clicks "Stop" → commands.stopEngine() → Rust sets AtomicBool flag
User reaches a position on the board
→ React calls commands.searchPosition(fen, gameQuery)
→ Rust queries memory-mapped binary search index
→ Returns: PositionStats (wins/losses/draws) + matching games
→ React renders DatabasePanel with results table
User steps forward with arrow key
→ tree.ts calls stopSpeaking(), then checks isAutoNarrateEnabled()
→ Calls speakMoveNarration(san, comment, annotations, halfMoves)
→ buildNarration() assembles text:
sanToSpoken("Nf3+") → "Knight f3, check"
annotationsToSpoken(["!"]) → "Good move."
cleanCommentForTTS(comment) → strips [%eval], expands inline SAN
→ speakText() checks audioCache
HIT → play blob URL instantly
MISS → fetch from provider API → cache as blob URL → play
→ HTMLAudioElement.play() with volume and playbackRate from atoms

en-parlant/
├── src-tauri/ # RUST BACKEND
│ ├── src/
│ │ ├── main.rs # Entry, command registration, plugins
│ │ ├── chess.rs # Engine analysis
│ │ ├── game.rs # Live game management
│ │ ├── db/ # SQLite database (largest module)
│ │ ├── engine/ # UCI protocol
│ │ ├── pgn.rs # PGN parsing
│ │ ├── puzzle.rs # Puzzle database
│ │ ├── opening.rs # Opening lookup
│ │ └── tts.rs # System TTS + KittenTTS management
│ ├── Cargo.toml # Rust dependencies
│ ├── tauri.conf.json # Tauri config
│ └── capabilities/main.json # Security permissions
├── src/ # REACT/TS FRONTEND
│ ├── App.tsx # Root component
│ ├── state/
│ │ ├── atoms.ts # Jotai atoms (all app state)
│ │ └── store/tree.ts # Game tree (Zustand + TTS hooks)
│ ├── routes/ # TanStack Router (file-based)
│ ├── components/
│ │ ├── boards/ # Chessboard + analysis
│ │ ├── panels/ # Side panels
│ │ ├── databases/ # DB browsing UI
│ │ ├── common/ # Comment display (with TTS speaker icon)
│ │ └── settings/ # Preferences, TTS settings
│ ├── utils/
│ │ ├── chess.ts # Game logic
│ │ ├── tts.ts # TTS engine (SAN-to-spoken, caching, 5 providers)
│ │ └── treeReducer.ts # Tree data structure
│ ├── bindings/ # Auto-generated TS from Rust
│ └── translation/ # i18n (13 languages)
├── docs/ # Bundled documentation (shown in Help menu)
├── vite.config.ts # Build config
└── package.json # Frontend deps

  1. Ağır işi Rust yapar — motorlar, veritabanı, dosya G/Ç, PGN ayrıştırma. React hiçbir zaman doğrudan dosya sistemine dokunmaz veya süreç başlatmaz.

  2. Sınır boyunca tür güvenliği — Specta, Rust yapılarından TypeScript türleri oluşturur, böylece bir Rust komutu imzasını değiştirirse TypeScript derlemesi anında hata verir.

  3. İki durum sistemi — Basit reaktif durum için Jotai (ayarlar, arayüz tercihleri, sekme bazlı motor durumu), karmaşık alan durumu için Zustand (dallanma ve değişmez güncellemeler içeren oyun ağacı).

  4. TTS bir ön işleme sorunudur — zor kısım bir konuşma API’si çağırmak değil, satranç notasyonunu ve PGN işaretlemesini birçok dilde temiz, doğal ses veren metne dönüştürmektir. Asıl iş sanToSpoken() ve cleanCommentForTTS() hatlarında gerçekleşir.

  5. Beş sağlayıcı, tek arayüz — ses ister ElevenLabs’tan, ister Google Cloud’dan, ister KittenTTS’ten, ister OpenTTS’ten, ister işletim sisteminizin konuşma motorundan gelsin, uygulamanın geri kalanı yalnızca speakText() çağırır. Sağlayıcı seçimi tek bir atom değişikliğidir.

  6. Derleme, tek bir ikili dosya üretirsrc-tauri/target/release/en-parlant konumunda Rust arka ucunu ve Vite ile derlenen ön yüz varlıklarını paketler.