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
|