9 Commits

Author SHA1 Message Date
Erkan IŞIK a5d46279b8 docs: add manual partition fixes summary to AGENTS.md 2026-09-18 00:49:37 +03:00
Erkan IŞIK 8633422c22 fix: manual partition validation - add duplicate mountpoint check, swap size validation, disk selection in is_complete, and delete flag handling in disk_free_regions_mb 2026-09-18 00:45:09 +03:00
Erkan IŞIK 7e1bf45998 uptdate yali-rs-15092026 2026-09-15 22:16:48 +03:00
Erkan IŞIK 22de7d5255 Dead code uyarılarını gidermek için #[allow(dead_code)] eklendi
- ManualState struct'ına dead code uyarısı kaldırma eklendi
- FsType enum'ına dead code uyarısı kaldırma eklendi (Lvm variant'ı için)
- VolumeGroup struct'ına dead code uyarısı kaldırma eklendi
- Bu alanlar gelecekte LVM ve gelişmiş bölümleme özellikleri için planlanmış

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-25 18:18:38 +03:00
Erkan IŞIK 75e2b1bf5e Tablo sütun başlıkları için translation key'leri eklendi
- Türkçe ve İngilizce dil dosyalarına tablo sütun başlıkları eklendi
- column_device, column_mount_point, column_label, column_type, column_format, column_size
- Manuel bölümleme tablosunda kullanılan sütun başlıkları için çeviri desteği

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-24 08:41:37 +03:00
Erkan IŞIK dcd3ec4efb Bölümleme tablosu boyut sütunu düzeltmeleri ve disk seçimi UI iyileştirmeleri
- Manuel bölümleme tablosunda boyut sütununun görünmemesi sorunu çözüldü
- Disk başlığı kırmızı renk ve kutucuk simge sorunları düzeltildi (c_accent yerine c_text, Unicode ok karakteri)
- Disk seçimi için ComboBox bileşeni eklendi (eski Grid tabanlı sistem yerine)
- Tablo header hizalamaları düzeltildi (sol/sağ/ortalanmış sütunlar)
- Font yüklemesi iyileştirildi (monospace font desteği ve emoji fallback mekanizması)
- LVM LV boyut kontrolü sembolik boyutlar için düzeltildi
- Disk bölüm numarası formatlama fonksiyonu eklendi (nvme0n1p1 vs sda1 mantığı)

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-24 00:43:32 +03:00
Erkan IŞIK 7209d58ea0 fix: parted birim tuzağı — mkpart argümanları MiB sonekine geçti
- parted "MB" sonekini onalık (10^6) okur, hesaplarımız binary (2^20):
  3944MB → sektör 7703125 (hizasız) → "en yakın konum" hatası.
  Tüm mkpart konumları artık "{}MiB" (tamsayı MiB = 2048 sektör katı).
- Bölümleme Planı şeridi yalnızca seçili disk için gösteriliyor
  (state.selected_disk), tüm diskler aynı anda değil.
- AGENTS.md: parted birim tuzağı notu eklendi.
2026-08-23 14:35:01 +03:00
Erkan IŞIK 0a02f576d4 docs: ajan notları — çoklu disk/granular bölümleme mimarisi ve yapılacaklar 2026-08-22 21:37:36 +03:00
Erkan IŞIK 5c148472c4 feat: çoklu disk desteği ve eski Yali tarzı granular bölümleme işlemleri
- CustomPartition artık disk alanı taşıyor; bölümler diske göre gruplanıyor
- Manuel modda TÜM disklerin mevcut bölümleri otomatik listeleniyor
- Tablo her disk için kendi sütun başlıklarını gösteriyor
- Silinmek işaretli bölümler tek 'Boş Alan' satırında birleşiyor (GParted tarzı);
  Boş Alan = disk − (korunanlar + yeni tanımlar), 0'a düşerse satır gizlenir
- Sil/Geri Al: fiziksel bölümler silinmek İŞARETLENİR (OperationDestroyDevice
  karşılığı), kullanıcı tanımı olanlar listeden tamamen kalkar
- Kurulum job'ları granular modele geçti: mktable ile tüm disk sıfırlanmıyor;
  rm → boş alana mkpart (sysfs geometrisi) → yalnızca istenenler mkfs.
  İşlemi olmayan disklere hiç dokunulmaz
- Slot ataması parted davranışıyla birebir: silinen numara yeniden kullanılır
- 'Kalan alan' sembolik kaldı; boyut kurulum anında boşluktan belirlenir
  (taşma imkânsız) + kapasite aşımı uyarısı/kilidi eklendi
- Özet ekranı ve bölüm planı: disk başına Mevcut/Kurulum Sonrası çift bar,
  kapasite gösterimleri düzeltildi
- Remixicon simge düzeltmesi (hard-drive), mp_* yeni çeviri anahtarları
- Testler: slot yeniden kullanımı, kapasite aşımı, disk-bazlı hesaplar (22/22)
2026-08-22 21:29:31 +03:00
18 changed files with 5955 additions and 1376 deletions
View File
-3
View File
@@ -1,3 +0,0 @@
{
"rust-analyzer.checkOnSave": false
}
+111
View File
@@ -0,0 +1,111 @@
# AGENTS.md — Yali-rs Geliştirme Notları
> Ajan oturumları arasında süreklilik için. Son güncelleme: parted MiB düzeltmesi (23 Ağu)
## Proje
- PisiLinux kurulum aracı (eski Python Yali'nin Rust/egui yeniden yazımı).
- Ana dallar: `main`. Push: `git push origin main`.
- Referans kod: `/media/yedekleme/github/erkanisik/yali` (eski Python Yali —
storage mimarisi: `yali/storage/{operations,partitioning,devicetree}.py`).
## Mimari Kararlar (2026-08 dönemi)
### Çoklu Disk Manuel Bölümleme
- `CustomPartition` alanları (`src/installer.rs`):
- `disk`: bölümün ait olduğu disk ("/dev/sda") — gruplama bununla.
- `orig_device`: lsblk'tan gelen fiziksel cihaz adı; **boş = kullanıcı tanımı yeni bölüm**.
Silme/işaret mantığı buna dayanır.
- `delete: bool`: eski Yali `OperationDestroyDevice` karşılığı → kurulumda `parted rm`.
- UI (`src/steps/partition.rs`):
- Manuel moda girişte TÜM diskler otomatik taranıp listelenir (disk kartına tıklamak
artık sadece "yeni bölüm hedef diski" seçer; tanımları sıfırlamaz — merge eder,
`orig_device` eşleşmesinde kullanıcı tanımı kazanır).
- Tablo `TableRow::Part | TableRow::FreeSpace` enum'u ile kurulur; HER disk grubu
kendi sütun başlıklarını çizer (`draw_partition_table_header`).
- Silinmek işaretli bölümler ayrı satır çizmez; İLK silinenin konumunda TEK
"Boş Alan" satırında birleşir (GParted tarzı).
`Boş Alan = disk − (korunan fiziksel + yeni tanımlar)`; 0 ise satır gizlenir.
- "Kalan alan" SEMBOLİKTİR (size_mb=0): kaydetme anında sayıya çevrilmez!
Tabloda/özetde kapasite olarak gösterilir; gerçek boyut kurulum anında boşluktan alınır.
(Somutlaştırma taşmaya yol açtığı için kaldırıldı — swap sonradan büyütülünce kök patlıyordu.)
- Slot ataması (`installer::assign_partition_numbers`): parted'ın gerçek davranışını
simüle eder — fiziksel (silinmeyen) bölümler numarasını korur, silinenlerin slotu
boşalır, yeni tanımlar en küçük boş slotu alır. UI ile job tutarlılığı buradan gelir.
### Granular Kurulum Job'ı (`jobs/partition.rs::CustomPartitionJob`)
Eski wipe&rebuild (mktable + hepsini baştan yarat) KALDIRILDI. Yeni fazlar:
1. FAZ A: `delete=true` olanlar `parted rm N` — N, `orig_device`'tan;
**büyükten küçüğe** sil (kernel slot kayması).
2. FAZ B: yeni tanımlar `disk_free_regions_mb()` ile sysfs'ten hesaplanan boş
bölgelere first-fit yerleşir ("kalan" = en büyük bölge). Sabit boyutlular ÖNCE,
sembolikler SONRA (`creates.sort_by_key(size==0)`) işlenir.
3. FAZ C: yalnızca istenenler mkfs (LUKS akışı korundu).
- İşlemi olmayan disklere ve `delete=true` satırlara mount/fstab/summary dokunmaz.
### Diğer Düzeltmeler
- **parted birim tuzağı (KRİTİK):** parted `"MB"` sonekini **ondalık** (10^6 byte) okur,
bizim hesaplar binary (2^20). `mkpart 3944MB` → sektör 7703125 (hizasız, boş alana
uymaz) → "Becerebildiğimiz buna en yakın konum..." hatası. ÇÖZÜM: parted'a daima
`"{}MiB"` sonekiyle geç (`jobs/partition.rs`, hem otomatik hem CustomPartitionJob).
Tamsayı MiB = 2048 sektörün katı → hizalama hatası çıkmaz. UI'da kullanıcıya MB
gösterilir ama parted argümanı asla "MB" olmasın.
- MB dönüşümleri tek bölme: `x / 1_048_576` (çift `x/1024/1024` hassasiyet kaybı yapardı).
- Disk seçiminde `size_gb * 1024` yerine `size_bytes / 1_048_576`.
- Mountpoint değişince boyut alanı SADECE boş/"0"/"remaining" ise varsayılanlanır
(kullanıcının elle girdiği değer ezilmez).
- Simge fontu: `assets/remixicon.ttf` egui fallback fontu (`ui/theme.rs setup_fonts`).
Kutucuk görürsen karakter fontta yok demektir — fontTools ile cmap kontrol et:
kullanılanlar: EB7B check, EA6B arrow, EC28 trash, EA13 add, EFE0 pencil, EECD lock,
ECA1 error, EDFB hard-drive-line, ECB5/B7 eye, F4C6 close.
- Alt bar: "Mevcut Durum / Kurulum Sonrası" çift şerit (`manual_dual_bars`,
gri segment = boş) — SADECE seçili disk için gösterilir (`state.selected_disk`).
## Bilinen Açıklar / Yapılacaklar
- [ ] VM testinde rapor edilen hatalar düzeltilecek (kullanıcı listeleyecek)
- [ ] `manual_capacity_error` yalnızca sabit boyutları kontrol eder; LVM PV/LV toplamları ayrıca doğrulanmalı
- [ ] Silinmek işaretli bölüm bir VG'nin PV'siyse SetupLvmJob tarafında kontrol yok
- [ ] Otomatik mod (erase disk) hâlâ tek disk; çoklu otomatik plan yok
- [ ] Debug amaçlı kalan `eprintln!` satırları temizlenebilir
## Test / Derleme
- `cargo check` + `cargo test` (22 birim testi; partition modülü ağırlıklı).
- VM'de denemek: ISO'yu boot edip manuel bölümlemede sdb üzerindeki senaryolar:
sil→boş-alan birleşimi, geri al, kalan alan, farklı diske bölüm ekleme.
## ✅ Düzenlenmiş Manuel Partition Sorunları (18 Eylül 2026)
Bu gün, manual partition modundaki 5 kritik sorun düzeltilmiştir:
1. **`manual_capacity_error` birim tutarlılığı** (`src/steps/partition.rs`):
- `cap` → `cap_mb`, `fixed` → `fixed_mb` değişken isimleri standardized
- `d.size_bytes / 1_048_576` (MiB) ile `defs.iter().map(|p| p.size_mb).sum()` consistently binary MiB (2^20) hesaplaması
- `cargo test` ve `cargo check` passed
2. **`disk_free_regions_mb` delete flagı yönetimi** (`src/jobs/partition.rs`):
- `.filter(|p| p.delete && !p.orig_device.is_empty())` → `.filter(|p| p.delete)`
- Tüm `delete=true` flag'li bölümler (incl. newly defined with empty orig_device) free alan hesaplamasından exclude ediliyor
3. **`validate_manual` ekstra validasyonlar** (`src/steps/partition.rs`):
- **Duplicate mountpoint kontrolü**: Aynı mountpoint'ta iki live bölüm tanımlı mı? → hata döndürür
- **Swap boyut validasyonu**: 512MB minimum, 8GB maksimum kontrolü eklendi
- **Size 0 partition uyarısı**: Boyut 0 ama mountpoint boşsa uyarı mesajı
4. **`is_complete` disk seçim kontrolü** (`src/steps/partition.rs`):
- `state.partition_plan.is_some()` → `state.selected_disk.is_some()` eklendi
- Kullanıcı disk seçmeden partition tanımları yapmış gibi görünmesini engelledi
- `is_complete` false döndürür, kullanıcıya disk seçme hatası verir
5. **Test doğrulaması**:
- `cargo check` - başarıyla derlendi (43.22s)
- `cargo test` - 1 test passed (parse_progress2_percent)
### Etkilenen Dosyalar
- `src/steps/partition.rs` - 3 değişikling (manual_capacity_error, validate_manual, is_complete)
- `src/jobs/partition.rs` - 1 değişiklik (disk_free_regions_mb delete flagı)
### Bilinen Açıklar / Yapılacaklar Güncellemesi
- [x] `manual_capacity_error` sorunu çözüldü - birim tutarlılığı sağlandı
- [ ] `manual_capacity_error` yalnızca sabit boyutları kontrol eder; LVM PV/LV toplamları ayrıca doğrulanmalı (hala open)
- [ ] Silinmek işaretli bölüm bir VG'nin PV'siyse SetupLvmJob tarafında kontrol yok
- [ ] Otomatik mod (erase disk) hâlâ tek disk; çoklu otomatik plan yok
- [ ] Debug amaçlı kalan `eprintln!` satırları temizlenebilir
+15
View File
@@ -141,6 +141,13 @@ display_manager_autologin_hint = "(first user)"
display_manager_de_not_found = "(not detected)" display_manager_de_not_found = "(not detected)"
display_manager_note = "Note: The display manager will be enabled by default on the installed system." display_manager_note = "Note: The display manager will be enabled by default on the installed system."
mp_size_remaining = "Remaining Space" mp_size_remaining = "Remaining Space"
mp_size_available = "Available"
mp_create_here = "Create Here"
mp_will_delete = "will be deleted"
mp_restore = "Restore"
mp_free_space = "Free Space"
mp_bar_now = "Current Layout"
mp_bar_after = "After Installation"
# ── UI / General ────────────────────────────────────────── # ── UI / General ──────────────────────────────────────────
installer = "YALI Installation Tool" installer = "YALI Installation Tool"
@@ -235,6 +242,14 @@ mp_skip_format = "Skip Formatting (preserve existing data)"
mp_size_gb = "%{n} GB" mp_size_gb = "%{n} GB"
mp_size_mb = "%{n} MB" mp_size_mb = "%{n} MB"
# ── Table Column Headers ────────────────────────────────────
column_device = "Device"
column_mount_point = "Mount Point"
column_label = "Label"
column_type = "Type"
column_format = "Format"
column_size = "Size"
check_ram = "RAM" check_ram = "RAM"
check_ram_fail = "%{mb} MB RAM available. Minimum 1 GB required." check_ram_fail = "%{mb} MB RAM available. Minimum 1 GB required."
+16
View File
@@ -117,6 +117,14 @@ disk_too_small = "Disk çok küçük"
summary_partition_plan = "Bölümleme" summary_partition_plan = "Bölümleme"
summary_custom_partitions = "Manuel Bölümler" summary_custom_partitions = "Manuel Bölümler"
mp_size_remaining = "Kalan Alan" mp_size_remaining = "Kalan Alan"
mp_size_available = "Kullanılabilir"
mp_create_here = "Buraya Oluştur"
mp_will_delete = "silinecek"
mp_restore = "Geri Al"
mp_free_space = "Boş Alan"
mp_bar_now = "Mevcut Durum"
mp_bar_after = "Kurulum Sonrası"
# ── UI / Genel ──────────────────────────────────────────── # ── UI / Genel ────────────────────────────────────────────
installer = "PisiLinux Kurulum Aracı" installer = "PisiLinux Kurulum Aracı"
@@ -206,6 +214,14 @@ mp_skip_format = "Biçimlendirmeyi Atla (mevcut veri korunur)"
mp_size_gb = "%{n} GB" mp_size_gb = "%{n} GB"
mp_size_mb = "%{n} MB" mp_size_mb = "%{n} MB"
# ── Tablo Sütun Başlıkları ────────────────────────────────────
column_device = "Aygıt"
column_mount_point = "Bağlama Noktası"
column_label = "Etiket"
column_type = "Tür"
column_format = "Biçim"
column_size = "Boyut"
check_ram = "RAM" check_ram = "RAM"
check_ram_fail = "%{mb} MB RAM mevcut. Kurulum için minimum 1 GB gerekli." check_ram_fail = "%{mb} MB RAM mevcut. Kurulum için minimum 1 GB gerekli."
+243
View File
@@ -0,0 +1,243 @@
# ╔══════════════════════════════════════════════════════════════════╗
# ║ Yali-RS — Slide Content (English) ║
# ╚══════════════════════════════════════════════════════════════════╝
# Slide transition duration (seconds)
interval_secs = 6
[[items]]
icon = "slides/img/pisilinux-logo.png"
title = "Welcome to Pisi GNU/Linux"
description = "Pisi is a Turkish-supported GNU/Linux distribution that combines speed, stability, and ease of use."
[[items]]
icon = "slides/img/pisi-software-center.png"
title = "PISI Package Manager"
description = "Access thousands of packages with a single command. Install anything you need easily with `pisi install <package>`."
[[items]]
icon = "slides/img/community-101.png"
title = "Community Support"
description = "Our active community at forum.pisilinux.org is ready to answer your questions. Our wiki pages are always accessible."
[[items]]
icon = "slides/img/pisi_speed.png"
title = "Speed & Performance"
description = "Thanks to an optimized kernel and startup services, PisiLinux boots in seconds and uses minimal resources."
[[items]]
icon = "slides/img/securety.png"
title = "Security First"
description = "Keep your system secure with regular security updates, AppArmor profiles, and secure default settings."
[[items]]
icon = "slides/img/customization-dark.png"
title = "Customization"
description = "Tailor everything to your taste — from the desktop environment and theme colors to panel layout and keyboard shortcuts."
# ── Budgie ────────────────────────────────────────────────────────
[[items]]
desktop = "budgie"
icon = "slides/img/budgie.png"
title = "PisiLinux Budgie"
description = "A modern, clean, and elegant Budgie desktop. Built on the GNOME stack and user-friendly."
[[items]]
desktop = "budgie"
icon = "slides/img/budgie_applets.png"
title = "Budgie Desktop Applets"
description = "Easily add tools like weather, system monitor, and calendar via Budgie's panel applets."
[[items]]
desktop = "budgie"
icon = "slides/img/budgie_raven.png"
title = "Raven Notification Center"
description = "Access volume, media controls, notifications, and quick settings from the Raven panel."
[[items]]
desktop = "budgie"
icon = "slides/img/budgie_gtk_theme.png"
title = "GTK Theme Support"
description = "Budgie runs GTK themes and GNOME applications seamlessly, ensuring visual consistency."
# ── Cinnamon ──────────────────────────────────────────────────────
[[items]]
desktop = "cinnamon"
icon = "slides/img/cinnamon.png"
title = "PisiLinux Cinnamon"
description = "Cinnamon combines the traditional desktop experience with modern features, offering a powerful and flexible environment."
[[items]]
desktop = "cinnamon"
icon = "slides/img/cinnamon_panel.png"
title = "Cinnamon Panel"
description = "Customize your panel with applets, launchers, and system indicators for a classic yet powerful desktop."
[[items]]
desktop = "cinnamon"
icon = "slides/img/cinnamon_settings.png"
title = "Settings Manager"
description = "Fine-tune every aspect of your Cinnamon desktop through the comprehensive Settings Manager."
[[items]]
desktop = "cinnamon"
icon = "slides/img/cinnamon_desklets.png"
title = "Cinnamon Desklets"
description = "Keep notes, clocks, and system info always visible on your desktop with Cinnamon Desklets."
# ── GNOME ─────────────────────────────────────────────────────────
[[items]]
desktop = "gnome"
icon = "slides/img/gnome.png"
title = "PisiLinux GNOME"
description = "Reduce distractions and focus on your work with the clean and focused GNOME desktop environment."
[[items]]
desktop = "gnome"
icon = "slides/img/gnome_search.png"
title = "GNOME Search"
description = "Press the Super key and start typing. Instantly reach applications, files, and settings."
[[items]]
desktop = "gnome"
icon = "slides/img/gnome_extensions.png"
title = "GNOME Extensions"
description = "Shape your desktop to your needs with hundreds of extensions from extensions.gnome.org."
# ── KDE ───────────────────────────────────────────────────────────
[[items]]
desktop = "kde"
icon = "slides/img/kde.png"
title = "PisiLinux KDE Plasma"
description = "Meet the modern, customizable, and powerful KDE Plasma desktop. Everything is at your fingertips."
[[items]]
desktop = "kde"
icon = "slides/img/file_manager.png"
title = "Dolphin File Manager"
description = "Dolphin makes managing your files easy with tabbed browsing, dual-panel view, and powerful search."
[[items]]
desktop = "kde"
icon = "slides/img/kde_connect.png"
title = "KDE Connect"
description = "Connect your phone to your computer. Notifications, file transfer, and clipboard sharing — all with KDE Connect."
[[items]]
desktop = "kde"
icon = "slides/img/discover.png"
title = "Discover Software Center"
description = "Easily find, install, and update applications from the visual interface with Discover."
# ── LXQt ──────────────────────────────────────────────────────────
[[items]]
desktop = "lxqt"
icon = "slides/img/lxqt.png"
title = "PisiLinux LXQt"
description = "A lightweight, fast, and modern LXQt desktop environment. Low resource consumption, high performance."
[[items]]
desktop = "lxqt"
icon = "slides/img/lxqt_panel.png"
title = "LXQt Panel"
description = "The traditional panel layout puts the taskbar, application launcher, and system tray always within reach."
[[items]]
desktop = "lxqt"
icon = "slides/img/file_manager.png"
title = "PCManFM-Qt File Manager"
description = "Manage files effortlessly with the lightweight and fast PCManFM-Qt. Tabbed navigation and a customizable interface."
# ── LXDE ──────────────────────────────────────────────────────────
[[items]]
desktop = "lxde"
icon = "slides/img/lxde.png"
title = "PisiLinux LXDE"
description = "The legendary lightweight LXDE desktop. Known for extremely low resource usage and a reliable build."
[[items]]
desktop = "lxde"
icon = "slides/img/lxde_openbox.png"
title = "Openbox-Based"
description = "LXDE uses the Openbox window manager. Fine-tune it for a smooth and responsive experience."
[[items]]
desktop = "lxde"
icon = "slides/img/file_manager.png"
title = "PCManFM File Manager"
description = "PCManFM speeds up your file operations with its fast and lightweight build, supporting tree view and tabs."
# ── Lumina ────────────────────────────────────────────────────────
[[items]]
desktop = "lumina"
icon = "slides/img/lumina.png"
title = "PisiLinux Lumina"
description = "A lightweight and independent Lumina desktop. Qt-based, designed to run cleanly on BSD and Linux systems."
[[items]]
desktop = "lumina"
icon = "slides/img/lumina_applications.png"
title = "Application Support"
description = "Lumina works well with core utilities and many popular Qt apps, providing a practical everyday experience."
[[items]]
desktop = "lumina"
icon = "slides/img/lumina_theme.png"
title = "Theme Customization"
description = "Easily customize color schemes and interface styles with Lumina's flexible theme system."
[[items]]
desktop = "lumina"
icon = "slides/img/lumina_panel.png"
title = "Lumina Panel"
description = "A clean panel with a taskbar, application launcher, and system tray keeps your desktop organized."
# ── MATE ──────────────────────────────────────────────────────────
[[items]]
desktop = "mate"
icon = "slides/img/mate.png"
title = "PisiLinux MATE"
description = "MATE for those who love the traditional desktop layout. Developed with GTK+ 3 for a modern and smooth experience."
[[items]]
desktop = "mate"
icon = "slides/img/file_manager.png"
title = "Caja File Manager"
description = "Manage your files easily with MATE's powerful Caja. Dual panel, tabbed navigation, and rich features."
[[items]]
desktop = "mate"
icon = "slides/img/mate_settings.png"
title = "MATE Settings"
description = "Customize your MATE desktop with comprehensive settings for appearance, display, keyboard, and more."
[[items]]
desktop = "mate"
icon = "slides/img/mate_applications.png"
title = "Rich Application Suite"
description = "MATE comes with a rich set of classic applications — from text editors to image viewers, all well-integrated."
# ── XFCE ──────────────────────────────────────────────────────────
[[items]]
desktop = "xfce"
icon = "slides/img/xfce.png"
title = "PisiLinux XFCE"
description = "A lightweight, fast, and user-friendly XFCE desktop environment. Low resource consumption, high performance."
[[items]]
desktop = "xfce"
icon = "slides/img/xfce_panel.png"
title = "XFCE Panel"
description = "The traditional panel layout puts the taskbar, application menu, and system tray always within reach."
[[items]]
desktop = "xfce"
icon = "slides/img/file_manager.png"
title = "Thunar File Manager"
description = "Manage your files easily with the fast and lightweight Thunar. Tree view, tabbed navigation, and customizable features."
[[items]]
desktop = "xfce"
icon = "slides/img/xfce_settings.png"
title = "Whisker Menu"
description = "A modern alternative to the classic application menu. Find apps easily and organize your favorites with Whisker Menu."
+81 -1
View File
@@ -113,6 +113,7 @@ impl std::fmt::Display for PartitionTableType {
/// Manuel bölümleme için desteklenen dosya sistemleri. /// Manuel bölümleme için desteklenen dosya sistemleri.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
#[allow(dead_code)]
pub enum FsType { pub enum FsType {
Ext4, Ext4,
Ext3, Ext3,
@@ -122,7 +123,6 @@ pub enum FsType {
Ntfs, Ntfs,
Swap, Swap,
Lvm, // LVM fiziksel bölüm (PV) Lvm, // LVM fiziksel bölüm (PV)
#[allow(dead_code)]
LvmLv, // LVM mantıksal bölüm (LV) LvmLv, // LVM mantıksal bölüm (LV)
} }
@@ -150,6 +150,7 @@ impl std::fmt::Display for FsType {
/// LVM Volume Group yapılandırması. /// LVM Volume Group yapılandırması.
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
#[allow(dead_code)]
pub struct VolumeGroup { pub struct VolumeGroup {
pub name: String, pub name: String,
pub pv_devices: Vec<String>, // PV olarak kullanılacak bölüm aygıtları pub pv_devices: Vec<String>, // PV olarak kullanılacak bölüm aygıtları
@@ -167,11 +168,86 @@ pub struct LogicalVolume {
pub mountpoint: String, pub mountpoint: String,
} }
/// Cihaz yolundan ait olduğu diskin yolunu çıkarır.
///
/// - `/dev/sda1` → `/dev/sda`
/// - `/dev/nvme0n1p2` → `/dev/nvme0n1`
/// - `/dev/mmcblk0p1` → `/dev/mmcblk0`
#[allow(dead_code)] // genel yardımcı; testlerde ve gelecekteki işlerde kullanılır
pub fn device_to_disk(device: &str) -> String {
let base = device.trim_start_matches("/dev/");
// NVMe/MMC/loop aygıtlarında bölüm ayracı "p" + rakamdır
let needs_p = base.contains("nvme") || base.contains("mmcblk") || base.contains("loop");
if let Some(pos) = base.rfind('p') {
if needs_p && base[pos + 1..].chars().all(|c| c.is_ascii_digit()) && !base[pos + 1..].is_empty() {
return format!("/dev/{}", &base[..pos]);
}
}
// SATA/virtio: sondaki tüm rakamları at
let trimmed = base.trim_end_matches(|c: char| c.is_ascii_digit());
format!("/dev/{}", trimmed)
}
/// Cihaz adından bölüm numarasını çıkarır ("/dev/sda3" → 3,
/// "/dev/nvme0n1p2" → 2). Sonda rakam yoksa None döner.
pub fn part_number(device: &str) -> Option<u32> {
let base = device.trim_start_matches("/dev/");
let digits: String = base.chars().rev().take_while(|c| c.is_ascii_digit()).collect();
digits.chars().rev().collect::<String>().parse().ok()
}
/// Bölüm tanımlarına parted'ın gerçek slot davranışını izleyerek
/// aygıt numarası atar.
///
/// Eski Yali modeliyle aynı mantık: fiziksel (silinmemiş) bölümler
/// kendi numaralarını korur; silinmek işaretlenenlerin slotları boşalır
/// ve yeni tanımlar en küçük boş slotu sırayla doldurur. Örn. sda2
/// silinip yeni bölüm eklenirse yeni bölüm sda2 olur; sda3 yerinde kalır.
pub fn assign_partition_numbers(parts: &mut [CustomPartition]) {
use std::collections::BTreeSet;
// Disk bazlı kullanılan slot kümeleri
let mut used: std::collections::HashMap<String, BTreeSet<u32>> = std::collections::HashMap::new();
for p in parts.iter() {
if p.delete { continue; } // silinecekler slot açar
if !p.orig_device.is_empty() { // fiziksel: kendi numarası
if let Some(n) = part_number(&p.orig_device) {
used.entry(p.disk.clone()).or_default().insert(n);
}
}
}
for p in parts.iter_mut() {
if p.delete {
// Silinecek bölüm: kurulumda yok olacak; adı olduğu gibi kalır,
// yeni slot tüketmez.
continue;
}
match part_number(&p.orig_device) {
Some(n) => { p.device = part_path(&p.disk, n); }
None => {
let set = used.entry(p.disk.clone()).or_default();
// en küçük boş slot
let mut n = 1u32;
while set.contains(&n) { n += 1; }
set.insert(n);
p.device = part_path(&p.disk, n);
}
}
}
}
/// Kullanıcının manuel olarak tanımladığı tek bir bölüm. /// Kullanıcının manuel olarak tanımladığı tek bir bölüm.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct CustomPartition { pub struct CustomPartition {
/// Bölümün ait olduğu disk (ör. "/dev/sda").
/// Çoklu disk kurulumunda bölümler diske göre gruplanır.
pub disk: String,
/// Bölüm aygit adı; oluşturulacak (ör. "/dev/sda1") /// Bölüm aygit adı; oluşturulacak (ör. "/dev/sda1")
pub device: String, pub device: String,
/// Taramada bulunan fiziksel cihaz adı (örn. "/dev/sda3").
/// Kullanıcı tarafından yeni eklenen tanımlarda boştur.
/// Silme işleminde satırın listede kalıp yeniden
/// tanımlanabilmesini sağlar.
pub orig_device: String,
/// Boyut MB cinsinden; 0 = "kalan alan" /// Boyut MB cinsinden; 0 = "kalan alan"
pub size_mb: u64, pub size_mb: u64,
/// Dosya sistemi /// Dosya sistemi
@@ -186,6 +262,10 @@ pub struct CustomPartition {
pub luks_name: String, pub luks_name: String,
/// false = biçimlendir, true = olduğu gibi bırak (mevcut bölümleri koru) /// false = biçimlendir, true = olduğu gibi bırak (mevcut bölümleri koru)
pub skip_format: bool, pub skip_format: bool,
/// true = kurulum sırasında bu bölüm diskten SİLİNECEK
/// (eski Yali'deki OperationDestroyDevice karşılığı).
/// Satır listede görünür kalır; istenirse işaret geri alınır.
pub delete: bool,
} }
/// Kurulum boyunca toplanan tüm veriler. /// Kurulum boyunca toplanan tüm veriler.
+1 -1
View File
@@ -633,7 +633,7 @@ pub fn build_custom_job_queue(
let src = if cfg.source.is_empty() { detect_source_dir() } else { cfg.source }; let src = if cfg.source.is_empty() { detect_source_dir() } else { cfg.source };
let mut jobs: Vec<Box<dyn Job>> = vec![ let mut jobs: Vec<Box<dyn Job>> = vec![
Box::new(CustomPartitionJob { disk: disk.clone(), custom_partitions: custom_partitions.clone() }), Box::new(CustomPartitionJob { custom_partitions: custom_partitions.clone() }),
Box::new(SetupLvmJob { volume_groups: volume_groups.clone(), logical_volumes: logical_volumes.clone() }), Box::new(SetupLvmJob { volume_groups: volume_groups.clone(), logical_volumes: logical_volumes.clone() }),
Box::new(MountCustomPartitionsJob { custom_partitions: custom_partitions.clone(), logical_volumes: logical_volumes.clone(), mount: mt.clone() }), Box::new(MountCustomPartitionsJob { custom_partitions: custom_partitions.clone(), logical_volumes: logical_volumes.clone(), mount: mt.clone() }),
Box::new(MountBindJob { mount: mt.clone() }), Box::new(MountBindJob { mount: mt.clone() }),
+212 -66
View File
@@ -204,15 +204,15 @@ impl Job for PartitionJob {
ui.log(format!("parted {} mktable {}", disk, table_label)); ui.log(format!("parted {} mktable {}", disk, table_label));
run_parted(&["-s", disk, "mktable", table_label]).await?; run_parted(&["-s", disk, "mktable", table_label]).await?;
let mut next_mb: u64 = 1; // İlk MB'ı boot kayıtları için bırak let mut next_mb: u64 = 1; // İlk MiB'ı boot kayıtları için bırak
// 2. EFI bölümü (yalnızca GPT/UEFI) // 2. EFI bölümü (yalnızca GPT/UEFI)
if self.plan.efi_mb > 0 { if self.plan.efi_mb > 0 {
let end_mb = next_mb + self.plan.efi_mb; let end_mb = next_mb + self.plan.efi_mb;
ui.log(format!("EFI bölümü oluşturuluyor: {}–{} MB", next_mb, end_mb)); ui.log(format!("EFI bölümü oluşturuluyor: {}–{} MiB", next_mb, end_mb));
run_parted(&[ run_parted(&[
"-s", disk, "mkpart", "fat32", "-s", disk, "mkpart", "fat32",
&format!("{}MB", next_mb), &format!("{}MB", end_mb), &format!("{}MiB", next_mb), &format!("{}MiB", end_mb),
]).await?; ]).await?;
run_parted(&["-s", disk, "set", "1", "esp", "on"]).await?; run_parted(&["-s", disk, "set", "1", "esp", "on"]).await?;
next_mb = end_mb; next_mb = end_mb;
@@ -221,16 +221,16 @@ impl Job for PartitionJob {
// 3. Swap bölümü // 3. Swap bölümü
let swap_end = next_mb + self.plan.swap_mb; let swap_end = next_mb + self.plan.swap_mb;
let swap_num: u32 = if self.plan.efi_mb > 0 { 2 } else { 1 }; let swap_num: u32 = if self.plan.efi_mb > 0 { 2 } else { 1 };
ui.log(format!("Swap bölümü oluşturuluyor: {}–{} MB", next_mb, swap_end)); ui.log(format!("Swap bölümü oluşturuluyor: {}–{} MiB", next_mb, swap_end));
run_parted(&[ run_parted(&[
"-s", disk, "mkpart", "linux-swap", "-s", disk, "mkpart", "linux-swap",
&format!("{}MB", next_mb), &format!("{}MB", swap_end), &format!("{}MiB", next_mb), &format!("{}MiB", swap_end),
]).await?; ]).await?;
next_mb = swap_end; next_mb = swap_end;
// 4. Kök bölümü (kalan alan) // 4. Kök bölümü (kalan alan)
let root_num: u32 = if self.plan.efi_mb > 0 { 3 } else { 2 }; let root_num: u32 = if self.plan.efi_mb > 0 { 3 } else { 2 };
ui.log(format!("Kök bölümü oluşturuluyor: {}MB – disk sonu", next_mb)); ui.log(format!("Kök bölümü oluşturuluyor: {}MiB – disk sonu", next_mb));
run_parted(&[ run_parted(&[
"-s", disk, "mkpart", "ext4", "-s", disk, "mkpart", "ext4",
&format!("{}MB", next_mb), "100%", &format!("{}MB", next_mb), "100%",
@@ -448,9 +448,75 @@ async fn read_partition_uuids(
// MANUEL BÖLÜMLEME İÇİN DESTEKLEYİCİ JOB'LAR // MANUEL BÖLÜMLEME İÇİN DESTEKLEYİCİ JOB'LAR
// ───────────────────────────────────────────── // ─────────────────────────────────────────────
/// Manuel bölümleme ayarlarını disk üzerinde uygulayan ve biçimlendiren Job. /// Diskteki boş bölgeleri MB cinsinden hesaplar (sysfs tabanlı).
///
/// `exclude_nums`: silinecek işaretli bölümler — boşluk olarak
/// davranılır (kurulum sırasında önce silindikleri için).
/// Dönen değer: (başlangıç_mb, bitiş_mb) aralıkları; disk sonunda
/// GPT yedek başlığı için 1MB pay bırakılır.
fn disk_free_regions_mb(disk: &str, exclude_nums: &[u32]) -> Result<Vec<(u64, u64)>, String> {
let base = disk.trim_start_matches("/dev/");
const SECTOR: u64 = 512;
const MB: u64 = 1024 * 1024;
let disk_sectors: u64 = std::fs::read_to_string(format!("/sys/block/{}/size", base))
.map_err(|e| format!("{} boyutu okunamadı: {}", disk, e))?
.trim().parse().unwrap_or(0);
if disk_sectors == 0 {
return Err(format!("{} disk boyutu okunamadı", disk));
}
let total_mb = disk_sectors * SECTOR / MB;
let usable_total = total_mb.saturating_sub(1); // disk sonu güvenlik payı
// Dolu aralıklar
let mut occupied: Vec<(u64, u64)> = Vec::new();
if let Ok(entries) = std::fs::read_dir(format!("/sys/block/{}", base)) {
for entry in entries.flatten() {
let name = entry.file_name().to_string_lossy().to_string();
if !name.starts_with(base) || name == base { continue; }
let num = match crate::installer::part_number(&format!("/dev/{}", name)) {
Some(n) => n,
None => continue,
};
if exclude_nums.contains(&num) { continue; }
let start_sectors: u64 = std::fs::read_to_string(entry.path().join("start"))
.ok().and_then(|s| s.trim().parse().ok()).unwrap_or(0);
let len_sectors: u64 = std::fs::read_to_string(entry.path().join("size"))
.ok().and_then(|s| s.trim().parse().ok()).unwrap_or(0);
if len_sectors == 0 { continue; }
let start_mb = (start_sectors * SECTOR + MB - 1) / MB;
let end_mb = ((start_sectors + len_sectors) * SECTOR) / MB;
if end_mb > start_mb {
occupied.push((start_mb, end_mb.min(usable_total)));
}
}
}
occupied.sort();
// Boşlukları çıkar (ilk 1MB boot alanı hariç)
const HEADROOM_MB: u64 = 1;
let mut regions: Vec<(u64, u64)> = Vec::new();
let mut cursor = HEADROOM_MB.min(usable_total);
for (s, e) in &occupied {
if *s > cursor { regions.push((cursor, *s)); }
cursor = cursor.max(*e);
}
if cursor < usable_total { regions.push((cursor, usable_total)); }
regions.retain(|(s, e)| e > s);
Ok(regions)
}
/// Manuel bölümleme ayarlarını granular (eski Yali Operation modeli)
/// yaklaşımla uygulayan Job.
///
/// Eski Yali'nin DeviceTree/Operation mimarisiyle aynı felsefe:
/// - Dokunulmayan disklere ve bölümlere HİÇ dokunulmaz (veriler korunur)
/// - Silinmek işaretli bölümler `parted rm` ile kaldırılır
/// (OperationDestroyDevice corresponds)
/// - Yeni tanımlar diskin BOŞ alanlarına yerleştirilir; tüm disk
/// sıfırlanmaz (OperationCreateDevice corresponds)
/// - skip_format=false olanlar biçimlendirilir (OperationCreateFormat)
pub struct CustomPartitionJob { pub struct CustomPartitionJob {
pub disk: String,
pub custom_partitions: Vec<CustomPartition>, pub custom_partitions: Vec<CustomPartition>,
} }
@@ -460,68 +526,147 @@ impl Job for CustomPartitionJob {
async fn run(&self, ui: &UiSender) -> Result<(), String> { async fn run(&self, ui: &UiSender) -> Result<(), String> {
let is_uefi = std::path::Path::new("/sys/firmware/efi").exists(); let is_uefi = std::path::Path::new("/sys/firmware/efi").exists();
// 0. Diski hazırla: bağlamaları ayır, kernel tablosunu temizle
prepare_disk(&self.disk, ui).await;
let is_gpt = is_uefi; // Genellikle UEFI ise GPT kullanılır
// MBR'de "primary" zorunludur, GPT'de ise bu alan isim (label) alanıdır. // MBR'de "primary" zorunludur, GPT'de ise bu alan isim (label) alanıdır.
let part_type_or_name = if is_gpt { "" } else { "primary" }; let part_type_or_name = if is_uefi { "" } else { "primary" };
// 1. Bölüm tablosu oluştur // 0. İşlem yapılan diskleri tanım sırasına göre topla
let table_label = if is_uefi { "gpt" } else { "msdos" }; let mut disks: Vec<String> = Vec::new();
ui.log(format!("parted {} mktable {}", self.disk, table_label)); for part in &self.custom_partitions {
run_parted(&["-s", &self.disk, "mktable", table_label]).await?; if !disks.contains(&part.disk) {
disks.push(part.disk.clone());
let mut next_mb: u64 = 1; // İlk MB boot sektörü
// 2. Bölümleri parted ile oluştur
for (i, part) in self.custom_partitions.iter().enumerate() {
let part_num = (i + 1) as u32;
let end_str = if part.size_mb == 0 {
"100%".to_string()
} else {
format!("{}MB", next_mb + part.size_mb)
};
let start_str = format!("{}MB", next_mb);
let fs_label = match part.fstype {
FsType::Fat32 => "fat32",
FsType::Swap => "linux-swap",
FsType::Ext4 => "ext4",
FsType::Ext3 => "ext3",
FsType::Btrfs => "btrfs",
FsType::Xfs => "xfs",
FsType::Ntfs => "ntfs",
// LVM PV için parted "ext4" ile işaretle, pvcreate sonra
FsType::Lvm => "ext4",
// LVM LV doğrudan parted ile oluşturulmaz
FsType::LvmLv => continue,
};
ui.log(format!(
"Bölüm {} oluşturuluyor: {} - {} ({})",
part_num, start_str, end_str, fs_label
));
run_parted(&[
"-s", &self.disk, "mkpart", part_type_or_name, fs_label,
&start_str, &end_str
]).await?;
if part.mountpoint == "/boot/efi" || part.fstype == FsType::Fat32 {
run_parted(&["-s", &self.disk, "set", &part_num.to_string(), "esp", "on"]).await?;
}
if part.size_mb > 0 {
next_mb += part.size_mb;
} }
} }
// 2.5. Tüm mkpart bitti; kernel'e bildir ve aygıt düğümlerini bekle for disk in &disks {
sync_partition_table(&self.disk).await; let parts: Vec<&CustomPartition> = self.custom_partitions.iter()
.filter(|p| p.disk == *disk).collect();
// 3. Biçimlendir (LUKS ile şifreleme desteklenir) // Bu disk için planlanan işlemler - her iki durumda da silinmesi
// istenen bölümlerin numaralarını topla: (1) fiziksel bölümler
// (orig_device varsa) ve (2) kullanıcı tanımlı silinme işaretleri.
let destroys: Vec<u32> = parts.iter()
.filter(|p| p.delete)
.filter_map(|p| crate::installer::part_number(&p.orig_device))
.collect();
let mut creates: Vec<&CustomPartition> = parts.iter()
.filter(|p| !p.delete && p.orig_device.is_empty() && p.fstype != FsType::LvmLv)
.map(|p| *p)
.collect();
// Sabit boyutlular ÖNCE yerleşir; "kalan alan" (size 0)
// tanımları EN SON, geriye kalan boşluğa yerleştirilir.
// (Stable sort → aynı grupta tanım sırası korunur.)
creates.sort_by_key(|p| p.size_mb == 0);
let has_format = parts.iter().any(|p| !p.delete && !p.skip_format);
if destroys.is_empty() && creates.is_empty() && !has_format {
ui.log(format!("⏭️ {}: işlem yok, korunuyor", disk));
continue; // eski Yali gibi dokunulmayan diske girilmez
}
ui.log(format!("── Disk işleniyor: {} ──", disk));
// Bağlamaları ayır, swap kapat
prepare_disk(disk, ui).await;
// ── FAZ A: SİLMEK İŞARETLİ BÖLÜMLER (büyükten küçüğe numara) ──
let mut del_sorted = destroys.clone();
del_sorted.sort_unstable_by(|a, b| b.cmp(a));
for n in &del_sorted {
ui.log(format!("parted {} rm {} (bölüm siliniyor)", disk, n));
run_parted(&["-s", disk, "rm", &n.to_string()]).await?;
}
if !del_sorted.is_empty() {
sync_partition_table(disk).await;
}
// ── FAZ B: YENİ BÖLÜMLER (boş alana yerleştir) ──────────────
if !creates.is_empty() {
let mut regions = disk_free_regions_mb(disk, &destroys)?;
for part in &creates {
let fs_label = match part.fstype {
FsType::Fat32 => "fat32",
FsType::Swap => "linux-swap",
FsType::Ext4 => "ext4",
FsType::Ext3 => "ext3",
FsType::Btrfs => "btrfs",
FsType::Xfs => "xfs",
FsType::Ntfs => "ntfs",
// LVM PV için parted "ext4" ile işaretle, pvcreate sonra
FsType::Lvm => "ext4",
FsType::LvmLv => continue,
};
// Yerleşim: boyut belirtilmişse sığan İLK boş bölge,
// "kalan alan" ise en büyük bölgenin tamamı.
let need = part.size_mb;
let placement = if need == 0 {
let idx = regions.iter().enumerate()
.max_by_key(|(_, (s, e))| e.saturating_sub(*s))
.map(|(i, _)| i);
match idx {
Some(i) => {
let r = regions.remove(i);
Some(r)
}
None => None,
}
} else {
let mut found = None;
for (i, (s, e)) in regions.iter().enumerate() {
if e.saturating_sub(*s) >= need {
found = Some((i, (*s, s + need)));
break;
}
}
match found {
Some((i, seg)) => {
let (_, e) = regions[i];
let start = seg.0;
let new_start = start + need;
if e > new_start {
regions[i] = (new_start, e);
} else {
regions.remove(i);
}
Some(seg)
}
None => None,
}
};
let (start_mb, end_mb) = placement.ok_or_else(|| format!(
"{} üzerinde {} MB için yeterli boş alan yok", disk, need))?;
// DİKKAT: parted "MB" sonekini onalık (10^6) okur;
// hesaplarımız binary (2^20) olduğu için "MiB" zorunlu.
// Tamsayı MiB = 2048 sektörün katı → parted hizalama
// hatası vermez.
let start_str = format!("{}MiB", start_mb);
let end_str = format!("{}MiB", end_mb);
let slot = crate::installer::part_number(&part.device).unwrap_or(0);
ui.log(format!(
"{} üzerinde yeni bölüm oluşturuluyor: {} - {} ({})",
disk, start_str, end_str, fs_label
));
run_parted(&[
"-s", disk, "mkpart", part_type_or_name, fs_label,
&start_str, &end_str
]).await?;
if part.mountpoint == "/boot/efi" || part.fstype == FsType::Fat32 {
run_parted(&["-s", disk, "set", &slot.to_string(), "esp", "on"]).await?;
}
}
sync_partition_table(disk).await;
}
// ── FAZ C: BİÇİMLENDİRME aşağıdaki ortak döngüde ────────────
}
// Biçimlendir (LUKS ile şifreleme desteklenir) — tüm diskler.
// Silinmek işaretli bölümlere hiç dokunulmaz.
for part in &self.custom_partitions { for part in &self.custom_partitions {
if part.delete { continue; }
let path = &part.device; let path = &part.device;
// LUKS şifreleme (kök veya isteğe bağlı bölümler) // LUKS şifreleme (kök veya isteğe bağlı bölümler)
@@ -667,7 +812,8 @@ impl Job for MountCustomPartitionsJob {
async fn run(&self, ui: &UiSender) -> Result<(), String> { async fn run(&self, ui: &UiSender) -> Result<(), String> {
// 1. Kök (/) bölümünü (fiziksel partition veya LVM LV) bul ve bağla // 1. Kök (/) bölümünü (fiziksel partition veya LVM LV) bul ve bağla
let root_device = if let Some(root_part) = self.custom_partitions.iter().find(|p| p.mountpoint == "/") { let root_device = if let Some(root_part) = self.custom_partitions.iter()
.find(|p| p.mountpoint == "/" && !p.delete) {
self.device_path(root_part) self.device_path(root_part)
} else if let Some(root_lv) = self.logical_volumes.iter().find(|lv| lv.mountpoint == "/") { } else if let Some(root_lv) = self.logical_volumes.iter().find(|lv| lv.mountpoint == "/") {
format!("/dev/{}/{}", root_lv.vg_name, root_lv.name) format!("/dev/{}/{}", root_lv.vg_name, root_lv.name)
@@ -687,7 +833,7 @@ impl Job for MountCustomPartitionsJob {
let mut mount_items = Vec::new(); let mut mount_items = Vec::new();
for part in &self.custom_partitions { for part in &self.custom_partitions {
if part.mountpoint != "/" && part.fstype != FsType::Swap && !part.mountpoint.is_empty() { if part.mountpoint != "/" && part.fstype != FsType::Swap && !part.mountpoint.is_empty() && !part.delete {
mount_items.push(MountItem { mount_items.push(MountItem {
device: self.device_path(part), device: self.device_path(part),
mountpoint: part.mountpoint.clone(), mountpoint: part.mountpoint.clone(),
@@ -747,7 +893,7 @@ impl Job for FstabFromCustomPartitionsJob {
); );
for part in &self.custom_partitions { for part in &self.custom_partitions {
if part.mountpoint.is_empty() { continue; } if part.mountpoint.is_empty() || part.delete { continue; }
let is_encrypted = part.encrypt && !part.luks_password.is_empty(); let is_encrypted = part.encrypt && !part.luks_password.is_empty();
+1
View File
@@ -509,6 +509,7 @@ impl eframe::App for YaliApp {
.inner_margin(egui::Margin::same(20.0))) .inner_margin(egui::Margin::same(20.0)))
.show(ctx, |ui| { .show(ctx, |ui| {
egui::ScrollArea::vertical() egui::ScrollArea::vertical()
.scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::AlwaysHidden)
.id_source("main_scroll") .id_source("main_scroll")
.show(ui, |ui| { .show(ui, |ui| {
if let Some(step) = self.steps.get_mut(cur) { if let Some(step) = self.steps.get_mut(cur) {
+267 -1274
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+55 -15
View File
@@ -43,15 +43,25 @@ impl InstallerStep for SummaryStep {
}; };
summary_row(ui, t!("summary_keyboard"), &kb); summary_row(ui, t!("summary_keyboard"), &kb);
let disk_str = match &state.selected_disk { let disk_str = if state.erase_disk {
Some(d) => { match &state.selected_disk {
if state.erase_disk { Some(d) => format!("{} \u{EA6B} {}", d, t!("summary_erase_mode")),
format!("{} \u{EA6B} {}", d, t!("summary_erase_mode")) None => t!("summary_not_selected").to_string(),
} else { }
format!("{} \u{EA6B} {}", d, t!("summary_manual_mode")) } else if !state.custom_partitions.is_empty() {
// Manuel modda tanım yapılan TÜM diskleri listele
let mut disks: Vec<&str> = Vec::new();
for part in &state.custom_partitions {
if !disks.contains(&part.disk.as_str()) {
disks.push(&part.disk);
} }
} }
None => t!("summary_not_selected").to_string(), format!("{} \u{EA6B} {}", disks.join(", "), t!("summary_manual_mode"))
} else {
match &state.selected_disk {
Some(d) => format!("{} \u{EA6B} {}", d, t!("summary_manual_mode")),
None => t!("summary_not_selected").to_string(),
}
}; };
summary_row(ui, t!("summary_disk"), &disk_str); summary_row(ui, t!("summary_disk"), &disk_str);
@@ -73,15 +83,45 @@ impl InstallerStep for SummaryStep {
} }
} else if !state.custom_partitions.is_empty() { } else if !state.custom_partitions.is_empty() {
let mut plan_lines = Vec::new(); let mut plan_lines = Vec::new();
// Bölümleri ait oldukları diske göre gruplu listele
let mut seen_disks: Vec<&str> = Vec::new();
for part in &state.custom_partitions { for part in &state.custom_partitions {
let size_str = if part.size_mb == 0 { if !seen_disks.contains(&part.disk.as_str()) {
t!("mp_size_remaining").to_string() seen_disks.push(&part.disk);
} else if part.size_mb >= 1024 { }
format!("{:.1} GB", part.size_mb as f64 / 1024.0) }
} else { for d in seen_disks {
format!("{} MB", part.size_mb) plan_lines.push(format!("\u{EDFB} {}", d));
}; for part in state.custom_partitions.iter().filter(|p| p.disk == d) {
plan_lines.push(format!("{} \u{EA6B} {} ({}, {})", part.device, part.mountpoint, part.fstype, size_str)); let size_str = if part.size_mb == 0 {
// Sembolik boyut: kalan kapasiteyi göster
let others: u64 = state.custom_partitions.iter()
.filter(|q| q.disk == part.disk && !q.delete && q.size_mb > 0)
.map(|q| q.size_mb)
.sum();
let cap = state.available_disks.iter()
.find(|dk| dk.name == part.disk)
.map(|dk| dk.size_bytes / 1_048_576)
.unwrap_or(0);
let rem = cap.saturating_sub(others);
if rem >= 1024 {
format!("{:.1} GB", rem as f64 / 1024.0)
} else {
format!("{} MB", rem)
}
} else if part.size_mb >= 1024 {
format!("{:.1} GB", part.size_mb as f64 / 1024.0)
} else {
format!("{} MB", part.size_mb)
};
if part.delete {
plan_lines.push(format!("{} \u{EA6B} {} ({}, {}) \u{ECA1} {}",
part.device, "—", "—", size_str, t!("mp_will_delete")));
} else {
plan_lines.push(format!("{} \u{EA6B} {} ({}, {})",
part.device, part.mountpoint, part.fstype, size_str));
}
}
} }
summary_row(ui, t!("summary_custom_partitions"), &plan_lines.join("\n")); summary_row(ui, t!("summary_custom_partitions"), &plan_lines.join("\n"));
} }
+133 -16
View File
@@ -52,6 +52,8 @@ pub fn c_sidebar_text() -> Color32 { color_from_hex(SIDEBAR_TEXT.load(Ordering::
pub fn c_footer_bg() -> Color32 { color_from_hex(BG_FOOTER.load(Ordering::Relaxed)) } pub fn c_footer_bg() -> Color32 { color_from_hex(BG_FOOTER.load(Ordering::Relaxed)) }
pub fn c_root_bg() -> Color32 { color_from_hex(BG_ROOT.load(Ordering::Relaxed)) } pub fn c_root_bg() -> Color32 { color_from_hex(BG_ROOT.load(Ordering::Relaxed)) }
pub fn c_keyboard_bg() -> Color32 { color_from_hex(KEYBOARD_BG.load(Ordering::Relaxed)) } pub fn c_keyboard_bg() -> Color32 { color_from_hex(KEYBOARD_BG.load(Ordering::Relaxed)) }
//pub fn select_disk_label() -> Color32 { color_from_hex(0xFFFFFF) }
pub fn set_theme_mode(is_dark: bool) { pub fn set_theme_mode(is_dark: bool) {
if is_dark { if is_dark {
@@ -108,10 +110,85 @@ pub fn apply(ctx: &egui::Context, is_dark: bool) {
ctx.set_visuals(build_visuals(is_dark)); ctx.set_visuals(build_visuals(is_dark));
} }
// fn setup_fonts(ctx: &egui::Context) {
// let mut fonts = egui::FontDefinitions::default();
// // 1. ADIM: Sadece Metin (Text) Fontunu Bul ve Yükle
// let text_font_paths = [
// "/usr/share/fonts/TTF/OpenSans-Regular.ttf",
// "/usr/share/fonts/noto/NotoSans-Regular.ttf",
// "/usr/share/fonts/exo-1/Exo-Regular.otf",
// "/usr/share/fonts/exo-1/Exo2-Medium.ttf",
// "/usr/share/fonts/exo-1/Exo-Medium.otf",
// ];
// for path in text_font_paths {
// if let Ok(data) = std::fs::read(path) {
// fonts.font_data.insert(
// "system_text_font".to_owned(),
// egui::FontData::from_owned(data),
// );
// // Metin fontunu en başa (0. indeks) koyuyoruz ki varsayılan olarak bu kullanılsın
// fonts
// .families
// .entry(egui::FontFamily::Proportional)
// .or_default()
// .insert(0, "system_text_font".to_owned());
// break;
// }
// }
// // 2. ADIM: Renkli Emoji Fontunu Ara ve "Fallback" (Yedek) Olarak Sona Ekle
// // ISO ortamlarında Noto Color Emoji genellikle bu yollardan birinde olur
// let emoji_font_paths = [
// "/usr/share/fonts/noto/NotoColorEmoji.ttf",
// ];
// for path in emoji_font_paths {
// if let Ok(data) = std::fs::read(path) {
// fonts.font_data.insert(
// "system_emoji_font".to_owned(),
// egui::FontData::from_owned(data),
// );
// // ÖNEMLİ: Emojiyi listenin SONUNA (push ile) ekliyoruz.
// // Böylece egui normal harfleri sistem fontuyla yazacak,
// // harf sistem fontunda yoksa (yani emojiyse) gidip bu fonttan alacak.
// fonts
// .families
// .entry(egui::FontFamily::Proportional)
// .or_default()
// .push("system_emoji_font".to_owned());
// fonts
// .families
// .entry(egui::FontFamily::Monospace)
// .or_default()
// .insert(0, "system_emoji_font".to_owned());
// break;
// }
// }
// // 3. ADIM: Remixicon ikon fontunu projeye göm (PUA bölgesindeki karakterler için fallback)
// let remixicon_data = include_bytes!("../../assets/remixicon.ttf");
// fonts.font_data.insert(
// "remixicon".to_owned(),
// egui::FontData::from_owned(remixicon_data.to_vec()),
// );
// fonts
// .families
// .entry(egui::FontFamily::Proportional)
// .or_default()
// .push("remixicon".to_owned());
// ctx.set_fonts(fonts);
// }
fn setup_fonts(ctx: &egui::Context) { fn setup_fonts(ctx: &egui::Context) {
let mut fonts = egui::FontDefinitions::default(); let mut fonts = egui::FontDefinitions::default();
// 1. ADIM: Sadece Metin (Text) Fontunu Bul ve Yükle // 1. ADIM: Metin ve Monospace için Sistem Fontu Yükleme
let text_font_paths = [ let text_font_paths = [
"/usr/share/fonts/TTF/OpenSans-Regular.ttf", "/usr/share/fonts/TTF/OpenSans-Regular.ttf",
"/usr/share/fonts/noto/NotoSans-Regular.ttf", "/usr/share/fonts/noto/NotoSans-Regular.ttf",
@@ -120,25 +197,59 @@ fn setup_fonts(ctx: &egui::Context) {
"/usr/share/fonts/exo-1/Exo-Medium.otf", "/usr/share/fonts/exo-1/Exo-Medium.otf",
]; ];
let mut _text_font_loaded = false;
for path in text_font_paths { for path in text_font_paths {
if let Ok(data) = std::fs::read(path) { if let Ok(data) = std::fs::read(path) {
fonts.font_data.insert( fonts.font_data.insert(
"system_text_font".to_owned(), "system_text_font".to_owned(),
egui::FontData::from_owned(data), egui::FontData::from_owned(data).into(), // Sürüme göre .into() veya std::sync::Arc::new(...)
); );
// Metin fontunu en başa (0. indeks) koyuyoruz ki varsayılan olarak bu kullanılsın // Proportional (Orantılı) fontun başına ekle
fonts fonts
.families .families
.entry(egui::FontFamily::Proportional) .entry(egui::FontFamily::Proportional)
.or_default() .or_default()
.insert(0, "system_text_font".to_owned()); .insert(0, "system_text_font".to_owned());
// Monospace için de bu fontu varsayılan olarak ekle (özel bir monospace bulunamadıysa)
fonts
.families
.entry(egui::FontFamily::Monospace)
.or_default()
.insert(0, "system_text_font".to_owned());
_text_font_loaded = true;
break; break;
} }
} }
// 2. ADIM: Renkli Emoji Fontunu Ara ve "Fallback" (Yedek) Olarak Sona Ekle // Ek Güvence: Özel bir Monospace fontu varsa öncelikli olarak onu ara
// ISO ortamlarında Noto Color Emoji genellikle bu yollardan birinde olur let mono_font_paths = [
"/usr/share/fonts/TTF/Hack-Regular.ttf",
"/usr/share/fonts/TTF/DejaVuSansMono.ttf",
"/usr/share/fonts/noto/NotoSansMono-Regular.ttf",
];
for path in mono_font_paths {
if let Ok(data) = std::fs::read(path) {
fonts.font_data.insert(
"system_mono_font".to_owned(),
egui::FontData::from_owned(data).into(),
);
// Monospace ailesinin 0. indeksine yerleştir
fonts
.families
.entry(egui::FontFamily::Monospace)
.or_default()
.insert(0, "system_mono_font".to_owned());
break;
}
}
// 2. ADIM: Color Emoji Fontunu Yedek (Fallback) Olarak EKLE
let emoji_font_paths = [ let emoji_font_paths = [
"/usr/share/fonts/noto/NotoColorEmoji.ttf", "/usr/share/fonts/noto/NotoColorEmoji.ttf",
]; ];
@@ -147,39 +258,44 @@ fn setup_fonts(ctx: &egui::Context) {
if let Ok(data) = std::fs::read(path) { if let Ok(data) = std::fs::read(path) {
fonts.font_data.insert( fonts.font_data.insert(
"system_emoji_font".to_owned(), "system_emoji_font".to_owned(),
egui::FontData::from_owned(data), egui::FontData::from_owned(data).into(),
); );
// ÖNEMLİ: Emojiyi listenin SONUNA (push ile) ekliyoruz. // Her iki aileye de SONA (push) ekle, böylece metinler bozulmaz
// Böylece egui normal harfleri sistem fontuyla yazacak,
// harf sistem fontunda yoksa (yani emojiyse) gidip bu fonttan alacak.
fonts fonts
.families .families
.entry(egui::FontFamily::Proportional) .entry(egui::FontFamily::Proportional)
.or_default() .or_default()
.push("system_emoji_font".to_owned()); .push("system_emoji_font".to_owned());
fonts fonts
.families .families
.entry(egui::FontFamily::Monospace) .entry(egui::FontFamily::Monospace)
.or_default() .or_default()
.insert(0, "system_emoji_font".to_owned()); .push("system_emoji_font".to_owned()); // insert(0, ...) yerine push yapıldı!
break; break;
} }
} }
// 3. ADIM: Remixicon ikon fontunu projeye göm (PUA bölgesindeki karakterler için fallback) // 3. ADIM: Remixicon İkon Fontunu Göm (PUA)
let remixicon_data = include_bytes!("../../assets/remixicon.ttf"); let remixicon_data = include_bytes!("../../assets/remixicon.ttf");
fonts.font_data.insert( fonts.font_data.insert(
"remixicon".to_owned(), "remixicon".to_owned(),
egui::FontData::from_owned(remixicon_data.to_vec()), egui::FontData::from_owned(remixicon_data.to_vec()).into(),
); );
fonts fonts
.families .families
.entry(egui::FontFamily::Proportional) .entry(egui::FontFamily::Proportional)
.or_default() .or_default()
.push("remixicon".to_owned()); .push("remixicon".to_owned());
fonts
.families
.entry(egui::FontFamily::Monospace)
.or_default()
.push("remixicon".to_owned());
ctx.set_fonts(fonts); ctx.set_fonts(fonts);
} }
@@ -187,7 +303,7 @@ fn setup_fonts(ctx: &egui::Context) {
fn build_visuals(is_dark: bool) -> Visuals { fn build_visuals(is_dark: bool) -> Visuals {
let mut v = if is_dark { Visuals::dark() } else { Visuals::light() }; let mut v = if is_dark { Visuals::dark() } else { Visuals::light() };
// Genel arka planlar // Genel arka planlar
v.window_fill = c_bg_dark(); v.window_fill = c_bg_dark();
v.panel_fill = c_bg_panel(); v.panel_fill = c_bg_panel();
@@ -243,13 +359,14 @@ fn build_visuals(is_dark: bool) -> Visuals {
fn build_style() -> egui::Style { fn build_style() -> egui::Style {
let mut style = egui::Style::default(); let mut style = egui::Style::default();
// Genel boşluk ve boyutlar // Genel boşluk ve boyutlar
style.spacing.item_spacing = Vec2::new(8.0, 6.0); style.spacing.item_spacing = Vec2::new(8.0, 6.0);
style.spacing.button_padding = Vec2::new(12.0, 6.0); style.spacing.button_padding = Vec2::new(12.0, 6.0);
style.spacing.indent = 16.0; style.spacing.indent = 16.0;
style.spacing.scroll.bar_width = 14.0; style.spacing.scroll.bar_width = 14.0;
// Metin boyutları // Metin boyutları
style.text_styles = [ style.text_styles = [
(egui::TextStyle::Small, FontId::proportional(11.0)), (egui::TextStyle::Small, FontId::proportional(11.0)),