tests
This commit is contained in:
parent
55a7a06daa
commit
7fd4f5bf8e
@ -14,6 +14,7 @@
|
|||||||
[centerPanelWidth]="centerPanelWidth()"
|
[centerPanelWidth]="centerPanelWidth()"
|
||||||
[searchTerm]="sidebarSearchTerm()"
|
[searchTerm]="sidebarSearchTerm()"
|
||||||
[tags]="allTags()"
|
[tags]="allTags()"
|
||||||
|
[activeView]="activeView()"
|
||||||
(noteSelected)="selectNote($event)"
|
(noteSelected)="selectNote($event)"
|
||||||
(tagClicked)="handleTagClick($event)"
|
(tagClicked)="handleTagClick($event)"
|
||||||
(wikiLinkActivated)="handleWikiLink($event)"
|
(wikiLinkActivated)="handleWikiLink($event)"
|
||||||
@ -25,6 +26,7 @@
|
|||||||
(navigateHeading)="scrollToHeading($event)"
|
(navigateHeading)="scrollToHeading($event)"
|
||||||
(searchTermChange)="onSidebarSearchTermChange($event)"
|
(searchTermChange)="onSidebarSearchTermChange($event)"
|
||||||
(searchOptionsChange)="onHeaderSearchOptionsChange($event)"
|
(searchOptionsChange)="onHeaderSearchOptionsChange($event)"
|
||||||
|
(markdownPlaygroundSelected)="setView('markdown-playground')"
|
||||||
></app-shell-nimbus-layout>
|
></app-shell-nimbus-layout>
|
||||||
} @else {
|
} @else {
|
||||||
<main class="relative flex min-h-screen flex-col bg-bg-main text-text-main lg:flex-row lg:h-screen lg:overflow-hidden">
|
<main class="relative flex min-h-screen flex-col bg-bg-main text-text-main lg:flex-row lg:h-screen lg:overflow-hidden">
|
||||||
@ -525,6 +527,10 @@
|
|||||||
(nodeSelected)="selectNoteFromGraph($event)">
|
(nodeSelected)="selectNoteFromGraph($event)">
|
||||||
</app-graph-view-container-v2>
|
</app-graph-view-container-v2>
|
||||||
</div>
|
</div>
|
||||||
|
} @else if (activeView() === 'markdown-playground') {
|
||||||
|
<div class="h-[calc(100vh-180px)] lg:h-[calc(100vh-140px)]">
|
||||||
|
<app-markdown-playground></app-markdown-playground>
|
||||||
|
</div>
|
||||||
} @else {
|
} @else {
|
||||||
@if (activeView() === 'drawings') {
|
@if (activeView() === 'drawings') {
|
||||||
@if (currentDrawingPath()) {
|
@if (currentDrawingPath()) {
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import { GraphInlineSettingsComponent } from './app/graph/ui/inline-settings-pan
|
|||||||
import { DrawingsEditorComponent } from './app/features/drawings/drawings-editor.component';
|
import { DrawingsEditorComponent } from './app/features/drawings/drawings-editor.component';
|
||||||
import { DrawingsFileService, ExcalidrawScene } from './app/features/drawings/drawings-file.service';
|
import { DrawingsFileService, ExcalidrawScene } from './app/features/drawings/drawings-file.service';
|
||||||
import { AppShellNimbusLayoutComponent } from './app/layout/app-shell-nimbus/app-shell-nimbus.component';
|
import { AppShellNimbusLayoutComponent } from './app/layout/app-shell-nimbus/app-shell-nimbus.component';
|
||||||
|
import { MarkdownPlaygroundComponent } from './app/features/tests/markdown-playground/markdown-playground.component';
|
||||||
import { RawViewOverlayComponent } from './shared/overlays/raw-view-overlay.component';
|
import { RawViewOverlayComponent } from './shared/overlays/raw-view-overlay.component';
|
||||||
import { BookmarksPanelComponent } from './components/bookmarks-panel/bookmarks-panel.component';
|
import { BookmarksPanelComponent } from './components/bookmarks-panel/bookmarks-panel.component';
|
||||||
import { AddBookmarkModalComponent, type BookmarkFormData, type BookmarkDeleteEvent } from './components/add-bookmark-modal/add-bookmark-modal.component';
|
import { AddBookmarkModalComponent, type BookmarkFormData, type BookmarkDeleteEvent } from './components/add-bookmark-modal/add-bookmark-modal.component';
|
||||||
@ -61,6 +62,7 @@ interface TocEntry {
|
|||||||
SearchPanelComponent,
|
SearchPanelComponent,
|
||||||
DrawingsEditorComponent,
|
DrawingsEditorComponent,
|
||||||
AppShellNimbusLayoutComponent,
|
AppShellNimbusLayoutComponent,
|
||||||
|
MarkdownPlaygroundComponent,
|
||||||
],
|
],
|
||||||
templateUrl: './app.component.simple.html',
|
templateUrl: './app.component.simple.html',
|
||||||
styleUrls: ['./app.component.css'],
|
styleUrls: ['./app.component.css'],
|
||||||
@ -86,7 +88,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
isSidebarOpen = signal<boolean>(true);
|
isSidebarOpen = signal<boolean>(true);
|
||||||
isOutlineOpen = signal<boolean>(true);
|
isOutlineOpen = signal<boolean>(true);
|
||||||
outlineTab = signal<'outline' | 'settings'>('outline');
|
outlineTab = signal<'outline' | 'settings'>('outline');
|
||||||
activeView = signal<'files' | 'graph' | 'tags' | 'search' | 'calendar' | 'bookmarks' | 'drawings'>('files');
|
activeView = signal<'files' | 'graph' | 'tags' | 'search' | 'calendar' | 'bookmarks' | 'drawings' | 'markdown-playground'>('files');
|
||||||
currentDrawingPath = signal<string | null>(null);
|
currentDrawingPath = signal<string | null>(null);
|
||||||
selectedNoteId = signal<string>('');
|
selectedNoteId = signal<string>('');
|
||||||
sidebarSearchTerm = signal<string>('');
|
sidebarSearchTerm = signal<string>('');
|
||||||
@ -856,7 +858,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
handle?.addEventListener('lostpointercapture', cleanup);
|
handle?.addEventListener('lostpointercapture', cleanup);
|
||||||
}
|
}
|
||||||
|
|
||||||
setView(view: 'files' | 'graph' | 'tags' | 'search' | 'calendar' | 'bookmarks' | 'drawings'): void {
|
setView(view: 'files' | 'graph' | 'tags' | 'search' | 'calendar' | 'bookmarks' | 'drawings' | 'markdown-playground'): void {
|
||||||
const previousView = this.activeView();
|
const previousView = this.activeView();
|
||||||
this.activeView.set(view);
|
this.activeView.set(view);
|
||||||
this.sidebarSearchTerm.set('');
|
this.sidebarSearchTerm.set('');
|
||||||
|
|||||||
10
src/app/core/guards/dev-only.guard.ts
Normal file
10
src/app/core/guards/dev-only.guard.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { CanMatchFn } from '@angular/router';
|
||||||
|
import { environment } from '../../../environments/environment';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Guard qui bloque l'accès aux routes en mode production.
|
||||||
|
* Utilisé pour protéger les routes de test et de développement.
|
||||||
|
*/
|
||||||
|
export const devOnlyGuard: CanMatchFn = () => {
|
||||||
|
return !environment.production;
|
||||||
|
};
|
||||||
@ -1,14 +1,16 @@
|
|||||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
import { FileExplorerComponent } from '../../../components/file-explorer/file-explorer.component';
|
import { FileExplorerComponent } from '../../../components/file-explorer/file-explorer.component';
|
||||||
import { QuickLinksComponent } from '../quick-links/quick-links.component';
|
import { QuickLinksComponent } from '../quick-links/quick-links.component';
|
||||||
import { ScrollableOverlayDirective } from '../../shared/overlay-scrollbar/scrollable-overlay.directive';
|
import { ScrollableOverlayDirective } from '../../shared/overlay-scrollbar/scrollable-overlay.directive';
|
||||||
import type { VaultNode, TagInfo } from '../../../types';
|
import type { VaultNode, TagInfo } from '../../../types';
|
||||||
|
import { environment } from '../../../environments/environment';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-nimbus-sidebar',
|
selector: 'app-nimbus-sidebar',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, FileExplorerComponent, QuickLinksComponent, ScrollableOverlayDirective],
|
imports: [CommonModule, RouterModule, FileExplorerComponent, QuickLinksComponent, ScrollableOverlayDirective],
|
||||||
host: { class: 'block h-full' },
|
host: { class: 'block h-full' },
|
||||||
template: `
|
template: `
|
||||||
<div class="h-full flex flex-col overflow-hidden select-none">
|
<div class="h-full flex flex-col overflow-hidden select-none">
|
||||||
@ -20,6 +22,22 @@ import type { VaultNode, TagInfo } from '../../../types';
|
|||||||
|
|
||||||
<!-- Content (scroll) -->
|
<!-- Content (scroll) -->
|
||||||
<div class="flex-1 overflow-y-auto min-h-0" appScrollableOverlay>
|
<div class="flex-1 overflow-y-auto min-h-0" appScrollableOverlay>
|
||||||
|
<!-- Section Tests (dev-only) -->
|
||||||
|
<section *ngIf="env.features.showTestSection" class="border-b border-gray-200 dark:border-gray-800">
|
||||||
|
<button class="w-full flex items-center justify-between px-3 py-2 text-sm font-semibold hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||||
|
(click)="open.tests = !open.tests">
|
||||||
|
<span>Section Tests</span>
|
||||||
|
<span class="text-xs text-gray-500">{{ open.tests ? '▾' : '▸' }}</span>
|
||||||
|
</button>
|
||||||
|
<div *ngIf="open.tests" class="px-3 py-2">
|
||||||
|
<button
|
||||||
|
(click)="onMarkdownPlaygroundClick()"
|
||||||
|
class="w-full text-left block text-sm px-2 py-1.5 rounded hover:bg-gray-100 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100">
|
||||||
|
Markdown Playground
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<!-- Quick Links accordion -->
|
<!-- Quick Links accordion -->
|
||||||
<section class="border-b border-gray-200 dark:border-gray-800">
|
<section class="border-b border-gray-200 dark:border-gray-800">
|
||||||
<button class="w-full flex items-center justify-between px-3 py-2 text-sm font-semibold hover:bg-gray-100 dark:hover:bg-gray-800"
|
<button class="w-full flex items-center justify-between px-3 py-2 text-sm font-semibold hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||||
@ -91,8 +109,14 @@ export class NimbusSidebarComponent {
|
|||||||
@Output() fileSelected = new EventEmitter<string>();
|
@Output() fileSelected = new EventEmitter<string>();
|
||||||
@Output() tagSelected = new EventEmitter<string>();
|
@Output() tagSelected = new EventEmitter<string>();
|
||||||
@Output() quickLinkSelected = new EventEmitter<string>();
|
@Output() quickLinkSelected = new EventEmitter<string>();
|
||||||
|
@Output() markdownPlaygroundSelected = new EventEmitter<void>();
|
||||||
|
|
||||||
open = { quick: true, folders: true, tags: false, trash: false };
|
env = environment;
|
||||||
|
open = { quick: true, folders: true, tags: false, trash: false, tests: true };
|
||||||
|
|
||||||
onQuickLink(id: string) { this.quickLinkSelected.emit(id); }
|
onQuickLink(id: string) { this.quickLinkSelected.emit(id); }
|
||||||
|
|
||||||
|
onMarkdownPlaygroundClick(): void {
|
||||||
|
this.markdownPlaygroundSelected.emit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,129 @@
|
|||||||
|
import { Component, signal, computed, inject } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { MarkdownService } from '../../../../services/markdown.service';
|
||||||
|
import { HttpClient, HttpClientModule } from '@angular/common/http';
|
||||||
|
|
||||||
|
const DEFAULT_MD_PATH = 'assets/samples/markdown-playground.md';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-markdown-playground',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule, FormsModule, HttpClientModule],
|
||||||
|
template: `
|
||||||
|
<div class="h-full flex flex-col bg-gray-50 dark:bg-gray-900">
|
||||||
|
<!-- Header -->
|
||||||
|
<header class="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 px-6 py-4">
|
||||||
|
<h1 class="text-2xl font-semibold text-gray-900 dark:text-gray-100">Markdown Playground</h1>
|
||||||
|
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||||||
|
Page de test interne pour valider tous les formatages Markdown supportés par ObsiViewer.
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Content -->
|
||||||
|
<div class="flex-1 flex gap-4 p-4 overflow-hidden">
|
||||||
|
<!-- Editor Panel -->
|
||||||
|
<div class="flex-1 flex flex-col bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden">
|
||||||
|
<div class="px-4 py-2 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-750">
|
||||||
|
<h2 class="text-sm font-semibold text-gray-700 dark:text-gray-300">Markdown Source</h2>
|
||||||
|
</div>
|
||||||
|
<textarea
|
||||||
|
[(ngModel)]="sample"
|
||||||
|
class="flex-1 p-4 font-mono text-sm bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 resize-none focus:outline-none"
|
||||||
|
placeholder="Entrez votre Markdown ici..."
|
||||||
|
spellcheck="false"
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Preview Panel -->
|
||||||
|
<div class="flex-1 flex flex-col bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden">
|
||||||
|
<div class="px-4 py-2 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-750 flex items-center justify-between">
|
||||||
|
<h2 class="text-sm font-semibold text-gray-700 dark:text-gray-300">Preview</h2>
|
||||||
|
<button
|
||||||
|
(click)="resetToDefault()"
|
||||||
|
class="text-xs px-3 py-1 rounded bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300"
|
||||||
|
>
|
||||||
|
Reset
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 overflow-auto p-4">
|
||||||
|
<div
|
||||||
|
class="prose prose-slate dark:prose-invert max-w-none"
|
||||||
|
[innerHTML]="renderedHtml()"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
styles: [`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
tab-size: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark textarea::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark textarea::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
`]
|
||||||
|
})
|
||||||
|
export class MarkdownPlaygroundComponent {
|
||||||
|
private markdownService = inject(MarkdownService);
|
||||||
|
private http = inject(HttpClient);
|
||||||
|
|
||||||
|
sample = signal<string>('');
|
||||||
|
|
||||||
|
renderedHtml = computed(() => {
|
||||||
|
const markdown = this.sample();
|
||||||
|
try {
|
||||||
|
return this.markdownService.render(markdown, [], undefined);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Markdown render error:', error);
|
||||||
|
return `<div class="text-red-500">Erreur de rendu: ${error}</div>`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.loadDefaultSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadDefaultSample(): void {
|
||||||
|
this.http.get(DEFAULT_MD_PATH, { responseType: 'text' }).subscribe({
|
||||||
|
next: (text) => this.sample.set(text ?? ''),
|
||||||
|
error: (err) => {
|
||||||
|
console.error('Failed to load default markdown:', err);
|
||||||
|
this.sample.set('');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
resetToDefault(): void {
|
||||||
|
this.loadDefaultSample();
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/app/features/tests/tests.routes.ts
Normal file
14
src/app/features/tests/tests.routes.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { Routes } from '@angular/router';
|
||||||
|
import { MarkdownPlaygroundComponent } from './markdown-playground/markdown-playground.component';
|
||||||
|
|
||||||
|
export const TESTS_ROUTES: Routes = [
|
||||||
|
{
|
||||||
|
path: 'markdown',
|
||||||
|
component: MarkdownPlaygroundComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
pathMatch: 'full',
|
||||||
|
redirectTo: 'markdown'
|
||||||
|
}
|
||||||
|
];
|
||||||
@ -15,11 +15,12 @@ import { NotesListComponent } from '../../features/list/notes-list.component';
|
|||||||
import { NimbusSidebarComponent } from '../../features/sidebar/nimbus-sidebar.component';
|
import { NimbusSidebarComponent } from '../../features/sidebar/nimbus-sidebar.component';
|
||||||
import { QuickLinksComponent } from '../../features/quick-links/quick-links.component';
|
import { QuickLinksComponent } from '../../features/quick-links/quick-links.component';
|
||||||
import { ScrollableOverlayDirective } from '../../shared/overlay-scrollbar/scrollable-overlay.directive';
|
import { ScrollableOverlayDirective } from '../../shared/overlay-scrollbar/scrollable-overlay.directive';
|
||||||
|
import { MarkdownPlaygroundComponent } from '../../features/tests/markdown-playground/markdown-playground.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-shell-nimbus-layout',
|
selector: 'app-shell-nimbus-layout',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, FileExplorerComponent, NoteViewerComponent, AppBottomNavigationComponent, AppSidebarDrawerComponent, AppTocOverlayComponent, SwipeNavDirective, NotesListComponent, NimbusSidebarComponent, QuickLinksComponent, ScrollableOverlayDirective],
|
imports: [CommonModule, FileExplorerComponent, NoteViewerComponent, AppBottomNavigationComponent, AppSidebarDrawerComponent, AppTocOverlayComponent, SwipeNavDirective, NotesListComponent, NimbusSidebarComponent, QuickLinksComponent, ScrollableOverlayDirective, MarkdownPlaygroundComponent],
|
||||||
template: `
|
template: `
|
||||||
<div class="h-screen flex flex-col bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
|
<div class="h-screen flex flex-col bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
|
||||||
<!-- Header (desktop/tablet), compact on mobile) -->
|
<!-- Header (desktop/tablet), compact on mobile) -->
|
||||||
@ -57,6 +58,7 @@ import { ScrollableOverlayDirective } from '../../shared/overlay-scrollbar/scrol
|
|||||||
(fileSelected)="noteSelected.emit($event)"
|
(fileSelected)="noteSelected.emit($event)"
|
||||||
(tagSelected)="onTagSelected($event)"
|
(tagSelected)="onTagSelected($event)"
|
||||||
(quickLinkSelected)="onQuickLink($event)"
|
(quickLinkSelected)="onQuickLink($event)"
|
||||||
|
(markdownPlaygroundSelected)="onMarkdownPlaygroundSelected()"
|
||||||
/>
|
/>
|
||||||
</aside>
|
</aside>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@ -117,7 +119,8 @@ import { ScrollableOverlayDirective } from '../../shared/overlay-scrollbar/scrol
|
|||||||
<!-- Note View + ToC -->
|
<!-- Note View + ToC -->
|
||||||
<section class="flex-1 relative min-w-0 flex">
|
<section class="flex-1 relative min-w-0 flex">
|
||||||
<div class="note-content-area flex-1 overflow-y-auto px-4 py-4 lg:px-8" appScrollableOverlay>
|
<div class="note-content-area flex-1 overflow-y-auto px-4 py-4 lg:px-8" appScrollableOverlay>
|
||||||
<app-note-viewer
|
<app-markdown-playground *ngIf="activeView === 'markdown-playground'"></app-markdown-playground>
|
||||||
|
<app-note-viewer *ngIf="activeView !== 'markdown-playground'"
|
||||||
[note]="selectedNote || null"
|
[note]="selectedNote || null"
|
||||||
[noteHtmlContent]="renderedNoteContent"
|
[noteHtmlContent]="renderedNoteContent"
|
||||||
[allNotes]="vault.allNotes()"
|
[allNotes]="vault.allNotes()"
|
||||||
@ -201,6 +204,7 @@ export class AppShellNimbusLayoutComponent {
|
|||||||
@Input() searchTerm = '';
|
@Input() searchTerm = '';
|
||||||
@Input() centerPanelWidth = 384;
|
@Input() centerPanelWidth = 384;
|
||||||
@Input() tags: TagInfo[] = [];
|
@Input() tags: TagInfo[] = [];
|
||||||
|
@Input() activeView: string = 'files';
|
||||||
|
|
||||||
@Output() noteSelected = new EventEmitter<string>();
|
@Output() noteSelected = new EventEmitter<string>();
|
||||||
@Output() tagClicked = new EventEmitter<string>();
|
@Output() tagClicked = new EventEmitter<string>();
|
||||||
@ -213,6 +217,7 @@ export class AppShellNimbusLayoutComponent {
|
|||||||
@Output() navigateHeading = new EventEmitter<string>();
|
@Output() navigateHeading = new EventEmitter<string>();
|
||||||
@Output() searchTermChange = new EventEmitter<string>();
|
@Output() searchTermChange = new EventEmitter<string>();
|
||||||
@Output() searchOptionsChange = new EventEmitter<any>();
|
@Output() searchOptionsChange = new EventEmitter<any>();
|
||||||
|
@Output() markdownPlaygroundSelected = new EventEmitter<void>();
|
||||||
|
|
||||||
folderFilter: string | null = null;
|
folderFilter: string | null = null;
|
||||||
listQuery: string = '';
|
listQuery: string = '';
|
||||||
@ -303,4 +308,8 @@ export class AppShellNimbusLayoutComponent {
|
|||||||
this.flyoutCloseTimer = null;
|
this.flyoutCloseTimer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMarkdownPlaygroundSelected(): void {
|
||||||
|
this.markdownPlaygroundSelected.emit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
129
src/assets/samples/markdown-playground.md
Normal file
129
src/assets/samples/markdown-playground.md
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
# Titre H1
|
||||||
|
## Titre H2
|
||||||
|
### Titre H3
|
||||||
|
|
||||||
|
**Gras**, *Italique*, ~~Barré~~, `inline code`, [lien externe](https://example.com)
|
||||||
|
|
||||||
|
> Blockquote / Citation
|
||||||
|
> Peut s'étendre sur plusieurs lignes
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Callout de type NOTE
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> Callout de type WARNING
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> Callout de type TIP
|
||||||
|
|
||||||
|
## Listes
|
||||||
|
|
||||||
|
- Liste non ordonnée
|
||||||
|
- Deuxième élément
|
||||||
|
- Sous-élément
|
||||||
|
- Troisième élément
|
||||||
|
|
||||||
|
1. Liste ordonnée
|
||||||
|
2. Deuxième élément
|
||||||
|
3. Troisième élément
|
||||||
|
|
||||||
|
## Tâches
|
||||||
|
|
||||||
|
- [ ] Tâche non cochée
|
||||||
|
- [x] Tâche cochée
|
||||||
|
- [ ] Autre tâche en attente
|
||||||
|
|
||||||
|
## Code
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function hello(name: string): string {
|
||||||
|
return `Hello ${name}!`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = hello("World");
|
||||||
|
console.log(result);
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const data = [1, 2, 3, 4, 5];
|
||||||
|
const doubled = data.map(x => x * 2);
|
||||||
|
console.log(doubled);
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
def fibonacci(n):
|
||||||
|
if n <= 1:
|
||||||
|
return n
|
||||||
|
return fibonacci(n-1) + fibonacci(n-2)
|
||||||
|
|
||||||
|
print(fibonacci(10))
|
||||||
|
```
|
||||||
|
|
||||||
|
## Mermaid
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
A[Start] --> B{Decision}
|
||||||
|
B -->|Yes| C[Action 1]
|
||||||
|
B -->|No| D[Action 2]
|
||||||
|
C --> E[End]
|
||||||
|
D --> E
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
Alice->>John: Hello John, how are you?
|
||||||
|
John-->>Alice: Great!
|
||||||
|
Alice-)John: See you later!
|
||||||
|
```
|
||||||
|
|
||||||
|
## Math (LaTeX)
|
||||||
|
|
||||||
|
Inline math: $E = mc^2$
|
||||||
|
|
||||||
|
Block math:
|
||||||
|
|
||||||
|
$$
|
||||||
|
\frac{-b \pm \sqrt{b^2 - 4ac}}{2a}
|
||||||
|
$$
|
||||||
|
|
||||||
|
$$
|
||||||
|
\int_{0}^{\infty} e^{-x^2} dx = \frac{\sqrt{\pi}}{2}
|
||||||
|
$$
|
||||||
|
|
||||||
|
## Tableaux
|
||||||
|
|
||||||
|
| Colonne 1 | Colonne 2 | Colonne 3 |
|
||||||
|
|-----------|-----------|-----------|
|
||||||
|
| A | B | C |
|
||||||
|
| D | E | F |
|
||||||
|
| G | H | I |
|
||||||
|
|
||||||
|
## Images
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Tags
|
||||||
|
|
||||||
|
Les tags inline fonctionnent: #test #markdown #playground
|
||||||
|
|
||||||
|
## Liens internes (WikiLinks)
|
||||||
|
|
||||||
|
[[Note Example]] - Lien vers une note
|
||||||
|
[[Note Example#Section]] - Lien vers une section
|
||||||
|
[[Note Example|Alias personnalisé]] - Lien avec alias
|
||||||
|
|
||||||
|
## Footnotes
|
||||||
|
|
||||||
|
Voici un texte avec une note de bas de page[^1].
|
||||||
|
|
||||||
|
Et une autre référence[^2].
|
||||||
|
|
||||||
|
[^1]: Ceci est la première note de bas de page.
|
||||||
|
[^2]: Ceci est la deuxième note de bas de page avec plus de détails.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Séparateur horizontal
|
||||||
|
|
||||||
|
Le séparateur ci-dessus est créé avec `---`
|
||||||
@ -7,4 +7,7 @@
|
|||||||
export const environment = {
|
export const environment = {
|
||||||
production: true,
|
production: true,
|
||||||
serviceURL: "/AuMenuManager",
|
serviceURL: "/AuMenuManager",
|
||||||
|
features: {
|
||||||
|
showTestSection: false
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -8,4 +8,7 @@ export const environment = {
|
|||||||
production: false,
|
production: false,
|
||||||
serviceURL: "http://localhost:8080/AuMenuManager",
|
serviceURL: "http://localhost:8080/AuMenuManager",
|
||||||
// serviceURL: "https://public-tomcat.guru.lan/AuMenuManager",
|
// serviceURL: "https://public-tomcat.guru.lan/AuMenuManager",
|
||||||
|
features: {
|
||||||
|
showTestSection: true
|
||||||
|
}
|
||||||
};
|
};
|
||||||
@ -379,6 +379,25 @@ export class MarkdownService {
|
|||||||
return placeholder;
|
return placeholder;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const codeBlockPlaceholders: { placeholder: string; content: string }[] = [];
|
||||||
|
const inlineCodePlaceholders: { placeholder: string; content: string }[] = [];
|
||||||
|
|
||||||
|
const stashSegments = (
|
||||||
|
source: string,
|
||||||
|
regex: RegExp,
|
||||||
|
collection: { placeholder: string; content: string }[],
|
||||||
|
marker: string
|
||||||
|
): string => {
|
||||||
|
return source.replace(regex, (match) => {
|
||||||
|
const placeholder = `@@__${marker}_${collection.length}__@@`;
|
||||||
|
collection.push({ placeholder, content: match });
|
||||||
|
return placeholder;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
text = stashSegments(text, /```[\s\S]*?```/g, codeBlockPlaceholders, 'CODE_BLOCK');
|
||||||
|
text = stashSegments(text, /`[^`]*`/g, inlineCodePlaceholders, 'CODE_INLINE');
|
||||||
|
|
||||||
const addMathPlaceholder = (expression: string, display: 'block' | 'inline') => {
|
const addMathPlaceholder = (expression: string, display: 'block' | 'inline') => {
|
||||||
const placeholder = `@@MATH::${display.toUpperCase()}::${math.length}@@`;
|
const placeholder = `@@MATH::${display.toUpperCase()}::${math.length}@@`;
|
||||||
math.push({ placeholder, expression: expression.trim(), display });
|
math.push({ placeholder, expression: expression.trim(), display });
|
||||||
@ -395,6 +414,14 @@ export class MarkdownService {
|
|||||||
text = text.replace(/(?<!\\)\$(?!\s)([^$]+?)(?<!\\)\$(?!\d)/g, (_match, expr) => addMathPlaceholder(expr, 'inline'));
|
text = text.replace(/(?<!\\)\$(?!\s)([^$]+?)(?<!\\)\$(?!\d)/g, (_match, expr) => addMathPlaceholder(expr, 'inline'));
|
||||||
text = text.replace(/\\\((.+?)\\\)/g, (_match, expr) => addMathPlaceholder(expr, 'inline'));
|
text = text.replace(/\\\((.+?)\\\)/g, (_match, expr) => addMathPlaceholder(expr, 'inline'));
|
||||||
|
|
||||||
|
for (const { placeholder, content } of inlineCodePlaceholders) {
|
||||||
|
text = text.split(placeholder).join(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const { placeholder, content } of codeBlockPlaceholders) {
|
||||||
|
text = text.split(placeholder).join(content);
|
||||||
|
}
|
||||||
|
|
||||||
return { markdown: text, wikiLinks, math };
|
return { markdown: text, wikiLinks, math };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user