Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/kovetskiy/mark

Sync your markdown files with Confluence pages.
https://github.com/kovetskiy/mark

confluence git markdown

Last synced: 9 days ago
JSON representation

Sync your markdown files with Confluence pages.

Awesome Lists containing this project

README

        

# Mark

[![All Contributors](https://img.shields.io/badge/all_contributors-44-orange.svg?style=flat-square)](#contributors-)

Mark — a tool for syncing your markdown documentation with Atlassian Confluence
pages.

Read the blog post discussing the tool —

This is very useful if you store documentation to your software in a Git
repository and don't want to do an extra job of updating Confluence page using
a tinymce wysiwyg enterprise core editor which always breaks everything.

Mark does the same but in a different way. Mark reads your markdown file, creates a Confluence page
if it's not found by its name, uploads attachments, translates Markdown into HTML and updates the
contents of the page via REST API. It's like you don't even need to create sections/pages in your
Confluence anymore, just use them in your Markdown documentation.

Mark uses an extended file format, which, still being valid markdown,
contains several HTML-ish metadata headers, which can be used to locate page inside
Confluence instance and update it accordingly.

File in the extended format should follow the specification:

```markdown

```

There can be any number of `Parent` headers, if Mark can't find specified
parent by title, Mark creates it.

Also, optional following headers are supported:

```markdown

```

* (default) article: content will be put in narrow column for ease of
reading;
* plain: content will fill all page;

```markdown

```

* (default) page: normal Confluence page - defaults to this if omitted
* blogpost: [Blog post](https://confluence.atlassian.com/doc/blog-posts-834222533.html) in `Space`. Cannot have `Parent`(s)

```markdown

```

* (default) full-width: content will fill the full page width
* fixed: content will be rendered in a fixed narrow view

```markdown

```

Setting the sidebar creates a column on the right side. You're able to add any valid HTML content. Adding this property sets the layout to `article`.

Mark supports Go templates, which can be included into article by using path
to the template relative to current working dir, e.g.:

```markdown

```

If the template cannot be found relative to the current directory, a fallback directory can be defined via `--include-path`. This way it is possible to have global include files while local ones will still take precedence.

Optionally the delimiters can be defined:

```markdown

```

Or they can be switched off to disable processing:

```markdown

```

**Note:** Switching delimiters off really simply changes
them to ASCII characters "\x00" and "\x01" which, usually
should not occure in a template.

Templates can accept configuration data in YAML format which immediately
follows the `Include` and `Delims` tag, if present:

```markdown

```

Mark also supports attachments. The standard way involves declaring an
`Attachment` along with the other items in the header, then have any links
with the same path:

```markdown

An attached link is [here]()
```

**NOTE**: Be careful with `Attachment`! If your path string is a subset of
another longer string or referenced in text, you may get undesired behavior.

Mark also supports macro definitions, which are defined as regexps which will
be replaced with specified template:

```markdown

```

**NOTE**: Make sure to define your macros after your metadata (Title/Space),
mark will stop processing metadata if it hits a Macro.

Capture groups can be defined in the macro's which can be later
referenced in the `` using `${}` syntax, where `` is
number of a capture group in regexp (`${0}` is used for entire regexp match),
for example:

```markdown

```

Macros can also use inline templates.
Inline templates are templates where the template content
is described in the ``.
The `Template` value starts with a `#`, followed by the key
used in the ``.
The key's value must be a string which defines the template's content.

```markdown



and some
content

```

## Customizing the page layout

If you set the Layout to plain, the page layout can be customized using HTML comments inside the markdown:

```markdown

More Content

More Content

Even More Content

Still More Content

```

Please be aware that mark does not validate the layout, so it's your responsibility to create a valid layout.

### Placeholders

You can use this to define placeholders:

```markdown

Placeholder

```

### Code Blocks

If you have long code blocks, you can make them collapsible with the [Code Block Macro]:

```bash collapse
...
some long bash code block
...
```

And you can also add a title:

```bash collapse title Some long long bash function
...
some long bash code block
...
```

Or linenumbers, by giving the first number

```bash 1 collapse title Some long long bash function
...
some long bash code block
...
```

And even themes

```bash 1 collapse midnight title Some long long bash function
...
some long bash code block
...
```

Please note that, if you want to have a code block without a language
use `-` as the first character, if you want to have the other goodies

``` - 1 collapse midnight title Some long long code
...
some long code block
...
```

[Code Block Macro]: https://confluence.atlassian.com/doc/code-block-macro-139390.html

### Block Quotes

Block Quotes are converted to Confluence Info/Warn/Note box when the following conditions are met

1. The BlockQuote is on the root level of the document (not nested)
1. The first line of the BlockQuote contains one of the following patterns `Info/Warn/Note` or [Github MD Alerts style](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts) `[!NOTE]/[!TIP]/[!IMPORTANT]/[!WARNING]/[!CAUTION]`

| Github Alerts | Confluence |
|---------------|------------|
| Tip (green lightbulb) | Tip (green checkmark in circle) |
| Note (blue I in circle) | Info (blue I in circle) |
| Important (purple exclamation mark in speech bubble) | Info (blue I in circle) |
| Warning (yellow exclamation mark in triangle) | Note (yellow exclamation mark in triangle) |
| Caution (red exclamation mark in hexagon) | Warning (red exclamation mark in hexagon) |

In any other case the default behaviour will be resumed and html `

` tag will be used

## Template & Macros

By default, mark provides several built-in templates and macros:

* template `ac:status` to include badge-like text, which accepts following
parameters:
* Title: text to display in the badge
* Color: color to use as background/border for badge
* Grey
* Red
* Yellow
* Green
* Blue
* Subtle: specify to fill badge with background or not
* true
* false

* template `ac:box`to include info, tip, note, and warning text boxes. Parameters:
* Name: select box style
* info
* tip
* note
* warning
* Icon: show information/tip/exclamation mark/warning icon
* true
* false
* Title: title text of the box
* Body: text to display in the box

See:

* template `ac:jira:ticket` to include JIRA ticket link. Parameters:
* Ticket: Jira ticket number like BUGS-123.

See:

* template `ac:jira:filter` to include JIRA Filters/Searches. Parameters:
* JQL: The "JQL" query of the search
* Server (Optional): The Jira server to fetch the query from if its not the default of "System Jira"

* template `ac:jiraissues` to include a list of JIRA tickets. Parameters:
* URL (Required), The URL of the XML view of your selected issues. (link to the filter)
* Anonymous (Optional) If this parameter is set to 'true', your JIRA application will return only the issues which allow unrestricted viewing. That is, the issues which are visible to anonymous viewers. If this parameter is omitted or set to 'false', then the results depend on how your administrator has configured the communication between the JIRA application and Confluence. By default, Confluence will show only the issues which the user is authorised to view.
* BaseURL (Optional) If you specify a 'baseurl', then the link in the header, pointing to your JIRA application, will use this base URL instead of the value of the 'url' parameter. This is useful when Confluence connects to JIRA with a different URL from the one used by other users.
* Columns (Optional) A list of JIRA column names, separated by semi-colons (;). You can include many columns recognized by your JIRA application, including custom columns.
* Count (Optional) If this parameter is set to 'true', the issue list will show the number of issues in JIRA. The count will be linked to your JIRA site.
* Cache (Optional) The macro maintains a cache of the issues which result from the JIRA query. If the 'cache' parameter is set to 'off', the relevant part of the cache is cleared each time the macro is reloaded. (The value 'false' also works and has the same effect as 'off'.)
* Height (Optional) The height in pixels of the table displaying the issues.
* RenderMode (Optional) If the value is 'dynamic', the JIRA Issues macro offers an interactive display.
* Title (Optional) You can customise the title text at the top of the issues table with this parameter. For instance, setting the title to 'Bugs-to-fix' will replace the default 'JIRA Issues' text. This can help provide more context to the list of issues displayed.
* Width (Optional) The width of the table displaying the issues. Can be entered as a percentage (%) or in pixels (px).

See:

* template: `ac:emoticon` to include emoticons. Parameters:
* Name: select emoticon
* smile
* sad
* cheeky
* laugh
* wink
* thumbs-up
* thumbs-down
* information
* tick
* cross
* warning
* plus
* minus
* question
* light-on
* light-off
* yellow-star
* red-star
* green-star
* blue-star

See:

* template: `ac:youtube` to include YouTube Widget. Parameters:
* URL: YouTube video endpoint
* Width: Width in px. Defaults to "640px"
* Height: Height in px. Defaults to "360px"

See:

* template: `ac:children` to include Children Display macro
* Reverse (Reverse Sort): Use with the `Sort Children By` parameter. When set, the sort order changes from ascending to descending.
* `true`
* `false` (Default)
* Sort (Sort Children By):
* `creation` — to sort by content creation date
* `title` — to sort alphabetically on title
* `modified` — to sort of last modification date.
* If not specified, manual sorting is used if manually ordered, otherwise alphabetical.
* Style (Heading Style): Choose the style used to display descendants.
* from `h1` to `h6`
* If not specified, default style is applied.
* Page (Parent Page):
* `/` — to list the top-level pages of the current space, i.e. those without parents.
* `pagename` — to list the children of the specified page.
* `spacekey:pagename` — to list the children of the specified page in the specified space.
* If not specified, the current page is used.
* Excerpt (Include Excerpts): Allows you to include a short excerpt under each page in the list.
* `none` - no excerpt will be displayed. (Default)
* `simple` - displays the first line of text contained in an Excerpt macro any of the returned pages. If there is not an Excerpt macro on the page, nothing will be shown.
* `rich content` - displays the contents of an Excerpt macro, or if there is not an Excerpt macro on the page, the first part of the page content, including formatted text, images and some macros.
* First (Number of Children): Restrict the number of child pages that are displayed at the top level.
* If not specified, no limit is applied.
* Depth (Depth of Descendants): Enter a number to specify the depth of descendants to display. For example, if the value is 2, the macro will display 2 levels of child pages. This setting has no effect if `Show Descendants` is enabled.
* If not specified, no limit is applied.
* All (Show Descendants): Choose whether to display all the parent page's descendants.
* `true`
* `false` (Default)

See:

* template: `ac:iframe` to include iframe macro (cloud only)
* URL: URL to the iframe.
* Frameborder: Choose whether to draw a border around content in the iframe.
* `show` (Default)
* `hide`
* Width: Width in px. Defaults to "640px"
* Height: Height in px. Defaults to "360px"
* Scrolling: Allow or prevent scrolling in the iframe to see additional content.
* `yes`
* `no`
* `auto` (Default)
* Align: Align the iframe to the left or right of the page.
* `left` (Default)
* `right`

See:

* template: `ac:blog-posts`to include blog-posts
* Content: How much content will be shown
* titles (default)
* excerpts
* entire
* Time: Specify how much back in time Confluence should look for blog posts (default: unlimited)
* Label: Restrict to blog posts with specific labels
* Author: Restrict to blog posts by specific authors
* Spaces: Restrict to blog posts in specific spaces
* Max: Maximum number of blog posts shown (default: 15)
* Sort: Sorting posts by
* title
* creation (default)
* modified
* Reverse: Reverses the Sort parameter from oldest to newest (default: false)

See:

* template: `ac:include` to include a page
* Page: the page to be included
* Space: the space the page is in (optional, otherwise same space)

* template: `ac:excerpt-include` to include the excerpt from another page
* Page: the page the excerpt should be included from
* NoPanel: Determines whether Confluence will display a panel around the excerpted content (optional, default: false)

* template: `ac:excerpt` to create an excerpt and include it in the page
* Excerpt: The text you want to include
* OutputType: Determines whether the content of the Excerpt macro body is displayed on a new line or inline (optional, options: "BLOCK" or "INLINE", default: BLOCK)
* Hidden: Hide the excerpt content (optional, default: false)

* template: `ac:anchor` to set an anchor inside a page
* Anchor: Text for the anchor

* template: `ac:expand` to display an expandable/collapsible section of text on your page
* Title: Defines the text next to the expand/collapse icon.
* Body: The Text that it is expanded to.

* template: `ac:profile` to display a short summary of a given Confluence user's profile.
* Name: The username of the Confluence user whose profile summary you wish to show.

* template: `ac:contentbylabel` to display a list of pages, blog posts or attachments that have particular labels
* CQL: The CQL query to discover the content

* template: `ac:detailssummary` to show summary information from one page on a another page
* Headings: Column headings to show
* FirstColumn: Name of the Title Column
* CQL: The CQL query to discover the pages
* SortBy: Sort by a specific column heading

* template: `ac:details` to create page properties
* Body: Must contain a table with two rows, the table headings are used as property key. The table content is the value.

* template: `ac:panel` to display a block of text within a customisable panel
* Title: Panel title (optional)
* Body: Body text of the panel
* BGColor: Background Color
* TitleBGColor: Background color of the title bar
* TitleColor: Text color of the title
* BorderStyle: Style of the panel's border

* template `ac:recently-updated` to display a list of most recently changed content
* Spaces: List of Spaces to watch (optional, default is current Space)
* ShowProfilePic: Show profile picture of editor
* Max: Maximum number of changes
* Types: Include these content types only (comments, blogposts, pages)
* Theme: Apperance of the macro (concise, social, sidebar)
* HideHeading: Determines whether the macro hides or displays the text 'Recently Updated' as a title above the list of content
* Labels: Filter the results by label. The macro will display only the pages etc which are tagged with the label(s) you specify here.

* template: `ac:pagetreesearch` to add a search box to your Confluence page.
* Root: Name of the root page whose hierarchy of pages will be searched by this macro. If this not specified, the root page is the current page.

* template: `ac:column` To be used with the section macro to define the columns in a page.
* Width: Width of the column
* Body: The content of the column

* template: `ac:multimedia` to embedd an attached video, animation or other multimedia files in a Confluence page
* Name: Name of the file
* Width: Width of the video (optional)
* AutoPlay: Start playing the file on page load (default: false)

* macro `@{...}` to mention user by name specified in the braces.

## Template & Macros Usecases

### Insert Disclaimer

This should be in **disclaimer.md**.

```markdown
**NOTE**: this document is generated, do not edit manually.
```

Add this to your **article.md**.

```markdown

This is my article.
```

### Insert Status Badge

```markdown

* :done: Write Article
* :todo: Publish Article
```

### Insert Colored Text Box

```markdown

:box:info::Foobar:
:box:tip:Tip of day:Foobar:
:box:note::Foobar:
:box:warning:Alert!:Foobar:
```

### Insert Table of Contents

```markdown

```

If default TOC looks don't find a way to your heart, try [parametrizing it][Confluence TOC Macro], for example:

```markdown

# This is my nice title

:toc:
```

You can call the `Macro` as you like but the `Template` field must have the `ac:toc` value.
Also, note the single quotes around `'false'`.

See [Confluence TOC Macro] for the list of parameters - keep in mind that here
they start with capital letters. Every skipped field will have the default
value, so feel free to include only the ones that you require.

[Confluence TOC Macro]:https://confluence.atlassian.com/conf59/table-of-contents-macro-792499210.html

### Insert PageTree

```markdown
# My First Heading

```

The pagetree macro works almost the same as the TOC above, but the tree behavior
is more desirable for creating placeholder pages above collections of SOPs.

The default pagetree macro behavior is to insert a tree rooted @self.

The following parameters can be used to alter your default configuration with
parameters described more in depth here:[Confluence Pagetree Macro].

Parameters:

* Title (of tree root page)
* Sort
* Excerpt
* Reverse
* SearchBox
* ExpandCollapseAll
* StartDepth

[Confluence Pagetree Macro]:https://confluence.atlassian.com/conf59/page-tree-macro-792499177.html

E.G.

```markdown

# My First Heading

:pagetree:
```

### Insert Children Display

To include Children Display (TOC displaying children pages) use following macro:

```markdown

# This is my nicer title

:children:
```

You can use various [parameters](https://confluence.atlassian.com/conf59/children-display-macro-792499081.html) to modify Children Display:

```markdown

# This is my nicest title

:children:
```

### Insert Jira Ticket

```markdown

See task MYJIRA-123.
```

### Insert link to existing confluence page by title

```markdown
This is a [link to an existing confluence page](ac:Pagetitle)

And this is how to link when the linktext is the same as the [Pagetitle](ac:)

Link to a [page title containing spaces]()
```

### Upload and included inline images

```markdown
![Example](../images/examples.png)
```

will automatically upload the inlined image as an attachment and inline the image using the `ac:image` template.

If the file is not found, it will inline the image using the `ac:image` template and link to the image.

### Add width for an image

Use the following macro:

```markdown

```

And attach any image with the following

```markdown
![Example](../images/example.png)
```

The width will be the commented html after the image (in this case 300px).

Currently this is not compatible with the automated upload of inline images.

### Render Mermaid Diagram

Confluence doesn't provide [mermaid.js](https://github.com/mermaid-js/mermaid) support natively. Mark provides a convenient way to enable the feature like [Github does](https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/).
As long as you have a code block and are marked as "mermaid", the mark will automatically render it as a PNG image and insert into before the code block.

```mermaid title diagrams_example
graph TD;
A-->B;
```

In order to properly render mermaid, you can choose between the following mermaid providers:

* "mermaid-go" via [mermaid.go](https://github.com/dreampuf/mermaid.go)
* "cloudscript" via [cloudscript-io-mermaid-addon](https://marketplace.atlassian.com/apps/1219878/cloudscript-io-mermaid-addon)

## Installation

### Homebrew

```bash
brew tap kovetskiy/mark
brew install mark
```

### Go Install / Go Get

```bash
go install github.com/kovetskiy/mark@latest
```

For older versions

```bash
go get -v github.com/kovetskiy/mark
```

### Releases

[Download a release from the Releases page](https://github.com/kovetskiy/mark/releases)

### Docker

```bash
docker run --rm -i kovetskiy/mark:latest mark
```

### Compile and install using docker-compose

Mostly useful when you intend to enhance `mark`.

```bash
# Create the binary
$ docker-compose run markbuilder
# "install" the binary
$ cp mark /usr/local/bin
```

## Usage

```bash
NAME:
mark - A tool for updating Atlassian Confluence pages from markdown.

USAGE:
mark [global options]

VERSION:
11.3.0

DESCRIPTION:
Mark is a tool to update Atlassian Confluence pages from markdown. Documentation is available here: https://github.com/kovetskiy/mark

GLOBAL OPTIONS:
--files value, -f value use specified markdown file(s) for converting to html. Supports file globbing patterns (needs to be quoted). [$MARK_FILES]
--compile-only show resulting HTML and don't update Confluence page content. (default: false) [$MARK_COMPILE_ONLY]
--dry-run resolve page and ancestry, show resulting HTML and exit. (default: false) [$MARK_DRY_RUN]
--edit-lock, -k lock page editing to current user only to prevent accidental manual edits over Confluence Web UI. (default: false) [$MARK_EDIT_LOCK]
--drop-h1, --h1_drop don't include the first H1 heading in Confluence output. (default: false) [$MARK_H1_DROP]
--strip-linebreaks, -L remove linebreaks inside of tags, to accomodate non-standard Confluence behavior (default: false) [$MARK_STRIP_LINEBREAK]
--title-from-h1, --h1_title extract page title from a leading H1 heading. If no H1 heading on a page exists, then title must be set in the page metadata. (default: false) [$MARK_H1_TITLE]
--title-append-generated-hash appends a short hash generated from the path of the page (space, parents, and title) to the title (default: false) [$MARK_TITLE_APPEND_GENERATED_HASH]
--minor-edit don't send notifications while updating Confluence page. (default: false) [$MARK_MINOR_EDIT]
--version-message value add a message to the page version, to explain the edit (default: "") [$MARK_VERSION_MESSAGE]
--color value display logs in color. Possible values: auto, never. (default: "auto") [$MARK_COLOR]
--debug enable debug logs. (default: false) [$MARK_DEBUG]
--trace enable trace logs. (default: false) [$MARK_TRACE]
--username value, -u value use specified username for updating Confluence page. [$MARK_USERNAME]
--password value, -p value use specified token for updating Confluence page. Specify - as password to read password from stdin, or your Personal access token. Username is not mandatory if personal access token is provided. For more info please see: https://developer.atlassian.com/server/confluence/confluence-server-rest-api/#authentication. [$MARK_PASSWORD]
--target-url value, -l value edit specified Confluence page. If -l is not specified, file should contain metadata (see above). [$MARK_TARGET_URL]
--base-url value, -b value, --base_url value base URL for Confluence. Alternative option for base_url config field. [$MARK_BASE_URL]
--config value, -c value use the specified configuration file. (default: System specific) [$MARK_CONFIG]
--ci run on CI mode. It won't fail if files are not found. (default: false) [$MARK_CI]
--space value use specified space key. If the space key is not specified, it must be set in the page metadata. [$MARK_SPACE]
--parents value A list containing the parents of the document separated by parents-delimiter (default: '/'). These will be prepended to the ones defined in the document itself. [$MARK_PARENTS]
--parents-delimiter value The delimiter used for the parents list (default: "/") [$MARK_PARENTS_DELIMITER]
--mermaid-provider value defines the mermaid provider to use. Supported options are: cloudscript, mermaid-go. (default: "cloudscript") [$MARK_MERMAID_PROVIDER]
--mermaid-scale value defines the scaling factor for mermaid renderings. (default: 1) [$MARK_MERMAID_SCALE]
--include-path value Path for shared includes, used as a fallback if the include doesn't exist in the current directory. [$MARK_INCLUDE_PATH]
--help, -h show help
--version, -v print the version
```

You can store user credentials in the configuration file, which should be
located in a system specific directory (or specified via `-c --config `) with the following format (TOML):

```toml
username = "your-email"
password = "password-or-api-key-for-confluence-cloud"
# If you are using Confluence Cloud add the /wiki suffix to base_url
base-url = "http://confluence.local"
title-from-h1 = true
drop-h1 = true
```

**NOTE**: Labels aren't supported when using `minor-edit`!

**NOTE**: The system specific locations are described in here:
.
Currently these are:
On Unix systems, it returns $XDG_CONFIG_HOME as specified by https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html if non-empty, else $HOME/.config. On Darwin, it returns $HOME/Library/Application Support. On Windows, it returns %AppData%. On Plan 9, it returns $home/lib.

## Tricks

### Continuous Integration

It's quite trivial to integrate Mark into a CI/CD system, here is an example with [Snake CI](https://snake-ci.com/)
in case of self-hosted Bitbucket Server / Data Center.

```yaml
stages:
- sync

Sync documentation:
stage: sync
only:
branches:
- main
image: kovetskiy/mark
commands:
- for file in $(find -type f -name '*.md'); do
echo "> Sync $file";
mark -u $MARK_USER -p $MARK_PASS -b $MARK_URL -f $file || exit 1;
echo;
done
```

In this example, I'm using the `kovetskiy/mark` image for creating a job container where the
repository with documentation will be cloned to. The following command finds all `*.md` files and runs mark against them one by one:

```bash
for file in $(find -type f -name '*.md'); do
echo "> Sync $file";
mark -u $MARK_USER -p $MARK_PASS -b $MARK_URL -f $file || exit 1;
echo;
done
```

The following directive tells the CI to run this particular job only if the changes are pushed into the
`main` branch. It means you can safely push your changes into feature branches without being afraid
that they automatically shown in Confluence, then go through the reviewal process and automatically
deploy them when PR got merged.

```yaml
only:
branches:
- main
```

### File Globbing

Rather than running `mark` multiple times, or looping through a list of files from `find`, you can use file globbing (i.e. wildcard patterns) to match files in subdirectories. For example:

```bash
mark -f "helpful_cmds/*.md"
```

You can also use `**` to get all files recursively.

```bash
mark -f "**/docs/*.md"
```

### Linting markdown

We recommend to lint your markdown files with [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2) before publishing them to confluence to catch any conversion errors early.

## Issues, Bugs & Contributions

I've started the project to solve my own problem and open sourced the solution so anyone who has a problem like me can solve it too.
I have no profits/sponsors from this projects which means I don't really prioritize working on this project in my free time.
I still check the issues and do code reviews for Pull Requests which means if you encounter a bug in
the program, you should not expect me to fix it as soon as possible, but I'll be very glad to
merge your own contributions into the project and release the new version.

I try to label all new issues so it's easy to find a bug or a feature request to fix/implement, if
you are willing to help with the project, you can use the following labels to find issues, just make
sure to reply in the issue to let everyone know you took the issue:

* [label:feature-request](https://github.com/kovetskiy/mark/issues?q=is%3Aissue+is%3Aopen+label%3Afeature-request)
* [label:bug](https://github.com/kovetskiy/mark/issues?q=is%3Aissue+is%3Aopen+label%3Abug)

## Contributors ✨

Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):



Manuel Rüger
Manuel Rüger

🚧 💻
Egor Kovetskiy
Egor Kovetskiy

🚧 💻
Nick Klauer
Nick Klauer

💻
Rolf Ahrenberg
Rolf Ahrenberg

💻
Charles Southerland
Charles Southerland

💻
Šarūnas Nejus
Šarūnas Nejus

💻
Alexey Baranov
Alexey Baranov

💻


Anthony Barbieri
Anthony Barbieri

💻
Devin Auclair
Devin Auclair

💻
Gezim Sejdiu
Gezim Sejdiu

💻
Josip Ćavar
Josip Ćavar

💻
Juho Saarinen
Juho Saarinen

💻
Luke Fritz
Luke Fritz

💻
Matt Radford
Matt Radford

💻


Planktonette
Planktonette

💻
Stefano Teodorani
Stefano Teodorani

💻
Tim Schrumpf
Tim Schrumpf

💻
Tyler Cole
Tyler Cole

💻
elgreco247
elgreco247

💻
emead-indeed
emead-indeed

💻
Will Hegedus
Will Hegedus

💻


Leandro Carneiro
Leandro Carneiro

💻
beeme1mr
beeme1mr

💻
Taldrain
Taldrain

💻
Hugo Cisneiros
Hugo Cisneiros

💻
jevfok
jevfok

💻
Mateus Miranda
Mateus Miranda

💻
Stephan Hradek
Stephan Hradek

💻


Dreampuf
Dreampuf

💻
Joel Andritsch
Joel Andritsch

💻
guoweis-outreach
guoweis-outreach

💻
klysunkin
klysunkin

💻
Florent Monbillard
Florent Monbillard

💻
Joey Freeland
Joey Freeland

💻
Noam Asor
Noam Asor

💻


Philipp
Philipp

💻
Pommier Vincent
Pommier Vincent

💻
Toru Kawaguchi
Toru Kawaguchi

💻
Will Gorman
Will Gorman

💻
Zackery Griesinger
Zackery Griesinger

💻
cc-chris
cc-chris

💻
datsickkunt
datsickkunt

💻


recrtl
recrtl

💻
Stanislav Seletskiy
Stanislav Seletskiy

💻

This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!