Add logo and rename
This commit is contained in:
4
API.md
4
API.md
@ -1,4 +1,4 @@
|
||||
# Project Notes REST API Documentation
|
||||
# PersoNotes REST API Documentation
|
||||
|
||||
Version: **v1**
|
||||
Base URL: `http://localhost:8080/api/v1`
|
||||
@ -20,7 +20,7 @@ Base URL: `http://localhost:8080/api/v1`
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
L'API REST de Project Notes permet de gérer vos notes Markdown via HTTP. Elle supporte :
|
||||
L'API REST de PersoNotes permet de gérer vos notes Markdown via HTTP. Elle supporte :
|
||||
|
||||
- **Listage** : Récupérer la liste de toutes les notes avec métadonnées
|
||||
- **Lecture** : Télécharger une note en JSON ou Markdown brut
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Architecture Overview
|
||||
|
||||
Project Notes is a web-based Markdown note-taking application built with a hybrid architecture combining Go backend, HTMX for interactions, and modern JavaScript for UI enhancements.
|
||||
PersoNotes is a web-based Markdown note-taking application built with a hybrid architecture combining Go backend, HTMX for interactions, and modern JavaScript for UI enhancements.
|
||||
|
||||
## Design Philosophy
|
||||
|
||||
@ -68,7 +68,7 @@ User → Browser
|
||||
│ ├─ Inject file tree
|
||||
│ └─ Return HTML
|
||||
│
|
||||
├─ Load static/dist/project-notes-frontend.es.js (Vite bundle)
|
||||
├─ Load static/dist/personotes-frontend.es.js (Vite bundle)
|
||||
│ │
|
||||
│ ├─ Initialize FileTree (file-tree.js)
|
||||
│ ├─ Initialize Search (search.js)
|
||||
@ -237,8 +237,8 @@ frontend/src/
|
||||
↓ (Vite build)
|
||||
|
||||
static/dist/
|
||||
├── project-notes-frontend.es.js (1.0 MB - ES modules)
|
||||
└── project-notes-frontend.umd.js (679 KB - UMD)
|
||||
├── personotes-frontend.es.js (1.0 MB - ES modules)
|
||||
└── personotes-frontend.umd.js (679 KB - UMD)
|
||||
|
||||
↓ (Loaded by browser)
|
||||
|
||||
@ -472,7 +472,7 @@ HTTP Request
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
auth_basic "Project Notes";
|
||||
auth_basic "PersoNotes";
|
||||
auth_basic_user_file /etc/nginx/.htpasswd;
|
||||
proxy_pass http://localhost:8080;
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to Project Notes will be documented in this file.
|
||||
All notable changes to PersoNotes will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
@ -46,7 +46,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Graceful fallback if package not installed
|
||||
|
||||
- **About Page** ℹ️
|
||||
- New "About Project Notes" page accessible from sidebar
|
||||
- New "About PersoNotes" page accessible from sidebar
|
||||
- Features overview with keyboard shortcuts reference
|
||||
- Visual guide to all shortcuts with `<kbd>` styling
|
||||
- Accessible via ℹ️ button next to settings
|
||||
|
||||
18
CLAUDE.md
18
CLAUDE.md
@ -316,8 +316,8 @@ npm run build # Compile frontend modules to static/dist/
|
||||
```
|
||||
|
||||
Output files (loaded by templates):
|
||||
- `static/dist/project-notes-frontend.es.js` (ES module)
|
||||
- `static/dist/project-notes-frontend.umd.js` (UMD format)
|
||||
- `static/dist/personotes-frontend.es.js` (ES module)
|
||||
- `static/dist/personotes-frontend.umd.js` (UMD format)
|
||||
|
||||
Frontend dependencies (from `frontend/package.json`):
|
||||
- `@codemirror/basic-setup` (^0.20.0) - Base editor functionality
|
||||
@ -388,8 +388,8 @@ The frontend uses Vite (`frontend/vite.config.js`) for bundling JavaScript modul
|
||||
1. Vite reads all source files from `frontend/src/`
|
||||
2. Resolves npm dependencies (@codemirror packages)
|
||||
3. Bundles everything into two formats:
|
||||
- ES module (`project-notes-frontend.es.js`) - 1.0 MB
|
||||
- UMD (`project-notes-frontend.umd.js`) - 679 KB
|
||||
- ES module (`personotes-frontend.es.js`) - 1.0 MB
|
||||
- UMD (`personotes-frontend.umd.js`) - 679 KB
|
||||
4. Outputs to `static/dist/` where Go server can serve them
|
||||
5. Templates load the ES module version via `<script type="module">`
|
||||
|
||||
@ -737,13 +737,13 @@ Loaded in `templates/index.html`:
|
||||
|
||||
### Build Output
|
||||
The Vite build process produces:
|
||||
- `static/dist/project-notes-frontend.es.js` - ES module format (1.0 MB, includes all CodeMirror 6 dependencies)
|
||||
- `static/dist/project-notes-frontend.umd.js` - UMD format (679 KB, legacy compatibility)
|
||||
- `static/dist/personotes-frontend.es.js` - ES module format (1.0 MB, includes all CodeMirror 6 dependencies)
|
||||
- `static/dist/personotes-frontend.umd.js` - UMD format (679 KB, legacy compatibility)
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
project-notes/
|
||||
personotes/
|
||||
├── cmd/
|
||||
│ └── server/
|
||||
│ └── main.go # Server entry point
|
||||
@ -776,8 +776,8 @@ project-notes/
|
||||
│ └── vite.config.js # Vite build configuration
|
||||
├── static/
|
||||
│ ├── dist/ # Compiled frontend (generated)
|
||||
│ │ ├── project-notes-frontend.es.js
|
||||
│ │ └── project-notes-frontend.umd.js
|
||||
│ │ ├── personotes-frontend.es.js
|
||||
│ │ └── personotes-frontend.umd.js
|
||||
│ └── theme.css # Material Darker theme
|
||||
├── templates/
|
||||
│ ├── index.html # Main page layout
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
## ✅ Fonctionnalité Implémentée
|
||||
|
||||
Un système complet de gestion de thèmes a été ajouté à Project Notes, permettant aux utilisateurs de personnaliser l'apparence de l'application.
|
||||
Un système complet de gestion de thèmes a été ajouté à PersoNotes, permettant aux utilisateurs de personnaliser l'apparence de l'application.
|
||||
|
||||
## 📁 Fichiers Créés
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# Project Notes
|
||||
# PersoNotes
|
||||
|
||||
A lightweight, web-based Markdown note-taking application with a Go backend and a minimalist frontend built with htmx.
|
||||
|
||||
@ -13,7 +13,7 @@ A lightweight, web-based Markdown note-taking application with a Go backend and
|
||||
- 🛠️ Super Easy to build
|
||||
- 🚀 Powerful REST API
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
## Features
|
||||
@ -56,8 +56,8 @@ A lightweight, web-based Markdown note-taking application with a Go backend and
|
||||
|
||||
1. **Clone the repository:**
|
||||
```bash
|
||||
git clone https://github.com/mathieu/project-notes.git
|
||||
cd project-notes
|
||||
git clone https://github.com/mathieu/personotes.git
|
||||
cd personotes
|
||||
```
|
||||
2. **Download Go modules:**
|
||||
```bash
|
||||
|
||||
@ -13,9 +13,9 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/mathieu/project-notes/internal/api"
|
||||
"github.com/mathieu/project-notes/internal/indexer"
|
||||
"github.com/mathieu/project-notes/internal/watcher"
|
||||
"github.com/mathieu/personotes/internal/api"
|
||||
"github.com/mathieu/personotes/internal/indexer"
|
||||
"github.com/mathieu/personotes/internal/watcher"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
## Hybrid Architecture
|
||||
|
||||
Project Notes uses a **hybrid architecture** that combines the best of multiple paradigms:
|
||||
PersoNotes uses a **hybrid architecture** that combines the best of multiple paradigms:
|
||||
|
||||
### Core Components
|
||||
|
||||
@ -240,7 +240,7 @@ Internet → Reverse Proxy (nginx/Caddy)
|
||||
↓
|
||||
Basic Auth / OAuth
|
||||
↓
|
||||
Project Notes (Go)
|
||||
PersoNotes (Go)
|
||||
↓
|
||||
Filesystem (notes/)
|
||||
```
|
||||
@ -248,7 +248,7 @@ Internet → Reverse Proxy (nginx/Caddy)
|
||||
Example nginx config:
|
||||
```nginx
|
||||
location / {
|
||||
auth_basic "Project Notes";
|
||||
auth_basic "PersoNotes";
|
||||
auth_basic_user_file /etc/nginx/.htpasswd;
|
||||
proxy_pass http://localhost:8080;
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Daily Notes - Documentation
|
||||
|
||||
Les **Daily Notes** sont des notes quotidiennes permettant une prise de notes rapide et organisée par date. Cette fonctionnalité s'intègre parfaitement dans Project Notes avec un calendrier interactif et des raccourcis clavier.
|
||||
Les **Daily Notes** sont des notes quotidiennes permettant une prise de notes rapide et organisée par date. Cette fonctionnalité s'intègre parfaitement dans PersoNotes avec un calendrier interactif et des raccourcis clavier.
|
||||
|
||||
## 🎯 Fonctionnalités
|
||||
|
||||
@ -385,4 +385,4 @@ if ((event.ctrlKey || event.metaKey) && event.key === 'j') {
|
||||
|
||||
**Version** : 1.0.0
|
||||
**Date** : 2025-01-11
|
||||
**Auteur** : Project Notes Team
|
||||
**Auteur** : PersoNotes Team
|
||||
|
||||
@ -69,7 +69,7 @@ Le serveur démarre sur `http://localhost:8080`
|
||||
cp server /usr/local/bin/project-notes
|
||||
|
||||
# Créer un utilisateur dédié
|
||||
pw useradd -n notes -c "Project Notes" -d /var/notes -s /usr/sbin/nologin
|
||||
pw useradd -n notes -c "PersoNotes" -d /var/notes -s /usr/sbin/nologin
|
||||
|
||||
# Créer le dossier de notes
|
||||
mkdir -p /var/notes/notes
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# ⌨️ Raccourcis Clavier - Project Notes
|
||||
# ⌨️ Raccourcis Clavier - PersoNotes
|
||||
|
||||
Cette documentation liste tous les raccourcis clavier disponibles dans l'application Project Notes.
|
||||
Cette documentation liste tous les raccourcis clavier disponibles dans l'application PersoNotes.
|
||||
|
||||
## 📋 Liste des Raccourcis
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Documentation
|
||||
|
||||
Ce dossier contient la documentation détaillée des fonctionnalités de Project Notes.
|
||||
Ce dossier contient la documentation détaillée des fonctionnalités de PersoNotes.
|
||||
|
||||
## Guides Disponibles
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
L'application Project Notes dispose d'un système de thèmes complet permettant aux utilisateurs de personnaliser l'apparence de l'interface. Six thèmes sombres professionnels sont disponibles par défaut.
|
||||
L'application PersoNotes dispose d'un système de thèmes complet permettant aux utilisateurs de personnaliser l'apparence de l'interface. Six thèmes sombres professionnels sont disponibles par défaut.
|
||||
|
||||
## Thèmes Disponibles
|
||||
|
||||
|
||||
@ -105,7 +105,7 @@ Everforest est un thème inspiré de la nature avec une palette de couleurs vert
|
||||
|
||||
## Installation
|
||||
|
||||
Les deux thèmes sont maintenant disponibles par défaut dans Project Notes !
|
||||
Les deux thèmes sont maintenant disponibles par défaut dans PersoNotes !
|
||||
|
||||
### Activation
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Usage Guide
|
||||
|
||||
Complete guide for using Project Notes - from creating your first note to advanced features.
|
||||
Complete guide for using PersoNotes - from creating your first note to advanced features.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@ -19,14 +19,14 @@ Complete guide for using Project Notes - from creating your first note to advanc
|
||||
|
||||
## Quick Start
|
||||
|
||||
The fastest way to get started with Project Notes:
|
||||
The fastest way to get started with PersoNotes:
|
||||
|
||||
1. **Open the application** at `http://localhost:8080`
|
||||
2. **Press `Ctrl/Cmd+D`** to create today's daily note
|
||||
3. **Start writing** - the editor saves automatically with `Ctrl/Cmd+S`
|
||||
4. **Press `Ctrl/Cmd+K`** to search your notes anytime
|
||||
|
||||
That's it! You're now using Project Notes.
|
||||
That's it! You're now using PersoNotes.
|
||||
|
||||
---
|
||||
|
||||
@ -158,7 +158,7 @@ Your Markdown content...
|
||||
|
||||
## Searching Notes
|
||||
|
||||
Project Notes includes a powerful search system with two interfaces.
|
||||
PersoNotes includes a powerful search system with two interfaces.
|
||||
|
||||
### Quick Search Modal (Recommended)
|
||||
|
||||
|
||||
@ -16,7 +16,8 @@ function initDailyNotesShortcut() {
|
||||
if (typeof htmx !== 'undefined') {
|
||||
htmx.ajax('GET', '/api/daily/today', {
|
||||
target: '#editor-container',
|
||||
swap: 'innerHTML'
|
||||
swap: 'innerHTML',
|
||||
pushUrl: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,6 +10,12 @@ class FileTree {
|
||||
}
|
||||
|
||||
init() {
|
||||
this.setupEventListeners();
|
||||
|
||||
console.log('FileTree initialized with event delegation');
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
// Utiliser la délégation d'événements sur le conteneur de la sidebar
|
||||
// Cela évite de perdre les listeners après les swaps htmx
|
||||
const sidebar = document.getElementById('sidebar');
|
||||
@ -18,8 +24,13 @@ class FileTree {
|
||||
return;
|
||||
}
|
||||
|
||||
// Event listener délégué pour les clics sur les folder-headers
|
||||
sidebar.addEventListener('click', (e) => {
|
||||
// Supprimer les anciens listeners s'ils existent
|
||||
if (this.clickHandler) {
|
||||
sidebar.removeEventListener('click', this.clickHandler);
|
||||
}
|
||||
|
||||
// Créer et stocker le handler pour pouvoir le supprimer plus tard
|
||||
this.clickHandler = (e) => {
|
||||
// Ignorer les clics sur les checkboxes
|
||||
if (e.target.classList.contains('selection-checkbox')) {
|
||||
return;
|
||||
@ -41,12 +52,13 @@ class FileTree {
|
||||
// Ne pas bloquer la propagation pour les fichiers
|
||||
return;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Attacher le handler
|
||||
sidebar.addEventListener('click', this.clickHandler);
|
||||
|
||||
// Event listeners délégués pour le drag & drop
|
||||
this.setupDelegatedDragAndDrop(sidebar);
|
||||
|
||||
console.log('FileTree initialized with event delegation');
|
||||
}
|
||||
|
||||
toggleFolder(header) {
|
||||
@ -69,8 +81,17 @@ class FileTree {
|
||||
}
|
||||
|
||||
setupDelegatedDragAndDrop(sidebar) {
|
||||
// Supprimer les anciens handlers s'ils existent
|
||||
if (this.dragStartHandler) {
|
||||
sidebar.removeEventListener('dragstart', this.dragStartHandler);
|
||||
sidebar.removeEventListener('dragend', this.dragEndHandler);
|
||||
sidebar.removeEventListener('dragover', this.dragOverHandler);
|
||||
sidebar.removeEventListener('dragleave', this.dragLeaveHandler);
|
||||
sidebar.removeEventListener('drop', this.dropHandler);
|
||||
}
|
||||
|
||||
// Drag start - délégué pour fichiers et dossiers
|
||||
sidebar.addEventListener('dragstart', (e) => {
|
||||
this.dragStartHandler = (e) => {
|
||||
const fileItem = e.target.closest('.file-item');
|
||||
const folderHeader = e.target.closest('.folder-header');
|
||||
|
||||
@ -79,41 +100,48 @@ class FileTree {
|
||||
} else if (folderHeader && folderHeader.draggable) {
|
||||
this.handleDragStart(e, 'folder', folderHeader);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Drag end - délégué
|
||||
sidebar.addEventListener('dragend', (e) => {
|
||||
this.dragEndHandler = (e) => {
|
||||
const fileItem = e.target.closest('.file-item');
|
||||
const folderHeader = e.target.closest('.folder-header');
|
||||
|
||||
if (fileItem || folderHeader) {
|
||||
this.handleDragEnd(e);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Drag over - délégué sur les folder-headers
|
||||
sidebar.addEventListener('dragover', (e) => {
|
||||
this.dragOverHandler = (e) => {
|
||||
const folderHeader = e.target.closest('.folder-header');
|
||||
if (folderHeader) {
|
||||
this.handleDragOver(e, folderHeader);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Drag leave - délégué
|
||||
sidebar.addEventListener('dragleave', (e) => {
|
||||
this.dragLeaveHandler = (e) => {
|
||||
const folderHeader = e.target.closest('.folder-header');
|
||||
if (folderHeader) {
|
||||
this.handleDragLeave(e, folderHeader);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Drop - délégué
|
||||
sidebar.addEventListener('drop', (e) => {
|
||||
this.dropHandler = (e) => {
|
||||
const folderHeader = e.target.closest('.folder-header');
|
||||
if (folderHeader) {
|
||||
this.handleDrop(e, folderHeader);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Attacher les handlers
|
||||
sidebar.addEventListener('dragstart', this.dragStartHandler);
|
||||
sidebar.addEventListener('dragend', this.dragEndHandler);
|
||||
sidebar.addEventListener('dragover', this.dragOverHandler);
|
||||
sidebar.addEventListener('dragleave', this.dragLeaveHandler);
|
||||
sidebar.addEventListener('drop', this.dropHandler);
|
||||
|
||||
// Rendre les dossiers draggables (sauf racine)
|
||||
this.updateDraggableAttributes();
|
||||
@ -124,6 +152,7 @@ class FileTree {
|
||||
// Vérifier si le swap concerne le file-tree
|
||||
const target = event.detail?.target;
|
||||
if (target && (target.id === 'file-tree' || target.closest('#file-tree'))) {
|
||||
console.log('FileTree: afterSwap detected, updating attributes...');
|
||||
this.updateDraggableAttributes();
|
||||
}
|
||||
});
|
||||
@ -131,10 +160,22 @@ class FileTree {
|
||||
// Écouter aussi les swaps out-of-band (oob) qui mettent à jour le file-tree
|
||||
document.body.addEventListener('htmx:oobAfterSwap', (event) => {
|
||||
const target = event.detail?.target;
|
||||
// Ignorer les swaps de statut (auto-save-status, save-status)
|
||||
if (target && target.id === 'file-tree') {
|
||||
console.log('FileTree: oobAfterSwap detected, updating attributes...');
|
||||
this.updateDraggableAttributes();
|
||||
}
|
||||
});
|
||||
|
||||
// Écouter les restaurations d'historique (bouton retour du navigateur)
|
||||
document.body.addEventListener('htmx:historyRestore', () => {
|
||||
console.log('FileTree: History restored, re-initializing event listeners...');
|
||||
// Réinitialiser complètement les event listeners après restauration de l'historique
|
||||
setTimeout(() => {
|
||||
this.setupEventListeners();
|
||||
this.updateDraggableAttributes();
|
||||
}, 50);
|
||||
});
|
||||
}
|
||||
|
||||
updateDraggableAttributes() {
|
||||
@ -415,7 +456,8 @@ window.handleNewNote = function(event) {
|
||||
if (typeof htmx !== 'undefined') {
|
||||
htmx.ajax('GET', `/api/notes/${encodeURIComponent(noteName)}`, {
|
||||
target: '#editor-container',
|
||||
swap: 'innerHTML'
|
||||
swap: 'innerHTML',
|
||||
pushUrl: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
193
frontend/src/sidebar-sections.js
Normal file
193
frontend/src/sidebar-sections.js
Normal file
@ -0,0 +1,193 @@
|
||||
/**
|
||||
* SidebarSections - Gère les sections rétractables de la sidebar
|
||||
* Permet de replier/déplier les favoris et le répertoire de notes
|
||||
*/
|
||||
class SidebarSections {
|
||||
constructor() {
|
||||
this.sections = {
|
||||
favorites: { key: 'sidebar-favorites-expanded', defaultState: true },
|
||||
notes: { key: 'sidebar-notes-expanded', defaultState: true }
|
||||
};
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
console.log('SidebarSections: Initialisation...');
|
||||
|
||||
// Restaurer l'état sauvegardé au démarrage
|
||||
this.restoreStates();
|
||||
|
||||
// Écouter les événements HTMX pour réattacher les handlers après les swaps
|
||||
document.body.addEventListener('htmx:afterSwap', (event) => {
|
||||
const targetId = event.detail?.target?.id;
|
||||
|
||||
if (targetId === 'favorites-list') {
|
||||
console.log('Favoris rechargés, restauration de l\'état...');
|
||||
setTimeout(() => this.restoreSectionState('favorites'), 50);
|
||||
}
|
||||
|
||||
if (targetId === 'file-tree') {
|
||||
console.log('File-tree rechargé, restauration de l\'état...');
|
||||
setTimeout(() => this.restoreSectionState('notes'), 50);
|
||||
}
|
||||
});
|
||||
|
||||
document.body.addEventListener('htmx:oobAfterSwap', (event) => {
|
||||
const targetId = event.detail?.target?.id;
|
||||
|
||||
// Ne restaurer l'état que pour les swaps du file-tree complet
|
||||
// Les swaps de statut (auto-save-status) ne doivent pas déclencher la restauration
|
||||
if (targetId === 'file-tree') {
|
||||
console.log('File-tree rechargé (oob), restauration de l\'état...');
|
||||
setTimeout(() => this.restoreSectionState('notes'), 50);
|
||||
}
|
||||
});
|
||||
|
||||
// Écouter les restaurations d'historique (bouton retour du navigateur)
|
||||
document.body.addEventListener('htmx:historyRestore', () => {
|
||||
console.log('SidebarSections: History restored, restoring section states...');
|
||||
// Restaurer les états des sections après restauration de l'historique
|
||||
setTimeout(() => {
|
||||
this.restoreSectionState('favorites');
|
||||
this.restoreSectionState('notes');
|
||||
}, 100);
|
||||
});
|
||||
|
||||
console.log('SidebarSections: Initialisé');
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère l'état sauvegardé d'une section
|
||||
*/
|
||||
getSectionState(sectionName) {
|
||||
const section = this.sections[sectionName];
|
||||
if (!section) return true;
|
||||
|
||||
const saved = localStorage.getItem(section.key);
|
||||
return saved !== null ? saved === 'true' : section.defaultState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sauvegarde l'état d'une section
|
||||
*/
|
||||
setSectionState(sectionName, isExpanded) {
|
||||
const section = this.sections[sectionName];
|
||||
if (!section) return;
|
||||
|
||||
localStorage.setItem(section.key, isExpanded.toString());
|
||||
console.log(`État sauvegardé: ${sectionName} = ${isExpanded}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle une section (ouvert/fermé)
|
||||
*/
|
||||
toggleSection(sectionName, headerElement) {
|
||||
if (!headerElement) {
|
||||
console.error(`Header element not found for section: ${sectionName}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const contentElement = headerElement.nextElementSibling;
|
||||
const toggleIcon = headerElement.querySelector('.section-toggle');
|
||||
|
||||
if (!contentElement) {
|
||||
console.error(`Content element not found for section: ${sectionName}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const isCurrentlyExpanded = contentElement.style.display !== 'none';
|
||||
const newState = !isCurrentlyExpanded;
|
||||
|
||||
if (newState) {
|
||||
// Ouvrir
|
||||
contentElement.style.display = 'block';
|
||||
if (toggleIcon) {
|
||||
toggleIcon.classList.add('expanded');
|
||||
}
|
||||
} else {
|
||||
// Fermer
|
||||
contentElement.style.display = 'none';
|
||||
if (toggleIcon) {
|
||||
toggleIcon.classList.remove('expanded');
|
||||
}
|
||||
}
|
||||
|
||||
this.setSectionState(sectionName, newState);
|
||||
console.log(`Section ${sectionName} ${newState ? 'ouverte' : 'fermée'}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restaure l'état d'une section depuis localStorage
|
||||
*/
|
||||
restoreSectionState(sectionName) {
|
||||
const isExpanded = this.getSectionState(sectionName);
|
||||
const header = document.querySelector(`[data-section="${sectionName}"]`);
|
||||
|
||||
if (!header) {
|
||||
console.warn(`Header not found for section: ${sectionName}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const content = header.nextElementSibling;
|
||||
const toggleIcon = header.querySelector('.section-toggle');
|
||||
|
||||
if (!content) {
|
||||
console.warn(`Content not found for section: ${sectionName}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isExpanded) {
|
||||
content.style.display = 'block';
|
||||
if (toggleIcon) {
|
||||
toggleIcon.classList.add('expanded');
|
||||
}
|
||||
} else {
|
||||
content.style.display = 'none';
|
||||
if (toggleIcon) {
|
||||
toggleIcon.classList.remove('expanded');
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`État restauré: ${sectionName} = ${isExpanded ? 'ouvert' : 'fermé'}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restaure tous les états au démarrage
|
||||
*/
|
||||
restoreStates() {
|
||||
// Attendre que le DOM soit complètement chargé et que HTMX ait fini de charger les contenus
|
||||
// Délai augmenté pour correspondre aux délais des triggers HTMX (250ms + marge)
|
||||
setTimeout(() => {
|
||||
this.restoreSectionState('favorites');
|
||||
this.restoreSectionState('notes');
|
||||
}, 400);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction globale pour toggle une section (appelée depuis le HTML)
|
||||
*/
|
||||
window.toggleSidebarSection = function(sectionName, event) {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
const headerElement = event?.currentTarget || document.querySelector(`[data-section="${sectionName}"]`);
|
||||
|
||||
if (window.sidebarSections && headerElement) {
|
||||
window.sidebarSections.toggleSection(sectionName, headerElement);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialisation automatique
|
||||
*/
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
window.sidebarSections = new SidebarSections();
|
||||
});
|
||||
} else {
|
||||
// DOM déjà chargé
|
||||
window.sidebarSections = new SidebarSections();
|
||||
}
|
||||
@ -13,8 +13,8 @@ export default defineConfig({
|
||||
build: {
|
||||
lib: {
|
||||
entry: 'src/main.js', // This will be our main entry point
|
||||
name: 'ProjectNotesFrontend',
|
||||
fileName: (format) => `project-notes-frontend.${format}.js`
|
||||
name: 'PersoNotesFrontend',
|
||||
fileName: (format) => `personotes-frontend.${format}.js`
|
||||
},
|
||||
outDir: '../static/dist', // Output to a new 'dist' folder inside the existing 'static' directory
|
||||
emptyOutDir: true,
|
||||
|
||||
@ -138,8 +138,8 @@ frontend/
|
||||
## Build
|
||||
|
||||
\`npm run build\` génère:
|
||||
- \`project-notes-frontend.es.js\` (ES modules)
|
||||
- \`project-notes-frontend.umd.js\` (UMD)
|
||||
- \`personotes-frontend.es.js\` (ES modules)
|
||||
- \`personotes-frontend.umd.js\` (UMD)
|
||||
|
||||
## Watch Mode
|
||||
|
||||
@ -737,7 +737,7 @@ Not aligned with minimalist approach."
|
||||
|
||||
# Quelques notes à la racine
|
||||
create_note "welcome.md" "Welcome" '"default"' \
|
||||
"# Welcome to Project Notes
|
||||
"# Welcome to PersoNotes
|
||||
|
||||
This is your personal note-taking app.
|
||||
|
||||
|
||||
2
go.mod
2
go.mod
@ -1,4 +1,4 @@
|
||||
module github.com/mathieu/project-notes
|
||||
module github.com/mathieu/personotes
|
||||
|
||||
go 1.22
|
||||
|
||||
|
||||
@ -212,6 +212,12 @@ func (h *Handler) handleDailyCalendar(w http.ResponseWriter, r *http.Request, ye
|
||||
return
|
||||
}
|
||||
|
||||
// Si ce n'est pas une requête HTMX, rediriger vers la page principale
|
||||
if r.Header.Get("HX-Request") == "" {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
// Parser année et mois
|
||||
year, err := strconv.Atoi(yearStr)
|
||||
if err != nil || year < 1900 || year > 2100 {
|
||||
@ -353,6 +359,12 @@ func (h *Handler) handleDailyRecent(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Si ce n'est pas une requête HTMX, rediriger vers la page principale
|
||||
if r.Header.Get("HX-Request") == "" {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
// Chercher les daily notes des 14 derniers jours (au cas où certaines manquent)
|
||||
recentNotes := make([]*DailyNoteInfo, 0, 7)
|
||||
|
||||
|
||||
@ -87,6 +87,13 @@ func (h *Handler) handleFavorites(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// handleGetFavorites retourne la liste des favoris (HTML)
|
||||
func (h *Handler) handleGetFavorites(w http.ResponseWriter, r *http.Request) {
|
||||
// Pas de redirection ici car cet endpoint est utilisé par HTMX ET par fetch()
|
||||
// depuis le JavaScript pour mettre à jour la liste après ajout/suppression
|
||||
h.renderFavoritesList(w)
|
||||
}
|
||||
|
||||
// renderFavoritesList rend le template des favoris (méthode interne)
|
||||
func (h *Handler) renderFavoritesList(w http.ResponseWriter) {
|
||||
favorites, err := h.loadFavorites()
|
||||
if err != nil {
|
||||
h.logger.Printf("Erreur chargement favoris: %v", err)
|
||||
@ -194,22 +201,32 @@ func (h *Handler) handleAddFavorite(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Retourner la liste mise à jour
|
||||
h.handleGetFavorites(w, r)
|
||||
h.renderFavoritesList(w)
|
||||
}
|
||||
|
||||
// handleRemoveFavorite retire un élément des favoris
|
||||
func (h *Handler) handleRemoveFavorite(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
// Pour DELETE, il faut lire le body manuellement
|
||||
body, _ := io.ReadAll(r.Body)
|
||||
// Pour DELETE, il faut toujours lire le body manuellement
|
||||
// car ParseForm() ne lit pas le body pour les méthodes DELETE
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
h.logger.Printf("Erreur lecture body: %v", err)
|
||||
http.Error(w, "Erreur lecture requête", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
r.Body.Close()
|
||||
values, _ := url.ParseQuery(string(body))
|
||||
r.Form = values
|
||||
|
||||
values, err := url.ParseQuery(string(body))
|
||||
if err != nil {
|
||||
h.logger.Printf("Erreur parsing query: %v", err)
|
||||
http.Error(w, "Erreur parsing requête", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
path := r.FormValue("path")
|
||||
path := values.Get("path")
|
||||
|
||||
if path == "" {
|
||||
h.logger.Printf("Chemin requis manquant dans la requête")
|
||||
http.Error(w, "Chemin requis", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@ -251,7 +268,7 @@ func (h *Handler) handleRemoveFavorite(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Retourner la liste mise à jour
|
||||
h.handleGetFavorites(w, r)
|
||||
h.renderFavoritesList(w)
|
||||
}
|
||||
|
||||
// handleReorderFavorites réorganise l'ordre des favoris
|
||||
|
||||
@ -16,7 +16,7 @@ import (
|
||||
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/mathieu/project-notes/internal/indexer"
|
||||
"github.com/mathieu/personotes/internal/indexer"
|
||||
)
|
||||
|
||||
// TreeNode représente un nœud dans l'arborescence des fichiers
|
||||
@ -235,6 +235,12 @@ func (h *Handler) handleFileTree(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Si ce n'est pas une requête HTMX, rediriger vers la page principale
|
||||
if r.Header.Get("HX-Request") == "" {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
tree, err := h.buildFileTree()
|
||||
if err != nil {
|
||||
h.logger.Printf("erreur lors de la construction de l arborescence: %v", err)
|
||||
@ -261,6 +267,12 @@ func (h *Handler) handleHome(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Si ce n'est pas une requête HTMX (ex: accès direct via URL), rediriger vers la page principale
|
||||
if r.Header.Get("HX-Request") == "" {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
// Générer le contenu Markdown avec la liste de toutes les notes
|
||||
content := h.generateHomeMarkdown()
|
||||
|
||||
@ -269,10 +281,12 @@ func (h *Handler) handleHome(w http.ResponseWriter, r *http.Request) {
|
||||
Filename string
|
||||
Content string
|
||||
IsHome bool
|
||||
Backlinks []BacklinkInfo
|
||||
}{
|
||||
Filename: "🏠 Accueil - Index",
|
||||
Content: content,
|
||||
IsHome: true,
|
||||
Backlinks: nil, // Pas de backlinks pour la page d'accueil
|
||||
}
|
||||
|
||||
err := h.templates.ExecuteTemplate(w, "editor.html", data)
|
||||
@ -580,10 +594,12 @@ func (h *Handler) createAndRenderNote(w http.ResponseWriter, r *http.Request, fi
|
||||
Filename string
|
||||
Content string
|
||||
IsHome bool
|
||||
Backlinks []BacklinkInfo
|
||||
}{
|
||||
Filename: filename,
|
||||
Content: initialContent,
|
||||
IsHome: false,
|
||||
Backlinks: nil, // Pas de backlinks pour une nouvelle note
|
||||
}
|
||||
|
||||
err = h.templates.ExecuteTemplate(w, "editor.html", data)
|
||||
@ -599,6 +615,11 @@ func (h *Handler) handleSearch(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Pas de redirection ici car cet endpoint est utilisé par:
|
||||
// 1. La sidebar de recherche (HTMX)
|
||||
// 2. La modale de recherche Ctrl+K (fetch)
|
||||
// 3. Le link inserter pour créer des backlinks (fetch)
|
||||
|
||||
query := strings.TrimSpace(r.URL.Query().Get("query"))
|
||||
if query == "" {
|
||||
query = strings.TrimSpace(r.URL.Query().Get("tag"))
|
||||
@ -673,6 +694,13 @@ func (h *Handler) handleDeleteNote(w http.ResponseWriter, r *http.Request, filen
|
||||
}
|
||||
|
||||
func (h *Handler) handleGetNote(w http.ResponseWriter, r *http.Request, filename string) {
|
||||
// Si ce n'est pas une requête HTMX (ex: refresh navigateur), rediriger vers la page principale
|
||||
// Cela évite d'afficher un fragment HTML sans CSS lors d'un Ctrl+F5
|
||||
if r.Header.Get("HX-Request") == "" {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
fullPath := filepath.Join(h.notesDir, filename)
|
||||
content, err := os.ReadFile(fullPath)
|
||||
if err != nil {
|
||||
@ -800,8 +828,12 @@ func (h *Handler) handlePostNote(w http.ResponseWriter, r *http.Request, filenam
|
||||
}
|
||||
}()
|
||||
|
||||
// Repondre a htmx pour vider l'editeur et rafraichir l'arborescence
|
||||
// Pour les notes existantes, ne pas recharger le file-tree (évite de fermer les dossiers ouverts)
|
||||
// Le file-tree sera rechargé uniquement lors de la création de nouveaux fichiers/dossiers
|
||||
if isNewFile {
|
||||
// Nouvelle note : mettre à jour le file-tree pour l'afficher
|
||||
h.renderFileTreeOOB(w)
|
||||
}
|
||||
|
||||
// Répondre avec les statuts de sauvegarde OOB
|
||||
nowStr := time.Now().Format("15:04:05")
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mathieu/project-notes/internal/indexer"
|
||||
"github.com/mathieu/personotes/internal/indexer"
|
||||
)
|
||||
|
||||
func newTestHandler(t *testing.T, notesDir string) *Handler {
|
||||
|
||||
@ -12,7 +12,7 @@ import (
|
||||
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/mathieu/project-notes/internal/indexer"
|
||||
"github.com/mathieu/personotes/internal/indexer"
|
||||
)
|
||||
|
||||
// REST API Structures
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
|
||||
"github.com/mathieu/project-notes/internal/indexer"
|
||||
"github.com/mathieu/personotes/internal/indexer"
|
||||
)
|
||||
|
||||
// Watcher observe les modifications dans le repertoire des notes et relance l indexation au besoin.
|
||||
|
||||
@ -7,96 +7,68 @@
|
||||
"added_at": "2025-11-11T13:55:41.091354066+01:00",
|
||||
"order": 0
|
||||
},
|
||||
{
|
||||
"path": "documentation/old-ideas.md",
|
||||
"is_dir": false,
|
||||
"title": "old-ideas",
|
||||
"added_at": "2025-11-11T13:55:46.034104752+01:00",
|
||||
"order": 1
|
||||
},
|
||||
{
|
||||
"path": "documentation/bienvenue.md",
|
||||
"is_dir": false,
|
||||
"title": "bienvenue",
|
||||
"added_at": "2025-11-11T13:55:46.95626865+01:00",
|
||||
"order": 2
|
||||
},
|
||||
{
|
||||
"path": "research/ai",
|
||||
"is_dir": true,
|
||||
"title": "ai",
|
||||
"added_at": "2025-11-11T13:55:49.371541279+01:00",
|
||||
"order": 3
|
||||
},
|
||||
{
|
||||
"path": "research/design/typography.md",
|
||||
"is_dir": false,
|
||||
"title": "typography",
|
||||
"added_at": "2025-11-11T13:55:51.238574069+01:00",
|
||||
"order": 4
|
||||
"order": 1
|
||||
},
|
||||
{
|
||||
"path": "research/design/ui-inspiration.md",
|
||||
"is_dir": false,
|
||||
"title": "ui-inspiration",
|
||||
"added_at": "2025-11-11T14:20:49.985321698+01:00",
|
||||
"order": 5
|
||||
},
|
||||
{
|
||||
"path": "research/tech/go-performance.md",
|
||||
"is_dir": false,
|
||||
"title": "go-performance",
|
||||
"added_at": "2025-11-11T14:20:53.861619294+01:00",
|
||||
"order": 6
|
||||
"order": 2
|
||||
},
|
||||
{
|
||||
"path": "research/tech/websockets.md",
|
||||
"is_dir": false,
|
||||
"title": "websockets",
|
||||
"added_at": "2025-11-11T14:20:55.347335695+01:00",
|
||||
"order": 7
|
||||
"order": 3
|
||||
},
|
||||
{
|
||||
"path": "tasks/backlog.md",
|
||||
"is_dir": false,
|
||||
"title": "backlog",
|
||||
"added_at": "2025-11-11T14:20:57.762787363+01:00",
|
||||
"order": 8
|
||||
"order": 4
|
||||
},
|
||||
{
|
||||
"path": "ideas/client-feedback.md",
|
||||
"is_dir": false,
|
||||
"title": "client-feedback",
|
||||
"added_at": "2025-11-11T14:22:16.497953232+01:00",
|
||||
"order": 9
|
||||
"order": 5
|
||||
},
|
||||
{
|
||||
"path": "ideas/collaboration.md",
|
||||
"is_dir": false,
|
||||
"title": "collaboration",
|
||||
"added_at": "2025-11-11T14:22:18.012032002+01:00",
|
||||
"order": 10
|
||||
"order": 6
|
||||
},
|
||||
{
|
||||
"path": "ideas/mobile-app.md",
|
||||
"is_dir": false,
|
||||
"title": "mobile-app",
|
||||
"added_at": "2025-11-11T14:22:19.048311608+01:00",
|
||||
"order": 11
|
||||
"order": 7
|
||||
},
|
||||
{
|
||||
"path": "meetings/2025",
|
||||
"is_dir": true,
|
||||
"title": "2025",
|
||||
"added_at": "2025-11-11T14:22:21.531283601+01:00",
|
||||
"order": 12
|
||||
"order": 8
|
||||
},
|
||||
{
|
||||
"path": "meetings/outscale.md",
|
||||
"is_dir": false,
|
||||
"title": "outscale",
|
||||
"added_at": "2025-11-11T14:22:22.519332518+01:00",
|
||||
"order": 13
|
||||
"order": 9
|
||||
}
|
||||
]
|
||||
}
|
||||
28
notes/daily/2025/11/12.md
Normal file
28
notes/daily/2025/11/12.md
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
title: Daily Note - 2025-11-12
|
||||
date: 12-11-2025
|
||||
last_modified: 12-11-2025:10:37
|
||||
tags:
|
||||
- daily
|
||||
---
|
||||
|
||||
# 📅 Mercredi 12 novembre 2025
|
||||
|
||||
## 🎯 Objectifs du jour
|
||||
-
|
||||
|
||||
## 📝 Notes
|
||||
- C'est la note du jour
|
||||
|
||||
Elle est cool
|
||||
|
||||
/i
|
||||
|
||||
## ✅ Accompli
|
||||
-
|
||||
|
||||
## 💭 Réflexions
|
||||
-
|
||||
|
||||
## 🔗 Liens
|
||||
-
|
||||
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Bienvenue dans Project Notes
|
||||
title: Bienvenue dans PersoNotes
|
||||
date: 08-11-2025
|
||||
last_modified: 09-11-2025:01:13
|
||||
tags:
|
||||
@ -17,7 +17,7 @@ C'est mon application de prise de note
|
||||
|
||||
## J'espére qu'elle va bien marcher
|
||||
|
||||
# Bienvenue dans Project Notes
|
||||
# Bienvenue dans PersoNotes
|
||||
|
||||
Bienvenue dans votre application de prise de notes en Markdown ! Cette page vous explique comment utiliser l'application et le format front matter.
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
---
|
||||
title: "Sprint Planning January"
|
||||
date: "10-11-2025"
|
||||
last_modified: "10-11-2025:19:21"
|
||||
tags: ["meeting", "planning"]
|
||||
title: Sprint Planning January
|
||||
date: 10-11-2025
|
||||
last_modified: 12-11-2025:10:26
|
||||
tags:
|
||||
- meeting
|
||||
- planning
|
||||
---
|
||||
|
||||
# Sprint Planning - Janvier 2025
|
||||
@ -26,3 +28,6 @@ tags: ["meeting", "planning"]
|
||||
|
||||
- Complexité du drag & drop de dossiers
|
||||
- Tests E2E à mettre en place
|
||||
|
||||
|
||||
/il
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
---
|
||||
title: 2025 Learning Goals
|
||||
date: 10-11-2025
|
||||
last_modified: 11-11-2025:17:15
|
||||
last_modified: 12-11-2025:10:28
|
||||
tags:
|
||||
- personal
|
||||
- learning
|
||||
@ -28,5 +28,3 @@ tags:
|
||||
2. The Pragmatic Programmer
|
||||
3. Clean Architecture
|
||||
|
||||
|
||||
/
|
||||
@ -1,7 +1,7 @@
|
||||
---
|
||||
title: API Design
|
||||
date: 10-11-2025
|
||||
last_modified: 11-11-2025:15:23
|
||||
last_modified: 12-11-2025:10:32
|
||||
tags:
|
||||
- projet
|
||||
- backend
|
||||
@ -29,3 +29,6 @@ Pour l'instant, pas d'authentification. À implémenter avec JWT.
|
||||
|
||||
|
||||
<!-- -->
|
||||
|
||||
|
||||
## This is a test
|
||||
@ -1,8 +1,11 @@
|
||||
---
|
||||
title: "CodeMirror Integration"
|
||||
date: "10-11-2025"
|
||||
last_modified: "10-11-2025:19:21"
|
||||
tags: ["projet", "frontend", "editor"]
|
||||
title: CodeMirror Integration
|
||||
date: 10-11-2025
|
||||
last_modified: 12-11-2025:09:37
|
||||
tags:
|
||||
- projet
|
||||
- frontend
|
||||
- editor
|
||||
---
|
||||
|
||||
# CodeMirror 6 Integration
|
||||
@ -22,6 +25,7 @@ Système de commandes rapides avec `/`:
|
||||
- /table - Tableau
|
||||
- /code - Bloc de code
|
||||
|
||||
|
||||
## Auto-save
|
||||
|
||||
Déclenché après 2 secondes d'inactivité.
|
||||
|
||||
@ -23,8 +23,8 @@ frontend/
|
||||
## Build
|
||||
|
||||
`npm run build` génère:
|
||||
- `project-notes-frontend.es.js` (ES modules)
|
||||
- `project-notes-frontend.umd.js` (UMD)
|
||||
- `personotes-frontend.es.js` (ES modules)
|
||||
- `personotes-frontend.umd.js` (UMD)
|
||||
|
||||
## Watch Mode
|
||||
|
||||
|
||||
@ -11,4 +11,4 @@ ddddddddlffdfdddddddddddddd
|
||||
|
||||
[texte](url)
|
||||
|
||||
<a href="#" onclick="return false;" hx-get="/api/notes/documentation/bienvenue.md" hx-target="#editor-container" hx-swap="innerHTML">Bienvenue dans Project Notes</a>
|
||||
<a href="#" onclick="return false;" hx-get="/api/notes/documentation/bienvenue.md" hx-target="#editor-container" hx-swap="innerHTML">Bienvenue dans PersoNotes</a>
|
||||
@ -5,7 +5,7 @@ last_modified: "10-11-2025:19:21"
|
||||
tags: ["default"]
|
||||
---
|
||||
|
||||
# Welcome to Project Notes
|
||||
# Welcome to PersoNotes
|
||||
|
||||
This is your personal note-taking app.
|
||||
|
||||
|
||||
BIN
personotes.jpg
Normal file
BIN
personotes.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
76
static/images/logo.svg
Normal file
76
static/images/logo.svg
Normal file
@ -0,0 +1,76 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">
|
||||
<defs>
|
||||
<style>
|
||||
.dog-face { fill: #E8DCC8; }
|
||||
.dog-fur-dark { fill: #8B7355; }
|
||||
.dog-ear-inner { fill: #D4A574; }
|
||||
.dog-eye { fill: #2C1810; }
|
||||
.dog-nose { fill: #1A0F0A; }
|
||||
.dog-mouth { fill: none; stroke: #2C1810; stroke-width: 1; stroke-linecap: round; }
|
||||
.pencil-body { fill: #F4C430; }
|
||||
.pencil-tip { fill: #2C1810; }
|
||||
.pencil-eraser { fill: #FF6B9D; }
|
||||
.pencil-band { fill: #C0C0C0; }
|
||||
.highlight { fill: white; opacity: 0.6; }
|
||||
</style>
|
||||
</defs>
|
||||
|
||||
<!-- Oreille gauche -->
|
||||
<path class="dog-fur-dark" d="M 18 15 L 12 5 L 22 8 Z"/>
|
||||
<path class="dog-ear-inner" d="M 18 15 L 14 8 L 20 10 Z"/>
|
||||
|
||||
<!-- Oreille droite -->
|
||||
<path class="dog-fur-dark" d="M 46 15 L 52 5 L 42 8 Z"/>
|
||||
<path class="dog-ear-inner" d="M 46 15 L 50 8 L 44 10 Z"/>
|
||||
|
||||
<!-- Tache de fourrure sombre sur le front -->
|
||||
<ellipse class="dog-fur-dark" cx="32" cy="20" rx="12" ry="8"/>
|
||||
|
||||
<!-- Visage principal -->
|
||||
<circle class="dog-face" cx="32" cy="32" r="20"/>
|
||||
|
||||
<!-- Taches de fourrure claires sur les joues -->
|
||||
<ellipse class="dog-face" cx="22" cy="35" rx="8" ry="10" opacity="0.8"/>
|
||||
<ellipse class="dog-face" cx="42" cy="35" rx="8" ry="10" opacity="0.8"/>
|
||||
|
||||
<!-- Yeux -->
|
||||
<ellipse class="dog-eye" cx="26" cy="28" rx="3" ry="4"/>
|
||||
<ellipse class="dog-eye" cx="38" cy="28" rx="3" ry="4"/>
|
||||
|
||||
<!-- Reflets dans les yeux pour le rendre plus vivant -->
|
||||
<circle class="highlight" cx="27" cy="27" r="1.5"/>
|
||||
<circle class="highlight" cx="39" cy="27" r="1.5"/>
|
||||
|
||||
<!-- Museau -->
|
||||
<ellipse class="dog-face" cx="32" cy="38" rx="10" ry="8"/>
|
||||
|
||||
<!-- Nez -->
|
||||
<ellipse class="dog-nose" cx="32" cy="36" rx="3" ry="2.5"/>
|
||||
|
||||
<!-- Crayon dans la bouche -->
|
||||
<!-- Corps du crayon (partie hexagonale) -->
|
||||
<g transform="translate(32, 42) rotate(0)">
|
||||
<!-- Partie principale du crayon -->
|
||||
<rect class="pencil-body" x="-15" y="-2" width="30" height="4" rx="0.5"/>
|
||||
|
||||
<!-- Bandes décoratives -->
|
||||
<rect class="pencil-band" x="-15" y="-2" width="2" height="4"/>
|
||||
<rect class="pencil-band" x="13" y="-2" width="2" height="4"/>
|
||||
|
||||
<!-- Pointe du crayon (gauche) -->
|
||||
<path class="pencil-body" d="M -15 -2 L -19 0 L -15 2 Z"/>
|
||||
<path class="pencil-tip" d="M -19 -0.5 L -22 0 L -19 0.5 Z"/>
|
||||
|
||||
<!-- Gomme (droite) -->
|
||||
<rect class="pencil-eraser" x="15" y="-2" width="3" height="4" rx="0.5"/>
|
||||
|
||||
<!-- Reflet sur le crayon -->
|
||||
<rect class="highlight" x="-12" y="-1.5" width="20" height="1" rx="0.5" opacity="0.3"/>
|
||||
</g>
|
||||
|
||||
<!-- Petites moustaches -->
|
||||
<line class="dog-mouth" x1="18" y1="36" x2="12" y2="35"/>
|
||||
<line class="dog-mouth" x1="18" y1="38" x2="12" y2="39"/>
|
||||
<line class="dog-mouth" x1="46" y1="36" x2="52" y2="35"/>
|
||||
<line class="dog-mouth" x1="46" y1="38" x2="52" y2="39"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Project Notes - Material Darker Theme
|
||||
* PersoNotes - Material Darker Theme
|
||||
* Inspired by Material Design with dark palette
|
||||
*/
|
||||
|
||||
@ -219,6 +219,38 @@ aside h2,
|
||||
margin: 0 0 var(--spacing-sm) 0;
|
||||
}
|
||||
|
||||
/* Sections rétractables de la sidebar */
|
||||
.sidebar-section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 0;
|
||||
user-select: none;
|
||||
transition: opacity var(--transition-fast);
|
||||
}
|
||||
|
||||
.sidebar-section-header:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.section-toggle {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-secondary);
|
||||
display: inline-block;
|
||||
transition: transform var(--transition-fast);
|
||||
width: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.section-toggle.expanded {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.sidebar-section-content {
|
||||
overflow: hidden;
|
||||
transition: opacity var(--transition-fast);
|
||||
}
|
||||
|
||||
aside hr {
|
||||
border: none;
|
||||
border-top: 1px solid var(--border-primary);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Project Notes - Multi-Theme System
|
||||
* PersoNotes - Multi-Theme System
|
||||
* Supports: Material Dark (default), Monokai Dark, Dracula, One Dark, Solarized Dark, Nord
|
||||
*/
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<div id="about-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;">
|
||||
📝 About Project Notes
|
||||
📝 About PersoNotes
|
||||
</h1>
|
||||
<p style="font-size: 1.2rem; color: var(--text-secondary); margin-bottom: 2rem;">
|
||||
Un gestionnaire de notes Markdown moderne et puissant
|
||||
|
||||
@ -36,6 +36,7 @@
|
||||
hx-get="/api/daily/{{.Date.Format "2006-01-02"}}"
|
||||
hx-target="#editor-container"
|
||||
hx-swap="innerHTML"
|
||||
hx-push-url="true"
|
||||
{{end}}
|
||||
title="{{if .HasNote}}Note du {{.Date.Format "02/01/2006"}}{{else}}{{.Date.Format "02/01/2006"}} - Pas de note{{end}}">
|
||||
<span class="calendar-day-number">{{.Day}}</span>
|
||||
@ -49,6 +50,7 @@
|
||||
hx-get="/api/daily/today"
|
||||
hx-target="#editor-container"
|
||||
hx-swap="innerHTML"
|
||||
hx-push-url="true"
|
||||
title="Ouvrir la note du jour">
|
||||
📅 Aujourd'hui
|
||||
</button>
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
hx-get="/api/daily/{{.Date.Format "2006-01-02"}}"
|
||||
hx-target="#editor-container"
|
||||
hx-swap="innerHTML"
|
||||
hx-push-url="true"
|
||||
title="Note du {{.Date.Format "02/01/2006"}}">
|
||||
<span class="daily-recent-icon">📄</span>
|
||||
<div class="daily-recent-content">
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
{{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">
|
||||
<button type="button" class="toggle-preview-btn" hx-get="/api/home" hx-target="#editor-container" hx-swap="innerHTML" hx-push-url="true" title="Actualiser la page d'accueil">
|
||||
🔄 Actualiser
|
||||
</button>
|
||||
{{else}}
|
||||
@ -32,7 +32,7 @@
|
||||
<ul class="backlinks-list">
|
||||
{{range .Backlinks}}
|
||||
<li class="backlink-item">
|
||||
<a href="#" onclick="return false;" hx-get="/api/notes/{{.Path}}" hx-target="#editor-container" hx-swap="innerHTML" class="backlink-link">
|
||||
<a href="#" onclick="return false;" hx-get="/api/notes/{{.Path}}" hx-target="#editor-container" hx-swap="innerHTML" hx-push-url="true" class="backlink-link">
|
||||
📄 {{.Title}}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@ -16,7 +16,8 @@
|
||||
data-path="{{.Path}}"
|
||||
hx-get="/api/notes/{{.Path}}"
|
||||
hx-target="#editor-container"
|
||||
hx-swap="innerHTML">
|
||||
hx-swap="innerHTML"
|
||||
hx-push-url="true">
|
||||
<span class="favorite-icon">⭐</span>
|
||||
<span class="favorite-file-icon">{{.Icon}}</span>
|
||||
<span class="favorite-name">{{.Title}}</span>
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
<!-- Indicateur de racine (non cliquable) -->
|
||||
<div class="root-indicator">
|
||||
<!-- Indicateur de racine (maintenant cliquable et rétractable) -->
|
||||
<div class="sidebar-section-header" data-section="notes" onclick="toggleSidebarSection('notes', event)" style="cursor: pointer;">
|
||||
<span class="section-toggle expanded">▶</span>
|
||||
<span class="folder-icon">🏠</span>
|
||||
<span class="folder-name">Racine</span>
|
||||
<span class="root-hint">(notes/)</span>
|
||||
</div>
|
||||
|
||||
<!-- Contenu rétractable du file-tree -->
|
||||
<div class="sidebar-section-content" id="notes-content" style="display: block;">
|
||||
<hr style="border: none; border-top: 1px solid var(--border-primary); margin: 0.75rem 0;">
|
||||
|
||||
{{if .Tree}}
|
||||
@ -16,6 +19,7 @@
|
||||
{{else}}
|
||||
<p style="color: var(--text-muted); font-size: 0.85rem;">Aucune note trouvée.</p>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{define "tree-node"}}
|
||||
{{range .Children}}
|
||||
@ -43,6 +47,7 @@
|
||||
hx-get="/api/notes/{{.Path}}"
|
||||
hx-target="#editor-container"
|
||||
hx-swap="innerHTML"
|
||||
hx-push-url="true"
|
||||
draggable="true">
|
||||
📄 {{.Name}}
|
||||
</a>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Project Notes</title>
|
||||
<title>PersoNotes</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
@ -21,15 +21,19 @@
|
||||
<script src="/frontend/src/font-manager.js"></script>
|
||||
<script src="/frontend/src/vim-mode-manager.js"></script>
|
||||
<script src="/frontend/src/favorites.js"></script>
|
||||
<script src="/frontend/src/sidebar-sections.js"></script>
|
||||
<script src="/frontend/src/keyboard-shortcuts.js"></script>
|
||||
<script type="module" src="/static/dist/project-notes-frontend.es.js"></script>
|
||||
<script type="module" src="/static/dist/personotes-frontend.es.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<button id="toggle-sidebar-btn" title="Afficher/Masquer la barre latérale (Ctrl/Cmd+B)" 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>
|
||||
<div style="display: flex; align-items: center; gap: 0.75rem;">
|
||||
<img src="/static/images/logo.svg" alt="Logo" style="width: 40px; height: 40px;">
|
||||
<h1 style="margin: 0;">PersoNotes</h1>
|
||||
</div>
|
||||
<input
|
||||
type="search"
|
||||
name="query"
|
||||
@ -44,6 +48,7 @@
|
||||
hx-get="/api/home"
|
||||
hx-target="#editor-container"
|
||||
hx-swap="innerHTML"
|
||||
hx-push-url="true"
|
||||
style="white-space: nowrap;"
|
||||
title="Retour à la page d'accueil (Ctrl/Cmd+H)">
|
||||
🏠 Accueil
|
||||
@ -52,6 +57,7 @@
|
||||
hx-get="/api/daily/today"
|
||||
hx-target="#editor-container"
|
||||
hx-swap="innerHTML"
|
||||
hx-push-url="true"
|
||||
style="white-space: nowrap;"
|
||||
title="Note du jour (Ctrl/Cmd+D)">
|
||||
📅 Note du jour
|
||||
@ -478,14 +484,19 @@
|
||||
<hr>
|
||||
|
||||
<section>
|
||||
<h2 class="sidebar-section-title">⭐ Favoris</h2>
|
||||
<div class="sidebar-section-header" data-section="favorites" onclick="toggleSidebarSection('favorites', event)" style="cursor: pointer; display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem 0;">
|
||||
<span class="section-toggle expanded">▶</span>
|
||||
<h2 class="sidebar-section-title" style="margin: 0; flex: 1;">⭐ Favoris</h2>
|
||||
</div>
|
||||
<div class="sidebar-section-content" id="favorites-content" style="display: block;">
|
||||
<div id="favorites-list"
|
||||
hx-get="/api/favorites"
|
||||
hx-trigger="load"
|
||||
hx-trigger="load once delay:100ms"
|
||||
hx-swap="innerHTML">
|
||||
<!-- Les favoris apparaîtront ici -->
|
||||
<p style="color: var(--text-muted); font-size: 0.85rem; text-align: center;">Chargement...</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<hr>
|
||||
@ -494,7 +505,7 @@
|
||||
<h2 class="sidebar-section-title">📅 Daily Notes</h2>
|
||||
<div id="daily-calendar-container"
|
||||
hx-get="/api/daily/calendar/{{.Now.Format "2006/01"}}"
|
||||
hx-trigger="load"
|
||||
hx-trigger="load once delay:150ms"
|
||||
hx-swap="innerHTML">
|
||||
<!-- Le calendrier apparaîtra ici -->
|
||||
<p style="color: var(--text-muted); font-size: 0.85rem; text-align: center;">Chargement...</p>
|
||||
@ -503,7 +514,7 @@
|
||||
<h3 style="font-size: 0.8rem; margin-top: var(--spacing-md); margin-bottom: var(--spacing-sm); color: var(--text-secondary);">Récentes</h3>
|
||||
<div id="daily-recent-container"
|
||||
hx-get="/api/daily/recent"
|
||||
hx-trigger="load"
|
||||
hx-trigger="load once delay:200ms"
|
||||
hx-swap="innerHTML">
|
||||
<!-- Les notes récentes apparaîtront ici -->
|
||||
</div>
|
||||
@ -521,7 +532,7 @@
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div id="file-tree" hx-get="/api/tree" hx-trigger="load" hx-swap="innerHTML">
|
||||
<div id="file-tree" hx-get="/api/tree" hx-trigger="load once delay:250ms" hx-swap="innerHTML">
|
||||
<!-- L'arborescence des fichiers apparaîtra ici -->
|
||||
<p style="color: var(--text-muted); font-size: 0.85rem;">Chargement...</p>
|
||||
</div>
|
||||
@ -546,7 +557,7 @@
|
||||
<!-- Bouton À propos -->
|
||||
<button
|
||||
class="sidebar-action-btn"
|
||||
title="À propos de Project Notes"
|
||||
title="À propos de PersoNotes"
|
||||
hx-get="/api/about"
|
||||
hx-target="#editor-container"
|
||||
hx-swap="innerHTML"
|
||||
@ -562,7 +573,7 @@
|
||||
<main id="main-content">
|
||||
<div id="editor-container"
|
||||
hx-get="/api/home"
|
||||
hx-trigger="load"
|
||||
hx-trigger="load once"
|
||||
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">
|
||||
|
||||
@ -10,7 +10,8 @@
|
||||
class="search-result-link"
|
||||
hx-get="/api/notes/{{.Path}}"
|
||||
hx-target="#editor-container"
|
||||
hx-swap="innerHTML">
|
||||
hx-swap="innerHTML"
|
||||
hx-push-url="true">
|
||||
<div class="search-result-icon">📄</div>
|
||||
<div class="search-result-content">
|
||||
<div class="search-result-header">
|
||||
|
||||
Reference in New Issue
Block a user