12 KiB
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
import { MarkdownViewerComponent } from './components/markdown-viewer/markdown-viewer.component';
@Component({
imports: [MarkdownViewerComponent]
})
Exemple basique
<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
<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
import { SmartFileViewerComponent } from './components/smart-file-viewer/smart-file-viewer.component';
Exemple
<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
// 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
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
> [!NOTE]
> Ceci est une note informative
> [!TIP]
> Conseil utile pour l'utilisateur
> [!WARNING]
> Attention, soyez prudent
> [!DANGER]
> Action dangereuse, évitez cela
Math LaTeX
Inline:
La formule $E = mc^2$ est célèbre.
Block:
$$
\frac{-b \pm \sqrt{b^2 - 4ac}}{2a}
$$
Diagrammes Mermaid
\`\`\`mermaid
graph TD
A[Start] --> B{Decision}
B -->|Yes| C[Action 1]
B -->|No| D[Action 2]
\`\`\`
Code avec syntax highlighting
\`\`\`typescript
function hello(name: string): string {
return `Hello ${name}!`;
}
\`\`\`
WikiLinks
[[Note Example]]
[[Note Example#Section]]
[[Note Example|Alias personnalisé]]
Tags inline
Les tags inline fonctionnent: #test #markdown #playground
Task lists
- [ ] Tâche non cochée
- [x] Tâche cochée
- [ ] Autre tâche en attente
Tables
| Colonne 1 | Colonne 2 | Colonne 3 |
|-----------|-----------|-----------|
| A | B | C |
| D | E | F |
Footnotes
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 :
---
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.
// 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 :
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é :
if (this.canUseFastPath(markdown)) {
return this.md.render(markdown, env);
}
Tests
Exécuter les tests
# 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
: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
/* 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 :
import { MarkdownPlaygroundComponent } from './app/features/tests/markdown-playground/markdown-playground.component';
// Accessible via la route /markdown-playground
Intégration dans une application
@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
- Vérifier que le contenu est bien passé au composant
- Vérifier la console pour les erreurs de rendu
- Vérifier que
MarkdownServiceest bien injecté
Les images ne se chargent pas
- Vérifier les chemins des images
- Vérifier les CORS si images externes
- Vérifier que le lazy loading est activé
Excalidraw ne s'affiche pas
- Vérifier que le fichier a l'extension
.excalidraw.md - Vérifier que le contenu contient un bloc
compressed-json - Vérifier que
DrawingsEditorComponentest importé
Erreurs TypeScript
# 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 :
- Lire le guide d'architecture dans
/docs/ARCHITECTURE/ - Ajouter des tests pour toute nouvelle fonctionnalité
- Suivre les conventions de code existantes
- 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