{"id":14155470,"url":"https://github.com/davidmyersdev/ink-mde","last_synced_at":"2025-04-08T08:17:50.146Z","repository":{"id":37964893,"uuid":"352188463","full_name":"davidmyersdev/ink-mde","owner":"davidmyersdev","description":"A beautiful, modern, customizable Markdown editor powered by CodeMirror 6 and TypeScript","archived":false,"fork":false,"pushed_at":"2024-10-18T01:25:43.000Z","size":132177,"stargazers_count":232,"open_issues_count":19,"forks_count":16,"subscribers_count":6,"default_branch":"main","last_synced_at":"2024-10-30T00:45:24.364Z","etag":null,"topics":["codemirror","extensible","hacktoberfest","javascript","markdown","mde","plugins","typescript","vim","vue"],"latest_commit_sha":null,"homepage":"https://stackblitz.com/fork/github/davidmyersdev/ink-mde/tree/main/examples/template-ts?file=src/main.ts","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/davidmyersdev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"davidmyersdev","ko_fi":"davidmyersdev","open_collective":"davidmyersdev","patreon":"davidmyersdev"}},"created_at":"2021-03-27T22:12:55.000Z","updated_at":"2024-10-29T19:21:40.000Z","dependencies_parsed_at":"2024-03-06T16:10:27.385Z","dependency_job_id":"94d6a0d4-80ea-461d-8eb4-d5e0be1b0580","html_url":"https://github.com/davidmyersdev/ink-mde","commit_stats":{"total_commits":349,"total_committers":4,"mean_commits":87.25,"dds":"0.014326647564469885","last_synced_commit":"920c3eef5e6645899ef1583ecfc8a9de25d0e64a"},"previous_names":["voracious/ink-mde","writewithocto/hybrid-mde"],"tags_count":83,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidmyersdev%2Fink-mde","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidmyersdev%2Fink-mde/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidmyersdev%2Fink-mde/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidmyersdev%2Fink-mde/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davidmyersdev","download_url":"https://codeload.github.com/davidmyersdev/ink-mde/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247757026,"owners_count":20990906,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["codemirror","extensible","hacktoberfest","javascript","markdown","mde","plugins","typescript","vim","vue"],"created_at":"2024-08-17T08:03:25.346Z","updated_at":"2025-04-08T08:17:50.128Z","avatar_url":"https://github.com/davidmyersdev.png","language":"TypeScript","readme":"[![NPM Package](https://img.shields.io/npm/v/ink-mde?color=blue\u0026style=for-the-badge)](https://npmjs.com/package/ink-mde)\n[![License](https://img.shields.io/github/license/davidmyersdev/ink-mde?color=blue\u0026style=for-the-badge)](https://github.com/davidmyersdev/ink-mde/blob/main/LICENSE)\n[![Chat on Discord](https://img.shields.io/discord/776165182560403547?color=blue\u0026style=for-the-badge)](https://voracious.link/chat)\n\n# ink-mde\n\nA beautiful, modern, customizable Markdown editor powered by CodeMirror 6 and TypeScript. This is the editor that powers https://octo.app.\n\n![](screenshot.png)\n\n## Features\n\n- [x] Automatic, dark, or light themes (automatic by default)\n- [x] Hybrid plain-text Markdown rendering\n- [x] Supports GitHub Flavored Markdown ([an extension of CommonMark](https://github.github.com/gfm/#what-is-github-flavored-markdown-))\n- [x] Syntax highlighting for many common languages (in code blocks)\n- [x] Drag-and-drop or paste to upload files\n- [x] Inline Markdown image previews\n- [x] Configurable and stylable\n- [x] An optional formatting toolbar (great for mobile)\n- [x] Optionally enable Vim Mode\n- [x] Framework agnostic\n- [x] Vue wrapper (`ink-mde/vue` subpath export)\n- [x] Svelte wrapper (`ink-mde/svelte` subpath export)\n- [x] Supports Server-Side Rendering (SSR)\n- [x] Wrap a native `textarea` element with the `wrap` export\n- [x] Plugin API (experimental)\n\n## Getting Started\n\nWith your preferred package manager, add `ink-mde` to your project.\n\n```sh\n# npm\nnpm i ink-mde\n\n# pnpm\npnpm i ink-mde\n\n# yarn\nyarn add ink-mde\n```\n\n#### Import from a CDN\n\nThe officially supported CDN for `ink-mde` is [esm.sh](https://esm.sh). Visit [esm.sh/ink-mde](https://esm.sh/ink-mde) and you will be redirected to the latest version. The URL will look something like this.\n\n```\nhttps://esm.sh/ink-mde@0.22.0\n```\n\nThen, import `ink` from that URL in your project.\n\n```ts\nimport { ink } from 'https://esm.sh/ink-mde@0.22.0'\n```\n\n### Examples for `ink-mde`\n\nNext, import `ink-mde` and customize it to fit your needs.\n\n#### Minimal setup\n\nMount the component and start writing.\n\n```ts\n// ./examples/minimal.ts\nimport { ink } from 'ink-mde'\n\n// The only requirement is an HTML element.\nink(document.getElementById('editor')!)\n```\n\n##### Wrap a native `textarea` with `wrap`\n\nTo wrap a native `textarea` element, use the `wrap` export.\n\n```ts\nimport { wrap } from 'ink-mde'\n\nwrap(document.querySelector('textarea')!)\n```\n\n#### Track state changes with hooks\n\nTo sync the editor with your app's state, you can use the `afterUpdate` hook.\n\n```ts\n// ./examples/hooks.ts\nimport { defineOptions, ink } from 'ink-mde'\n\n// With hooks, you can keep your state in sync with the editor.\nconst state = { doc: '# Start with some text' }\n\n// Use defineOptions for automatic type hinting.\nconst options = defineOptions({\n  doc: state.doc,\n  hooks: {\n    afterUpdate: (doc: string) =\u003e {\n      state.doc = doc\n    },\n  },\n})\n\nconst editor = ink(document.getElementById('editor')!, options)\n\n// You can also update the editor directly.\neditor.update(state.doc)\n```\n\n#### Web Components\n\n```ts\n// ./examples/web-component.ts#L1-L16\nimport { ink } from 'ink-mde'\nimport { LitElement, html } from 'lit'\n\nclass InkMde extends LitElement {\n  firstUpdated() {\n    ink(this.renderRoot.querySelector('#editor')!, {\n      doc: '# Hello, World!',\n    })\n  }\n\n  render() {\n    return html`\u003cdiv id=\"editor\"\u003e\u003c/div\u003e`\n  }\n}\n\ncustomElements.define('ink-mde', InkMde)\n```\n\n### Examples for `ink-mde/vue`\n\nThe `ink-mde/vue` subpath exports a Vue 3 component.\n\n#### Minimal setup\n\n```vue\n\u003cscript lang=\"ts\" setup\u003e\nimport InkMde from 'ink-mde/vue'\nimport { ref } from 'vue'\n\nconst markdown = ref('# Hello, World!')\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n  \u003cInkMde v-model=\"markdown\" /\u003e\n\u003c/template\u003e\n```\n\n#### Custom Options\n\nThe Vue component forwards all options that `ink-mde` supports, and it uses a deep watcher to ensure your `options` are reactive.\n\n```vue\n\u003cscript lang=\"ts\" setup\u003e\nimport InkMde from 'ink-mde/vue'\nimport { reactive, ref } from 'vue'\n\nconst markdown = ref('# Hello, World!')\nconst options = reactive({\n  interface: {\n    appearance: 'dark',\n  },\n})\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n  \u003cinput v-model=\"options.interface.appearance\" type=\"radio\" value=\"dark\"\u003e dark\n  \u003cinput v-model=\"options.interface.appearance\" type=\"radio\" value=\"light\"\u003e light\n  \u003cInkMde v-model=\"markdown\" :options=\"options\" /\u003e\n\u003c/template\u003e\n```\n\n### Examples for `ink-mde/svelte`\n\nThe `ink-mde/svelte` subpath exports a Svelte component.\n\n#### Minimal setup\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n  import InkMde from 'ink-mde/svelte'\n\n  // doc\n  let value = '# Hello, world'\n\u003c/script\u003e\n\n\u003cInkMde\n  bind:value\n  options={{\n    interface: {\n      appearance: 'dark'\n    }\n  }}\n/\u003e\n```\n\n#### Reactive options and the editor instance\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n  import InkMde from 'ink-mde/svelte'\n  import type { Instance } from 'ink-mde'\n\n  // doc\n  let value = '# Hello, world'\n  // reactive option, if this change, the editor will be reconfigured.\n  let isDarkTheme = false\n\u003c/script\u003e\n\n\u003cinput type=\"checkbox\" bind:checked={isDarkTheme} name=\"isDarkTheme\" /\u003e\n\n\u003cInkMde\n  bind:value\n  options={{\n    interface: {\n      appearance: isDarkTheme ? 'dark' : 'light',\n    },\n  }}\n/\u003e\n```\n\n## Further customization\n\nThese are the default options, and any of them can be overridden when initializing (or reconfiguring) an instance of `ink-mde`.\n\n```ts\n// ./src/store.ts#L12-L66\nconst options = {\n  doc: '',\n  files: {\n    clipboard: false,\n    dragAndDrop: false,\n    handler: () =\u003e {},\n    injectMarkup: true,\n    types: ['image/*'],\n  },\n  hooks: {\n    afterUpdate: () =\u003e {},\n    beforeUpdate: () =\u003e {},\n  },\n  interface: {\n    appearance: InkValues.Appearance.Auto,\n    attribution: true,\n    autocomplete: false,\n    images: false,\n    lists: false,\n    readonly: false,\n    spellcheck: true,\n    toolbar: false,\n  },\n  katex: false,\n  keybindings: {\n    // Todo: Set these to false by default. https://codemirror.net/examples/tab\n    tab: true,\n    shiftTab: true,\n  },\n  lists: false,\n  placeholder: '',\n  plugins: [\n    katex(),\n  ],\n  readability: false,\n  search: true,\n  selections: [],\n  toolbar: {\n    bold: true,\n    code: true,\n    codeBlock: true,\n    heading: true,\n    image: true,\n    italic: true,\n    link: true,\n    list: true,\n    orderedList: true,\n    quote: true,\n    taskList: true,\n    upload: false,\n  },\n  // This value overrides both `tab` and `shiftTab` keybindings.\n  trapTab: undefined,\n  vim: false,\n}\n```\n\n### Plugins\n\nThe editor can be extended with custom grammars, completions, and more through the Plugin API. Examples coming soon.\n\n### Appearance\n\nMany aspects of the editor's appearance can be customized with CSS custom properties (aka CSS variables).\n\n#### General-purpose styles\n\n| CSS Custom Property    | CSS Property       | Default (Dark) | Override (Light) |\n| ----                   | ----               | ----           | ----             |\n| `--ink-border-radius`  | `border-radius`    | `0.25rem`      |                  |\n| `--ink-color`          | `color`            | `#fafafa`      | `#171717`        |\n| `--ink-font-family`    | `font-family`      | `sans-serif`   |                  |\n| `--ink-flex-direction` | `flex-direction`   | `column`       |                  |\n\n#### Block styles\n\nBlocks are used to provide a dynamic user experience. Examples of blocks are images, multiline code blocks, and the toolbar.\n\n| CSS Custom Property                     | CSS Property       | Default (Dark) | Override (Light) |\n| ----                                    | ----               | ----           | ----             |\n| `--ink-block-background-color`          | `background-color` | `#121212`      | `#f5f5f5`        |\n| `--ink-block-background-color-on-hover` | `background-color` | `#0f0f0f`      | `#e0e0e0`        |\n| `--ink-block-max-height`                | `max-height`       | `20rem`        |                  |\n| `--ink-block-padding`                   | `padding`          | `0.5rem`       |                  |\n\n#### Code styles\n\nThese styles are for code blocks and inline code.\n\n| CSS Custom Property           | CSS Property       | Default (Dark)                      | Override (Light) |\n| ----                          | ----               | ----                                | ----             |\n| `--ink-code-background-color` | `background-color` | `var(--ink-block-background-color)` |                  |\n| `--ink-code-color`            | `color`            | `inherit`                           |                  |\n| `--ink-code-font-family`      | `font-family`      | `'Monaco', Courier, monospace`      |                  |\n\n#### Syntax highlighting\n\nYou can customize the entire syntax theme too.\n\n| CSS Custom Property                           | CSS Property       | Default (Dark) | Override (Light) |\n| ----                                          | ----               | ----           | ----             |\n| `--ink-syntax-atom-color`                     | `color`            | `#d19a66`      |                  |\n| `--ink-syntax-comment-color`                  | `color`            | `#abb2bf`      |                  |\n| `--ink-syntax-emphasis-color`                 | `color`            | `inherit`      |                  |\n| `--ink-syntax-emphasis-font-style`            | `font-style`       | `italic`       |                  |\n| `--ink-syntax-heading-color`                  | `color`            | `#e06c75`      |                  |\n| `--ink-syntax-heading-font-size`              | `font-size`        | `1em`          |                  |\n| `--ink-syntax-heading-font-weight`            | `font-weight`      | `600`          |                  |\n| `--ink-syntax-heading1-color`                 | `color`            | `#e06c75`      |                  |\n| `--ink-syntax-heading1-font-size`             | `font-size`        | `1.6em`        |                  |\n| `--ink-syntax-heading1-font-weight`           | `font-weight`      | `600`          |                  |\n| `--ink-syntax-heading2-color`                 | `color`            | `#e06c75`      |                  |\n| `--ink-syntax-heading2-font-size`             | `font-size`        | `1.5em`        |                  |\n| `--ink-syntax-heading2-font-weight`           | `font-weight`      | `600`          |                  |\n| `--ink-syntax-heading3-color`                 | `color`            | `#e06c75`      |                  |\n| `--ink-syntax-heading3-font-size`             | `font-size`        | `1.4em`        |                  |\n| `--ink-syntax-heading3-font-weight`           | `font-weight`      | `600`          |                  |\n| `--ink-syntax-heading4-color`                 | `color`            | `#e06c75`      |                  |\n| `--ink-syntax-heading4-font-size`             | `font-size`        | `1.3em`        |                  |\n| `--ink-syntax-heading4-font-weight`           | `font-weight`      | `600`          |                  |\n| `--ink-syntax-heading5-color`                 | `color`            | `#e06c75`      |                  |\n| `--ink-syntax-heading5-font-size`             | `font-size`        | `1.2em`        |                  |\n| `--ink-syntax-heading5-font-weight`           | `font-weight`      | `600`          |                  |\n| `--ink-syntax-heading6-color`                 | `color`            | `#e06c75`      |                  |\n| `--ink-syntax-heading6-font-size`             | `font-size`        | `1.1em`        |                  |\n| `--ink-syntax-heading6-font-weight`           | `font-weight`      | `600`          |                  |\n| `--ink-syntax-keyword-color`                  | `color`            | `#c678dd`      |                  |\n| `--ink-syntax-link-color`                     | `color`            | `#96c0d8`      |                  |\n| `--ink-syntax-meta-color`                     | `color`            | `#abb2bf`      |                  |\n| `--ink-syntax-name-color`                     | `color`            | `#d19a66`      |                  |\n| `--ink-syntax-name-label-color`               | `color`            | `#abb2bf`      |                  |\n| `--ink-syntax-name-property-color`            | `color`            | `#96c0d8`      |                  |\n| `--ink-syntax-name-property-definition-color` | `color`            | `#e06c75`      |                  |\n| `--ink-syntax-name-variable-color`            | `color`            | `#e06c75`      |                  |\n| `--ink-syntax-name-variable-definition-color` | `color`            | `#e5c07b`      |                  |\n| `--ink-syntax-name-variable-local-color`      | `color`            | `#d19a66`      |                  |\n| `--ink-syntax-name-variable-special-color`    | `color`            | `inherit`      |                  |\n| `--ink-syntax-number-color`                   | `color`            | `#d19a66`      |                  |\n| `--ink-syntax-operator-color`                 | `color`            | `#96c0d8`      |                  |\n| `--ink-syntax-processing-instruction-color`   | `color`            | `#444444`      | `#bbbbbb`        |\n| `--ink-syntax-punctuation-color`              | `color`            | `#abb2bf`      |                  |\n| `--ink-syntax-strikethrough-color`            | `color`            | `inherit`      |                  |\n| `--ink-syntax-strikethrough-text-decoration`  | `text-decoration`  | `line-through` |                  |\n| `--ink-syntax-string-color`                   | `color`            | `#98c379`      |                  |\n| `--ink-syntax-string-special-color`           | `color`            | `inherit`      |                  |\n| `--ink-syntax-strong-color`                   | `color`            | `inherit`      |                  |\n| `--ink-syntax-strong-font-weight`             | `font-weight`      | `600`          |                  |\n| `--ink-syntax-url-color`                      | `color`            | `#aaaaaa`      | `#666666`        |\n\n## Support\n\nYour support is appreciated. Here are some ways you can help. ♥️\n\n### Leave the Attribution enabled\n\nThere 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.\n\n### Tell us what you think\n\nYour feedback is immensely important for building `ink-mde` into a library that we all love. Consider [starting a discussion](https://github.com/davidmyersdev/octo/discussions) under [Octo](https://github.com/davidmyersdev/octo) if you have a question or just want to chat about ideas!\n\n### Open a Pull Request\n\nIf you feel comfortable with [an existing issue](https://github.com/davidmyersdev/ink-mde/issues), please consider opening a Pull Request. I would love to work with you to get it merged!\n\n### Become a financial backer\n\n- [GitHub Sponsors](https://github.com/sponsors/davidmyersdev)\n- [Open Collective](https://opencollective.com/davidmyersdev)\n- [Patreon](https://patreon.com/davidmyersdev)\n- [Ko-Fi](https://ko-fi.com/davidmyersdev)\n- [Buy Me a Coffee](https://www.buymeacoffee.com/davidmyersdev)\n\n## A note about v0 releases\n\nSince `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](https://semver.org/#spec-item-4), but I do my best to make patch releases non-breaking.\n","funding_links":["https://github.com/sponsors/davidmyersdev","https://ko-fi.com/davidmyersdev","https://opencollective.com/davidmyersdev","https://patreon.com/davidmyersdev","https://www.buymeacoffee.com/davidmyersdev"],"categories":["TypeScript","vue"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidmyersdev%2Fink-mde","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavidmyersdev%2Fink-mde","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidmyersdev%2Fink-mde/lists"}