npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nsupp/desktop

v0.1.0

Published

nsupp desktop shell — Electron module (corner icon or embedded) plus the bridge contract and host examples for WebView2, WebKitGTK and Tauri.

Readme

@nsupp/desktop

Masaüstü uygulamalarına nsupp destek sohbetini ekleyen kabuk katmanı: Electron modülü (iki kip)

  • köprü sözleşmesi + WebView2 / WebKitGTK / Tauri host örnekleri.

Durum: deneysel. Paket ve Windows örnekleri artık gerçek bir Windows 11 makinesinde DERLENİYOR (aşağıda ölçümler), ama hiçbiri gerçek bir pencerede gözle doğrulanmadı — derlenmek çalışmak değildir. Gözle doğrulanana kadar "destekleniyor" demiyoruz. Bkz. Doğrulama.

Ne GÖNDERİYORUZ, ne GÖNDERMİYORUZ

| | | |---|---| | ✅ Electron modülü (köşe ikonu / gömülü kip) | Windows · Linux · macOS | | ✅ Köprü sözleşmesi + kaynak kapısı + önyükleme üreteci (JS) | her kabuk aynı kopyayı kullanır | | ✅ Host örnekleri: WebView2 (C#), WebKitGTK (C), Tauri (Rust) | ornekler/ | | ❌ Windows için derlenmiş yerel SDK | yazılmadı — gerekmiyor (aşağıda gerekçe) | | ❌ Linux için derlenmiş yerel SDK | yazılmadı — WebKitGTK ABI çatallı, köprü zaten JS |

Neden yerel SDK yok? Sohbetin görünümü ve işlevinin tamamı web widget'ıdır (~4900 satır); yerel arayüzü ikinci kez yazma yolu bu üründe zaten ölçülerek terk edildi. Kabuğun işi ~50 satırdır: gezinme kilidi, dış-bağlantı kapısı, jeton saklama, önyükleme enjeksiyonu. Linux'ta WebKitGTK'nın register_script_message_handler'ı sayfaya birebir window.webkit.messageHandlers.nsupp.postMessage verdiği için widget'ın mevcut köprü dalı hiçbir değişiklik olmadan çalışır; Windows'ta ise kabuk, widget'ın bildiği adı (window.NsuppNative) WebView2'nin chrome.webview kanalının üstüne tanımlar. widget.js'e bu program kapsamında tek satır eklenmemiştir.

Kurulum (Electron)

npm i @nsupp/desktop

Electron 30+ gerekir: gömülü kip WebContentsView kullanır (Electron 30.0.0'da eklendi, BrowserViewın yerini aldı). electron bu paketin isteğe bağlı peer bağımlılığıdır ve paket onu kendisi import ETMEZ — modülü siz verirsiniz (test edilebilirliğin ve sürüm bağımsızlığının şartı).

① Köşede ikon (balon)

import * as elektron from 'electron';
import { NsuppElectronChatController, NsuppElectronChatPresenter } from '@nsupp/desktop';

const kontrolcu = new NsuppElectronChatController({
  elektron,
  config: { apiBase: 'https://api.nsupp.com', publicKey: 'pk_…', appKey: 'ak_…' },
});
const sunum = new NsuppElectronChatPresenter(kontrolcu, { kip: 'balon' });
sunum.start();          // köşede 60×60 ikon
// sunum.present() / dismiss() / toggle()
await sunum.reset();    // çıkışta: jeton + WebView deposu silinir, pencereler kapanır

kip: 'panel' seçerseniz ikon çizilmez; sohbeti kendi menünüzden present() ile açarsınız.

② Kendi arayüzünüze gömülü

import { NsuppElectronChatView } from '@nsupp/desktop';

const gorunum = new NsuppElectronChatView(kontrolcu);
gorunum.mount(pencere, { x: 0, y: 0, width: 360, height: 520 });
// pencere yeniden boyutlanınca: gorunum.setBounds({ … })
await gorunum.reset();  // çıkışta: görünüm sökülür, jeton + WebView deposu silinir

Gömülü kipte kontrolcu.reset()'i doğrudan çağırmayın: pencere sizindir, kapatacak bir pencere yoktur ve görünüm sökülmediği için WebContentsView sıfırlanmış oturumun sohbetini ekranda göstermeye devam eder. gorunum.reset() önce söker, sonra siler.

Gömülü kip iframe değildir: /widget/:publicKey/app sayfası frame-ancestors 'none' + X-Frame-Options: DENY taşır (bilinçli — o sayfa aktif ziyaretçi oturumu taşır ve çerçevelenmesi clickjacking yüzeyi açar). Bunun yerine ayrı bir üst-düzey WebContentsView kullanılır. O sayfayı çerçevelenebilir listeye eklemeyin.

Preload

Modül preload dosyasını paket kökünden çözer (preload.cjs). Paketleyici (asar/webpack) yolu değiştiriyorsa preloadYolu ile kendi kopyanızın yolunu verin — dosyayı değiştirmeyin, üç guard'ı orada yaşıyor.

Görünüm ve ayarlar

Widget'ın görünümü, metinleri ve bölümleri (Makaleler, ön-sohbet, hamburger menüsü, marka satırı) yalnız çalışma alanı ayarından gelir; kabuk hiçbirini geçersiz kılmaz. Pencere ölçüleri web widget'ıyla eşlenmiştir ve tek kaynaktan okunur: src/olculer.json (panel 360×520, ikon 60, kenar 20, panel alt boşluğu 76, dikey pay 120). olculer-kayma.test.ts bu sayıları widget.js kaynağıyla karşılaştırır — CSS değişirse test kırmızı yanar. Panel, çalışma alanına web'deki max-width/max-height kurallarıyla AYNI biçimde sığdırılır (panelBoyutu(); iOS/macOS NsuppOlculer.panelBoyutu ile aynı formül).

Köşe ikonunun rengi çalışma alanından ÇEKİLMEZ (ikon, widget yapılandırması indirilmeden önce görünür; rengi ağdan beklemek ikonun geç ve renk atlayarak belirmesi olurdu). Sistem vurgu rengi kullanılır; Linux'ta sistem vurgu rengi API'si YOKTUR, orada nötr bir ton kullanılır ve balonRengi: '#rrggbb' ile kendi renginizi verebilirsiniz.

Diğer kabuklar (WebView2 · WebKitGTK · Tauri)

ornekler/ altındaki üç dosya, aynı kapıları (gezinme kilidi · dış bağlantı · köprü kaynak doğrulaması · oturum nesli · medya izni) kendi platformlarında kurar. JS tarafı için bu paketi kullanın — kaynak kapısını ve önyükleme scriptini yeniden yazmayın:

import { kaynakCoz, hostAdresi, onyuklemeJS } from '@nsupp/desktop';

const kaynak = kaynakCoz('https://api.nsupp.com');
const adres = hostAdresi('https://api.nsupp.com', 'pk_…');
const js = onyuklemeJS({ kaynak, appKey, visitorToken, kopru: 'webview2' }); // Linux/macOS: 'hazir'

Güvenlik — platform platform

| Kontrol | Electron | WebView2 | WebKitGTK | |---|---|---|---| | Gezinme kilidi (ana çerçeve) | will-navigate | NavigationStarting | decide-policy | | Gezinme kilidi (alt çerçeve) | will-frame-navigate | FrameNavigationStarting | aynı sinyal (çerçeve ayrımı yok) | | Yeni pencere → dışarı | setWindowOpenHandler + shell.openExternal | NewWindowRequested | NEW_WINDOW_ACTION | | Köprüde kaynak doğrulaması | ✅ senderFrame.url + ana çerçeve kimliği | ✅ args.Source | ❌ yok (aşağıya bakın) | | Oturum verisi | bellek-içi partition + clearStorageData() | ClearBrowsingDataAsync() | website_data_manager_clear() | | Çıkış nesli (epok) | ✅ webContents başına (bu paket) | ✅ NavigationId başına (yalnız örnekte) | ✅ load-changed damgası (yalnız örnekte) | | Medya izni (mikrofon) | setPermissionRequestHandler + setPermissionCheckHandler | PermissionRequested (+ FrameCreated) | permission-request (+ enable-media-stream) |

Ortak kurallar:

  • Kaynak karşılaştırması ayrıştırılmıştır (şema+host+port, varsayılan port normalize). https://[email protected]/x ve https://api.nsupp.com.evil.example/x metin öneki testini geçer, kaynak kapısını GEÇMEZ (altın vektör olarak testte kilitlidir).
  • Dış bağlantı yalnız pencere-açma yolundan sistem tarayıcısına gider (izinli şemalar: http, https, mailto, tel). Gezinme yolunda tarayıcı AÇILMAZ: gezinme kullanıcı niyetini bildirmez ve niyetsiz bir yönlendirmenin varsayılan tarayıcıyı açması kimlik-avı yüzeyidir.
  • Sohbet gövdeleri diske yazılmaz (bellek-içi WebView oturumu). Ziyaretçi jetonu ise kabuk tarafından kalıcı saklanır — saklanmasaydı her açılış yeni ziyaretçi üretir ve geçmiş kaybolurdu. Jeton anonim oturum tanıtıcısıdır, kimlik sırrı değildir; reset() onu siler.

Medya izni (Electron) — kapı bir yüzeyi AÇMAZ, DARALTIR

Widget'ın sesli mesajı web'de eksiksiz çalışır, çünkü izni tarayıcı sorar. WebView'da soran yoktur: kararı barındıran uygulama verir. Electron'un bu karardaki varsayılanı — resmî güvenlik dokümanı, "Handle session permission requests from remote content" — tüm izin isteklerini otomatik onaylamaktır. Yani kanca kurulmadan önceki durum "mikrofon kapalı" değil, "mikrofon da kamera da konum da bildirim de uzak içeriğe açık"tı.

NsuppElectronChatController kurulurken iki kancayı da bağlar ve ikisi de aynı kararı verir:

  • setPermissionRequestHandler — gerçek yakalama isteği. Detay mediaTypes taşır (çoğul, dizi).
  • setPermissionCheckHandler — izin sorgusu. Detay mediaType taşır (tekil dize, unknown olabilir). İki kanca ayrı çağrılır; resmî sıralama önce kontrol, reddedilirse istektir — yalnız birini kurmak diğerinin varsayılanını yürürlükte bırakır.

Kapı üç koşulu birlikte arar, ve media içinde yalnız audio geçer:

| Koşul | Sonuç | |---|---| | İzin media değil (kamera, konum, bildirim, MIDI, display-capture, fileSystem…) | RET | | Kaynak bizim origin'imiz değil (ayrıştırılmış şema+host+port) | RET | | İstek ana çerçeveden gelmiyor (isMainFrame !== true) | RET | | mediaTypes içinde audio dışında bir tür var (video, ['audio','video']) | RET | | Tür hiç bildirilmemiş ya da unknown | RET (sesin istendiği kanıtlanamaz) | | Bizim ana çerçevemiz + yalnız audio | İZİN |

Kanca session.defaultSessiona değil, yalnız kendi bellek-içi partition'ımıza (nsupp-chat) kurulur — satıcının kendi pencerelerinin kamerası/konumu bizim "yalnız ses" kuralımıza takılmaz. Her ret sebebiyle birlikte onHata'ya yazılır: "mikrofon düğmesine bastım, hiçbir şey olmuyor" tek başına teşhis edilemez.

macOS'ta kod tek başına YETMEZ (satıcının işi). Oturum kancası Chromium katmanını açar; işletim sistemi katmanını açmaz. macOS 10.14+ mikrofon için TCC onayı ister ve uygulamanın Info.plist'inde NSMicrophoneUsageDescription yoksa istem hiç gösterilmez — sesli mesaj sessizce ölür. Paketleyicinizde bu anahtarı tanımlayın; durumu systemPreferences.getMediaAccessStatus('microphone') ile okuyabilir, istemi systemPreferences.askForMediaAccess('microphone') ile gösterebilirsiniz (yalnız macOS; daha önce reddedilmişse istem gösterilmez, kullanıcı Sistem Ayarları'ndan değiştirmelidir). Bu paket bu çağrıyı sizin adınıza yapmaz: TCC istemi uygulamanın tamamına aittir ve ne zaman gösterileceği satıcının ürün kararıdır.

Kapının kablolaması electron-kablolama.test.ts içinde sahte bir oturumla sürülerek pinlidir (kanca gerçekten kuruldu mu, ['audio','video'] reddedildi mi, ret onHata'ya yazıldı mı). Yine de gerçek bir Electron penceresinde mikrofonla ölçülmedi — yeşil test "çalışıyor" demek değildir.

reset() GERİ ALINAMAZ — oturum nesli

reset() asenkrondur (clearStorageData bir Promise döner) ve sayfa, pencereler kapanana kadar yaşamaya devam eder. O aralıkta sayfadan çıkan bir jeton mesajı bütün güvenlik kapılarını geçer — kaynağı bizim, gönderen ana çerçeve — ve jetonu geri yazarak çıkışı sessizce geri alırdı: kullanıcı çıkış yaptığını sanarken bir sonraki açılışta aynı ziyaretçi oturumu geri gelirdi. Kaynak ve çerçeve kapıları bunu yakalayamaz; yakalayan tek şey nesildir.

Bu yüzden Electron denetleyicisi bir oturum nesli (epok) tutar: reset() nesli senkron olarak, ilk satırda artırır ve kablolanmış her webContents kablolandığı nesli taşır. Nesli tutmayan gönderenin mesajı yok sayılır (ve sebebi onHata'ya yazılır — sessiz reddetme teşhis edilemez). Sıfırlamadan sonra açılan pencere yeni nesille kablolanır, normal çalışır.

ornekler/webview2-host.cs aynı korumayı Windows tarafında kurar (kopyalayanın referansı odur): nesil Sifirlanın ilk satırında, senkron artar; her gezinme NavigationStartingte başladığı nesille işaretlenir ve ContentLoading ekrandaki belgeye o damgayı koyar — sıfırlamadan ÖNCE başlamış bir gezinme sonradan yüklense bile bayat sayılır. Önyükleme script'i de Sifirlada sökülüp jetonsuz hâliyle yeniden eklenir (script bir kopyadır: tazelenmezse eski jetonu taşır).

ornekler/webkitgtk-host.c aynı korumayı Linux'ta kurar: nesil nsupp_sifirlanın ilk satırında, senkron artar; load-changed → WEBKIT_LOAD_STARTED yüklemenin başladığı nesli saklar ve WEBKIT_LOAD_COMMITTED ekrandaki belgeye o damgayı koyar (damgasız/tanınmayan belge bayat sayılır). WebKitGTK'da gezinme kimliği yoktur; tek WebView'da aynı anda tek provisional yükleme olduğu için tek bekleme yuvası yeterlidir. Mesaj anında gönderen belge ayırt edilemediğinden (aşağıdaki "bilinen sınır") kapı "ekrandaki belge güncel nesle ait mi" sorusuna indirgenir.

ornekler/tauri-host.rs damgayı pencere başına koyar: initialization_script (jetonu taşıyan kopya) pencere kurulurken sabitlendiği için o pencerede doğan her belge, pencerenin kurulduğu nesle aittir; damgayı on_page_load (PageLoadEvent::Started) yazar. Çıkış nesli artırır, pencereyi destroy() eder ve jetonsuz script'le yeniden kurar — yalnız reload etmek eski jetonu sayfaya geri enjekte ederdi.

Linux örneği (webkitgtk-host.c) artık DERLENİYOR ve ÇALIŞIYOR — gerçek WebKitGTK 2.50.6 / GTK 3.24.38 üzerinde ölçüldü (Xvfb'li konteyner): köprü kuruluyor, panel açılıyor, ziyaretçi jetonu depoya yazılıyor. O ölçüm bir kusur da buldu: enjeksiyon izin-listesi kalıbı origin/* biçiminde kuruluyordu ve WebKit kalıpları host'ta PORT kabul etmediği için özel portlu her kurulumda script SESSİZCE hiç enjekte edilmiyordu (bkz. dosya içindeki gerekçe).

Windows örnekleri (webview2-host.cs, tauri-host.rs) gerçek bir Windows 11 makinesinde DERLENDİ — her ikisinin gerektirdiği paketler artık dosya başlıklarında yazılı (bu ölçüm sırasında Tauri örneğinin iki bağımlılığının hiç yazılmadığı ortaya çıktı: urlencoding ve tauri-plugin-opener; kopyalayan cannot find module or crate ile duvara toslardı). webkitgtk-host.c HÂLÂ derlenmedi (Linux makinesi yok). Kopyaladıktan sonra en az iki yönü kendiniz ölçün: çıkıştan sonra eski sayfanın jetonu yazılmamalı, çıkıştan sonra açılan sayfanın jetonu yazılabilmeli.

Medya izni (örnek host'lar) — WebView2 · WebKitGTK · Tauri

Electron tarafı için yukarıdaki bölüme bakın; varsayılanı terstir (Electron işlenmemiş izni otomatik ONAYLAR, aşağıdaki üç platform ise REDDEDER). Kapının kendisi her yerde aynı: aynı üç koşul, aynı deny-by-default.

Widget'ın sesli mesajı web'de eksiksiz çalışır: mikrofon düğmesi (çalışma alanındaki allowVoiceMessage ayarına bağlı) → getUserMedia({audio:true}) → MediaRecorder → 2 dk tavanı → /upload → sohbette <audio controls>.

Tarayıcıda izni tarayıcı sorar; WebView'da soracak kimse yoktur. Bu üç platformda kabuk izni işlemezse varsayılan reddetmektir — yani özellik hata bile vermeden, sessizce ölür. Örnek host'lar bu kapıyı kurar ve izni kör vermez; üç koşul birlikte aranır:

  1. isteği yapan belge bizim origin'imizde mi,
  2. istenen izin mikrofon mu — kamera, konum, bildirim, MIDI vb. reddedilir (widget yalnız sesli mesaj için mikrofon ister; fazlasını açmak gereksiz yüzeydir),
  3. ana çerçeve mi.

Reddedilen izin sessiz kalmaz: sebebi teşhis kanalına yazılır (Teshis / h->teshis / ayar.teshis). Bu depoda "sessiz kilit = teşhis edilemez hata" dersi kayıtlıdır — mikrofon açılmadığında kullanıcı "bozuk" der, geliştirici hiçbir iz bulamaz.

| | WebView2 (C#) | WebKitGTK (C) | Tauri (Rust) | |---|---|---|---| | ① kaynak | ✅ args.Uri | ⚠️ ekrandaki belgenin URI'si | ⚠️ Linux: ekrandaki belge · ✅ Windows: args.Uri | | ② yalnız mikrofon | ✅ PermissionKind | ✅ is_for_audio_device + video reddi | ✅ aynı | | ③ ana çerçeve | ✅ FrameCreated + Handled | ❌ uygulanamaz | ❌ uygulanmıyor |

Üç uyarı, örnek dosyaların içinde de gerekçesiyle yazılıdır:

  • WebKitGTK'da enable-media-stream varsayılanı FALSE. Açılmazsa sayfada navigator.mediaDevices hiç oluşmaz: izin kancası tek başına yetmez, düğme izin sorulmadan düşer. İkisi birlikte gerekir.
  • ③ koşulu WebKit'te uygulanamaz. permission-request sinyali gönderen çerçeveyi bildirmez (imzasında ne origin ne frame vardır) — köprü mesajındaki bilinen sınırın aynısı. Taşıyıcı kontrol gezinme izin-listesidir; artakalan risk kendi origin'imizden türeyen bir alt çerçevedir ve o çerçeve zaten sayfayla aynı origin'de olduğu için sayfanın deposuna da erişir. "Alt çerçeve reddediliyor" diye yazmayın; Linux'ta reddedilmiyor.
  • Yalnız ses. getUserMedia({audio:true, video:true}) isteği "ses için mi" sorusuna da true der; bu yüzden video ayrıca yasaklanır — yoksa aynı izinle kamera da açılırdı.

Windows gizlilik ayarı: bu kapı yalnız web tarafını açar. İşletim sistemi düzeyinde uygulamanın mikrofon erişimi kapalıysa (Ayarlar → Gizlilik ve güvenlik → Mikrofon) getUserMedia yine düşer. Bunu kabuk kodu veremez; kurulum dokümanınıza yazın.

Tauri: taşınabilir izin API'si YOK

WebviewWindowBuilderın tüm kancaları sayıldı (on_navigation, on_new_window, on_page_load, on_download, on_web_resource_request, …) — izin/medya karşılığı olan hiçbiri yok. Kapı bu yüzden Tauri'nin belgelenmiş kaçış kapısı with_webview ile, platformun yerel tutamacı üzerinden kurulur (Windows: webview2-com, Linux: webkit2gtk). Bu iki crate'in sürümü Tauri'ninkiyle eşleşmelidir (cargo tree -i webkit2gtk), yoksa tipler aynı sayılmaz ve derleme düşer.

🔴 macOS kapsanmıyor. WKWebView'da karar WKUIDelegatein requestMediaCapturePermissionFor… yöntemine düşer; o delegate'i wry kurar ve Tauri onu değiştirmek için yüzey vermez. Tutamacı ele geçirip delegate'i değiştirmek wry'nin kendi işleyişini (yeni pencere, dosya seçici, JS diyalogları) kırardı. wry tarafında konu hâlâ açıktır (tauri-apps/wry#1195). Yani macOS'ta mikrofon davranışı wry sürümüne bağlıdır ve bu örnek onu garanti etmez — örnek, boşluğu teşhis kanalına yazarak ilan eder.

DERLENMEDİ. Bu bölümdeki üç dosya değişikliği (C#, C, Rust) yazıldığı makinede derlenemedi — araç zinciri yok. API adlarının tamamı resmî dokümandan doğrulandı (WebView2 CoreWebView2PermissionKind/PermissionRequested, WebKitGTK webkit_user_media_permission_is_for_audio_device, webkit2gtk/webview2-com crate imzaları), ama doğrulanmış API adı derlenmiş kod değildir. Windows/Linux'ta derleyip çalıştırın. Kaynak testleri (ornekler-kaynak.test.ts) kapının metnini pinler, davranışını değil.

Linux'ta bilinen sınır (dokümana yazılıdır, çünkü kod bunu yapamıyor)

WebKitGTK'nın script-message-received sinyali gönderen çerçeve ve origin bilgisi taşımaz. Bu yüzden Linux'ta köprü mesajı geldiği anda kaynak doğrulaması yapılamaz (Electron'da senderFrame, WebView2'de args.Source ile yapılır). Taşıyıcı kontrol gezinme izin-listesidir: yabancı bir belge o WebView'a hiç yüklenemez. Artakalan risk kendi origin'imizden türeyen bir alt çerçevedir — ki o çerçeve zaten sayfayla aynı origin'de olduğu için sayfanın deposuna erişebilir. "Köprü origin'e bağlıdır" diye yazmayın; Linux'ta değildir.

Kapsam dürüstlüğü

  • Bu paket Electron için bir modül, diğer kabuklar için örnek + sözleşme gönderir. "Windows/Linux yerel SDK" DEMİYORUZ.
  • MAUI ve WPF Linux'u kapsamaz; webview2-host.cs örneği Windows içindir.
  • Flutter masaüstü listelenmiyor: birinci-taraf webview_flutter yalnız Android/iOS/macOS destekler. Hedeflenen üçüncü-taraf paket adıyla seçilip doğrulanmadan "destekleniyor" yazılmaz.

Doğrulama

Bu paket macOS bir makinede yazıldı. Windows tarafı 2026-08-04'te gerçek bir makinede (Windows 11 Home / AMD64 · node 24.16 · .NET 8.0.408 · rustc 1.96.0 · WebView2 Runtime 151.0.4129.59) SSH üzerinden ölçüldü:

| Ne | Komut | Sonuç | |---|---|---| | Üretim kaynağı | tsc -p tsconfig.json --noEmit | çıkış 0 | | Test kaynağı | tsc -p tsconfig.test.json | çıkış 0 | | Testler | vitest run . | 43/43, çıkış 0 | | WebView2 örneği | dotnet build (net8.0-windows) | çıkış 0, 0 hata | | Tauri örneği | cargo check | çıkış 0 (bağımlılık eksiği bulundu ve yazıldı) |

Yani sayılar macOS'takiyle birebir aynı. Hiçbiri gerçek bir pencerede çalıştırılmadı — derlenmek çalışmak değildir. Linux (WebKitGTK) hâlâ hiç derlenmedi.

Depoda koşan doğrulama şunlardır:

npx tsc -p packages/desktop --noEmit        # üretim kaynağı
npx tsc -p packages/desktop/tsconfig.test.json  # testler de tip-denetiminde
npx vitest run packages/desktop             # 43 test

Testler Electron API'sini sahte nesnelerle sürer ve kablolamayı kanıtlar (gezinme gerçekten durdu mu, yabancı gönderen depoya ulaşabildi mi, reset() depoyu sildi mi, uçuştaki bir mesaj çıkışı geri alabildi mi) — çünkü bu üründe en sık yapılan hata saf fonksiyonu test edip üretim kablolamasını test etmemektir. Yine de yeşil test "çalışıyor" demek değildir.

Doğrulama durumu (2026-08-04)

Aşağıdaki altı maddenin 1, 2, 3, 5 ve 6'sı ölçüldü; 4 hâlâ ölçülmedi.

| Ne | Nerede | Nasıl | |---|---|---| | Electron kabuğu | Windows 11 | öz-denetim koşumu 16/16 (panel 360×520 · odak çalınmıyor · ekranın %24'ü · reset() pencereyi beklemeden kaldırıyor · gömülü kip mount/unmount) | | WebView2 (yerel .NET) | Windows 11 | kullanıcının oturumunda 5/5 — köprü object, panel acik, localStorage'da ph_support_token_… | | WebKitGTK | Linux konteyneri | derlendi + çalıştı — köprü kuruldu, panel açıldı, jeton yazıldı | | Tauri v2 | Windows 11 | derlendi + kullanıcı tarafından çalıştırıldı ve çalıştığı teyit edildi |

Madde 4 de ölçüldü (2026-08-04), iki kabukta:

| Ne | Electron (Windows 11) | WebKitGTK (Linux) | |---|---|---| | Yabancı origin'e gezinme engellendi | ✅ URL değişmedi ve gerçek yabancı sunucuya 0 istek | ✅ deneme sonrası adres hâlâ host sayfası | | İzinli https sistem tarayıcısına devredildi | ✅ tam 1 kez, WebView gezinmedi | ⚠️ içeride açılmadığı ölçüldü; "tarayıcıya gitti" yarısı konteynerde gözlenemez | | İzinsiz şema (myapp://, bankauygulamasi://) devredilmedi | ✅ hiçbiri | ✅ içeride açılmadı |

"Yabancı sunucuya 0 istek" ölçümü bilinçli: .invalid gibi çözülmeyen bir adres kullanmak "engellendi" ile "adres zaten çözülemedi"yi karıştırırdı. Gerçek bir yerel sunucu dinlendi ve istek sayacı sıfır kaldı.

KALAN: madde 4 WebView2 ve Tauri kabuklarında ÇALIŞTIRILARAK ölçülmedi (ikisi de etkileşimli masaüstü oturumu istiyor). Kod yolu üçünde de aynı sözleşmeyi kuruyor ve kaynak testleriyle pinli, ama bu bir ölçüm değil.

Paketin "deneysel" etiketi şunların tamamı gerçek bir kabukta gözle doğrulandığında kalkar:

  1. Gömülü kip ve balon kipi, panel ayarlarının tamamını (renk, ön-sohbet, Makaleler, marka satırı) web ile aynı gösteriyor.
  2. Balon kipinde satıcının kendi arayüzü tıklanabilir kalıyor (şeffaf tam-ekran katman yok).
  3. Sohbet penceresi açıldığında odak çalınmıyor.
  4. İzin dışı bir URL içeride açılmıyor; dış bağlantı sistem tarayıcısına gidiyor.
  5. Uygulama kapanıp açıldığında ziyaretçi geçmişi KORUNUYOR; reset() sonrası SİLİNİYOR — ve reset() çağrıldığı anda sohbet penceresi/görünümü ekrandan KALKIYOR (depo temizlemeyi beklemeden), sonraki açılışta eski sohbet GERİ GELMİYOR.
  6. Windows'ta (WebView2) ve Linux'ta (WebKitGTK) köprü kuruluyor — jeton kabuğa ulaşıyor.

Lisans

Apache-2.0