Skip to content

Fologan/veloxmd

Repository files navigation

VeloxMD

In-place markdown editor for the web. Zero dependencies. ~2KB gzipped.

The text formats itself as you type — no split view, no separate preview pane. Markdown syntax markers stay in the DOM but fade to near-invisible, so what you see is what you get.

Live Demo

Alpha — API may change between minor versions.

Install

npm install veloxmd

Quick Start

Basic Editor

import { LiveEditor } from 'veloxmd'
import 'veloxmd/styles.css'

const editor = new LiveEditor(document.getElementById('editor'))
editor.setValue('# Hello **world**')

Full-Featured Editor with Toolbar

import { LiveEditorPlus } from 'veloxmd'
import 'veloxmd/styles.css'

const editor = new LiveEditorPlus(document.getElementById('editor'), {
  toolbar: true,
  onChange: (text) => console.log('Content changed:', text.length, 'chars'),
  placeholder: 'Start writing...',
})

editor.setValue('# Hello **world**')

Static Viewer (read-only)

import { LiveViewer } from 'veloxmd'
import 'veloxmd/styles.css'

const viewer = new LiveViewer(document.getElementById('preview'))
viewer.setValue('# Hello **world**')

Same CSS, same styling — but no contenteditable, no event listeners, no undo stack. One-pass render, zero runtime overhead.

View Modes

VeloxMD has three ways to display markdown:

Mode Class What you see
Source LiveEditorPlus Full editor with live formatting. Headings scale up, bold/italic render in place, and syntax markers (#, **, etc.) stay visible alongside the formatted text. The focused line brightens markers for clarity.
Hybrid LiveEditorPlus + setViewMode('hybrid') Same live formatting, but syntax markers collapse to zero-width on unfocused lines and expand with animation on focus — a clean reading experience that reveals markdown on demand.
Static LiveViewer Read-only rendered output. No syntax markers, no editing, no runtime overhead.

Source mode is what makes VeloxMD unique — it's not a plain-text editor with a preview pane. The text formats itself as you type while keeping every markdown character editable in place.

// Switch between source and hybrid at runtime
editor.setViewMode('hybrid')
editor.setViewMode('source')

How It Works

VeloxMD uses character parity — a design principle where every raw markdown character stays in the DOM. Syntax markers like #, **, *, ` are not removed; they're wrapped in <span class="syntax"> and dimmed with CSS opacity.

This means the flat character offset in your raw text always matches the flat offset in the DOM. Cursor save/restore after re-rendering becomes trivial — no complex position mapping needed. The entire cursor system is ~50 lines of code.

The editor uses contenteditable with full re-rendering on each keystroke. Because every character has a 1:1 DOM representation, this "naive" approach just works, and the codebase stays small.

Hybrid Mode

In hybrid mode, syntax spans on unfocused lines collapse to width: 0 via CSS transitions. The HybridController measures text widths using an offscreen canvas (measureText) so the CSS can animate between collapsed and expanded states. Click correction compensates for the layout shift — when you click a collapsed line, the controller maps the visual click position to the correct character offset in the expanded layout.

Markdown Support

Feature LiveEditor LiveEditorPlus
Headings (# to ######)
Bold, Italic, Bold-Italic
Strikethrough (~~)
Inline code
Links [text](url)
Images ![alt](url)
Blockquotes (nested)
Ordered & unordered lists
Code blocks (fenced)
Horizontal rules
Escape sequences
Tables
Task lists
Footnotes
Autolinks (<url>)
Reference links
HTML inline (<kbd>, <mark>, <u>)
Superscript / Subscript
Math ($...$)
Highlight (==..==)
<details> / <summary>
Hard breaks
HTML comments
Alt heading syntax (===, ---)
Image preview
Hybrid view mode

Toolbar

Enable a native formatting toolbar by passing toolbar: true in options. No external dependencies — all buttons are text/unicode with a single SVG for the link icon.

const editor = new LiveEditorPlus(container, { toolbar: true })

The toolbar includes: Undo/Redo, Headings (dropdown H1-H6), Bold, Italic, Underline, Strikethrough, Highlight, Inline Code, Link, Image, Table, Horizontal Rule, Bullet List, Ordered List, Task List, Blockquote, Code Block, Details/Summary, Superscript, Subscript, Math, and Footnote.

Smart Insertion

Toolbar buttons and keyboard shortcuts detect whether text is selected:

  • Text selected — wraps the selection (e.g. select "hello" + click Bold → **hello**)
  • No selection — inserts a placeholder with the text pre-selected for immediate typing

Keyboard Shortcuts

Shortcut Action
Ctrl+B Bold
Ctrl+I Italic
Ctrl+U Underline
Ctrl+E Inline code
Ctrl+K Link
Ctrl+Shift+X Strikethrough
Ctrl+Shift+K Code block
Ctrl+Shift+Q Blockquote
Ctrl+Shift+O Ordered list
Ctrl+Shift+U Unordered list
Ctrl+Shift+H Horizontal rule

Toolbar Theming

The toolbar uses its own set of CSS custom properties for easy customization:

.my-editor-container {
  --veloxmd-toolbar-bg: #ffffff;
  --veloxmd-toolbar-border: #e1e4e8;
  --veloxmd-toolbar-text: #24292e;
  --veloxmd-toolbar-button-hover: #f0f1f3;
  --veloxmd-toolbar-button-active: #e1e4e8;
  --veloxmd-toolbar-separator: #d1d5da;
  --veloxmd-toolbar-radius: 6px;
  --veloxmd-toolbar-dropdown-bg: #ffffff;
  --veloxmd-toolbar-dropdown-shadow: 0 2px 8px rgba(0,0,0,0.12);
}

The toolbar is responsive — buttons that overflow are automatically moved to a overflow menu.

Theming

VeloxMD uses CSS custom properties prefixed with --veloxmd-. Override them to match your app's design:

.my-editor-container {
  --veloxmd-bg: #1a1a2e;
  --veloxmd-text: #eaeaea;
  --veloxmd-text-bright: #ffffff;
  --veloxmd-text-muted: #888;
  --veloxmd-syntax: #555;
  --veloxmd-accent: #e94560;
  --veloxmd-green: #0f3460;
  --veloxmd-purple: #a855f7;
  --veloxmd-red: #ef4444;
  --veloxmd-orange: #f97316;
  --veloxmd-surface: #16213e;
  --veloxmd-surface-2: #1a1a2e;
  --veloxmd-border: #333;
  --veloxmd-border-bright: #444;
}

A built-in dark theme is available by setting data-theme="dark" on any ancestor element.

API

LiveEditor / LiveEditorPlus

new LiveEditor(container: HTMLElement, options?: EditorOptions)
new LiveEditorPlus(container: HTMLElement, options?: EditorOptions)

interface EditorOptions {
  onChange?: (text: string) => void  // fires on every content change
  placeholder?: string              // placeholder text when editor is empty
  toolbar?: boolean                 // show formatting toolbar (default: false)
}

editor.setValue(markdown: string): void
editor.getValue(): string
editor.setViewMode(mode: 'source' | 'hybrid'): void
editor.getViewMode(): 'source' | 'hybrid'
editor.onChange(callback: (text: string) => void): void
editor.insert(text: string): void                              // insert at cursor
editor.toggleInline(before: string, after: string, placeholder: string): void  // wrap selection or insert
editor.toggleBlock(prefix: string): void                       // toggle line prefix
editor.insertTemplate(template: string): void                  // insert multi-line template
editor.undo(): void
editor.redo(): void
editor.destroy(): void

LiveViewer

new LiveViewer(container: HTMLElement)

viewer.setValue(markdown: string): void
viewer.destroy(): void

Parsers

import { parseLiveDocument, parseLiveDocumentPlus } from 'veloxmd'

const lines = parseLiveDocument(['# Hello', '', '**Bold** text'])
// Returns LiveLine[] with block types and inline segments

Cursor Utilities

import { getFlatOffset, setFlatOffset } from 'veloxmd'

// Convert DOM position -> flat character offset
const offset = getFlatOffset(container, node, nodeOffset)

// Convert flat offset -> DOM position
const pos = setFlatOffset(container, 42)

License

MIT - Fologan

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors