npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

agitcms

v2.0.6

Published

A hackable headless CMS for markdown blogs

Downloads

10

Readme

image /eɪdʒɪt/

Agit CMS is a simple web frontend interface for markdown-based static site generators, like Hugo and Jekyll.
Write markdown blog posts the hackable way, get rid of your itch points.

frontmatter

Install

npm install -g agitcms

To start: agitcms
To change port: AGIT_FRONTEND=3001 agitcms

Features

  • vertical split style markdown editor
  • type-aware frontmatter editor
  • custom editor snippet/toolbar/keymap
  • custom frontmatter language(yaml/toml) & delimiters
  • Integrated Terminal
  • mathjax rendering: $ a + b = c$
  • image pasting into the editor

Agit CMS tries to be a hackable headless CMS for developers.

Comparison with other CMS

| | Agit CMS | Netlify CMS | |---|---|---| | Where CMS runs | Runs on your computer | Runs on website's /admin (which is a security issue) | | Installation | easy(npm i) | hard | image pasting | O | X | mathjax rendering | O | △(requires additional setup) | Integrated Terminal | O | X | customizability | O | X

Quick Start

Adding a new site

Open Agit CMS and click NEW button.

Type your site’s name, and root folder path of the static site. These two configurations cannot be changed afterwards unless you modify ~/.agitcms/config.json directly.

Once you add a new site, click on it, then Agit CMS provides a file explorer where you can navigate through folders and files thas have .md extension.

Click PIN button at the very top to pin a folder or a file to left sidebar for easy access.

Integrated Terminal

Press Control + @ to open up a integrated terminal.

Try starting a server of your static sites generator.

In hugo: hugo server
In jekyll: jekyll server

Now your preview server is running on your host!
Try modifying a post as you wish, with a nice markdown&frontmatter editor!

Usage

Markdown Editor

default keymaps

Visit here and here for keymaps available by default.

custom keymaps

Want to add your own keymaps?
Try creating your own plugin!

mathjax

Type

$$
a + b = c
$$

to represent block math.
Type

$$ a + b = c $$

or

$ a + b = c $

or 

$
a + b = c
$

to represent inline math.

image pasting

Agit CMS allows Ctrl + v to paste images into the editor.

This feature is useful when you want to paste a screen capture without looking for its file name.

Set media folder path and media public path to enable this feature.

A couple of things to note

  • Images larger than 1MB cannot be pasted.
  • At this version, all images pasted into the editor are saved in year-month-date-time.png format

Media

![](${media_public_path}/example.png) written in the markdown editor panel (left side) gets parsed into <img src="http://localhost:${random_port}/${media_public_path}/example.png"> in the preview panel at the right side . (${media_public_path} is a media public path)

Agit CMS starts local http server on ${random_port} at media folder path in the filesystem to serve media contents.
HTTP server automatically closes when Agit CMS is closed.

There are a couple of things to note

  • On Windows, you might have to tell windows to allow Agit CMS to start HTTP server when a warning is shown.
  • Image placed in the same folder as the markdown post file is in cannot be previewed at this version.

To quickly get the image path you want to insert into the editor, Agit CMS has a file explorer shortcut button Media for it.
It opens media folder path, and the selected image's public path will be copied into your clipboard.
This way, you can easily type ![](${ctrl + v}) (where ${ctrl + v} means pressing ctrl + v) to insert the image.

Plugins

The markdown editor of Agit CMS (Codemirror) can be customized by creating your own plugins.

Agit CMS evaluate javascript files in ~/.agitcms/plugins as plugins when boot, so you can place your own javascript file here to create a plugin. Name of the file does not matter.

Creating your own plugin requires a bit of knowledge of codemirror. If you don't know any, Examples is helpful.

There are two types of plugins.

  • Toolbar Item: manually invoked
  • TransactionFilter: automatically invoked

Toolbar Item

To create Toolbar Item plugin, create a new instance of ToolbarItem class and provide a valid config as a first argument.

If you don't want your plugin to show up in the toolbar, set config.initialChar to empty. That way you can only call the plugin via config.keyAlias

See table plugin for an exmaple.

Transaction Filter

Every time editor updates its document, this type of plugins, i.e., Transaction Filter plugin is called. Unlike ToolbarItem plugin, Transaction Filter does not modify,

To create Transaction Filter plugin, create a new instance of Transaction Filter class and provide a valid config as a first argument.

Examples

table plugin

A plugin that inserts markdown table syntax when clicked on the toolbar, or when ctrl + shift + t is pressed.

//put this in ~/.agitcms/plugins
new ToolbarItem({
  initialChar: "T",
  keyAlias: "Ctrl-T",
  run: (editorView) => {
    const from =
      editorView.state.selection.ranges[editorView.state.selection.mainIndex]
        .to;
    const insert = "|  |  |\n|---|---|\n";

    editorView.dispatch({
      changes: { from, insert },
      selection: { anchor: from + 2 }, //puts cursor to from+2
    });
  },
});
shortcode snippet plugin

This is a plugin that works as a snippet. If you are using Hugo for example, you might want to add shortcode snippets.

The plugin below inserts {{</*alert `\n\n` [info] */>}} to editor and sets cursor at position between two \n.
Also note that this plugin is activated for sites that has name "0xsuk's blog". (Name of the site is the one you set in the home)

const sitesToEnablePlugin = ["0xsuk's blog"];
new ToolbarItem({
  initialChar: "A",
  weight: 5,
  run: (editorView) => {
    const from =
      editorView.state.selection.ranges[editorView.state.selection.mainIndex]
        .to;
    const insert = "{{</*alert `\n\n` info*/>}}";

    editorView.dispatch({
      changes: { from, insert },
      selection: { anchor: from + 11 },
    });

    editorView.focus();
  },
  isActive: (siteConfig) => {
    if (sitesToEnablePlugin.includes(siteConfig.name)) {
      return true;
    }
    return false;
  },
});
autoclosebracket plugin
function handleAutoclose(transactionSpecList, transaction) {
  return function forBracket(opening, closing) {
    transaction.changes.iterChanges((fromA, _, fromB, toB) => {
      const newChars = transaction.newDoc.sliceString(fromB, toB);
      if (newChars === opening) {
        transactionSpecList.push({
          changes: { from: fromA, insert: opening + closing },
          selection: { anchor: fromA + 1 },
          filter: false, //do not filter transaction for this update to prevent unintended behaviour
        });
      }
      if (newChars === closing) {
        //if the next char is also closing bracket, move cursor right
        const nextChar = transaction.startState.sliceDoc(fromA, fromA + 1);
        if (nextChar === closing) {
          transactionSpecList.push({
            selection: { anchor: fromA + 1 },
            filter: false,
          });
        }
      }
    });
  };
}
new TransactionFilter({
  fn: (transaction) => {
    const transactionSpecList = [];
    const forBracket = handleAutoclose(transactionSpecList, transaction);
    forBracket("[", "]");
    forBracket("(", ")");

    return transactionSpecList;
  },
});
japanese keymap
new TransactionFilter({
  map: new Map([
    ["#", "#"], //mapping japanese # to english #
    [" ", " "], //mapping japanese space to english space
  ]),
});

Integrated Terminal

Press Ctrl + @ to open/hide.
Press ctrl+d to kill a process. If a process is killed, integrated terminal automatically closes.

Click + to add an instance.

If you navigate to Home while integrated terminal is open, all instances will be inaccesible, but still be running in the background.

Global settings

Navigate to Home (start page), and click on Settings in GLOBAL section of the sidebar.

Open Integrated Terminal with Ctrl+@

Disable this to prevent Agit CMS from opening an integrated terminal when ctrl+@ is captured.

Site settings

Go to one of site you added, and click on Settings in SITE section of the sidebar.

Frontmatter language

Define what language you use to represent frontmatter in markdown posts.

Default: yaml

Frontmatter delimiters

Define what delimiters you use to wrap frontmatter.
If you want to parse frontmatter in toml like below, you want to set frontmatter delimiters to +++, and frontmatter language to toml.

+++
title = example
+++
# Heading of the article
...

Default: ---

Frontmatter template

This one is optional but recommended if you want to parse frontmatter with correct types, or if you want to create new posts by CREATE NEW button of the Agit CMS file explorer.

Frontmatter template specifies what type each property of frontmatter is attributed to.

This information is used when Agit CMS parses markdown and generates a type-aware frontmatter editor, or when Agit CMS creates a new post with default frontmatter values.

example

Let's take a frontmatter below for example.

---
title: Configuration
date: '2022-07-03T17:52:46+09:00'
draft: false
tags: ["React.js", "Web Dev"]
---

When Agit CMS parses frontmatter and generates a type-aware frontmatter editor, it tries to find a type for each property of frontmatter.

If you set date property of frontmatter to be a type of Date for instance, Agit CMS provides a date picker in the frontmatter editor.
If you set draft to be a type of Bool, Agit CMS provides a boolean toggler in the frontmatter editor.

Supported types

| type | | | --- | --- | | Text | plain text (ex. title: Configuration)| | List of Text | list of text (ex. tags: ["React.js", "Web Dev"]) | | Multiline Text | text with multiple lines | | Date | date in ISO 8601 format (ex. date: '2022-07-03T17:52:46+09:00') | | Bool | true or false (ex. draft: false) | | Nest | property that contains child properties |

Media Folder Path

Specify where you store media (image, GIF and so on) in your file system.

Media Public Path

Specify the url path media content is accesible from.

example

Suppose media folder path is /home/mysite/static/uploads, and /home/mysite/static/uploads/example.png's url is https://mywebsite.com/contents/images/example.png.

Then media public path should be /contents/images.

API Reference

siteConfig

| property | desc | |---|---| | name | name of the site | | key | unique id | | path | the root path of the site | | frontmatterLanguage | frontmatter language (yaml/toml) | | frontmatterDelimiter | frontmatter delimiter | | media.staticPath | static path (filesystem path) of the media folder | | media.publicPath | public path (url path) of the media contents | | pinnedDirs | array of pinned folders and files | | frontmatter | frontmatter template |

class Plugin

| property/method | desc | type | default | |---|---|---|---| | constructor({isActive}) | create new instance | | isActive | Plugin is active or not | bool | (siteConfig) => bool | true |

class ToolbarItem

subclass of Plugin | property/method | desc | |---|---| | constructor(config) | create new instance |

config

| property | desc | type | default | |---|---|---|---| | initialChar | single character that represents item in the toolbar | string | | | tooltip | description of the tool | string | | | weight | The more weight, the latter the item appears in the toolbar | number | | | keyAlias | key to invoke the plugin. To learn more about key notation, visit https://codemirror.net/docs/ref/#view.KeyBinding | string | | | run | action to perform. editorView holds information of the editor, and siteConfig holds configuration of the site. stateModule is a codemirror's state module, which has access to all the classes/constants exported from https://codemirror.net/docs/ref/#state. In most cases, you want to call editorView.dispatch to perform action on editor. | (editorView, siteConfig, stateModule) => void | () => alert("No action is registered for this toolbar item"). | | isActive | = Plugin.isActive

class TransactionFilter

subclass of Plugin | property/method | desc | |---|---| | constructor(config) | create new instance |

config

| property | desc | type | default | |---|---|---|---| | map | map key to another key | Map | new Map() | | fn | function to perform. Use this field to perform complicated job that you can't with map. | (transaction) => transaction | | | isActive | = Plugin.isActive |

Bugs and Questions

If you want to report a bug or have a question, create a new issue. Don't forget to label it!

Development

Agit CMS is built with React.js, Typescript, webpack, Material UI, and CodeMirror

environment setup

Install: git clone [email protected]:0xsuk/agitcms.git && npm i
Start: npm run dev

changelog

From version 2.0.0, Agit CMS became a web interface instead of a desktop app. This way you can use your favorite chrome extension like Grammarly.

TODO

  • [ ] markdown editor - custom markdown rendering
  • [ ] markdown editor - auto saving config
  • [ ] markdown editor - Table of contents