18: Markdown and Structure - transcript

Download the MP3

Liam: The QUILL Cast, episode eighteen. I'm Liam. Today we open the third arc of the show, documents and formats, with the single most load-bearing layer underneath everything else: Markdown and the structure it carries.

Jessica: I'm Jessica. Recap of episode seventeen, briefly, because the safety stack matters as we move into a new arc. Episode seventeen was the full protective net: autosave and crash recovery, backups on save, versions, snapshots, atomic writes, honest error reporting, and the crash report path. The point of that episode was courage. Once you trust the net, you stop saving out of fear, you stop restructuring timidly, and you start cutting versions, taking snapshots, and trying the wild reorganization. The safety stack is not infrastructure. It is the foundation that lets the next few episodes assume you are willing to break things. We will be breaking things today, in the form of a Heading Organizer demo, so the foundation matters.

Liam: And one quick arc transition. Part one was installation and orientation. Part two was the everyday editor arc, fourteen episodes of moving through text, finding, replacing, formatting, comparing, saving, never losing work. You are now fast, safe, fluent, and trusted with the net. Part three, starting today, is documents and formats. Markdown first, because everything in the formats arc builds on it. Then the text-supply toolkit, rich formatting, and the export family. Then OCR and document rescue. Then we hand off to speech, AI, vault, story studio, glow, and braille.


Jessica: Let's define the terms. Markdown is a plain-text convention. You write punctuation, and the punctuation means structure. One hash, space, then text, is a top-level heading. Two hashes, a second-level heading. Six hashes are the deepest. A line starting with a dash and a space is a bullet item. One dot and a space starts a numbered list. Asterisks around words mean italics. Double asterisks around words mean bold. Square brackets around the visible text, parentheses around the address, that is a link. Add an exclamation point at the front, and the link becomes an image, and the bracket text becomes the alt text. Greater-than starts a quote. Backticks wrap inline code, and triple backticks wrap a code block. A blank line separates paragraphs. That's the whole vocabulary, and it really is most of it.

Liam: And structure is the noun that lives underneath the punctuation. The parser is in quill core markdown sections, and the regex is one to six hashes, optional whitespace, then the title. The fences are respected: a line that looks like a heading but sits inside a triple-backtick code block is not a heading, and the parser walks the document line by line to skip those. For HTML, the equivalent parser matches an opening h-tag through h six, attributes, body, closing tag. Plain text gets a fallback: form-feed characters become section boundaries. Three surfaces, three parsers, all returning the same shape: a HeadingBlock with a level, a title, a start, an end, and a section span.

Jessica: Why should a screen-reader user care, and we are going to say this straight, because the answer is non-obvious if you have only ever used a visual word processor. In a visual word processor, a heading is a font size you cannot hear. You apply it through a dialog, the cursor doesn't change, the structural information is not stored in the text, and your screen reader has to guess at it from the formatting hints it can recover. In Markdown, a heading is two characters you type, hash space, and structure you can hear directly. Your screen reader reads hash hash budget and you know exactly where you are. The hash is a marker, the level is the count, the title is the rest of the line. Nothing is hidden. Nothing has to be inferred. Structure is the file.

Liam: And structure pays compound interest across the whole product. Episode seven's jump-by-heading navigation, the section-move commands, the Outline Navigator, the Heading Organizer, Story Studio's book binder, the Accessible Vault's note collection, the DAISY talking-book export, the GLOW accessibility audit, every single one of those features reads your hashes. The hash is the substrate. One habit, hash space before your titles, powers a dozen features. You write Markdown once, and twelve different surfaces consume it.


Jessica: Now QUILL's Markdown comforts, the things the editor adds so you are not alone with raw punctuation. The first comfort is recognition. QUILL sniffs the file extension, dot md, dot markdown, dot mdx for Markdown, dot html, dot htm, dot xhtml for HTML, and falls back to a content sniff when the extension is missing. Plain text gets the plain treatment. The detected kind drives the renderer, the parser, the navigation verbs, and the heading tools. The default is right ninety-eight percent of the time.

Liam: The second comfort is the outline. Open the Outline Navigator with control shift O. A dialog appears with a tree of every heading in the document, organized by level. Pick a heading, hit enter, the cursor lands on the heading line, the editor announces the heading title and the level, and the dialog closes. The tree is built by extract outline entries, which is the same fence-aware parser the navigation verbs use, so what you see in the navigator is what you will land on. For YAML documents the same dialog becomes YAML Navigator, with the same tree shape and the same jump gesture.

Jessica: The third comfort is heading navigation. Two commands, navigate dot next heading and navigate dot previous heading. They live in quill core navigation as next heading start and previous heading start, which take the text, the cursor, and the markup kind, and return the character offset of the next or previous heading line. The pattern is fence-aware, so a hash-prefix line inside a triple-backtick block is skipped. The current draft of the help topic for these commands does not list a default keystroke, which means the easiest way to reach them is the command palette, control shift P, type next heading, hit enter. The announcement on arrival is informative: it says moved to next heading, H two, three of seven, then the title. The level, the ordinal, the total, the title. Four pieces of information, all in one breath.

Liam: The fourth comfort is section-move. Two commands, format dot move section up and format dot move section down, the keymap defaults are alt shift up and alt shift down. Press one, the whole section your cursor is in, meaning the heading line and all the text until the next heading of equal or shallower level, swaps position with its nearest sibling. The function is in quill core markdown sections, the function is move section, and it returns four things: the new text, the new caret position, a result code, and a screen-reader-friendly announcement. The result codes are OK, NO_SECTION, TOP, BOTTOM, NO_SIBLING. The announcements are: the sibling's title if the move was OK, the literal string "Top!" if you were already at the top, the literal string "Bottom!" if you were already at the bottom, "No sibling to swap with" if there is no sibling, and "No section to move" if your cursor is not in a section. The result codes are tested in the unit suite, and the announcements are tested separately, so the wire and the speech stay in sync.


Liam: Do this now beat, and this is the one we will ask you to pause the audio for. Open a new document. Save it as something dot md. Type hash space My First Heading, enter, type a sentence. Enter twice. Type hash hash space My Second Heading, enter, type another sentence. Save the file. Resume the audio when the file is saved. The reason for the pause: every later step in this episode assumes a Markdown file is open, and we want the rest of the walkthrough to land on something real.

Jessica: Now the Markdown extensions QUILL supports. First, headings, which we just covered. Second, lists: any line starting with whitespace, a dash, an asterisk, or a plus, followed by a space, for bullets, and any line starting with whitespace, digits, a dot, and a space, for numbered. The toggle commands, format dot toggle bullet list and format dot toggle numbered list, have defaults at control alt seven and control alt eight. Third, code blocks, triple backticks on their own line, content, triple backticks on their own line. Fenced code blocks are respected by the heading parser, so a hash-prefix line inside a code block is code, not a heading.

Liam: Fourth, tables, GFM style. A header row of pipes, a separator row of dashes and pipes, data rows of pipes. The preview renders this as a real HTML table, with thead, tbody, th for the header, and td for the data. The Insert menu has Insert Table, which opens a small dialog for rows and columns. Fifth, block quotes, a greater-than sign at the start of the line. Sixth, task lists: a line with a dash, space, open bracket, space, close bracket, is an undone task. A line with a dash, space, open bracket, x, close bracket, is a done task. The preview renders the brackets as checkboxes. Seventh, footnotes: a reference is caret open square bracket identifier close bracket. Eighth, definition lists: a term, a colon, a space, then the definition. Ninth, strikethrough, double tildes. Tenth, horizontal rules, three or more hyphens, asterisks, or underscores on a line. Eleventh, hard line breaks, two trailing spaces at the end of a line.

Jessica: And the table of contents, the eleventh extension. The marker is literal square bracket TOC close bracket on its own line. The Insert menu has Insert Table of Contents, which finds the marker and replaces it with a generated bullet list of links to every heading, anchored by the slugified title. The slugify function lowercases the title, replaces every non-alphanumeric run with a single hyphen, strips leading and trailing hyphens, and falls back to the word "section" if the result is empty. The same slugifier is used by the browser preview renderer, so a TOC link in the preview lands on the correct heading id. If there is no TOC marker, the Insert Table of Contents command inserts one after the first heading. If there are no headings at all, the command is a no-op and the status line says so.


Liam: The Markdown profiles, in one breath, because the catalog is in quill core markdown profiles and the description function is screen-reader friendly. There are eight built-in profiles. Standard Markdown, no extensions, the maximum-compatibility default. GitHub-Style Markdown, tables, fenced code, task lists, strikethrough. Documentation, table of contents, tables, fenced code, footnotes, definition lists. Poetry and Lyrics, just the preserve-single-line-breaks extension. Accessible Publishing, table of contents, tables, footnotes, definition lists. Technical Writing, tables, fenced code, footnotes, definition lists. PRD and Release Notes, table of contents, tables, task lists, footnotes, strikethrough. And Custom, which is your own combination. The description function says the profile name, the count of enabled extensions, and the profile's purpose in plain English. So the screen-reader announcement is "Markdown profile: Documentation. Five extensions enabled. Supports manuals, guides, help files, and structured documentation." That is the friendly-form version of the catalog.

Jessica: The check heading structure function is the lightweight audit you can run on any document, and it is also the right thing to call before you publish. The function returns a list of diagnostics. Two warnings the function flags: the first heading is not a level one, and the level skipped from N to N plus two or more. Both warnings are grounded in the screen-reader guidance from the Web Content Accessibility Guidelines, which says headings should start at level one and should not skip levels, because skipping confuses the heading-level navigation that screen readers expose. The function is pure, it does not modify the document, and it is in quill core markdown extensions.

Liam: Now the structure commands, the verbs that let you reshape a document without hand-editing. Command one: Move Section Up and Move Section Down, alt shift up and alt shift down. Command two: Increase Heading Level and Decrease Heading Level, alt shift right and alt shift left. Command three: the Heading Organizer, control shift backtick O, which opens a dialog with a list of every heading in the document, a preview pane, and the ability to reorder, rename, and re-level. The Heading Organizer is the right tool for the structural pass: it shows you the whole outline at once, lets you grab a heading and drop it in a new place, and re-emits the document with the new order preserved. The rewrite preserves the inter-section whitespace, so a document that used blank lines between sections still has blank lines after reorganization. The parsing is fence-aware, the rewrite is idempotent, and the result is one undo step.

Jessica: Command four: Style Headings, which opens a dialog for font family, font size in points, and text alignment, and applies the choices to either the current heading level or all levels. The style lives in quill core heading styles, and the function is apply heading style. For Markdown documents, the function emits a literal HTML span with the style attributes around the heading title, which the preview renderer respects and the HTML export honors. For HTML documents, the function merges the style attributes into the existing h tag. The function returns the count of headings it actually changed, so the status line tells you how many headings were styled.

Liam: Command five: the AutoOutline, which is a separate verb, not a heading style. AutoOutline inserts literal-text numbering into your headings, by nesting level. Numeric style is one, one dot one, one dot one dot one. Legal style is Roman, capital letter, then digit. The numbers ride in the heading title itself, which means they survive copy, paste, export, and any screen reader that does not depend on the visual rendering. The function is in quill core auto outline, the entry points are apply auto outline and remove outline numbers, and both are idempotent, meaning running them again after adding or removing a heading refreshes the numbers rather than stacking them.


Jessica: Now the live preview, which is the part of the experience you should see for yourself. There are three preview surfaces in QUILL, and the design reason for three is that they are optimized for different reading postures. The in-app modal preview, view dot preview, default key control shift V, opens the rendered HTML in a WebView dialog. It is the right choice for a deep read of a single document, because the dialog is its own window with a title bar, you can resize it, you can scroll it, and you can close it without leaving the editor. The in-app side preview, view dot split preview, default key control shift backslash, opens the rendered HTML in a WebView to the right of the editor, and the preview updates as you type, debounced by a 250-millisecond timer. The external browser preview, view dot browser preview, default key control shift backtick V, writes the rendered HTML to a temp file and opens it in your default browser, where you get a full-page experience with proper scrollbars, find-in-page, and print.

Liam: Focus into the preview with view dot focus preview, default key control F6. The editor is a text field, so screen readers cannot use browse-mode single-letter navigation there. The preview is a real web document, so once focus lands in it, NVDA switches to browse mode and H, one through six, and the other browse-mode letters all work natively. The status line says "Moved to preview. Press Escape or F6 to return to the editor." Escape closes the focus in the WebView and brings you back. F6 cycles regions, so you can also F6 from the editor into the preview, the preview into the status bar, and back.

Jessica: The side preview auto-shows for Markdown and HTML documents if the setting auto side preview is on, which it is by default. The auto-show is silent, so a new document opens with the preview already on the right, no flicker, no announcement spam. The WebView2 runtime warm-up is deferred, which is a craft detail: WebView2's first New call blocks the UI thread for several seconds the very first time, so QUILL creates a one-by-one hidden sentinel WebView during deferred startup and then destroys it, paying the cost before the user ever presses F6. The visible result: the first F6 is near-instant, the editor stays responsive, and there is no startup stall.

Liam: The preview itself is built by render preview body in quill core browser preview. The function takes the text, the markup kind, and a dark flag, and returns an HTML body fragment. For Markdown, the renderer is a hand-rolled CommonMark-style line walker: it handles paragraphs, headings with slugified id attributes, ordered and unordered lists, GFM tables, fenced code blocks, thematic breaks, block quotes, the hidden-codes divs, and the page-break marker. For HTML, the renderer returns the source HTML if it already starts with a tag, and wraps a plain string in a pre tag if it does not. The dark mode flag injects a style block at the top of the body, recoloring the background, text, links, code, and blockquotes for the WebView's own background, so the half-dark half-bright split view never happens.


Jessica: A code-verified walkthrough, because we said we would verify. Open the file you created during the do-this-now beat, or any Markdown file. Press control shift O. The Outline Navigator opens with your two headings. Pick the second, hit enter, the cursor lands on "My Second Heading." The status line says "Moved to heading: My Second Heading." Press the command palette, control shift P, type next heading, hit enter. The cursor moves to the next heading, which in a two-heading file means wrapping to the top, and the status line says "Moved to next heading, H one, one of two: My First Heading." Press the previous-heading version, and the cursor moves back. The ordinal, the level, the total, and the title. That is what next heading start and previous heading start computed, and the heading context at function built the announcement.

Liam: Now try section-move. Place the cursor on the second heading line, press alt shift up. The whole section, which is the heading plus its body, swaps with the first section. The document is rewritten, the cursor lands at the same offset within the section, and the status line announces the sibling's title, in this case "My First Heading." Press alt shift up again, and the section is already at the top, so the status line says "Top!" Press alt shift down to move it back, and the status line announces the sibling's title again. Move the cursor into the body of the second section, run the same command, and the same section-move happens, because the section is the heading plus everything until the next heading of equal or shallower level.

Jessica: Now the Heading Organizer. Press control shift backtick O. The dialog opens, lists both headings, and shows a preview of the first heading's section in the preview pane. Use the up and down buttons to reorder, or grab a row and drag it. Change a heading's level using the level spinner. Edit a heading's title in the edit field. The preview updates on every change. When you click OK, the document is rewritten: the sections are in the new order, the level and title are the new values, and the inter-section blank lines are preserved. The rewrite is one undo step, so control Z brings you back to the original.

Liam: Now the preview. Press control shift V. The preview dialog opens, showing the rendered HTML. Headings are h1 and h2 with the slugified id attributes, paragraphs are wrapped in p tags, the dialog title includes the file name. Close the dialog with Escape. Now press control shift backslash to open the side preview. The editor and the preview are side by side, the sash is at half the window width. Type a new line in the editor, watch the preview update after a 250-millisecond pause. The dark mode toggle, if you have it on, applies the dark style block to the preview body. The split is the recommended preview for a screen reader user, because the editor stays focused, no dialog to manage, and the preview updates in place.


Jessica: Honest corrections, because the brief asked us to call out drift. Correction one: the original draft of this script's ending said the next episode is rich formatting. That is wrong. The actual next episode, episode nineteen, is the text-supply toolkit, the Copy Tray, snippets, abbreviations, and macros. Rich formatting is episode twenty-one, two episodes from now. Correction two: the help topic for "Next Heading" and "Previous Heading" does not list a default keystroke, which means the command palette is the only default route. The status line and the menu are also there, but there is no control-alt-down default, despite what some out-of-date material may say. Correction three: the preview dark mode is a separate setting, not automatic, and it does not follow the system theme in this version. If you want dark preview, turn it on in Settings.

Liam: Correction four: the Markdown profiles are a catalog, not a runtime transformation. Picking a profile changes the named extension set you can see and select, it does not change the renderer. The renderer is CommonMark-style and always on. The profile is the friendly name for the surface; the actual render is in quill core browser preview. Correction five: the AutoOutline, despite living in quill core auto outline, is not in the default menu in the current build. It is callable from the command palette and from a Quillin command, but if you do not see it on a menu, the menu layer has not yet been wired. We are calling that out because it is exactly the kind of drift between the code and the menu surface that we want to be honest about. When the menu wiring lands, this correction becomes obsolete and we will note that in a future episode.

Jessica: A do this now beat for the second half. Press control shift O. Pick a heading. Press enter. Press control shift P, type next heading, hit enter. Press it again. Notice the wrap to the top, and notice the announcement. Now open the Heading Organizer, reorder one heading, click OK, and undo with control Z. Notice the structural rewrite as one undo step, not twenty. Now press control shift backslash, type a new line, watch the preview update. Resume when you have done at least one of each. The reason that beat matters: the rest of this episode assumes you have used the navigation verbs and seen the preview. If we kept talking about structure without asking you to use it, we would be describing a feature instead of teaching it.


Liam: The strategy, in one breath. Write in Markdown, the most accessible format ever devised. Use the Heading Organizer to keep the structure clean. Use the live preview to verify the structure reads the way you intend. Export to whatever the recipient needs, Word with real heading styles, HTML, EPUB, PDF, the choice is theirs, the source is yours. One habit, hash space before your titles, powers the navigation, the preview, the export, the audit, the binder, the talking book. Compound interest is the whole point. The most common GLOW finding on a Markdown document is "heading level skipped from one to three," which is the habit we are building right now to avoid. We will go deeper in the GLOW episode. Today, the habit is: start at one, do not skip, write something the audit will approve.


Liam: Homework, four steps, designed to be done in twenty minutes with QUILL open. One: open a new document, save it as something dot md, write a real document with at least three headings, a list with three items, a bolded word, a link, and an image with a thoughtful alt text. Save the file. Two: open the Outline Navigator, walk the structure, and then use the command palette to call next heading and previous heading. Notice the announcement shape: level, ordinal, total, title. Three: open the Heading Organizer, reorder one heading, change one level, click OK, and undo with control Z. Notice the rewrite as one undo step. Four: open the side preview, control shift backslash, type a new line, watch the preview update. Listen to the screen reader on the preview, the in-app preview is a real web document, and H, one through six, all work.

Jessica: The setup for next episode. Next episode, episode nineteen, is the text-supply toolkit, the Copy Tray, snippets, abbreviations, and macros, the four tools that bring words to your cursor instead of making you fetch them. We are taking a one-episode detour from the formats arc for this one, because these tools make every remaining episode's homework faster, and the brief for episode eighteen specifically asked for the setup. Consider it equipment before the expedition. The Copy Tray is twelve slots of clipboard, snippets are templates with placeholders, abbreviations are automatic expansions, and macros are recorded action sequences. Four tools, one shared philosophy: your attention is the scarce resource, and every one of these converts a repeated, attention-eating sequence into a single intention. That is the setup.

Liam: And a frame for the rest of the formats arc. Episode twenty is the snippet gallery and prompts, episode twenty-one is rich formatting and the hidden-codes system, episode twenty-two is the export family. We will go from structure today, through the text-supply toolkit, through rich formatting, to export. The arc is three to four episodes long, and the destination is a document you can hand to anyone, in any format, with the structure preserved.

Jessica: That's the close. I'm Jessica.

Liam: I'm Liam. Hash space, and away you go.

Jessica: Fifty-four episodes total, thirty-six to go, and one habit that powers a dozen features.

Back to all episodes