@htmleditor/core
v0.2.0
Published
Lightweight inline HTML editor — WYSIWYG + code view. Zero runtime dependencies.
Maintainers
Readme
HTMLEditor
Editor WYSIWYG + código HTML inline. Sin dependencias en runtime. MIT License.
Características
- 3 modos de edición — Visual (WYSIWYG), Código HTML directo y Preview
- Toolbar completamente personalizable — elige solo los botones que necesitas
- Sin dependencias en runtime — vanilla TypeScript/CSS compilado
- Distribución universal — UMD (
<script>), ESM (import), TypeScript types - Sanitizador HTML integrado — allowlist de tags/atributos, bloqueo de XSS y URLs peligrosas
- Bloques de código — 14 lenguajes, 8 temas de color, badge de lenguaje, botón de copia
- Tablas dinámicas — selector visual, agregar/eliminar filas y columnas, toggle de bordes
- Adjuntos en base64 — PDF, XLS, PPT, DOCX sin necesidad de servidor de archivos
- Selector de emojis — categorías, navegación por teclado
- Familias tipográficas — selector integrado con fuentes Google
- Theming via variables CSS — sin modificar el source
Instalación
npm install @htmleditor/core
# o
pnpm add @htmleditor/coreQuickstart
Via <script> (UMD)
<link rel="stylesheet" href="dist/htmleditor.css" />
<script src="dist/htmleditor.js"></script>
<div id="editor"></div>
<script>
// El bundle UMD expone window.HTMLEditor como namespace del módulo
const { HTMLEditor } = window.HTMLEditor
const editor = new HTMLEditor('#editor', {
content: '<p>Hola mundo</p>',
onChange: (html) => console.log(html),
onSave: (html) => fetch('/api/save', { method: 'POST', body: html }),
})
</script>Via npm / ESM
import { HTMLEditor } from '@htmleditor/core'
import '@htmleditor/core/dist/htmleditor.css'
const editor = new HTMLEditor('#editor', {
content: '<p>Hola mundo</p>',
toolbar: ['bold', 'italic', '|', 'h1', 'h2', '|', 'link', 'image'],
onChange: (html) => console.log(html),
})API
Constructor
new HTMLEditor(target: string | HTMLElement, options?: HTMLEditorOptions)Opciones
| Opción | Tipo | Default | Descripción |
|---|---|---|---|
| content | string | '' | HTML inicial del editor |
| mode | 'visual'\|'code'\|'preview' | 'visual' | Modo inicial |
| toolbar | ToolbarItem[] | full toolbar | Botones a mostrar. Usa '|' como separador |
| placeholder | string | 'Empieza a escribir...' | Texto placeholder |
| onChange | (html: string) => void | — | Se llama en cada cambio con el HTML actual |
| onSave | (html: string) => void | — | Se llama con Ctrl+S / Cmd+S |
| onReady | (editor: HTMLEditor) => void | — | Se llama al terminar de inicializar |
Métodos de instancia
editor.getHTML(): string // HTML actual del editor
editor.setHTML(html: string): void // Reemplaza el contenido (sanitizado)
editor.setMode(mode: EditorMode): void // 'visual' | 'code' | 'preview'
editor.getMode(): EditorMode // Modo activo
editor.focus(): void // Foco en el área editable
editor.destroy(): void // Desmonta el editor y libera listeners
// Eventos programáticos
editor.on('change', (html) => { ... })
editor.on('save', (html) => { ... })
editor.off('change', handler)Toolbar items disponibles
| Item | Descripción |
|---|---|
| 'bold' | Negrita |
| 'italic' | Cursiva |
| 'underline' | Subrayado |
| 'strikethrough' | Tachado |
| 'h1' 'h2' 'h3' | Encabezados |
| 'paragraph' | Párrafo normal |
| 'ul' | Lista sin orden |
| 'ol' | Lista ordenada |
| 'indent' | Aumentar sangría |
| 'outdent' | Reducir sangría |
| 'blockquote' | Cita |
| 'link' | Insertar enlace |
| 'image' | Insertar imagen (desde archivo) |
| 'code' | Código inline |
| 'codeblock' | Bloque de código con lenguaje y color |
| 'table' | Insertar tabla |
| 'hr' | Línea horizontal |
| 'fontfamily' | Selector de familia tipográfica |
| 'emoji' | Selector de emojis |
| 'attachment' | Adjuntar archivo (PDF, XLS, PPT, DOCX…) |
| '|' | Separador visual |
Toolbar por defecto:
['h1', 'h2', 'h3', 'paragraph', '|',
'bold', 'italic', 'underline', 'strikethrough', '|',
'ul', 'ol', 'indent', 'outdent', 'blockquote', '|',
'link', 'image', 'table', 'hr', '|',
'code', 'codeblock', '|',
'fontfamily', '|',
'emoji', 'attachment']Theming
Sobreescribe las variables CSS en el selector del contenedor:
/* Tema violeta */
#mi-editor {
--he-primary: #7c3aed;
--he-primary-hover: #6d28d9;
--he-primary-light: #f5f3ff;
--he-bg-toolbar: #faf5ff;
}
/* Tema oscuro */
#mi-editor {
--he-primary: #60a5fa;
--he-primary-hover: #93c5fd;
--he-primary-light: rgba(96, 165, 250, .12);
--he-bg: #1e293b;
--he-bg-toolbar: #0f172a;
--he-border: #334155;
--he-text: #f1f5f9;
--he-text-muted: #94a3b8;
}Variables disponibles
| Variable | Default | Descripción |
|---|---|---|
| --he-primary | #2563eb | Color de acento principal |
| --he-primary-hover | #1d4ed8 | Hover del color primario |
| --he-primary-light | #eff6ff | Fondo claro de acento |
| --he-bg | #ffffff | Fondo del área de edición |
| --he-bg-toolbar | #f8fafc | Fondo de la toolbar |
| --he-border | #e2e8f0 | Color de bordes |
| --he-text | #0f172a | Color del texto |
| --he-text-muted | #64748b | Texto secundario / íconos inactivos |
Integración con frameworks
Angular
// angular.json → projects.architect.build.options.styles
"node_modules/@htmleditor/core/dist/htmleditor.css"
// editor.component.ts
import { Component, AfterViewInit, OnDestroy, Output, EventEmitter } from '@angular/core'
import { HTMLEditor } from '@htmleditor/core'
@Component({
selector: 'app-editor',
template: `<div id="he-host"></div>`,
})
export class EditorComponent implements AfterViewInit, OnDestroy {
@Output() contentChange = new EventEmitter<string>()
private editor!: HTMLEditor
ngAfterViewInit() {
this.editor = new HTMLEditor('#he-host', {
toolbar: ['bold', 'italic', '|', 'h1', 'h2', '|', 'link', 'image'],
onChange: (html) => this.contentChange.emit(html),
})
}
ngOnDestroy() { this.editor.destroy() }
getValue(): string { return this.editor.getHTML() }
setValue(html: string) { this.editor.setHTML(html) }
}Vue 3
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue'
import { HTMLEditor } from '@htmleditor/core'
import '@htmleditor/core/dist/htmleditor.css'
const html = ref('')
let editor: HTMLEditor
onMounted(() => {
editor = new HTMLEditor('#editor', {
onChange: (h) => { html.value = h },
})
})
onUnmounted(() => editor?.destroy())
</script>
<template>
<div id="editor"></div>
</template>Spring Boot + Thymeleaf
<link rel="stylesheet" th:href="@{/vendor/htmleditor.css}"/>
<div id="content" th:utext="${post.content}"></div>
<script th:src="@{/vendor/htmleditor.js}"></script>
<script>
const EditorClass = window.HTMLEditor.HTMLEditor
const el = document.getElementById('content')
const initialHTML = el.innerHTML
el.innerHTML = '' // limpiar antes de montar
const editor = new EditorClass(el, { content: initialHTML })
document.querySelector('form').addEventListener('submit', () => {
const hidden = document.createElement('input')
hidden.type = 'hidden'
hidden.name = 'content'
hidden.value = editor.getHTML()
document.querySelector('form').appendChild(hidden)
})
</script>Seguridad
El método setHTML() y el callback content del constructor pasan el HTML por un sanitizador integrado que:
- Mantiene solo un allowlist de tags seguros (
p,h1–h6,strong,em,a,img,ul,ol,pre,code,table, etc.) - Elimina completamente
<script>,<style>,<iframe>,<object>,<embed>,<noscript> - Valida
hrefysrccontra protocolos seguros (https:,mailto:,/,data:image/*;base64,) - Bloquea
javascript:yexpression()en atributosstyle - Fuerza
rel="noopener noreferrer"en enlaces externos
Para aplicaciones en producción se recomienda complementar con sanitización server-side (ej. jsoup en Java, DOMPurify en Node).
Desarrollo
git clone https://github.com/apessss001/htmleditor
cd htmleditor
npm install
npm run dev # servidor de desarrollo con HMR en demo/
npm run build # genera dist/ (UMD + ESM + types + CSS)
npm run test # tests unitarios con Vitest
npm run typecheck # verificación de tipos sin compilarEstructura del proyecto
src/
├── HTMLEditor.ts # Clase principal y API pública
├── types.ts # Tipos TypeScript exportados
├── styles/
│ └── editor.css # Todos los estilos del editor
├── toolbar/
│ ├── Toolbar.ts # Construcción y lógica de la toolbar
│ └── commands.ts # execCommand wrappers + utilidades
├── modes/
│ ├── WysiwygMode.ts # Modo visual (contenteditable)
│ ├── CodeMode.ts # Modo código (textarea)
│ └── PreviewMode.ts # Modo preview (iframe)
└── utils/
├── sanitize.ts # Sanitizador HTML con allowlist
├── imageUpload.ts # Carga y redimensión de imágenes
├── fileUpload.ts # Carga de archivos adjuntos
├── emojiPicker.ts # Picker de emojis
├── fontLoader.ts # Carga de fuentes Google
├── tableDialog.ts # Diálogo de inserción de tablas
├── codeBlockDialog.ts # Diálogo de bloque de código
└── resizeDialog.ts # Diálogo de redimensión de imágenesLicencia
MIT — Andres Pacheco
