diff --git a/AGENTS.md b/AGENTS.md index f592505..1399558 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -204,21 +204,29 @@ sda1 ile sdb1 çakışmaz. seçicide hangi disk seçiliyse bölüm tablosu onun. Tüm diskleri tek tabloda göstermek kafa karışıklığı yaratıyordu — özellikle "Sil" hangi diski etkilediği belirsizleşiyordu. -- **Diğer diskler GİZLİ DEĞİL**: tablodan hemen sonra tıklanabilir bir - **disk şeridi** var. Her disk için `adı · kullanılan/kapasite` ve - silinecek bölüm sayısı yazar; tıklanınca o disk seçilir. Böylece - "sda'da 3 bölüm var" bilgisi kaybolmaz, dikkat tek diskte kalır. - Disk değişince `manual.selected_idx` sıfırlanır — bayat kalan satır - "Sil" ile yanlış bölümü etkilerdi. +- **Disk ŞERİDİ YOK** (27 Eyl, kullanıcı isteği). Tablo altındaki + `/dev/sda · 3/20 GB` gibi tıklanabilir disk düğmeleri kaldırıldı: + üstteki **"Aktif Disk" ComboBox'ı** zaten tek ve açık kaynak, iki + kaynak olması "hangi disk geçerli" sorusunu belirsizleştiriyordu. + Başka bir diske geçmek için ComboBox kullanılır, sonra "Yeni Bölüm". + "sda'da 3 bölüm var" bilgisi tablo başlığında ve satırlarda durur. + - ⚠️ Şerit sadece çizim değildi: disk değişince + `on_disk_changed()` ile `selected_idx` / `selected_free` + sıfırlıyordu. Bu sorumluluk **ComboBox'a taşındı**; taşınmazsa + bayat satır indeksi "Sil" ile **yanlış bölümü** siler. +- **"Aktif Disk" ComboBox'ı TEMA sarmalayıcısını kullanır** + (`theme::show_combo_box` + `theme::selectable_value`). Ham + `egui::ComboBox` + `ui.selectable_value` egui'nin varsayılan stilini + kullanıyor ve koyu temada okunmuyordu. Mountpoint ve önyükleyici + ComboBox'ları zaten temalı sürümde — **disk ComboBox'ı geride kalmıştı**. - **Diyalogda HEDEF DİSK SEÇİCİSİ YOK** (27 Eyl, kullanıcı isteği) — ne eklemede ne düzenlemede. Bölümleme arayüzü tek disk üzerinde çalışıyor: - ekleme → `state.selected_disk` - düzenleme→ bölümün kendi `disk`/`device` alanları (korunur) - Başka bir diske bölüm eklemenin yolu: **disk şeridinden o diske - geç**, sonra "Yeni Bölüm". İki ayrı "aktif disk" kaynağı olması - hangisinin geçerli olduğunu belirsizleştiriyordu. + Başka bir diske bölüm eklemenin yolu: **"Aktif Disk" + ComboBox'ından o diske geç**, sonra "Yeni Bölüm". Bu karar `ManualState::add_disk` alanını da tamamen gereksiz kıldı ve alan **silindi**. Alan varken şu akış bölümü yanlış diske taşıyordu: @@ -661,11 +669,11 @@ nedeni yorumla sabitlendi. Log bile "MiB" derken argüman "MB" gönderiyordu. ``` cargo check --all-targets : 0 hata, 0 uyarı -cargo test : 141/141 (test modülü sayısı arttıkça güncel tut) +cargo test : 143/143 (test modülü sayısı arttıkça güncel tut) cargo clippy : ~77 uyarı, tamamı stil (collapsed_if, replace zinciri) ``` -Bu oturumda 1 → 141 test. Test modülü olmayan dosyalar (`users.rs`, +Bu oturumda 1 → 143 test. Test modülü olmayan dosyalar (`users.rs`, `location.rs`, `rescue.rs`, `funct.rs`, `autoinstall/mod.rs`, `main.rs`) içine modüller eklendi. Çoklu test modülü paylaşılan yardımcıları `steps/partition.rs::testutil` altındadır (`disk()`, `part()`). @@ -677,13 +685,15 @@ senaryoları dene: **Çoklu disk (asıl hedef):** 1. İki disk bağlı: sda + sdb -2. Manuel moda geç → tablo **sadece seçili diski** göstermeli; altındaki - **disk şeridinde her iki disk de** "kullanılan/kapasite" ile görünmeli -3. Disk şeridinden sdb'ye tıkla → tablo sdb'nin bölümlerine geçmeli -4. sda'ya geri dön → tablo sda'nın bölümleri (seçili satır sıfırlanmalı, +2. Manuel moda geç → tablo **sadece seçili diski** göstermeli; tablo + altında **disk şeridi OLMAMALI** +3. **"Aktif Disk" ComboBox'ı açılmalı ve KOYU TEMADA OKUNMALI** + (gradient arkaplan, açık metin rengi) +4. ComboBox'tan sdb'yi seç → tablo sdb'nin bölümlerine geçmeli +5. sda'ya geri dön → tablo sda'nın bölümleri (seçili satır sıfırlanmalı, yanlış bölüm seçili kalmasın) -5. sda'da kök + EFI, sdb'de `/home` → **diyalogdaki disk seçiciyle** -6. Özet ekranında: iki disk, başlıklarda kapasite, altlarında "boş: …", +6. sda'da kök + EFI, sdb'de `/home` → **Aktif Disk ComboBox'ı ile** +7. Özet ekranında: iki disk, başlıklarda kapasite, altlarında "boş: …", kök diski "[kök]" etiketiyle işaretli **Silme + yeni bölüm (veri kaybı senaryosu):** @@ -742,7 +752,11 @@ senaryoları dene: `2>/dev/null` ile çalışıyor, `let _ =` ile yutuluyor → süreç ortada ölürse kurulu sistem yamalı kalıyor - [ ] `is_mount_point` `/proc/mounts` okurken `\040` kaçışlarını çözmüyor -- [ ] Debug `eprintln!` / `println!` satırları (GUI uygulamasında) +- [ ] Debug `eprintln!` / `println!` satırları (GUI uygulamasında) — + kısmen: disk ComboBox'ındaki `println!("Seçilen Disk Değişti")` + **silindi**; kalanlar `main.rs` CLI yolu, `branding.rs` ve + `slideshow.rs` başlangıç uyarıları (GUI'de görünmez ama + stdout'a yazıyor) - [ ] ~100 ölü i18n anahtarı (slayt + eski ekran anahtarları) ### Bilinçli olarak yapılmadı @@ -760,7 +774,7 @@ senaryoları dene: |---|---| | `src/jobs/partition.rs` | `plan_disk_layout`, `PhysicalExtent`, `PlannedCreate`, `disk_total_mb`, `read_physical_extents`, `device_map`; `disk_free_regions_mb` silindi | | `src/jobs/cleanup.rs` | **YENİ** — `EmergencyCleanupJob` | -| `src/steps/partition.rs` | **tek diske odaklı tablo + disk şeridi** (diyalogda disk seçicisi yok), **seçime göre görünen butonlar** (`selected_free`), diske filtreli LVM, `validate_manual_on`, `manual_capacity_error`, `mountpoint_required`, `suggest_mountpoint`, `has_symbolic_remaining`, `build_table_rows`, `format_checkbox_from`, `skip_format_from`; `multidisk_tests`, `table_scope_tests`, `free_space_tests`, `symbolic_capacity_tests`, `selection_tests`, `edit_flow_tests`, `edit_disk_tests`, `testutil` | +| `src/steps/partition.rs` | **tek diske odaklı tablo, şerit YOK** (diyalogda da disk seçicisi yok), **seçime göre görünen butonlar** (`selected_free`), diske filtreli LVM, `validate_manual_on`, `manual_capacity_error`, `mountpoint_required`, `suggest_mountpoint`, `has_symbolic_remaining`, `build_table_rows`, `on_disk_changed`, `format_checkbox_from`, `skip_format_from`; `multidisk_tests`, `table_scope_tests`, `free_space_tests`, `symbolic_capacity_tests`, `selection_tests`, `edit_flow_tests`, `edit_disk_tests`, `testutil` | | `src/jobs/users.rs` | `set_password_via_chpasswd` (stdin pipe) | | `src/jobs/bootloader.rs` | `CRYPTROOT_NAME`, `luks_backing_device`, `mounted_root_device` | | `src/jobs/finish.rs` | `RunComarJob` geri geldi; `ldconfig`/`update-environment`/depo fatal | diff --git a/src/steps/partition.rs b/src/steps/partition.rs index fe239f6..d0b3584 100644 --- a/src/steps/partition.rs +++ b/src/steps/partition.rs @@ -610,6 +610,21 @@ fn skip_format_from(format_checked: bool) -> bool { !format_checked } +/// Aktif disk değişti: satır seçimleri diske özgüdür, sıfırlanır. +/// +/// ⚠️ Bu, **veri güvenliği** kuralıdır. `selected_idx` tablo dışındaki +/// gerçek bir bölüm indeksidir; tablo ise tek diski gösterir. Sıfır +///lanmazsa yeni disk, eski diskin satır indeksini taşır ve "Sil" +/// YANLIŞ bölümü siler. `selected_free` de diske özgüdür (o diskin +/// boş alan satırıdır). +/// +/// Tablo altındaki disk şeridi kaldırıldığında bu sorumluluk "Aktif +/// Disk" ComboBox'ına taşındı; `edit_disk_tests` sabitler. +fn on_disk_changed(manual: &mut ManualState) { + manual.selected_idx = None; + manual.selected_free = false; +} + /// Bir tanımın bağlama noktası BOŞ olmak zorunda mı? /// /// Boş bırakmak "bu bölüm kuruluma ait değil" demektir. Üç durumda @@ -1252,19 +1267,42 @@ impl PartitionStep { .unwrap_or_else(|| t!("mp_select_disk_first").to_string()); // ComboBox Oluşturma - egui::ComboBox::from_id_source("disk_selection_combobox") - .selected_text(selected_label) - .width(ui.available_width() - 20.0) // Genişliği panele yay - .show_ui(ui, |ui| { + let disk_combo_w = ui.available_width() - 20.0; + theme::show_combo_box( + ui, + "disk_selection_combobox", + &selected_label, + disk_combo_w, + |ui, w| { for disk in &state.available_disks { let label = format!("{} - {} ({:.1} GB)", disk.name, disk.model, disk.size_bytes as f64 / 1_073_741_824.0); - if ui.selectable_value(&mut state.selected_disk, Some(disk.name.clone()), label).changed() { - // Disk değiştiğinde tetiklenecek işlemler (örn: disk bölümlerini yeniden okuma) - println!("Seçilen Disk Değişti: {}", disk.name); + // Ham `ui.selectable_value` yerine temalı sürüm. + // Ham sürüm egui'nin varsayılan stilini kullanıyor ve + // koyu temada okunmuyordu; `theme::selectable_value` + // gradient arkaplan ve temaya uygun metin rengi + // veriyor. Mountpoint ve önyükleyici ComboBox'ları + // zaten bunu kullanıyor. + let mut value = state.selected_disk.clone(); + theme::selectable_value( + ui, &mut value, Some(disk.name.clone()), &label, w, + ); + if value != state.selected_disk { + state.selected_disk = value; + // Disk değişince önceki diskin seçili satırı + // bayat kalırdı; "Sil" yanlış bölümü etkilerdi. + // Boş alan seçimi de diske özgüdür, o da sıfırlanır. + // + // ⚠️ Bu sorumluluk tablo altındaki disk + // şeridindeydi; şerit kaldırıldığı için buraya + // taşındı. Taşınmazsa disk değiştirince + // `selected_idx` başka diskin bölümünü gösterir + // ve "Sil" YANLIŞ bölümü siler. + on_disk_changed(&mut self.manual); } } - }); + }, + ); }); @@ -1380,17 +1418,6 @@ impl PartitionStep { // test edilebiliyor (boş alan satırının ne zaman çizildiği // buradan görünmüyordu ve iki kez yanlış hesaplandı). // - // Disk şeridi için: `available_disks` + tanımı olan ama listede - // olmayan diskler. Sıra `available_disks`'teki tarama sırasıdır. - let seen_disk_names: Vec = { - let mut names: Vec = state.available_disks.iter().map(|d| d.name.clone()).collect(); - for part in &state.custom_partitions { - if !part.disk.is_empty() && !names.contains(&part.disk) { - names.push(part.disk.clone()); - } - } - names - }; // Hangi disk GÖSTERİLECEK? // // Kullanıcı isteği: tablo TÜM diskleri değil, üstteki seçicide @@ -1630,66 +1657,25 @@ impl PartitionStep { } } - // ── Disk şeridi ──────────────────────────────────────── + // ── Disk şeridi: KALDIRILDI ───────────────────────────── // - // Tablo tek diski gösteriyor. Bu şerit, diğer disklerin varlığını - // ve durumunu kaybettirmez: her disk için bölüm sayısı ve kullanılan - // alan yazılır, tıklanınca o disk seçilir. Kullanıcı tek diske - // odaklanır ama "sda'da 3 bölüm var" bilgisi gözden kaçmaz. - if seen_disk_names.len() > 1 { - ui.add_space(8.0); - ui.horizontal_wrapped(|ui| { - for dname in &seen_disk_names { - let is_active = state.selected_disk.as_deref() == Some(dname.as_str()); - let count = state.custom_partitions.iter() - .filter(|p| p.disk == *dname && !p.delete) - .count(); - let del_count = state.custom_partitions.iter() - .filter(|p| p.disk == *dname && p.delete) - .count(); - let used: u64 = state.custom_partitions.iter() - .filter(|p| p.disk == *dname && !p.delete) - .map(|p| p.size_mb) - .sum(); - let cap = disk_size_mb(state, dname); - let used_str = if used >= 1024 { - format!("{:.0} GB", used as f64 / 1024.0) - } else { - format!("{} MB", used) - }; - let cap_str = if cap >= 1024 { - format!("{:.0} GB", cap as f64 / 1024.0) - } else { - format!("{} MB", cap) - }; - - let mut label = format!( - "{} · {}/{}{}", - dname, used_str, cap_str, - if del_count > 0 { format!(" \u{EA6B}{}", del_count) } else { String::new() } - ); - if count == 0 && del_count == 0 { - label = format!("{} · boş", dname); - } - - let text = egui::RichText::new(label).size(11.5); - let btn = if is_active { - // Aktif disk vurgulu - egui::Button::new(text.color(crate::ui::theme::c_accent()).strong()) - } else { - egui::Button::new(text.color(crate::ui::theme::c_text_dim())) - }; - if ui.add(btn).on_hover_cursor(egui::CursorIcon::PointingHand).clicked() { - state.selected_disk = Some(dname.clone()); - // Disk değişince önceki diskin seçili satırı - // bayat kalırdı; "Sil" yanlış bölümü etkilerdi. - // Boş alan seçimi de diske özgüdür, o da sıfırlanır. - self.manual.selected_idx = None; - self.manual.selected_free = false; - } - } - }); - } + // Kullanıcı isteği: "manuel disk penceresindeki tablonun altındaki + // /dev/sda sdb gibi seçilileri kaldır. Aktif disk bölümünde + // seçiyoruz zaten tablonun altında olmasına gerek yok." + // + // İki disk kaynağı olması, "hangi disk geçerli" sorusunu + // belirsizleştiriyordu — üstteki "Aktif Disk" ComboBox'ı zaten + // tek ve açık kaynak. + // + // ⚠️ Bu blok sadece çizim değildi: disk değişince + // `selected_idx` / `selected_free` sıfırlıyordu. Bu sorumluluk + // **ComboBox'a taşındı**; taşınmazsa disk değiştirince seçili + // satır başka diskin bölümünü gösterir ve "Sil" YANLIŞ bölümü + // siler. + // + // "sda'da 3 bölüm var" bilgisi kaybolmadı: tablonun üstündeki + // başlık satırı seçili diskin adını ve toplam kapasitesini + // yazıyor, satırlar da bölümleri listeliyor. // Silme onayı if let Some(idx) = self.manual.delete_idx { @@ -4246,24 +4232,58 @@ mod table_scope_tests { ); } - /// Disk şeridi her diski listelemeli — tabloda tek disk görünse de - /// diğer diskler gizlenmemeli, yoksa kullanıcı "sda'da 3 bölüm var" - /// bilgisini kaybeder (kullanıcı isteğinin tam tersi). + /// ⭐ Disk değişince seçimler SIFIRLANMALI. + /// + /// REGRESYON: tablo altındaki disk şeridi kaldırıldı; onun yaptığı + /// seçim sıfırlamayı "Aktif Disk" ComboBox'ı devraldı. Taşınmazsa + /// `selected_idx` eski diskin indeksini taşır, tablo yeni diski + /// gösterir ve "Sil" **yanlış bölümü** siler. #[test] - fn disk_strip_lists_every_disk_even_when_one_is_shown() { - let state = crate::installer::GlobalState::default(); - let mut seen: Vec = state.available_disks.iter() - .map(|d| d.name.clone()).collect(); - for part in &state.custom_partitions { - if !part.disk.is_empty() && !seen.contains(&part.disk) { - seen.push(part.disk.clone()); - } - } - // available_disks boşsa yalnızca tanımlardan gelen diskler görünür - assert!( - seen.iter().all(|s| s.starts_with("/dev/")), - "şeritte yalnızca aygıt yolları olmalı: {:?}", - seen + fn switching_disk_clears_the_row_selection() { + use super::on_disk_changed; + let mut manual = super::ManualState::default(); + + // sdb'de 2. satır seçili + manual.selected_idx = Some(2); + manual.selected_free = true; // iki bayrağın da temizlendiğini gör + + on_disk_changed(&mut manual); + + assert_eq!(manual.selected_idx, None, "bölüm seçimi sıfırlanmalı"); + assert!(!manual.selected_free, "boş alan seçimi sıfırlanmalı"); + } + + /// Disk DEĞİŞMEDEN seçim korunmalı — sıfırlama her karede olsaydı + /// kullanıcı hiçbir satırı seçemezdi. + #[test] + fn selection_survives_while_the_disk_is_unchanged() { + let mut manual = super::ManualState::default(); + manual.selected_idx = Some(0); + // on_disk_changed ÇAĞRILMAZ: disk değişmedi. + assert_eq!(manual.selected_idx, Some(0), "disk değişmeden seçim korunmalı"); + } + + /// Tablo tek diski gösterir; disk şeridi KALDIRILDI (kullanıcı + /// isteği) — seçim yalnızca "Aktif Disk" ComboBox'ından yapılır. + #[test] + fn disk_strip_is_gone() { + // Tablo satırları yalnızca aktif diskin tanımlarından üretilir. + let parts = vec![ + part("/dev/sda1", 500, FsType::Ntfs, ""), + part("/dev/sdb3", 400_000, FsType::Ext4, "/"), + ]; + let rows = super::build_table_rows(&parts, "/dev/sda", 20_480); + let devices: Vec<&str> = rows + .iter() + .filter_map(|r| match r { + super::TableRow::Part(_, p) => Some(p.device.as_str()), + _ => None, + }) + .collect(); + assert_eq!( + devices, + vec!["/dev/sda1"], + "sdb'nin bölümleri şerit olmadığı için de görünmemeli" ); }