manual.typ
1 #import "@preview/fontawesome:0.4.0": * 2 #import "@preview/mantys:0.1.4": * 3 #import "@preview/metalogo:1.0.2": LaTeX 4 #import "@preview/suiji:0.3.0" 5 #import "@preview/wrap-it:0.1.0": * 6 7 #import "@preview/babel:0.1.1": * 8 #import "../assets/logo.typ": logo 9 #import "../src/alphabets.typ": alphabets, maze 10 11 #let redcell = table.cell.with(fill: rgb("#FFCCCC")) 12 #let grncell = table.cell.with(fill: rgb("#CCDDAA")) 13 #let ylwcell = table.cell.with(fill: rgb("#EEEEBB")) 14 15 // By default Mantys sets the font on the title page; this is a way around that. 16 #let gentium-titlepage(..args) = { 17 set text(font: ("Gentium Plus", "Noto Emoji")) 18 show raw: set text(font: "Iosevka") // https://typeof.net/Iosevka/ 19 titlepage(..args, toc: false) 20 } 21 #show: mantys.with( 22 titlepage: gentium-titlepage, 23 title: "Babel", 24 ..toml("../typst.toml"), 25 abstract: [ 26 #align(center, text(size: 32pt, logo())) 27 28 This package provides functions that replace actual text with random characters, which is useful for redacting confidential information or sharing the design and structure of an existing document without disclosing the content itself. 29 A variety of ready-made sets of characters for replacement are available (#alphabets.len() in total), representing diverse writing systems, codes, notations and symbols. 30 Some of these are more conservative (such as emulating redaction using a wide black pen) and many are more whimsical, as demonstrated by the following example: 31 32 #example[``` 33 #import "@preview/babel:0.1.1": * 34 35 #baffle(alphabet: "welsh")[Hello]. My #tippex[name] is #baffle(alphabet: "underscore")[Inigo Montoya]. You #baffle(alphabet: "alchemy")[killed] my #baffle(alphabet: "shavian")[father]. Prepare to #redact[die]. 36 37 Using show rules strings, regular expressions and other selectors can be redacted automatically: 38 39 #show "jan Maja": baffle.with(alphabet: "sitelen-pona") 40 #show regex("[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*"): baffle.with(alphabet: "maze-3") 41 42 I’m jan Maja, and my email is `foo@digitalwords.net`. 43 ```] 44 ], 45 examples-scope: ( 46 baffle: baffle, 47 redact: redact, 48 tippex: tippex, 49 redcell: redcell, 50 grncell: grncell, 51 ylwcell: ylwcell, 52 alert: mty.alert, 53 ), 54 ) 55 56 57 #set text(font: (("Gentium Plus", "Noto Emoji"))) 58 #show regex("[⟨⟩]"): it => {text(font: "Gentium Basic", it)} // for some reason the Gentium Plus glyphs look horrible 𓂜 59 #show raw: set text(font: "Iosevka") // https://typeof.net/Iosevka/ 60 #set raw(theme: "tokyonight_day.tmTheme") // https://github.com/folke/tokyonight.nvim/blob/main/extras/sublime/tokyonight_day.tmTheme 61 #show link: it => [#it#text(fill: rgb("#993333"))[°]] 62 #show figure.where(kind: table): set figure.caption(position: top) 63 #set heading(supplement: "§") 64 #set table(stroke: none) 65 66 #let hbo = text.with(font: "SBL Hebrew", lang: "he") 67 68 = Introduction 69 70 == Purpose and usage scenarios 71 72 At times one wishes to make portions of text (or the whole text) hidden from the recipient: 73 74 - The most common case is when redacting confidential information. 75 Traditionally this is done by overwriting portions of text with a wide black pen and photocopying the result. 76 - Another usage scenario is sharing the design and structure of a document, but not the text itself. 77 While _lorem ipsum_#footnote[ 78 Typst provides a built-in function for this, #link("https://typst.app/docs/reference/text/lorem/")[`lorem()`], which outputs pseudo-Latin. 79 For a Japanese blind text generator, see #link("https://typst.app/universe/package/roremu")[#package[roremu]]. 80 ] blocks help when demonstrating the design of a template~— replacing places where actual text would go with placeholder text~— when sharing the way a particular existing document looks they are less helpful, since in order to use them one would have to make a copy of the document and manually substitute text with placeholder text of more or less the same length, which is tiresome and prone to errors. 81 82 In addition, playing with various contemporary, historical and constructed writing systems is a special kind of geeky fun… 83 While the package does have serious, practical use, most of the provided alphabets (@alphabets) are there just for fun. 84 85 One thing I ask you to avoid is using #package[Babel] for mocking cultures, as often done with mimicry typefaces such as #link("https://en.wikipedia.org/wiki/Faux_Cyrillic")[Faux Cyrillic], #link("https://en.wikipedia.org/wiki/Faux_Hebrew")[Faux Hebrew] or #link("https://en.wikipedia.org/wiki/Wonton_font")[Wonton font], which have more than subtle racist undertone. This package is a celebration of the variety and diversity of writing#footnote[ 86 Just look at @alphabets! Human beings~— as well as Klingons and Elves…~— came up with so many different graphic symbols to represent sounds and ideas it’s mind-boggling. 87 ]. 88 89 If you wish to share the Typst source files of your document, not just the precompiled output, a tool called #link("https://github.com/frozolotl/typst-mutilate")[_Typst Mutilate_] might be useful for you. 90 Unlike #package[Babel], it is not a Typst package but an external tool, written in Rust. 91 It replaces the content of a Typst document with random words selected from a wordlist or random characters (similarly to Babel), changing the document in place (so make sure to run it on a _copy_!). 92 As a package for Typst, #package[Babel] cannot change your source files. 93 94 == Name 95 96 Have a seat, it’s story time. 97 #package[Babel] is named so as a wordplay on two things: the Biblical myth of the Tower of Babel and the #link("https://ctan.org/pkg/babel")[#LaTeX package] sharing the same name. 98 For anyone who isn’t familiar with the story, here is the full fragment (Genesis 11.1–9): 99 100 #let verse(number) = [#super(number) ] 101 #let hl = highlight.with(fill: eastern.transparentize(85%), radius: 1pt) 102 #[ 103 #set text(size: 10pt) 104 #table( 105 columns: (65%, 35%), 106 gutter: 0.75em, 107 [ 108 #show "YHWH": smallcaps[Yhwh] 109 #verse("1")Now all the earth was of one language and one set-of-words. 110 #verse("2")And it was when they migrated to the east that they found a valley in the land of Shin’ar and settled there. 111 #verse("3")They said, each one to his neighbor: Come-now! Let us bake bricks and let us burn them well-burnt! —;For them brick-stone was like building-stone, and raw-bitumen was for them like red-mortar. 112 #verse("4")And they said: Come-now! Let us build ourselves a city and a tower, its top in the heavens, and let us make ourselves a name, lest we be scattered over the face of all the earth! 113 #verse("5")But YHWH came down to look over the city and the tower that the humans were building. 114 #verse("6")YHWH said: Here, [they are] one people with one language for them all, and this is [merely] the first of their doings— now there will be no barrier for them in all that they scheme to do! 115 #verse("7")#hl[Come-now! Let us go down and there let us baffle their language, so that no one will understand the language of his neighbor.] 116 #verse("8")So YHWH scattered them from there over the face of all the earth, and they had to stop building the city. 117 #verse("9")#hl[Therefore its name was called Bavel/Babble, for there YHWH baffled the language of all the earth-folk, and from there, YHWH scattered them over the face of all the earth.] 118 ], 119 hbo[ 120 #verse("1")וַיְהִ֥י כׇל־הָאָ֖רֶץ שָׂפָ֣ה אֶחָ֑ת וּדְבָרִ֖ים אֲחָדִֽים׃ 121 #verse("2")וַיְהִ֖י בְּנׇסְעָ֣ם מִקֶּ֑דֶם וַֽיִּמְצְא֥וּ בִקְעָ֛ה בְּאֶ֥רֶץ שִׁנְעָ֖ר וַיֵּ֥שְׁבוּ שָֽׁם׃ 122 #verse("3")וַיֹּאמְר֞וּ אִ֣ישׁ אֶל־רֵעֵ֗הוּ הָ֚בָה נִלְבְּנָ֣ה לְבֵנִ֔ים וְנִשְׂרְפָ֖ה לִשְׂרֵפָ֑ה וַתְּהִ֨י לָהֶ֤ם הַלְּבֵנָה֙ לְאָ֔בֶן וְהַ֣חֵמָ֔ר הָיָ֥ה לָהֶ֖ם לַחֹֽמֶר׃ 123 #verse("4")וַיֹּאמְר֞וּ הָ֣בָה ׀ נִבְנֶה־לָּ֣נוּ עִ֗יר וּמִגְדָּל֙ וְרֹאשׁ֣וֹ בַשָּׁמַ֔יִם וְנַֽעֲשֶׂה־לָּ֖נוּ שֵׁ֑ם פֶּן־נָפ֖וּץ עַל־פְּנֵ֥י כׇל־הָאָֽרֶץ׃ 124 #verse("5")וַיֵּ֣רֶד יְהֹוָ֔ה לִרְאֹ֥ת אֶת־הָעִ֖יר וְאֶת־הַמִּגְדָּ֑ל אֲשֶׁ֥ר בָּנ֖וּ בְּנֵ֥י הָאָדָֽם׃ 125 #verse("6")וַיֹּ֣אמֶר יְהֹוָ֗ה הֵ֣ן עַ֤ם אֶחָד֙ וְשָׂפָ֤ה אַחַת֙ לְכֻלָּ֔ם וְזֶ֖ה הַחִלָּ֣ם לַעֲשׂ֑וֹת וְעַתָּה֙ לֹֽא־יִבָּצֵ֣ר מֵהֶ֔ם כֹּ֛ל אֲשֶׁ֥ר יָזְמ֖וּ לַֽעֲשֽׂוֹת׃ 126 #verse("7")#hl[הָ֚בָה נֵֽרְדָ֔ה וְנָבְלָ֥ה שָׁ֖ם שְׂפָתָ֑ם אֲשֶׁר֙ לֹ֣א יִשְׁמְע֔וּ אִ֖ישׁ שְׂפַ֥ת רֵעֵֽהוּ׃] 127 #verse("8")וַיָּ֨פֶץ יְהֹוָ֥ה אֹתָ֛ם מִשָּׁ֖ם עַל־פְּנֵ֣י כׇל־הָאָ֑רֶץ וַֽיַּחְדְּל֖וּ לִבְנֹ֥ת הָעִֽיר׃ 128 #verse("9")#hl[עַל־כֵּ֞ן קָרָ֤א שְׁמָהּ֙ בָּבֶ֔ל כִּי־שָׁ֛ם בָּלַ֥ל יְהֹוָ֖ה שְׂפַ֣ת כׇּל־הָאָ֑רֶץ וּמִשָּׁם֙ הֱפִיצָ֣ם יְהֹוָ֔ה עַל־פְּנֵ֖י כׇּל־הָאָֽרֶץ׃] 129 ] 130 ) 131 ] 132 133 The myth explains why people are scattered everywhere and why they speak different languages, and it also provides folk etymology for the name of the city of Babylon (#hbo[בָּבֶל] _Bāḇel_): #hbo[בָּלַ֥ל] _bālal_ ‘he mixed, he confounded’ in verse~9 and #hbo[וְנָבְלָ֥ה] _wə-nāḇlâ_ ‘and let us mix, and let us confound’ in verse~7 (both from the root #hbo[ב־ל־ל] _√BLL_ ‘to mix, to confound, to confuse’) sounds a bit like #hbo[בָּבֶל] _Bāḇel_ ‘Babel’.#footnote[ 134 Interestingly, the Babylonian Akkadian name which is the basis for the Hebrew name is 𒆍𒀭𒊏𒆠 _Bābilim_ ‘(lit.) the gate of the gods’. 135 Even more interestingly, there is evidence this Akkadian interpretation of the name (as ‘the gate of the gods’) itself was a Semitic folk etymology on a non-Semitic name! 136 This is all very… confusing, how people mix up things. 137 ] 138 Everett Fox translated these verbs brilliantly in the #link("https://www.sefaria.org.il/Genesis.11?ven=The_Five_Books_of_Moses,_by_Everett_Fox._New_York,_Schocken_Books,_1995&lang=bi")[_Schocken Bible_], with the English verb _baffle_, where all other translation I looked at have _confound_ or _confuse_. 139 This is the reason for choosing that translation for the above excerpt and the name #cmd("baffle") for the main function provided by #package[Babel]. 140 141 Now, idea of the Tower of Babel as the explanatory myth behind linguistic diversity still persists in contemporary culture, as demonstrated by the Babel fish in Douglas Adams’s _the Hitchhiker's Guide to the Galaxy_, the dictionary and machine-translation software _Babylon_ and the #LaTeX package #link("https://ctan.org/pkg/babel")[_Babel_]. 142 Fittingly, the Babel #LaTeX package enhances the capabilities of localisation and internationalisation. 143 With our Typst #package[Babel] I chose to take the other connotation, of confusion, bafflement and mixing 🙃 144 145 == Logo 146 147 #wrap-content( 148 column-gutter: 0.5em, 149 text(size: 16pt, logo()), 150 [ 151 The logo features a minimalist icon of the Tower of Babel or a ziggurat; see @licence for attribution. 152 The background colour is the same shade of turquoise used by #package[Mantys]. 153 ] 154 ) 155 156 == Copyright and licence <licence> 157 158 The this package is released under #link("https://spdx.org/licenses/MIT-0.html")[MIT-0]. 159 160 #package[Babel]’s logo features an #link("https://thenounproject.com/icon/babel-2526388/")[image] by #link("https://andrejskirma.com/")[Andrejs Kirma] which is released under CC~BY-3.0. 161 I attribute them willingly, as I find the graphics very fitting for the logo. 162 Go check #link("https://thenounproject.com/creator/andrejs/")[their other icons]. 163 164 == Versioning and stability <ver> 165 166 #package[Babel] follows the Semantic Versioning scheme (#link("https://semver.org/spec/v2.0.0.html")[SemVer 2.0.0]). 167 While it is fully usable in its current form (version `0.*.*`), changes to the API might occur in future versions. 168 This should not pose a problem: 169 170 - When you import a package in Typst you can indicate the version (for example, `#import "@preview/example:0.1.0"`), so no surprises should occur. 171 - Changes to the API will be clearly indicated in the documentation.#footnote[ 172 If the characters replaced by the package change between versions, this is not counted as a change: the whole point is the actual identity of the random characters is, well, random. 173 Changes to the alphabets (@alphabets) are also considered minor. 174 ] 175 176 == Participation and contact <contact> 177 178 If there is anything that doesn’t work well or any feature you want added or changed, don’t hesitate to #link("https://codeberg.org/afiaith/babel/issues")[open an issue] on the Git repository, and I will do my best to make the package more useful for you and others. 179 If you want to contribute code/documentation (changes, additions, corrections, improvements, etc. no matter how small or large), #link("https://codeberg.org/afiaith/babel/pulls")[pull requests] are very welcome; thanks! 180 181 In particular, contributions of alphabets (`src/alphabets.yaml`) are welcome. 182 This version contains #alphabets.len()~(!) alphabets, but like Pokémon, you gotta catch ’em all… When choosing a font for the script of your alphabet: 183 - If only basic Latin characters are used, don’t set a font. 184 - If the new alphabet uses a script already represented on #package[Babel], prefer the font already in use (for example, Gentium Plus for Latin, Greek and Cyrillic, SBL Hebrew for Hebrew,~…). 185 - Prefer free, gratis and libre open-source fonts (FLOSS). 186 - Prefer serif#footnote[ 187 Admittedly, for many scripts Noto Sans is the only good FLOSS option. 188 ] fonts (wherever serif makes sense) that go well with Gentium Plus. 189 190 Not everyone is familiar with Git, so if that is a problem feel free to contact me in any other way; see #link("https://me.digitalwords.net/")[`https://me.digitalwords.net/`] for contact information. 191 192 == Disclaimer 193 194 I hold no responsibility for anything that may occur as a result of using this package, nor can I guarantee there are no edge cases where text that should have been redacted stays readable (please do report such cases; see @contact). 195 If you use this package with actual confidential information, please read the manual (especially @limitations), check the results and understand the risks. 196 197 = Usage 198 199 == Provided functions <functions> 200 201 #import "../src/baffle.typ": punctuation 202 #tidy-module( 203 read("../src/baffle.typ"), 204 include-examples-scope: true, 205 name: "fonts", 206 show-outline: false, 207 scope: ( 208 punctuation: punctuation 209 ) 210 ) 211 212 If you frequently use #cmd("baffle") with certain parameters, defining an alias of your own makes things simpler, easier, and more elegant; for example: 213 214 #example(``` 215 #let tp = baffle.with(alphabet: "sitelen-pona", punctuate: false, output-word-divider: "\u{200b}") 216 Hi! #tp[this!] and #tp[that…] are confidential. 217 ```) 218 219 === Using show rules <show-rules> 220 221 While surrounding segments of commands is useful for short amounts of text, applying commands to long segments~— or even the whole document~— is cumbersome. 222 Fortunately, Typst provides us with a clever solution for that: show rules. 223 Consider the following example: 224 225 #example[``` 226 This text is shown as plaintext. 227 228 #show: baffle.with(alphabet: "astrology") 229 230 Now from here on the text is baffled! 231 ```] 232 233 Show rules can be used for redacting strings, regular expressions and other selectors automatically (see the #link("https://typst.app/docs/reference/styling/#show-rules")[documentation]): 234 235 #example[``` 236 #show "Ramona Flowers": baffle.with(alphabet: "redaction") 237 #show regex("[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*"): baffle.with(alphabet: "maze-3") 238 239 Her name is Ramona Flowers, and her email is `ramona@flowers.name`. 240 ```] 241 242 At the moment (version 0.11.0) there is no way to revoke show rules#footnote[ 243 It is planned, though. 244 See the #link("https://typst.app/docs/roadmap/")[roadmap] and #link("https://github.com/typst/typst/issues/420")[this issue]. 245 ]. 246 As a workaround, bracketing the relevant segments can limit the scope of a show rule: 247 248 #example[``` 249 This is plaintext. 250 251 #[ 252 #show: baffle 253 254 A baffled part of the document. 255 ] 256 257 Plaintext again. 258 ```] 259 260 261 262 === Limitations and considerations <limitations> 263 264 ==== General matters 265 266 ===== Educated guesses 267 268 From an information-theoretical point of view, the #cmd("baffle") loses a lot of information: it may retain only the length of each word, capitalisation, and punctuation, and each of the three can be turned off. 269 Unlike encryption, where by definition the plaintext can be deciphered using the proper technique and secret information, here the original text cannot be recovered algorithmically. 270 Given certain conditions, educated guesses can be made though: for example (and an extreme one at that…), if you see a baffled word that is 27 letters long, you can assume with confidence that it is _electroencephalographically_ if the text deals with neurology or _ethylenediaminetetraacetate_ if it deals with chemistry. 271 In more realistic scenarios contextual information and the nature of the text (rigid forms are more predictable than literary prose, for example) can assist in making educated guesses. 272 273 For maximal obfuscation, turn word division, capitalisation and punctuation off, but realistically I don’t think this is needed, and the result might be less aesthetically pleasing, depending on the alphabet used. 274 275 Note that #cmd("baffle") retain information about the length of words in characters#footnote[ 276 Some alphabets may increase the number of characters, as they include multi-character ‘letters’ (e.g. digraphs). 277 ], not in horizontal length. 278 This covers some attack vectors involving proportional fonts and kerning, but opens others, based on counting characters. 279 280 ===== Unintended meanings 281 282 Letters are chosen from the alphabets randomly. 283 In theory, unintended meanings may occur, especially with relatively short words. 284 Also, note that some scripts (Egyptian hieroglyphs and Phaistos Disc, sitelen sitelen,~…) contain pictograms which might be taboo in your culture. 285 286 ==== Particular, implementation-dependant matters <particular-limitations> 287 288 Limitations concerning the #arg("as-string") argument are discussed in @functions, and those concerning particular alphabets are discussed in @alphabets. 289 290 If elements which are included in the table of contents appear within #cmd("baffle"), they appear in plaintext in the PDF table of contents#footnote[ 291 This is the one accessible using a sidebar or the tab key, depending on the PDF reader, not the one typeset in the document itself. 292 ] unless they are bound within a #cmd("baffle") command with #arg("as-string") set to #value(true), in which case the bound text disappears from the PDF table of contents. 293 The baffled text in a typeset table of contents (#cmd("outline")) is different to the one used in the headings. 294 295 == Provided alphabets <alphabets> 296 297 In total #alphabets.len() alphabets are provided by #package[Babel]. 298 ‘Alphabet’ is used here in the sense used in formal language theory, not linguistics (neither in the narrow nor the wide sense).#footnote[ 299 If these distinctions are not clear to you, read the following Wikipedia articles if you’d like to learn about them: #link("https://en.wikipedia.org/wiki/Alphabet")[Alphabet] (linguistics, both senses) and #link("https://en.wikipedia.org/wiki/Alphabet_(formal_languages)")[Alphabet (formal languages)]. 300 ] 301 Many of the ‘alphabets’ below are not alphabets in the linguistic sense. 302 303 #let font-icon = text(font: "Linux Libertine", size: 15pt)[_Aa_] 304 305 === Legend 306 307 #table( 308 columns: 2, 309 align: (x, y) => if x == 0 { center } else { left }, 310 fa-icon("wikipedia-w"), 311 [a link to the relevant article in Wikipedia. If anything piques your interest, down to the rabbit hole you go.], 312 font-icon, 313 [a link to the font used in the example.], 314 [`slug`], 315 [the string you provide the #arg("alphabet") argument with (`arabic` for Arabic, `alchemy` for Alchemical symbols, …).], 316 ) 317 318 === Notes 319 - The characters from the output alphabet are chosen at random, which has several implications: 320 - Phonotactics is not taken into consideration. 321 - The output contains mostly non-words which defy the rules of how words look in the alphabet in question. 322 - Letter frequency is also not taken into consideration. 323 At most, the vowels or consonants are superficially doubled in order to account for severe disparity in the type:token ratio. 324 Because characters are chosen at random final letters have been removed from the Hebrew script and the Canadian Aboriginal syllabics, so they will not occur in incorrect positions.#footnote[ 325 It’s a better compromise not to represent the final forms than to have them occur in incorrect positions. 326 ] 327 - Some of the scripts~— such as Egyptian and Anatolian hieroglyphs or the sitelen pona and sitelen sitelen scripts~— are normally written with grouping of characters in a non-linear manner. This is not done here, where the glyphs are stringed one after the other in a linear manner. 328 329 === A menu of alphabets 330 331 #let rng = suiji.gen-rng(0) 332 333 #let sample = "Ni malleviĝu do, kaj Ni konfuzu tie ilian lingvon, por ke unu ne komprenu la parolon de alia. Kaj la Eternulo disigis ilin de tie sur la supraĵon de la tuta tero, kaj ili ĉesis konstrui la urbon."//Tial oni donis al ĝi la nomon Babel, ĉar tie la Eternulo konfuzis la lingvon de la tuta tero kaj de tie la Eternulo disigis ilin sur la supraĵon de la tuta tero." 334 335 #let describe-alphabet(slug, alphabet) = [ 336 #box( 337 fill: luma(245), 338 inset: 0.75em, 339 radius: 0.75em, 340 )[ 341 #strong(alphabet.at("name")) 342 #if "native" in alphabet.keys() [ 343 #h(1em)#[#set text(font: alphabet.at("font")) if "font" in alphabet.keys();#alphabet.at("native")] 344 ] 345 #h(1fr) 346 #text(font: "Iosevka", slug) 347 #h(1em) 348 #link("https://en.wikipedia.org/wiki/" + alphabet.at("wiki"), fa-icon("wikipedia-w")) 349 #if "fonturl" in alphabet.keys() {link(alphabet.at("fonturl"), font-icon)} 350 \ 351 #[ 352 #set text(lang: alphabet.at("lang")) if "lang" in alphabet.keys() 353 #box( 354 width: 100%, height: 1.5em, clip: true, baseline: -0.2em, 355 // TODO Make less hackish and ugly 356 box(width: 1000%, baseline: 1.2em)[ 357 #baffle( 358 sample, 359 alphabet: slug, 360 punctuate: if "punctuate" in alphabet.keys() {alphabet.at("punctuate")} else {true}, 361 output-word-divider: if "word-divider" in alphabet.keys() {alphabet.at("word-divider")} else {" "}, 362 as-string: true, 363 ) 364 ] 365 ) 366 ] 367 #v(-0.5em)#if "note" in alphabet.keys() { 368 eval( 369 mode: "markup", 370 alphabet.at("note"), 371 scope: ( 372 maze: maze, 373 arg: arg, 374 cmd: cmd, 375 baffle: baffle, 376 )) 377 } 378 ] 379 #v(0.5em) 380 ] 381 382 #for pair in alphabets.pairs() { 383 describe-alphabet(pair.at(0), pair.at(1)) 384 }