21: Read Aloud and Voices - transcript

Download the MP3

Jessica: The QUILL Cast, episode twenty-one. I'm Jessica. Today: the hidden codes, the screen-reader-first answer to the oldest problem in word processing, how do you dress up a document without your editor turning into symbol soup, and without the formatting being invisible to the writer who cannot see the screen.

Liam: I'm Liam. The problem first, because it explains every design decision that follows. Visual word processors paint the formatting on the page, useful if you can see. Markup word processors write the formatting into the text, useful for a screen reader, until every sentence is bracketed with stars, underscores, and color tags, and your reader is dutifully reading the syntax along with the words. The standard joke is that a Markdown document is a beautiful document that happens to be unreadable to a screen reader, and a Word document is an unreadable document that happens to look beautiful. The history of accessible word processing is a history of choosing which flavor of unreadable you can live with.

Jessica: QUILL refuses both. The buffer you edit is clean plain text, exactly what a screen reader was designed to read, exactly what every other feature in QUILL was designed to operate on. The formatting you apply, bold, italic, underline, strikethrough, superscript, subscript, font family, point size, text color, highlight, plus paragraph alignment, line spacing, space before, space after, left indent, first-line indent, and named styles like Quote, Title, Subtitle, Caption, all ride along as invisible codes. You set them from the Format menu or the Font dialog, and your editor buffer stays completely clean. The display is rich, the buffer is plain, the two are decoupled by design.


Liam: For the full menu of fonts, sizes, colors, and highlights, the Format menu holds preset submenus, and we verified the exact list against the code in quill-ui-main-frame-format-codes, the file that owns the rich-text hidden-codes commands. Font family has six presets, Arial, Calibri, Times New Roman, Courier New, Verdana, Georgia. Font size has twelve presets, eight, nine, ten, eleven, twelve, fourteen, sixteen, eighteen, twenty-four, thirty-six, forty-eight, seventy-two points. Text color has six, Black, Red, Green, Blue, Orange, Purple, stored as hex values behind friendly names, so the menu reads naturally and the storage reads as data. Highlight has five, Yellow, Green, Turquoise, Pink, Gray. And there is a More Font Options entry that opens the accessible Font dialog for arbitrary combinations, useful when the preset list does not cover what you need.

Jessica: Bold and italic and the rest of the standard toggles, strikethrough, superscript, subscript, also live in the Format menu, and so does Clear Formatting when you want to start over from a known state. Paragraph-level choices live in the same menu. Alignment is a submenu with four entries, Left, Center, Right, Justify. Line Spacing is a submenu with three entries, Single, one-and-a-half lines, Double. Paragraph Spacing is a submenu with four entries, six or twelve points of space before, six or twelve points of space after. Paragraph Indent is a submenu with five entries, left indent at eighteen, thirty-six, fifty-four points, and first-line indent at eighteen or thirty-six points. Paragraph Style is a submenu with four entries, Quote, Title, Subtitle, Caption. And then Insert Page Break. Everything is one submenu away from the caret, every entry is keyboard-navigable, the dialog contract in quill-ui-dialog-contract ensures the labels land in a screen-reader-readable order.

Liam: Now the part that matters most for listeners who work by ear, the interrogation hotkey. Describe Formatting at Cursor, the binding is Ctrl plus Shift plus D by default, reads the formatting in effect at your caret and speaks it. Move your caret to a formatted paragraph, press the chord, and you hear exactly what is in effect at that position, "Arial, fourteen point, centered, bold," or whatever the truth is. We verified the function in quill-ui-main-frame-format-codes, the method is called describe-formatting-at-cursor, and the spoken phrase is built by quill-core-format-speech, the function is describe-inline-format. The phrase is empty when nothing is in effect, and the method translates that to "plain text, no formatting" so you always get an answer, and the answer is always honest about being empty when it is.

Jessica: And there is a second mode, optional, off by default. Announce Formatting on Cursor Move is a checkable Format menu item. When you turn it on, QUILL announces the delta in formatting as you arrow through the document. Entering a bold run says "bold." Leaving it says "plain." Identical contexts say nothing. The implementation is the function describe-format-transition in quill-core-format-speech, and it lives in the setting announce-formatting-on-move, persisted with your other settings. The whole point of the optional mode is that navigation stays terse, no chatter, just the transitions, so you can arrow through a document and hear only the places where the formatting changes, not the formatting itself every time you move a single character.

Liam: For the full X-ray there is Reveal Codes, the loving tribute to WordPerfect, and if you know, you know. The binding is Alt plus F3, which was the original WordPerfect chord and is now the QUILL chord, and we confirmed that the previous occupant of Alt plus F3, edit-find-all-matches, moved to Ctrl plus Shift plus F3 to make room. There is also a View menu item, Reveal Codes, which is a checkable toggle. When you turn it on, a pane appears below the editor, above the status bar, and it shows every code in the document, with your caret highlight synchronized to where your caret is in the editor. We verified the layout in quill-ui-main-frame-reveal-codes, the toggle is the method toggle-reveal-codes, and the pane widget is in quill-ui-reveal-codes-pane. The pure model is in quill-core-reveal-codes, the file we have open, the screen-reader-first revival of the WordPerfect feature.

Jessica: Reveal Codes has two presentations you can flip between. The default is Structured, a read-only list where every code and every run of text is a separate, named list item, the most accessible shape, because a screen reader can land on any item and announce it in isolation. The other is Flowed, a read-only text view that renders the codes inline within the text, the classic visual layout, and the shape a braille display prefers because the bracketed codes are right there in the buffer. The setting is reveal-codes-view. There is also a verbosity setting, reveal-codes-verbosity, with three values, quiet, balanced, detailed, that gate how much each move announces. The pane is a first-class focus region, so F6 cycles editor, Reveal Codes, status bar, and Shift plus F6 cycles back. No new chord to learn, the standard focus cycle does the work.

Liam: Here is a do-this-now beat, and I mean literally now. Pause the audio. Open QUILL, type a few words in a fresh document, and do three things. One, select one of the words and apply a font and a color from the Format menu, maybe Arial, fourteen point, Blue. Two, select a different word and apply a paragraph alignment, say Center. Three, with your caret inside the first word, press Ctrl plus Shift plus D, and hear what the Describe Formatting hotkey says. Then move your caret into the centered paragraph and press it again, and hear the difference. Come back when you have heard both announcements. That single exercise will make the rest of today's episode make sense, because the announcements you heard are the same words the Reveal Codes pane will speak, and the same words the exporters will materialize.

Jessica: The reason that exercise matters is the separation it demonstrates. The buffer you see in the editor is plain text. There are no star markers, no curly-brace tags, no square-bracket codes polluting the read-out. The formatting lives elsewhere, as a structured overlay on the canonical markup, and the only way you find out what it is, is by asking, or by reading the codes in Reveal Codes, or by exporting. That is the engineering decision. The editor stays clean so every other feature, search, spell check, the AI actions, undo, all operate on the same clean text the editor already understands. Formatting never gets in the way of anything else, because the text the features see is pure. The other features do not know hidden codes exist, and that is the gift.


Liam: Now the part the show notes asked us to verify carefully, and we did, the storage model. The hidden codes are not annotations on the buffer. They are not a sidecar database. They are carried inside the canonical QUILL markup itself, in two small grammatical additions that are invisible to the eye and silent to a screen reader but fully materializable at export. We confirmed this in quill-io-rtf-model, the canonical home of the rich surface's pure model, the same file the rich lens reads and writes through.

Jessica: Run-level formatting, font family, point size, color, highlight, underline, strikethrough, superscript, subscript, is carried as a Pandoc attribute span, square-bracket text, curly-brace key equals value pairs. The grammar is exactly the existing link grammar extended with named attributes, so it round-trips through the existing parser without a new code path. A paragraph formatted in Arial, fourteen point, blue, is stored as a span with the attributes font-family Arial, font-size fourteen, color blue. None of those characters show up in the editor. They live in the markup, they ride along on save, and they are realized as real native formatting when you export to Word, RTF, or HTML. The run attributes that go in the span are the same ones describe-inline-format knows how to speak, so the storage and the spoken vocabulary share a single shape.

Liam: Paragraph-level formatting, alignment, line spacing, space before, space after, left indent, first-line indent, and named style, is carried as a Pandoc fenced div, three-colon space, curly-brace key equals value pairs, the paragraph text, three colons. A centered paragraph is wrapped in a div with align equals center. A double-spaced paragraph is wrapped in a div with line-spacing equals two. A page break is its own marker line, three colons, the word pagebreak, three colons. Again, none of these markers show up in the editor, none of them are read by a screen reader, none of them count as visible characters. The format at markdown offset function in quill-io-rtf-model is what Describe Formatting calls to answer the question, and the same parser is what Reveal Codes uses to build its token stream. One model, two consumers, no drift.


Jessica: For the full X-ray let us walk through the token model in quill-core-reveal-codes, because it is genuinely worth your time. The model defines a stream of tokens. Every token has a kind, a label, a spoken phrase, both a markup offset range and a visible offset range, and optionally a pair index linking an opening code to its closing partner. The six token kinds are TEXT, a run of visible characters. FORMAT_ON, an inline code that turns something on, like Bold On or Font Arial. FORMAT_OFF, the matching close, like Bold Off. BLOCK, a paragraph-level code at the line head, like Heading Two or Center or Style Quote. STRUCTURE, like Tab or Hard Return or Page Break. INVISIBLE, like No-Break Space or Smart Quote or Em Dash. The token kinds are an enum, TokenKind, and every formatter and pane reads from it, so the vocabulary is consistent end to end.

Liam: The inline attributes that produce paired ON and OFF codes are kept in a stable display order. We read the constant in the source, it is a tuple of six flags followed by five valued attributes, and the order is load-bearing. The flags, in order, are bold, italic, underline, strike, superscript, subscript. The valued attributes, in order, are href, font-family, font-size-pt, color, highlight. That order matters, because the tokens emit in that order, so a reader who lands on a region hears the codes in the same sequence every time, and that sequence is the most-defining-attributes-first, then the inline weight and decoration, then the layout and color. The pair index lets the pane say "bold on, twelve characters" when the verbosity is balanced, or just "bold" when it is quiet. The reach, the twelve characters, is computed by pair-distance, the visible-character distance from the ON code to its matching OFF.

Jessica: Special characters get their own token rather than hiding inside a text run. We verified the dictionary in the source, it has fourteen entries, and they are the characters screen-reader users most often miss in plain buffers. Tab, No-Break Space, Narrow No-Break Space, Zero-Width Space, Zero-Width Non-Joiner, Zero-Width Joiner, the four smart quotes, En Dash, Em Dash, Ellipsis. Each one has a label and a spoken phrase. Tab says "tab." The smart quotes say "left smart quote," "right smart quote," and so on. Detailed verbosity adds the Unicode name and the code point, so a developer who is hunting a stray zero-width space can hear the exact glyph identified as ZERO WIDTH SPACE, U plus two-zero-B, and know what to grep for.

Liam: Now the on-caret-move behaviour we mentioned, because the engineering is worth understanding. Reveal Codes does not rebuild its token stream on every keystroke. The idle handler in quill-ui-main-frame-reveal-codes, the method is reveal-on-idle, samples a small state tuple, the editor id, the document length, the caret offset, and only rebuilds when one of those changes. If only the caret moved, the token stream is still valid, and the pane just re-syncs its highlight to the new caret position. That is cheap. The pane also skips re-syncing while the pane itself has focus, so when you are navigating inside the pane with arrow keys, the editor caret does not fight you. That is a small but real accessibility win, the same as how a sighted user expects the mouse to stay parked while the keyboard owns the focus.

Jessica: In the pane, the navigation commands are reveal-next-code, reveal-previous-code, and reveal-go-to-pair. They are remappable, registered as commands in the command registry, and they have friendly titles in the keymap, "Reveal Codes: Next Code," and so on. The structured list announces each item as you land, the flowed view renders the bracketed codes inline. Either way, the codes you see are the same codes the editor knows about, the codes Describe Formatting will speak, and the codes the exporters will materialize. There is exactly one source of truth for hidden formatting, and that is the canonical markup, and Reveal Codes is a view onto it, not a separate database. Close the pane, reopen it, the same codes are still there because they were always there, in the buffer you could not see them in.


Liam: Now the persistence question, which is where the third major code path lives. You can save a document with hidden codes in four ways, and the result is different in each case. Save as Markdown, the file is plain text, the hidden codes are carried as Pandoc attribute spans and fenced divs, and any Markdown tool that understands the grammar will see the formatting. Save as Word, or RTF, or HTML, and the codes materialize into real native formatting, your Arial fourteen blue becomes Word Arial fourteen blue, next episode's territory, but worth knowing that the round trip is lossless for the attributes we set. The same source of truth, four different materializations, none of them lossy in the way that matters.

Jessica: Save as plain text, the third case, is the clever one. A dot-t-x-t file cannot physically hold font or color, so QUILL gives you a choice, and the choice is a setting called plain-text-with-formatting, with three values. Ask, the default, offers you a three-way choice every time you save a formatted document as text. Illuminate, always writes a sidecar. Plain, always saves plain and drops formatting. The sidecar is the Illumination, named for the decorative layer scribes added over manuscript text in the Middle Ages, and it lives at your-file-dot-t-x-t-dot-illumination, next to the text file. The model is in quill-io-illumination, the function is build-illumination on save and restore-markup on open. Open the text file anywhere else, and it is perfect ordinary text. Open it in QUILL with the sidecar present, and every font, color, and alignment comes back.

Liam: The drift safety is handled honestly. The Illumination carries a sha-256 hash of the clean text it was built from. On open, QUILL hashes the current text. If the hashes match, the Illumination is reapplied, and the document is fully formatted again. If the hashes do not match, meaning another program edited the text file, the Illumination is treated as stale and the file opens as plain text, because applying a font map to text the font map was not built for would be worse than dropping the formatting. The user is told in the status bar, "Opened file, formatting restored from its Illumination," when the round trip is clean. We verified the code path in quill-ui-main-frame, the method is maybe-apply-illumination, and the staleness check is in restore-markup. The behavior is correct, and it is the behavior the design called for, and the behavior is the only one that respects the text.


Jessica: One honest correction, because we promised to be honest. The original brief for this episode referenced quill-core-illuminations and quill-core-format-codes as potential homes for the implementation. Neither file exists. The real model lives in quill-core-reveal-codes for the code-stream model, in quill-core-format-speech for the spoken vocabulary, in quill-core-autoformat for the typography helpers like smart quotes and the em-dash merge, and in quill-io-illumination for the sidecar. The UI surface is in quill-ui-main-frame-format-codes for the menu wiring and the interrogation hotkey, in quill-ui-main-frame-reveal-codes for the Reveal Codes pane toggle and the idle sync, and in quill-ui-reveal-codes-pane for the pane widget itself. If you ever go to read the code, those are the doors to open. The brief was aspirational, the reality is split across the right files for the right reasons, and the split keeps each file small enough to fit the module size budget.

Liam: And one more honest note. The autoformat settings, the ones that turn a typed quote into a smart quote and a typed double hyphen into an em-dash, are off by default. We confirmed this in quill-core-settings, the fields are autoformat-smart-quotes and autoformat-dashes, both default to False. That is a deliberate choice. Hidden formatting is meant to be something you set, and the autoformat helpers are a typing convenience, not a hidden code. If you want the smart quotes and the em-dash, turn the settings on, and the helper functions in quill-core-autoformat, smart-quote-for and is-dash-merge, will do the work as you type. The decision to default them off means a clean QUILL install is a clean buffer, all the way down, and the only formatting in your file is the formatting you chose to put there.


Jessica: Who is this whole system for? The honest answer, in three groups. The first group is anyone submitting formatted work, resumes, manuscripts, school papers, while working by ear. You can now state with confidence, "my document is Times New Roman, twelve point, double spaced, headings centered and bold," because you set it, and you verified it with Describe Formatting, not because a sighted friend checked. The second group is anyone who has been burned by a word processor that drops formatting on save. The Illumination makes that problem go away for plain-text workflows, and the Word and RTF export make it go away for rich-text workflows. The third group is anyone who has wanted the WordPerfect Reveal Codes screen back. It is back, screen-reader-first, with structured and flowed views, and a verbosity you can tune, and the activation chord is the same one WordPerfect used thirty years ago.

Liam: The design lesson is worth saying out loud. The hidden codes are not a hack around the limits of plain text. They are an honest storage model that keeps the editor buffer readable, keeps every other feature working, and lets the user interrogate the formatting on demand. Three views onto the same source of truth. The clean editor view, the spoken view via Describe Formatting and Announce on Move, the explicit view via Reveal Codes. The exporters materialize the same source of truth into Word, RTF, HTML, or carry it forward as a Markdown attribute grammar. The Illumination sidecar carries it forward into plain text. One model, every direction, and the writer at the keyboard is the one who decides which view they want to look at, and which view they want to hear.


Jessica: Homework, four steps. One: take the formatted paragraph from the do-this-now beat. Press Ctrl plus Shift plus D inside the Arial fourteen blue word, and hear the announcement. Then press it in a different paragraph that is unformatted, and hear the difference. Two: open Reveal Codes with Alt plus F3, and just watch it track your caret for a minute. Move through the formatted and unformatted paragraphs and hear the codes announce as you cross them. Three: turn on Announce Formatting on Cursor Move, and arrow through your document, listening to the deltas. Then turn it back off if the chatter is too much, the setting is yours, and the right answer is the one that helps you write. Four: save your formatted document as plain text, accept the Illumination, close the file, reopen the text file, and confirm the formatting survived. Then peek at the folder and meet the sidecar, the little companion that keeps the manuscript's decoration safe.

Liam: And if you have time, a bonus step. Toggle between the Structured and Flowed views in Reveal Codes, and decide which one you prefer. The Structured view is the most accessible, every code is its own named list item, and a screen reader can read it as a list. The Flowed view is the most familiar to anyone who used WordPerfect, the codes sit inline, and a braille display reads it as flowing text. The setting reveal-codes-view remembers your choice, and so does the verbosity, reveal-codes-verbosity. There is no wrong answer. There is only the answer that makes the codes useful to you, in the way that helps you write better.


Jessica: Next episode, episode twenty-two, the format gateway. Word, EPUB, PDF, and friends, the in and out with fidelity. Where the hidden codes we talked about today materialize into native Word formatting, where EPUB chapters get the chapter-aware navigator, where PDFs get the honest extraction-quality conversation. The designers of next week's episode have earned the easy job, because the hard design work happened this week, and the source of truth is in place.

Liam: And we are a little past the halfway mark. The QUILL Cast is a fifty-four episode course, and we are standing in the deep middle, where the systems are real, the trade-offs are honest, and the engineering is something you can rely on. Episode twenty-two next, and then the audio side returns for the speech and dictation arc, and then the AI hub, and then the vault, and then GLOW, and then braille, and then Quillins, and then the finale. The road is long, the road is good, and we will walk it together.

Jessica: I'm Jessica.

Liam: I'm Liam. Dress your words, quietly, and let the editor stay clean.

Back to all episodes