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
 |