Premier commit déjà bien avancé

This commit is contained in:
2025-11-10 18:33:24 +01:00
commit db4f0508cb
652 changed files with 440521 additions and 0 deletions

48
templates/editor.html Normal file
View File

@ -0,0 +1,48 @@
<div id="editor-content">
<form class="editor-form" {{if not .IsHome}}hx-post="/api/notes/{{.Filename}}" hx-swap="none"{{end}}>
<div class="editor-header">
<label for="editor">
{{if .IsHome}}
<strong>{{.Filename}}</strong>
{{else}}
Édition de&nbsp;: <strong>{{.Filename}}</strong>
<span id="auto-save-status" class="auto-save-status"></span>
{{end}}
</label>
{{if .IsHome}}
<button type="button" class="toggle-preview-btn" hx-get="/api/home" hx-target="#editor-container" hx-swap="innerHTML" title="Actualiser la page d'accueil">
🔄 Actualiser
</button>
{{else}}
<button type="button" id="toggle-preview-btn" class="toggle-preview-btn" onclick="togglePreview()" title="Mode: Éditeur + Preview (cliquer pour Éditeur seul)">
◫ Split
</button>
{{end}}
</div>
<div class="editor-grid {{if .IsHome}}editor-hidden{{end}}" id="editor-grid">
<div class="editor-panel {{if .IsHome}}hidden{{end}}">
<textarea id="editor" name="content">{{.Content}}</textarea>
</div>
<div id="preview" class="preview markdown-preview">
</div>
</div>
{{if not .IsHome}}
<div class="editor-actions">
<div class="editor-actions-primary">
<button type="submit">Enregistrer</button>
<button
hx-delete="/api/notes/{{.Filename}}"
hx-confirm="Êtes-vous sûr de vouloir supprimer cette note ({{.Filename}}) ?"
hx-target="#editor-container"
hx-swap="innerHTML"
class="secondary"
type="button"
>
Supprimer
</button>
</div>
<span id="save-status" class="save-status"></span>
</div>
{{end}}
</form>
</div>

39
templates/file-tree.html Normal file
View File

@ -0,0 +1,39 @@
{{if .Tree}}
{{if .Tree.Children}}
{{template "tree-node" .Tree}}
{{else}}
<p style="color: var(--text-muted); font-size: 0.85rem;">Aucune note trouvée.</p>
{{end}}
{{else}}
<p style="color: var(--text-muted); font-size: 0.85rem;">Aucune note trouvée.</p>
{{end}}
{{define "tree-node"}}
{{range .Children}}
{{if .IsDir}}
<div class="folder-item" data-path="{{.Path}}" data-is-dir="true">
<div class="folder-header">
<span class="folder-toggle"></span>
<span class="folder-icon">📁</span>
<span class="folder-name">{{.Name}}</span>
</div>
<div class="folder-children" style="display: none;">
{{if .Children}}
{{template "tree-node" .}}
{{end}}
</div>
</div>
{{else}}
<a href="#"
class="file-item"
data-path="{{.Path}}"
data-is-dir="false"
hx-get="/api/notes/{{.Path}}"
hx-target="#editor-container"
hx-swap="innerHTML"
draggable="true">
📄 {{.Name}}
</a>
{{end}}
{{end}}
{{end}}

71
templates/home.html Normal file
View File

@ -0,0 +1,71 @@
<div id="home-content" style="padding: 3rem; max-width: 900px; margin: 0 auto;">
<div style="text-align: center; margin-bottom: 3rem;">
<h1 style="font-size: 2.5rem; color: #c792ea; margin-bottom: 1rem;">
📝 Project Notes
</h1>
<p style="font-size: 1.2rem; color: var(--text-secondary); margin-bottom: 2rem;">
Bienvenue dans votre espace de notes Markdown
</p>
<button onclick="showNewNoteModal()" style="font-size: 1.1rem; padding: 1rem 2rem;">
✨ Créer une nouvelle note
</button>
</div>
<div style="margin-top: 3rem;">
<h2 style="color: #82aaff; font-size: 1.5rem; margin-bottom: 1.5rem;">
🚀 Démarrage rapide
</h2>
<div style="display: grid; gap: 1.5rem; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));">
<div style="background: var(--bg-secondary); border: 1px solid var(--border-primary); border-radius: var(--radius-md); padding: 1.5rem;">
<h3 style="color: #89ddff; margin-bottom: 0.5rem;">📁 Parcourir</h3>
<p style="color: var(--text-secondary); font-size: 0.9rem; margin-bottom: 1rem;">
Explorez vos notes dans l'arborescence à gauche
</p>
</div>
<div style="background: var(--bg-secondary); border: 1px solid var(--border-primary); border-radius: var(--radius-md); padding: 1.5rem;">
<h3 style="color: #c3e88d; margin-bottom: 0.5rem;">🔍 Rechercher</h3>
<p style="color: var(--text-secondary); font-size: 0.9rem; margin-bottom: 1rem;">
Utilisez la barre de recherche en haut pour trouver vos notes
</p>
</div>
<div style="background: var(--bg-secondary); border: 1px solid var(--border-primary); border-radius: var(--radius-md); padding: 1.5rem;">
<h3 style="color: #ffcb6b; margin-bottom: 0.5rem;">⚡ Slash commands</h3>
<p style="color: var(--text-secondary); font-size: 0.9rem; margin-bottom: 1rem;">
Tapez <code style="color: #f07178;">/</code> dans l'éditeur pour insérer du Markdown
</p>
</div>
</div>
</div>
<div style="margin-top: 3rem;">
<h2 style="color: #82aaff; font-size: 1.5rem; margin-bottom: 1.5rem;">
✨ Fonctionnalités
</h2>
<ul style="color: var(--text-secondary); line-height: 2; list-style: none; padding: 0;">
<li style="padding: 0.5rem 0; border-bottom: 1px solid var(--border-secondary);">
<span style="color: #c792ea;"></span> Éditeur Markdown avec coloration syntaxique (CodeMirror)
</li>
<li style="padding: 0.5rem 0; border-bottom: 1px solid var(--border-secondary);">
<span style="color: #82aaff;"></span> Prévisualisation en temps réel avec scroll synchronisé
</li>
<li style="padding: 0.5rem 0; border-bottom: 1px solid var(--border-secondary);">
<span style="color: #89ddff;"></span> Organisation par dossiers avec arborescence dynamique
</li>
<li style="padding: 0.5rem 0; border-bottom: 1px solid var(--border-secondary);">
<span style="color: #c3e88d;"></span> Recherche avancée par tag, titre ou contenu
</li>
<li style="padding: 0.5rem 0; border-bottom: 1px solid var(--border-secondary);">
<span style="color: #ffcb6b;"></span> Thème Material Darker pour vos yeux
</li>
<li style="padding: 0.5rem 0;">
<span style="color: #f07178;"></span> Sauvegarde automatique avec Ctrl/Cmd+S
</li>
</ul>
</div>
<div style="margin-top: 3rem; text-align: center; padding: 2rem; background: var(--bg-secondary); border-radius: var(--radius-md); border: 1px solid var(--border-primary);">
<p style="color: var(--text-muted); font-size: 0.9rem;">
💡 Astuce : Cliquez sur une note dans l'arborescence pour commencer à éditer
</p>
</div>
</div>

186
templates/index.html Normal file
View File

@ -0,0 +1,186 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Project Notes</title>
<link rel="stylesheet" href="/static/theme.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@11.9.0/styles/atom-one-dark.min.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/codemirror@5.65.16/lib/codemirror.min.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/codemirror@5.65.16/theme/material-darker.min.css" />
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dompurify/dist/purify.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/highlight.min.js"></script>
<script type="module" src="/static/dist/project-notes-frontend.es.js"></script>
</head>
<body>
<header>
<button id="toggle-sidebar-btn" title="Afficher/Masquer la barre latérale" style="background: none; border: none; padding: 0; margin-right: 1rem; cursor: pointer; color: var(--text-primary); display: flex; align-items: center;">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg>
</button>
<h1>📝 Project Notes</h1>
<input
type="search"
name="query"
placeholder="Rechercher une note (mot-clé, tag:projet, title:... )"
hx-get="/api/search"
hx-trigger="keyup changed delay:500ms, search"
hx-target="#search-results"
hx-indicator="#search-spinner"
style="flex-grow: 1; max-width: 350px;"
/>
<button
hx-get="/api/home"
hx-target="#editor-container"
hx-swap="innerHTML"
style="white-space: nowrap;"
title="Retour à la page d'accueil">
🏠 Accueil
</button>
<button onclick="showNewNoteModal()" style="white-space: nowrap;">
✨ Nouvelle note
</button>
<div id="search-spinner" class="htmx-indicator">
<progress></progress>
</div>
</header>
<!-- Modal pour nouvelle note -->
<div id="new-note-modal" style="display: none;">
<div class="modal-overlay" onclick="hideNewNoteModal()"></div>
<div class="modal-content">
<h2>📝 Nouvelle note</h2>
<form onsubmit="handleNewNote(event)">
<label for="note-name">Nom de la note</label>
<input
type="text"
id="note-name"
placeholder="ma-note.md"
autocomplete="off"
autofocus
required
/>
<p style="color: var(--text-muted); font-size: 0.85rem; margin-top: 0.5rem;">
💡 Si la note existe déjà, elle sera ouverte.
</p>
<div style="display: flex; gap: 0.5rem; margin-top: 1.5rem;">
<button type="submit">Créer / Ouvrir</button>
<button type="button" class="secondary" onclick="hideNewNoteModal()">Annuler</button>
</div>
</form>
</div>
</div>
<!-- Modal pour nouveau dossier -->
<div id="new-folder-modal" style="display: none;">
<div class="modal-overlay" onclick="hideNewFolderModal()"></div>
<div class="modal-content">
<h2>📁 Nouveau dossier</h2>
<form onsubmit="handleNewFolder(event)">
<label for="folder-name">Nom du dossier</label>
<input
type="text"
id="folder-name"
placeholder="mon-dossier"
autocomplete="off"
autofocus
required
/>
<p style="color: var(--text-muted); font-size: 0.85rem; margin-top: 0.5rem;">
💡 Vous pouvez créer des sous-dossiers avec "/", ex: projets/backend
</p>
<div style="display: flex; gap: 0.5rem; margin-top: 1.5rem;">
<button type="submit">Créer</button>
<button type="button" class="secondary" onclick="hideNewFolderModal()">Annuler</button>
</div>
</form>
</div>
</div>
<!-- Overlay pour fermer la sidebar sur mobile -->
<div class="sidebar-overlay" onclick="toggleSidebar()"></div>
<div class="main-layout">
<aside>
<button class="sidebar-close-btn" onclick="toggleSidebar()" title="Fermer le menu">
</button>
<section>
<h2>🔍 Recherche</h2>
<div id="search-results">
<!-- Les résultats de la recherche apparaîtront ici -->
</div>
</section>
<hr>
<section>
<h2>📚 Notes</h2>
<button onclick="showNewFolderModal()" class="folder-create-btn">
📁 Nouveau dossier
</button>
<div id="file-tree" hx-get="/api/tree" hx-trigger="load" hx-swap="innerHTML">
<!-- L'arborescence des fichiers apparaîtra ici -->
<p style="color: var(--text-muted); font-size: 0.85rem;">Chargement...</p>
</div>
</section>
</aside>
<main id="main-content">
<div id="editor-container"
hx-get="/api/home"
hx-trigger="load"
hx-swap="innerHTML">
<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 50vh; text-align: center; color: var(--text-secondary);">
<svg style="width: 64px; height: 64px; margin-bottom: 1rem; opacity: 0.5;" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
</svg>
<p style="font-size: 1.1rem; margin: 0;">Chargement...</p>
</div>
</div>
</main>
</div>
<script>
// Fonction pour gérer l'accordéon des dossiers dans la page d'accueil
window.toggleFolder = function(folderId) {
const content = document.getElementById('folder-' + folderId);
const icon = document.getElementById('icon-' + folderId);
if (content && icon) {
if (content.style.display === 'none') {
content.style.display = 'block';
icon.textContent = '📁';
localStorage.setItem('folder-' + folderId, 'open');
} else {
content.style.display = 'none';
icon.textContent = '📂';
localStorage.setItem('folder-' + folderId, 'closed');
}
}
};
// Restaurer l'état des dossiers depuis localStorage
window.restoreFolderStates = function() {
document.querySelectorAll('.folder-content').forEach(function(content) {
const folderId = content.id.replace('folder-', '');
const state = localStorage.getItem('folder-' + folderId);
const icon = document.getElementById('icon-' + folderId);
if (state === 'closed' && icon) {
content.style.display = 'none';
icon.textContent = '📂';
}
});
};
// Restaurer les états des dossiers après chargement de la page d'accueil
document.body.addEventListener('htmx:afterSwap', function(event) {
if (event.detail.target.id === 'editor-container') {
// Attendre un peu que le DOM soit prêt
setTimeout(restoreFolderStates, 100);
}
});
</script>
</body>
</html>

View File

@ -0,0 +1,6 @@
<form hx-post="/api/notes/create-custom" hx-target="#editor-container" hx-swap="innerHTML">
<label for="new-filename">Nom de la nouvelle note (ex: ma-super-note.md)</label>
<input type="text" id="new-filename" name="filename" placeholder="nom-de-la-note.md" required>
<button type="submit">Créer la note</button>
<button type="button" hx-get="/api/tree" hx-target="#editor-container" hx-swap="innerHTML" class="secondary">Annuler</button>
</form>

View File

@ -0,0 +1,66 @@
{{if .Query}}
{{if .Results}}
<div class="search-results-header">
<span class="search-results-count">{{len .Results}} résultat{{if gt (len .Results) 1}}s{{end}}</span>
</div>
<ul class="search-results-list">
{{range .Results}}
<li class="search-result-item">
<a href="#"
class="search-result-link"
hx-get="/api/notes/{{.Path}}"
hx-target="#editor-container"
hx-swap="innerHTML">
<div class="search-result-icon">📄</div>
<div class="search-result-content">
<div class="search-result-header">
<span class="search-result-title">{{.Title}}</span>
</div>
<div class="search-result-path">{{.Path}}</div>
{{if .Snippet}}
<p class="search-result-snippet">{{.Snippet}}</p>
{{end}}
<div class="search-result-footer">
{{if .Tags}}
<div class="search-result-tags">
{{range .Tags}}
<span class="tag-pill">{{.}}</span>
{{end}}
</div>
{{end}}
{{if .LastModified}}
<span class="search-result-date">Modifié : {{.LastModified}}</span>
{{end}}
</div>
</div>
</a>
</li>
{{end}}
</ul>
{{else}}
<div class="search-no-results">
<div class="search-no-results-icon">🔍</div>
<p class="search-no-results-text">Aucun résultat pour « <strong>{{.Query}}</strong> »</p>
<p class="search-no-results-hint">Essayez d'autres mots-clés ou utilisez les filtres</p>
</div>
{{end}}
{{else}}
<div class="search-help">
<p class="search-help-title">💡 Recherche avancée</p>
<p class="search-help-text">Saisissez des mots-clés pour rechercher dans vos notes</p>
<div class="search-help-examples">
<div class="search-help-example">
<code>tag:projet</code>
<span>Rechercher par tag</span>
</div>
<div class="search-help-example">
<code>title:réunion</code>
<span>Rechercher dans les titres</span>
</div>
<div class="search-help-example">
<code>path:meetings</code>
<span>Rechercher dans les chemins</span>
</div>
</div>
</div>
{{end}}