Git Product home page Git Product logo

quickmd's Introduction

Crate Documentation Maintenance status

QuickMD

This project is a simple tool that solves a simple problem: I'd like to preview markdown files as they'll show up on Github, so I don't have to push my READMEs before I can tell whether they're formatted well enough. It ends up looking like this:

Demo

It's a Rust app that launches a GtkWebkit window that renders the compiled HTML of the given markdown file. It monitors this file for any changes and reloads. It uses a stylesheet that's literally copied off Github's markdown stylesheet.

Note: I have no idea if I'm allowed to use Github's stylesheet. The relevant file is in res/style/github.css, and if I am told I shouldn't be using it I'll go ahead and scrub it from git history.

Installation

From a release binary

You should be able to find a compiled 64-bit linux binary in every release since v0.4.1. You can put it in your $PATH and launch it, as long as you have GTK3 and Webkit2Gtk installed on your system. On Arch Linux, you can install these like so:

# pacman -S gtk3 webkit2gtk

From source

You'll need to have Rust and the cargo tool. The easiest way to get that done is through rustup.rs.

You'll also need the GTK3 and Webkit2Gtk development files to be installed on your system. The Gtk-rs installation page might be a good start.

After that, you can build and install the app from crates.io using:

cargo install quickmd

Make sure that ~/.cargo/bin is in your PATH so you can call the quickmd executable.

Usage

Running the app is as simple as:

quickmd <markdown-file>

Pressing CTRL+Q will close the window. Running it with --help should provide more info on the available options. Here's how the output looks for me:

quickmd 0.6.0
A simple self-contained markdown previewer.

Code highlighting via highlight.js version 9.18.1

Edit configuration in: /home/andrew/.config/quickmd/config.yaml
Add custom CSS in:     /home/andrew/.config/quickmd/custom.css

USAGE:
    quickmd [FLAGS] [OPTIONS] [input-file.md]

FLAGS:
    -d, --debug
            Activates debug logging

    -h, --help
            Prints help information

        --install-default-config
            Creates a configuration file for later editing if one doesn't exist. Exits when done

    -V, --version
            Prints version information

        --no-watch
            Disables watching file for changes


OPTIONS:
        --output <directory>
            Builds output HTML and other assets in the given directory instead of in a tempdir. Will be created if it
            doesn't exist. Not deleted on application exit

ARGS:
    <input-file.md>
            Markdown file to render. Use "-" to read markdown from STDIN (implies --no-watch). If not provided, the app
            will launch a file picker

Features

  • Github-like rendering, though not guaranteed to be perfectly identical. Relying on whatever pulldown-cmark provides, which is good enough for me.

  • Fast and seamless preview updates on file write.

  • Code highlighting via highlight.js. Currently, the relevant javascript is included via a CDN, which unfortunately means it won't work without an internet connection.

  • Ability to render STDIN, which allows partial rendering of target markdown. Try putting this bit of Vimscript in your ~/.vim/ftplugin/markdown.vim, select a few lines and press !.

  • Scroll with Vi-like keybindings, Press e to spawn an editor, if configured.

  • Customizable keybindings. Check the API documentation or try --install-default-config to get an annotated config with examples.

Configuration

You can change the CSS of the preview HTML by writing a file named "custom.css" in the application's config directory. On a linux machine, it would be: ~/.config/quickmd/.

You can also change some configuration options in a config file. Run quickmd with --install-default-config to create that file with all the defaults and comments.

Run --help to see where the config files will be located on your system, or press F1 in the interface.

The built-in CSS that is used is stored in /res/style and the default config is in /res/default_config.yaml

quickmd's People

Contributors

andrewradev avatar cheap-glitch avatar vladimiroff avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

quickmd's Issues

Autolink URLs

Right now, URLs that are not explicitly made into links are just text. It would be nice to auto-link them for consistency with what github does.

This could be done on the Rust side in the markdown parser -- for each Text event, run a search-and-replace on the contents. Or it could be done in javascript, which could be done after the document is rendered, and might be easier (would it?).

Themes and/or "dark mode"

Right now, there's only one CSS used, github's default one, which I literally ripped out of the site. It would be nice to allow changing the CSS to the dark-mode version and possibly others, like crates.io's markdown rendering. So, --theme github-light, --theme github-dark, --theme crates.io.

Alternatively, it would be plugged into the file config -- that would make it easier to pick just one, but harder to switch. Could be that "both" would be a good option for this particular setting.

A help popup on F1, maybe others

A help dialog that shows keybindings, like Ctrl+scroll to zoom in/out, would be nice. It should be relatively easy to do this with GTK.

An about dialog with some basic info, a github repo, documentation links, would also be nice and easy, probably. An "open file" dialog would be something I never use, but I guess rather common for something like this.

Settings being editable via GTK might be nice... but treating the config as simply a storage mechanism might be a problem, since right now, it has explanatory comments.

(Very much a side note, I'd love a YAML parser that allows updating keys without touching comments. Definitely not in scope for the project, though.)

Support relative paths for embedded images

GitHub lets you use relative links when referring to images (and any other files) in the repo, e.g.

![Company logo](distribution/media/logo.png)

It would be nice if quickmd supported this. Ideally it would look for the file starting at the root of the current Git repo, but for a start using the directory of the current file would be good enough.

I can try opening a PR for this if it's not too hard to implement.

Exploratory: Try porting to web-view

Right now, quickmd doesn't work on MacOS, because there's no working webview2gtk. It might work on windows, but it'd need GTK installed.

It might be nice to use the web-view project instead, since it seems like it's cross-platform and it uses whatever works on each platform... but it might be much more inconvenient to work with, with a lot of the logic needing to be in javascript. I have no realistic idea of how it would work.

HTML generation, file-watching, and configuration have nothing to do with GTK, so a good chunk of the logic might be fine without it. Whether web-view is convenient enough UI-wise is the big question.

It's something I'd try at some point, at least. Though maybe not even in this project, but in a separate one by copy-pasting stuff ¯\_(ツ)_/¯

Scrolling with j/k, maybe others

It shouldn't be difficult to listen to keybindings and scroll up and down. Maybe gg and G could also jump to the top and bottom of the document respectively. I'd also like to use J and K for faster scrolling, though these are not as vim-compatible.

It would be nice to get this done in the GTK portion of the code... but I'm not sure if it's possible. I can't see a way to scroll the webview from GTK, but I might be missing something. It'd be relatively easy to do in javascript, but it might make it complicated to implement #5, since keybindings would be kind of split between GTK and javascript in a non-obvious way.

It might be that GTK simply triggers some javascript -- it might be the best compromise, and probably won't have a whole lot of overhead. Difficult to say without trying it out.

Handle remote links

This could be a hard one, depending on what's being attempted.

Ideally, clicking a link in the markdown document would actually open it in the default browser. There's no point in opening it in the embedded webview. But I don't see a good way to hook this "link was clicked" event into GTK other than maybe some kind of polling to check a global variable?

Alternatively, it might be possible to use the javascript clipboard API to just copy an external link to the clipboard, and the only next step would be pasting it in the browser. If the clipboard API works, it should be relatively easy to do this in the javascript part of the app.

If so, it would be nice to show some kind of notification. The "link preview" div in the bottom-right could also change between Copy <url> and Jump to <url>, the second one showing up for internal links like anchors.

Spawn an external editor with a keypress

There's a branch called exec-editor that is unfinished. Right now, it execs the app into a GVim when pressing Ctrl+e.

Instead, the app should look up the configured editor from the YAML configuration and exec into that. If there's no editor configured, it would be ideal to show some GTK error message that says "go fill the editor in ", or to default to one of several known editors that exists in PATH. Maybe gvim and code to start.

In terms of mappings, I'm considering e for spawning an editor and E for exec-ing into one (only for unix). Switching from preview to editing, effectively. The preview is supposed to be read-only, so using standard keys for actions should not be a problem.

Would it be possible to get the "global" GUI editor? Maybe by fetching the association for text files? I remember investigating it, but I realized that markdown files are also considered "text", and I wanted to associate them with quickmd for double-clicking... although that might be a problem as well if quickmd gets associated with all text files. Starting with an explicit config might be easiest.

Rendering is broken with `glib2-2.72`

After upgrading glib2 to the 2.72 version, the preview window became completely blank. quickmd --debug doesn't produce any errors. Downgrading the package fixed the issue.

Edit: This is on Arch Linux with Wayland.

Provide Binary releases

Hi.

Great tool ;)

It would be nice to provide a binary release, It took me some time to build everything on my machine, and had to install some missing dependencies, as well as having the rust toolchain (which I already had, but for other users might be complicated to setup).

It should be simple to do with GitHub actions. For example: https://alican.codes/rust-github-actions/

File watching is broken with NeoVim

The preview isn't updated when saving changes to the watched file in NeoVim.

Looking at the debug logs, it seems quickmd detects the file as being removed and recreated, instead of being modified:

[DEBUG quickmd::background] Ignored watcher event: NoticeRemove("/home/florent/dev/devlint/README.md")
[DEBUG quickmd::background] Ignored watcher event: Create("/home/florent/dev/devlint/README.md")
[DEBUG quickmd::background] Ignored watcher event: NoticeRemove("/home/florent/dev/devlint/README.md")
[DEBUG quickmd::background] Ignored watcher event: Create("/home/florent/dev/devlint/README.md")

This doesn't happen with other editors (tested in Leafpad and nano).

I have some rudimentary knowledge of Rust, so if this is an easy fix I can try to take a stab at it. Otherwise, I'd still be happy with a quick keybinding to manually refresh the preview, e.g. r of F11 😄

Customizable keybindings

It would be really nice if it was possible to customize keybindings for spawning an editor, for scrolling, etc.

It could be implemented the way Alacritty does it:

  - { key: Insert, mods: Shift, action: PasteSelection   }

Alternatively, I'd love to have vim-like <S-Insert> descriptions of keys, which are pretty compact and readable (though I might be biased here).

Embed highlight.js files

This one might be done quite easily by using include_str... but it would be a lot of repetitive lines, since highlight.js has tons of separate files with different language support. It could all be packed into one file and exported, but it would be a large file, with lots of stuff that would never be used.

Currently, the code knows which code languages are being used on the markdown page, so there's no issue in including just the right files. So an "easy" way to solve this issue is something like:

  • Create a folder in res with an import of highlight.js files.
  • Create a hashmap, maybe with lazy_static or once_cell with include_str lines for these.
  • Package them up in a struct with a sensible interface to write these script files like we do here:

    quickmd/src/assets.rs

    Lines 88 to 93 in 9e09b02

    fs::write(output_path.join("main.js"), MAIN_JS).
    unwrap_or_else(|e| warn!("{}", e));
    fs::write(output_path.join("main.css"), MAIN_CSS).
    unwrap_or_else(|e| warn!("{}", e));
    fs::write(output_path.join("github.css"), GITHUB_CSS).
    unwrap_or_else(|e| warn!("{}", e));

An interesting, more complicated idea, might be to make this reusable, maybe even pull this concept into a separate crate. Something like an include_dir! proc macro that recursively reads the whole contents of a directory, and does the whole lazy-static hashmap setup above, it just doesn't require somebody to manually go over the individual files in the source. The hashmap that include_dir! spits out could be keyed by a Path. The contents might even be compressed like include_flate does, but that's a separate thing.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.