From c85b21739488f99df53403eacfca5fbf7a3dd495 Mon Sep 17 00:00:00 2001 From: Mark Pitman Date: Fri, 21 Aug 2026 15:14:57 -0700 Subject: [PATCH] Read markdown from standard input when the filename is - Piping markdown in avoids writing it to disk first, which matters when the content is decrypted or generated on the fly. Relative image paths resolve against the working directory, since there is no input file to anchor them to. --- CLAUDE.md | 2 +- README.md | 13 +++++++++++++ main.go | 21 +++++++++++++++++---- mdview.1.md | 5 +++++ 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index db99807..2ae0ac5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -31,7 +31,7 @@ Requires Go (see `go.mod`/`mise.toml` for version) and, for packaging, `just`, ` Everything happens in `main()` in `main.go`, in a straight-line pipeline: -1. **Flag parsing** — `-o`, `-v/-version`, `-h/-help`, `-b/-bare`. First positional arg is the input markdown file. +1. **Flag parsing** — `-o`, `-v/-version`, `-h/-help`, `-b/-bare`. First positional arg is the input markdown file; a single `-` reads the markdown from stdin instead, in which case `baseDir` for image resolution is the working directory rather than the input file's directory. 2. **Image inlining** (`processMarkdownImages` → `processHTMLImages`/markdown image regex + `imageToDataURI`) — rewrites relative image references (both `![]()` markdown syntax and raw `` HTML) into base64 `data:` URIs *before* markdown parsing, so the output HTML is fully self-contained/offline-viewable. This includes path-traversal guards (caps `..` traversal depth) and a 10MB per-image size cap. 3. **Markdown parsing** via Goldmark, configured with: - `extension.GFM` (tables, task lists, strikethrough, etc.) diff --git a/README.md b/README.md index 472bce0..b416b7e 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,19 @@ A lightweight command-line tool that converts markdown files to styled HTML and ## Usage +Pass a filename, or a single dash to read markdown from standard input: + +```sh +mdview notes.md +cat notes.md | mdview - +age -d -i ~/.config/age/key.txt notes.md.age | mdview - +``` + +Reading from standard input avoids writing the markdown to disk first, +which is useful when the content is decrypted or generated on the fly. +Relative image paths are then resolved against the current working +directory. + By default, `mdview` writes the generated HTML to a temporary directory. It tries these in order: - A path defined in the `MDVIEW_DIR` environment variable diff --git a/main.go b/main.go index 4d028d3..7250eb7 100644 --- a/main.go +++ b/main.go @@ -9,6 +9,7 @@ import ( "errors" "flag" "fmt" + "io" "io/fs" "log" "os" @@ -59,16 +60,28 @@ func main() { } if inputFilename == "" || *helpPtr { - os.Stderr.WriteString("Usage:\nmdview [options] \nFormats markdown and launches it in a browser.\nIf the environment variable MDVIEW_DIR is set, the temporary file will be written there.\n") + os.Stderr.WriteString("Usage:\nmdview [options] \nFormats markdown and launches it in a browser.\nUse - as the filename to read markdown from standard input.\nIf the environment variable MDVIEW_DIR is set, the temporary file will be written there.\n") flag.PrintDefaults() os.Exit(1) } - dat, err := os.ReadFile(inputFilename) - check(err) + // A filename of - means read the markdown from standard input. Relative image + // paths are then resolved against the working directory, as there is no input + // file to anchor them to. + var dat []byte + var err error + baseDir := "." + + if inputFilename == "-" { + dat, err = io.ReadAll(os.Stdin) + check(err) + } else { + dat, err = os.ReadFile(inputFilename) + check(err) + baseDir = filepath.Dir(inputFilename) + } // Convert relative image links to data URIs in the markdown source - baseDir := filepath.Dir(inputFilename) processedMarkdown := processMarkdownImages(string(dat), baseDir) processedBytes := []byte(processedMarkdown) diff --git a/mdview.1.md b/mdview.1.md index 30ac148..67d3f43 100644 --- a/mdview.1.md +++ b/mdview.1.md @@ -7,6 +7,7 @@ # SYNOPSIS **mdview** _filename_ +**mdview** **-** **mdview** \[**-h**|**--help**|**-v**|**--version**] # DESCRIPTION @@ -16,6 +17,10 @@ then launches that file in the default web browser. By default, it will use the operating system's default temporary directory unless the environment variable MDVIEW_DIR is set. +If _filename_ is a single dash (**-**), the markdown is read from +standard input. Relative image paths are then resolved against the +current working directory. + ## Options **-b**, **-bare**