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 versionUn 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.)
npm install markdown-text-editor |
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.
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.
1 2 3 4 | interface EditorProps { value: string; options?: MarkdownEditorOptions; } |
CDN: módulo ES
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.
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.
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').valuecomo 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ística | Editor de rebajas | Fá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 en | A través de CodeMirror direction opción |
| Controladores de eventos en línea (CSP) | ✅ Ninguno | Alguno |
| Modo oscuro/temática | ✅ | Limitado |
| Tamaño del paquete, comprimido | 53KB | 107KB (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.
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'}), } |
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.
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.
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.
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.
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.
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 <textarea ref={ref} name={name} defaultValue={defaultValue} />; } |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <script setup> 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 => emit('update:modelValue', value) }); }); onBeforeUnmount(() => editor?.destroy()); </script> <template><textarea ref="el"></textarea></template> |
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.
| Propiedad | Tipo | Por defecto | Objetivo |
|---|---|---|---|
mode | string | 'plain' | Establece la vista inicial. Utilice híbrido para una experiencia WYSIWYG o simple para sintaxis sin formato. |
placeholder | string | 'Write...' | Texto que se muestra cuando el editor está vacío. |
toolbar | array | [...] | Define qué herramientas aparecen y en qué orden. |
footer | falso | objeto | todo visible | Controla 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. |
theme | string | inherited | Establece 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. |
minHeight | number | 200 | Altura 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. |
maxHeight | number | 500 | Altura 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. |
renderer | function | marked | Reemplaza el analizador de rebajas utilizado para la vista previa. Recibe la cadena de rebajas y debe devolver una cadena HTML. |
sanitizer | function | DOMPurify | Reemplaza el desinfectante HTML. Recibe el HTML renderizado y debe devolver el HTML seguro para mostrarlo. |
onChange | function | undefined | La 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. |
labels | object | undefined | Traducciones 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ía | Teclas de herramientas |
|---|---|
| Tipografía | heading, bold, italic, strikethrough, blockquote |
| Liza | ul (bullet), ol (numbered), checklist |
| Código | code (en línea), codeblock (bloque cercado) |
| Insertos | hr (regla horizontal), table (plantilla de tabla) |
| Medios de comunicación | link, image |
| Edición | undo, redo, indent, outdent |
| Vista | preview |
| Plantillas | { variables: [...] }, configurado en línea |
Referencia de herramientas
| Herramienta | Descripción |
|---|---|
heading | Abre un menú desplegable para seleccionar el nivel de título H1–H6 |
bold | Habilita el formato de texto en negrita. |
italic | Habilita el formato de texto en cursiva. |
strikethrough | Permite 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. |
checklist | Agrega 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. |
blockquote | Resalte el texto citado o enfatizado. |
code | Envuelve el texto seleccionado con comillas invertidas individuales para el código en línea. Al hacer clic nuevamente se eliminan las comillas invertidas. |
codeblock | Envuelve el texto seleccionado en un bloque de código delimitado con triple acento grave. Al hacer clic nuevamente se eliminan las vallas. |
hr | Inserta un --- regla horizontal en la posición del cursor en su propia línea. |
table | Inserta una plantilla de tabla de rebajas inicial de 2x3 en la posición del cursor. |
image | Le permite insertar imágenes mediante la sintaxis de rebajas. |
link | Le permite agregar hipervínculos a su texto. |
undo | Para revertir los últimos cambios. |
redo | Para volver a aplicar los últimos cambios deshechos. |
indent | Para aumentar el nivel de sangría. |
outdent | Para disminuir el nivel de sangría. |
preview | Alterna 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
placeholderen JS, el complemento utilizará automáticamente elplaceholderatributo 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.
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.
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
| Propiedad | Tipo | Objetivo |
|---|---|---|
title | string | Requerido. Texto de información sobre herramientas y el origen de la clase CSS del botón. Traducible a través de labels. |
action | function | Requerido. 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. |
icon | string | SVG en línea para la cara del botón. |
shortcut | string | Un 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.
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
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().
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().
El ayudante de diálogo
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().
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.
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.
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.
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.
| Llave | Tipo | Por defecto | Descripción |
|---|---|---|---|
line | boolean | true | Muestra el número de línea actual. |
col | boolean | true | Muestra el número de columna actual. |
chars | boolean | true | Muestra el recuento total de caracteres. |
words | boolean | false | Muestra el recuento total de palabras. Desactivado de forma predeterminada: establecido en true para habilitar. |
Ejemplos de uso
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 Markdownhybrid: 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.
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)
themeopción: anulación explícita pasada en el objeto de opcionesdata-themeen el<textarea>: establecido directamente en el elementodata-themeen 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)
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>
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)
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.
| Variable | Objetivo | Luz predeterminada | Valor predeterminado oscuro |
|---|---|---|---|
--color-base | Fondo del editor | oklch(100% 0 0) | oklch(10.9% 0 0) |
--color-on-base | Color de texto principal | oklch(22% 0 0) | oklch(98% 0 0) |
--color-primary | Acento principal (barra de herramientas activa, enlaces) | oklch(51.1% .262 277) | oklch(66.4% .184 286) |
--color-on-primary | Texto en superficies de colores primarios | oklch(96.2% .018 272) | oklch(10% .01 270) |
--color-secondary | Acento secundario | oklch(59.1% .293 323) | oklch(65% .18 220) |
--color-accent | Resaltar acento (código en línea, cursiva) | oklch(54.1% .281 293) | oklch(75% .18 50) |
--color-neutral | Superficies neutras (bordes, divisores) | oklch(15% 0 0) | oklch(85% 0 0) |
--color-error | Color del estado de error | oklch(57.7% .245 27) | oklch(60% .22 30) |
--border-radius | Redondeo de esquinas del marco del editor | 0.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:
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; } |
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.
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.
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.
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.
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.
samplesolo 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.
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.
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:
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 rebajasmarkdown-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
asyncfunción escribe[object Promise]en la vista previa. - Reemplazar el desinfectante reemplaza su protección. Un paso como
html => htmldesactiva 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.
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.
| Clase | Elemento |
|---|---|
.markdown-editor-wrapper | Contenedor exterior que envuelve todo el editor. |
.toolbar | Barra de herramientas encima del área de edición |
.markdown-btn | Botón de barra de herramientas individual |
.preview-btn | El botón de alternancia de vista previa/pantalla completa |
.editor-layout | Cuadrícula que contiene el área de edición y vista previa una al lado de la otra |
.textarea-wrapper | Envoltura alrededor del área de edición |
.editor-textarea | El elemento de área de texto subyacente |
.display-layer | Capa de formato renderizada, solo modo híbrido |
.preview-wrapper | Columna de vista previa |
.preview-content | Rebaja renderizada dentro de la vista previa |
.editor-footer | Barra de estado debajo del editor |
.find-replace-panel | Buscar y reemplazar panel |
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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 / Idioma | Cómo acceder al contenido de Markdown |
|---|---|
| PHP | $_POST['content'] |
| Django | request.POST.get('content') |
| Node.js (Rápido) | req.body.content |
| Laravel | $request->input('content') |
| Rubí sobre rieles | params[: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 dondeFileEl objeto será enviado a través dePOSTparams: 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)
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 realimage_alt: El texto alternativo ingresado por el usuario.- ...más cualquier dato personalizado definido en el
paramsobjeto
La respuesta requerida
Para confirmar una carga exitosa e insertar la imagen en el editor, su servidor debe devolver la siguiente estructura 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.
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
altInputno 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)
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.
1 2 3 | image: { altInput: false // Users can now skip the alt text field } |
3. Basado en objetos (explícito)
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.
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.
| Atajo | Acción |
|---|---|
Ctrl + B / ⌘ B | Alternar Negrita |
Ctrl + I / ⌘ I | Alternar Cursiva |
Ctrl + K / ⌘ K | Insertar enlace |
Ctrl + ` / ⌘ ` | Alternar en línea Code |
Ctrl + Shift + S / ⌘ ⇧ S | Alternar |
Ctrl + 1 / ⌘ 1 | Título 1 |
Ctrl + 2 / ⌘ 2 | Título 2 |
Ctrl + 3 / ⌘ 3 | Título 3 |
Ctrl + L / ⌘ L | Alternar lista de viñetas |
Ctrl + Z / ⌘ Z | Deshacer |
Ctrl + Shift + Z / ⌘ ⇧ Z | Rehacer |
Tab | Sangrar líneas seleccionadas |
Shift + Tab | Eliminar sangría de líneas seleccionadas |
Ctrl + F / ⌘ F | Abrir panel de búsqueda |
Ctrl + H / ⌘ H | Abrir el panel Buscar y reemplazar |
Ctrl + Shift + F / ⌘ ⇧ F | Alternar vista previa a pantalla completa |
F11 | Alternar vista previa a pantalla completa |
Escape | Cerrar 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
Escapepara 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ó.
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.
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.