commit db4f0508cb852cd66dd4aa40f72975ea5196d5d0 Author: Mathieu Aumont Date: Mon Nov 10 18:33:24 2025 +0100 Premier commit déjà bien avancé diff --git a/API.md b/API.md new file mode 100644 index 0000000..79e5330 --- /dev/null +++ b/API.md @@ -0,0 +1,569 @@ +# Project Notes REST API Documentation + +Version: **v1** +Base URL: `http://localhost:8080/api/v1` + +## Table des matières + +- [Vue d'ensemble](#vue-densemble) +- [Authentification](#authentification) +- [Formats de données](#formats-de-données) +- [Endpoints](#endpoints) + - [Lister les notes](#lister-les-notes) + - [Récupérer une note](#récupérer-une-note) + - [Créer/Mettre à jour une note](#créermettre-à-jour-une-note) + - [Supprimer une note](#supprimer-une-note) +- [Codes de statut HTTP](#codes-de-statut-http) +- [Exemples d'utilisation](#exemples-dutilisation) + +--- + +## Vue d'ensemble + +L'API REST de Project Notes 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 +- **Écriture** : Créer ou mettre à jour une note +- **Suppression** : Supprimer une note définitivement + +### Caractéristiques + +- ✅ **Versionnée** : `/api/v1` pour assurer la compatibilité future +- ✅ **Content Negotiation** : Support JSON et Markdown selon le header `Accept` +- ✅ **Idempotence** : PUT crée ou met à jour (idempotent) +- ✅ **Automatisation** : Front matter géré automatiquement +- ✅ **Ré-indexation** : Recherche mise à jour automatiquement après chaque modification +- ✅ **Support sous-dossiers** : Organisation hiérarchique native + +--- + +## Authentification + +**Version actuelle : Aucune authentification requise** + +⚠️ **IMPORTANT** : L'API n'a pas d'authentification. Si vous exposez le serveur publiquement : +1. Utilisez un reverse proxy (nginx, Caddy) avec authentification +2. Implémentez Basic Auth ou API Key +3. Utilisez un VPN ou tunnel SSH + +--- + +## Formats de données + +### Note complète (NoteResponse) + +```json +{ + "path": "projet/backend.md", + "title": "Backend API", + "content": "---\ntitle: Backend API\n...\n---\n\n# Content", + "body": "\n# Content", + "frontMatter": { + "title": "Backend API", + "date": "10-11-2025", + "last_modified": "10-11-2025:14:30", + "tags": ["projet", "backend"] + }, + "lastModified": "10-11-2025:14:30", + "size": 1024 +} +``` + +### Métadonnées de note (NoteMetadata) + +```json +{ + "path": "projet/backend.md", + "title": "Backend API", + "tags": ["projet", "backend"], + "lastModified": "10-11-2025:14:30", + "date": "10-11-2025", + "size": 1024 +} +``` + +### Requête de création/modification (NoteRequest) + +Deux options : + +**Option 1 : Content complet** (recommandé pour migration) +```json +{ + "content": "---\ntitle: Ma note\ntags: [test]\n---\n\n# Contenu" +} +``` + +**Option 2 : Body + FrontMatter séparés** (recommandé pour création) +```json +{ + "body": "\n# Mon contenu\n\nTexte ici...", + "frontMatter": { + "title": "Ma note", + "tags": ["test", "exemple"] + } +} +``` + +### Erreur (ErrorResponse) + +```json +{ + "error": "Bad Request", + "message": "Invalid path", + "code": 400 +} +``` + +--- + +## Endpoints + +### Lister les notes + +Récupère la liste de toutes les notes avec leurs métadonnées. + +**Endpoint** : `GET /api/v1/notes` + +**Paramètres** : Aucun + +**Réponse** : `200 OK` + +```json +{ + "notes": [ + { + "path": "projet/backend.md", + "title": "Backend API", + "tags": ["projet", "backend"], + "lastModified": "10-11-2025:14:30", + "date": "10-11-2025", + "size": 1024 + }, + { + "path": "daily/2025-11-10.md", + "title": "Notes du 10 novembre", + "tags": ["daily"], + "lastModified": "10-11-2025:09:15", + "date": "10-11-2025", + "size": 2048 + } + ], + "total": 2 +} +``` + +**Exemple curl** : + +```bash +curl http://localhost:8080/api/v1/notes +``` + +--- + +### Récupérer une note + +Télécharge une note spécifique. Le format dépend du header `Accept`. + +**Endpoint** : `GET /api/v1/notes/{path}` + +**Paramètres** : +- `{path}` : Chemin relatif de la note (ex: `projet/backend.md`) + +**Headers** : +- `Accept: application/json` (défaut) : Retourne JSON avec métadonnées +- `Accept: text/markdown` : Retourne Markdown brut +- `Accept: text/plain` : Retourne Markdown brut + +**Réponse JSON** : `200 OK` + +```json +{ + "path": "projet/backend.md", + "title": "Backend API", + "content": "---\ntitle: Backend API\ndate: 10-11-2025\nlast_modified: 10-11-2025:14:30\ntags:\n - projet\n - backend\n---\n\n# Backend API\n\nContenu de la note...", + "body": "\n# Backend API\n\nContenu de la note...", + "frontMatter": { + "title": "Backend API", + "date": "10-11-2025", + "last_modified": "10-11-2025:14:30", + "tags": ["projet", "backend"] + }, + "lastModified": "10-11-2025:14:30", + "size": 1024 +} +``` + +**Réponse Markdown** : `200 OK` + +```markdown +--- +title: Backend API +date: 10-11-2025 +last_modified: 10-11-2025:14:30 +tags: + - projet + - backend +--- + +# Backend API + +Contenu de la note... +``` + +**Erreurs** : +- `404 Not Found` : Note inexistante + +**Exemples curl** : + +```bash +# Récupérer en JSON +curl http://localhost:8080/api/v1/notes/projet/backend.md \ + -H "Accept: application/json" + +# Récupérer en Markdown brut +curl http://localhost:8080/api/v1/notes/projet/backend.md \ + -H "Accept: text/markdown" + +# Sauvegarder dans un fichier +curl http://localhost:8080/api/v1/notes/projet/backend.md \ + -H "Accept: text/markdown" \ + -o backend.md +``` + +--- + +### Créer/Mettre à jour une note + +Crée une nouvelle note ou met à jour une note existante (idempotent). + +**Endpoint** : `PUT /api/v1/notes/{path}` + +**Paramètres** : +- `{path}` : Chemin relatif de la note (ex: `projet/nouvelle-note.md`) + +**Headers** : +- `Content-Type: application/json` : Envoyer du JSON +- `Content-Type: text/markdown` : Envoyer du Markdown brut + +**Body JSON** : + +```json +{ + "body": "\n# Ma nouvelle note\n\nContenu ici...", + "frontMatter": { + "title": "Ma nouvelle note", + "tags": ["test", "exemple"] + } +} +``` + +Ou avec `content` complet : + +```json +{ + "content": "---\ntitle: Ma note\ntags: [test]\n---\n\n# Contenu" +} +``` + +**Body Markdown brut** : + +```markdown +# Ma nouvelle note + +Contenu ici... +``` + +**Réponse** : `201 Created` (nouvelle note) ou `200 OK` (mise à jour) + +```json +{ + "path": "projet/nouvelle-note.md", + "title": "Ma nouvelle note", + "content": "---\ntitle: Ma nouvelle note\n...", + "body": "\n# Ma nouvelle note\n\nContenu ici...", + "frontMatter": { + "title": "Ma nouvelle note", + "date": "10-11-2025", + "last_modified": "10-11-2025:14:35", + "tags": ["test", "exemple"] + }, + "lastModified": "10-11-2025:14:35", + "size": 256 +} +``` + +**Comportement** : +- ✅ Crée automatiquement les dossiers parents +- ✅ Génère le front matter si absent +- ✅ Met à jour `last_modified` automatiquement +- ✅ Préserve `date` pour notes existantes +- ✅ Ré-indexe automatiquement pour la recherche + +**Erreurs** : +- `400 Bad Request` : Chemin invalide ou JSON malformé +- `415 Unsupported Media Type` : Content-Type non supporté +- `500 Internal Server Error` : Erreur d'écriture + +**Exemples curl** : + +```bash +# Créer avec JSON +curl -X PUT http://localhost:8080/api/v1/notes/projet/test.md \ + -H "Content-Type: application/json" \ + -d '{ + "body": "\n# Test\n\nCeci est un test.", + "frontMatter": { + "title": "Note de test", + "tags": ["test"] + } + }' + +# Créer avec Markdown brut +curl -X PUT http://localhost:8080/api/v1/notes/projet/simple.md \ + -H "Content-Type: text/markdown" \ + -d "# Simple note + +Contenu simple sans front matter." + +# Upload depuis un fichier +curl -X PUT http://localhost:8080/api/v1/notes/projet/from-file.md \ + -H "Content-Type: text/markdown" \ + --data-binary @local-file.md +``` + +--- + +### Supprimer une note + +Supprime définitivement une note. + +**Endpoint** : `DELETE /api/v1/notes/{path}` + +**Paramètres** : +- `{path}` : Chemin relatif de la note (ex: `projet/old-note.md`) + +**Réponse** : `200 OK` + +```json +{ + "message": "Note deleted successfully", + "path": "projet/old-note.md" +} +``` + +**Erreurs** : +- `404 Not Found` : Note inexistante +- `500 Internal Server Error` : Erreur de suppression + +**Exemple curl** : + +```bash +curl -X DELETE http://localhost:8080/api/v1/notes/projet/old-note.md +``` + +--- + +## Codes de statut HTTP + +| Code | Signification | Description | +|------|---------------|-------------| +| `200` | OK | Requête réussie | +| `201` | Created | Note créée avec succès | +| `400` | Bad Request | Requête invalide (chemin, JSON, etc.) | +| `404` | Not Found | Note inexistante | +| `405` | Method Not Allowed | Méthode HTTP non supportée | +| `415` | Unsupported Media Type | Content-Type non supporté | +| `500` | Internal Server Error | Erreur serveur | + +--- + +## Exemples d'utilisation + +### Synchronisation de notes + +**Télécharger toutes les notes** : + +```bash +#!/bin/bash +# Télécharger toutes les notes localement + +mkdir -p notes-backup + +# Récupérer la liste +curl -s http://localhost:8080/api/v1/notes | jq -r '.notes[].path' | while read path; do + # Créer les dossiers parents + mkdir -p "notes-backup/$(dirname "$path")" + + # Télécharger la note + curl -s http://localhost:8080/api/v1/notes/"$path" \ + -H "Accept: text/markdown" \ + -o "notes-backup/$path" + + echo "✓ Downloaded: $path" +done + +echo "Backup terminé!" +``` + +**Uploader un dossier de notes** : + +```bash +#!/bin/bash +# Uploader toutes les notes .md d'un dossier + +find ./my-notes -name "*.md" | while read file; do + # Calculer le chemin relatif + relpath="${file#./my-notes/}" + + # Upload + curl -X PUT http://localhost:8080/api/v1/notes/"$relpath" \ + -H "Content-Type: text/markdown" \ + --data-binary @"$file" + + echo "✓ Uploaded: $relpath" +done + +echo "Upload terminé!" +``` + +### Recherche et filtrage + +**Lister toutes les notes avec un tag spécifique** : + +```bash +curl -s http://localhost:8080/api/v1/notes | \ + jq '.notes[] | select(.tags | contains(["projet"])) | .path' +``` + +**Compter les notes par dossier** : + +```bash +curl -s http://localhost:8080/api/v1/notes | \ + jq -r '.notes[].path' | \ + xargs -n1 dirname | \ + sort | uniq -c +``` + +### Création automatique de notes + +**Note quotidienne** : + +```bash +#!/bin/bash +# Créer une note quotidienne + +TODAY=$(date +%Y-%m-%d) +TITLE="Notes du $(date +%d/%m/%Y)" + +curl -X PUT http://localhost:8080/api/v1/notes/daily/${TODAY}.md \ + -H "Content-Type: application/json" \ + -d "{ + \"body\": \"\\n# ${TITLE}\\n\\n## Tasks\\n- [ ] TODO\\n\\n## Notes\\n\", + \"frontMatter\": { + \"title\": \"${TITLE}\", + \"tags\": [\"daily\"] + } + }" + +echo "Note quotidienne créée: daily/${TODAY}.md" +``` + +### Intégration avec d'autres outils + +**Exporter en HTML avec Pandoc** : + +```bash +curl -s http://localhost:8080/api/v1/notes/projet/doc.md \ + -H "Accept: text/markdown" | \ + pandoc -f markdown -t html -o doc.html + +echo "Exporté en HTML: doc.html" +``` + +**Recherche full-text avec jq** : + +```bash +# Chercher "API" dans tous les titres +curl -s http://localhost:8080/api/v1/notes | \ + jq '.notes[] | select(.title | contains("API"))' +``` + +### Script de maintenance + +**Vérifier les notes sans tags** : + +```bash +curl -s http://localhost:8080/api/v1/notes | \ + jq -r '.notes[] | select(.tags | length == 0) | .path' +``` + +**Statistiques** : + +```bash +#!/bin/bash +# Afficher des statistiques sur les notes + +STATS=$(curl -s http://localhost:8080/api/v1/notes) + +TOTAL=$(echo "$STATS" | jq '.total') +TOTAL_SIZE=$(echo "$STATS" | jq '[.notes[].size] | add') +AVG_SIZE=$(echo "$STATS" | jq '[.notes[].size] | add / length | floor') + +echo "📊 Statistiques des notes" +echo "========================" +echo "Total de notes: $TOTAL" +echo "Taille totale: $(numfmt --to=iec-i --suffix=B $TOTAL_SIZE)" +echo "Taille moyenne: $(numfmt --to=iec-i --suffix=B $AVG_SIZE)" +echo "" +echo "Top 5 tags:" +echo "$STATS" | jq -r '.notes[].tags[]' | sort | uniq -c | sort -rn | head -5 +``` + +--- + +## Bonnes pratiques + +### Sécurité + +1. **Ne pas exposer publiquement sans authentification** +2. **Utiliser HTTPS en production** +3. **Valider les chemins côté client** (éviter `../` malveillants) +4. **Limiter la taille des uploads** (si nécessaire, ajouter un middleware) + +### Performance + +1. **Pagination** : L'API liste ne pagine pas actuellement (toutes les notes en une requête). Pour beaucoup de notes (>1000), envisager d'ajouter `?limit=` et `?offset=` +2. **Cache** : Utiliser un proxy cache (Varnish, nginx) pour GET +3. **Compression** : Activer gzip sur le reverse proxy + +### Workflow recommandé + +1. **Backup régulier** : Script cron qui télécharge toutes les notes +2. **Versioning Git** : Synchroniser le dossier de notes avec Git +3. **CI/CD** : Valider le format Markdown avec des linters +4. **Monitoring** : Logger les accès API pour audit + +--- + +## Support et contribution + +- **Issues** : Rapporter les bugs sur GitHub +- **Documentation** : Ce fichier (`API.md`) +- **Code source** : `internal/api/rest_handler.go` + +--- + +## Changelog + +### v1 (2025-11-10) +- ✨ Première version de l'API REST +- ✅ Endpoints: LIST, GET, PUT, DELETE +- ✅ Content negotiation JSON/Markdown +- ✅ Support sous-dossiers +- ✅ Gestion automatique du front matter +- ✅ Ré-indexation automatique + +--- + +**Note** : Cette API coexiste avec l'interface web HTML. Les deux peuvent être utilisées simultanément sans conflit. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..e644fc9 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,421 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +A lightweight web-based Markdown note-taking application with a Go backend and modern JavaScript frontend. Notes are stored as plain Markdown files with YAML front matter containing metadata (title, date, last_modified, tags). The system provides a sophisticated CodeMirror 6 editor with live preview, rich search capabilities, hierarchical organization, and automatic front matter management. + +**Recent Modernization**: The project has been migrated from a simple textarea editor to CodeMirror 6, with a Vite build system for frontend modules. The backend remains unchanged, maintaining the same Go architecture with htmx for dynamic interactions. + +## Architecture + +### Backend (Go) + +Three main packages under `internal/`: +- **indexer**: Maintains an in-memory index mapping tags to note files. Parses YAML front matter from `.md` files to build the index. Thread-safe with RWMutex. +- **watcher**: Uses `fsnotify` to monitor filesystem changes and trigger re-indexing with 200ms debounce. Recursively watches all subdirectories. +- **api**: HTTP handlers that serve templates and handle CRUD operations on notes. Updates front matter automatically on save. + +The server (`cmd/server/main.go`) coordinates these components: +1. Loads initial index from notes directory +2. Starts filesystem watcher for automatic re-indexing +3. Pre-parses HTML templates from `templates/` +4. Serves routes: `/` (main page), `/api/search`, `/api/notes/*`, `/api/tree`, `/api/folders/create`, `/api/files/move` +5. Handles static files from `static/` directory + +### Frontend + +The frontend uses a modern build system with Vite and CodeMirror 6: + +#### Architecture +- **Build System**: Vite compiles frontend modules from `frontend/src/` to `static/dist/` +- **Editor**: CodeMirror 6 with Markdown language support, One Dark theme, and syntax highlighting +- **Templates**: `index.html`, `editor.html`, `file-tree.html`, `search-results.html`, `new-note-prompt.html` +- **HTMX Integration**: Server returns HTML fragments that htmx swaps into the DOM +- **Out-of-band swaps**: Update the file tree after saves/deletes without full page reload + +#### Frontend Source Structure +``` +frontend/src/ +├── main.js # Entry point - imports all modules +├── editor.js # CodeMirror 6 editor implementation with slash commands +├── file-tree.js # Drag-and-drop file organization +└── ui.js # Sidebar toggle functionality +``` + +#### CodeMirror 6 Editor Features +- **Syntax Highlighting**: Full Markdown language support (`@codemirror/lang-markdown`) +- **Theme**: One Dark theme (`@codemirror/theme-one-dark`) - VS Code-inspired dark theme +- **Live Preview**: Debounced updates (150ms) synchronized with editor scroll position +- **Auto-Save**: Triggers after 2 seconds of inactivity +- **Keyboard Shortcuts**: + - `Ctrl/Cmd+S` for manual save + - `Tab` for proper indentation + - Full keyboard navigation +- **View Modes**: Toggle between split view, editor-only, and preview-only +- **Slash Commands**: Type `/` to open command palette for quick Markdown insertion +- **Front Matter Handling**: Automatically strips YAML front matter in preview + +#### File Tree Features +- **Folder Management**: Expand/collapse folders with visual indicators (📁/📂) +- **Drag & Drop**: Move files between folders with visual feedback +- **Folder Creation**: Modal-based creation supporting nested paths +- **Safe Validation**: Prevents dangerous path operations + +#### Rendering Pipeline +- **marked.js**: Markdown to HTML conversion +- **DOMPurify**: HTML sanitization to prevent XSS attacks +- **Highlight.js**: Syntax highlighting for code blocks in preview +- **Custom Theme**: Material Darker theme in `static/theme.css` with CSS custom properties + +### Note Format + +Notes have YAML front matter with these fields: +```yaml +--- +title: "Note Title" +date: "08-11-2025" +last_modified: "08-11-2025:13:02" +tags: [tag1, tag2] +--- +``` + +The `indexer` package handles both single-value and array-format tags via custom `UnmarshalYAML`. + +## Development Commands + +### Building the Frontend + +**IMPORTANT**: The frontend must be built before running the application. The compiled JavaScript is required. + +```bash +cd frontend +npm install # Install dependencies (first time only) +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) + +Frontend dependencies (from `frontend/package.json`): +- `@codemirror/basic-setup` - Base editor functionality +- `@codemirror/lang-markdown` - Markdown language support +- `@codemirror/state` - Editor state management +- `@codemirror/view` - Editor view layer +- `@codemirror/theme-one-dark` - Dark theme +- `vite` - Build tool + +### Running the Server + +```bash +go run ./cmd/server +``` + +Server starts on `http://localhost:8080`. Use flags to customize: +- `-addr :PORT` - Change server address (default: `:8080`) +- `-notes-dir PATH` - Change notes directory (default: `./notes`) + +### Testing + +Run all tests: +```bash +go test ./... +``` + +Run specific package tests: +```bash +go test ./internal/indexer +go test ./internal/api +``` + +Run tests with verbose output: +```bash +go test -v ./... +``` + +### Building the Server Binary + +```bash +go build ./cmd/server +``` + +### Backend Dependencies + +Update Go dependencies: +```bash +go mod tidy +``` + +Key backend dependencies: +- `github.com/fsnotify/fsnotify` - Filesystem watcher +- `gopkg.in/yaml.v3` - YAML parsing for front matter + +### Vite Build System + +The frontend uses Vite (`frontend/vite.config.js`) for bundling JavaScript modules: + +**Configuration**: +- **Entry Point**: `frontend/src/main.js` (imports editor.js, file-tree.js, ui.js) +- **Output Directory**: `static/dist/` (served by Go server) +- **Library Mode**: Builds as a library with both ES and UMD formats +- **Single Bundle**: No code splitting - all dependencies bundled together +- **Aliases**: Path aliases for `@codemirror/state` and `@codemirror/view` to ensure consistent versions + +**Build Process**: +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 +4. Outputs to `static/dist/` where Go server can serve them +5. Templates load the ES module version via ` + +==> + +Document(Element(OpenTag(StartTag,TagName,Attribute(AttributeName,Is,AttributeValue),EndTag), + ScriptText, +CloseTag(StartCloseTag,TagName,EndTag))) + +# Does parse type-less script tags as JS + + + +==> + +Document(Element(OpenTag(StartTag,TagName,EndTag), + Script(ExpressionStatement(RegExp)), +CloseTag(StartCloseTag,TagName,EndTag))) + +# Still doesn't end script tags on closing tags + + + +==> + +Document(Element(OpenTag(StartTag,TagName,Attribute(AttributeName,Is,UnquotedAttributeValue),EndTag), + ScriptText, +CloseTag(StartCloseTag,TagName,EndTag))) + +# Missing end tag + + + +==> + +Document(Element(OpenTag(StartTag,TagName,Attribute(AttributeName,Is,AttributeValue),EndTag), + Script(...), +CloseTag(StartCloseTag,TagName,EndTag))) + +# JS with unquoted script type + + + +==> + +Document(Element(OpenTag(StartTag,TagName,Attribute(AttributeName,Is,UnquotedAttributeValue),EndTag), + Script(...), +CloseTag(StartCloseTag,TagName,EndTag))) + +# Error in JS + + + +==> + +Document(Element(OpenTag(StartTag,TagName,EndTag), + Script(...), +CloseTag(StartCloseTag,TagName,EndTag))) diff --git a/frontend/node_modules/@lezer/html/test/tags.txt b/frontend/node_modules/@lezer/html/test/tags.txt new file mode 100644 index 0000000..8484ab8 --- /dev/null +++ b/frontend/node_modules/@lezer/html/test/tags.txt @@ -0,0 +1,370 @@ +# Regular tag + +bar + +==> + +Document(Element(OpenTag(StartTag,TagName,EndTag),Text,CloseTag(StartCloseTag,TagName,EndTag))) + +# Nested tag + +c
+ +==> + +Document(Element(OpenTag(StartTag,TagName,EndTag), + Element(OpenTag(StartTag,TagName,EndTag),Text,CloseTag(StartCloseTag,TagName,EndTag)), + Element(SelfClosingTag(StartTag,TagName,EndTag)), +CloseTag(StartCloseTag,TagName,EndTag))) + +# Attribute + +
+ +==> + +Document(Element(SelfClosingTag(StartTag,TagName,Attribute(AttributeName,Is,AttributeValue),EndTag))) + +# Multiple attributes + + + +==> + +Document(Element(OpenTag(StartTag,TagName, + Attribute(AttributeName,Is,AttributeValue), + Attribute(AttributeName,Is,AttributeValue), + Attribute(AttributeName,Is,AttributeValue),EndTag), +CloseTag(StartCloseTag,TagName,EndTag))) + +# Value-less attributes + + + +==> + +Document(Element(OpenTag(StartTag,TagName, + Attribute(AttributeName), + Attribute(AttributeName,Is,AttributeValue), + Attribute(AttributeName),EndTag), +CloseTag(StartCloseTag,TagName,EndTag))) + +# Unquoted attributes + + + +==> + +Document(Element(OpenTag(StartTag,TagName, + Attribute(AttributeName,Is,UnquotedAttributeValue), + Attribute(AttributeName), + Attribute(AttributeName,Is,UnquotedAttributeValue),EndTag), +CloseTag(StartCloseTag,TagName,EndTag))) + +# Unquoted attributes with slashes + + + +==> + +Document(Element(SelfClosingTag(StartTag,TagName, + Attribute(AttributeName,Is,UnquotedAttributeValue), + Attribute(AttributeName,Is,UnquotedAttributeValue), + Attribute(AttributeName,Is,UnquotedAttributeValue), + Attribute(AttributeName,Is,UnquotedAttributeValue), +EndTag))) + +# Single-quoted attributes + + + +==> + +Document(Element(SelfClosingTag(StartTag, TagName, + Attribute(AttributeName, Is, AttributeValue), + Attribute(AttributeName, Is, AttributeValue(EntityReference)), +EndTag))) + +# Entities + +&C + +==> + +Document(Element(OpenTag(StartTag,TagName, + Attribute(AttributeName,Is,AttributeValue(EntityReference)),EndTag), + EntityReference,CharacterReference, +CloseTag(StartCloseTag,TagName,EndTag))) + +# Doctype + + + + +==> + +Document(DoctypeDecl,Text,Element(OpenTag(StartTag,TagName,EndTag),CloseTag(StartCloseTag,TagName,EndTag))) + +# Processing instructions + + + +==> + +Document(ProcessingInst,Element(OpenTag(StartTag,TagName,EndTag),ProcessingInst,CloseTag(StartCloseTag,TagName,EndTag))) + +# Comments + + + text + + + +==> + +Document(Comment,Text,Element(OpenTag(StartTag,TagName,EndTag),Comment,Text,CloseTag(StartCloseTag,TagName,EndTag)),Text,Comment,Text,Comment) + +# Mismatched tag + + + +==> + +Document(Element(OpenTag(StartTag,TagName,EndTag),MismatchedCloseTag(StartCloseTag,TagName,EndTag))) + +# Unclosed tag + + + +==> + +Document(Element(OpenTag(StartTag,TagName,EndTag))) + +# Ignore pseudo-xml self-closers + +
+ +==> + +Document(Element(SelfClosingTag(StartTag,TagName,EndTag))) + +# Unclosed implicitly closed tag + +

+ +==> + +Document(Element(OpenTag(StartTag,TagName,EndTag))) + +# Nested mismatched tag + + + +==> + +Document(Element(OpenTag(StartTag,TagName,EndTag), + Element(OpenTag(StartTag,TagName,EndTag), + Element(OpenTag(StartTag,TagName,EndTag),CloseTag(StartCloseTag,TagName,EndTag)), + MismatchedCloseTag(StartCloseTag,TagName,EndTag), + ⚠), + CloseTag(StartCloseTag,TagName,EndTag))) + +# Incomplete close tag + + + +==> + +Document(Element(OpenTag(StartTag,TagName,EndTag), + Element(OpenTag(StartTag,TagName,EndTag), IncompleteCloseTag, ⚠), + CloseTag(StartCloseTag,TagName,EndTag))) + +# Re-synchronize close tags + + + +==> + +Document(Element(OpenTag(StartTag,TagName,EndTag), + Element(OpenTag(StartTag,TagName,EndTag), + Element(OpenTag(StartTag,TagName,EndTag), + MismatchedCloseTag(StartCloseTag,TagName,EndTag), + CloseTag(StartCloseTag,TagName,EndTag)), + ⚠), + CloseTag(StartCloseTag,TagName,EndTag))) + +# Top-level mismatched close tag + + + +==> + +Document( + Element(OpenTag(StartTag,TagName,EndTag),CloseTag(StartCloseTag,TagName,EndTag)), + MismatchedCloseTag(StartCloseTag,TagName,EndTag)) + +# Self-closing tags + + + +==> + +Document(Element(OpenTag(StartTag,TagName,EndTag), + Element(SelfClosingTag(StartTag,TagName,Attribute(AttributeName,Is,UnquotedAttributeValue),EndTag)), +CloseTag(StartCloseTag,TagName,EndTag))) + +# Implicitly closed + +

Hello
+ +==> + +Document(Element(OpenTag(StartTag,TagName,EndTag), + Element(OpenTag(StartTag,TagName,EndTag),Text), +CloseTag(StartCloseTag,TagName,EndTag))) + +# Closed by sibling + +
+

Foo +

Bar +

+ +==> + +Document(Element(OpenTag(StartTag,TagName,EndTag), + Text, + Element(OpenTag(StartTag,TagName,EndTag),Text), + Element(OpenTag(StartTag,TagName,EndTag),Text), +CloseTag(StartCloseTag,TagName,EndTag))) + +# Closed by sibling at top + +

Foo +

Bar + +==> + +Document(Element(OpenTag(StartTag,TagName,EndTag),Text),Element(OpenTag(StartTag,TagName,EndTag),Text)) + +# Textarea + +

Enter something: + +==> + +Document(Element(OpenTag(StartTag,TagName,EndTag), + Text, + Element(OpenTag(StartTag,TagName,Attribute(AttributeName,Is,UnquotedAttributeValue),EndTag), + TextareaText, + CloseTag(StartCloseTag,TagName,EndTag)))) + +# Script + + + +==> + +Document(Element(OpenTag(StartTag,TagName,EndTag),ScriptText,CloseTag(StartCloseTag,TagName,EndTag))) + +# Doesn't get confused by a stray ampersand + +a&b + +==> + +Document(Element(OpenTag(StartTag,TagName,EndTag),Text,InvalidEntity,Text,CloseTag(StartCloseTag,TagName,EndTag))) + +# Can ignore mismatches {"dialect": "noMatch"} + +

foo

+ +==> + +Document(Element(OpenTag(StartTag,TagName,EndTag),Text,CloseTag(StartCloseTag,TagName,EndTag))) + +# Can handle lone close tags {"dialect": "noMatch"} + + + +==> + +Document(CloseTag(StartCloseTag,TagName,EndTag)) + +# Parses ampersands in attributes + + + +==> + +Document(Element(SelfClosingTag(StartTag, TagName, Attribute(AttributeName, Is, AttributeValue(InvalidEntity)), EndTag))) + +# Supports self-closing dialect {"dialect": "selfClosing"} + +
+ +==> + +Document(Element( + OpenTag(StartTag,TagName,EndTag), + Element(SelfClosingTag(StartTag,TagName,Attribute(AttributeName,Is,UnquotedAttributeValue),SelfClosingEndTag)), + CloseTag(StartCloseTag,TagName,EndTag))) + +# Allows self-closing in foreign elements + +
+ +==> + +Document(Element(OpenTag(StartTag,TagName,EndTag), + Element(OpenTag(StartTag,TagName,EndTag), + Element(SelfClosingTag(StartTag,TagName,SelfClosingEndTag)), + CloseTag(StartCloseTag,TagName,EndTag)), + CloseTag(StartCloseTag,TagName,EndTag))) + +# Parses multiple unfinished tags in a row + +
+ +Document(Element(OpenTag(StartTag,TagName,⚠), + Element(OpenTag(StartTag,TagName,⚠), + Element(OpenTag(StartTag,TagName,⚠),⚠),⚠),⚠)) + +# Allows self-closing on special tags {"dialect": "selfClosing"} + + +
+ +
+
+
+
+ {{if not .IsHome}} +
+
+ + +
+ +
+ {{end}} + + \ No newline at end of file diff --git a/templates/file-tree.html b/templates/file-tree.html new file mode 100644 index 0000000..e666863 --- /dev/null +++ b/templates/file-tree.html @@ -0,0 +1,39 @@ +{{if .Tree}} +{{if .Tree.Children}} + {{template "tree-node" .Tree}} +{{else}} +

Aucune note trouvée.

+{{end}} +{{else}} +

Aucune note trouvée.

+{{end}} + +{{define "tree-node"}} + {{range .Children}} + {{if .IsDir}} +
+
+ + 📁 + {{.Name}} +
+ +
+ {{else}} + + 📄 {{.Name}} + + {{end}} + {{end}} +{{end}} \ No newline at end of file diff --git a/templates/home.html b/templates/home.html new file mode 100644 index 0000000..237c377 --- /dev/null +++ b/templates/home.html @@ -0,0 +1,71 @@ +
+
+

+ 📝 Project Notes +

+

+ Bienvenue dans votre espace de notes Markdown +

+ +
+ +
+

+ 🚀 Démarrage rapide +

+
+
+

📁 Parcourir

+

+ Explorez vos notes dans l'arborescence à gauche +

+
+
+

🔍 Rechercher

+

+ Utilisez la barre de recherche en haut pour trouver vos notes +

+
+
+

⚡ Slash commands

+

+ Tapez / dans l'éditeur pour insérer du Markdown +

+
+
+
+ +
+

+ ✨ Fonctionnalités +

+
    +
  • + Éditeur Markdown avec coloration syntaxique (CodeMirror) +
  • +
  • + Prévisualisation en temps réel avec scroll synchronisé +
  • +
  • + Organisation par dossiers avec arborescence dynamique +
  • +
  • + Recherche avancée par tag, titre ou contenu +
  • +
  • + Thème Material Darker pour vos yeux +
  • +
  • + Sauvegarde automatique avec Ctrl/Cmd+S +
  • +
+
+ +
+

+ 💡 Astuce : Cliquez sur une note dans l'arborescence pour commencer à éditer +

+
+
\ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..a4aa53b --- /dev/null +++ b/templates/index.html @@ -0,0 +1,186 @@ + + + + + + Project Notes + + + + + + + + + + + +
+ +

📝 Project Notes

+ + + +
+ +
+
+ + + + + + + + + + +
+ +
+
+
+ + + +

Chargement...

+
+
+
+
+ + + + \ No newline at end of file diff --git a/templates/new-note-prompt.html b/templates/new-note-prompt.html new file mode 100644 index 0000000..2091e6f --- /dev/null +++ b/templates/new-note-prompt.html @@ -0,0 +1,6 @@ +
+ + + + +
diff --git a/templates/search-results.html b/templates/search-results.html new file mode 100644 index 0000000..e78bdb9 --- /dev/null +++ b/templates/search-results.html @@ -0,0 +1,66 @@ +{{if .Query}} + {{if .Results}} +
+ {{len .Results}} résultat{{if gt (len .Results) 1}}s{{end}} +
+ + {{else}} +
+
🔍
+

Aucun résultat pour « {{.Query}} »

+

Essayez d'autres mots-clés ou utilisez les filtres

+
+ {{end}} +{{else}} +
+

💡 Recherche avancée

+

Saisissez des mots-clés pour rechercher dans vos notes

+
+
+ tag:projet + Rechercher par tag +
+
+ title:réunion + Rechercher dans les titres +
+
+ path:meetings + Rechercher dans les chemins +
+
+
+{{end}}