Blok parçalama, blok düzeyindeki bir CSS kutusunu (bölüm veya paragraf gibi) parçalayıcı olarak adlandırılan bir parça kapsayıcıya bütün olarak sığmadığında birden fazla parçaya böler. Bir öğe bir öğe değildir, ancak çok sütunlu düzendeki bir sütunu veya sayfalandırılmış medyadaki bir sayfayı temsil eder.
Parçalanmanın gerçekleşmesi için içeriğin bir parçalama bağlamı içinde olması gerekir. Parçalanma bağlamı genellikle çok sütunlu kapsayıcı (içerik sütunlara bölünür) veya yazdırılırken (içerik sayfalara bölünür) oluşturulur. Çok satırlı uzun bir paragrafın, ilk satırların ilk parçaya, kalan satırların ise sonraki parçalara yerleştirilmesi için birden fazla parçaya bölünmesi gerekebilir.
![İki sütuna bölünmüş bir metin paragrafı.](https://developer.chrome.google.cn/static/docs/chromium/renderingng-fragmentation/image/a-paragraph-text-broken-9d19ed438bdc3.png?authuser=5&hl=tr)
Blok parçalanması, iyi bilinen başka bir parçalanma türüne benzer: çizgi parçalanması, diğer bir deyişle "satır sonu" olarak da bilinir. Birden fazla kelimeden oluşan (herhangi bir metin düğümü, <a>
öğesi vb.) ve satır sonlarına izin veren herhangi bir satır içi öğe birden fazla parçaya bölünebilir. Her parça farklı bir satır kutusuna yerleştirilir. Çizgi kutusu, sütunlar ve sayfalar için bir parçalayıcı ile eşdeğer satır içi parçalamadır.
LayoutNG blok parçalanması
LayoutNGBlockFragmentation, LayoutNG için parçalama motorunun yeniden yazılmış bir hali olan LayoutNGBlockFragmentation, ilk olarak Chrome 102 sürümünde kullanıma sunulmuştur. Veri yapıları açısından bakıldığında, NG öncesi birden fazla veri yapısını, doğrudan parça ağacında temsil edilen NG parçaları ile değiştirdi.
Örneğin, artık yazarların başlıktan hemen sonra aralardan kaçınmasına olanak tanıyan "break-before" ve "break-sonra" CSS özelliklerinde "kaçınma" değerini destekliyoruz. Bir sayfadaki son öğe başlık olduğunda, bölümün içeriği sonraki sayfada başladığında genellikle tuhaf görünür. Başlığın önüne ara vermek daha iyidir.
![Başlık hizalaması örneği.](https://developer.chrome.google.cn/static/docs/chromium/renderingng-fragmentation/image/the-example-shows-headi-f569d50783019.png?authuser=5&hl=tr)
Chrome, parçalanma taşmasını da destekler. Böylece monolitik (kırılamaz olduğu varsayılır) içerik birden fazla sütuna bölünmez ve gölgeler ve dönüştürmeler gibi boya efektleri doğru şekilde uygulanır.
LayoutNG'ta blok parçalanması tamamlandı
Chrome 102'de gönderilen temel parçalanma (çizgi düzeni, kayan öğeler ve akış dışı konumlandırma dahil, blok kapsayıcıları) Esnek ve ızgara parçalanması Chrome 103'te, tablo parçalanması ise Chrome 106'da gönderilir. Son olarak, yazdırma Chrome 108 sürümünde gönderilir. Blok parçalanması, düzen performansı için eski motora bağlı olan son özellikti.
Chrome 108 sürümünden itibaren eski motor artık düzeni gerçekleştirmek için kullanılmamaktadır.
Buna ek olarak, LayoutNG veri yapıları, boyamayı ve isabet testini destekler, ancak offsetLeft
ve offsetTop
gibi düzen bilgilerini okuyan JavaScript API'leri için bazı eski veri yapılarını kullanırız.
Her şeyi NG ile düzenlemek; CSS kapsayıcı sorguları, çapa konumlandırma, MathML ve özel düzen (Houdini) gibi yalnızca LayoutNG uygulamaları olan (ve eski arama motoru eşdeğeri olmayan) yeni özelliklerin uygulanmasını ve gönderilmesini mümkün kılar. Konteyner sorguları için bu seçeneği biraz önceden ilettik ve geliştiricilere, yazdırma işleminin henüz desteklenmediğine dair bir uyarıda bulunduk.
LayoutNG'nin ilk bölümünü 2019'da kullanıma sunduk. Düzenli blok kapsayıcı düzeni, satır içi düzen, kayan öğeler ve akış dışı konumlandırmadan oluşan ancak esnek, ızgara veya tablolar desteğinin yanı sıra blok parçalama desteği de sunulmuyor. Esnek, ızgara, tablolar ve blok parçalama içeren her şey için eski düzen motorunu kullanmaya devam ederiz. Bu durum, parçalanmış içerik içindeki blok, satır içi, kayan ve akış dışı öğeler için bile geçerliydi. Gördüğünüz gibi, böyle karmaşık bir düzen motorunun yerini değiştirmek çok hassas bir danstır.
Buna ek olarak, 2019'un ortalarına kadar LayoutNG blok parça düzeninin temel işlevlerinin çoğu zaten uygulanmıştır (bir bayrak arkasında). Peki kargoya verilmesi neden bu kadar uzun sürdü? Kısa cevap: Parçalanmanın, sistemin çeşitli eski bölümleriyle doğru bir şekilde bir arada bulunması gerekir. Bu bölümler, tüm bağımlılıklar yükseltilene kadar kaldırılamaz veya yükseltilemez.
Eski motor etkileşimi
Eski veri yapıları, düzen bilgilerini okuyan JavaScript API'lerinden sorumlu olmaya devam eder. Bu nedenle, verileri eski motora onun anlayacağı bir şekilde geri yazmamız gerekir. Buna, LayoutMultiColumnFlowThread gibi eski çok sütunlu veri yapılarının doğru bir şekilde güncellenmesi de dahildir.
Eski motor yedeği algılama ve işleme
LayoutNG blok parçalaması tarafından henüz işlenemeyen içerik bulunduğunda eski düzen motoruna geri dönmemiz gerekti. Esnek, ızgara, tablolar ve yazdırılan her şeyi içeren, kargo çekirdeği LayoutNG blok parçalanması sırasında. Bu, düzen ağacında nesneler oluşturmadan önce eski yedek ihtiyacını tespit etmemiz gerektiği için özellikle zordu. Örneğin, çok sütunlu bir kapsayıcı üst öğesinin olup olmadığını ve hangi DOM düğümlerinin biçimlendirme bağlamı olacağını öğrenmeden önce bunu tespit etmemiz gerekiyordu. Bu, mükemmel bir çözümü olmayan bir tavuk ve yumurta problemidir, ancak tek hatalı davranışı yanlış pozitifler (gerçekte ihtiyaç duyulmadığında eskiye geri döner) olduğu sürece sorun değildir, çünkü bu düzen davranışındaki hatalar Chromium'da bulunan hatalardır, yeni olanlar değildir.
Ağaç boyama öncesi yürüyüş
Ön boyama, boyamadan önce değil, düzenden sonra yaptığımız bir işlemdir. Buradaki en büyük zorluk, düzen nesnesi ağacını hâlâ yürümemiz gerekmesidir, ancak şu anda NG parçalarımız var. Peki bununla nasıl başa çıkabiliriz? Aynı anda hem düzen nesnesini hem de NG parçası ağaçlarını yürütüyoruz. Bu oldukça karmaşıktır, çünkü iki ağacın haritasını çıkarmak önemsiz değildir.
Düzen nesne ağacı yapısı, DOM ağacının yapısına çok benzese de parça ağacı, düzene bir giriş değil, düzenin bir çıktısıdır. Parça ağacı, satır içi parça (satır parçaları) ve blok parçalanması (sütun veya sayfa parçaları) dahil olmak üzere herhangi bir parçalanmanın etkisini gerçekten yansıtmanın dışında, içeren blok ile bu parçayı içeren blok olarak içeren DOM alt öğeleri arasında doğrudan bir üst-alt ilişkisine de sahiptir. Örneğin, parça ağacında kesinlikle konumlandırılmış bir öğe tarafından oluşturulan parça, içeren blok parçasının doğrudan alt öğesidir. Üst öğe zincirinde, akış dışı konumlandırılmış alt öğe ile içeren blok arasında başka düğümler olsa bile bu parça, öğenin bulunduğu blok parçasının doğrudan alt öğesidir.
Bu durum, parçalanmanın içinde akış dışı konumlandırılmış bir öğe olduğunda daha da karmaşık hale gelebilir. Çünkü bu durumda, akış dışı parçalar parçalayıcının doğrudan alt öğeleri olur (CSS'nin kapsayıcı blok olduğunu düşündüğü bir alt öğe olmaz). Bunun eski motorla bir arada var olması için çözülmesi gereken bir sorundu. LayoutNG, tüm modern düzen modlarını esnek bir şekilde destekleyecek şekilde tasarlandığından, gelecekte bu kodu basitleştirebiliriz.
Eski parça motoruyla ilgili sorunlar
Web'in daha eski bir çağında tasarlanan eski altyapı, o zamanlar teknik olarak da mevcut olsa bile (yazdırmayı desteklemek için) parçalanma kavramına aslında sahip değildir. Parçalama desteği üst kısmına cıvatalanan (yazdırma) veya geriye dönük (çok sütunlu) bir destekti.
Eski motor, parçalara ayrılmış içeriği düzenlerken her şeyi, genişliği bir sütunun veya sayfanın satır içi boyutu olan ve yüksekliği, içeriği barındırmak için gereken kadar yüksek olan uzun bir şeride yerleştirir. Bu uzun şerit sayfada oluşturulmaz. Bunu, son gösterim için yeniden düzenlenen sanal bir sayfanın oluşturulması olarak düşünebilirsiniz. Kavramsal olarak, basılı bir gazete makalesinin tamamını bir sütuna yazdırmaya ve ikinci adım olarak bunu birden çok sütuna kesmek için makas kullanmaya benzer. (Eskiden bazı gazeteler buna benzer teknikler kullanıyordu.)
Eski motor, şeritteki hayali bir sayfa veya sütun sınırını izler. Bu şekilde, sınırların dışına sığmayan içeriği bir sonraki sayfaya veya sütuna yönlendirebilirsiniz. Örneğin, bir satırın yalnızca üst yarısı motorda geçerli sayfa olduğunu düşündüğü şeye sığacaksa, onu motorun bir sonraki sayfanın üst kısmında olduğunu varsaydığı konuma aşağı itmek için bir "sayfalara ayırma desteği" ekler. Daha sonra, asıl parçalama çalışmasının büyük kısmı ("makas ve yerleşim", içeriği boyama ve boyama sürecinin uzun kısımlarını kırparak düzenlendikten sonra gerçekleşir). Bu, dönüşüm uygulama ve parçalamadan sonra göreli konumlandırma (spesifikasyonun gerektirdiği) gibi bazı şeyleri esasen imkansız hale getirdi. Ayrıca, eski motorda tablo parçalanması için bir miktar destek olsa da, esnek veya ızgara parçalanması desteği yoktur.
Burada, makas, yerleşim ve tutkal kullanmadan önce eski motorda üç sütunlu düzenin dahili olarak nasıl temsil edildiğini görebilirsiniz (yüksekliği belirli bir değere sahip olduğumuz için yalnızca dört satır sığdır, ancak alt kısımda biraz fazla alan vardır):
![](https://developer.chrome.google.cn/static/docs/chromium/renderingng-fragmentation/image/the-internal-representati-5a7302c1d6de2.png?authuser=5&hl=tr)
Eski düzen motoru, düzen sırasında içeriği parçalamadığından, yanlış uygulanan göreli konumlandırma ve dönüştürmeler ve sütun kenarlarında kutu gölgelerinin kırpılması gibi pek çok tuhaf yapı bulunur.
Metin gölgelendirmeli bir örneği aşağıda bulabilirsiniz:
Eski motor, bu konuda iyi performans göstermez:
![](https://developer.chrome.google.cn/static/docs/chromium/renderingng-fragmentation/image/clipped-text-shadows-plac-f3b1624435c9a.png?authuser=5&hl=tr)
İlk sütundaki satırdaki metin gölgesinin nasıl kırpıldığını ve bunun yerine ikinci sütunun üzerine nasıl yerleştirildiğini görüyor musunuz? Bunun nedeni, eski düzen motorunun parçalanmayı anlamamasıdır.
Aşağıdaki gibi görünecektir:
Şimdi, dönüştürmeler ve kutu gölgesi ile bunu biraz daha karmaşık hale getirelim. Eski motorda nasıl yanlış kırpma ve sütun taşması olduğuna dikkat edin. Bunun nedeni, dönüşümlerin özellik olarak bir düzen sonrası, parçalama sonrası efekt olarak uygulanması gerekir. LayoutNG parçalanmasıyla her ikisi de düzgün çalışır. Bu, bir süre boyunca iyi parçalama desteğine sahip olan ve bu alandaki çoğu test oradan geçen Firefox ile birlikte çalışma olanağını geliştiriyor.
Eski motorda uzun monolitik içerikle ilgili sorunlar da vardır. İçerik, birden çok parçaya bölünmeye uygun değilse monolitik olur. Taşma kaydırmalı öğeler monolitiktir, çünkü kullanıcıların dikdörtgen olmayan bir bölgede kaydırması mantıklı değildir. Çizgi kutuları ve resimler, monolitik içeriklerin diğer örnekleridir. Aşağıda bir örnek verilmiştir:
Monolitik içerik parçası bir sütun içine sığmayacak kadar uzunsa eski motor bu parçayı acımasız bir şekilde dilimler (kaydırılabilir kapsayıcıyı kaydırmaya çalışırken çok "ilginç" bir davranışa yol açar):
İlk sütundan taşmasına izin vermek yerine (LayoutNG blok parçalanmasında olduğu gibi):
Eski motor, zorunlu araları destekler. Örneğin, <div style="break-before:page;">
, DIV öğesinin önüne bir sayfa sonu ekler. Ancak en uygun zorunlu olmayan araları bulmak için sınırlı bir desteğe sahiptir. break-inside:avoid
ile yetimler ve dullar desteklenir ancak break-before:avoid
aracılığıyla istenmesi durumunda, bloklar arasında boşluklardan kaçınmak için destek sunulmaz. Aşağıdaki örneğe bakın:
Burada, #multicol
öğesi için her sütunda 5 satırlık yer vardır (çünkü öğenin yüksekliği 100 piksel, çizgi yüksekliği 20 pikseldir). Bu nedenle, #firstchild
öğesinin tamamı ilk sütuna sığabilir. Ancak #secondchild
adlı eşdüzey öğede break-before:avoid özelliği kullanılıyor. Yani içerik, aralarında bir boşluk olmamasını istiyor. widows
değeri 2 olduğundan, tüm aradan kaçınma isteklerini yerine getirmek amacıyla ikinci sütuna 2 #firstchild
satırını aktarmamız gerekir. Chromium, bu özellik kombinasyonunu tam olarak destekleyen ilk tarayıcı motorudur.
NG parçalanmasının işleyiş şekli
NG düzen motoru genellikle önce CSS kutu ağacı derinliğinden geçiş yaparak dokümanı düzenler. Bir düğümün tüm alt öğeleri yerleştirildiğinde, NGPhysicalFragment üretip üst düzen algoritmasına dönerek söz konusu düğümün düzeni tamamlanabilir. Bu algoritma, parçayı alt parçalar listesine ekler ve tüm alt parçalar tamamlandığında, tüm alt parçaları içinde kendisi için bir parça oluşturur. Bu yöntemle, tüm belge için bir parça ağacı oluşturulur. Ancak bu işlem fazla basitleştirilir: Örneğin, akış dışı konumlandırılmış öğelerin, yerleştirilmeden önce DOM ağacında bulundukları yerden kapsayıcı bloklarına çıkması gerekir. Kolaylık sağlamak adına bu ileri düzey ayrıntıyı yok sayıyorum.
LayoutNG, CSS kutusunun kendisiyle birlikte bir düzen algoritması için kısıtlama alanı sağlar. Bu işlem, algoritmaya düzen için kullanılabilir alan, yeni bir biçimlendirme bağlamı oluşturulup oluşturulmadığı ve önceki içerikten ara kenar boşluğu daraltma sonuçları gibi bilgiler sağlar. Kısıtlama alanı, parçalayıcının yerleşik blok boyutunu ve mevcut blok ofsetini de bilir. Bu, nerede son verileceğini gösterir.
Blok parçalanması söz konusu olduğunda, alt öğelerin düzeni bir molada durmalıdır. Bozulma nedenleri arasında, sayfada veya sütunda alanın bitmesi ya da zorunlu bir kesme yer alır. Ardından, ziyaret ettiğimiz düğümler için parçalar üretiriz ve tamamen parçalı bağlam köküne (çoklu sütun kapsayıcısı veya yazdırmada belge kökü) kadar geri döneriz. Ardından, parçalanma bağlamı kökünde yeni bir parçalayıcı için hazırlanır ve ağaca tekrar iner, aradan önce kaldığımız yerden devam ederiz.
Bir aradan sonra düzeni devam ettirme yöntemlerini sağlayan kritik veri yapısına NGBlockBreakToken adı verilir. Bir sonraki parçalayıcıda düzeni doğru şekilde devam ettirmek için gereken tüm bilgileri içerir. NGBlockBreakToken, bir düğümle ilişkilendirilir. Ardından, devam ettirilmesi gereken her bir düğümün temsil edilmesi için bir NGBlockBreakToken ağacı oluşturulur. İçeride kırılan düğümler için oluşturulan NGPhysicalBoxFragment bölümüne bir NGBlockBreakToken eklenir. Ara jetonları üst öğelere yayılarak bir ara jetonu ağacı oluşturur. Bir düğümün öncesinde kırmamız gerekirse hiçbir parça oluşturulmaz. Ancak bir sonraki parçalayıcıda düğüm ağacında aynı konuma ulaştığımızda bunu yerleştirmeye başlayabilmemiz için üst düğümün, düğüm için "öncesi/önce" ayrılma jetonu oluşturması gerekir.
Aralar, parçalı alan tükendiğinde (zorunlu olmayan bir kesme) veya zorunlu kesme istendiğinde eklenir.
Spesifikasyonda en iyi uygulanmamış aralarla ilgili kurallar vardır ve sadece depolama alanımızın tükentiği yere bir ara eklemek her zaman doğru bir yöntem değildir. Örneğin, ara konumu seçimini etkileyen çeşitli CSS özellikleri (break-before
) vardır.
Düzen sırasında, zorunlu kılınmayan aralar spesifikasyon bölümünü doğru bir şekilde uygulamak için olası iyi ayrılma noktalarını takip etmemiz gerekir. Bu kayıt, aradan kaçma isteklerini ihlal edeceğimiz bir noktada alanımız tükenirse (örneğin, break-before:avoid
veya orphans:7
) geri dönüp bulunan mümkün olan en son ayrılma noktasını kullanabileceğimiz anlamına gelir. Olası her ayrılma noktasına, "bunu yalnızca son çare olarak yap"dan "kırılmak için mükemmel bir yer"e kadar değişen bir puan verilir. Bir ara konumunun puanı "kusursuz" olarak görünüyorsa bu, orayı ihlal ettiğimizde hiçbir çiğnenme kuralının ihlal edilmeyeceği anlamına gelir (ve bu puanı tam olarak yerimiz tükendiğinde alırsak daha iyi bir şey bulmak için geriye dönüp bakmamıza gerek yoktur). Skor "son çare" ise, ayrılma noktası geçerli bir nokta bile değildir, ancak daha iyi bir şey bulamazsak parçalayıcı taşmasını önlemek için ayrılma noktası yine de bozulabilir.
Geçerli ayrılma noktaları genellikle yalnızca kardeşler (satır kutuları veya bloklar) arasında geçerlidir ve örneğin, bir üst ile ilk alt öğesi arasında geçerli değildir (C sınıfı ayrılma noktaları istisnadır ancak bunları burada açıklamamıza gerek yoktur). Örneğin, break-before:avoid" içeren bir blok eşdüzey öğeden önce geçerli bir ayrılma noktası olur, ancak bu nokta "mükemmel" ile "son-çare" arasında bir yerdedir.
Düzen sırasında, NGEarlyBreak adlı bir yapıda şu ana kadar bulunan en iyi ayrılma noktasını takip ederiz. Erken ara, bir blok düğümünden önce veya sonra ya da bir satırdan (blok kapsayıcı satırı veya esnek çizgi) önceki olası bir ayrılma noktasıdır. En iyi ayrılma noktası, daha önce alanımız tükendiğinde geçmişte yürüdüğümüz bir şeyin derinliklerinde bir yerde olması ihtimaline karşı NGnyBreak nesnelerinden bir zincir veya yol oluşturabiliriz. Aşağıda bir örnek verilmiştir:
Bu durumda, #second
tarihinden hemen önce depolama alanımız tükenir ancak "break-before:avoid" değeri bulunur. Bu da "break-before:avoid" ve "break-before:avoid" değerini alır. "Kuralları ihlal eden aradan kaçınma" puanı bu şekildedir. Bu noktada, "#outer
içinde > #middle
içinde > #inner
içinde > "3. satırdan" önce olan ve "mükemmel" olan bir NGPreviousBreak zincirimiz var, yani oradan devam etmeyi tercih ederiz. Bu nedenle, #inner öğesindeki "3. satırdan" önce kırabilmemiz için düzeni #outer öğesinin başından (ve bu kez bulduğumuz NGPreviousBreak değerini geç) geri dönüp yeniden çalıştırmamız gerekiyor. ("Satır 3"ten önce bölünür, böylece geri kalan 4 satır bir sonraki parçalayıcıda yer alır ve widows:4
onurlandırmak için kullanılır.)
Algoritma, hepsi karşılanamayacaksa kuralları doğru sırada bırakarak her zaman mümkün olan en iyi ayrılma noktasında (özellikte tanımlandığı gibi) bozulacak şekilde tasarlanmıştır. Her parçalama akışı için en fazla bir kez yeniden düzenleme yapmamız gerektiğini unutmayın. İkinci düzen geçişine geldiğimizde, en iyi ara konumu düzen algoritmalarına geçirilmiştir. Bu, ilk düzen geçişinde keşfedilen ve o turda düzen çıktısının bir parçası olarak sağlanan ara konumudur. İkinci düzen geçişinde, depolama alanımız tükenene kadar depolama yapmıyoruz. Aslında, çiğneme kurallarını gereksiz yere ihlal etmemek için, toplantıdaki alanın bitmesini beklemiyoruz (bu aslında bir hataydı). Çünkü elimizde, kırılma kurallarını gereksiz yere ihlal etmemek üzere erken bir mola vermemiz için çok hoş (olabildiğince sevimli) bir yer sağlanmıştı. Bu noktada konuyu toparlayıp mola veriyoruz.
Bu nedenle, parçalayıcı taşmasını önlemeye yardımcı olması durumunda bazen, aradan kaçınma isteklerinden bazılarını ihlal etmemiz gerekebilir. Örneğin:
Burada, #second
tarihinden hemen önce depolama alanımız tükeniyor ancak "break-before:avoid" ifadesi var. Bu, son örnekte olduğu gibi "arada kaçışın ihlali" anlamına geliyor. Ayrıca "ihlal eden yetimler ve dullar"ın (#first
içinde > "2. satırdan" önce) yer aldığı bir NGPreviousBreak'i de sunuyoruz. Bu yöntem, mükemmel olmasa da "aradan kaçma işlemini ihlal edenler"den daha iyidir. Bu yüzden "2. satırdan" önce başlar ve yetimlerin / dulların isteğini ihlal ederiz. Bu spesifikasyon, 4.4. Zorunlu Kılınmayan Kesmeler: Burada, fragmanlı öğe taşmasını önlemek için yeterli ayrılma noktamız olmadığında ilk olarak hangi kırma kurallarının yok sayılacağını tanımlar.
Sonuç
LayoutNG blok parçalama projesinin işlevsel hedefi, eski motorun desteklediği her şeyin ve hata düzeltmelerinin dışında mümkün olduğunca az şeyin uygulanmasını sağlamak üzere LayoutNG mimarisini destekleyen bir uygulama sağlamaktı. Temel istisna, parçalama motorunun temel bir parçası olduğu için parçalama motorunun temel bir parçası olduğu ve daha sonra eklenmesi başka bir yeniden yazma anlamına geleceği için başlangıçtan itibaren orada olması gerektiğinden daha iyi bozulma önleme desteğidir (örneğin break-before:avoid
).
LayoutNG blok parçalaması tamamlandığına göre, yazdırma sırasında karma sayfa boyutlarını, yazdırma sırasında @page
kenar boşluğu kutularını, box-decoration-break:clone
ve daha fazlasını destekleme gibi yeni işlevler eklemek için çalışmaya başlayabiliriz. Ve genel olarak LayoutNG'ta olduğu gibi, yeni sistemin hata oranının ve bakım yükünün zaman içinde önemli ölçüde azalmasını bekliyoruz.
Teşekkür
- Güzel "el yapımı ekran görüntüsü" için Una Kravets.
- Gözden geçirme özelliği, geri bildirim ve öneriler için Chris Harrelson.
- Geri bildirim ve önerilerinizi Philip Jägenstedt ile paylaşın.
- Düzenleme için Rachel Andrew ve çok sütunlu ilk örnek figür için.