Components / Data display
Markdown view
Renders markdown text as native content: headings, lists, tables, code, task lists and links.
Functions: cui_markdown, cui_markdown_apply_task

Usage
static char notes[4096] = "## Release 2.4\n\nRun `tern migrate` once. See the [upgrade guide](https://tern.dev/docs/upgrade).\n\n- [ ] Tag the release\n";
static char status[160];
cui_markdown_result r = cui_markdown(ui, "notes", notes);
if (r.link) snprintf(status, sizeof status, "Opened: %s", r.link); /* open it yourself, if you want to */
cui_markdown_apply_task(notes, &r); /* a task box was clicked: write it into the text */
/* an AI reply that grows every frame */
cui_markdown(ui, "reply", reply, .length = reply_len, .streaming = !reply_done);
Renders your markdown text as native content every frame, straight from your buffer: the view keeps no copy and allocates only from the frame arena. Supported: ATX headings (# to ######) and setext headings, paragraphs (a single newline is a space; two trailing spaces, a trailing backslash or <br> break the line), **bold**, *italic*, ~~strikethrough~~, ` inline code , links and <https://...> autolinks, bare https:// and www. links, images (shown as a link to the image), backslash escapes and the common entities, bullet and ordered lists nested by indentation, task items (- [ ], - [x]), block quotes, fenced code blocks with a language (drawn by the code block above, with its highlighter and copy button), indented code, horizontal rules and pipe tables with an alignment row (drawn with the table's styling). Everything else, such as HTML, reference links and footnotes, shows as plain text, and nothing a stray marker or a broken byte can do crashes the view. Headings use the theme's heading sizes, links the accent, code the code font, and italics are slanted from the regular face. .compact uses smaller headings and tighter spacing, and .color` sets the text color for a colored surface (links then share it, underlined).
The view wraps to whatever width it gets: words wrap between fonts, a word wider than the line breaks between characters, code blocks scroll sideways and tables let long cells wrap before they scroll sideways. With .streaming set, an unclosed **, *, ~~, backtick or link at the very end renders as if it were closed, and markers that may still grow wait for the next characters, so a reply never flickers between styles as it arrives; an unclosed code fence runs to the end of the text. cui_chat_message renders its text this way with .markdown = true, sized to the content, and reports what was clicked through .markdown_result.
Links are never opened for you. The result reports the URL of a link clicked or activated this frame (and the one under the pointer), and a link's URL also shows as its tooltip. A task box reports the byte offset of the character between its brackets, its line and the state it was toggled to; cui_markdown_apply_task writes that into your text, or you can edit it yourself. Tab moves through the links and task boxes; Enter opens the focused link and Space toggles a box. Text is not selectable, since CUI text has no selection yet; code blocks have their copy button.
Keyboard
- Tab moves through the links and task boxes; Enter opens the focused link and Space toggles a box.
Options of cui_markdown
| Option | Type | Default | Description |
|---|---|---|---|
.width | cui_size | fill | default fill |
.fit_content | bool | as wide as the content needs, up to the space there is (chat bubbles) | |
.length | int | strlen (text then need not end in a NUL) | bytes of text to show, default strlen (text then need not end in a NUL) |
.size | float | theme text_body | body text size, default theme text_body |
.color | cui_color | foreground | body text color, default foreground; links then take it too, underlined |
.streaming | bool | text is still arriving | |
.compact | bool | smaller headings and tighter spacing, for chat bubbles |
cui_markdown_result
| Field | Type | Description |
|---|---|---|
link | const char * | URL of the link clicked (or activated with Enter) this frame, or NULL; frame memory |
hovered_link | const char * | URL of the link under the pointer, or NULL; frame memory |
task_offset | int | a task box was toggled: byte offset in text of the character between its brackets, else -1 |
task_line | int | that item's 1 based line in text, 0 when none |
task_checked | bool | the state the box was toggled to |
height | float | height of the view in the latest layout |
Related
- Code block: Read-only source code with syntax highlighting, optional line numbers and a copy button.
- Chat: Message bubbles for a conversation, with grouping, dividers and a typing indicator.
- Code editor: An editable code field with syntax highlighting, for shader editors, script consoles and config files.
- JSON viewer: JSON shown as a tree that folds, with a color for each type of value.