| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 |
- <script setup lang="ts">
- import { EditorBlock } from "@enesis/editor"
- import { ref } from "vue"
- const example = ref(`# Welcome to the Editor
- This is a **block-based markdown editor** built with Vue 3, TypeScript, and ProseMirror.
- > [!NOTE] Every paragraph is its own Block component.
- > Blocks are independent editors with their own state.
- Try editing any of the examples on the left.
- `)
- </script>
- <template>
- <UPage>
- <UPageHeader
- title="Editor"
- description="A block-based markdown editor with live preview, decorations, and extensible syntax."
- />
- <UPageBody>
- <div class="space-y-8">
- <section class="space-y-4">
- <h2 class="text-lg font-semibold text-highlighted">What is this?</h2>
- <p class="text-sm text-muted leading-relaxed">
- The editor is a headless engine — each <code class="text-xs font-mono px-1 py-0.5 rounded bg-elevated border border-default">Block</code> component manages a single paragraph with its own ProseMirror instance.
- Markdown syntax is rendered as rich decorations in live-preview mode: delimiters fade when blurred, syntax elements get contextual styling.
- </p>
- <div class="rounded-lg border border-default dark:bg-neutral-950/50 p-4">
- <EditorBlock
- v-model:content="example"
- :focused="true"
- cursor-position="end"
- marker-mode="always-visible"
- />
- </div>
- </section>
- <section class="space-y-4">
- <h2 class="text-lg font-semibold text-highlighted">Key Concepts</h2>
- <div class="grid gap-4 sm:grid-cols-2">
- <div class="rounded-lg border border-default p-4 space-y-2">
- <div class="flex items-center gap-2">
- <span class="i-lucide-layers size-4 text-primary shrink-0" />
- <h3 class="text-sm font-medium text-highlighted">Blocks</h3>
- </div>
- <p class="text-xs text-muted leading-relaxed">
- Documents are split into distinct Block components — each one is an independent editor.
- Blocks communicate through a shared document model.
- </p>
- </div>
- <div class="rounded-lg border border-default p-4 space-y-2">
- <div class="flex items-center gap-2">
- <span class="i-lucide-palette size-4 text-primary shrink-0" />
- <h3 class="text-sm font-medium text-highlighted">Decorations</h3>
- </div>
- <p class="text-xs text-muted leading-relaxed">
- Markdown syntax is rendered as ProseMirror decorations — styled spans overlaid on the source text.
- Delimiters animate in and out based on focus.
- </p>
- </div>
- <div class="rounded-lg border border-default p-4 space-y-2">
- <div class="flex items-center gap-2">
- <span class="i-lucide-parse size-4 text-primary shrink-0" />
- <h3 class="text-sm font-medium text-highlighted">Parsing Pipeline</h3>
- </div>
- <p class="text-xs text-muted leading-relaxed">
- A 3-stage pipeline classifies blocks, builds a Lezer AST, then extracts decorations.
- Everything runs in under 16ms per keystroke.
- </p>
- </div>
- <div class="rounded-lg border border-default p-4 space-y-2">
- <div class="flex items-center gap-2">
- <span class="i-lucide-plug size-4 text-primary shrink-0" />
- <h3 class="text-sm font-medium text-highlighted">Extensible</h3>
- </div>
- <p class="text-xs text-muted leading-relaxed">
- Inline and block rules are registered as factories. Add new syntax by creating a rule, registering it, and writing a test.
- </p>
- </div>
- </div>
- </section>
- <section class="space-y-4">
- <h2 class="text-lg font-semibold text-highlighted">Block API</h2>
- <div class="rounded-lg border border-default overflow-hidden">
- <table class="w-full text-sm">
- <thead>
- <tr class="border-b border-default bg-elevated">
- <th class="text-left px-4 py-2 font-medium text-muted text-xs uppercase tracking-wider">Prop</th>
- <th class="text-left px-4 py-2 font-medium text-muted text-xs uppercase tracking-wider">Type</th>
- <th class="text-left px-4 py-2 font-medium text-muted text-xs uppercase tracking-wider">Default</th>
- <th class="text-left px-4 py-2 font-medium text-muted text-xs uppercase tracking-wider">Description</th>
- </tr>
- </thead>
- <tbody>
- <tr class="border-b border-default"><td class="px-4 py-2 font-mono text-xs">content</td><td class="px-4 py-2 text-xs">string</td><td class="px-4 py-2 text-xs">—</td><td class="px-4 py-2 text-xs text-muted">Markdown content (v-model)</td></tr>
- <tr class="border-b border-default"><td class="px-4 py-2 font-mono text-xs">focused</td><td class="px-4 py-2 text-xs">boolean</td><td class="px-4 py-2 text-xs">false</td><td class="px-4 py-2 text-xs text-muted">Automatically focus the editor</td></tr>
- <tr class="border-b border-default"><td class="px-4 py-2 font-mono text-xs">cursorPosition</td><td class="px-4 py-2 text-xs">'start' | 'end'</td><td class="px-4 py-2 text-xs">—</td><td class="px-4 py-2 text-xs text-muted">Where to place cursor on focus</td></tr>
- <tr class="border-b border-default"><td class="px-4 py-2 font-mono text-xs">markerMode</td><td class="px-4 py-2 text-xs">'live-preview' | 'always-visible'</td><td class="px-4 py-2 text-xs">'always-visible'</td><td class="px-4 py-2 text-xs text-muted">How decorator markers behave</td></tr>
- <tr class="border-b border-default"><td class="px-4 py-2 font-mono text-xs">debug</td><td class="px-4 py-2 text-xs">string</td><td class="px-4 py-2 text-xs">—</td><td class="px-4 py-2 text-xs text-muted">Debug namespace filter</td></tr>
- </tbody>
- </table>
- </div>
- </section>
- <section class="space-y-4">
- <h2 class="text-lg font-semibold text-highlighted">Events</h2>
- <div class="rounded-lg border border-default overflow-hidden">
- <table class="w-full text-sm">
- <thead>
- <tr class="border-b border-default bg-elevated">
- <th class="text-left px-4 py-2 font-medium text-muted text-xs uppercase tracking-wider">Event</th>
- <th class="text-left px-4 py-2 font-medium text-muted text-xs uppercase tracking-wider">Payload</th>
- <th class="text-left px-4 py-2 font-medium text-muted text-xs uppercase tracking-wider">Description</th>
- </tr>
- </thead>
- <tbody>
- <tr class="border-b border-default"><td class="px-4 py-2 font-mono text-xs">change</td><td class="px-4 py-2 text-xs">string</td><td class="px-4 py-2 text-xs text-muted">Content changed</td></tr>
- <tr class="border-b border-default"><td class="px-4 py-2 font-mono text-xs">focus</td><td class="px-4 py-2 text-xs">void</td><td class="px-4 py-2 text-xs text-muted">Editor gained focus</td></tr>
- <tr class="border-b border-default"><td class="px-4 py-2 font-mono text-xs">blur</td><td class="px-4 py-2 text-xs">void</td><td class="px-4 py-2 text-xs text-muted">Editor lost focus</td></tr>
- <tr class="border-b border-default"><td class="px-4 py-2 font-mono text-xs">selection-change</td><td class="px-4 py-2 text-xs">{ from, to }</td><td class="px-4 py-2 text-xs text-muted">Cursor/selection moved</td></tr>
- <tr class="border-b border-default"><td class="px-4 py-2 font-mono text-xs">split</td><td class="px-4 py-2 text-xs">string</td><td class="px-4 py-2 text-xs text-muted">Block split on Enter</td></tr>
- <tr class="border-b border-default"><td class="px-4 py-2 font-mono text-xs">merge-previous</td><td class="px-4 py-2 text-xs">void</td><td class="px-4 py-2 text-xs text-muted">Backspace at start merges blocks</td></tr>
- <tr class="border-b border-default"><td class="px-4 py-2 font-mono text-xs">delete-if-empty</td><td class="px-4 py-2 text-xs">void</td><td class="px-4 py-2 text-xs text-muted">Backspace on empty block</td></tr>
- </tbody>
- </table>
- </div>
- </section>
- </div>
- </UPageBody>
- </UPage>
- </template>
|