Recipe: Markdown Note Editor

TWrite includes a built-in Markdown battery that provides live syntax highlighting, concealed markup tokens, interactive task lists, and clickable hyperlinks.

1. Enable the markdown Feature

In your Cargo.toml, enable the optional markdown feature flag:

[dependencies]
twrite = { version = "0.9", features = ["markdown"] }

2. One-Line Initialization

Call ed.enable_markdown() when creating the editor:

#![allow(unused)]
fn main() {
use gpui::*;
use twrite::Editor;

let editor = cx.new(|cx| {
    let mut ed = Editor::new("# Welcome to TWrite\n\n- [ ] Task item\n", cx);

    // Activates markdown syntax highlighting, task lists, and shortcuts:
    ed.enable_markdown();

    ed.config.line_numbers = true;
    ed
});
}

3. Features Included Out of the Box

When ed.enable_markdown() is called, the following behaviors are activated automatically:

Interactive Task Checkboxes

Lines starting with - [ ] or - [x] (or numbered 1. [ ]) render as clickable checkboxes. Single-clicking the checkbox with the mouse toggles its state between checked and unchecked without having to type.

Inline links formatted as [Link text](https://example.com) are detected. Single-clicking a link opens the URL in the system default browser. Double-clicking still selects the text for editing.

Smart List Continuation

Pressing Enter at the end of a list item automatically inserts the next bullet point (- or * ) or increments ordered list numbers (1. , 2. ). Pressing Enter on an empty list bullet removes the prefix.

Formatting Shortcuts

Selecting text and pressing formatting keys wraps the selection automatically:

  • Ctrl+B: Toggles bold (**text**)
  • Ctrl+I: Toggles italic (*text*)
  • Backtick (`): Wraps selection in code span (`text`)
  • Auto-pairs: Typing (, [, {, ", ' wraps the selection or inserts matching pairs.

4. Conceal Modes

TWrite supports three conceal levels for Markdown syntax markers (such as **, #, and backticks):

ModeBehavior
ConcealMode::Dimmed (default)Syntax markers are drawn in a subtle, dimmed color to keep content legible while keeping raw characters visible.
ConcealMode::HiddenSyntax markers are hidden completely. When the cursor enters a line, markers un-conceal so you can edit them directly.
ConcealMode::OffAll characters are rendered at full normal opacity.

To change or toggle conceal mode dynamically:

#![allow(unused)]
fn main() {
use twrite::markdown::{ConcealMode, MarkdownHighlighter};

ed.update(cx, |ed, cx| {
    ed.config.markdown.conceal_mode = ConcealMode::Hidden;
    ed.set_highlighter(MarkdownHighlighter::with_config(ed.config.markdown));
    cx.notify();
});
}

5. Complete Markdown Example

Here is a full view component demonstrating Markdown setup with an active status badge:

#![allow(unused)]
fn main() {
use gpui::*;
use twrite::Editor;
use twrite::SearchHook;

struct MarkdownEditorApp {
    editor: Entity<Editor>,
}

impl MarkdownEditorApp {
    pub fn new(cx: &mut Context<Self>) -> Self {
        let editor = cx.new(|cx| {
            let initial_content = r#"# Project Notes

# Features
- [x] GPU-accelerated canvas
- [ ] Customizable theme colors
- [x] Interactive task lists

Visit the [TWrite GitHub](https://github.com/ToonionOfficial/twrite) for updates.
"#;
            let mut ed = Editor::new(initial_content, cx);
            ed.enable_markdown();
            ed.add_hook(SearchHook::new());
            ed
        });

        Self { editor }
    }
}

impl Render for MarkdownEditorApp {
    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
        div()
            .size_full()
            .bg(rgb(0x1e1e2e))
            .child(self.editor.clone())
    }
}
}