npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

pluton-html-editor

v0.0.2

Published

Editor HTML híbrido para Angular com modo visual (EJS) e modo código (Monaco)

Readme

pluton-html-editor

Editor HTML híbrido para Angular com suporte a templates EJS: modo Visual (contenteditable com chips) e modo Código (Monaco + preview ao vivo).

Demo online: StackBlitz

Recursos principais

  • Variáveis EJS (<%= ... %>) e lógica de template (<% if (...) { %>, <% } %>, loops, etc.) como chips no modo visual
  • Toolbar sincronizada com a seleção (negrito, itálico, sublinhado, tachado, alinhamento)
  • Formatação em variáveis — negrito, itálico, cores e tamanho acumulam no style do chip
  • Tamanho de fonte personalizado (ex.: 27px) além da lista fixa
  • Layout de páginaAutomático (largura máxima, padrão), A4/A3/Letter, retrato/paisagem, múltiplas folhas e quebra de página
  • Config global + local com defaults exportáveis e botões customizados na toolbar
  • Modo código com altura dobrada: editor Monaco e preview em 50% / 50%

Instalação

A biblioteca é compatível com npm e pnpm. js-beautify já vem como dependência da lib (não precisa instalar à parte).

npm

npm install pluton-html-editor monaco-editor ngx-monaco-editor-v2

pnpm

pnpm add pluton-html-editor monaco-editor ngx-monaco-editor-v2

Pacote local (desenvolvimento / tarball)

# no repositório da lib
npm run build:lib
npm run pack:lib
# gera pluton-html-editor-0.0.1.tgz em editor-workspace/

# no app Angular consumidor
npm install /caminho/para/pluton-html-editor-0.0.1.tgz
# ou
npm install ../editor-workspace/dist/pluton-html-editor

npm 7+ instala peerDependencies automaticamente. Em versões antigas, instale Angular, Monaco e ngx-monaco-editor-v2 manualmente.

Configuração do Monaco (obrigatório)

Em angular.json, adicione os assets do Monaco ao app consumidor:

{
  "glob": "**/*",
  "input": "node_modules/monaco-editor/min",
  "output": "assets/monaco"
}

Em app.config.ts (loader.js fica em assets/monaco/vs/):

import { provideMonacoEditor } from 'ngx-monaco-editor-v2';

export const appConfig: ApplicationConfig = {
  providers: [provideMonacoEditor({ baseUrl: 'assets/monaco/vs' })],
};

Estilos dos blocos (cards, headers, chips EJS)

Importe o SCSS no styles.scss do app:

@use 'pluton-html-editor/styles/editor-blocks';

Sem esse import, cards, chips EJS e estilos de debug não aparecem corretamente no modo visual.

Uso mínimo

import { PlutonHtmlEditorComponent } from 'pluton-html-editor';

@Component({
  imports: [PlutonHtmlEditorComponent],
  template: `<pluton-html-editor [content]="html" (contentChange)="html = $event" />`,
})
export class MyPage {
  html = '<p>Olá <%= user.name %></p>';
}

Chips EJS no modo visual

| Tipo | Sintaxe | Classe CSS | Aparência | |------|---------|------------|-----------| | Variável / saída | <%= data.name %> | pluton-editor-var | Chip cinza | | Lógica de template | <% if (x) { %>, <% } %>, <% for (...) { %> | pluton-editor-template | Chip violeta |

Edição

  • Duplo clique no chip abre edição inline da expressão ou do bloco de lógica
  • Enter confirma, Esc cancela

Formatação na seleção

  • Selecione o chip (ou texto dentro dele) e use B, I, U, S, cores ou tamanho
  • Vários estilos acumulam no atributo style do chip (ex.: negrito + itálico + 27px)
  • Clicar de novo no mesmo botão remove só aquele estilo (toggle)
  • A toolbar não tira o foco da seleção ao clicar nos botões de formatação

Digitação no editor

  • Tags EJS digitadas como texto (<%= ... %> ou <% ... %>) são promovidas a chips automaticamente

Padrões regex (configuráveis)

import {
  DEFAULT_VARIABLE_PATTERN,
  DEFAULT_TEMPLATE_LOGIC_PATTERN,
} from 'pluton-html-editor';

// Padrão: <%= expressão %>
DEFAULT_VARIABLE_PATTERN; // /<%=\s*([\s\S]*?)\s*%>/g

// Padrão: <% lógica %> (exceto <%=)
DEFAULT_TEMPLATE_LOGIC_PATTERN; // /<%(?!=)\s*([\s\S]*?)\s*%>/g

Modos Visual e Código

Modo visual

  • Canvas com folhas; padrão Automático (largura 100% do editor) ou tamanho fixo (A4, A3, etc.)
  • Toolbar fixa; scroll apenas no corpo do editor
  • Painel Debug CSS destaca blocos, seções, páginas e chips

Modo código

  • Altura total do componente dobra em relação ao modo visual (editorHeight × 2)
  • Monaco (metade superior) e Preview (metade inferior), cada um com altura próxima ao canvas visual
  • Botão flutuante F formata o HTML com js-beautify (se features.formatCodeButton)

Configuração global, local e defaults da lib

A configuração é mesclada nesta ordem (o último vence em chaves de primeiro nível; objetos aninhados são mesclados em profundidade):

  1. DEFAULT_PLUTON_HTML_EDITOR_CONFIG — defaults exportados pela biblioteca
  2. PLUTON_HTML_EDITOR_CONFIG — token global (app)
  3. [config] no <pluton-html-editor> — config local da instância

Importar defaults da lib

import {
  DEFAULT_PLUTON_HTML_EDITOR_CONFIG,
  createDefaultPlutonHtmlEditorConfig,
  createPlutonHtmlEditorConfig,
  mergePlutonHtmlEditorConfig,
  PLUTON_HTML_EDITOR_CONFIG,
  PlutonHtmlEditorComponent,
} from 'pluton-html-editor';

const base = createDefaultPlutonHtmlEditorConfig();

const custom = createPlutonHtmlEditorConfig({
  theme: 'vs',
  features: { insertImage: false },
});

const merged = mergePlutonHtmlEditorConfig(
  { theme: 'vs-dark', defaults: { typography: { fontSize: '16px' } } },
  { defaults: { typography: { fontFamily: 'Georgia, serif' } } },
);

Exemplo: global + local

// app.config.ts
import { PLUTON_HTML_EDITOR_CONFIG } from 'pluton-html-editor';

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: PLUTON_HTML_EDITOR_CONFIG,
      useValue: {
        theme: 'vs-dark',
        minifyOnEmit: false,
        defaultViewMode: 'visual',
        features: {
          insertPageBreak: true,
          debugCss: true,
        },
        defaults: {
          typography: {
            fontSize: '16px',
            fontFamily: 'Arial, sans-serif',
            foreColor: '#1e293b',
          },
          pageLayout: {
            pageSize: { preset: 'A4', orientation: 'portrait' },
            margin: { top: '16px', right: '16px', bottom: '16px', left: '16px' },
          },
        },
      },
    },
  ],
};
<pluton-html-editor
  [content]="html"
  [height]="720"
  [config]="{
    theme: 'vs',
    features: { insertImage: true },
    defaults: { typography: { fontSize: '18px' } }
  }"
  (contentChange)="html = $event"
/>

PlutonHtmlEditorConfig

| Campo | Descrição | |-------|-----------| | theme | 'vs' | 'vs-dark' — tema do Monaco | | minifyOnEmit | Minifica HTML no contentChange | | variablePattern | Regex para saída/variáveis (<%= ... %>, padrão exportado) | | templateLogicPattern | Regex para lógica (<% ... %> exceto <%=, padrão exportado) | | visualDebounceMs | Debounce do sync no modo visual (padrão 300) | | defaultViewMode | 'visual' | 'code' | | editorHeight | Altura no modo visual. Número = px; string = CSS (560px, min(85vh, 720px)). Padrão: min(85vh, 720px) | | editorMinHeight | Altura mínima (padrão 420 px). No modo código, valor efetivo × 2 | | features | Liga/desliga grupos da toolbar | | defaults | Valores iniciais dos painéis e seletores | | lists | Listas customizadas (fontes, tamanhos, margens, etc.) | | customToolbarButtons | Botões extras no header |

features (todos true por padrão)

| Chave | Toolbar | |-------|---------| | undoRedo | Desfazer / Refazer | | textFormatting | Negrito, itálico, sublinhado, tachado | | lists | Listas numerada e com marcadores | | alignment | Alinhamento | | typography | Caixa, tamanho, Px personalizado, fonte, entrelinhas | | colors | Cor do texto e destaque | | collapseDoubleSpaces | Botão 1⎵ | | removeFormat | Limpar formatação | | moveCursorToEnd | Ir ao final do documento | | insertImage | Imagem | | insertColumns | Colunas (grid 12) | | insertMargin | Margem do bloco | | insertCard | Card | | insertLine | Linha horizontal | | insertLink | Link | | insertTable | Tabela | | insertPageBreak | Quebra de página (nova folha) | | insertSection | Seção (agrupamento + margem) | | insertPageLayout | Página (tamanho, orientação, fundo, padding) | | debugCss | Debug CSS | | viewModeSwitch | Visual / Código | | formatCodeButton | Botão flutuante F (modo código) |

Tipografia na toolbar

| Controle | Uso | |----------|-----| | Caixa | text-transform (normal, maiúsculas, minúsculas) | | Tamanho | Lista pré-definida (12px32px) | | Px + OK | Tamanho personalizado — digite 27 e confirme (aplica 27px na seleção) | | Fonte | font-family | | Entre | line-height do bloco | | A / | Cor do texto e destaque |

Os botões B / I / U / S ficam destacados quando a seleção já possui aquele estilo (incluindo chips de variável).

defaults

| Caminho | Uso | |---------|-----| | defaults.typography | Valores iniciais dos selects e color pickers | | defaults.card | Painel Card | | defaults.margin | Painel Margin | | defaults.sectionMargin | Painel Seção | | defaults.pageLayout | Painel Página: A4/A3/Letter/custom, orientação, fundo, margens | | defaults.table | Linhas/colunas/cabeçalho | | defaults.image.maxFileBytes | Limite de upload (padrão 2 MB) | | defaults.columns | maxColumns, customSpans |

lists

Substitui FONT_FAMILIES, FONT_SIZES, LINE_HEIGHT_OPTIONS, MARGIN_PRESETS, TEXT_TRANSFORM_OPTIONS.

customToolbarButtons

Botões extras no header. Mescla por id (local sobrescreve global).

customToolbarButtons: [
  {
    id: 'delete-word',
    label: 'Remover palavra',
    icon: 'trash',
    row: 'format',
    order: 10,
    visualOnly: true,
    action: { type: 'deleteWord' },
  },
  {
    id: 'paste-model',
    label: 'Modelo',
    icon: 'template',
    action: {
      type: 'insertHtml',
      html: '<% if (order.show) { %><p><%= order.id %></p><% } %>',
      modes: ['visual', 'code'],
    },
  },
  {
    id: 'custom-log',
    label: 'Log',
    icon: 'clipboard',
  },
],

| action.type | Comportamento | |---------------|----------------| | insertHtml | Cola HTML na seleção/cursor | | deleteSelection | Apaga a seleção | | deleteWord | Apaga seleção ou palavra sob o cursor |

<pluton-html-editor (customToolbarClick)="onCustom($event)" />
onCustom(e: PlutonCustomToolbarClickEvent) {
  console.log(e.buttonId, e.selectedText, e.viewMode);
}

// ViewChild
editor.insertHtmlAtCursor('<span>ok</span>');
editor.deleteSelectionOrWord();
editor.getToolbarSelection();

iconSvg aceita SVG inline. showLabel: true exibe texto ao lado do ícone.

Página, folhas e quebra

No painel Página:

  • Tamanho: Automático (largura máxima — padrão), A4, A3, Carta ou personalizado (mm)
  • Retrato / Paisagem
  • Cor de fundo e padding (margens internas da folha)

Aplicar layout atualiza todas as folhas [data-pluton-page].

Quebra (modo visual): insere data-pluton-page-break, cria nova folha e move o cursor para ela.

defaults: {
  pageLayout: {
    pageSize: { preset: 'auto', orientation: 'portrait' },
    margin: { top: '20px', right: '20px', bottom: '20px', left: '20px' },
    backgroundColor: '#ffffff',
  },
},

Arquitetura e fluxo de dados

flowchart LR
  subgraph visual [Modo Visual]
    V[contenteditable]
    Chips[chips EJS]
    V --> Chips
  end
  subgraph code [Modo Código]
    M[Monaco]
    P[Preview HTML]
  end
  Raw[rawContent string]
  V -->|HTML do surface| Raw
  Raw -->|codeToVisual| V
  M --> Raw
  Raw --> P
  • Fonte da verdade: string HTML com tags EJS e estilos inline
  • Modo visual → contentChange: emite o HTML do surface (chips permanecem como no editor)
  • Modo código → visual: EJS vira chips para edição (codeToVisual)
  • Modo código → visual: regex configuráveis geram chips; Monaco e preview compartilham o mesmo rawContent
  • Exportação: o contentChange emite o mesmo HTML do modo visual (sem converter chips em EJS nem alterar colunas existentes); minifyOnEmit opcional

PDF / Puppeteer / html2pdf (opcional, fora do editor)

Somente colunas inseridas pelo botão Colunas da toolbar usam <table> (melhor em PDF). HTML que você colar ou editar com display:flex não é alterado pelo editor.

Se precisar converter flex legado só na geração do PDF (no seu backend), use de forma explícita:

import { normalizeFlexColumnsInHtml } from 'pluton-html-editor';

const html = normalizeFlexColumnsInHtml(ejs.render('template', data));

API do componente

| Input / Output | Tipo | Descrição | |----------------|------|-----------| | content | string | HTML inicial | | contentChange | string | HTML emitido (inline + EJS) | | config | Partial<PlutonHtmlEditorConfig> | Config local | | height | string \| number | Sobrescreve editorHeight nesta instância | | defaultViewMode | 'code' \| 'visual' | Modo inicial | | viewModeChange | 'code' \| 'visual' | Troca de modo | | customToolbarClick | PlutonCustomToolbarClickEvent | Botão customizado sem action embutida |

Métodos públicos (ViewChild)

| Método | Descrição | |--------|-----------| | insertHtmlAtCursor(html) | Insere HTML no cursor (visual ou código) | | deleteSelectionOrWord() | Remove seleção ou palavra | | getToolbarSelection() | { selectedText, hasSelection } |

Exports úteis

import {
  normalizeFlexColumnsInHtml,
  PlutonHtmlEditorComponent,
  PLUTON_HTML_EDITOR_CONFIG,
  DEFAULT_PLUTON_HTML_EDITOR_CONFIG,
  DEFAULT_VARIABLE_PATTERN,
  DEFAULT_TEMPLATE_LOGIC_PATTERN,
  PLUTON_EDITOR_VAR_CLASS,
  PLUTON_EDITOR_TEMPLATE_CLASS,
  mergePlutonHtmlEditorConfig,
  scaleCssSize,
} from 'pluton-html-editor';

Desenvolvimento do repositório

O workspace aceita npm ou pnpm (sem packageManager fixo no package.json raiz).

cd editor-workspace

# npm
npm install
npm start
npm run build:lib
npm run test:lib

# pnpm (opcional)
pnpm install
pnpm start
pnpm run build:lib
pnpm run test:lib

Playground

Demo no navegador: StackBlitz

Após npm install ou pnpm install:

npm start
# ou: pnpm start

O demo inclui variáveis, lógica <% if %>, tipografia, blocos, colunas, tabela, seção e quebra de página.

Testes da biblioteca

npm run test:lib
# ou: pnpm run test:lib