Windows XP, tarayıcıda
Açılış ekranından Mayın Tarlası'na kadar, uygulamaları gerçekten çalışan bir Windows XP masaüstü.
Tarayıcıda çalışan bir Windows XP (Luna) masaüstü. Önce açılış ekranı geliyor, sonra Hoş Geldiniz ekranından oturum açıyorsun. Pencereleri sürükleyip boyutlandırabilirsin; uygulamaların hepsi de tarayıcıda saklanan aynı dosya sistemini kullanıyor.

- Durum
- Tamamlandı
- Kategori
- Tarayıcıda OS
- Güncelleme
- 5 Ekim 2026
- Yığın
- React 19TypeScriptBunxp.cssZustand
- Bağlantılar
- Kaynak kod
Tarayıcıda çalışan bir Windows XP (Luna) masaüstü. O tanıdık ilerleme çubuklu açılış ekranından sonra Hoş Geldiniz ekranı geliyor; kullanıcı kutucuğuna tıkladığında oturum açılıyor. Pencereleri sürükleyip boyutlandırabilir, görev çubuğuna küçültebilirsin. Açılış, oturum açma, oturum kapatma ve kapanış sesleri Web Audio ile üretiliyor. Bekleme süresi dolunca Blank, Marquee ya da Starfield ekran koruyucusu başlıyor. Oturumu Kapat ya da Bilgisayarı Kapat penceresi açıldığında da arkadaki masaüstü, tıpkı XP'deki gibi griye dönüyor.
Gezgin, Notepad, Paint ve Komut İstemi tarayıcıda saklanan aynı sanal diski kullanıyor, yani birinde oluşturduğun dosya ötekilerde de karşına çıkıyor. Projeyi React 19, TypeScript, Bun, xp.css, react-rnd ve zustand ile yazdım. Arka uç yok; sunucu yalnızca uygulama paketini, isteğe bağlı duvar kâğıdını ve ses dosyalarını veriyor. Arayüz, gerçek XP gibi İngilizce. Bu bir hayran çalışması; Microsoft ile bir bağlantısı yok ve projede Microsoft'a ait hiçbir dosya yok. Bütün simgeleri ve açılış logosunu SVG olarak sıfırdan çizdim. Sunucu public/wallpapers/ altında bir Bliss dosyası bulursa onu kullanıyor, bulamazsa yerine çizilmiş bir tepe çıkıyor.
Masaüstünde neler çalışıyor
Masaüstü simgeleri bir ızgaraya oturuyor. Fareyle alan seçebilir, simgeleri sürükleyip taşıyabilir, F2 ile yeniden adlandırabilirsin. New, Arrange Icons By, Refresh ve Properties içeren sağ tık menüsü de çalışıyor. Görev çubuğunun bildirim alanında Güvenlik Merkezi, ağ ve ses simgeleriyle saat duruyor; ses simgesine tıklarsan ses kapanıyor. Geri Dönüşüm Kutusu'na attığın öğeleri de geri yükleyebilirsin.
- Windows Gezgini: görev bölmesi, Geri/İleri/Yukarı, adres çubuğu, Simgeler, Liste ve Ayrıntılar görünümleri, kes-kopyala-yapıştır.
- Notepad: XP tarzı dosya penceresiyle aç ve kaydet, Word Wrap, saat/tarih ekleme, kaydedilmemiş değişiklik uyarısı.
- Paint: kalem, fırça, sprey, silgi, doldurma, renk seçici, metin ve şekil araçları; çizim PNG olarak Resimlerim'e kaydediliyor.
- Komut İstemi: ortak dosya sistemi üzerinde
CD,DIR,TYPE,MD,DEL,STARTgibi komutlar ve komut geçmişi. - Internet Explorer 7: sekmeler, geçmiş, arama kutusu ve sık kullanılanlar.
- Hesap Makinesi ve Mayın Tarlası: bellek tuşlu standart mod; dört zorluk seviyesi ve en iyi süreler.
Sanal disk ve üç store
Bütün durum üç zustand store'unda duruyor. useFs sanal diskin tamamı: Record<id, FsNode> biçiminde düz bir tablo. Her düğüm yalnızca parentId tutuyor; yollar, alt öğeler ve ağaç bu bağlardan türetiliyor. Ad karşılaştırması da Windows'taki gibi büyük/küçük harfe duyarsız. Oluşturma, taşıma, silme ve geri yükleme tablonun bir kopyası üzerinde çalışıp sonucu tek bir set ile yazıyor. Böylece işlem yarıda hata verse bile disk bozulmuyor; arayüz de hatayı yakalayıp bir XP mesaj kutusunda gösteriyor. Silinen öğe BIN altına taşınıyor ve eski yerini origParentId alanında saklıyor. SYSTEM içindeki klasörler ise "Access is denied" yanıtı veriyor.
useWindows açık pencerelerin konumunu, boyutunu, z-sırasını, durumunu ve oturum aşamasını tutuyor. Etkin pencereyi ayrıca saklamıyorum; küçültülmemiş pencerelerden en üstte olanı etkin sayılıyor. useSettings ise duvar kâğıdını, ekran koruyucuyu, sesi, simge konumlarını ve Mayın Tarlası rekorlarını tutuyor. zustand'ın sığ birleştirmesi sayesinde sonradan eklenen bir ayar, eski kayıtlarda kendi varsayılanına düşüyor. Dosyalar ve ayarlar localStorage'da, açık pencereler sessionStorage'da duruyor. Yani sekmeyi kapattığında pencereler kayboluyor ama disk yerinde kalıyor.
Pencereler, ızgara ve Promise dönen iletişim kutuları
Oturum boot → login → desktop → logoff/shutdown/restart aşamalarından geçiyor. Masaüstü dışındaki her ekranı Session.tsx çiziyor; ilgili sesi çalıp bir zamanlayıcıyla sonraki aşamaya geçiyor. Zamanlayıcıyı React 19'un useEffectEvent'iyle yazdım, böylece effect yeniden çalışmadan en güncel callback çağrılıyor. Her pencere memo ile sarılmış bir react-rnd öğesi; bir pencereyi taşımak da yalnızca o pencerenin Win nesnesini değiştiriyor.
Simgeler 75 piksellik bir ızgaraya diziliyor. grid.ts içindeki saf fonksiyonlar her simgeyi kayıtlı hücresine ya da ilk boş hücreye yerleştiriyor. Dolu bir hücreye bıraktığın simge için en yakın boş hücre, halka halka genişleyerek aranıyor. Masaüstü ile Gezgin, açma, pano, silme ve sağ tık menüsü için aynı dosya katmanını paylaşıyor.
İletişim kutuları (msgBox, confirmBox, formBox, fileDialog) ve sağ tık menüleri aynı kalıbı izliyor: fonksiyon bir store'a kayıt ekleyip bir Promise dönüyor, ekrandaki tek "Host" bileşeni de bu kaydı çizip kullanıcının seçimiyle Promise'i çözüyor. Bu sayede uygulama kodunda doğrudan if (await confirmBox(...)) yazabiliyorum; projede en sevdiğim kalıp da bu.
Her uygulama src/lib/registry.ts içinde başlığı, simgesi, bileşeni ve açtığı dosya uzantılarıyla kayıtlı; id'si de aynı zamanda Çalıştır kutusuna ve Komut İstemi'ne yazacağın komutun adı.
Uygulamaların içi
Hesap Makinesi, Mayın Tarlası, Komut İstemi ve Paint'in mantığını React'ten ayrı saf modüllere koydum. Hesap Makinesi'nde press(state, key) bir durum makinesi. Mayın Tarlası'nda mayınlar ancak ilk hücreyi açtığında yerleştiriliyor, bu yüzden ilk tıklaman her zaman güvenli. Komut İstemi'nde exec(line, cwd) komutu sanal diskte çalıştırıp çıktı satırlarını ve arayüz için talimatları döndürüyor. Paint'te bir ana tuval ve şeffaf bir önizleme tuvali var. Kalem ve dolgu, kenar yumuşatması olmadan doğrudan piksel basıyor (Bresenham çizgisi, satır tabanlı flood fill); dosyalar sanal diske PNG data URL olarak yazılıyor.
Internet Explorer'da her sekmenin kendi geçmişi ve yükleme durumu var. Siteler sandbox'lı bir <iframe> içinde açılıyor. 15 saniye içinde yüklenmeyen sayfa hata sayfasına düşüyor; çerçevede açılmayı reddeden siteler için de onları gerçek tarayıcı sekmesinde açan bir bağlantı çıkıyor. Seslerde önce public/sounds/ altındaki dosya deneniyor, dosya yoksa ses Web Audio osilatörleriyle sentezleniyor.
Sunucu bir Bun.serve. index.html'i doğrudan import ediyor, Bun da sayfanın TSX ve CSS dosyalarını istek geldiğinde paketliyor. xp.css'teki birkaç seçici Bun'ın katı CSS ayrıştırıcısına takıldığı için onları yükleme sırasında düzelten küçük bir eklenti yazdım. Bir tuzak da çıktı: zustand persist'in set'i ve Chrome'un scrollIntoView'u Promise döndürüyor. Effect bunu geri döndürürse React onu bozuk bir cleanup sanıp hata fırlatıyor. O yüzden useEffect gövdelerini her zaman süslü parantezle yazıyorum.