markless 0.9.14

A terminal markdown viewer with image support
Documentation
# Markless - Design Document

This document describes the current architecture and behavior of Markless as implemented in the codebase. It is intentionally pragmatic and scoped to what exists today.

## Overview

Markless is a terminal markdown viewer that renders CommonMark with selected GFM extensions, supports inline images, and provides fast navigation for long documents. It uses ratatui/crossterm for UI, comrak for markdown parsing, syntect for syntax highlighting, and notify for file watching.

## Goals

- Fast startup and responsive scrolling on large files
- Clear, stable rendering that reflows on terminal resize
- Image support with graceful fallback
- Predictable navigation with keyboard and mouse
- Minimal, testable state transitions (TEA)

## Non-Goals

- Editing or authoring content
- Export to other formats
- Custom markdown extensions beyond CommonMark + enabled GFM features
- GUI support

## Architecture

The app follows The Elm Architecture (TEA):

- **Model**: all application state
- **Message**: user/system events
- **Update**: pure function that transforms state
- **View**: rendering via ratatui

Main flow:

1. Load file into `Document`
2. Create `Model` and run the event loop
3. Handle input to produce `Message`
4. Apply `update` to mutate `Model`
5. Render the UI

Side effects (file reloads, external link open, clipboard copy) are handled in `app/effects.rs`.

## Core Data Model

### Model (src/app/model.rs)

- `document`: parsed and rendered markdown
- `viewport`: scroll + size management
- `toc_visible`, `toc_selected`, `toc_focused`
- `search_query`, `search_matches`, `search_match_index`
- `hovered_link_url`
- `selection`: line-based mouse selection
- `watch_enabled`
- `images_enabled`
- Image caches and layout state

### Document (src/document/types.rs)

- `lines`: rendered lines (with optional styled spans)
- `headings`: TOC entries
- `images`: image references with line ranges
- `links`: link references by rendered line
- `footnotes`: definition lookup

`RenderedLine` tracks text, line type, and optional inline spans.

## Input and Interaction

### Keyboard

Key bindings are handled in `src/app/input.rs` and documented in the README. Important groups:

- Navigation: `j/k`, arrows, page/half-page, `g/G`
- Search: `/`, `Enter` (next), `Esc` (clear)
- TOC: `t`, `T`, `Tab`, `Enter`/`Space`
- Other: `w`, `r/R`, `o`, `?`/`F1`, `q`/`Ctrl-c`

### Mouse

- Scroll wheel: scroll
- Click: follow links or image placeholders
- Hover: show URL for links and images
- Click+drag: select whole lines and copy

Selection is line-based and copies text as fixed-width blocks. Inline link labels are copied as URLs. Code block borders are stripped during copy.

## Rendering

### Layout

The UI has two primary regions:

- Optional TOC sidebar
- Document view

Footer rows are reserved for status, search, toast, and hover link bar.

### Document Rendering

`render_document` builds a list of `Line` items with styles derived from `LineType` and inline spans. Search matches are highlighted. Selected lines render with a background tint.

### Lists

List items render with hanging indents and have a trailing blank line after the list to provide visual separation.

### Horizontal Rules

Horizontal rules render as a short light line: `─────`.

## Images

### Rendering

Images are rendered via `ratatui-image` using protocol detection. Supported protocols:

- Kitty
- Sixel
- iTerm2
- Half-block fallback

Images are scaled to ~65% of the document width. Image protocol dimensions determine the reserved row height for layout.

### Captions

When an image height is known (rendered images), the image alt text is rendered **above** the image, indented by four spaces. The caption is omitted when images are not rendered.

### No-Images Mode

`--no-images` disables inline rendering and shows placeholders only.

## Links and Footnotes

Links and footnotes are tracked by rendered line for hover/click. Footnote references render with superscript for numeric labels. Footnote definitions render the same label format without colons and with a trailing space.

## File Watching

File watching uses `notify`. When enabled, changes reload the document and reflow the layout.

## Configuration

Flags can be saved to a global config and overridden locally.

Global (macOS): `~/Library/Application Support/markless/config`
Local: `.marklessrc` in the current directory

Saved flags are merged with CLI flags (CLI wins on conflicting options).

## Command Line Options

- `--watch`
- `--no-toc`
- `--toc`
- `--no-images`
- `--force-half-cell`
- `--theme <auto|light|dark>`
- `--perf`
- `--render-debug-log <PATH>`
- `--save`
- `--clear`

## Project Structure

```
src/
  app/           TEA model, update, input, effects, event loop
  config.rs      Flag parsing and config persistence
  document/      Markdown parsing and rendered document types
  highlight/     Syntect integration
  image/         Image loading and terminal protocol selection
  input/         Low-level input handling helpers
  perf.rs        Performance logging utilities
  search/        Search and match helpers
  ui/            Rendering, overlays, and styling
  watcher/       File watcher wrapper
```

## Testing Strategy

Tests live alongside modules and focus on:

- Parsing and rendering of markdown primitives
- TEA state transitions
- Input handling
- Image layout behavior

The project follows strict TDD (red/green/refactor).

## Future Considerations

- Improved link picking UX
- More granular theme controls
- Additional render performance profiling