···11+MIT License
22+33+Copyright (c) 2026 Rivo Link <rivo.link@gmail.com>
44+55+Permission is hereby granted, free of charge, to any person obtaining a copy
66+of this software and associated documentation files (the "Software"), to deal
77+in the Software without restriction, including without limitation the rights
88+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
99+copies of the Software, and to permit persons to whom the Software is
1010+furnished to do so, subject to the following conditions:
1111+1212+The above copyright notice and this permission notice shall be included in all
1313+copies or substantial portions of the Software.
1414+1515+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1616+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1717+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1818+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1919+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2020+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121+SOFTWARE.
+97
README.md
···11+<p align="center">
22+ <img src="images/logo-wordmark.svg" alt="leaf" width="360" />
33+</p>
44+55+<p align="center">
66+ Terminal Markdown previewer — GUI-like experience.
77+</p>
88+99+## Build & install
1010+1111+Build the release binary:
1212+1313+```bash
1414+cargo build --release
1515+```
1616+1717+Create a local bin directory if needed and symlink `leaf` into it:
1818+1919+```bash
2020+mkdir -p ~/.local/bin
2121+ln -sf "$(pwd)/target/release/leaf" ~/.local/bin/leaf
2222+```
2323+2424+If `~/.local/bin` is not already on your `PATH`, add it to `~/.bashrc` or `~/.zshrc`:
2525+2626+```bash
2727+export PATH="$HOME/.local/bin:$PATH"
2828+```
2929+3030+Check the installed version:
3131+3232+```bash
3333+leaf --version
3434+```
3535+3636+## Usage
3737+3838+```bash
3939+# Preview a file
4040+leaf TESTING.md
4141+4242+# Watch mode — reloads automatically on save
4343+leaf --watch TESTING.md
4444+leaf -w TESTING.md
4545+4646+# Open a dash-prefixed filename
4747+leaf -- -notes.md
4848+4949+# Pipe from stdin
5050+claude "explain Rust lifetimes" | leaf
5151+cat TESTING.md | leaf
5252+```
5353+5454+## Keybindings
5555+5656+| Key | Action |
5757+|---|---|
5858+| `j` / `↓` | Scroll down |
5959+| `k` / `↑` | Scroll up |
6060+| `d` / PgDn | Page down (20 lines) |
6161+| `u` / PgUp | Page up (20 lines) |
6262+| `g` / Home | Top |
6363+| `G` / End | Bottom |
6464+| `t` | Toggle TOC sidebar |
6565+| `1`–`9` | Jump to TOC section N |
6666+| `/` | Search |
6767+| `n` / `N` | Next / prev match |
6868+| `r` | Force reload (watch mode) |
6969+| `q` | Quit |
7070+7171+## Features
7272+7373+- ✅ **Watch mode** `--watch` / `-w` — reloads every 250ms, with `⟳ reloaded` flash feedback
7474+- ✅ Syntax highlighting (200+ languages, syntect)
7575+- ✅ Unicode box-drawing tables with left / center / right alignment
7676+- ✅ TOC sidebar with active section tracking and two-level navigation
7777+- ✅ Search with match highlighting and `n` / `N`
7878+- ✅ Code blocks `╭─ lang ───╮`
7979+- ✅ Bold, italic, strikethrough, blockquotes, lists, and horizontal rules
8080+- ✅ YAML frontmatter is ignored in both preview and TOC
8181+- ✅ Native stdin input
8282+8383+## Typical AI Workflow
8484+8585+```bash
8686+# Terminal 1: generate the file
8787+aichat "..." > notes.md
8888+8989+# Terminal 2: live watch
9090+leaf --watch notes.md
9191+```
9292+9393+## Roadmap
9494+9595+- [ ] Themes (light / custom)
9696+- [ ] Copy code block `y`
9797+- [ ] Improve search performance on large files
+256
TESTING.md
···11+# Testing
22+33+This project has two kinds of testing:
44+55+- automated unit tests with `cargo test`
66+- manual end-to-end checks using the fixture in this file
77+88+## Quick Start
99+1010+Run automated tests:
1111+1212+```bash
1313+cargo test
1414+```
1515+1616+Open the manual fixture from a file:
1717+1818+```bash
1919+leaf TESTING.md
2020+```
2121+2222+Open it in watch mode:
2323+2424+```bash
2525+leaf --watch TESTING.md
2626+```
2727+2828+Open it through stdin:
2929+3030+```bash
3131+cat TESTING.md | leaf
3232+```
3333+3434+## Manual Coverage
3535+3636+Use the fixture in the `Manual Fixture` section of this file to verify the current feature set.
3737+3838+### Rendering
3939+4040+Confirm these render correctly:
4141+4242+- headings and TOC entries
4343+- paragraphs with normal spacing
4444+- bold, italic, strikethrough, inline code, and links
4545+- blockquotes with multiple paragraphs
4646+- unordered, loose, nested, and ordered lists
4747+- horizontal rules
4848+- tables with left, center, and right alignment
4949+- fenced code blocks with language labels
5050+- wide characters such as `東京`
5151+5252+### Navigation And Search
5353+5454+Use these keys while viewing the fixture:
5555+5656+- `j`, `k`, `d`, `u` for scrolling
5757+- `g`, `G` for top and bottom
5858+- `t` to toggle the TOC
5959+- `1` through `9` to jump to TOC entries
6060+- `/` to search for `tokyo-signal`
6161+- `n` and `N` to move through search matches
6262+6363+### Watch Mode
6464+6565+While running `leaf --watch TESTING.md`:
6666+6767+- edit one of the repeated search terms
6868+- confirm the content reloads automatically
6969+- confirm the `⟳ reloaded` indicator appears
7070+- press `r` to force a reload manually
7171+7272+### Stdin Mode
7373+7474+While running `cat TESTING.md | leaf`:
7575+7676+- confirm the content matches file-backed rendering
7777+- confirm watch mode is not available
7878+7979+### Startup And Error Handling
8080+8181+Run these checks manually:
8282+8383+```bash
8484+leaf --watch
8585+leaf /path/that/does/not/exist.md
8686+leaf --help
8787+```
8888+8989+Confirm each command exits cleanly and does not leave the terminal in raw mode or an alternate screen.
9090+9191+## Notes
9292+9393+The fixture intentionally includes repeated search terms, loose list items, ordered lists starting at non-`1` values, tables, code blocks, and wide characters because those are easy places for terminal Markdown renderers to regress.
9494+9595+## Manual Fixture
9696+9797+Open this file in `leaf` and use the content below as the end-to-end render sample.
9898+9999+Search terms:
100100+101101+- `tokyo-signal`
102102+- `watch-reload-marker`
103103+- `table-edge-check`
104104+- `unicode-width-check`
105105+106106+### Navigation
107107+108108+This section exists to populate the TOC and provide enough content for scrolling and search.
109109+110110+tokyo-signal appears here once.
111111+112112+#### Repeated Search Block
113113+114114+tokyo-signal appears here twice.
115115+116116+tokyo-signal appears here three times.
117117+118118+watch-reload-marker appears here once.
119119+120120+watch-reload-marker appears here twice.
121121+122122+### Paragraph Styles
123123+124124+Plain paragraph text should render with the default body style and spacing.
125125+126126+This line mixes **bold**, *italic*, ~~strikethrough~~, and `inline code` in a single paragraph.
127127+128128+This paragraph also includes a [link to Rust](https://www.rust-lang.org/) so link styling and the leading link marker can be checked.
129129+130130+### Blockquote
131131+132132+> This is a blockquote with *emphasis* and `inline code`.
133133+>
134134+> The second quoted paragraph ensures paragraph flushing still keeps the quote prefix.
135135+>
136136+> unicode-width-check is present here too.
137137+138138+### Lists
139139+140140+#### Unordered Tight List
141141+142142+- first bullet
143143+- second bullet
144144+- third bullet with `inline code`
145145+146146+#### Unordered Loose List
147147+148148+- first loose item
149149+150150+- second loose item after a blank line
151151+152152+- third loose item with two paragraphs
153153+154154+ This continuation paragraph should keep list structure without repeating the bullet.
155155+156156+#### Nested List
157157+158158+- outer one
159159+ - inner one
160160+ - inner two
161161+ - deeper item
162162+- outer two
163163+164164+#### Ordered List
165165+166166+3. item three
167167+4. item four
168168+5. item five
169169+170170+#### Ordered Loose List
171171+172172+7. item seven
173173+174174+8. item eight with a second paragraph
175175+176176+ This continuation paragraph should align with item eight instead of relying on the numeric marker.
177177+178178+### Rules
179179+180180+The horizontal rule below should span cleanly.
181181+182182+---
183183+184184+The document should continue normally after the rule.
185185+186186+### Tables
187187+188188+| Name | Align Left | Center | Right |
189189+| --- | :--- | :---: | ---: |
190190+| Alpha | left | mid | 12 |
191191+| Beta | table-edge-check | centered | 345 |
192192+| Tokyo | unicode-width-check 東京 | wide | 6789 |
193193+| Tabs | tab value | cell | 10 |
194194+195195+The table above is intended to check borders, alignment, tab expansion, and wide-character handling.
196196+197197+### Code Blocks
198198+199199+```rust
200200+fn main() {
201201+ let city = "東京";
202202+ println!("tokyo-signal: {city}");
203203+}
204204+```
205205+206206+```bash
207207+printf '%s\n' "watch-reload-marker"
208208+leaf --watch TESTING.md
209209+```
210210+211211+```yaml
212212+search:
213213+ primary: tokyo-signal
214214+ secondary: unicode-width-check
215215+```
216216+217217+### Wide Characters
218218+219219+These lines are here to verify width calculations:
220220+221221+- 東京
222222+- café
223223+- naïve
224224+225225+### Long Scroll Area
226226+227227+Line 01: scrolling sample
228228+Line 02: scrolling sample
229229+Line 03: scrolling sample
230230+Line 04: scrolling sample
231231+Line 05: scrolling sample
232232+Line 06: scrolling sample
233233+Line 07: scrolling sample
234234+Line 08: scrolling sample
235235+Line 09: scrolling sample
236236+Line 10: scrolling sample
237237+Line 11: scrolling sample
238238+Line 12: scrolling sample
239239+Line 13: scrolling sample
240240+Line 14: scrolling sample
241241+Line 15: scrolling sample
242242+Line 16: scrolling sample
243243+Line 17: scrolling sample
244244+Line 18: scrolling sample
245245+Line 19: scrolling sample
246246+Line 20: scrolling sample
247247+Line 21: scrolling sample
248248+Line 22: scrolling sample
249249+Line 23: scrolling sample
250250+Line 24: scrolling sample
251251+Line 25: scrolling sample
252252+Line 26: scrolling sample
253253+Line 27: scrolling sample
254254+Line 28: scrolling sample
255255+Line 29: scrolling sample
256256+Line 30: scrolling sample