feat: enhance Excalidraw editor UI and autosave functionality

- Redesigned editor toolbar with improved layout and accessibility attributes
- Added automatic save after 7 seconds of inactivity for better data protection
- Replaced emoji icons with SVG icons for consistent styling and accessibility
- Added success toasts for PNG/SVG exports with destination path
- Updated button styles and layout for better visual hierarchy
- Added aria-labels and improved keyboard navigation support
- Made action buttons optionally hideable via
This commit is contained in:
Bruno Charest 2025-10-28 22:25:40 -04:00
parent 5647b42d8a
commit b1f142c4f7
17 changed files with 335 additions and 284 deletions

View File

@ -3,25 +3,27 @@
<div class="flex items-center justify-between gap-2">
<div class="flex items-center gap-2">
<!-- Indicateur de sauvegarde (cliquable) -->
<button
type="button"
class="flex items-center gap-2 px-3 py-2 rounded-md transition-colors"
<button
*ngIf="showInlineActions"
type="button"
class="btn btn-sm btn-ghost flex items-center gap-2"
[class.cursor-pointer]="!isLoading() && !isSaving()"
[class.cursor-wait]="isSaving()"
[class.cursor-not-allowed]="isLoading()"
(click)="saveNow()"
[disabled]="isLoading()"
[attr.aria-label]="dirty() ? 'Sauver maintenant' : 'Déjà sauvegardé'"
title="{{dirty() ? 'Non sauvegardé - Cliquer pour sauvegarder (Ctrl+S)' : isSaving() ? 'Sauvegarde en cours...' : 'Sauvegardé'}}"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
[class.text-red-500]="dirty() && !isSaving()"
[class.text-muted]="!dirty() && !isSaving()"
@ -32,33 +34,51 @@
<polyline points="17 21 17 13 7 13 7 21"></polyline>
<polyline points="7 3 7 8 15 8"></polyline>
</svg>
<span class="text-xs font-medium" [class.text-red-500]="dirty() && !isSaving()" [class.text-muted]="!dirty() && !isSaving()" [class.text-yellow-500]="isSaving()">
<span *ngIf="showInlineActions" class="text-xs font-medium" [class.text-red-500]="dirty() && !isSaving()" [class.text-muted]="!dirty() && !isSaving()" [class.text-yellow-500]="isSaving()">
{{isSaving() ? 'Sauvegarde...' : dirty() ? 'Non sauvegardé' : 'Sauvegardé'}}
</span>
</button>
<button
type="button"
class="btn btn-sm"
<button
*ngIf="showInlineActions"
type="button"
class="btn btn-sm flex items-center gap-2"
(click)="exportPNG()"
[disabled]="isLoading() || isSaving() || !excalidrawReady"
aria-label="Exporter en PNG"
>
🖼️ Export PNG
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="3" width="18" height="14" rx="2" ry="2"></rect>
<circle cx="8" cy="8" r="2"></circle>
<path d="M21 17l-5-6-4 5-2-3-5 6"></path>
</svg>
Export PNG
</button>
<button
type="button"
class="btn btn-sm"
<button
*ngIf="showInlineActions"
type="button"
class="btn btn-sm flex items-center gap-2"
(click)="exportSVG()"
[disabled]="isLoading() || isSaving() || !excalidrawReady"
aria-label="Exporter en SVG"
>
🧩 Export SVG
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="7 10 12 15 17 10"/>
<line x1="12" y1="15" x2="12" y2="3"/>
</svg>
Export SVG
</button>
<button
type="button"
class="btn btn-sm"
<button
*ngIf="showInlineActions"
type="button"
class="btn btn-sm flex items-center gap-2"
(click)="toggleFullscreen()"
[disabled]="isLoading()"
[title]="isFullscreen() ? 'Quitter le mode pleine écran' : 'Passer en mode pleine écran'"
aria-label="Basculer le mode plein écran"
>
<svg
xmlns="http://www.w3.org/2000/svg"
@ -76,7 +96,7 @@
<polyline points="10,17 15,12 10,7"></polyline>
<line x1="15" x2="3" y1="12" y2="12"></line>
</svg>
{{isFullscreen() ? 'Quitter FS' : 'Pleine écran'}}
{{isFullscreen() ? 'Quitter FS' : 'Plein écran'}}
</button>
</div>

View File

@ -16,7 +16,7 @@ import {
signal
} from '@angular/core';
import { firstValueFrom, fromEvent, of, Subscription, interval } from 'rxjs';
import { debounceTime, distinctUntilChanged, map, switchMap, tap, catchError, filter } from 'rxjs/operators';
import { debounceTime, distinctUntilChanged, map, switchMap, tap, catchError, filter, mergeWith } from 'rxjs/operators';
import { DrawingsFileService, ExcalidrawScene } from './drawings-file.service';
import { ExcalidrawIoService } from './excalidraw-io.service';
import { DrawingsPreviewService } from './drawings-preview.service';
@ -32,6 +32,7 @@ import { ThemeService } from '../../core/services/theme.service';
})
export class DrawingsEditorComponent implements OnInit, AfterViewInit, OnDestroy {
@Input() path: string = '';
@Input() showInlineActions: boolean = true;
@ViewChild('editorEl', { static: false }) editorEl?: ElementRef<HTMLElement & {
setScene: (scene: any) => void;
@ -628,42 +629,76 @@ export class DrawingsEditorComponent implements OnInit, AfterViewInit, OnDestroy
}
});
// Subscription 2: Handle auto-saving (disabled temporarily)
// this.saveSub = sceneChange$.pipe(
// distinctUntilChanged((prev, curr) => prev.hash === curr.hash),
// debounceTime(2000),
// filter(({ hash }) => this.lastSavedHash !== hash),
// filter(() => !this.isSaving()),
// tap(({ hash }) => {
// console.log('💾 Autosaving... hash:', hash.substring(0, 10));
// this.isSaving.set(true);
// this.error.set(null);
// }),
// switchMap(({ scene, hash }) =>
// this.files.put(this.path, scene).pipe(
// tap(async () => {
// console.log('✅ Autosave successful', { newHash: hash.substring(0, 10) });
// this.lastSavedHash = hash;
// this.dirty.set(false);
// this.isSaving.set(false);
// this.hasConflict.set(false);
// try { await this.savePreviewPNG(); } catch (e) { console.warn('PNG preview update failed', e); }
// }),
// catchError((e) => {
// console.error('❌ Save error:', e);
// const status = e?.status ?? e?.statusCode;
// if (status === 409) {
// this.error.set('Conflit: le fichier a été modifié sur le disque.');
// this.hasConflict.set(true);
// } else {
// this.error.set('Erreur de sauvegarde. Veuillez réessayer.');
// }
// this.isSaving.set(false);
// return of({ rev: '' } as any);
// })
// )
// )
// ).subscribe();
// Subscription 2: Natural autosave after inactivity (idle-based)
const triggers$ = fromEvent(host, 'pointermove').pipe(
mergeWith(
fromEvent(document, 'keydown'),
fromEvent(host, 'wheel'),
sceneChange$.pipe(map(() => null))
),
debounceTime(7000),
filter(() => this.dirty() && !this.isSaving())
);
this.saveSub = triggers$.pipe(
switchMap(() => {
const scene = this.scene();
if (!scene) return of(null);
const hash = this.hashScene(scene);
if (this.lastSavedHash === hash) return of(null);
this.isSaving.set(true);
this.error.set(null);
const isExMd = /\.excalidraw\.md$/i.test(this.path || '');
const save$ = isExMd
? this.files.getText(this.path).pipe(
catchError(() => of('')),
switchMap((existing) => {
let fm = this.excalIo.extractFrontMatter(existing) || null;
if (fm) {
if (!/excalidraw-plugin\s*:/i.test(fm)) {
fm = fm.replace(/^---\s*\n/, `---\nexcalidraw-plugin: parsed\n`);
} else {
fm = fm.replace(/excalidraw-plugin\s*:\s*.*?(\r?\n)/i, `excalidraw-plugin: parsed$1`);
}
const stamp = new Date().toISOString();
if (/^updated\s*:/mi.test(fm)) {
fm = fm.replace(/updated\s*:\s*.*?(\r?\n)/i, `updated: "${stamp}"$1`);
} else {
fm = fm.replace(/^---\s*\n/, `---\nupdated: "${stamp}"\n`);
}
} else {
fm = `---\nexcalidraw-plugin: parsed\nupdated: "${new Date().toISOString()}"\n---`;
}
const md = this.excalIo.toObsidianMd(scene as any, fm);
return this.files.putText(this.path, md);
})
)
: this.files.put(this.path, scene);
return save$.pipe(
tap(async () => {
this.lastSavedHash = hash;
this.dirty.set(false);
this.isSaving.set(false);
this.hasConflict.set(false);
try { await this.savePreviewPNG(); } catch (e) { console.warn('PNG preview update failed', e); }
}),
catchError((e) => {
console.error('❌ Autosave error:', e);
const status = (e as any)?.status ?? (e as any)?.statusCode;
if (status === 409) {
this.error.set('Conflit: le fichier a été modifié sur le disque.');
this.hasConflict.set(true);
} else {
this.error.set('Erreur de sauvegarde automatique.');
}
this.isSaving.set(false);
return of(null);
})
);
})
).subscribe();
console.log('✓ Dirty check and Autosave subscriptions active');
@ -725,6 +760,11 @@ export class DrawingsEditorComponent implements OnInit, AfterViewInit, OnDestroy
const target = `${base}.png`;
await firstValueFrom(this.files.putBinary(target, blob, 'image/png'));
this.error.set(null);
try {
const norm = target.replace(/\\/g, '/');
const dir = norm.includes('/') ? norm.substring(0, norm.lastIndexOf('/')) : '/';
this.showToast(`PNG exporté avec succès dans ${dir}`, 'success');
} catch {}
} catch (err) {
console.error('Error exporting PNG:', err);
this.error.set('Erreur lors de l\'export en PNG');
@ -752,6 +792,11 @@ export class DrawingsEditorComponent implements OnInit, AfterViewInit, OnDestroy
const target = `${base}.svg`;
await firstValueFrom(this.files.putBinary(target, blob, 'image/svg+xml'));
this.error.set(null);
try {
const norm = target.replace(/\\/g, '/');
const dir = norm.includes('/') ? norm.substring(0, norm.lastIndexOf('/')) : '/';
this.showToast(`SVG exporté avec succès dans ${dir}`, 'success');
} catch {}
} catch (err) {
console.error('Error exporting SVG:', err);
this.error.set('Erreur lors de l\'export en SVG');

View File

@ -45,6 +45,16 @@ import { TrashExplorerComponent } from '../../layout/sidebar/trash/trash-explore
class="w-full text-left block text-sm px-3 py-2 rounded-lg hover:bg-surface1 dark:hover:bg-card active:bg-surface2 dark:active:bg-gray-700 text-main dark:text-main hover:text-main dark:hover:text-gray-100 transition-all active:scale-[0.98] transform">
🧪 Markdown Playground
</button>
<button
(click)="onApiTestsPanelClick()"
class="mt-1 w-full text-left block text-sm px-3 py-2 rounded-lg hover:bg-surface1 dark:hover:bg-card active:bg-surface2 dark:active:bg-gray-700 text-main dark:text-main hover:text-main dark:hover:text-gray-100 transition-all active:scale-[0.98] transform">
🔬 API Tests Panel
</button>
<button
(click)="onTestsExcalidrawClick()"
class="mt-1 w-full text-left block text-sm px-3 py-2 rounded-lg hover:bg-surface1 dark:hover:bg-card active:bg-surface2 dark:active:bg-gray-700 text-main dark:text-main hover:text-main dark:hover:text-gray-100 transition-all active:scale-[0.98] transform">
🎨 Test Excalidraw
</button>
</div>
</section>
@ -158,6 +168,8 @@ export class AppSidebarDrawerComponent {
@Output() tagSelected = new EventEmitter<string>();
@Output() quickLinkSelected = new EventEmitter<string>();
@Output() markdownPlaygroundSelected = new EventEmitter<void>();
@Output() testsPanelSelected = new EventEmitter<void>();
@Output() testsExcalidrawSelected = new EventEmitter<void>();
@Output() helpPageSelected = new EventEmitter<void>();
@Output() aboutSelected = new EventEmitter<void>();
@ -189,6 +201,16 @@ export class AppSidebarDrawerComponent {
this.mobileNav.sidebarOpen.set(false);
}
onApiTestsPanelClick(): void {
this.testsPanelSelected.emit();
this.mobileNav.sidebarOpen.set(false);
}
onTestsExcalidrawClick(): void {
this.testsExcalidrawSelected.emit();
this.mobileNav.sidebarOpen.set(false);
}
onHelpPageClick(): void {
this.helpPageSelected.emit();
this.mobileNav.sidebarOpen.set(false);

View File

@ -1,5 +1,5 @@
import { CommonModule } from '@angular/common';
import { Component, OnDestroy, OnInit, ViewChild, inject, signal } from '@angular/core';
import { AfterViewInit, Component, OnDestroy, OnInit, ViewChild, inject, signal } from '@angular/core';
import { Router } from '@angular/router';
import { DrawingsEditorComponent } from '../drawings/drawings-editor.component';
import { DrawingsFileService } from '../drawings/drawings-file.service';
@ -13,58 +13,75 @@ import { firstValueFrom } from 'rxjs';
imports: [CommonModule, DrawingsEditorComponent],
template: `
<div class="h-full flex flex-col gap-3">
<div class="rounded-xl border border-border bg-card shadow-subtle">
<div class="flex items-center justify-between px-3 py-2">
<div class="flex items-center gap-2">
<h2 class="text-sm font-semibold">Test Excalidraw</h2>
<span class="text-[10px] font-semibold uppercase tracking-widest rounded-full bg-surface1 px-2 py-0.5 text-muted">Legacy Integration</span>
<ng-container *ngIf="currentPath(); else nofile">
<span class="ml-2 text-xs text-muted select-all truncate max-w-[32rem]" [title]="currentPath()">{{ currentPath() }}</span>
</ng-container>
<ng-template #nofile>
<span class="ml-2 text-xs text-muted">No file</span>
</ng-template>
<span class="ml-2 text-xs" [class.text-success]="!dirty()" [class.text-warning]="dirty()"> {{ dirty() ? 'Dirty' : 'Saved' }}</span>
</div>
<div class="flex items-center gap-1">
<button type="button" class="btn btn-ghost btn-sm" (click)="createNew()" title="Nouveau">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14"/><path d="M5 12h14"/></svg>
<span class="ml-1 hidden sm:inline">Nouveau</span>
</button>
<div class="relative">
<button type="button" class="btn btn-ghost btn-sm" (click)="toggleOpenPicker()" title="Ouvrir">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h5l2 2h9a2 2 0 0 1 2 2z"/></svg>
<span class="ml-1 hidden sm:inline">Ouvrir</span>
</button>
<div *ngIf="openPicker()" class="absolute right-0 mt-1 w-80 max-h-80 overflow-y-auto rounded-lg border border-border bg-card shadow-xl z-10">
<div class="p-2 text-xs text-muted">Sélectionner un fichier *.excalidraw.md</div>
<ul>
<li *ngFor="let f of excalidrawFiles()">
<button type="button" class="w-full text-left px-3 py-1.5 text-sm hover:bg-surface1" (click)="openFile(f.filePath)">{{ f.filePath }}</button>
</li>
</ul>
<div class="sticky top-0 z-40">
<div class="rounded-2xl border border-border/60 bg-card/80 backdrop-blur-[2px] shadow-subtle max-w-full overflow-visible">
<div class="flex flex-wrap items-center gap-2 px-3 pt-2">
<div class="flex items-center gap-2 min-w-0 flex-1 overflow-hidden">
<!-- <h2 class="text-sm font-semibold">Test Excalidraw</h2> -->
<ng-container *ngIf="currentPath(); else nofile">
<span class="ml-2 text-xs text-muted truncate select-text" [title]="currentPath()">{{ currentPath() }}</span>
</ng-container>
<ng-template #nofile>
<span class="ml-2 text-xs text-muted">Aucun fichier</span>
</ng-template>
<div class="ml-auto flex items-center">
<span
class="inline-flex h-7 w-7 items-center justify-center rounded-full border border-border/60 bg-card/70 transition-colors"
[class.text-emerald-400]="!dirty()"
[class.text-red-500]="dirty()"
[title]="dirty() ? 'Dessin non sauvegardé' : 'Dessin sauvegardé'"
aria-label="Statut de sauvegarde"
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" />
<polyline points="17 21 17 13 7 13 7 21" />
<polyline points="7 3 7 8 15 8" />
</svg>
<span class="sr-only">{{ dirty() ? 'Non sauvegardé' : 'Sauvegardé' }}</span>
</span>
</div>
</div>
<button type="button" class="btn btn-ghost btn-sm" (click)="closeFile()" [disabled]="!currentPath()" title="Fermer le fichier">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
<span class="ml-1 hidden sm:inline">Fermer</span>
</button>
<div class="mx-1 h-5 w-px bg-border"></div>
<button type="button" class="btn btn-ghost btn-sm" (click)="saveDebounced()" [disabled]="!currentPath()" title="Enregistrer (Ctrl/Cmd+S)">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21H5a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h11l5 5v9a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
<span class="ml-1 hidden sm:inline">Enregistrer</span>
</button>
<button type="button" class="btn btn-ghost btn-sm" (click)="saveAs()" title="Enregistrer sous">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14"/><path d="M19 12H5"/></svg>
<span class="ml-1 hidden sm:inline">Enregistrer sous</span>
</button>
<div class="mx-1 h-5 w-px bg-border"></div>
<div class="relative">
<button type="button" class="btn btn-ghost btn-sm" (click)="toggleExport()" title="Exporter">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v12"/><path d="M7 11l5 5 5-5"/><path d="M5 19h14"/></svg>
<span class="ml-1 hidden sm:inline">Exporter</span>
</div>
<!-- Row 1: Primary actions -->
<div class="flex items-center justify-center gap-1 md:gap-2 px-3">
<div class="flex items-center gap-1">
<button type="button" class="btn btn-ghost btn-sm" (click)="createNew()" title="Nouveau" aria-label="Nouveau">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14"/><path d="M5 12h14"/></svg>
<span class="ml-1 hidden md:inline">Nouveau</span>
</button>
<div *ngIf="openExport()" class="absolute right-0 mt-1 w-52 rounded-lg border border-border bg-card shadow-xl z-10">
<div class="relative">
<button type="button" class="btn btn-ghost btn-sm" (click)="toggleOpenPicker()" title="Ouvrir" aria-haspopup="menu" aria-expanded="{{openPicker()}}">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h5l2 2h9a2 2 0 0 1 2 2z"/></svg>
<span class="ml-1 hidden md:inline">Ouvrir</span>
</button>
<div *ngIf="openPicker()" class="absolute right-0 mt-1 w-72 max-w-[80vw] max-h-80 overflow-y-auto rounded-xl border border-border bg-card shadow-2xl z-50">
<div class="px-3 py-2 text-xs text-muted">Sélectionner un fichier *.excalidraw.md</div>
<ul class="divide-y divide-border/40">
<li *ngFor="let f of excalidrawFiles()">
<button type="button" class="w-full text-left px-3 py-2 text-sm hover:bg-surface1 truncate" (click)="openFile(f.filePath)" [title]="f.filePath">{{ f.filePath }}</button>
</li>
</ul>
</div>
</div>
<button type="button" class="btn btn-ghost btn-sm" (click)="closeFile()" [disabled]="!currentPath()" title="Fermer le fichier" aria-label="Fermer le fichier">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
<span class="ml-1 hidden md:inline">Fermer</span>
</button>
<button type="button" class="btn btn-ghost btn-sm" (click)="saveDebounced()" [disabled]="!currentPath()" title="Enregistrer (Ctrl/Cmd+S)" aria-label="Enregistrer">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21H5a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h11l5 5v9a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
<span class="ml-1 hidden md:inline">Enregistrer</span>
</button>
</div>
</div>
<div class="h-px bg-border/70 mx-2 my-1"></div>
<!-- Row 2: Export only -->
<div class="flex items-center justify-center gap-1 md:gap-2 px-3 pb-2">
<div class="relative">
<button type="button" class="btn btn-ghost btn-sm" (click)="toggleExport()" title="Exporter" aria-haspopup="menu" aria-expanded="{{openExport()}}">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v12"/><path d="M7 11l5 5 5-5"/><path d="M5 19h14"/></svg>
<span class="ml-1">Exporter</span>
</button>
<div *ngIf="openExport()" class="absolute right-0 mt-1 w-64 max-w-[75vw] rounded-xl border border-border bg-card shadow-2xl z-50">
<button class="w-full text-left px-3 py-2 text-sm hover:bg-surface1" (click)="exportPNG(true)">PNG (avec fond)</button>
<button class="w-full text-left px-3 py-2 text-sm hover:bg-surface1" (click)="exportPNG(false)">PNG (sans fond)</button>
<div class="h-px bg-border mx-2"></div>
@ -74,20 +91,13 @@ import { firstValueFrom } from 'rxjs';
<button class="w-full text-left px-3 py-2 text-sm hover:bg-surface1" (click)="exportJSON()">JSON (télécharger)</button>
</div>
</div>
<div class="mx-1 h-5 w-px bg-border"></div>
<label class="inline-flex items-center gap-2 text-xs text-muted select-none">
<input type="checkbox" [checked]="autosave()" (change)="setAutosave($any($event.target).checked)" /> Autosave 10s
</label>
<button type="button" class="btn btn-ghost btn-sm" (click)="toggleFullscreen()" title="Plein écran">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3"/><path d="M16 3h3a2 2 0 0 1 2 2v3"/><path d="M8 21H5a2 2 0 0 1-2-2v-3"/><path d="M16 21h3a2 2 0 0 0 2-2v-3"/></svg>
</button>
</div>
</div>
</div>
<div class="flex-1 min-h-0">
<ng-container *ngIf="currentPath(); else noSelection">
<app-drawings-editor #editor [path]="currentPath()!"></app-drawings-editor>
<app-drawings-editor #editor [path]="currentPath()!" [showInlineActions]="false"></app-drawings-editor>
</ng-container>
<ng-template #noSelection>
<div class="h-full flex items-center justify-center rounded-xl border border-border bg-card text-muted">
@ -104,7 +114,7 @@ import { firstValueFrom } from 'rxjs';
</div>
`,
})
export class TestExcalidrawPageComponent implements OnInit, OnDestroy {
export class TestExcalidrawPageComponent implements OnInit, OnDestroy, AfterViewInit {
private readonly drawings = inject(DrawingsFileService);
private readonly excalIo = inject(ExcalidrawIoService);
private readonly vault = inject(VaultService);
@ -120,6 +130,7 @@ export class TestExcalidrawPageComponent implements OnInit, OnDestroy {
private autosaveTimer: any = null;
private saveTimer: any = null;
private dirtySyncInterval: any = null;
excalidrawFiles = signal<Array<{ filePath: string }>>([]);
@ -141,17 +152,20 @@ export class TestExcalidrawPageComponent implements OnInit, OnDestroy {
// build files list for picker
this.refreshFileList();
// poll editor dirty state via save hash comparison using existing editor polling
// here we just mirror the editor's internal dirty state by attempting a heuristic:
const syncDirty = () => {
// not accessible directly; rely on presence of '*' in title not available. Skip.
};
void syncDirty;
this.startDirtySync();
}
ngAfterViewInit(): void {
this.startDirtySync();
}
ngOnDestroy(): void {
if (this.autosaveTimer) clearInterval(this.autosaveTimer);
if (this.saveTimer) clearTimeout(this.saveTimer);
if (this.dirtySyncInterval) {
clearInterval(this.dirtySyncInterval);
this.dirtySyncInterval = null;
}
}
refreshFileList(): void {
@ -191,6 +205,7 @@ export class TestExcalidrawPageComponent implements OnInit, OnDestroy {
this.currentPath.set(path);
this.updateUrlFileParam(path);
this.openPicker.set(false);
this.dirty.set(false);
// focus editor next tick
setTimeout(() => this.editor?.onExcalidrawReady?.(), 0);
}
@ -199,6 +214,7 @@ export class TestExcalidrawPageComponent implements OnInit, OnDestroy {
this.currentPath.set(path);
this.updateUrlFileParam(path);
this.openPicker.set(false);
this.dirty.set(false);
}
async saveDebounced(): Promise<void> {
@ -208,6 +224,17 @@ export class TestExcalidrawPageComponent implements OnInit, OnDestroy {
async save(): Promise<void> {
await this.editor?.saveNow();
this.dirty.set(this.editor?.dirty?.() ?? false);
}
async exportPNG(withBg: boolean): Promise<void> {
await this.editor?.exportPNG(withBg);
this.openExport.set(false);
}
async exportSVG(withBg: boolean): Promise<void> {
await this.editor?.exportSVG(withBg);
this.openExport.set(false);
}
async saveAs(): Promise<void> {
@ -228,16 +255,6 @@ export class TestExcalidrawPageComponent implements OnInit, OnDestroy {
this.updateUrlFileParam(path);
}
async exportPNG(withBg: boolean): Promise<void> {
await this.editor?.exportPNG(withBg);
this.openExport.set(false);
}
async exportSVG(withBg: boolean): Promise<void> {
await this.editor?.exportSVG(withBg);
this.openExport.set(false);
}
private updateUrlFileParam(filePath: string | null): void {
try {
this.router.navigate([], { queryParams: { file: filePath || null }, queryParamsHandling: 'merge', preserveFragment: true });
@ -247,8 +264,8 @@ export class TestExcalidrawPageComponent implements OnInit, OnDestroy {
closeFile(): void {
this.currentPath.set(null);
this.openPicker.set(false);
this.openExport.set(false);
this.updateUrlFileParam(null);
this.dirty.set(false);
}
async exportJSON(): Promise<void> {
@ -275,6 +292,7 @@ export class TestExcalidrawPageComponent implements OnInit, OnDestroy {
this.autosaveTimer = setInterval(() => {
if (this.currentPath()) {
this.editor?.saveNow();
this.dirty.set(this.editor?.dirty?.() ?? false);
}
}, 10000);
}
@ -284,4 +302,14 @@ export class TestExcalidrawPageComponent implements OnInit, OnDestroy {
this.editor?.toggleFullscreen();
}
private startDirtySync(): void {
if (this.dirtySyncInterval) return;
this.dirtySyncInterval = setInterval(() => {
const childDirty = this.editor?.dirty?.();
if (typeof childDirty === 'boolean' && childDirty !== this.dirty()) {
this.dirty.set(childDirty);
}
}, 800);
}
}

View File

@ -279,6 +279,7 @@ import { InPageSearchOverlayComponent } from '../../shared/search/in-page-search
(quickLinkSelected)="onQuickLink($event)"
(markdownPlaygroundSelected)="onMarkdownPlaygroundSelected()"
(testsPanelSelected)="onTestsPanelSelected()"
(testsExcalidrawSelected)="onTestsExcalidrawSelected()"
(helpPageSelected)="onHelpPageSelected()"
(aboutSelected)="onAboutSelected()"
></app-sidebar-drawer>
@ -860,14 +861,23 @@ export class AppShellNimbusLayoutComponent implements AfterViewInit {
}
onMarkdownPlaygroundSelected(): void {
if (this.responsive.isMobile()) {
this.mobileNav.setActiveTab('page');
}
this.markdownPlaygroundSelected.emit();
}
onTestsPanelSelected(): void {
if (this.responsive.isMobile()) {
this.mobileNav.setActiveTab('page');
}
this.testsPanelRequested.emit();
}
onTestsExcalidrawSelected(): void {
if (this.responsive.isMobile()) {
this.mobileNav.setActiveTab('page');
}
this.testsExcalidrawRequested.emit();
}

View File

@ -4,17 +4,19 @@
"type": "split",
"children": [
{
"id": "b123bf2cfbaf681c",
"id": "511c66a5c96502b2",
"type": "tabs",
"children": [
{
"id": "a71c9cf3bbb86552",
"id": "8309e5042a8fb85c",
"type": "leaf",
"state": {
"type": "empty",
"state": {},
"icon": "lucide-file",
"title": "New tab"
"type": "excalidraw",
"state": {
"file": "Drawing-20251028-1452.excalidraw.md"
},
"icon": "excalidraw-icon",
"title": "Drawing-20251028-1452.excalidraw"
}
}
]
@ -174,8 +176,17 @@
"obsidian-excalidraw-plugin:New drawing": false
}
},
"active": "a71c9cf3bbb86552",
"active": "8309e5042a8fb85c",
"lastOpenFiles": [
"Drawing-20251028-1452.png",
"Drawing-20251028-1452.excalidraw.md.bak",
"Drawing-20251028-1452.excalidraw.md",
"dessin.svg",
"dessin.excalidraw.md",
"dessin.png",
"dessin.excalidraw.md.bak",
"dessin_05.svg",
"dessin_05.png",
"dessin_05.excalidraw.md",
"dessin_03.excalidraw.md",
"dessin_05.excalidraw.md.bak",
@ -189,9 +200,7 @@
"dessin-002.excalidraw.md.bak",
"Dessin_001.excalidraw.md",
"Dessin_001.excalidraw.md.tmp",
"Dessin_001.excalidraw.md.bak",
"Drawing 2025-10-28 11.11.59.excalidraw.md",
"Drawing 2025-10-28 11.11.59.excalidraw.md.tmp",
"dessin-06.excalidraw.md",
"Dessin-5.excalidraw.md",
"Drawing-20251027-2203.excalidraw.md",
@ -209,8 +218,6 @@
"Drawing-20251027-1914.excalidraw.md",
"Drawing-20251027-1705.excalidraw.md",
"Drawing 2025-10-27 16.43.48.excalidraw.md",
"test-drawing.excalidraw.md",
"Untitled.canvas",
"test.md"
"Untitled.canvas"
]
}

View File

@ -1,49 +0,0 @@
---
excalidraw-plugin: parsed
updated: "2025-10-28T15:45:35.205Z"
---
==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠==
You can decompress Drawing data with the command palette: 'Decompress current Excalidraw file'. For more info check in plugin settings under 'Saving'
# Excalidraw Data
## Text Elements
%%
## Drawing
```compressed-json
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
```
%%

View File

@ -0,0 +1,15 @@
---
excalidraw-plugin: parsed
updated: "2025-10-29T00:30:06.609Z"
---
==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠==
# Excalidraw Data
## Text Elements
%%
## Drawing
```compressed-json
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
```
%%

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -1,21 +0,0 @@
---
excalidraw-plugin: parsed
updated: "2025-10-28T15:52:34.935Z"
---
==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠==
# Text Elements
# Drawing
```json
{"elements":[{"id":"FH8lzL5Mi3fE2hkUwBpjg","type":"ellipse","x":186.5,"y":194.5,"width":184,"height":143.5,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":2},"seed":980334059,"version":87,"versionNonce":1002008523,"isDeleted":false,"boundElements":[{"id":"RwNj5xmpJCXxtCVzpGsCc","type":"arrow"},{"id":"tVxfMX9-gzA1gIMHh04AP","type":"arrow"}],"updated":1761666749806,"link":null,"locked":false},{"id":"RwNj5xmpJCXxtCVzpGsCc","type":"arrow","x":186.5,"y":271.5,"width":183,"height":5,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":2},"seed":1981797643,"version":68,"versionNonce":624371685,"isDeleted":false,"boundElements":null,"updated":1761666744662,"link":null,"locked":false,"points":[[0,0],[183,-5]],"lastCommittedPoint":null,"startBinding":{"elementId":"FH8lzL5Mi3fE2hkUwBpjg","focus":0.03811374375552938,"gap":1},"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"},{"id":"tVxfMX9-gzA1gIMHh04AP","type":"arrow","x":277,"y":192,"width":1,"height":145,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":2},"seed":132945413,"version":89,"versionNonce":1141263659,"isDeleted":false,"boundElements":null,"updated":1761666749806,"link":null,"locked":false,"points":[[0,0],[1,145]],"lastCommittedPoint":null,"startBinding":{"elementId":"FH8lzL5Mi3fE2hkUwBpjg","focus":0.01073822548811009,"gap":2.5093386662695423},"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}],"appState":{"showWelcomeScreen":true,"theme":"dark","collaborators":{},"currentChartType":"bar","currentItemBackgroundColor":"transparent","currentItemEndArrowhead":"arrow","currentItemFillStyle":"solid","currentItemFontFamily":1,"currentItemFontSize":20,"currentItemOpacity":100,"currentItemRoughness":1,"currentItemStartArrowhead":null,"currentItemStrokeColor":"#1e1e1e","currentItemRoundness":"round","currentItemStrokeStyle":"solid","currentItemStrokeWidth":2,"currentItemTextAlign":"left","cursorButton":"up","activeEmbeddable":null,"draggingElement":null,"editingElement":null,"editingGroupId":null,"editingLinearElement":null,"activeTool":{"type":"selection","customType":null,"locked":false,"lastActiveTool":null},"penMode":false,"penDetected":false,"errorMessage":null,"exportBackground":true,"exportScale":2,"exportEmbedScene":false,"exportWithDarkMode":false,"fileHandle":null,"gridSize":null,"isBindingEnabled":true,"defaultSidebarDockedPreference":false,"isLoading":false,"isResizing":false,"isRotating":false,"lastPointerDownWith":"mouse","multiElement":null,"name":"Untitled-2025-10-28-1152","contextMenu":null,"openMenu":null,"openPopup":null,"openSidebar":null,"openDialog":null,"pasteDialog":{"shown":false,"data":null},"previousSelectedElementIds":{"tVxfMX9-gzA1gIMHh04AP":true},"resizingElement":null,"scrolledOutside":false,"scrollX":0,"scrollY":0,"selectedElementIds":{},"selectedGroupIds":{},"selectedElementsAreBeingDragged":false,"selectionElement":null,"shouldCacheIgnoreZoom":false,"showStats":false,"startBoundElement":null,"suggestedBindings":[],"frameRendering":{"enabled":true,"clip":true,"name":true,"outline":true},"frameToHighlight":null,"editingFrame":null,"elementsToHighlight":null,"toast":null,"viewBackgroundColor":"#f8f9fa","zenModeEnabled":false,"zoom":{"value":1},"viewModeEnabled":false,"pendingImageElementId":null,"showHyperlinkPopup":false,"selectedLinearElement":null,"snapLines":[],"originSnapOffset":null,"objectsSnapModeEnabled":false,"offsetLeft":723,"offsetTop":155,"width":665,"height":546},"files":{}}
```
# Embedded Files
%%
```compressed-json
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
```
%%

View File

@ -0,0 +1,15 @@
---
excalidraw-plugin: parsed
updated: "2025-10-28T18:48:43.751Z"
---
==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠==
# Excalidraw Data
## Text Elements
%%
## Drawing
```compressed-json
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
```
%%

BIN
vault/dessin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

21
vault/dessin.svg Normal file
View File

@ -0,0 +1,21 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 270.3366543638838 194.5" width="540.6733087277676" height="389">
<!-- svg-source:excalidraw -->
<defs>
<style class="style-fonts">
@font-face {
font-family: "Virgil";
src: url("https://unpkg.com/@excalidraw/excalidraw@undefined/dist/excalidraw-assets/Virgil.woff2");
}
@font-face {
font-family: "Cascadia";
src: url("https://unpkg.com/@excalidraw/excalidraw@undefined/dist/excalidraw-assets/Cascadia.woff2");
}
@font-face {
font-family: "Assistant";
src: url("https://unpkg.com/@excalidraw/excalidraw@undefined/dist/excalidraw-assets/Assistant-Regular.woff2");
}
</style>
</defs>
<g stroke-linecap="round" transform="translate(10 10) rotate(0 122.75 87.25)"><path d="M123.66 0.36 C137.66 -0.96, 155.36 2.01, 169.1 5.73 C182.84 9.46, 195.35 15.56, 206.13 22.69 C216.9 29.82, 227.4 38.97, 233.76 48.53 C240.11 58.09, 243.03 69.45, 244.23 80.05 C245.44 90.65, 245.15 101.93, 241 112.12 C236.86 122.32, 228.55 132.89, 219.37 141.21 C210.19 149.52, 198.38 156.71, 185.91 162.01 C173.44 167.32, 158.96 171.21, 144.53 173.05 C130.11 174.88, 114.04 175.18, 99.35 173.01 C84.66 170.85, 68.91 165.59, 56.38 160.06 C43.84 154.52, 32.97 148.12, 24.15 139.8 C15.34 131.48, 7.46 120.29, 3.48 110.14 C-0.49 99.98, -1.42 89.19, 0.28 78.86 C1.98 68.54, 6.82 57.63, 13.69 48.19 C20.56 38.75, 30.3 29.35, 41.49 22.2 C52.67 15.04, 64.83 8.92, 80.79 5.28 C96.75 1.65, 125.77 0.58, 137.24 0.4 C148.71 0.22, 149.76 2.79, 149.59 4.2 M140.94 2.38 C155.18 2.39, 169.62 5.6, 182.49 10.33 C195.36 15.07, 208.87 22.62, 218.16 30.79 C227.44 38.96, 233.62 49.12, 238.19 59.34 C242.77 69.55, 246.29 81.58, 245.59 92.08 C244.89 102.58, 240.11 113.03, 234.01 122.35 C227.91 131.67, 219.45 140.36, 208.99 147.99 C198.52 155.62, 184.68 163.57, 171.22 168.14 C157.76 172.71, 142.91 175.03, 128.22 175.41 C113.53 175.78, 97.33 174.03, 83.11 170.38 C68.89 166.73, 54.05 160.21, 42.9 153.51 C31.76 146.81, 23.28 139.23, 16.25 130.17 C9.22 121.11, 2.62 109.78, 0.73 99.13 C-1.16 88.48, 1.51 76.33, 4.91 66.26 C8.3 56.19, 12.92 47.33, 21.1 38.7 C29.29 30.07, 41.85 20.82, 54 14.49 C66.15 8.16, 79.61 3.08, 94.01 0.71 C108.41 -1.66, 132.56 -0.23, 140.41 0.29 C148.27 0.82, 141.65 2.29, 141.12 3.85" stroke="#1e1e1e" stroke-width="2" fill="none"/></g><g stroke-linecap="round"><g transform="translate(67.5 25) rotate(0 2.75 73.75)"><path d="M-1.04 -0.25 C-0.35 24.08, 3.39 121.84, 4.66 146.55 M0.62 -1.42 C1.63 22.98, 5.98 123.03, 6.93 147.58" stroke="#1e1e1e" stroke-width="2" fill="none"/></g></g><mask/><g stroke-linecap="round"><g transform="translate(71.57179654033308 97.21709399425663) rotate(0 93.5 -1.75)"><path d="M-1.18 0.93 C29.85 0.14, 154.96 -2.98, 186.38 -3.66 M0.41 0.37 C31.8 0.28, 157.71 -1.26, 188.76 -2.21" stroke="#1e1e1e" stroke-width="2" fill="none"/></g></g><mask/></svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -1,21 +0,0 @@
---
excalidraw-plugin: parsed
updated: "2025-10-28T16:00:51.235Z"
---
==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠==
# Text Elements
# Drawing
```json
{"elements":[{"id":"k815FAbmfYF5PnwUH8C-Y","type":"diamond","x":145,"y":168,"width":184,"height":162,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":2},"seed":1003701346,"version":78,"versionNonce":177304866,"isDeleted":false,"boundElements":null,"updated":1761667243295,"link":null,"locked":false},{"id":"-5PKdSwNmlYANRFbrw08a","type":"arrow","x":236.5,"y":170.5,"width":1,"height":154,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":2},"seed":1957331170,"version":45,"versionNonce":596418814,"isDeleted":false,"boundElements":null,"updated":1761667248128,"link":null,"locked":false,"points":[[0,0],[1,154]],"lastCommittedPoint":null,"startBinding":null,"endBinding":null,"startArrowhead":null,"endArrowhead":"arrow"}],"appState":{"showWelcomeScreen":true,"theme":"dark","collaborators":{},"currentChartType":"bar","currentItemBackgroundColor":"transparent","currentItemEndArrowhead":"arrow","currentItemFillStyle":"solid","currentItemFontFamily":1,"currentItemFontSize":20,"currentItemOpacity":100,"currentItemRoughness":1,"currentItemStartArrowhead":null,"currentItemStrokeColor":"#1e1e1e","currentItemRoundness":"round","currentItemStrokeStyle":"solid","currentItemStrokeWidth":2,"currentItemTextAlign":"left","cursorButton":"up","activeEmbeddable":null,"draggingElement":null,"editingElement":null,"editingGroupId":null,"editingLinearElement":null,"activeTool":{"type":"selection","customType":null,"locked":false,"lastActiveTool":null},"penMode":false,"penDetected":false,"errorMessage":null,"exportBackground":true,"exportScale":2,"exportEmbedScene":false,"exportWithDarkMode":false,"fileHandle":null,"gridSize":null,"isBindingEnabled":true,"defaultSidebarDockedPreference":false,"isLoading":false,"isResizing":false,"isRotating":false,"lastPointerDownWith":"mouse","multiElement":null,"name":"Untitled-2025-10-28-1200","contextMenu":null,"openMenu":null,"openPopup":null,"openSidebar":null,"openDialog":null,"pasteDialog":{"shown":false,"data":null},"previousSelectedElementIds":{"-5PKdSwNmlYANRFbrw08a":true},"resizingElement":null,"scrolledOutside":false,"scrollX":0,"scrollY":0,"selectedElementIds":{},"selectedGroupIds":{},"selectedElementsAreBeingDragged":false,"selectionElement":null,"shouldCacheIgnoreZoom":false,"showStats":false,"startBoundElement":null,"suggestedBindings":[],"frameRendering":{"enabled":true,"clip":true,"name":true,"outline":true},"frameToHighlight":null,"editingFrame":null,"elementsToHighlight":null,"toast":null,"viewBackgroundColor":"#f8f9fa","zenModeEnabled":false,"zoom":{"value":1},"viewModeEnabled":false,"pendingImageElementId":null,"showHyperlinkPopup":false,"selectedLinearElement":null,"snapLines":[],"originSnapOffset":null,"objectsSnapModeEnabled":false,"offsetLeft":723,"offsetTop":155,"width":665,"height":546},"files":{}}
```
# Embedded Files
%%
```compressed-json
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=
```
%%

View File

@ -1,21 +0,0 @@
---
excalidraw-plugin: parsed
updated: "2025-10-28T16:26:01.524Z"
---
==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠==
# Text Elements
# Drawing
```json
{"elements":[{"id":"GeEImoYg9respBPK3wmrF","type":"diamond","x":284.5,"y":175,"width":151,"height":168,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":2},"seed":1926584722,"version":64,"versionNonce":1088033746,"isDeleted":false,"boundElements":null,"updated":1761668759766,"link":null,"locked":false}],"appState":{"showWelcomeScreen":true,"theme":"dark","collaborators":{},"currentChartType":"bar","currentItemBackgroundColor":"transparent","currentItemEndArrowhead":"arrow","currentItemFillStyle":"solid","currentItemFontFamily":1,"currentItemFontSize":20,"currentItemOpacity":100,"currentItemRoughness":1,"currentItemStartArrowhead":null,"currentItemStrokeColor":"#1e1e1e","currentItemRoundness":"round","currentItemStrokeStyle":"solid","currentItemStrokeWidth":2,"currentItemTextAlign":"left","cursorButton":"up","activeEmbeddable":null,"draggingElement":null,"editingElement":null,"editingGroupId":null,"editingLinearElement":null,"activeTool":{"type":"selection","customType":null,"locked":false,"lastActiveTool":null},"penMode":false,"penDetected":false,"errorMessage":null,"exportBackground":true,"exportScale":2,"exportEmbedScene":false,"exportWithDarkMode":false,"fileHandle":null,"gridSize":null,"isBindingEnabled":true,"defaultSidebarDockedPreference":false,"isLoading":false,"isResizing":false,"isRotating":false,"lastPointerDownWith":"mouse","multiElement":null,"name":"Untitled-2025-10-28-1225","contextMenu":null,"openMenu":null,"openPopup":null,"openSidebar":null,"openDialog":null,"pasteDialog":{"shown":false,"data":null},"previousSelectedElementIds":{"GeEImoYg9respBPK3wmrF":true},"resizingElement":null,"scrolledOutside":false,"scrollX":0,"scrollY":0,"selectedElementIds":{},"selectedGroupIds":{},"selectedElementsAreBeingDragged":false,"selectionElement":null,"shouldCacheIgnoreZoom":false,"showStats":false,"startBoundElement":null,"suggestedBindings":[],"frameRendering":{"enabled":true,"clip":true,"name":true,"outline":true},"frameToHighlight":null,"editingFrame":null,"elementsToHighlight":null,"toast":null,"viewBackgroundColor":"#f8f9fa","zenModeEnabled":false,"zoom":{"value":1},"viewModeEnabled":false,"pendingImageElementId":null,"showHyperlinkPopup":false,"selectedLinearElement":null,"snapLines":[],"originSnapOffset":null,"objectsSnapModeEnabled":false,"offsetLeft":723,"offsetTop":155,"width":665,"height":546},"files":{}}
```
# Embedded Files
%%
```compressed-json
N4IgpgNmC2YHYBcDOIBcBtUBLAJmkA4mAKICS0A9gJoDmAnAE5hIAOAQgAoDSAzAO7QGAMRAAaEAgCeLMPhxYAhpTh5xADzQAmABwAWAHQBWcZLQBGAOzGQfXAgAW5w2fH2wWGvYTmAbNvEKcDRQaAAM4kgIDBQA1mAAwhQQFAz4AMRmYJmZYiAARgoAxjE00QCuKonJqagSDIGsCkyIuQBmWBAQAMpSIbVISbi5kdFxAOp2jqiaEVGxYD2SfSADEEPi5Z5wzCioLiAULEVYUuah4SClFGUspDi76AC64q31sHdocGWdG9cq20hdqApDItABfCJgMB4PZ0TQ+Qx6CyaGYgABuYAYSCwFDgaB8unEGKxOLgADlcYVZHtQtptKEeDwLLofOIsEgACKQMAIaFoVoKCBIMDiPJ/HDEKCwRC7L4/EA3HAKXkwyw+Mw+PxWOgWTXiNZwGKfb4QfUUYp81ACoVgMHPEAKFgsHrK6mgJD2Ch8MaQQoUWBdQpMeBoKJlEUSNywORNI3iP2dBRi+oIFJAiEgQplBjNBDxexNBAAFWk1PyTVyWZz8AQpF50DYRRK5UqSRS+CiDSOucr2dzdZgxBUAEEc163AoYQ6x3xe9XEAPoEIOt1emXVutM32a4uhLiEEIlB1THt49uF/W94gulgAF7UzQXKv9+sAeSOhROJ7M5zP89r9YAErXFsOzmH+L4wC6DAIKO0R8BOMJyqaW7/ouPSjAkbY1CAGRZPhc6QdAwEVDgAK7CALaqKhREYfMizLBu1HPju9Z0eMkxaBBrEwEWYBqLBaw0HitRQK03h/gMDBsGUCCpiJCosLkRQIFgGLENAeTQkqeR9Mh4g4PUNA0FgQSSjANbGvK0InKZNDmdK3ioPp4DyKpQQEOUtxISa4g2e5NAADKmWATQOZZzm+Q6hSqRiRYUEkaDAqW+DClAMWkr2kT+iWoKRfKyQWjC1rCvqCiRMOGVxQlEBWRAGYyHAACyFA4NSJURo1XK8jFlodX5Y4ME1OwKDQ1IufxLApAgjbFFcpGhgw4Z+WoU0wYGgoPita0IBpWk4IG8DtYKpXgKt00TA4HKxi1bX8idEbtFAAASgQ4HpUWlLgN73nVbJIGwpnyGZcBJlAMJhhGbUCt8CA3m1BQMBy5pxDgHBMK0mLwFS902v9gUUJOdm46d7KAcwd7E1aD3/cBCDKlT/UgBA5UIBwFCmbySNenAl1TCAlBlMKuTQLDWDhS0+UoaD0a1AAqogJzgwAtI+miGMrP6q9omsooYlb7vxCDDV8f0HI1JtlGbhzwOzLA3NbjXw2AiOO/AHKKMkNBm0ckRgB7goUN7qDup6fAiUzSr03VDVMGiOJC103K9RKUo7vcSWECQ5DUPQTCsJwvACMIi3hhm+eU2ZaeSy5SBBkk4OvrJ2J3dTeMrPXnQABphBEncQFQvcrMnKoS7WGchxmaVgCnnnXN56aQulo/V8go5gGw7hBByRljcVNPD8vpJj2bHrXBAODxEUbikMJKRgAAWgl0AkxGZ98C6yCv7MhZsOKJ9SwiGUYyzAVSAxUHZB49pXhKDAOTFQmIqagHgGDS0kN4xrCUqgdBIAZbUhwdcBABp8FLVtC8N4YB4rPQ8PYISXgzb+TskIChDDV5ICoTQuhTkXKplZmbeOYA+CzWbOKKo7ZahpFaNoVodABS5HvM1VqJBQa6T6gfW8z9M5okFOGcwGYBF8Fuso1B+926NWBjQcgo0SCrw+IAlYYdnqlgYAaGIdsHZt1OtPFOwVthhVXqfUGLBfHMDQE8cQKQPCmS6EE18rRWjCm4VFCgeQABWM9kAxMdEYocJjv4HHiYkwKYBxJoGRDwCJhSeTxSwWYQw1hbA4AcPiBErh3CeCcoYFkGYnqhMnmCIAA===
```
%%

View File

@ -1,15 +0,0 @@
---
excalidraw-plugin: parsed
updated: "2025-10-28T16:46:06.902Z"
---
==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠==
# Excalidraw Data
## Text Elements
%%
## Drawing
```compressed-json
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
```
%%

View File

@ -12,11 +12,6 @@ task: true
archive: true
draft: true
private: true
color: "#EF4444"
tags:
- configuration
- tag4
- test
---
# TEST