BookGen – A felhasználó szemszögébÅ‘l
Az elÅ‘zÅ‘ cikkben a BookGen keletkezésének körülményeit és az architektúráját ismertettem nagy vonalakban. A mai cikkben viszont a felhasználói oldalról közelÃteném meg.
A program alapvetÅ‘en egy parancssoros alkalmazás. A parancsok szintaxisa, amivel használható, az a GIT-bÅ‘l merÃtett inspirációt. EbbÅ‘l adódóan a forrás Markdown fájlokat tartalmazó mappából statikus weblapot készÃteni az alábbi módon lehet:
BookGen Build --action BuildWeb
Felmerülhet jogosan a kérdés, hogy ha valaki nem ismeri a programot, akkor hogyan tanulhatja meg a használatát? Erre az egyszerű válasz az, hogy úgy, mint jó 20 évvel ezelőtt ahogy tették az emberek a DOS időszakában: dokumentáció olvasásával.
Na de nem 1984-et Ãrunk, maximum csak metaforikusan. Éppen ezért több tonna dokumentáció olvasása és fejlesztÅ‘ként a megÃrása sem egy kellemes élmény. Emiatt a programba beépÃtettem a parancsok dokumentációját, Ãgy lényegében interaktÃvan a
BookGen Help
parancs kiadásával tájékozódni lehet a program alapvetÅ‘ használatáról, illetve a Help-nek célzottan megadható a kÃvánt parancs neve, ami az ahhoz tartozó súgót jelenÃti meg. Pl:
BookGen Help Build
Ez már egy fokkal felhasználóbarátabb, de még mindig nem az igazi. A „modern” parancssori shellek támogatják az automatikus parancskiegészÃtést is valamilyen módon. Windows alatt a PowerShell képes ilyesmire, ha a program, amit használni szeretnénk, rendelkezik ilyen funkcióval. Természetesen ezt a funkciót is beleépÃtettem a programba. Viszont ez out-of-the-box nem működik, fel kell okosÃtani a shell profilt. Ez a következÅ‘ parancs kiadásával lehetséges, ha PowerShell-bÅ‘l indÃtottuk a programot:
BookGen InstallPsAutocomplete $profile
GUI kell vagy nem kell?
A terminál alapú programok reneszánszukat élik, legalábbis fejlesztői téren. Viszont attól, hogy valami konzol alapú, az még nem zárja ki azt, hogy rendelkezzen karakteres GUI-val. Régi motorosoknak talán ismerős lehet a Turbo Vision, ami a maga idejében egy remek eszköz volt.
Ennek egy modern, C# megfelelÅ‘je a Miguel de Icaza által készÃtett gui.cs. Aki ismerÅ‘s Icaza korábbi munkáival, annak nem lesz meglepetés, hogy egyszerre zseniális és nagyon rossz minÅ‘ségű ez is. Az ötlet remek, de az implementáció CRAP indexe az egekben van. Szerencsére a nézegetését megúszhatjuk, mivel NuGet csomagból is elérhetÅ‘.
A gui.cs legnagyobb problémája, hogy gyárilag nem biztosÃt megoldást arra, hogy a UI kódtól leválasszuk az üzleti logikát. Itt persze nézÅ‘pont kérdése, hogy ez probléma-e egyáltalán, mivel egy UI könyvtárnak nem feltétlen kell, hogy célja legyen valami magasabb szintű absztrakció.
Jelen esetben az absztrakciót egy saját MVVM megoldással váltottam ki, amit a XAML inspirált. Utólag végiggondolva azonban ez egy picit ágyúval a verébre kategória lett és lehet hogy jobban jártam volna egy MVC implementációval. Nagy valószÃnűséggel ez a jövÅ‘ben majd változni fog, de térjünk vissza a GUI használhatóságára.

A GUI leginkább egy proxyként működik a Build parancsra és az új könyv létrehozásához szolgáló parancsokhoz. Egy viszonylag új funkciója, hogy a súgó is beépÃtésre került.

Konfiguráció és új könyv
Az új könyv létrehozását megkezdhetjük manuálisan is, de ez nem nagy élmény, éppen ezért beépÃtetten tartalmaz erre is eszközt a program, amit a
BookGen Init
parancs kiadásával tudunk aktiválni. Ez konzol grafikusan lehetÅ‘vé teszi, hogy létrehozzunk minden olyan fájlt, ami a könyvÃráshoz kell.

A BookGen tervezésekor az elején a fő szempont az egyszerűség volt. Éppen ezért csupán 2db fő fájlból áll a konfiguráció.
Az egyik ilyen fÅ‘ fájl, aminek a nevét nem lehet módosÃtani, az a bookgen.json fájl. A tool build közben ezt a fájl olvassa fel. Ez konfigurál minden olyan beállÃtást, ami a kimeneti fájlok előállÃtásához szükséges.
A JSON-re azért esett a választásom, mivel mondhatni ipari szabvány. Ezen felül könnyen szerkeszthető és olvasható. Tudom, hogy manapság konfigurációra inkább a YAML népszerűbb meg talán felhasználóbarátabb is, de megmondom őszintén, hogy a hideg kiráz az olyan formátumoktól, amelyek különbséget tesznek a space és tab közötti tagolásban.
Egyetlen hátránya a JSON konfigurációnak, hogy a JSON szabvány szerint nem tartalmazhat kommenteket. Ez néha napján jól jönne a konfiguráció megértéséhez és szerkesztéséhez. Azonban igyekeztem minden beállÃtásnak beszédes, jól érthetÅ‘ nevet adni. Ha pedig valahol tényleg elakadna a felhasználó, akkor a
BookGen confighelp
parancs kiadásával részletes információt tud kapni a beállÃtási lehetÅ‘ségekrÅ‘l.
A másik fÅ‘ fájl a tartalomjegyzék fájlja. Ez egy Markdown fájl, ami linkeket tartalmaz a könyvben szereplÅ‘ fájlokra. Itt Markdown-ra azért esett a választásom, mivel a tartalomjegyzék könyvenként eltérÅ‘ stÃlusú és mélységű.
Az Init parancs még létrehoz egy Visual Studio Code számára érthetÅ‘ tasks.json fájlt is, hacsak nem módosÃtjuk a beállÃtást. Ez leginkább egy kényelmi funkció, ugyanis az egész könyv szövege Visual Studio Code segÃtségével készült el.
Képfeldolgozás
A BookGen egyik olyan szolgáltatása, amire a legbüszkébb vagyok a képfeldolgozáshoz kapcsolódik és nem igen találkoztam még hasonló megoldással statikus weblap készÃtÅ‘ eszközöknél.
A build konfiguráció során be lehet állÃtani kimeneti formátumoknál az alábbiakat:
- Jpeg, png, svg fájlok Webp formátumba konvertálása
- Képek átméretezése, ha a megadott maximális méretet (szélesség x magasság) átlépnék
- Képek base64 kódolt beágyazása a kimeneti HTML-be
A funkciót az ihlette, hogy a különbözÅ‘ kimeneti formátumok számára eltérÅ‘ felbontású és minÅ‘ségű képekre van szükség. Például web esetén nem szerencsés, hogy ha 40 MiB méretű PNG képeket publikálunk, viszont nyomtatás esetén meg az nem szerencsés, ha 40 KiB méretű rommá tömörÃtett Webp képeket küldünk a nyomdába.
Az SVG átkonvertálása funkció szintén kimeneti formátum támogatás miatt került be. Ugyan az EPUB lényegében HTML fájlok összessége egy ZIP-ben, mégsem támogat rendesen SVG-t, mivel az EPUB3 még mindig XHTML alapú, ami valahol logikus is: ritkán fordul elÅ‘, hogy a HTML5 összes jósága kellene egy elektronikus könyv megjelenÃtéséhez.
BÅ‘vÃthetÅ‘ség
A legtöbb statikus weblap készÃtÅ‘ eszköz kötött ahhoz a nyelvhez, amiben Ãrták. Ez azt jelenti, hogy ha az eszköz JavaScript-ben készült, akkor a bÅ‘vÃtményeket és az egyedi kiegészÃtÅ‘ket is JavaScript-ben kell megÃrnunk. Ez nem a legkényelmesebb felhasználói szempontból, mivel ha nem vagyunk profik egy adott nyelven, akkor elÅ‘ször meg kell tanulnunk a nyelvet annyira, hogy tudjunk benne alkotni, vagy hagyjuk az eszközt a fenébe és keresünk egy olyat, amit olyan nyelven Ãrtak, amihez értünk.
Ha az utóbbit választjuk, akkor azonban sok mindent dobhatunk a kukába és lehet, hogy csak későn jövünk rá, hogy a választott eszköz mégsem lesz jó arra, amire szeretnénk.
A BookGen esetén ezt el szerettem volna kerülni. Az alap rendszer C#-ban van megÃrva, de lehetÅ‘vé szerettem volna tenni, hogy bármilyen nyelven bÅ‘vÃthetÅ‘ legyen a template rendszere a korábban már ismertetett Shortcode-szerű rendszerrel.
Ez elsÅ‘ hallásra lehetetlen, vagy legalábbis elég nehéz feladatnak tűnhet. Természetesen túl lehetett volna ennek a szekerét is tolni, de igyekeztem a realitás talaján maradni. Jelenleg a rendszer JavaScript, PHP és Python bÅ‘vÃthetÅ‘séget tartalmaz.
Ennek a kivitelezését és működését a legjobban egy példán keresztül lehet elmagyarázni. Tételezzük fel, hogy a template fájl az alábbi Shortcode-ot tartalmazza:
<!--{NodeJs file="script.js"}-->
Ebben az esetben az fog történni, hogy a program elindÃtja a beállÃtásokban konfigurált NodeJs.exe fájlt, majd lefuttatja a script.js fájlt. Ami pedig amúgy a konzolra kerülne kimenetként, az a Shortcode helyére lesz behelyettesÃtve. A nagyobb flexibilitás miatt a könyv generálásához tartozó összes beállÃtást is megkapja a szkript egy natÃv JS objektumban.
PHP és Python esetén is hasonló a helyzet. Kérdés az lehet, hogy hogy az Istenben lesz egy C# objektumból PHP vagy Python objektum? A válasz erre igen egyszerű. Mint emlÃtettem a JSON ipari szabvány, ezért minden nyelv támogatja. Ez alól a Python és a PHP sem kivétel. Vagyis lényegében JSON-be szerializálódik az összes beállÃtás, ami aztán nyelvfüggÅ‘ módon dekódolva lesz egy változóba. Ennek a változónak a létrehozása pedig a futtatandó script fájlnak az elsÅ‘ sorába másolódik a tényleges futtatás elÅ‘tt.
Puruttya egy megoldás, viszont működÅ‘képes és sokkal kevesebb overhead implementálással jár, mint mondjuk egy JSON-RPC megvalósÃtása. Természetesen a szabványosság és kompatibilitási okok miatt ha lesz idÅ‘m, akkor Ãrok egy JSON-RPC hidat is.
C# scripting
Szégyen lenne, ha egy C#-ban Ãrt statikus weboldal generátort nem lehetne C#-ban bÅ‘vÃteni, szkriptezni.
AlapvetÅ‘en a C# nem egy szkript nyelv, de mióta a kód Roslyn-al fordul IL kódra, azóta sok új érdekes lehetÅ‘sége van az embernek. Egyik ilyen érdekes lehetÅ‘ség, hogy C# kódot akár egy C# alkalmazásból tudunk fordÃtani. (Ilyenre példa a RoslynPad) Ezt felhasználva megalkottam a saját szkript rendszeremet, ami lényegében ugyanazt tudja, mint a NodeJs, PHP vagy Python szkript felület: bÅ‘vÃteni a generátor lehetÅ‘ségeit.
A szkripting és a bÅ‘vÃthetÅ‘ség tipikusan nem az a funkció volt, amire a tervezés elsÅ‘ lépéseként gondoltam (részletekért lásd elÅ‘zÅ‘ cikkek) és ennek meg is volt az ára: az elsÅ‘ szkript rendszer implementálásakor a program nagy részét át kellett Ãrnom és strukturálnom, ami bÅ‘ven több idÅ‘t vett igénybe, mint gondoltam. Ennek ellenére nem volt haszontalan, mivel beleástam magam a Roslyn fordÃtó működésébe, ami a C# könyv késÅ‘bbi változataihoz hasznos lesz.
Az indÃtó
Részben a kényelem és a lustaság szülte, hogy a programhoz készÃtettem egy indÃtó alkalmazást is, amivel grafikusan ki tudunk választani egy mappát ahol futtatni szeretnénk a programot. A futtatás alatt itt az értendÅ‘, hogy indÃt egy PowerShell munkamenetet az adott mappában úgy, hogy a korábban emlÃtett parancs kiegészÃtés is működik. Ha a gépre telepÃtve van a Windows Terminal, akkor a PowerShell munkamenet természetesen abban indul el 🙂

Jövőbeli tervek
Az egyik közel jövÅ‘beli terv, hogy a jelenleg WPF-ben Ãrt indÃtó alkalmazást átÃrom Avalonia-ra. Elvileg az Avalonia többé-kevésbé WPF kompatibilis. Nagy elÅ‘nye, hogy platformfüggetlen, illetve nagyobb közösség fejleszti, mint a lassan 10 éve csak életben tartott WPF-et.
Egy hosszabb távú tervem nem szorosan kapcsolódik a cikk eddigi tartalmához, de egy másik számomra fájó pontját kÃvánja javÃtani az Ãrási folyamatnak. Ez nem más, mint a helyesÃrás ellenÅ‘rzés. Számtalan Markdown-képes szerkesztÅ‘t kipróbáltam, de valahogy mindig a Visual Studio Code mellett kötöttem ki. Egyetlen hátránya, hogy „normális”, 2GiB (sajnos nem vicc) alatt fogyasztó helyesÃrás ellenÅ‘rzÅ‘ plugin nincs hozzá.
Ez leginkább annak köszönhető, hogy a megoldások többsége ugyanazt a memória faló Hunspell kompatibilis JavaScript könyvtárat használja. A nevetséges memóriaigény mellett sokkal nagyobb probléma, hogy a használatától belassul a szerkesztő. Ez a lassulás olyan szintig tud fajulni, hogy egy gomb lenyomására kb. 5 másodperc, mire reagál a szerkesztő nagyobb fájlok esetén.
Szerencsére van megoldás a problémára, de tudtommal még senki sem csinálta meg. A Visual Studio Code tervezésekor gondoltak arra, hogy rendesen bÅ‘vÃthetÅ‘ legyen. Egyik ilyen opció az úgynevezett Language Server Protocol lehetÅ‘ség.
A Language Server Protocol mögött az volt az alapötlet, hogy egy adott programozási nyelv támogatását (auto complete, syntax check, stb…) az adott nyelven lehet a leghatékonyabban megoldani. Az ötletem az, hogy Ãrok egy language szervert Markdown fájlokhoz, ami a háttérben egy gyors Hunspell implementáció segÃtségével jól használható helyesÃrás ellenÅ‘rzést végez majd. De ez igazán a jövÅ‘ zenéje. Esetlegesen ha ez a része felkeltette az érdeklÅ‘désedet, akkor tekintsd egyfajta felkérésnek a táncra 🙂