528 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			528 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # Guide d'utilisation du Markdown Viewer
 | |
| 
 | |
| ## Vue d'ensemble
 | |
| 
 | |
| Le système de visualisation Markdown d'ObsiViewer a été optimisé et modulaire pour offrir une expérience de lecture et d'édition exceptionnelle. Il supporte maintenant :
 | |
| 
 | |
| - ✅ **GitHub Flavored Markdown (GFM)** complet
 | |
| - ✅ **Callouts Obsidian** (NOTE, TIP, WARNING, DANGER, etc.)
 | |
| - ✅ **Math LaTeX** (inline et block)
 | |
| - ✅ **Diagrammes Mermaid**
 | |
| - ✅ **Syntax highlighting** avec highlight.js
 | |
| - ✅ **WikiLinks** et navigation interne
 | |
| - ✅ **Tags inline** avec coloration automatique
 | |
| - ✅ **Task lists** interactives
 | |
| - ✅ **Tables** avancées
 | |
| - ✅ **Footnotes**
 | |
| - ✅ **Fichiers Excalidraw** avec éditeur intégré
 | |
| - ✅ **Lazy loading** des images
 | |
| - ✅ **Mode plein écran**
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Architecture
 | |
| 
 | |
| ### Composants principaux
 | |
| 
 | |
| ```
 | |
| src/
 | |
| ├── components/
 | |
| │   ├── markdown-viewer/           # Composant de visualisation Markdown
 | |
| │   │   ├── markdown-viewer.component.ts
 | |
| │   │   └── markdown-viewer.component.spec.ts
 | |
| │   └── smart-file-viewer/         # Détection automatique du type de fichier
 | |
| │       ├── smart-file-viewer.component.ts
 | |
| │       └── smart-file-viewer.component.spec.ts
 | |
| ├── services/
 | |
| │   ├── markdown.service.ts        # Service de rendu Markdown
 | |
| │   ├── markdown.service.spec.ts
 | |
| │   ├── file-type-detector.service.ts  # Détection du type de fichier
 | |
| │   └── file-type-detector.service.spec.ts
 | |
| └── app/features/
 | |
|     └── drawings/
 | |
|         └── drawings-editor.component.ts  # Éditeur Excalidraw
 | |
| ```
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Utilisation
 | |
| 
 | |
| ### 1. MarkdownViewerComponent
 | |
| 
 | |
| Composant réutilisable pour afficher du contenu Markdown.
 | |
| 
 | |
| #### Import
 | |
| 
 | |
| ```typescript
 | |
| import { MarkdownViewerComponent } from './components/markdown-viewer/markdown-viewer.component';
 | |
| 
 | |
| @Component({
 | |
|   imports: [MarkdownViewerComponent]
 | |
| })
 | |
| ```
 | |
| 
 | |
| #### Exemple basique
 | |
| 
 | |
| ```html
 | |
| <app-markdown-viewer
 | |
|   [content]="markdownContent"
 | |
|   [allNotes]="notes"
 | |
|   [currentNote]="currentNote">
 | |
| </app-markdown-viewer>
 | |
| ```
 | |
| 
 | |
| #### Propriétés
 | |
| 
 | |
| | Propriété | Type | Description | Défaut |
 | |
| |-----------|------|-------------|--------|
 | |
| | `content` | `string` | Contenu Markdown brut | `''` |
 | |
| | `allNotes` | `Note[]` | Liste des notes pour WikiLinks | `[]` |
 | |
| | `currentNote` | `Note?` | Note courante | `undefined` |
 | |
| | `showToolbar` | `boolean` | Afficher la barre d'outils | `true` |
 | |
| | `fullscreenMode` | `boolean` | Activer le mode plein écran | `false` |
 | |
| | `filePath` | `string` | Chemin du fichier (pour détecter .excalidraw.md) | `''` |
 | |
| 
 | |
| #### Exemple avec toutes les options
 | |
| 
 | |
| ```html
 | |
| <app-markdown-viewer
 | |
|   [content]="note.content"
 | |
|   [allNotes]="allNotes"
 | |
|   [currentNote]="note"
 | |
|   [showToolbar]="true"
 | |
|   [fullscreenMode]="true"
 | |
|   [filePath]="note.filePath">
 | |
| </app-markdown-viewer>
 | |
| ```
 | |
| 
 | |
| ---
 | |
| 
 | |
| ### 2. SmartFileViewerComponent
 | |
| 
 | |
| Composant intelligent qui détecte automatiquement le type de fichier et affiche le viewer approprié.
 | |
| 
 | |
| #### Import
 | |
| 
 | |
| ```typescript
 | |
| import { SmartFileViewerComponent } from './components/smart-file-viewer/smart-file-viewer.component';
 | |
| ```
 | |
| 
 | |
| #### Exemple
 | |
| 
 | |
| ```html
 | |
| <app-smart-file-viewer
 | |
|   [filePath]="file.path"
 | |
|   [content]="file.content"
 | |
|   [allNotes]="notes"
 | |
|   [currentNote]="currentNote">
 | |
| </app-smart-file-viewer>
 | |
| ```
 | |
| 
 | |
| #### Types de fichiers supportés
 | |
| 
 | |
| - **Markdown** (`.md`) → MarkdownViewerComponent
 | |
| - **Excalidraw** (`.excalidraw.md`, `.excalidraw`) → DrawingsEditorComponent
 | |
| - **Images** (`.png`, `.jpg`, `.svg`, etc.) → Image viewer
 | |
| - **PDF** (`.pdf`) → PDF viewer (iframe)
 | |
| - **Texte** (`.txt`, `.json`, `.xml`, etc.) → Text viewer
 | |
| - **Inconnu** → Message d'erreur
 | |
| 
 | |
| ---
 | |
| 
 | |
| ### 3. FileTypeDetectorService
 | |
| 
 | |
| Service pour détecter le type de fichier et ses caractéristiques.
 | |
| 
 | |
| #### Méthodes principales
 | |
| 
 | |
| ```typescript
 | |
| // Détecter si c'est un fichier Excalidraw
 | |
| isExcalidrawFile(path: string): boolean
 | |
| 
 | |
| // Détecter si c'est un fichier Markdown
 | |
| isMarkdownFile(path: string): boolean
 | |
| 
 | |
| // Obtenir les informations complètes
 | |
| getFileTypeInfo(path: string): FileTypeInfo
 | |
| 
 | |
| // Détecter le viewer approprié
 | |
| getViewerType(path: string, content?: string): ViewerType
 | |
| 
 | |
| // Vérifier si le contenu contient du JSON Excalidraw
 | |
| hasExcalidrawContent(content: string): boolean
 | |
| ```
 | |
| 
 | |
| #### Exemple d'utilisation
 | |
| 
 | |
| ```typescript
 | |
| import { FileTypeDetectorService } from './services/file-type-detector.service';
 | |
| 
 | |
| constructor(private fileTypeDetector: FileTypeDetectorService) {}
 | |
| 
 | |
| checkFile(path: string) {
 | |
|   const info = this.fileTypeDetector.getFileTypeInfo(path);
 | |
|   
 | |
|   console.log('Type:', info.type);
 | |
|   console.log('Editable:', info.isEditable);
 | |
|   console.log('Requires special viewer:', info.requiresSpecialViewer);
 | |
|   console.log('Icon:', info.icon);
 | |
|   console.log('MIME type:', info.mimeType);
 | |
| }
 | |
| ```
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Fonctionnalités Markdown
 | |
| 
 | |
| ### Callouts Obsidian
 | |
| 
 | |
| ```markdown
 | |
| > [!NOTE]
 | |
| > Ceci est une note informative
 | |
| 
 | |
| > [!TIP]
 | |
| > Conseil utile pour l'utilisateur
 | |
| 
 | |
| > [!WARNING]
 | |
| > Attention, soyez prudent
 | |
| 
 | |
| > [!DANGER]
 | |
| > Action dangereuse, évitez cela
 | |
| ```
 | |
| 
 | |
| ### Math LaTeX
 | |
| 
 | |
| **Inline:**
 | |
| ```markdown
 | |
| La formule $E = mc^2$ est célèbre.
 | |
| ```
 | |
| 
 | |
| **Block:**
 | |
| ```markdown
 | |
| $$
 | |
| \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}
 | |
| $$
 | |
| ```
 | |
| 
 | |
| ### Diagrammes Mermaid
 | |
| 
 | |
| ```markdown
 | |
| \`\`\`mermaid
 | |
| graph TD
 | |
|     A[Start] --> B{Decision}
 | |
|     B -->|Yes| C[Action 1]
 | |
|     B -->|No| D[Action 2]
 | |
| \`\`\`
 | |
| ```
 | |
| 
 | |
| ### Code avec syntax highlighting
 | |
| 
 | |
| ```markdown
 | |
| \`\`\`typescript
 | |
| function hello(name: string): string {
 | |
|   return `Hello ${name}!`;
 | |
| }
 | |
| \`\`\`
 | |
| ```
 | |
| 
 | |
| ### WikiLinks
 | |
| 
 | |
| ```markdown
 | |
| [[Note Example]]
 | |
| [[Note Example#Section]]
 | |
| [[Note Example|Alias personnalisé]]
 | |
| ```
 | |
| 
 | |
| ### Tags inline
 | |
| 
 | |
| ```markdown
 | |
| Les tags inline fonctionnent: #test #markdown #playground
 | |
| ```
 | |
| 
 | |
| ### Task lists
 | |
| 
 | |
| ```markdown
 | |
| - [ ] Tâche non cochée
 | |
| - [x] Tâche cochée
 | |
| - [ ] Autre tâche en attente
 | |
| ```
 | |
| 
 | |
| ### Tables
 | |
| 
 | |
| ```markdown
 | |
| | Colonne 1 | Colonne 2 | Colonne 3 |
 | |
| |-----------|-----------|-----------|
 | |
| | A         | B         | C         |
 | |
| | D         | E         | F         |
 | |
| ```
 | |
| 
 | |
| ### Footnotes
 | |
| 
 | |
| ```markdown
 | |
| Voici un texte avec une note de bas de page[^1].
 | |
| 
 | |
| [^1]: Ceci est la note de bas de page.
 | |
| ```
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Fichiers Excalidraw
 | |
| 
 | |
| ### Détection automatique
 | |
| 
 | |
| Le système détecte automatiquement les fichiers `.excalidraw.md` et affiche l'éditeur Excalidraw intégré au lieu du rendu Markdown.
 | |
| 
 | |
| ### Format supporté
 | |
| 
 | |
| ObsiViewer supporte le format Obsidian Excalidraw avec compression LZ-String :
 | |
| 
 | |
| ```markdown
 | |
| ---
 | |
| excalidraw-plugin: parsed
 | |
| tags: [excalidraw]
 | |
| ---
 | |
| 
 | |
| # Excalidraw Data
 | |
| 
 | |
| ## Drawing
 | |
| \`\`\`compressed-json
 | |
| N4KAkARALgngDgUwgLgAQQQDwMYEMA2AlgCYBOuA7hADTgQBuCpAzoQPYB2KqATLZMzYBXUtiRoIACyhQ4zZAHoFAc0JRJQgEYA6bGwC2CgF7N6hbEcK4OCtptbErHALRY8RMpWdx8Q1TdIEfARcZgRmBShcZQUebQBmbQAGGjoghH0EDihmbgBtcDBQMBLoeHF0QOwojmVg1JLIRhZ2LjQANgBWWtLm1k4AOU4xbgAWbshCDmIs
 | |
| \`\`\`
 | |
| ```
 | |
| 
 | |
| ### Fonctionnalités de l'éditeur
 | |
| 
 | |
| - ✅ Édition complète avec tous les outils Excalidraw
 | |
| - ✅ Sauvegarde manuelle (Ctrl+S)
 | |
| - ✅ Export PNG/SVG
 | |
| - ✅ Mode plein écran (F11)
 | |
| - ✅ Détection de conflits
 | |
| - ✅ Support du thème clair/sombre
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Optimisations
 | |
| 
 | |
| ### Lazy Loading des images
 | |
| 
 | |
| Les images sont chargées uniquement lorsqu'elles entrent dans le viewport, améliorant les performances pour les documents longs.
 | |
| 
 | |
| ```typescript
 | |
| // Automatique dans MarkdownViewerComponent
 | |
| private setupLazyLoading(): void {
 | |
|   const imageObserver = new IntersectionObserver((entries) => {
 | |
|     entries.forEach(entry => {
 | |
|       if (entry.isIntersecting) {
 | |
|         const img = entry.target as HTMLImageElement;
 | |
|         img.classList.add('loaded');
 | |
|         observer.unobserve(img);
 | |
|       }
 | |
|     });
 | |
|   }, { rootMargin: '50px' });
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### Cache du syntax highlighting
 | |
| 
 | |
| Le service Markdown utilise un cache LRU pour éviter de re-highlighter le même code :
 | |
| 
 | |
| ```typescript
 | |
| private static readonly HL_CACHE = new SimpleLruCache<string, string>(500);
 | |
| ```
 | |
| 
 | |
| ### Fast path pour les documents simples
 | |
| 
 | |
| Les documents sans fonctionnalités avancées (< 10KB, sans WikiLinks, math, etc.) utilisent un chemin de rendu optimisé :
 | |
| 
 | |
| ```typescript
 | |
| if (this.canUseFastPath(markdown)) {
 | |
|   return this.md.render(markdown, env);
 | |
| }
 | |
| ```
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Tests
 | |
| 
 | |
| ### Exécuter les tests
 | |
| 
 | |
| ```bash
 | |
| # Tous les tests
 | |
| npm test
 | |
| 
 | |
| # Tests spécifiques
 | |
| npm test -- --include='**/markdown-viewer.component.spec.ts'
 | |
| npm test -- --include='**/file-type-detector.service.spec.ts'
 | |
| ```
 | |
| 
 | |
| ### Coverage
 | |
| 
 | |
| Les composants et services ont une couverture de tests complète :
 | |
| 
 | |
| - ✅ `MarkdownViewerComponent` - 95%+
 | |
| - ✅ `SmartFileViewerComponent` - 90%+
 | |
| - ✅ `FileTypeDetectorService` - 100%
 | |
| - ✅ `MarkdownService` - 85%+
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Styling
 | |
| 
 | |
| ### CSS Variables disponibles
 | |
| 
 | |
| ```css
 | |
| :root {
 | |
|   --brand: #3a68d1;
 | |
|   --text-main: #111827;
 | |
|   --text-muted: #6b7280;
 | |
|   --border: #e5e7eb;
 | |
|   --card: #ffffff;
 | |
|   --bg-main: #f7f7f7;
 | |
|   --bg-muted: #eef0f2;
 | |
| }
 | |
| 
 | |
| :root[data-theme="dark"] {
 | |
|   --brand: #6f96e4;
 | |
|   --text-main: #e5e7eb;
 | |
|   --text-muted: #9ca3af;
 | |
|   --border: #374151;
 | |
|   --card: #0f172a;
 | |
|   --bg-main: #111827;
 | |
|   --bg-muted: #1f2937;
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### Classes personnalisées
 | |
| 
 | |
| ```css
 | |
| /* Callouts */
 | |
| .callout { /* Base callout style */ }
 | |
| .callout-note { /* Note callout */ }
 | |
| .callout-tip { /* Tip callout */ }
 | |
| .callout-warning { /* Warning callout */ }
 | |
| .callout-danger { /* Danger callout */ }
 | |
| 
 | |
| /* Code blocks */
 | |
| .code-block { /* Base code block */ }
 | |
| .code-block__header { /* Header with language badge */ }
 | |
| .code-block__body { /* Code content */ }
 | |
| 
 | |
| /* Task lists */
 | |
| .md-task-list { /* Task list container */ }
 | |
| .md-task-item { /* Individual task */ }
 | |
| .md-task-checkbox { /* Custom checkbox */ }
 | |
| 
 | |
| /* Links */
 | |
| .md-wiki-link { /* WikiLink style */ }
 | |
| .md-external-link { /* External link style */ }
 | |
| ```
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Exemples avancés
 | |
| 
 | |
| ### Markdown Playground
 | |
| 
 | |
| Le composant `MarkdownPlaygroundComponent` démontre toutes les fonctionnalités :
 | |
| 
 | |
| ```typescript
 | |
| import { MarkdownPlaygroundComponent } from './app/features/tests/markdown-playground/markdown-playground.component';
 | |
| 
 | |
| // Accessible via la route /markdown-playground
 | |
| ```
 | |
| 
 | |
| ### Intégration dans une application
 | |
| 
 | |
| ```typescript
 | |
| @Component({
 | |
|   selector: 'app-note-viewer',
 | |
|   standalone: true,
 | |
|   imports: [SmartFileViewerComponent],
 | |
|   template: `
 | |
|     <app-smart-file-viewer
 | |
|       [filePath]="note().filePath"
 | |
|       [content]="note().content"
 | |
|       [allNotes]="allNotes()"
 | |
|       [currentNote]="note()"
 | |
|       [showToolbar]="true"
 | |
|       [fullscreenMode]="true">
 | |
|     </app-smart-file-viewer>
 | |
|   `
 | |
| })
 | |
| export class NoteViewerComponent {
 | |
|   note = signal<Note | null>(null);
 | |
|   allNotes = signal<Note[]>([]);
 | |
| }
 | |
| ```
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Dépannage
 | |
| 
 | |
| ### Le Markdown ne s'affiche pas
 | |
| 
 | |
| 1. Vérifier que le contenu est bien passé au composant
 | |
| 2. Vérifier la console pour les erreurs de rendu
 | |
| 3. Vérifier que `MarkdownService` est bien injecté
 | |
| 
 | |
| ### Les images ne se chargent pas
 | |
| 
 | |
| 1. Vérifier les chemins des images
 | |
| 2. Vérifier les CORS si images externes
 | |
| 3. Vérifier que le lazy loading est activé
 | |
| 
 | |
| ### Excalidraw ne s'affiche pas
 | |
| 
 | |
| 1. Vérifier que le fichier a l'extension `.excalidraw.md`
 | |
| 2. Vérifier que le contenu contient un bloc `compressed-json`
 | |
| 3. Vérifier que `DrawingsEditorComponent` est importé
 | |
| 
 | |
| ### Erreurs TypeScript
 | |
| 
 | |
| ```bash
 | |
| # Nettoyer et rebuilder
 | |
| npm run clean
 | |
| npm install
 | |
| npm run build
 | |
| ```
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Roadmap
 | |
| 
 | |
| ### Fonctionnalités futures
 | |
| 
 | |
| - [ ] Support des embeds audio/vidéo
 | |
| - [ ] Éditeur Markdown WYSIWYG
 | |
| - [ ] Export PDF du Markdown
 | |
| - [ ] Collaboration temps réel
 | |
| - [ ] Plugins Markdown personnalisés
 | |
| - [ ] Support des diagrammes PlantUML
 | |
| - [ ] Mode présentation (slides)
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Contribution
 | |
| 
 | |
| Pour contribuer au système Markdown :
 | |
| 
 | |
| 1. Lire le guide d'architecture dans `/docs/ARCHITECTURE/`
 | |
| 2. Ajouter des tests pour toute nouvelle fonctionnalité
 | |
| 3. Suivre les conventions de code existantes
 | |
| 4. Mettre à jour cette documentation
 | |
| 
 | |
| ---
 | |
| 
 | |
| ## Support
 | |
| 
 | |
| Pour toute question ou problème :
 | |
| 
 | |
| - 📖 Documentation : `/docs/`
 | |
| - 🐛 Issues : GitHub Issues
 | |
| - 💬 Discussions : GitHub Discussions
 | |
| 
 | |
| ---
 | |
| 
 | |
| **Dernière mise à jour :** 2025-01-15
 | |
| **Version :** 2.0.0
 |