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

@free-op/html-doc-studio

v0.1.4

Published

Figma-like WYSIWYG HTML document editor built on a node tree, exportable to standalone HTML.

Downloads

547

Readme

HTML Doc Studio

Éditeur WYSIWYG inspiré de Figma, distribué comme librairie React npm. Le modèle de données est un arbre de nœuds représentant directement des éléments HTML/CSS, emboîtés par composition stricte parent → enfants. Le document peut être exporté en fichier HTML autonome (HTML + CSS intégrés, images embarquées, aucune dépendance externe).

  • Stack : React (hooks) · TypeScript strict · Vite · Zustand · Oxlint
  • Aucune librairie UI lourde — l'interface est en CSS custom (esthétique Figma).

Références détaillées : SPEC.md (spécification fonctionnelle) et ARCHITECTURE.md (décisions techniques).

Fonctionnalités

  • Canvas WYSIWYG temps réel : sélection, déplacement (drag), redimensionnement (handles) et rotation (poignée au-dessus du nœud) — disponibles aussi bien en mode libre qu'en mode document, quelle que soit la position de l'élément. Rendu fidèle au HTML exporté.
  • Ribbon en haut de l'éditeur (type Word) : onglets Accueil / Insertion / Page / Affichage, groupes de commandes, chevron d'overflow quand la fenêtre est trop étroite.
  • Calques : arbre hiérarchique, développement/repli, visibilité et verrouillage, glisser-déposer pour réordonner / re-parenter, renommage inline.
  • Composants réutilisables (onglet Composants du panneau latéral, type Assets Figma) : liste, recherche, insertion par clic ou glisser-déposer, renommage, mise à jour depuis une sélection, suppression (détache les instances en nœuds natifs) et propagation maître → instance.
  • Édition inline du texte par double-clic sur le canvas (validation sur blur/Entrée, annulation sur Échap).
  • Tokens {{key}} : placeholders résolus à l'export (liste par défaut + personnalisation via la prop tokens).
  • Inspecteur : propriétés contextuelles selon le type de nœud sélectionné + formulaire de création de composant.
  • Mode document paginé type Word : pages (A4 / Letter / custom), marges, sauts de page, sections, en-têtes/pieds de page.
  • Images : encodage base64 (table d'assets), recadrage / fit.
  • Overflow : détection visuelle du dépassement, modes clip / scroll.
  • Thème clair / sombre, plein écran, undo / redo, export HTML autonome.

Démarrage rapide

npm install
npm run dev        # lance l'exemple de démonstration via Vite
npm run build      # build librairie (dist/) + exemple (example/dist/)
npm run build:lib  # librairie seule (ESM + CJS + .d.ts)
npm run lint       # oxlint
npx tsc -p tsconfig.lib.json   # typecheck + déclarations

Utilisation de la librairie

Intégration React minimale

import { WysiwygEditor, type EditorNode } from 'html-doc-studio';

function App() {
  const handleChange = (tree: EditorNode) => {
    console.log('Document modifié', tree);
  };

  const handleExport = (html: string) => {
    const blob = new Blob([html], { type: 'text/html' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'document.html';
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <WysiwygEditor
      theme="light"
      onChange={handleChange}
      onExportHtml={handleExport}
    />
  );
}

export default App;

CSS : les styles de l'éditeur sont injectés automatiquement par le bundle (dist/index.js). Aucun import CSS explicite n'est requis ; il suffit d'importer <WysiwygEditor />.

Props du composant

| Prop | Type | Défaut | Description | |------|------|--------|-------------| | theme | 'light' \| 'dark' | 'light' | Thème de l'UI éditeur | | customNodes | NodeTypeDefinition[] | [] | Types de nœuds injectés par l'hôte | | tokens | Tokens | {} | Dictionnaire de tokens fusionné sur DEFAULT_TOKENS, résolu à l'export | | initial | string | — | HTML de départ parsé en arbre | | initialTree | EditorNode | — | Arbre pré-construit (prioritaire sur initial) | | onChange | (tree) => void | — | Callback à chaque mutation | | onHtmlChange | (html: string) => void | — | Appelé à chaque changement avec le HTML généré (lisible par un service externe) | | resolveTokensInCanvas | boolean | false | Résout les tokens {{key}} directement sur le canvas (aperçu) | | onExportHtml | (html) => void | — | Callback quand l'utilisateur exporte |

Tokens personnalisés

<WysiwygEditor
  tokens={{
    prenom: 'Henri',
    organisation: 'ACME',
  }}
/>

Les {{key}} présents dans les nœuds texte sont résolus au moment de l'export (le canvas conserve le texte brut avec les {{...}}). Pour voir la valeur résolue dans le canvas (aperçu), passe resolveTokensInCanvas :

<WysiwygEditor tokens={{ prenom: 'Henri' }} resolveTokensInCanvas />

Helpers publics :

import { DEFAULT_TOKENS, resolveTokens, type Tokens } from 'html-doc-studio';

const merged: Tokens = { ...DEFAULT_TOKENS, prenom: 'Henri' };
const html = resolveTokens('Bonjour {{prenom}}', merged); // "Bonjour Henri"

API impérative (ref)

Le composant expose une API impérative via ref, lisible par des services externes (ex. sauvegarde auto, synchronisation, aperçu hors-contexte) :

import { useRef } from 'react';
import { WysiwygEditor, type WysiwygEditorApi } from 'html-doc-studio';

function App() {
  const editor = useRef<WysiwygEditorApi>(null);

  const sauvegarder = () => {
    const html = editor.current?.getHtml();        // HTML complet actuel
    console.log(editor.current?.getTree());        // arbre de nœuds actuel
  };

  return <WysiwygEditor ref={editor} onChange={sauvegarder} />;
}

| Méthode | Retour | Description | |---------|--------|-------------| | getTree() | EditorNode | L'arbre de nœuds courant | | getHtml() | string | Le HTML complet généré à l'instant T | | setTokens(tokens) | void | Met à jour les tokens de l'instance | | subscribe(listener) | () => void | Écoute les changements (retourne la fonction de désabonnement) |

Combinée à onHtmlChange, elle permet de récupérer le HTML à chaque changement (push) ou à la demande (pull) pour des services externes.

Nœuds personnalisés via customNodes

import { WysiwygEditor, type NodeTypeDefinition } from 'html-doc-studio';

const myBadge: NodeTypeDefinition = {
  type: 'badge',
  label: 'Badge',
  canHaveChildren: false,
  isLeaf: true,
  isPageLevel: false,
  defaultStyle: {},
  defaultName: 'Badge',
  icon: 'badge',
  defaultChildCount: 0,
  isSection: false,
};

function App() {
  return <WysiwygEditor customNodes={[myBadge]} />;
}

Import HTML et export programmatique

import { htmlToTree, generateHtml, exportHtmlFile } from 'html-doc-studio';

const tree = htmlToTree('<h1>Bonjour</h1><p>Mon document</p>');

const html = generateHtml(tree);   // HTML autonome (CSS intégré, images data:) 
// exportHtml(tree)                // même HTML, avec gestion des assets
// exportHtmlFile(tree)                    // télécharge document.html
// exportHtmlFile(tree, 'facture.html')    // télécharge avec un nom personnalisé

Architecture (en bref)

Le moteur est découplé de l'UI : un arbre de nœuds (EditorNode) avec composition Flutter-like, un NodeRegistry extensible (principe Ouvert/Fermé : types natifs, personnalisés et hôtes passent tous par le même pipeline de rendu / inspection / export), et un store Zustand pour l'état courant, la sélection et l'historique. Voir ARCHITECTURE.md pour le détail.

Structure des dossiers

src/
  core/        # moteur d'arbre de nœuds : types, registry, utils (agnostique UI)
  nodes/       # définitions des types de nœuds natifs
  assets/      # table d'assets images (encodage base64)
  pagination/  # utilitaires document paginé
  rendering/   # rendu canvas WYSIWYG (NodeRenderer, OverflowDetector)
  export/      # génération du HTML/CSS final (autonome, data URLs, @page)
  import/      # import HTML → arbre (htmlToTree)
  state/       # store Zustand (arbre, sélection, historique, thème, tokens)
  ui/          # ribbon, layers panel, composants, inspector
  hooks/       # hooks réutilisables (manipulation, useNodeTypes, useCustomComponents)
example/       # app de démonstration consommant la librairie