A block markdown editor built for practical wisdom: Lezer-parsed, ProseMirror-rendered, structurally correct. It infers tasks, callouts, tables, and refs from what you type — and gets out of the way the moment you're focused on writing.
|
|
vor 4 Tagen | |
|---|---|---|
| .forgejo | vor 1 Woche | |
| apps | vor 4 Tagen | |
| packages | vor 4 Tagen | |
| .editorconfig | vor 1 Monat | |
| .envrc | vor 1 Monat | |
| .gitignore | vor 5 Tagen | |
| AGENTS.md | vor 4 Tagen | |
| README.md | vor 4 Tagen | |
| biome.json | vor 1 Woche | |
| devenv.lock | vor 1 Monat | |
| devenv.nix | vor 1 Monat | |
| devenv.yaml | vor 1 Monat | |
| package.json | vor 4 Tagen | |
| pnpm-lock.yaml | vor 4 Tagen | |
| pnpm-workspace.yaml | vor 1 Monat | |
| skills-lock.json | vor 1 Monat |
A high-performance block markdown editor built with Vue 3, TypeScript, ProseMirror, and the Lezer parsing framework.
Documents are structured as distinct, interactive Blocks rather than a single flat string. Each block is a self-contained ProseMirror editor instance that stores raw markdown as plain text — no AST conversion, no mark storage. A Lezer-based 3-stage parsing pipeline renders syntax as visual decorations in real time, enabling live-preview editing of Obsidian-style references, task states, callouts, headings, and inline formatting.
The editor is designed for the philosophy that plain text should stay plain text — formatting is purely decorative, never structural. This makes it ideal for note-taking apps, knowledge bases, and any tool that needs to edit rich markdown with programmatic access to the raw source.
Enesis is being built as a complete block-editing substrate:
| Phase | Feature | Status |
|---|---|---|---|
| 1 | Clean content model — raw markdown in/out, ProseMirror never parses formatting | Done |
| 2 | Keyboard & boundary behavior — Enter, Backspace, Tab, arrows, pattern triggers | Done |
| 3 | Focus & cursor control — programmatic focus, expose API | Done |
| 4 | Core decoration system — Lezer-based inline tokens, cursor-reveal, caching | Done |
| 5 | Block-level decorations — headings, blockquotes, tasks, callouts, properties | Done |
| 6 | Code & math node views — CM6 fenced code blocks, KaTeX LaTeX rendering | Done |
| 6.5 | Formatting handlers — toggle bold/italic/code/strike/highlight, insertLink, setHeading, toggleTask | Done |
| 7 | Reference & tag insertion handlers — insertPageRef, insertBlockRef, insertTag | Done |
| 8 | Reference interactivity — clickable [[page]], ((block)), #tag chips with preview | Planned |
| 9 | Asset handling — image drop, upload protocol, inline preview | Planned |
| 10 | Multi-block editing — shared toolbar, insertion zones, suggestion/mention menus, undo/redo | Done (shell + history) |
| 11 | WCAG 2.1 AA compliance, screen reader support, RTL, high contrast | Planned |
| 12 | History orchestration API across blocks | Planned |
| Post-v1 | Third-party decoration plugin system | Planned |
**bold** to <strong>. What you type is what's stored.@lezer/markdown AST walk with custom extensions for Obsidian-style elements.split, merge-previous, arrow-up-from-start, etc.) — no shared state.├── apps/
│ └── dev/ Docs-style development site (Vue 3 + Vite)
│ ├── src/
│ │ ├── components/
│ │ │ ├── LiveExample.vue Editable demo wrapper with source view
│ │ │ └── AppLogo.vue Enesis brand mark
│ │ ├── pages/
│ │ │ ├── index.vue Overview
│ │ │ ├── basic-editing.vue Headings, paragraphs, rules
│ │ │ ├── inline-marks.vue Bold, italic, code, strike, highlight
│ │ │ ├── tasks.vue Task states & priorities
│ │ │ ├── blockquotes.vue Blockquotes & callouts
│ │ │ ├── links.vue Markdown links
│ │ │ ├── refs-tags.vue Page refs, block refs, tags
│ │ │ ├── code-blocks.vue Fenced code blocks
│ │ │ ├── properties.vue Key::value properties & dates
│ │ │ └── math.vue Inline & display LaTeX
│ │ ├── App.vue Shell layout (Nuxt UI) with sidebar
│ │ ├── main.ts App bootstrap (10 routes)
│ │ └── style.css Tailwind v4 + Nuxt UI theme
│ ├── public/ Static assets
│ └── vite.config.ts Vite with Nuxt UI plugin
│
├── packages/
│ └── editor/ Core headless engine
│ ├── src/
│ │ ├── index.ts Public API (Block + Editor + EditorToolbar components)
│ │ ├── components/
│ │ │ ├── EditorBlock.vue Self-contained ProseMirror block editor
│ │ │ ├── Editor.vue Multi-block shell with insertion zones, undo/redo
│ │ │ ├── EditorInsertionZone.vue 32px hit target between blocks, hover-reveal
│ │ │ └── EditorToolbar.vue Shared toolbar bound to active block
│ │ ├── composables/
│ │ │ ├── useMarkdownDecorations.ts ProseMirror decoration plugin
│ │ │ ├── useBlockKeyboardHandlers.ts Keyboard handler (Enter, Backspace, arrows)
│ │ │ ├── useCodeBlockView.ts CM6 NodeView for fenced code blocks
│ │ │ ├── useMathBlockView.ts KaTeX NodeView for display math
│ │ │ ├── usePatternPlugin.ts Pattern detection ([[, ((, /, #)
│ │ │ ├── usePasteHandler.ts Multi-line paste split handler
│ │ │ └── useFocusRegistry.ts Cross-block focus management
│ │ └── lib/
│ │ ├── block-parser.ts splitMarkdownIntoBlocks / serializeBlocks
│ │ ├── content-model.ts Markdown ↔ ProseMirror conversion
│ │ ├── schema.ts Minimal ProseMirror schema
│ │ ├── auto-close-plugin.ts Auto-close for ```, **, _
│ │ ├── formatting.ts Formatting handlers (toggleBold, insertLink, etc.)
│ │ ├── operation-history.ts EditorOperation types + undo/redo stack
│ │ ├── theme.ts CSS-variable theme system with presets
│ │ ├── katex.ts Dynamic KaTeX import + render helpers
│ │ ├── logger.ts Namespace-based debug logger
│ │ ├── markdown-parser.ts Lezer parser configuration
│ │ ├── markdown-extensions.ts Custom Lezer extensions
│ │ └── markdown-rules/
│ │ ├── engine.ts MarkdownRuleEngine (2-stage pipeline)
│ │ ├── types.ts Shared type interfaces
│ │ ├── inline-rules.ts Inline syntax rules
│ │ ├── block-rules.ts Block syntax rules
│ │ └── block-classifier.ts First-line regex classifier (paste only)
│ ├── vite.config.ts Library build (ESM, tailwind, vue)
│ └── vitest.config.ts Test runner configuration
│
├── .forgejo/
│ └── workflows/
│ └── deploy.yml CI: build + deploy to Codeberg Pages
│
├── package.json Workspace root
├── pnpm-workspace.yaml pnpm workspace definition
├── AGENTS.md Project conventions for AI coding agents
└── biome.json Linting & formatting
pnpm install
pnpm dev # Start the dev sandbox at localhost:5173
pnpm test # Run editor unit tests (Vitest)
pnpm check # Lint & format check (Biome)
| Script | Description |
|---|---|
pnpm dev |
Start Vite dev server for @enesis/dev sandbox |
pnpm build |
Build @enesis/editor library (ESM + type declarations) |
pnpm test |
Run unit tests for @enesis/editor |
pnpm check |
Run Biome lint & format check across the workspace |
On push to master, a Forgejo Actions workflow builds the dev app and deploys it to Codeberg Pages at https://enesismd.codeberg.page/editor/.
| Layer | Technology |
|---|---|
| Framework | Vue 3 (Composition API, <script setup>) |
| Editor Engine | ProseMirror (view, state, model, transform, keymap, gapcursor) |
| Parsing | Lezer (@lezer/markdown + GFM + custom extensions) |
| Styling | Tailwind CSS v4 + Nuxt UI v4 |
| Type Safety | TypeScript (strict: true) |
| Testing | Vitest with jsdom |
| Formatting | Biome |
| Package Manager | pnpm workspaces |
| CI/CD | Forgejo Actions → Codeberg Pages |
MIT