Hogyan kerül szín és karakter a képernyőre a rubbs-ban: a
BBS::Theme(minden preset és minden szerepkulcs), aBBS::Ansipaletta, aBBS::FrameBuffer, amibe az Application rajzol, és amit összehasonlít, aBBS::Renderera flow-k ERB screenjeihez, aBBS::Banner, és aBBS::Markdown, ami Markdownból ANSI-sorokat készít. Az ezekre épülő widgetek a Widgetek oldalon vannak. Minden a rubbs 1.0.0 forrásával egyeztetve.
A BBS::Theme (lib/bbs/theme.rb) egy név és egy Hash, ami szerepkulcsokat képez le ANSI SGR-stringekre. A widgetek sosem égetnek be színt: egy szerepet kérnek (:button_focused, :window_title, :listbox_selected…), és azzal az SGR-rel rajzolnak, amit a téma ad. A téma cseréje az egész applicationt átszínezi.
BBS::Theme.new(name, styles)
| Tag | Mit csinál |
|---|---|
Theme.new(name, styles) |
A name bármilyen érték (a presetek Symbolt használnak), a styles egy Hash szerepkulcsból SGR-Stringbe. A Theme::ROLES-on kívüli kulcsra ArgumentError-t dob. |
Theme.fetch(name) |
Az ilyen nevű (Symbol vagy String) preset a Theme::PRESETS-ből; egy BBS::Theme-et változatlanul visszaad. Ismeretlen névre ArgumentError. |
Theme::ROLES |
Az összes szerepkulcs (a synchronet preset kulcsai); a Theme.roles ugyanezt adja. |
name |
A név. |
styles |
A Hash, fagyasztva. |
style(key) / [key] |
Egy szerep SGR-je, vagy nil, ha a témában nincs ilyen. |
merge(overrides) |
Új téma ugyanazzal a névvel, a megadott kulcsokat lecserélve vagy hozzáadva. Az eredetit nem bántja; a Theme::ROLES-on kívüli kulcsra ArgumentError-t dob. |
A téma fagyasztott: a merge-dzsel változtatsz rajta, ami újat ad. Ha a Hashből kimarad egy szerep, az nem hiba: a cella nil stílust kap, a FrameBuffer#diff pedig egy sima \e[0m reset után írja ki, így a terminál alapszíneivel jelenik meg.
require 'bbs'
t = BBS::Theme.synchronet
p t[:accent] # => "\e[1;33;40m"
p t.style(:no_such_role) # => nil
cyan = t.merge(accent: "\e[1;36;40m")
p [cyan.name, cyan[:accent]] # => [:synchronet, "\e[1;36;40m"]
p t.frozen? # => true
t.merge(check_on: "\e[1;32m") # ArgumentError: unknown theme roles: [:check_on]
A presetek adatok: mindegyik egy Hash a BBS::Theme::PRESETS-ben (lib/bbs/theme/presets.rb), és a BBS::Theme.fetch(:név) épít belőlük témát; az alábbi öt osztálymetódus ennek rövidítése. Ha nincs megadva téma, az application a synchronet-et használja (vagy a BBS.config.theme-et, ha az be van állítva).
| Preset | Kinézet |
|---|---|
BBS::Theme.synchronet |
Fekete képernyő, szürke szöveg. Kék window-k élénkfehér kerettel és sárga címmel; az aktív window címe fekete világosszürkén. A menüsor és a státuszsor fekete világosszürkén, piros hotkeyekkel, a kijelölt menü fehér zölden. A gombok feketék szürkén, fókuszban fehérek zölden; a beviteli mezők fehérek kéken, fókuszban feketék szürkén. |
BBS::Theme.nortoncommander |
Norton Commander: kék képernyő és kék window-k cián kerettel, a címek feketék ciánon (az aktív fehér ciánon). A menüsor és a státuszsor fekete ciánon, a legördülő menük feketék világosszürkén. A gombok fehérek zölden, fókuszban fehérek ciánon; a beviteli mezők feketék ciánon. |
BBS::Theme.dosnavigator |
DOS Navigator: világosszürke képernyő és window-k fekete szöveggel, a címek ciánon, kék kiemelések. A gombok feketék szürkén, fókuszban fehérek zölden; a lista kijelölése fekete ciánon. |
BBS::Theme.neumanntronics |
Visszafogott 256 színű paletta (38;5;N / 48;5;N): majdnem fekete háttér, kékeszöld szöveg, világos kékeszöld kiemelés, borostyánsárga hangsúlyok és hotkeyek, sötét kékeszöld sávok. 256 színű terminál kell hozzá. A window árnyéka a háttér színe, így a window-knak nincs látható árnyéka. |
BBS::Theme.mono |
Egyáltalán nincs szín, csak félkövér, halvány, aláhúzott és inverz, a szín nélküli terminálokhoz. A screen és a window_bg üres string: a terminál saját háttere. |
A Theme::ROLES: a presetek összes kulcsa (mind az öt mindet definiálja), mi rajzol vele a rubbs 1.0.0-ban, és a synchronet-beli értéke. A „a gem nem használja” jelzésű kulcsok a saját widgetjeidnek és screenjeidnek valók. Az 1.0 kidobta azokat a szerepeket, amelyekkel semmi nem rajzolt: a check_on, a check_off, a listbox_header, a warning és a reset szerepet.
| Kulcs | Mit színez | synchronet |
|---|---|---|
screen |
Az egész képernyő, minden más előtt kitöltve: az asztal háttere. | \e[40m |
text |
A Label alapértéke; a Checkbox; a ScrollView és minden Markdown-sor alapja. |
\e[0;37;40m |
text_dim |
A TextInput placeholdere; a Markdown dőlt szövege és az idézetjel. |
\e[0;2;37;40m |
text_bright |
A Markdown első szintű címsorai és a félkövér. | \e[1;37;40m |
label |
A Markdown harmadik és mélyebb szintű címsorai, a beágyazott kód, a kódblokkok, a vízszintes vonalak és a táblázatkeretek; az Info window-k elválasztó vonalai. |
\e[0;36;40m |
accent |
A Markdown második szintű címsorai, a linkek, a listajelek és -számok. | \e[1;33;40m |
success |
A gem nem használja. | \e[1;32;40m |
error |
A Form window státuszsora (validációs hibák). |
\e[1;31;40m |
window_bg |
A window belseje. | \e[44m |
window_border |
A window kerete. | \e[1;37;44m |
window_title |
Az inaktív window címe; a bezáró [■] és a zoom gomb, a window száma, az átméretező sarok. |
\e[1;33;44m |
window_title_focused |
Az aktív window címe; mozgatás vagy átméretezés közben az egész keret. | \e[1;30;47m |
window_text |
A dialógusok és az Info window-k szövege. |
\e[0;37;44m |
window_shadow |
A window-k és a legördülő menük árnyéka. | \e[40m |
menubar |
A menüsor. | \e[1;30;47m |
menubar_hotkey |
A hotkeyek betűi a menüsorban. | \e[1;31;47m |
menubar_selected |
A nyitott vagy kiemelt menücím. | \e[1;37;42m |
menubar_sel_hot |
Ennek a címnek a hotkey-betűje. | \e[1;33;42m |
menu_bg |
A legördülő menü kitöltése. | \e[1;37;44m |
menu_text |
A menüpontjai. | \e[0;37;44m |
menu_hotkey |
A menüpontok hotkey-betűi. | \e[1;33;44m |
menu_selected |
A kiemelt menüpont. | \e[1;37;46m |
menu_disabled |
A letiltott menüpontok. | \e[0;2;37;44m |
menu_border |
A legördülő menü kerete és elválasztó vonalai. | \e[1;37;44m |
statusbar |
A státuszsor. | \e[1;30;47m |
statusbar_key |
A billentyűnevek a státuszsor tippjeiben. | \e[1;31;47m |
button |
A Button. |
\e[0;30;47m |
button_focused |
A fókuszban lévő Button; a fókuszban lévő Checkbox is. |
\e[1;37;42m |
button_hotkey |
A Button hotkey-betűje. |
\e[1;31;47m |
button_disabled |
A letiltott Button. |
\e[0;2;30;47m |
input |
A TextInput és a TextArea. |
\e[1;37;44m |
input_focused |
Ugyanezek fókuszban. | \e[1;30;47m |
input_label |
A mezőcímkék a Form, Info és MasterDetail window-kban, és a Form tippje. |
\e[0;36;40m |
listbox |
A ListBox sorai. |
\e[0;37;44m |
listbox_selected |
A fókusz nélküli ListBox kijelölt sora. |
\e[1;37;46m |
listbox_focused |
A fókuszban lévő ListBox kijelölt sora. |
\e[1;30;47m |
scrollbar |
A ScrollView görgetősávjának pályája. |
\e[0;36;44m |
scrollbar_thumb |
A csúszkája. | \e[1;37;46m |
A presetek minden SGR-értéke az előtér mellett a hátteret is megadja (37;40, 37;44), és többségük 0;-val kezdődik, így egy stílus sosem örökli az előtte rajzolt cella félkövérségét vagy halványságát. A saját értékeidben is így tégy.
A BBS::Application.define blokkjában a theme egy téma-példányt vagy egy preset nevét várja, akár egy felülíró blokkal együtt:
require 'bbs'
APP = BBS::Application.define do
theme BBS::Theme.nortoncommander
end
| Alak | Eredmény |
|---|---|
theme BBS::Theme.synchronet |
Az a téma. |
theme :synchronet |
Az ilyen nevű preset (Theme.fetch; ismeretlen névre ArgumentError). |
theme some_theme.merge(accent: "\e[1;36;40m") |
Bármilyen BBS::Theme példány. |
theme(:nortoncommander) { ... } |
Az ilyen nevű preset, a blokk felülírásaival. |
theme(BBS::Theme.mono) { ... } |
Az a példány, a blokk felülírásaival. |
theme { ... } |
A synchronet, a blokk felülírásaival. |
nincs theme hívás |
synchronet. |
A 0.17.0-ban a csupasz Symbol-alak (theme :synchronet) magát a Symbolt tárolta el, és az első újrarajzolás TypeError-ral elszállt, az argumentum nélküli APP.theme pedig visszaállította a tárolt témát; 0.17.1 óta a Symbol a presetet veszi, és a builder argumentum nélküli theme-je az eddig beállított témát adja vissza (nil-t, ha nem volt theme hívás, ami synchronet-et jelent).
Az applicationön belül a theme a használt BBS::Theme-et adja vissza; ezt kapja a BBS::Markdown.to_lines, és ebből színezhetsz saját stringet:
helpers do
def warn_line(text) = "#{theme[:error]}#{text}#{theme[:text]}"
end
A theme blokkban minden egyargumentumos hívás beállítja az adott kulcsot; a style(key, value) ennek explicit alakja, az inherit pedig lecseréli az alapot, amire a felülírások kerülnek:
require 'bbs'
APP = BBS::Application.define do
theme :nortoncommander do
inherit :neumanntronics # opcionális: másik preset legyen az alap
window_border "\e[1;36;44m"
accent "\e[1;33;44m"
style :screen, "\e[40m"
end
end
| Hívás | Hatás |
|---|---|
KULCS érték |
Beállítja a KULCS-ot, a Theme::ROLES egyik szerepét. Egy elgépelt kulcs NoMethodError-t dob. |
style(key, value) |
Ugyanez, explicit módon. |
inherit(base) |
Másik alap: presetnév (Symbol) vagy BBS::Theme. Az előtte és utána beállított felülírások megmaradnak. |
DSL nélkül a merge ugyanezt teszi:
require 'bbs'
custom = BBS::Theme.synchronet.merge(
accent: "\e[1;36;40m",
button_focused: "\e[1;37;44m"
)
Egy teljes Hashből építs BBS::Theme-et. A legegyszerűbben úgy kapod meg az összes kulcsot, ha egy preset Hashéből indulsz a Theme::PRESETS-ben:
require 'bbs'
AMBER = BBS::Theme.new(:amber, BBS::Theme::PRESETS[:mono].merge(
screen: "\e[40m",
text: "\e[0;33;40m",
text_dim: "\e[0;2;33;40m",
text_bright: "\e[1;33;40m",
label: "\e[0;33;40m",
accent: "\e[1;33;40m",
window_bg: "\e[40m",
window_border: "\e[0;33;40m",
window_title: "\e[1;33;40m",
window_title_focused: "\e[0;30;43m",
window_text: "\e[0;33;40m",
button: "\e[0;30;43m",
button_focused: "\e[1;37;43m",
input: "\e[0;33;40m",
input_focused: "\e[0;30;43m",
listbox: "\e[0;33;40m",
listbox_selected: "\e[0;30;43m",
listbox_focused: "\e[1;37;43m"
))
APP = BBS::Application.define do
theme AMBER
end
A témán kívül minden szín egyetlen palettából jön, a BBS::Ansi-ból (lib/bbs/ansi.rb): a flow stílusai, egy soralapú board say-e, egy FakeOS, egy logsor, egy gyors door. A BBS::Color (Color.c(:yellow, text)) az 1.0 óta nincs meg; helyette a konstanst és egy resetet írj.
| Konstans | SGR | Konstans | SGR |
|---|---|---|---|
GREY (sima szöveg) |
\e[0;37m |
BRIGHT_YELLOW |
\e[1;33m |
DIM_GREY |
\e[2;37m |
BLUE |
\e[0;34m |
DARK_GREY |
\e[1;30m |
BRIGHT_BLUE |
\e[1;34m |
WHITE |
\e[1;37m |
MAGENTA |
\e[0;35m |
BRIGHT_RED |
\e[1;31m |
CYAN |
\e[0;36m |
GREEN |
\e[0;32m |
BRIGHT_CYAN |
\e[1;36m |
BRIGHT_GREEN |
\e[1;32m |
REVERSE / REVERSE_OFF |
\e[7m / \e[27m |
YELLOW |
\e[0;33m |
RESET |
\e[0m |
require 'bbs'
p "#{BBS::Ansi::CYAN}hello#{BBS::Ansi::RESET}" # => "\e[0;36mhello\e[0m"
A BBS::Petscii::Colors ugyanezeknek a szekvenciáknak a C64-es nevei (WH YE CY LG GR DG LB BL GN RD PU OR, valamint RV RO az inverz be- és kikapcsolásához; lásd Commodore 64), és egy komponens ezekből a színekből álló szerep-Hasht kap: a BBS::Selector::COLORS-t, a Flow FlowRunner::STYLES-át, egy FakeOS colors-át. A C64 grafikus vásznán ugyanezekből C64-es színkód lesz: BBS::Petscii::Canvas.color(YE) == Canvas::YELLOW.
A BBS::FrameBuffer (lib/bbs/frame_buffer.rb) egy képernyőn kívüli cellarács; minden cella egy karakter és egy SGR-string. Az Application egy egész képkockát rajzol bele, összeveti az előzővel, és csak a különbséget küldi el. A widgetek a render(frame)-ben kapnak egyet.
Az Application minden újrarajzolása (a privát render_screen) ezt csinálja:
next képkockát. Megtartja a committed képkockát (amit a terminál most mutat); az első rajzoláskor vagy átméretezés után üresről indul, és letörli a képernyőt.next-et a téma screen stílusával, kirajzolja az asztalt, a window-kat alulról felfelé, a menüsort és a státuszsort.delta = next.diff(committed); ha nem üres, kurzorrejtés és -mutatás (\e[?25l … \e[?25h) közé csomagolva kiírja, és a next lesz az új committed képkocka.Így egy billentyűleütés után csak a megváltozott cellák mennek le a vonalon: egy listakijelölés mozgatása két sorba kerül, egy ketyegő óra néhány karakterbe. Kézzel is ugyanez működik:
require 'bbs'
committed = BBS::FrameBuffer.new(20, 2)
nxt = BBS::FrameBuffer.new(20, 2)
nxt.move(1, 1)
nxt.write('Time: 12:00', sgr: "\e[1;33m")
p nxt.diff(committed) # => "\e[1;1H\e[0m\e[1;33mTime: 12:00\e[0m"
committed = nxt
nxt = BBS::FrameBuffer.new(20, 2)
nxt.move(1, 1)
nxt.write('Time: 12:01', sgr: "\e[1;33m")
p nxt.diff(committed) # => "\e[1;11H\e[0m\e[1;33m1\e[0m" csak a megváltozott számjegy
p nxt.diff(nxt) # => ""
| Konstans | Érték |
|---|---|
Cell |
Struct.new(:char, :sgr). |
BLANK |
Egy fagyasztott Cell szóközzel, stílus nélkül: ezzel van tele egy új vagy törölt képkocka. |
BOX_CHARS |
A box által használt :single (┌┐└┘─│) és :double (╔╗╚╝═║) vonalkészlet. |
Ezek SGR-escape-eket tartalmazó stringeken dolgoznak, és csak a látható karaktereket számolják. Escape-szekvencia az, amire a BBS::Ansi::CSI_RE illeszkedik (/\e\[[\d;?]*[A-Za-z~]/: SGR, kurzormozgatás, \e[2~-féle billentyűk, és 1.0 óta a privát módú szekvenciák is, mint a \e[?25l); ez váltja a 0.x FrameBuffer::ANSI_ESC_RE-jét.
| Metódus | Mit csinál |
|---|---|
FrameBuffer.text(value) |
value.to_s, az érvénytelen bájtokat ?-re cserélve, hogy egy regexp el tudja olvasni. Az összes többi metódus ezen megy át, így a nyers telnet-bájtok nem dönthetik össze a rajzolást. |
FrameBuffer.plain(text) |
A szöveg escape-szekvenciák nélkül. |
FrameBuffer.visible_width(text) |
A hossz escape-szekvenciák nélkül. |
FrameBuffer.clip_ansi(text, max_width) |
max_width látható karakterre vágja a szöveget, a vágás előtti escape-eket megtartva. 0 vagy kisebb szélességre ''. |
FrameBuffer.wrap_ansi(text, width) |
A gem egyetlen tördelője (a dialógusok, a window-k és a Markdown-renderelő is ezt használják). Szavanként tördeli legfeljebb width széles sorok tömbjébe (a sorvégi szóközöket levágja). A tördelési ponton aktív SGR-t a következő sor elején megismétli, így a szín átível. A szélességnél hosszabb szót nem vágja el; az külön sorba kerül, és kilóg. 0 vagy kisebb szélességre ['']. |
require 'bbs'
fb = BBS::FrameBuffer
p fb.plain("\e[?25l\e[1;33mbright\e[0m") # => "bright"
p fb.visible_width("\e[1;33mbright\e[0m normal") # => 13
p fb.clip_ansi("\e[1;33mbright\e[0m normal", 4) # => "\e[1;33mbrig"
p fb.wrap_ansi("\e[1;32mthe quick brown fox jumps\e[0m over", 10)
# => ["\e[1;32mthe quick", "\e[1;32mbrown fox", "\e[1;32mjumps\e[0m over"]
p fb.wrap_ansi('anextremelylongword here', 8) # => ["anextremelylongword", "here"]
p fb.text("ok\xFFok") # => "ok?ok"
Minden koordináta 1-től számozott oszlop és sor, mint a terminálban. A képkockán kívül eső részt bármelyik szélen levágja, így nyugodtan rajzolhatsz részben a képernyőn kívülre.
| Metódus | Mit csinál |
|---|---|
FrameBuffer.new(cols, rows) |
Üres képkocka. A cols és a rows olvasható. |
clear |
Minden cellát kiürít, és a kurzort a bal felső sarokba teszi. |
move(col, row) |
Beállítja az írási pozíciót. |
write(text, sgr: nil) |
A pozíciótól kezdve kiírja a text karaktereit, mind egy stílusban, és továbblép. Az escape-eket nem értelmezi, karakterként írná ki őket: stílusos stringhez a write_ansi való. |
write_ansi(text, base_sgr: nil) |
Beágyazott escape-eket tartalmazó stringet ír ki. Lásd lent. |
fill(x:, y:, width:, height:, sgr: nil, char: ' ') |
Kitölt egy téglalapot a char-ral. |
hline(x:, y:, length:, char: '─', sgr: nil) |
Vízszintes vonal. |
vline(x:, y:, length:, char: '│', sgr: nil) |
Függőleges vonal. |
box(x:, y:, width:, height:, sgr: nil, fill_sgr: nil, style: :single, title: nil, title_sgr: nil, title_indent: 0) |
Keret sgr színben; a belseje szóközökkel kitöltve fill_sgr színben, ha meg van adva (különben érintetlen); a style :single vagy :double; a title a felső élben, title_sgr (vagy sgr) színben, title_indent oszloppal jobbra tolva és a helyre vágva. 2×2 alatt semmit nem rajzol. |
shadow(x:, y:, width:, height:, sgr: nil) |
Árnyék a megadott téglalaphoz: két oszlop jobbra, egy sor alul, egy sorral és két oszloppal eltolva. Az sgr alapértéke \e[40m. |
diff(from) |
Azok az ANSI-bájtok, amelyek a from képkockából ezt csinálják (lásd fent). |
A write_ansi úgy értelmezi az escape-eket, ahogy egy terminál tenné, a képkockán belül:
\e[...m) hozzáadódik az aktuális stílushoz; egy reset (\e[0m vagy \e[m) a base_sgr-hez tér vissza, nem a „stílus nélkülihez”. Így tartja meg egy kék window-ban egy színes szó utáni szöveg a kék hátteret.\e[NC (kurzor előre) N szóközt ír az aktuális stílusban, ahogy az ANSI-art használja.\e[SOR;OSZLOPH) is, így egy widgeten belüli szöveg nem ugorhat ki belőle; a 0.x cursor: opciója megszűnt. A vezérlőkarakterek is elvesznek (a \n és a \r is): egy hívás egy sort ír.require 'bbs'
fb = BBS::FrameBuffer.new(20, 4)
fb.move(1, 1)
fb.write('Hello', sgr: "\e[1;33m")
fb.move(18, 1)
fb.write('clipped') # csak a "cli" fér ki
fb.move(1, 2)
fb.write_ansi("a\e[1mb\e[0mc\e[3Cd", base_sgr: "\e[44m") # stílusok: 44 / 44+bold / 44, három szóköz, d
fb.move(1, 3)
fb.write_ansi("x\e[4;10Hy") # a H nem számít: "xy"
Az eredmény:
Hello cli
abc d
xy
Keretes panel mintás háttéren, árnyékkal:
require 'bbs'
fb = BBS::FrameBuffer.new(32, 9)
fb.fill(x: 1, y: 1, width: 32, height: 9, sgr: "\e[44m", char: '░')
fb.box(x: 3, y: 2, width: 24, height: 6, sgr: "\e[1;37;44m", fill_sgr: "\e[44m",
style: :double, title: 'Inbox', title_sgr: "\e[1;33;44m", title_indent: 4)
fb.shadow(x: 3, y: 2, width: 24, height: 6)
fb.hline(x: 4, y: 4, length: 22)
fb.vline(x: 15, y: 5, length: 2)
fb.move(5, 3)
fb.write('3 new messages', sgr: "\e[0;37;44m")
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
░░╔═════ Inbox═══════════╗░░░░░░
░░║ 3 new messages ║ ░░░░
░░║──────────────────────║ ░░░░
░░║ │ ║ ░░░░
░░║ │ ║ ░░░░
░░╚══════════════════════╝ ░░░░
░░░░ ░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
A framebuffer Ruby-karakterenként egy cellát számol. Ez helyes az ASCII-ra, az előre komponált ékezetes latin betűkre, a keretrajzoló, blokk- és braille-karakterekre, vagyis mindarra, amit maga a gem rajzol. Hibás viszont:
e plusz U+0301): két cellának számítanak, de egy oszlopon látszanak.A visible_width, a clip_ansi, a wrap_ansi és a Markdown-táblázatok ugyanígy számolnak. Normalizáld a szöveget NFC-re (text.unicode_normalize(:nfc)), és ami pontosan egymás alá kell kerüljön, abba ne tegyél széles karaktert.
A BBS::Renderer (lib/bbs/renderer.rb) egy ERB-sablonból stringet állít elő egy soros, flow-stílusú screenhez. Ezt használja a Flow screen lépése (lásd Flow). A framebufferhez nincs köze: a dupla pufferelést a fent leírt FrameBuffer#diff végzi.
BBS::Renderer.render(name, vars = {}, dir: BBS.config.screens_dir)
| Argumentum | Jelentés |
|---|---|
name |
A sablon neve: a dir mappa NÉV.erb fájlja. |
vars |
Egy Hash; minden párból példányváltozó lesz a sablonban (a username:-ből @username). |
dir: |
A sablonmappa. RuntimeError-t dob („No screens_dir: …”), ha sem ez, sem a BBS.config.screens_dir nincs beállítva. |
Az ERB trim_mode: '-'-dal fut (így a -%> elnyeli a sortörést), és az eredmény minden \n-je \r\n lesz a telnetvonal kedvéért. A sablon ezeket a helpereket hívhatja:
| Helper | Kimenet |
|---|---|
clr |
\e[2J\e[H: képernyőtörlés, kurzor a sarokba. |
reset |
\e[0m |
green, yellow, cyan, white, red |
Az élénk szín (\e[1;32m, \e[1;33m, \e[1;36m, \e[1;37m, \e[1;31m). |
gray, dim_green |
\e[0;37m, \e[0;32m |
banner(text, padding: 2) |
Zöld dupla vonalas keret a szöveg körül. |
big_banner(text, font: 'slant') |
FIGlet-betűk (az artii gemmel) zölden, két szóközzel beljebb. |
screens/welcome.erb:
<%= clr %><%= banner 'Welcome' %>
<%= green %>Hello <%= @username %>!<%= reset %>
<% if @new_mail > 0 -%>
<%= yellow %>You have <%= @new_mail %> new message(s).<%= reset %>
<% end -%>
require 'bbs'
out = BBS::Renderer.render('welcome', { username: 'tasi', new_mail: 2 }, dir: 'screens')
A terminálra írva (a keret és az üdvözlés élénkzöld, a levelekről szóló sor élénksárga):
╔═══════════╗
║ Welcome ║
╚═══════════╝
Hello tasi!
You have 2 new message(s).
A Flow banner és big_banner lépése a BBS::Banner-t (lib/bbs/banner.rb) használja, ami küldésre kész, \r\n sorvégű, két szóközzel behúzott stringeket ad:
| Metódus | Kimenet |
|---|---|
Banner.render(text, color: "\e[1;32m", padding: 2) |
Dupla vonalas keret a szöveg körül, mindkét oldalon padding szóközzel. |
Banner.render_big(text, color: "\e[1;32m", font: 'slant') |
FIGlet-betűk az artii-vel; bármely Artii-fontnév jó (slant, big, small…). |
require 'bbs'
print BBS::Banner.render_big('BBS', font: 'small').gsub("\r\n", "\n")
___ ___ ___
| _ ) _ ) __|
| _ \ _ \__ \
|___/___/___/
A BBS::Markdown (lib/bbs/markdown.rb) Markdown-szövegből ANSI-val színezett sorok tömbjét készíti, adott szélességre. Az eredményt egy BBS::Widgets::ScrollView-nak, egy görgethető Info window-nak adhatod, vagy magad írhatod ki a sorokat.
BBS::Markdown.to_lines(text, width:, theme:)
| Argumentum | Jelentés |
|---|---|
text |
A Markdown. Az érvénytelen bájtokból ? lesz. |
width: |
A tördelési szélesség. Ha a sorok ScrollView-ba kerülnek, adj meg eggyel kevesebbet a szélességénél, hogy maradjon hely a görgetősávnak. |
theme: |
Egy BBS::Theme (vagy bármi, ami válaszol a [key]-re); a színek a szerepeiből jönnek. |
Applicationön belül a szokásos hívás:
lines = BBS::Markdown.to_lines(text, width: width - 1, theme: ctx[:app].theme)
Minden stílusos szakasz a téma text SGR-jével zárul, így az utána következő szöveg visszakapja a hátterét: színezett window-háttéren ez számít. A sima bekezdéssorok nem kezdődnek saját SGR-rel; a ScrollView text alapstílussal rajzolja őket. Ha a sorokat közvetlenül egy sessionre írod, mindegyik elé tedd oda a theme[:text]-et.
| Markdown | Így jelenik meg | Szerep |
|---|---|---|
# Címsor |
A címsor szövege # nélkül (a záró #-ek is eltűnnek), tördelve. |
text_bright |
## Címsor |
Ugyanígy. | accent |
###-tól ######-ig |
Ugyanígy, minden szint egyformán. | label |
| Bekezdés | Az egymást követő sorok egy-egy szóközzel összefűzve, a whitespace összevonva, szavanként tördelve. | text |
| Üres sor | Lezárja a bekezdést, és egy üres sort ad. | |
**félkövér** |
A szöveg. | text_bright |
*dőlt*, _dőlt_ |
A szöveg. Szavon belül nem: a snake_case_name és az a*b*c változatlan marad. |
text_dim |
`kód` | A szöveg. | label |
||
[szöveg](url) |
Csak a szöveg; az URL elmarad. | accent |
> idézet |
Minden sor │ jellel, a szélesség mínusz 2-re tördelve. |
jel: text_dim |
- elem, * elem, + elem |
• elem, függő behúzással tördelve. A jel előtti szóközök megmaradnak, így készülnek a beágyazott listák. |
jel: accent |
1. elem |
1. elem, a szám úgy, ahogy le van írva, függő behúzással. |
szám: accent |
| Behúzott sor egy listaelem alatt | Az elem szövegét folytatja. | |
---, ***, ___ (legalább három) |
Teljes szélességű ─ vonal. |
label |
| Kódblokk (három backtick, bármilyen info stringgel) | A sorok változatlanul, a szélességre vágva (nem tördelve); a beágyazott ANSI átmegy. | label |
GFM-táblázat (fejléc, --- elválasztó sor, törzssorok) |
Keretrajzoló karakterekkel rajzolt táblázat; a fejléccellák félkövérek; :--, :-:, --: igazítás; a hiányzó cellák kitöltve; ha túl széles, a legszélesebb oszloptól kezdve szűkül, aztán a cellák vágódnak. |
keret: label, fejléc: text_bright |
\*, \_, \`, \#, \[… |
A karakter backslash nélkül (lásd a korlátokat). |
Egy minta 36-os szélességen, synchronet-tel:
require 'bbs'
md = <<~MD
# Welcome
## Rules
### Small print
Be **nice**, *kind* and _brief_. See [the wiki](https://wiki.example) or run `help`.
This line joins the paragraph above and wraps at the width.
> Quoted text gets a gutter
> on every line.
- first item
- second item that is long enough to wrap onto a second line
continued here
- nested
1. one
2. two
---
| Name | Calls | Note |
|:-----|------:|:----:|
| ra | 12 | ok |
| **zed** | 3 | |
MD
lines = BBS::Markdown.to_lines(md, width: 36, theme: BBS::Theme.synchronet)
puts lines.map { |l| BBS::FrameBuffer.plain(l) }
Színek nélkül (a terminálban: a Welcome élénkfehér, a Rules sárga, a Small print cián, a nice élénkfehér, a kind és a brief halvány, a the wiki sárga, a help cián, a listajelek és -számok sárgák, a vonal és a táblázatkeret cián):
Welcome
Rules
Small print
Be nice, kind and brief. See the
wiki or run help. This line joins
the paragraph above and wraps at the
width.
│ Quoted text gets a gutter
│ on every line.
• first item
• second item that is long enough to
wrap onto a second line continued
here
• nested
1. one
2. two
────────────────────────────────────
┌──────┬───────┬──────┐
│ Name │ Calls │ Note │
├──────┼───────┼──────┤
│ ra │ 12 │ ok │
│ zed │ 3 │ │
└──────┴───────┴──────┘
Egy el nem férő táblázat szűkül, aztán a cellái vágódnak (30-as szélesség):
┌──────────┬─────────────────┐
│ Door │ Description │
├──────────┼─────────────────┤
│ SERMO │ An AI character │
│ Impostor │ Social deductio │
└──────────┴─────────────────┘
A forrással ellenőrizve; ezek egyikére se építs:
\*not italic\* mégis dőlt lesz, a backslashek pedig megmaradnak: az eredmény egy backslash, egy halvány not italic, majd még egy backslash. Ugyanez a helyzet a \_x\_-szel. Ahol nem illeszkedik kiemelési szabály, ott működik az escape: a 2\*3\*4-ből 2*3*4, a \# no heading-ből # no heading lesz.`a\*b` a*b-t mutat.__félkövér__, ~~áthúzás~~, HTML vagy entitás (úgy jelennek meg, ahogy le vannak írva), setext-címsor (=== egy sor alatt), behúzott kódblokk, kemény sortörés (két sorvégi szóköz) és feladatlista (a - [ ] megtartja a szögletes zárójelet). egy !-et és az alt szöveget mutatja a link színében.> > x egy jelet és egy szó szerinti > x-et ad.theme DSL a helyén, a futási ciklusscreen, banner és big_banner lépések