Premier commit déjà bien avancé
This commit is contained in:
48
templates/editor.html
Normal file
48
templates/editor.html
Normal 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 : <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
39
templates/file-tree.html
Normal 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
71
templates/home.html
Normal 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
186
templates/index.html
Normal 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>
|
||||
6
templates/new-note-prompt.html
Normal file
6
templates/new-note-prompt.html
Normal 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>
|
||||
66
templates/search-results.html
Normal file
66
templates/search-results.html
Normal 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}}
|
||||
Reference in New Issue
Block a user