Code Monkey home page Code Monkey logo

davidmyersdev / ink-mde Goto Github PK

View Code? Open in Web Editor NEW
183.0 5.0 12.0 129.08 MB

A beautiful, modern, customizable Markdown editor powered by CodeMirror 6 and TypeScript

Home Page: https://stackblitz.com/fork/github/davidmyersdev/ink-mde/tree/main/examples/template-ts?file=src/main.ts

License: MIT License

HTML 1.74% TypeScript 89.40% Vue 1.07% CSS 5.20% JavaScript 1.52% Svelte 1.08%
typescript markdown extensible javascript vim codemirror mde plugins hacktoberfest vue

ink-mde's Introduction

NPM Package License Chat on Discord

ink-mde

A beautiful, modern, customizable Markdown editor powered by CodeMirror 6 and TypeScript. This is the editor that powers https://octo.app.

Features

  • Automatic, dark, or light themes (automatic by default)
  • Hybrid plain-text Markdown rendering
  • Supports GitHub Flavored Markdown (an extension of CommonMark)
  • Syntax highlighting for many common languages (in code blocks)
  • Drag-and-drop or paste to upload files
  • Inline Markdown image previews
  • Configurable and stylable
  • An optional formatting toolbar (great for mobile)
  • Optionally enable Vim Mode
  • Framework agnostic
  • Vue wrapper (ink-mde/vue subpath export)
  • Svelte wrapper (ink-mde/svelte subpath export)
  • Supports Server-Side Rendering (SSR)
  • Wrap a native textarea element with the wrap export
  • Plugin API (experimental)

Getting Started

With your preferred package manager, add ink-mde to your project.

# npm
npm i ink-mde

# pnpm
pnpm i ink-mde

# yarn
yarn add ink-mde

Import from a CDN

The officially supported CDN for ink-mde is esm.sh. Visit esm.sh/ink-mde and you will be redirected to the latest version. The URL will look something like this.

https://esm.sh/[email protected]

Then, import ink from that URL in your project.

import { ink } from 'https://esm.sh/[email protected]'

Examples for ink-mde

Next, import ink-mde and customize it to fit your needs.

Minimal setup

Mount the component and start writing.

// ./examples/minimal.ts
import { ink } from 'ink-mde'

// The only requirement is an HTML element.
ink(document.getElementById('editor')!)
Wrap a native textarea with wrap

To wrap a native textarea element, use the wrap export.

import { wrap } from 'ink-mde'

wrap(document.querySelector('textarea')!)

Track state changes with hooks

To sync the editor with your app's state, you can use the afterUpdate hook.

// ./examples/hooks.ts
import { defineOptions, ink } from 'ink-mde'

// With hooks, you can keep your state in sync with the editor.
const state = { doc: '# Start with some text' }

// Use defineOptions for automatic type hinting.
const options = defineOptions({
  doc: state.doc,
  hooks: {
    afterUpdate: (doc: string) => {
      state.doc = doc
    },
  },
})

const editor = ink(document.getElementById('editor')!, options)

// You can also update the editor directly.
editor.update(state.doc)

Web Components

// ./examples/web-component.ts#L1-L16
import { ink } from 'ink-mde'
import { LitElement, html } from 'lit'

class InkMde extends LitElement {
  firstUpdated() {
    ink(this.renderRoot.querySelector('#editor')!, {
      doc: '# Hello, World!',
    })
  }

  render() {
    return html`<div id="editor"></div>`
  }
}

customElements.define('ink-mde', InkMde)

Examples for ink-mde/vue

The ink-mde/vue subpath exports a Vue 3 component.

Minimal setup

<script lang="ts" setup>
import InkMde from 'ink-mde/vue'
import { ref } from 'vue'

const markdown = ref('# Hello, World!')
</script>

<template>
  <InkMde v-model="markdown" />
</template>

Custom Options

The Vue component forwards all options that ink-mde supports, and it uses a deep watcher to ensure your options are reactive.

<script lang="ts" setup>
import InkMde from 'ink-mde/vue'
import { reactive, ref } from 'vue'

const markdown = ref('# Hello, World!')
const options = reactive({
  interface: {
    appearance: 'dark',
  },
})
</script>

<template>
  <input v-model="options.interface.appearance" type="radio" value="dark"> dark
  <input v-model="options.interface.appearance" type="radio" value="light"> light
  <InkMde v-model="markdown" :options="options" />
</template>

Examples for ink-mde/svelte

The ink-mde/svelte subpath exports a Svelte component.

Minimal setup

<script lang="ts">
  import InkMde from 'ink-mde/svelte'

  // doc
  let value = '# Hello, world'
</script>

<InkMde
  bind:value
  options={{
    interface: {
      appearance: 'dark'
    }
  }}
/>

Reactive options and the editor instance

<script lang="ts">
  import InkMde from 'ink-mde/svelte'
  import type { Instance } from 'ink-mde'

  // doc
  let value = '# Hello, world'
  // reactive option, if this change, the editor will be reconfigured.
  let isDarkTheme = false
</script>

<input type="checkbox" bind:checked={isDarkTheme} name="isDarkTheme" />

<InkMde
  bind:value
  options={{
    interface: {
      appearance: isDarkTheme ? 'dark' : 'light',
    },
  }}
/>

Further customization

These are the default options, and any of them can be overridden when initializing (or reconfiguring) an instance of ink-mde.

// ./src/store.ts#L12-L65
const options = {
  doc: '',
  files: {
    clipboard: false,
    dragAndDrop: false,
    handler: () => {},
    injectMarkup: true,
    types: ['image/*'],
  },
  hooks: {
    afterUpdate: () => {},
    beforeUpdate: () => {},
  },
  interface: {
    appearance: InkValues.Appearance.Auto,
    attribution: true,
    autocomplete: false,
    images: false,
    lists: false,
    readonly: false,
    spellcheck: true,
    toolbar: false,
  },
  katex: false,
  keybindings: {
    // Todo: Set these to false by default. https://codemirror.net/examples/tab
    tab: true,
    shiftTab: true,
  },
  placeholder: '',
  plugins: [
    katex(),
  ],
  readability: false,
  search: true,
  selections: [],
  toolbar: {
    bold: true,
    code: true,
    codeBlock: true,
    heading: true,
    image: true,
    italic: true,
    link: true,
    list: true,
    orderedList: true,
    quote: true,
    taskList: true,
    upload: false,
  },
  // This value overrides both `tab` and `shiftTab` keybindings.
  trapTab: undefined,
  vim: false,
}

Plugins

The editor can be extended with custom grammars, completions, and more through the Plugin API. Examples coming soon.

Appearance

Many aspects of the editor's appearance can be customized with CSS custom properties (aka CSS variables).

General-purpose styles

CSS Custom Property CSS Property Default (Dark) Override (Light)
--ink-border-radius border-radius 0.25rem
--ink-color color #fafafa #171717
--ink-font-family font-family sans-serif
--ink-flex-direction flex-direction column

Block styles

Blocks are used to provide a dynamic user experience. Examples of blocks are images, multiline code blocks, and the toolbar.

CSS Custom Property CSS Property Default (Dark) Override (Light)
--ink-block-background-color background-color #121212 #f5f5f5
--ink-block-background-color-on-hover background-color #0f0f0f #e0e0e0
--ink-block-max-height max-height 20rem
--ink-block-padding padding 0.5rem

Code styles

These styles are for code blocks and inline code.

CSS Custom Property CSS Property Default (Dark) Override (Light)
--ink-code-background-color background-color var(--ink-block-background-color)
--ink-code-color color inherit
--ink-code-font-family font-family 'Monaco', Courier, monospace

Syntax highlighting

You can customize the entire syntax theme too.

CSS Custom Property CSS Property Default (Dark) Override (Light)
--ink-syntax-atom-color color #d19a66
--ink-syntax-comment-color color #abb2bf
--ink-syntax-emphasis-color color inherit
--ink-syntax-emphasis-font-style font-style italic
--ink-syntax-heading-color color #e06c75
--ink-syntax-heading-font-size font-size 1em
--ink-syntax-heading-font-weight font-weight 600
--ink-syntax-heading1-color color #e06c75
--ink-syntax-heading1-font-size font-size 1.6em
--ink-syntax-heading1-font-weight font-weight 600
--ink-syntax-heading2-color color #e06c75
--ink-syntax-heading2-font-size font-size 1.5em
--ink-syntax-heading2-font-weight font-weight 600
--ink-syntax-heading3-color color #e06c75
--ink-syntax-heading3-font-size font-size 1.4em
--ink-syntax-heading3-font-weight font-weight 600
--ink-syntax-heading4-color color #e06c75
--ink-syntax-heading4-font-size font-size 1.3em
--ink-syntax-heading4-font-weight font-weight 600
--ink-syntax-heading5-color color #e06c75
--ink-syntax-heading5-font-size font-size 1.2em
--ink-syntax-heading5-font-weight font-weight 600
--ink-syntax-heading6-color color #e06c75
--ink-syntax-heading6-font-size font-size 1.1em
--ink-syntax-heading6-font-weight font-weight 600
--ink-syntax-keyword-color color #c678dd
--ink-syntax-link-color color #96c0d8
--ink-syntax-meta-color color #abb2bf
--ink-syntax-name-color color #d19a66
--ink-syntax-name-label-color color #abb2bf
--ink-syntax-name-property-color color #96c0d8
--ink-syntax-name-property-definition-color color #e06c75
--ink-syntax-name-variable-color color #e06c75
--ink-syntax-name-variable-definition-color color #e5c07b
--ink-syntax-name-variable-local-color color #d19a66
--ink-syntax-name-variable-special-color color inherit
--ink-syntax-number-color color #d19a66
--ink-syntax-operator-color color #96c0d8
--ink-syntax-processing-instruction-color color #444444 #bbbbbb
--ink-syntax-punctuation-color color #abb2bf
--ink-syntax-strikethrough-color color inherit
--ink-syntax-strikethrough-text-decoration text-decoration line-through
--ink-syntax-string-color color #98c379
--ink-syntax-string-special-color color inherit
--ink-syntax-strong-color color inherit
--ink-syntax-strong-font-weight font-weight 600
--ink-syntax-url-color color #aaaaaa #666666

Support

Your support is appreciated. Here are some ways you can help. ♥️

Leave the Attribution enabled

There is a small powered by ink-mde attribution in the bottom-right corner of all editor instances by default. Being a free, MIT-licensed library under independent development, that attribution helps to increase awareness of this project and my work in general.

Tell us what you think

Your feedback is immensely important for building ink-mde into a library that we all love. Consider starting a discussion under Octo if you have a question or just want to chat about ideas!

Open a Pull Request

If you feel comfortable with an existing issue, please consider opening a Pull Request. I would love to work with you to get it merged!

Become a financial backer

A note about v0 releases

Since ink-mde is a v0 project, you should consider minor version increments to be breaking changes. Semantic Versioning considers all v0 releases to be breaking, but I do my best to make patch releases non-breaking.

ink-mde's People

Contributors

collinherber avatar davidmyersdev avatar demetrius-mp 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  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  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  avatar  avatar

ink-mde's Issues

Support smart punctuation

When GFM is involved, a common parsing/formatting option is to allow support for smart punctuation, so it would be nice to be able to configure Ink to use smart quotes/punctuation for writing and editing markdown, updating as punctuation, quotes, or matching quote-pairs are found. For example:

  • "Hello, world!" they exclaimed. ➡️ “Hello, world!” they exclaimed.
  • That's a smart looking apostrophe. ➡️ That’s a smart looking apostrophe.
  • The 's' is silent. ➡️ The ‘s’ is silent.
  • 2020--2022 was a bad time. ➡️ 2020–2022 was a bad time.
  • I wondered---out loud---if they were being serious. ➡️ I wondered—out loud—if they were being serious.
  • You don't say... ➡️ You don’t say…
    • I think this one isn't usually included, but it's the other "smart punctuation" I miss that other editors or modern operating systems do for you 😅

Inline syntax highlighting is broken in the latest version(s)

It looks like recent changes to reduce the final package size have omitted whatever code/files were necessary for the inline Markdown syntax highlighting. I recently updated the version of Ink that I use and pointed at https://cdn.jsdelivr.net/npm/[email protected]/+esm and just noticed that the inline Markdown styles no longer work. I had to go back a few versions to find one that jsdelivr would serve (other recent versions run into the issue where they exceed 100MB); it looks like https://cdn.jsdelivr.net/npm/[email protected]/+esm is the most recent working version that is also small enough for jsdelivr to serve up.

svelte wrapper

i dont know if you have any interest on a svelte wrapper for this component, but i have created the following ink-mde-svelte package. Also published on npm.

if you want to move it to this repo, or move it to your account, i'd be happy to help.

Bug: Toolbar Actions Don't Work Expectedly When No Selection

Summary

When you use the toolbar to perform an action with nothing selected, the default should not be to select whatever has changed, but rather to put the cursor into a position which can edit that change.

For example, when I click Bold on the toolbar, it should automatically put double asterisks around my current cursor position. Rather, it creates 4 asterisks and selects all of them.

Add SSR support

To better support the migration of Octo to Nuxt 3, Ink should support being included in an SSR context. Full SSR support (server-side initialization) will happen later.

Add KaTeX support

I feel like this would be a really nice feature and I use it daily, at least maybe highlighting $ and $$ signs would be nice.

(This is obv just an enhancement that I would rly appreciate)

Consolidate UI tooling

The UI layer is currently split between Svelte and Sinuous. While I like Sinuous more than Svelte (for the interoperability with TS and non-UI modules), I think SolidJS is actually going to be a great replacement for them both.

Input lag on Safari

My college noticed a severe input lag on Safari 17.1.2 on macOS Sonoma 14.1.2 spec: Macbook Pro 2018 15" i7, 32 GB

It doesn't happen on Chrome on the same computer.

We have also tested the editor https://octo.app/docs/new with more or less the same result.

I guess it's because recalculating styles each keypress is quite expensive. Could we introduce debounce time before recalculating the styles?

custom/external formatting toolbar?

Hello!
Great work on ink-mde!

I'm currently integrating it into a React Native project, which loads ink-mde inside of a webview.

Since this will be used on mobile devices, I'd like to create a custom formatting toolbar as an InputAccessoryView on the keyboard.

This makes me wonder: how can/could I externally control formatting options in ink-mde? Is it currently possible to do that?

Add markdown toolbar

Hi!

This is an idea, actually.

In my experience, markdown is simple but scary for newbies. Toolbars help new users to learn markdown. You could go one step ahead and hide the markup on element blur, as they are drawing a lot of attention right now.

Thank you!

cant install ink-mde when using svelte 4

most likely due to

"svelte": "^3.0.0",

what do you think of instead of providing a component for the framework (such as svelte or vue), just providing a "copy-pastable" component (since it is very likely that a wrapper will fit in a single component, no matter the framework), which could be shown in the readme, for example? one clear benefit is that version conflicts would never occur.

i believe this is a plausible approach, given that the core of the package itself (i.e. the ink export from the package) works in native js.

another approach to avoid version conflicts would be to provide the framework-specific components in a separate package, such as @ink-mde/svelte or @ink-mde/vue. however, probably not worth the effort

Is there a way to use CodeMirror's `placeholder` extension?

Hello! First off, thank you so much for Ink. I've been wanting to plug a markdown editor into my personal Rails site for a while and was dreading it, but I couldn't believe how easy it was to put Ink into an import map and just plug it in.

I was wondering if there's an out-of-the-box way to use CodeMirror 6's placeholder extension to get a… well, placeholder into my editor 😄 It's easy enough to provide an initial doc but it would be even better to get true placeholder functionality so that it isn't considered content and is automatically overridden as soon as content is typed in.

Update checkbox styles

Checkboxes should match the container background color (or be closer in color) with a border that provides a high contrast with the background.

How Do I get an instance object of codemirror from AwaitableInstance

Hi there, great project you've built here.

I created an ink instance using the following method:

const editor = ink(document.getElementById('editor')!, options)

But how can I obtain the object of the codemirror editor?
For example, I need to call the cm.getDoc().getCursor() method of codemirror to obtain the current cursor position, etc. to use the https://github.com/susisu/mte-kernel provided Table tools

It needs to implement the following interfaces:
image

So can you tell me how to obtain the instance of codemirror from AwaitableInstance, or is there any other way,look forward your prompt reply.♥️

Allow a text element to be supplied in constructor

For better progressive enhancement, a text element should be an acceptable "parent" element. It might require some investigation, but a naive approach, for now, would be to replace that element with a normal container (such as a div).

Ability to Get Codemirror Instance

Is there any way I can get the Codemirror instance from Ink MDE?

I'm likely missing something, but if there isn't this feature already, this will be very useful. Thank you for the awesome project!

Allow opt-out/opt-in for language specific syntax library

We use Ink-MDE in a Vue.js app as a Markdown editor for its wonderful wysiwyg-syntax-highlighting. When building the project (yarn build), I noticed more than forty dependencies, most of which are specific to a certain programming language. At the end, yarn printed a warning:

(!) Some chunks are larger than 500 KiB after minification. Consider:

I would like to be able to restrict the language-dependencies bundled into my app to the one I need, Markdown in our case. As this relates to bundle size, I imagine other users, too, to appreciate the possibility to reduce the supported languages to only the subset needed. Could this be a future feature—or is there already a way to do this?

Editor broken when used with Shadow DOM

It appears the editor is broken when you attempt to create a Web Component out of it that leverages the Shadow DOM. Is there a know way to make Ink compatible with this use?

Bottom border for headers

First of all thanks for this awesome editor. This is already the 3rd I'm trying out, best so far.

I've tried to add bottom borders to the h1 and h2 elements inside the editor:

image

So I've checked, they have

<div class="cm-line"><span class="ͼ14 ͼ1i">#</span><span class="ͼ14"> Header 1</span></div>
<div class="cm-line"><span class="ͼ15 ͼ1i">##</span><span class="ͼ15"> Header 2</span></div>
<div class="cm-line"><span class="ͼ16 ͼ1i">###</span><span class="ͼ16"> Header 3</span></div>

So there is the css I've come up with:

.cm-line:has(.ͼ14), .cm-line:has(.ͼ15) {
  padding-bottom: .3em;
  border-bottom: 1px solid #eaecef;
}

Afterward I've noticed that after deploying the code the class name changed:

<div class="cm-line"><span class="ͼl ͼy">##</span><span class="ͼl"> Header 1</span></div>

Is there a way to add the bottom borders?

List indentation with tabs

I often use outlines to organize my notes. One little thing is that I can easily create a list, but when I want a sublist, things get difficult.

As a user, I expect that I can hit tab to indent a list item, but this doesn't work. Instead, I have to backspace to delete the new list item, then tab a few times, and then start a new list item. This works and new items will be indented, but this is a bit of a drag and disrupts the note-taking flow.

Ideally, when it hit tab on a list, it indents the whole line, not the text inline. It would also be nice if the sub items had a different bullet style, but that isn't super important today.

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.