@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 proptokens). - 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éclarationsUtilisation 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