Skip to main content

Barra de herramientas, vista previa en vivo y WYSIWYG en el área de texto que ya tienes

Última actualización:

This translation is older than the English page and may be missing recent changes.

Read the English version

Un editor de rebajas que mejora su área de texto en lugar de reemplazarla, para que el envío de formularios, la validación y los campos obligatorios sigan funcionando. Modos WYSIWYG y Markdown simple, vista previa en vivo, buscar y reemplazar, compatibilidad con RTL, modo oscuro. Funciona de forma independiente con Django, Laravel, Rails, Node.js, PHP y cualquier pila.

No se requiere Frutjam ni Tailwind. Los estilos están agrupados, por lo que el editor se integra en cualquier proyecto.

Instalación

NPM (paquetes: Vite, webpack, Rollup, etc.)

bash
npm install markdown-text-editor
javascript
1
2
import MarkdownEditor from 'markdown-text-editor';
new MarkdownEditor('#markdown-editor');

Las definiciones de TypeScript se incluyen con el paquete, por lo que las opciones, las entradas de la barra de herramientas y las formas variables se verifican y se completan automáticamente sin necesidad de instalación adicional.

Los tipos también son importables, lo cual es importante siempre que sus opciones no estén escritas en línea en el sitio de la llamada. TypeScript solo busca claves desconocidas en un objeto literal pasado directamente, por lo que una configuración mantenida en su propia variable tiene una clave mal escrita que se ignora silenciosamente. Al anotarlo se devuelve el cheque.

typescript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import MarkdownEditor, { type MarkdownEditorOptions, type ToolbarEntry } from 'markdown-text-editor';

// Without the annotation this compiles, the editor renders with the default
// toolbar, and nothing tells you why.
const config: MarkdownEditorOptions = {
    placeholder: 'Write...',
    toolbr: ['bold', 'preview'],   // Error: did you mean to write 'toolbar'?
};

// Building a toolbar conditionally
const toolbar: ToolbarEntry[] = ['bold', 'italic'];
if (canUpload) toolbar.push({ image: { fileInput: { uploadUrl: '/api/upload' } } });
toolbar.push('preview');

Lo mismo se aplica a un componente contenedor, donde MarkdownEditorOptions es lo que reenvías en lugar de any, por lo que quien utilice su envoltorio se quedará con la comprobación.

typescript
1
2
3
4
interface EditorProps {
    value: string;
    options?: MarkdownEditorOptions;
}

CDN: módulo ES

html
1
2
3
4
<script type="module">
  import MarkdownEditor from 'https://cdn.jsdelivr.net/npm/markdown-text-editor/+esm';
  new MarkdownEditor('#markdown-editor');
</script>

CDN: etiqueta de secuencia de comandos global (IIFE)

No es necesario importar: MarkdownEditor está disponible como una variable global automáticamente.

html
1
2
3
4
5
6
7
8
9
<form action="/api/save" method="POST">
  <textarea id="markdown-editor" name="content"># Hello World</textarea>
  <button type="submit">Save Content</button>
</form>

<script src="https://cdn.jsdelivr.net/npm/markdown-text-editor"></script>
<script>
  new MarkdownEditor('#markdown-editor');
</script>

Demostración del editor de rebajas

Inicio rápido

Pase un objeto de opciones para personalizar el editor. Todas las opciones son opcionales: omita cualquiera para usar el valor predeterminado.

javascript
1
2
3
4
const editor = new MarkdownEditor('#markdown-editor', {
  placeholder: 'Write your markdown...',
  toolbar: ['heading', 'bold', 'italic', 'strikethrough', 'ul', 'ol', 'checklist', 'blockquote', 'link', 'preview'],
});

La filosofía: "los nativos primero"

La mayoría de los editores rompen el flujo de trabajo web estándar. MarkdownEditor lo acepta. Debido a que se encuentra directamente encima de un <textarea>, no es necesario que aprenda una nueva forma de manejar los datos.

  • No se necesita enlace de datos: Funciona con <form method="POST"> fuera de la caja
  • Acceso estándar: Uso document.getElementById('editor').value como una entrada normal.
  • Agnóstico del backend: Funciona con cualquier backend (Python, Node.js, PHP, etc.) como un campo de formulario normal.

MarkdownEditor frente a EasyMDE/SimpleMDE

La mayoría de los editores de rebajas de JavaScript (EasyMDE, SimpleMDE, editores basados ​​en CodeMirror) ocultan su <textarea> y edite una copia, escribiendo el contenido cuando se envíe el formulario. Eso funciona hasta que algo más necesita el valor: un required El campo en el que el navegador no puede centrarse bloquea el envío por completo, y .value o FormData leer antes de enviar devuelve una cadena vacía, lo que rompe las protecciones de htmx, Turbo, autoguardado y cambios no guardados. MarkdownEditor es diferente. Le da estilo al área de texto que ya tienes y la deja como el campo en el que escribes, para que el valor sea correcto en todo momento.

CaracterísticaEditor de rebajasFácilMDE/SimpleMDE
Área de texto nativa preservada✅❌ Oculto, editado como copia
Funciona con required campos✅❌ El navegador bloquea el envío
.value corregir antes de enviar✅❌ Vacío hasta que se envíe el formulario
Se serializa con FormData, htmx, Turbo✅❌ Necesita la API propia del editor
Modo híbrido WYSIWYG✅❌
Buscar y reemplazar incorporado✅❌
soporte RTL✅ Construido enA través de CodeMirror direction opción
Controladores de eventos en línea (CSP)✅ NingunoAlguno
Modo oscuro/temática✅Limitado
Tamaño del paquete, comprimido53KB107KB (JS+CSS)

Integración del marco

Porque MarkdownEditor preserva el formato nativo. <textarea>, se integra con todos los marcos de backend sin ningún código adicional. Su servidor recibe el contenido de rebajas exactamente como lo haría desde cualquier campo de formulario estándar. Para React y Vue se necesitan algunas líneas, que se tratan al final de esta sección.

Django

Añadir un class a su widget de área de texto e inicialice el editor: request.POST['content'] Funciona sin pasos adicionales.

python
1
2
3
4
5
6
7
8
# forms.py
class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['content']
        widgets = {
            'content': forms.Textarea(attrs={'class': 'markdown-editor'}),
        }
javascript
1
2
new MarkdownEditor('.markdown-editor');
// request.POST['content'] contains the markdown on submit

Laravel

Usar f.text_area con una clase: $request->input('content') recibe la rebaja directamente.

html
1
2
<textarea name="content" class="markdown-editor">{{ old('content') }}</textarea>
<script>new MarkdownEditor('.markdown-editor');</script>

Rubí sobre rieles

Funciona con form_with fuera de la caja: params[:content] contiene la rebaja. Para Turbo Drive, utilice turbo:load en lugar de DOMContentLoaded.

javascript
1
2
3
4
5
6
document.addEventListener('turbo:load', () => {
    document.querySelectorAll('.markdown-editor:not([data-mde-init])').forEach(el => {
        el.setAttribute('data-mde-init', 'true');
        new MarkdownEditor(el);
    });
});

Nodo.js/Expreso

req.body.content recibe la rebaja al enviar el formulario: sin paso de sincronización, sin extracción personalizada.

html
1
2
3
<textarea name="content" class="markdown-editor"></textarea>
<script src="https://cdn.jsdelivr.net/npm/markdown-text-editor"></script>
<script>new MarkdownEditor('.markdown-editor');</script>

PHP

$_POST['content'] funciona exactamente como con cualquier área de texto estándar: colóquelo y el manejo de su formulario existente no requerirá cambios.

html
1
2
3
4
5
6
<form method="POST" action="save.php">
  <textarea name="content" class="markdown-editor"></textarea>
  <button type="submit">Save</button>
</form>
<script src="https://cdn.jsdelivr.net/npm/markdown-text-editor"></script>
<script>new MarkdownEditor('.markdown-editor');</script>

Reaccionar

Crea el editor en un efecto y destrúyelo al desmontarlo. Usar defaultValue en vez de value: el editor escribe directamente en el área de texto, por lo que un enlace controlado sobrescribiría lo que el usuario está escribiendo.

javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import { useEffect, useRef } from 'react';
import MarkdownEditor from 'markdown-text-editor';

function MarkdownField({ name, defaultValue = '', onChange }) {
    const ref = useRef(null);

    useEffect(() => {
        const editor = new MarkdownEditor(ref.current, { onChange });
        return () => editor.destroy();
    }, []);

    return &lt;textarea ref={ref} name={name} defaultValue={defaultValue} /&gt;;
}

Regresando editor.destroy() El efecto también cubre StrictMode, que ejecuta efectos dos veces en desarrollo y, de lo contrario, dejaría dos editores en un área de texto. La matriz de dependencia vacía es deliberada: las opciones se leen una vez cuando se crea el editor, por lo que volver a ejecutar el efecto lo eliminaría y lo reconstruiría con cada cambio.

vista

Misma idea. Establezca el valor inicial una vez en onMounted y no atar :value, o cada pulsación de tecla emitida a través de v-model sobrescribiría el área de texto.

javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
&lt;script setup&gt;
import { ref, onMounted, onBeforeUnmount } from 'vue';
import MarkdownEditor from 'markdown-text-editor';

const el = ref(null);
const props = defineProps({ modelValue: { type: String, default: '' } });
const emit = defineEmits(['update:modelValue']);
let editor = null;

onMounted(() => {
    el.value.value = props.modelValue;
    editor = new MarkdownEditor(el.value, {
        onChange: value =&gt; emit('update:modelValue', value)
    });
});

onBeforeUnmount(() =&gt; editor?.destroy());
&lt;/script&gt;

&lt;template&gt;&lt;textarea ref="el"&gt;&lt;/textarea&gt;&lt;/template&gt;

Utilizado como <MarkdownField v-model="content" />. En forma simple puedes omitir v-model por completo y lea el valor del área de texto al enviar, como con cualquier otro marco.

Configuración

Puede personalizar completamente el comportamiento y la interfaz del editor pasando un options objeto. Si omite una opción, se utiliza el valor predeterminado.

Las opciones se leen una vez, cuando se construye el editor. Cambiarlos posteriormente no tiene ningún efecto: llame destroy() y cree un nuevo editor en su lugar.

PropiedadTipoPor defectoObjetivo
modestring'plain'Establece la vista inicial. Utilice híbrido para una experiencia WYSIWYG o simple para sintaxis sin formato.
placeholderstring'Write...'Texto que se muestra cuando el editor está vacío.
toolbararray[...]Define qué herramientas aparecen y en qué orden.
footerfalso | objetotodo visibleControla la barra de estado que se muestra debajo del editor. Empezar a false para ocultarlo por completo o pasar un objeto para alternar estadísticas individuales.
themestringinheritedEstablece explícitamente el tema del editor (claro, oscuro, snowberry, darkberry). Si se omite, el editor hereda data-theme del elemento ancestro más cercano o del <textarea> sí mismo.
minHeightnumber200Altura mínima en píxeles a la que se reducirá el editor cuando el contenido sea corto. Se empareja con maxHeight para establecer el rango de crecimiento automático.
maxHeightnumber500Altura máxima en píxeles hasta la que el editor puede crecer en modo no de pantalla completa. Una vez que el contenido excede esta altura, aparece una barra de desplazamiento dentro del editor. El editor también tiene un controlador de arrastre para que los usuarios puedan cambiar su tamaño manualmente más allá de este límite.
rendererfunctionmarkedReemplaza el analizador de rebajas utilizado para la vista previa. Recibe la cadena de rebajas y debe devolver una cadena HTML.
sanitizerfunctionDOMPurifyReemplaza el desinfectante HTML. Recibe el HTML renderizado y debe devolver el HTML seguro para mostrarlo.
onChangefunctionundefinedLa devolución de llamada se activa con cada cambio de contenido: escritura, acciones de la barra de herramientas, deshacer/rehacer y continuación de la lista. Recibe la cadena de rebajas actual como único argumento.
labelsobjectundefinedTraducciones para la propia interfaz del editor, codificadas por la cadena en inglés. Todo lo que omitas permanecerá en inglés.

🛠 Personalización de la barra de herramientas

La barra de herramientas es modular. Puede crear una experiencia mínima o un paquete de energía con todas las funciones modificando la matriz.

Herramientas disponibles

CategoríaTeclas de herramientas
Tipografíaheading, bold, italic, strikethrough, blockquote
Lizaul (bullet), ol (numbered), checklist
Códigocode (en línea), codeblock (bloque cercado)
Insertoshr (regla horizontal), table (plantilla de tabla)
Medios de comunicaciónlink, image
Ediciónundo, redo, indent, outdent
Vistapreview
Plantillas{ variables: [...] }, configurado en línea

Referencia de herramientas

HerramientaDescripción
headingAbre un menú desplegable para seleccionar el nivel de título H1–H6
boldHabilita el formato de texto en negrita.
italicHabilita el formato de texto en cursiva.
strikethroughPermite tachar texto.
ol(Lista ordenada): convierte texto a un formato de lista numerada.
ul(Lista desordenada): convierte el texto en una lista con viñetas.
checklistAgrega casillas de verificación a su texto, lo que lo hace ideal para tareas, listas de tareas pendientes o seguimiento del estado de finalización.
blockquoteResalte el texto citado o enfatizado.
codeEnvuelve el texto seleccionado con comillas invertidas individuales para el código en línea. Al hacer clic nuevamente se eliminan las comillas invertidas.
codeblockEnvuelve el texto seleccionado en un bloque de código delimitado con triple acento grave. Al hacer clic nuevamente se eliminan las vallas.
hrInserta un --- regla horizontal en la posición del cursor en su propia línea.
tableInserta una plantilla de tabla de rebajas inicial de 2x3 en la posición del cursor.
imageLe permite insertar imágenes mediante la sintaxis de rebajas.
linkLe permite agregar hipervínculos a su texto.
undoPara revertir los últimos cambios.
redoPara volver a aplicar los últimos cambios deshechos.
indentPara aumentar el nivel de sangría.
outdentPara disminuir el nivel de sangría.
previewAlterna una vista previa en pantalla completa, una al lado de la otra. Se puede hacer clic en las casillas de verificación en el panel de vista previa y actualizan la fuente de rebajas al instante. Presione Escape para salir de la pantalla completa. Si un encabezado fijo cubre el editor en pantalla completa, consulte --mte-fullscreen-z-index.
💡 Consejos de implementación:
  • Reordenación: los botones aparecen en el orden exacto en que los enumera en la matriz
  • Eliminar: Simplemente omita cualquier clave (como image) de la matriz para desactivar esa característica por completo para el usuario
  • Respaldo nativo: si no proporcionas un placeholder en JS, el complemento utilizará automáticamente el placeholder atributo de su HTML <textarea>

🧩 Herramientas personalizadas

Agregue un botón propio en la barra de herramientas sin bifurcar. Descríbelo y colócalo en el toolbar matriz, en la posición que desee.

javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
new MarkdownEditor('#markdown-editor', {
    toolbar: ['bold', 'italic', {
        custom: {
            title: 'Callout',
            icon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20">…</svg>',
            action(editor) {
                editor.insertText('> [!NOTE]\n> Something worth noting');
            }
        }
    }, 'preview']
});

El title se convierte en la información sobre herramientas y la clase CSS del botón, por lo que a esta se le puede aplicar estilo como .callout-btn, exactamente igual que las herramientas integradas.

Varias herramientas

Añade tantos como quieras. Cada uno es su propia entrada en la matriz, por lo que los coloca individualmente y pueden ubicarse entre las herramientas integradas en lugar de agruparse en un extremo.

javascript
1
2
3
4
5
6
7
toolbar: [
    'bold', 'italic',
    { custom: { title: 'Callout',   icon: svg1, action: insertCallout } },
    'ul', 'ol',
    { custom: { title: 'Signature', icon: svg2, action: insertSignature } },
    'preview'
]

Dale a cada uno un distintivo title. Dos herramientas que comparten un título compartirían una clase CSS, y una herramienta sin una recurre a .custom-btn.

Opciones

PropiedadTipoObjetivo
titlestringRequerido. Texto de información sobre herramientas y el origen de la clase CSS del botón. Traducible a través de labels.
actionfunctionRequerido. Recibe (editor, event). El evento de clic se transmite para que una herramienta que abre un cuadro de diálogo pueda entregárselo. MarkdownEditor.modal.
iconstringSVG en línea para la cara del botón.
shortcutstringUn atajo de teclado como Ctrl+Shift+K. Se agrega a la información sobre herramientas y tiene prioridad sobre una función integrada que usa las mismas teclas.

Una herramienta sin action se omite con una advertencia de la consola en lugar de romper la barra de herramientas. No muestra ningún botón y su shortcut, si lo declaró, tampoco está registrado.

Trabajando con el contenido

Usar editor.insertText() en lugar de escribir usted mismo en el área de texto. Reemplaza la selección, restaura el foco, mueve el cursor, se desplaza, vuelve a renderizar la vista previa y activa onChange. Deshacer sigue funcionando porque la edición sigue la ruta normal.

javascript
1
2
3
4
5
6
7
8
action(editor) {
    const textarea = editor.usertextarea;
    const selected = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
    const wrapped = `==${selected || 'highlight'}==`;

    // (text, where the caret lands, characters to leave outside the selection)
    editor.insertText(wrapped, wrapped.length, 0);
}

Atajos

javascript
1
2
3
4
5
6
{ custom: {
    title: 'Highlight',
    icon: '<svg>…</svg>',
    shortcut: 'Ctrl+Shift+H',
    action: (editor) => editor.insertText('====', 2, 2)
}}

La información sobre herramientas se convierte en "Resaltar (Ctrl+Shift+H)". Los accesos directos a herramientas se verifican antes que los integrados, por lo que una herramienta personalizada puede asumir deliberadamente una combinación que el editor manejaría de otro modo.

Herramientas que necesitan su propio marcado.

Un menú desplegable o un cuadro de diálogo no se pueden describir de forma declarativa, así que escriba una clase en su lugar. Extender MarkdownEditor.Tool, colocar this.button en el constructor e implementar applySyntax().

javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class EmbedTool extends MarkdownEditor.Tool {
    constructor(editor) {
        super(editor, 'Embed video');        // tooltip, and .embed-video-btn
        this.shortcut = 'Ctrl+Shift+E';      // optional
        this.button = this.createButton('<svg>…</svg>');
    }

    applySyntax(event) {
        const dialog = MarkdownEditor.modal(event, 'fj:max-w-sm', `
            <input type="url" class="embed-url fj:me-input fj:w-full">
            <button type="button" class="embed-go fj:me-btn fj:me-btn-sm">Insert</button>
        `, 'Embed');

        dialog.querySelector('.embed-go').addEventListener('click', () => {
            const url = dialog.querySelector('.embed-url').value.trim();
            if (url) this.editor.insertText(`\n<iframe src="${url}"></iframe>\n`);
            dialog.close();
        });
    }

    destroy() {
        // Called by editor.destroy(). Release anything you registered here.
    }
}

new MarkdownEditor('#markdown-editor', { toolbar: ['bold', EmbedTool, 'preview'] });

Pasar configuración con { tool: EmbedTool, config: {...} }, que llega como segundo argumento del constructor. Configuración this.button a null no representa nada, lo cual es útil para una herramienta que solo se aplica en algunas configuraciones.

Una cosa para mirar: Toolllamadas al constructor createButton(), por lo que cualquier cosa que necesite anular debe provenir de this.editor, no desde un campo que usted asigna después super().

MarkdownEditor.modal es el cuadro de diálogo que utilizan las herramientas integradas de enlaces e imágenes, por lo que una herramienta suya que recopila entradas se parece al resto del editor en lugar de inventar su propia ventana emergente. Devuelve el <dialog> elemento, ya abierto, que consulta para sus campos y cierra con dialog.close().

javascript
1
2
// (event, size classes, body HTML, aria label)
const dialog = MarkdownEditor.modal(event, 'fj:max-w-sm', '…', 'Embed');

Pasa el event su applySyntax() recibió. El asistente lo lee para encontrar de qué editor proviene el clic, por lo que el cuadro de diálogo se agrega dentro del contenedor de ese editor y retoma su tema. Reemplaza cualquier cuadro de diálogo que ya esté abierto en ese editor y se elimina del DOM cuando se cierra, por lo que no hay nada que limpiar.

Renderizando la rebaja usted mismo

Si su herramienta incorpora contenido renderizado, un cuerpo de información sobre herramientas, por ejemplo, editor.renderMarkdown() produce HTML exactamente como lo hace la vista previa, respetando su renderer y sanitizer.

javascript
const html = editor.renderMarkdown('**bold** text');

Una barra de herramientas para tu propia área de texto

Cuando un cuadro de diálogo recopila texto enriquecido, bríndele herramientas de formato reales en lugar de reconstruirlas. La barra de herramientas está vinculada al área de texto que pasa, su labels se heredan y no se agrega ningún botón de vista previa.

javascript
1
2
3
const body = dialog.querySelector('textarea');
const bar = editor.createToolbar(body, ['bold', 'italic', 'strikethrough']);
dialog.querySelector('.body-toolbar').appendChild(bar);

Esto no gestiona la vida útil de la barra de herramientas. Los botones se limpian con el cuadro de diálogo cuando se elimina, pero si una herramienta registra algo en toda la página, un detector de documentos o un temporizador, suéltelo usted mismo cuando se cierre el cuadro de diálogo.

Traducción

Las herramientas personalizadas se traducen como las integradas, según el título que les hayas asignado.

javascript
labels: { 'Callout': 'Aviso', 'Embed video': 'Insertar video' }

La clase CSS no se ve afectada, por lo que .callout-btn corsé .callout-btn en cada idioma.

📊 Pie de página (barra de estado)

El pie de página se encuentra debajo del editor y muestra la línea, la columna del cursor, el recuento de caracteres del documento y, opcionalmente, el recuento de palabras, todo actualizado en tiempo real. Es visible de forma predeterminada y cada estadística se puede alternar de forma independiente.

LlaveTipoPor defectoDescripción
linebooleantrueMuestra el número de línea actual.
colbooleantrueMuestra el número de columna actual.
charsbooleantrueMuestra el recuento total de caracteres.
wordsbooleanfalseMuestra el recuento total de palabras. Desactivado de forma predeterminada: establecido en true para habilitar.

Ejemplos de uso

javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// Default — line, col, and chars visible
new MarkdownEditor('#editor');

// Disable the footer entirely
new MarkdownEditor('#editor', { footer: false });

// Hide only character count
new MarkdownEditor('#editor', { footer: { chars: false } });

// Hide line and column, keep character count
new MarkdownEditor('#editor', { footer: { line: false, col: false } });

// Show only line number
new MarkdownEditor('#editor', { footer: { col: false, chars: false } });

// Enable word count alongside the defaults
new MarkdownEditor('#editor', { footer: { words: true } });

// Show word count only
new MarkdownEditor('#editor', { footer: { line: false, col: false, chars: false, words: true } });

🔀 Modos de edición

MarkdownEditor ofrece dos formas distintas de escribir y formatear su contenido. Puede alternar entre una vista tradicional centrada en la sintaxis o una experiencia moderna y visual.

  • plain (Predeterminado): un entorno Markdown limpio y de alto rendimiento donde la sintaxis (como **bold** o # heading) es visible. Ideal para desarrolladores y puristas de Markdown
  • hybrid: una experiencia inspirada en WYSIWYG que representa el formato (negrita, cursiva, encabezados) en tiempo real a medida que escribe, manteniendo al mismo tiempo la estructura subyacente de Markdown.
javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Default initialization (Plain Mode)
new MarkdownEditor('#markdown-editor');

// Explicit Plain Mode
new MarkdownEditor('#markdown-editor', {
    mode: 'plain'
});

// Hybrid (Visual) Mode
new MarkdownEditor('#markdown-editor', {
    mode: 'hybrid'
});

Vista previa del modo híbrido y normal:

Modo híbrido

El formato visual se representa en tiempo real mientras escribe.

Modo simple (predeterminado)

Se centra en la sintaxis original de Markdown para una experiencia ligera.

🌙 Tematización

MarkdownEditor hereda automáticamente su tema de la página circundante: no se requiere configuración. El editor lee data-theme desde el ancestro más cercano en la inicialización, por lo que permanece sincronizado con el tema de su sitio desde el primer momento.

Cómo se resuelve el tema (orden de prioridad)

  1. theme opción: anulación explícita pasada en el objeto de opciones
  2. data-theme en el <textarea>: establecido directamente en el elemento
  3. data-theme en cualquier antepasado: p.e. <html>, <body>, o un envoltorio <div>

Temas disponibles

'light' (por defecto), 'dark', 'snowberry', 'darkberry'

Opción 1, heredar de <html> o cualquier antepasado (configuración cero)
html
1
2
3
4
5
6
<html data-theme="dark">
  ...
  <textarea id="markdown-editor"></textarea>
  <script>
    new MarkdownEditor('#markdown-editor'); // picks up dark automatically
  </script>
Opción 2: configurar data-theme directamente en el <textarea>
html
1
2
3
4
<textarea id="markdown-editor" data-theme="dark"></textarea>
<script>
  new MarkdownEditor('#markdown-editor');
</script>
Opción 3: explícito theme opción (anula todo)
javascript
1
2
3
new MarkdownEditor('#markdown-editor', {
    theme: 'dark'
});

🎨 Tema personalizado a través de variables CSS

Puede personalizar completamente la apariencia del editor anulando sus variables CSS en el .markdown-editor-wrapper elemento o cualquier [data-theme] selector. Todos los colores usan el Espacio de color OKLCH para obtener resultados perceptualmente uniformes.

VariableObjetivoLuz predeterminadaValor predeterminado oscuro
--color-baseFondo del editoroklch(100% 0 0)oklch(10.9% 0 0)
--color-on-baseColor de texto principaloklch(22% 0 0)oklch(98% 0 0)
--color-primaryAcento principal (barra de herramientas activa, enlaces)oklch(51.1% .262 277)oklch(66.4% .184 286)
--color-on-primaryTexto en superficies de colores primariosoklch(96.2% .018 272)oklch(10% .01 270)
--color-secondaryAcento secundariooklch(59.1% .293 323)oklch(65% .18 220)
--color-accentResaltar acento (código en línea, cursiva)oklch(54.1% .281 293)oklch(75% .18 50)
--color-neutralSuperficies neutras (bordes, divisores)oklch(15% 0 0)oklch(85% 0 0)
--color-errorColor del estado de erroroklch(57.7% .245 27)oklch(60% .22 30)
--border-radiusRedondeo de esquinas del marco del editor0.25rem
Ejemplo de tema personalizado

Anular cualquier variable en .markdown-editor-wrapper después de que el editor se inicialice, o defina un personalizado [data-theme] bloquear en su hoja de estilo:

css
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
/* Override individual variables */
.markdown-editor-wrapper {
    --color-primary: oklch(60% 0.2 30);   /* orange accent */
    --border-radius: 0.5rem;
}

/* Or define a full custom theme */
[data-theme="brand"] .markdown-editor-wrapper,
.markdown-editor-wrapper[data-theme="brand"] {
    --color-base:       oklch(15% 0.01 250);
    --color-on-base:    oklch(95% 0 0);
    --color-primary:    oklch(65% 0.22 145);   /* green */
    --color-on-primary: oklch(10% 0 0);
    --color-accent:     oklch(75% 0.18 60);
    --color-neutral:    oklch(80% 0 0);
    --border-radius:    0.75rem;
}
javascript
new MarkdownEditor('#markdown-editor', { theme: 'brand' });

🌐 Idioma de la interfaz

El texto, la información sobre herramientas, los elementos de menú, los campos modales y los botones del propio editor están en inglés de forma predeterminada. pasar un labels objeto de traducirlo. Las claves son las cadenas en inglés, la forma en que funciona gettext, por lo que solo enumera lo que desea cambiar y todo lo que falta vuelve al inglés.

javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
new MarkdownEditor('#markdown-editor', {
    labels: {
        'Bold': 'Gras',
        'Italic': 'Italique',
        'Heading': 'Titre',
        'Link': 'Lien',
        'Image link': 'Lien de l\'image',
        'URL': 'URL',
        'Alt text': 'Texte alternatif',
        'Apply': 'Appliquer',
        'Uploading...': 'Téléversement...',
    },
});

Esto es más importante junto con el contenido de derecha a izquierda. El árabe, el urdu y el farsi se presentan correctamente por sí solos, pero una barra de herramientas que todavía dice "negrita" sobre el texto de derecha a izquierda está sólo a medias traducida.

🏷 Variables

Agrega un menú desplegable de barra de herramientas para insertar marcadores de posición. Útil cuando la persona que escribe un documento no es el desarrollador que definió la sintaxis del marcador de posición, como ocurre con las plantillas de correo electrónico, factura o contrato: eligen un nombre legible y se inserta la sintaxis correcta.

javascript
1
2
3
4
5
6
7
8
9
new MarkdownEditor('#markdown-editor', {
    toolbar: ['bold', 'italic', 'link',
        { variables: [
            { label: 'Customer Name', value: '{{customer.name}}' },
            { label: 'Invoice No',    value: '{{invoice.number}}' }
        ]},
        'preview'
    ]
});

La herramienta se configura en línea en el toolbar matriz, por lo que su posición entre los otros botones depende de usted. El botón enumera las etiquetas y al hacer clic en una se inserta su value en el cursor, reemplazando cualquier selección. No se representa nada si la lista está vacía.

Valores de muestra en la vista previa

Una entrada dada una sample muestra esa muestra en la vista previa, mientras que el área de texto mantiene el marcador de posición real. Las entradas sin uno aparecen tal como están escritas, por lo que puedes mezclar ambas.

javascript
1
2
3
4
5
6
7
{ variables: [
    { label: 'Customer Name', value: '{{customer.name}}', sample: 'Hannes' },
    { label: 'Invoice No',    value: '{{invoice.number}}' }
]}

// textarea : Hi {{customer.name}}, invoice {{invoice.number}}
// preview  : Hi Hannes, invoice {{invoice.number}}

Agrupamiento

dar una entrada items en lugar de un value para representar una sección con encabezado. Las entradas agrupadas y planas se pueden mezclar en una lista.

javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{ variables: [
    { label: 'Today', value: '{{today}}', sample: '10 September 2026' },
    { label: 'Customer', items: [
        { label: 'Name',  value: '{{customer.name}}',  sample: 'Hannes' },
        { label: 'Email', value: '{{customer.email}}', sample: 'hannes@example.com' }
    ]},
    { label: 'Invoice', items: [
        { label: 'Number', value: '{{invoice.number}}' }
    ]}
]}

Cosas que debes saber

  • El editor nunca resuelve variables. Inserta texto y su aplicación sustituye valores reales más adelante, normalmente en el lado del servidor. sample solo afecta lo que muestra la vista previa.
  • Las etiquetas se muestran como texto sin formato, por lo que el marcado en una etiqueta nunca se representa.
  • Las entradas con formato incorrecto se omiten en lugar de descartarse, y el botón no se representa en absoluto cuando no se configura nada utilizable.

🖋 Representador personalizado

La vista previa se representa con marcado y desinfectado con DOMPurificar. Ambos pueden ser reemplazados. Úselo cuando su aplicación ya muestre rebajas con otra biblioteca y desee que la vista previa coincida exactamente con la producción.

Ambas opciones son funciones simples que toman una cadena y devuelven una cadena, por lo que cualquier analizador y desinfectante funciona.

javascript
1
2
3
4
5
6
7
8
9
import MarkdownIt from 'markdown-it';
import taskLists from 'markdown-it-task-lists';
import MarkdownEditor from 'markdown-text-editor';

const md = new MarkdownIt({ linkify: true, breaks: true }).use(taskLists);

new MarkdownEditor('#markdown-editor', {
    renderer: markdown => md.render(markdown)
});

DOMPurify aún se ejecuta en la salida, por lo que mantiene la protección XSS sin configurar nada.

desinfectante personalizado

Solo es necesario cuando el valor predeterminado elimina algo que emite su renderizador. DOMPurify elimina <iframe> de forma predeterminada, por lo que las incrustaciones de vídeo deben permitirse explícitamente. DOMPurify debe importarse en su propio código, ya que la copia incluida con el editor es interna.

javascript
1
2
3
4
5
6
7
8
9
import DOMPurify from 'dompurify';

new MarkdownEditor('#markdown-editor', {
    renderer:  markdown => md.render(markdown),
    sanitizer: html => DOMPurify.sanitize(html, {
        ADD_TAGS: ['iframe'],
        ADD_ATTR: ['allow', 'allowfullscreen', 'frameborder']
    })
});

Usando una etiqueta de script en lugar de un paquete, cargue DOMPurify junto con el editor:

html
1
2
<script src="https://cdn.jsdelivr.net/npm/dompurify"></script>
<script src="https://cdn.jsdelivr.net/npm/markdown-text-editor"></script>

Cosas que debes saber

  • Solo vista previa. El formato en vivo del modo híbrido utiliza un procesador interno separado y no se ve afectado.
  • Las listas de tareas necesitan compatibilidad con complementos. Las casillas de verificación en las que se puede hacer clic se encuentran buscando input[type="checkbox"] en la salida, por lo que se necesita rebajas markdown-it-task-lists. El editor registra una advertencia si detecta la sintaxis de la lista de tareas y no hay casillas de verificación.
  • Ambos deben ser sincrónicos y devolver una cadena. Un async función escribe [object Promise] en la vista previa.
  • Reemplazar el desinfectante reemplaza su protección. Un paso como html => html desactiva la desinfección por completo y solo es seguro para contenido totalmente confiable.

🎨 Diseño de elementos internos

Las variables CSS cubren la mayoría de los temas porque heredan, por lo que establecer una en .markdown-editor-wrapper Llega a la barra de herramientas, botones, vista previa y pie de página. Busque un nombre de clase sólo cuando ninguna variable exponga lo que necesita.

css
1
2
3
4
5
/* Variables set on the wrapper inherit down to every child */
.markdown-editor-wrapper {
    --border-radius: 12px;   /* rounds the editor and its toolbar buttons */
    --color-primary: oklch(60% 0.2 30);
}

referencia de clase

Estos nombres de clases son estables y seguros para diseñarlos.

ClaseElemento
.markdown-editor-wrapperContenedor exterior que envuelve todo el editor.
.toolbarBarra de herramientas encima del área de edición
.markdown-btnBotón de barra de herramientas individual
.preview-btnEl botón de alternancia de vista previa/pantalla completa
.editor-layoutCuadrícula que contiene el área de edición y vista previa una al lado de la otra
.textarea-wrapperEnvoltura alrededor del área de edición
.editor-textareaEl elemento de área de texto subyacente
.display-layerCapa de formato renderizada, solo modo híbrido
.preview-wrapperColumna de vista previa
.preview-contentRebaja renderizada dentro de la vista previa
.editor-footerBarra de estado debajo del editor
.find-replace-panelBuscar y reemplazar panel
css
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/* Some variables are set by the component on itself, which beats an
   inherited value. Target the element directly for those. */
.markdown-editor-wrapper .markdown-btn {
    --btn--font-size: 0.875rem;
}

/* And use classes for anything no variable exposes */
.markdown-editor-wrapper .toolbar {
    border-bottom: 2px solid oklch(60% 0.2 30);
}

Dirigirse a un editor

Con varios editores en una página, apunte a uno de ellos con data-editor. El contenedor refleja la identificación de su <textarea>, entonces <textarea id="notes"> te da [data-editor="notes"]. La identificación en sí permanece en el área de texto, por lo que getElementById sigue funcionando.

css
1
2
3
4
/* one editor only */
[data-editor="notes"] {
    --border-radius: 0;
}

🪟 Pantalla completa y capas (índice z)

En pantalla completa el editor usa z-index: 10000, que borra las capas que la mayoría de los marcos de UI reservan para encabezados fijos y superposiciones. Si un encabezado fijo o una barra lateral aún cubre el editor, aumente este valor.

Anularlo con --mte-fullscreen-z-index. El valor se aplica solo en pantalla completa y se hereda, por lo que establecerlo en cualquier antecesor cubre todos los editores que se encuentran debajo de él.

css
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/* every editor on the page */
.markdown-editor-wrapper {
    --mte-fullscreen-z-index: 20000;
}

/* one section only - the value inherits down */
#admin-panel {
    --mte-fullscreen-z-index: 20000;
}

/* a single editor, by its textarea id */
[data-editor="notes"] {
    --mte-fullscreen-z-index: 20000;
}

API de contenido

Una de las principales fortalezas de MarkdownEditor es que mantiene la base <textarea> perfectamente sincronizado. Ya sea que esté utilizando un marco de JavaScript moderno o un backend tradicional como Django, PHP o Laravel, el flujo de trabajo sigue siendo simple y nativo.

Contenido de lectura y escritura

1. El estilo nativo (recomendado)

Debido a que el editor mejora un área de texto estándar, puede utilizar métodos DOM familiares. Esta es la forma más rápida de interactuar con sus datos sin necesidad de aprender una nueva API.

javascript
1
2
3
4
5
// Retrieve content via ID
const markdown = document.getElementById('markdown-editor').value;

// Set content via ID (The editor UI updates automatically)
document.getElementById('markdown-editor').value = "# New Heading Content";

2. Usando una referencia variable

Si tiene una referencia al elemento de área de texto, puede usarla directamente: no se necesita una API específica de la biblioteca.

javascript
1
2
3
4
5
6
7
const textarea = document.getElementById('markdown-editor');

// Retrieve content
const markdown = textarea.value;

// Set content (the editor UI reflects this immediately)
textarea.value = "## Updated via JS";

3. Configuración del contenido inicial del lado del servidor

La forma recomendada de configurar el contenido inicial es directamente en el <textarea> HTML: esto funciona de forma natural con todos los frameworks backend (Django, Laravel, Rails, PHP, etc.) y el editor lo renderiza automáticamente al iniciar.

html
1
2
<!-- Recommended: set content server-side -->
<textarea id="markdown-editor"># Hello World</textarea>

Para leer o actualizar contenido en tiempo de ejecución, use el nativo textarea valor. Llamar editor.render() después de una actualización para actualizar la vista previa y la capa híbrida.

javascript
1
2
3
4
5
6
7
8
const textarea = document.getElementById('markdown-editor');

// Read
const markdown = textarea.value;

// Update at runtime
textarea.value = '# New content';
editor.render();

4. Derribando al editor: destroy()

Llamar editor.destroy() para eliminar el contenedor DOM del editor y restaurar el original <textarea> a su posición en el documento. Útil en aplicaciones de una sola página al desmontar una vista.

javascript
1
2
3
4
const editor = new MarkdownEditor('#markdown-editor');

// Remove the editor and restore the plain textarea
editor.destroy();

Reaccionar a los cambios con onChange

pasar un onChange devolución de llamada para recibir notificaciones sobre cada cambio de contenido. Recibe la cadena de rebajas actual.

javascript
1
2
3
4
5
const editor = new MarkdownEditor('#markdown-editor', {
    onChange(value) {
        console.log('Content changed:', value.length, 'characters');
    }
});

Borrador de guardado automático con localStorage

Usar onChange para guardar un borrador con cada pulsación de tecla. Restáurelo completando previamente el área de texto antes de inicializar el editor.

javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
const DRAFT_KEY = 'my-page-draft';

// Restore saved draft before init (only if textarea starts empty)
const textarea = document.getElementById('markdown-editor');
const saved = localStorage.getItem(DRAFT_KEY);
if (saved && !textarea.value) textarea.value = saved;

// Save on every change
const editor = new MarkdownEditor('#markdown-editor', {
    onChange(value) {
        localStorage.setItem(DRAFT_KEY, value);
    }
});

// Clear draft after successful form submission
document.querySelector('form').addEventListener('submit', () => {
    localStorage.removeItem(DRAFT_KEY);
});

Envío de formulario

Porque MarkdownEditor está construido directamente en el nativo <textarea>, es compatible con todos los marcos de backend (Django, Laravel, PHP, Ruby on Rails, etc.) desde el primer momento.

Aquí es donde brilla la filosofía de "los nativos primero". No es necesario sincronizar datos manualmente antes de enviar un formulario. El navegador trata al editor exactamente como un campo de entrada estándar.

html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<form method="POST" action="/api/submit">
  <textarea id="markdown-editor" name="content" class="h-48" rows="5">
    # Initial Content
  </textarea>

  <button type="submit">Submit to Server</button>
</form>

<script>
  // Just initialize it. That's it.
  new MarkdownEditor('#markdown-editor');
</script>

Nota: Inicialización obligatoria del complemento MarkdownEditor

Simplemente use un HTML estándar <form>. El name El atributo en el área de texto es lo que su servidor utilizará para identificar el contenido.

🚀 Por qué cambia las reglas del juego para los backends

Dado que el editor conserva el nativo. <textarea> comportamiento, su servidor maneja los datos como una cadena estándar. No se requiere lógica adicional: no preventDefault() y sin manual FormData construcción.

💡 Por qué esta es una "función excelente":

La mayoría de los editores (como Quill, Editor.js, simpleMDE, easyMDE) guardan datos en estructuras JSON complejas. Si un desarrollador los usa, debe reescribir el esquema de su base de datos y su lógica de representación.

Con MarkdownEditor, un desarrollador puede tomar un sitio web antiguo y reemplazarlo por uno simple. <textarea> con su editor, y el backend ni siquiera sabe que cambió. Simplemente recibe el mismo texto sin formato de siempre, pero el usuario obtiene una experiencia 10 veces mejor.

Marco / IdiomaCómo acceder al contenido de Markdown
PHP$_POST['content']
Djangorequest.POST.get('content')
Node.js (Rápido)req.body.content
Laravel$request->input('content')
Rubí sobre rielesparams[:content]

🖼️ Carga de imágenes avanzada

Manejar la carga de imágenes de forma nativa: en lugar de depender de cadenas Base64 lentas y con mucha memoria, es una ganancia significativa tanto para el rendimiento como para el SEO.

Pegar y soltar

Pegue una captura de pantalla o arrastre un archivo de imagen al editor y se cargará a través del mismo punto final que el botón de la barra de herramientas. Aparece un marcador de posición inmediatamente para que el editor nunca parezca congelado y se reemplaza con la ruta real una vez que llega la carga. Si la carga falla, el marcador de posición se reemplaza con un marcador visible en lugar de desaparecer silenciosamente.

esto necesita uploadUrl configurado a continuación. Sin él, pegar una imagen no hace nada y el editor registra una advertencia que explica qué agregar, porque la alternativa sería una cadena Base64 en su base de datos.

Se cargan varios archivos a la vez, uno a la vez, por lo que las imágenes llegan en el orden en que llegaron.

Opciones de configuración

La herramienta de imagen admite una fileInput configuración para manejar cargas directas al servidor.

  • accept: Defina una variedad de formatos de imagen permitidos (por ejemplo, 'webp', 'avif')
  • uploadUrl: especifique el punto final de backend donde File El objeto será enviado a través de POST
  • params: Objeto opcional para enviar datos adicionales (como tokens CSRF, ID de usuario o nombres de carpetas) junto con el archivo de imagen.

Ejemplo de uso (configuración completa)

javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
const options = {
  placeholder: 'Start writing...',
  toolbar: [
    'link',
    {
      image: {
        fileInput: {
            accept: ['webp', 'avif'], // restrict the image upload format
            uploadUrl: '/api/upload', // Your upload endpoint
            params: {
                _token: 'your_csrf_token_here', // Essential for Laravel/Django
                folder: 'blog_posts'
            }
        },
        // Supports boolean: true/false OR object: { required: true }
        altInput: { required: true }
      }
    },
    'preview'
  ],
}
const editor = new MarkdownEditor('#markdown-editor', options);

📡 Integración del servidor

La Solicitud

El editor envía un POST solicitar como multipart/form-data. Por defecto, incluye:

  • image_file: El objeto de archivo real
  • image_alt: El texto alternativo ingresado por el usuario.
  • ...más cualquier dato personalizado definido en el params objeto

La respuesta requerida

Para confirmar una carga exitosa e insertar la imagen en el editor, su servidor debe devolver la siguiente estructura JSON:

json
1
2
3
4
{
  "success": true,
  "image_path": "https://cdn.yourdomain.com/uploads/image.webp"
}

Nota: asegúrese de utilizar la clave image_path para la URL de la imagen cargada.

También puedes regresar image_alt, que se utiliza como sugerencia sólo cuando el usuario dejó el campo alt vacío. Todo lo que ellos mismos hayan escrito siempre gana, por lo que un servidor que derive texto alternativo del nombre del archivo o del reconocimiento de imágenes puede ofrecerlo sin sobrescribir una descripción real.

json
1
2
3
4
5
{
  "success": true,
  "image_path": "https://cdn.yourdomain.com/uploads/image.webp",
  "image_alt": "Two people looking at a laptop"
}

Si falla, regresa success: false con un error o message cadena. Ese texto se muestra al usuario, así que conviértalo en algo sobre lo que pueda actuar.

Validación de texto alternativo de imagen (altInput)

Para garantizar que su contenido siga siendo accesible y compatible con SEO, MarkdownEditor aplica la validación de texto alternativo de forma predeterminada.

  • Comportamiento predeterminado: si altInput no está definido, por defecto es { required: true }
  • Hacer cumplir la accesibilidad: se impedirá que los usuarios inserten una imagen hasta que se proporcione una descripción alternativa.

1. Predeterminado (no se necesita configuración)

javascript
1
2
3
4
// Alt text is REQUIRED by default
image: {
  fileInput: { uploadUrl: '/api/upload' }
}

2. Taquigrafía (desactivar validación)

Si desea permitir imágenes sin descripciones, simplemente configure el valor booleano en false.

javascript
1
2
3
image: {
  altInput: false // Users can now skip the alt text field
}

3. Basado en objetos (explícito)

javascript
1
2
3
4
5
image: {
    altInput: {
        required: false // Disables alt text validation — users can skip the alt field
    }
}

Uso de imagen estándar (No fileInput)

Si fileInput no está configurado, el editor utiliza de forma predeterminada un modal simple basado en URL. Esto es ideal si sus usuarios se vinculan principalmente a servidores de imágenes externos.

javascript
1
2
3
4
5
6
7
8
const options = {
  toolbar: [
    'link',
    'image',
    'preview'
  ],
}
const editor = new MarkdownEditor('#markdown-editor', options);

💡 ¿Por qué utilizar parámetros?

En frameworks como Laravel o Django, no puedes cargar archivos sin un token CSRF. Al agregar _token hacia params objeto, su solicitud pasará a través del middleware de seguridad del backend sin problemas, manteniendo la filosofía de "Lógica Cero" para sus controladores del lado del servidor.

⌨️ Atajos de teclado

Las acciones de formato comunes se pueden activar directamente desde el teclado sin tocar la barra de herramientas. Cada acceso directo también se muestra en la información sobre herramientas del botón de la barra de herramientas correspondiente.

AtajoAcción
Ctrl + B  /  ⌘ BAlternar Negrita
Ctrl + I  /  ⌘ IAlternar Cursiva
Ctrl + K  /  ⌘ KInsertar enlace
Ctrl + `  /  ⌘ `Alternar en línea Code
Ctrl + Shift + S  /  ⌘ ⇧ SAlternar Tachado
Ctrl + 1  /  ⌘ 1Título 1
Ctrl + 2  /  ⌘ 2Título 2
Ctrl + 3  /  ⌘ 3Título 3
Ctrl + L  /  ⌘ LAlternar lista de viñetas
Ctrl + Z  /  ⌘ ZDeshacer
Ctrl + Shift + Z  /  ⌘ ⇧ ZRehacer
TabSangrar líneas seleccionadas
Shift + TabEliminar sangría de líneas seleccionadas
Ctrl + F  /  ⌘ FAbrir panel de búsqueda
Ctrl + H  /  ⌘ HAbrir el panel Buscar y reemplazar
Ctrl + Shift + F  /  ⌘ ⇧ FAlternar vista previa a pantalla completa
F11Alternar vista previa a pantalla completa
EscapeCerrar el panel Buscar/Salir de la vista previa en pantalla completa

🔍 Buscar y reemplazar

Un panel integrado de búsqueda y reemplazo está disponible dentro del editor: no se necesita extensión del navegador ni herramienta separada.

  • Prensa Ctrl + F (o ⌘ F) para abrir el panel Buscar
  • Prensa Ctrl + H (o ⌘ H) para abrir el panel Buscar y reemplazar
  • La búsqueda no distingue entre mayúsculas y minúsculas y muestra un contador de partidos en vivo (por ejemplo, 3 de 12)
  • Navegar partidos con los botones ▲ / ▼ o Enter / Shift + Enter
  • Reemplazar reemplaza la coincidencia resaltada actualmente; Reemplazar todo reemplaza cada ocurrencia a la vez
  • Prensa Escape para cerrar el panel y devolver el foco al editor

El panel flota en la esquina superior derecha del área de contenido del editor y no interrumpe la escritura.

🔗 Enlaces desde una URL pegada

Seleccione un texto, pegue una URL sobre él y obtendrá un enlace de rebajas en lugar de que la URL reemplace lo que seleccionó.

markdown
1
2
Read the docs                        <- select "the docs", paste https://example.com
Read [the docs](https://example.com) <- what you get

La regla es deliberadamente estricta: sólo una mínima http o https La URL sin espacios en blanco se trata de esta manera. Pegar una oración que contiene una URL, o pegar sin nada seleccionado, se comporta como un pegado normal.

Características

🔌 Integración de formularios nativos

Funciona exactamente como un estándar <textarea>. Sin API complejas: solo use el value o name atributo. "Simplemente funciona" con envíos de formularios HTML estándar en PHP, Django o Node.js.

🖼️ Carga de imágenes avanzada

Configure las cargas del servidor nativo a través de API. Evite cadenas Base64 pesadas para garantizar cargas de página más rápidas y un SEO superior al alojar imágenes en su propia CDN.

🔀 Modos híbridos y simples

Cambiar entre un Híbrido (WYSIWYG) experiencia en edición visual o Rebaja simple Modo para una sensación de codificación tradicional.

🚀 Alto rendimiento

A 53 KB comprimidos paquete (252 KB minimizado, CSS incluido) optimizado para "contenido pesado". Maneja documentos y archivos de gran tamaño sin ningún retraso en la entrada ni caída del rendimiento. Actualizaciones de vista previa sin rebote, cálculos de estilo en caché y manejo del teclado sin conflictos: por lo que Tab y Enter siempre hacen exactamente una cosa.

🌍 Soporte RTL incorporado

Soporte nativo para idiomas de derecha a izquierda como árabe, urdu y farsi. Perfecto para crear aplicaciones accesibles globalmente.

✨ Resaltado de sintaxis

Legibilidad mejorada con código claro y formato de rebajas.

🌙 Temas adaptativos

Incluye soporte automático para el modo oscuro. Se sincroniza con la configuración de su sistema o con el UI de mermelada de frutas Biblioteca para una experiencia visual perfecta.

📝 Edición inteligente

Continuación automática de listas estilo GitHub para listas ordenadas, listas desordenadas y listas de verificación: presione Enter y el editor continúa el patrón. Se puede hacer clic en las casillas de verificación en el panel de vista previa y se sincronizan con la fuente de rebajas al instante.

📱 Totalmente receptivo

Una interfaz de usuario fluida y pensada para dispositivos móviles que se adapta perfectamente a computadoras de escritorio, tabletas y teléfonos inteligentes para editar sobre la marcha.

📦 Soporte universal

compatible con ESM, UMD, CommonJS y IIFE. Funciona de inmediato a través de CDN (<script src>), npm o cualquier paquete (Vite, webpack, Rollup): no se necesita configuración adicional.

♿ Accesible por defecto

Compatibilidad total con ARIA integrada: punto de referencia de la barra de herramientas, región de vista previa etiquetada, botones fáciles de leer en pantalla, aria-pressed en la palanca de vista previa, disabled y aria-disabled en herramientas inactivas y restauración correcta del enfoque cuando se cierran los modales.

🛡️ Cero conflictos de CSS

Los estilos del editor tienen un alcance completo para .markdown-editor-wrapper. La verificación previa global de Tailwind está excluida, por lo que el editor convive de forma segura junto con Bootstrap, Tailwind o cualquier otro marco sin alterar sus estilos.

⌨️ Atajos de teclado

Ctrl+B, Ctrl+I, Ctrl+K, Ctrl+`, Ctrl+Shift+S: acciones de formato comunes sin tocar el mouse. Cada acceso directo se muestra en la información sobre herramientas del botón de la barra de herramientas.

🔍 Buscar y reemplazar

Prensa Ctrl+F encontrar o Ctrl+H para abrir buscar y reemplazar. Búsqueda que no distingue entre mayúsculas y minúsculas con contador de partidos en vivo, navegación siguiente/anterior, reemplazo único y reemplazar todo: sin salir del editor.

🔒 Vista previa segura XSS

La vista previa renderizada se desinfecta mediante DOMPurificar antes de escribirse en el DOM. Las etiquetas de script, los controladores de eventos en línea y las URL maliciosas en las entradas de rebajas diseñadas se eliminan automáticamente: no se requiere configuración.

▶️ Vista previa en tiempo real

Vea su descuento renderizado instantáneamente a medida que escribe.

🔗 Fácil integración

Integre perfectamente en cualquier proyecto web con una configuración mínima.

🛠️ Barra de herramientas personalizable

Configure y reordene dinámicamente las opciones de la barra de herramientas como negrita, cursiva y más.

Ejemplo de configuración completa

Utilice este ejemplo completo para inicializar MarkdownEditor con todas las funciones principales, incluido el orden personalizado de la barra de herramientas y el manejo avanzado de carga de imágenes.

javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
const editor = new MarkdownEditor('#markdown-editor', {
    mode: 'hybrid',
    placeholder: 'Start writing...',
    footer: {
        line: true,
        col: true,
        chars: true,
        words: true,
    },
    onChange(value) {
        console.log('Content updated:', value.length, 'characters');
    },
    labels: {
        'Bold': 'Gras',
        'Italic': 'Italique',
        'Uploading...': 'Téléversement...',
    },
    toolbar: [
        'heading', 'bold', 'italic', 'strikethrough', 'blockquote',
        'ul', 'ol', 'checklist',
        'code', 'codeblock', 'hr', 'table',
        {
            image: {
                fileInput: {
                    accept: ['webp', 'avif', 'png'],
                    uploadUrl: '/api/upload'
                }
            }
        },
        'link', 'undo', 'redo', 'indent', 'outdent', 'preview'
    ],
});

// Read content natively
const markdown = document.getElementById('markdown-editor').value;

// destroy() when the view unmounts (SPAs)
// editor.destroy();

¿Encontró esto útil?

Una estrella de GitHub ayuda a otros desarrolladores a descubrir el editor. es parte de mermelada de frutas. Una estrella allí también ayuda.

Estrella en GitHub
Edit page

Última actualización: