{"_id":"@alikmanukian/vue-markdown-editor","name":"@alikmanukian/vue-markdown-editor","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@alikmanukian/vue-markdown-editor","version":"1.0.0","description":"A Tiptap-based Markdown editor component for Vue 3","type":"module","main":"dist/index.cjs","module":"dist/index.es.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.es.js","require":"./dist/index.cjs"},"./styles":"./dist/vue-markdown-editor.css"},"scripts":{"dev":"vite","build":"vite build","typecheck":"vue-tsc --noEmit","test":"vitest","test:coverage":"vitest --coverage","test:ui":"vitest --ui"},"keywords":["vue","vue3","markdown","editor","tiptap","wysiwyg"],"author":{"name":"Alik Manukian"},"license":"MIT","peerDependencies":{"remixicon":">=4.0.0","vue":"^3.0.0"},"peerDependenciesMeta":{"remixicon":{"optional":false}},"dependencies":{"@tiptap/extension-link":"^3.0.0","@tiptap/markdown":"^3.0.0","@tiptap/starter-kit":"^3.0.0","@tiptap/vue-3":"^3.0.0"},"devDependencies":{"@vitejs/plugin-vue":"^6.0.0","@vue/test-utils":"^2.4.6","happy-dom":"^20.8.3","remixicon":"^4.9.1","typescript":"^5.6.0","vite":"^7.0.0","vite-plugin-dts":"^4.0.0","vitest":"^4.0.18","vue":"^3.5.0","vue-tsc":"^3.0.0"},"_id":"@alikmanukian/vue-markdown-editor@1.0.0","gitHead":"6c037bcb885b05616d2c976f187db89add474bac","_nodeVersion":"22.22.0","_npmVersion":"10.9.4","dist":{"integrity":"sha512-qhPWcgKlr3lv5PBZBNvy7tS9xtrJ2b45Pyx8R01MI2a0WhA2KMKHZHC3sjkdxhhhbUZGT89UeKuH+HgFXalCMQ==","shasum":"65c6479a9745935e0c02acc8f075cfa563236457","tarball":"https://registry.npmjs.org/@alikmanukian/vue-markdown-editor/-/vue-markdown-editor-1.0.0.tgz","fileCount":14,"unpackedSize":1064511,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIHcEaLumg8bsZLZtlAy9I6Gme7bGc9NzViAbUlsSUhIJAiBHrtvDXnPu/JR9pAme8biLHzKITefKY7jWCQ4x9a1XNQ=="}]},"_npmUser":{"name":"alikmanukian","email":"alikmanukian@gmail.com"},"directories":{},"maintainers":[{"name":"alikmanukian","email":"alikmanukian@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/vue-markdown-editor_1.0.0_1772806712603_0.22372586367058078"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-06T14:18:32.528Z","1.0.0":"2026-03-06T14:18:32.824Z","modified":"2026-03-06T14:18:33.005Z"},"maintainers":[{"name":"alikmanukian","email":"alikmanukian@gmail.com"}],"description":"A Tiptap-based Markdown editor component for Vue 3","keywords":["vue","vue3","markdown","editor","tiptap","wysiwyg"],"author":{"name":"Alik Manukian"},"license":"MIT","readme":"# @alikmanukian/vue-markdown-editor\n\nA lightweight, customizable Markdown editor component for Vue 3, built on top of [Tiptap](https://tiptap.dev/).\n\n- WYSIWYG editing with Markdown output\n- Configurable toolbar (bold, italic, strikethrough, lists, links, and more)\n- Default styles with full CSS variable theming — override anything without `!important`\n- Zero dependency on any UI framework (no shadcn, no Tailwind required)\n- Fully typed with TypeScript\n\n---\n\n## Installation\n\n```bash\nnpm install @alikmanukian/vue-markdown-editor remixicon\n```\n\n> **Why remixicon?** The toolbar uses [Remix Icons](https://remixicon.com/) for its buttons. It is listed as a peer dependency so you control how and where you import it — and you can reuse it across your project.\n\n---\n\n## Quick Start\n\n### 1. Import the styles\n\nIn your app's entry file (`main.ts` or equivalent):\n\n```ts\n// Remixicon — required for toolbar icons\nimport 'remixicon/fonts/remixicon.css';\n\n// Editor default styles\nimport '@alikmanukian/vue-markdown-editor/styles';\n```\n\n### 2. Use the component\n\n```vue\n<script setup lang=\"ts\">\nimport { ref } from 'vue';\nimport { MarkdownEditor } from '@alikmanukian/vue-markdown-editor';\n\nconst content = ref('Hello **world**!');\n</script>\n\n<template>\n  <MarkdownEditor v-model=\"content\" />\n</template>\n```\n\nThat's it. The editor binds to `v-model` and emits the current content as a Markdown string on every change.\n\n---\n\n## Props\n\n| Prop | Type | Default | Description |\n|---|---|---|---|\n| `modelValue` | `string` | `''` | The Markdown content (use with `v-model`) |\n| `toolbar` | `ToolbarAction[]` | See below | Which toolbar buttons to display and in what order |\n| `placeholder` | `string` | — | Placeholder text shown when the editor is empty |\n\n### Default toolbar\n\n```ts\n['bold', 'italic', 'strike', 'bulletList', 'orderedList']\n```\n\nThe link button is always appended at the end of the toolbar.\n\n### Available `ToolbarAction` values\n\n| Value | Description |\n|---|---|\n| `bold` | Bold |\n| `italic` | Italic |\n| `strike` | Strikethrough |\n| `bulletList` | Unordered list |\n| `orderedList` | Ordered list |\n| `blockquote` | Blockquote |\n| `code` | Inline code |\n| `codeBlock` | Code block |\n| `horizontalRule` | Horizontal rule |\n| `undo` | Undo |\n| `redo` | Redo |\n\n---\n\n## Events\n\n| Event | Payload | Description |\n|---|---|---|\n| `update:modelValue` | `string` | Emitted on every content change with the current Markdown string |\n\n---\n\n## Customizing Styles\n\n### Method 1 — CSS Custom Properties (recommended)\n\nAll visual aspects of the editor are controlled by CSS custom properties. Override them in your own stylesheet after importing the default styles:\n\n```css\n/* Apply to all editor instances */\n:root {\n  --vme-border-color: #a855f7;\n  --vme-focus-border-color: #9333ea;\n  --vme-focus-ring: rgba(147, 51, 234, 0.25);\n  --vme-border-radius: 10px;\n  --vme-content-min-height: 300px;\n}\n```\n\nOr scope it to a specific instance using the `.vme-editor` class:\n\n```css\n.my-form .vme-editor {\n  --vme-border-color: #f59e0b;\n  --vme-focus-border-color: #d97706;\n  --vme-border-radius: 0;\n}\n```\n\n### Method 2 — Replace default styles entirely\n\nSkip importing the default styles and write your own from scratch using the `vme-*` class names:\n\n```ts\n// main.ts — do NOT import the default styles\nimport 'remixicon/fonts/remixicon.css';\nimport './my-editor-styles.css'; // your own styles\n```\n\n```css\n/* my-editor-styles.css */\n.vme-editor { /* wrapper */ }\n.vme-toolbar { /* toolbar <menu> */ }\n.vme-toolbar-btn { /* toolbar button */ }\n.vme-toolbar-btn.is-active { /* active state */ }\n.vme-content { /* editor content wrapper */ }\n.vme-content .ProseMirror { /* the actual editable area */ }\n.vme-link-popover-wrapper { /* link button + popover wrapper */ }\n.vme-link-popover { /* link URL popover panel */ }\n.vme-link-input { /* URL <input> inside popover */ }\n.vme-link-confirm-btn { /* confirm button inside popover */ }\n.vme-link-remove-btn { /* remove link button */ }\n```\n\n### Full list of CSS custom properties\n\n| Property | Default | Description |\n|---|---|---|\n| `--vme-border-color` | `#d1d5db` | Editor border color |\n| `--vme-border-radius` | `6px` | Editor border radius |\n| `--vme-bg` | `transparent` | Editor background |\n| `--vme-shadow` | subtle | Editor box shadow |\n| `--vme-focus-border-color` | `#6366f1` | Border color on focus |\n| `--vme-focus-ring` | `rgba(99,102,241,.25)` | Focus ring color |\n| `--vme-toolbar-border-color` | `rgba(0,0,0,.12)` | Separator between toolbar and content |\n| `--vme-toolbar-padding` | `4px` | Toolbar inner padding |\n| `--vme-btn-size` | `32px` | Toolbar button size (width & height) |\n| `--vme-btn-radius` | `4px` | Toolbar button border radius |\n| `--vme-btn-hover-bg` | `rgba(0,0,0,.08)` | Button hover background |\n| `--vme-btn-active-bg` | `rgba(0,0,0,.15)` | Active/pressed button background |\n| `--vme-btn-icon-size` | `16px` | Icon font size inside buttons |\n| `--vme-content-padding` | `8px 12px` | Padding around the editable area |\n| `--vme-content-min-height` | `200px` | Minimum height of the editing area |\n| `--vme-font-family` | `inherit` | Editor font family |\n| `--vme-font-size` | `0.875rem` | Editor font size |\n| `--vme-line-height` | `1.6` | Editor line height |\n| `--vme-link-color` | `#6366f1` | Hyperlink color |\n| `--vme-list-padding` | `1.5em` | List indentation |\n| `--vme-popover-bg` | `#ffffff` | Link popover background |\n| `--vme-popover-border` | `#e5e7eb` | Link popover border color |\n| `--vme-popover-shadow` | subtle | Link popover shadow |\n| `--vme-popover-width` | `280px` | Link popover width |\n| `--vme-confirm-btn-bg` | `#6366f1` | Link confirm button background |\n| `--vme-confirm-btn-hover-bg` | `#4f46e5` | Link confirm button hover background |\n| `--vme-remove-btn-color` | `#ef4444` | Remove link button text color |\n\n---\n\n## Usage Examples\n\n### With Tailwind CSS\n\nThe component uses regular CSS class names, not Tailwind utilities, so it works alongside Tailwind without any conflicts. Override the design tokens in your CSS:\n\n```css\n/* resources/css/app.css */\n:root {\n  --vme-border-color: theme(colors.gray.300);\n  --vme-focus-border-color: theme(colors.violet.500);\n  --vme-focus-ring: theme(colors.violet.500 / 25%);\n}\n```\n\n### In a form (Laravel / Inertia)\n\n```vue\n<script setup lang=\"ts\">\nimport { useForm } from '@inertiajs/vue3';\nimport { MarkdownEditor } from '@alikmanukian/vue-markdown-editor';\n\nconst form = useForm({ body: '' });\n</script>\n\n<template>\n  <form @submit.prevent=\"form.post('/posts')\">\n    <MarkdownEditor v-model=\"form.body\" placeholder=\"Write your post...\" />\n    <p v-if=\"form.errors.body\" class=\"text-red-500\">{{ form.errors.body }}</p>\n    <button type=\"submit\">Publish</button>\n  </form>\n</template>\n```\n\n### Custom toolbar\n\n```vue\n<MarkdownEditor\n  v-model=\"content\"\n  :toolbar=\"['bold', 'italic', 'bulletList', 'orderedList', 'blockquote', 'undo', 'redo']\"\n/>\n```\n\n### Read-only display with `parseMarkdown`\n\nThe package does not export a render utility — for displaying stored Markdown as HTML, use [marked](https://marked.js.org/) + [DOMPurify](https://github.com/cure53/DOMPurify) directly:\n\n```ts\nimport DOMPurify from 'dompurify';\nimport { marked } from 'marked';\n\nfunction renderMarkdown(content: string): string {\n  const html = marked.parse(content, { async: false }) as string;\n  return DOMPurify.sanitize(html);\n}\n```\n\n---\n\n## Testing\n\nTests are written with [Vitest](https://vitest.dev/) and [Vue Test Utils](https://test-utils.vuejs.org/), using [happy-dom](https://github.com/nicedaycode/happy-dom) as the DOM environment.\n\n```bash\n# Run all tests once\nnpm test\n\n# Run in watch mode (re-runs on file changes)\nnpx vitest\n\n# Run with browser-like UI\nnpm run test:ui\n\n# Run with coverage report\nnpm run test:coverage\n```\n\n### Test structure\n\nTests live in `src/components/__tests__/`:\n\n| File | What it covers |\n|---|---|\n| `MarkdownEditor.test.ts` | Rendering, toolbar configuration, v-model binding, placeholder, markdown output |\n| `ToolbarButton.test.ts` | Icon mapping, active/inactive state, command execution |\n| `LinkToolbarButton.test.ts` | Popover open/close, keyboard handling, click outside, link active state |\n\n---\n\n## Development (live preview)\n\nClone or open the package directory and run:\n\n```bash\nnpm install\nnpm run dev\n```\n\nThis starts a Vite dev server at `http://localhost:5173` with a live preview of the component. Any changes to files in `src/` are hot-reloaded instantly.\n\nTo build the distributable package:\n\n```bash\nnpm run build\n```\n\nOutput is placed in the `dist/` folder.\n\n---\n\n## Local linking\n\nTo use the package in another local project before publishing it to npm:\n\n```bash\n# In the package directory\ncd /path/to/vue-markdown-editor\nnpm link\n\n# In your project\nnpm link @alikmanukian/vue-markdown-editor\n```\n\nOr reference it directly in your project's `package.json`:\n\n```json\n{\n  \"dependencies\": {\n    \"@alikmanukian/vue-markdown-editor\": \"file:/path/to/vue-markdown-editor\"\n  }\n}\n```\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-6e74339cd401b385492cc6b63e48dee9"}