# 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. **Key Features**: - **Daily Notes**: Quick daily journaling with interactive calendar, keyboard shortcuts (Ctrl/Cmd+D), and structured templates - **Favorites System**: Star important notes and folders for quick access from the sidebar - **Note Linking**: Create links between notes with `/link` command and fuzzy search modal - **Vim Mode**: Full Vim keybindings support (hjkl navigation, modes, commands) for power users - **Multiple Themes**: 8 dark themes (Material Dark, Monokai, Dracula, One Dark, Solarized, Nord, Catppuccin, Everforest) - **Font Customization**: 8 fonts (JetBrains Mono, Fira Code, Inter, etc.) with 4 size options - **Keyboard Shortcuts**: 10+ global shortcuts for navigation, editing, and productivity **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) Four 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. - `handler.go` - Main HTML endpoints for the web interface - `rest_handler.go` - REST API endpoints (v1) - `daily_notes.go` - Daily note creation and calendar functionality - `favorites.go` - Favorites management (star/unstar notes and folders) 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/v1/notes` and `/api/v1/notes/*` (REST API - JSON responses) - `/api/search` (HTML search results) - `/api/notes/*` (HTML editor for notes) - `/api/tree` (HTML file tree) - `/api/folders/create` (Folder management) - `/api/files/move` (File/folder moving) - `/api/home` (Home page) - `/api/daily-notes/*` (Daily note creation and calendar) - `/api/favorites/*` (Favorites management) 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 ├── vim-mode-manager.js # Vim mode integration for CodeMirror ├── search.js # Search modal with Ctrl/Cmd+K keyboard shortcut ├── link-inserter.js # Note linking modal for /link command ├── file-tree.js # Drag-and-drop file organization ├── favorites.js # Favorites system (star/unstar functionality) ├── daily-notes.js # Daily notes creation and calendar widget ├── keyboard-shortcuts.js # Global keyboard shortcuts management ├── theme-manager.js # Theme switching and persistence ├── font-manager.js # Font selection and size management └── 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 - **Vim Mode**: Optional full Vim keybindings (`@replit/codemirror-vim`) with hjkl navigation, modes, and commands - **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 - `Ctrl/Cmd+D` for daily notes - `Ctrl/Cmd+K` for search - `Ctrl/Cmd+B` for sidebar toggle - `Tab` for proper indentation - Full keyboard navigation (see docs/KEYBOARD_SHORTCUTS.md) - **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 - **Favorites**: Star notes and folders for quick access (★ icon in sidebar) #### 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 Themes**: 8 dark themes in `static/theme.css` with CSS custom properties - Material Dark (default) - Monokai - Dracula - One Dark - Solarized Dark - Nord - Catppuccin - Everforest ### HTMX + JavaScript Coordination (Optimized Architecture) **Key Principle**: HTMX handles ALL server interactions and DOM updates. JavaScript handles UI enhancements (editor, drag-and-drop, animations). #### Architecture Flow ``` User Interaction → HTMX (AJAX) → Go Server (HTML) → HTMX (DOM update) → JS Events (enhancements) ``` #### Best Practices **1. Use `htmx.ajax()` for JavaScript-initiated requests:** ```javascript // ✅ Good: Let HTMX handle the request and DOM updates htmx.ajax('POST', '/api/files/move', { values: { source, destination }, swap: 'none' // Server uses hx-swap-oob }); // ❌ Bad: Manual fetch + DOM manipulation const response = await fetch('/api/files/move', {...}); const html = await response.text(); target.innerHTML = html; htmx.process(target); // This is now unnecessary ``` **2. Listen to HTMX events instead of DOM observers:** ```javascript // ✅ Good: React to HTMX swaps document.body.addEventListener('htmx:afterSwap', (event) => { if (event.detail.target.id === 'file-tree') { updateDraggableAttributes(); } }); document.body.addEventListener('htmx:oobAfterSwap', (event) => { if (event.detail.target.id === 'file-tree') { updateDraggableAttributes(); } }); // ❌ Bad: MutationObserver (performance overhead) const observer = new MutationObserver(() => {...}); observer.observe(element, { childList: true, subtree: true }); ``` **3. Let HTMX process out-of-band swaps automatically:** The server returns HTML with `hx-swap-oob` attributes. HTMX automatically finds these elements and swaps them, even when they're not the primary target. ```go // Server response (Go template)
``` HTMX automatically: - Finds the element with `id="file-tree"` - Replaces its innerHTML - Processes any HTMX attributes in the new content - Triggers `htmx:oobAfterSwap` event **4. Event-driven architecture:** ```javascript // File tree initialization (file-tree.js) class FileTree { constructor() { this.init(); } init() { // Use event delegation on stable parent const sidebar = document.getElementById('sidebar'); // Click handlers (delegated) sidebar.addEventListener('click', (e) => { const folderHeader = e.target.closest('.folder-header'); if (folderHeader) this.toggleFolder(folderHeader); }); // Drag-and-drop handlers (delegated) sidebar.addEventListener('dragstart', (e) => {...}); sidebar.addEventListener('drop', (e) => {...}); // React to HTMX updates document.body.addEventListener('htmx:oobAfterSwap', (event) => { if (event.detail.target.id === 'file-tree') { this.updateDraggableAttributes(); } }); } } ``` #### Implementation Examples **Creating a folder (file-tree.js:438-476)**: ```javascript htmx.ajax('POST', '/api/folders/create', { values: { path: folderName }, swap: 'none' // Server handles oob swap }).then(() => { hideNewFolderModal(); }).catch(() => { alert('Erreur lors de la création du dossier'); }); ``` **Moving a file (file-tree.js:338-362)**: ```javascript htmx.ajax('POST', '/api/files/move', { values: { source: sourcePath, destination: destinationPath }, swap: 'none' // Server handles oob swap }).then(() => { console.log('File moved successfully'); }); ``` #### Benefits of This Architecture 1. **Less Code**: ~60 lines removed by eliminating manual DOM manipulation 2. **Better Performance**: HTMX events instead of MutationObserver 3. **Consistency**: All server interactions use the same pattern 4. **Maintainability**: Clear separation between HTMX (data) and JavaScript (UI enhancements) 5. **Reliability**: HTMX handles edge cases (race conditions, partial updates, etc.) #### When to Use What **Use HTMX for:** - Loading content from server - Form submissions - Search/filtering - File operations (move, delete, create) - Automatic DOM updates **Use JavaScript for:** - CodeMirror editor initialization - Drag-and-drop UI logic - Slash command palette - Scroll synchronization - View mode toggles - Client-side animations **Never:** - Parse HTML manually from fetch() responses - Call `htmx.process()` manually (HTMX does it automatically) - Use MutationObserver when HTMX events are available - Mix fetch() and htmx.ajax() for similar operations ### Daily Notes **Implementation**: `internal/api/daily_notes.go` and `frontend/src/daily-notes.js` Daily notes provide a quick journaling feature: - **Keyboard Shortcut**: `Ctrl/Cmd+D` creates or opens today's note - **Calendar Widget**: Interactive monthly calendar showing all daily notes - **Template System**: Uses `daily-note-template.md` if present in notes directory - **Auto-naming**: Creates notes as `daily/YYYY-MM-DD.md` by default - **Visual Indicators**: Calendar highlights days with existing notes - **One-click Access**: Click any calendar date to open or create that day's note The calendar is implemented using htmx for dynamic month navigation and rendering. ### Favorites System **Implementation**: `internal/api/favorites.go` and `frontend/src/favorites.js` The favorites system allows quick access to frequently used notes and folders: - **Star Icon**: Click ★ next to any note or folder in the file tree - **Persistence**: Favorites stored in `.favorites.json` in the notes directory - **Quick Access**: Starred items appear at the top of the sidebar - **Folder Support**: Star entire folders to quickly access project areas - **Visual Feedback**: Filled star (★) for favorites, empty star (☆) for non-favorites Favorites are loaded on server startup and updated in real-time via htmx. ### 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/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 - `@codemirror/lang-markdown` (^6.5.0) - Markdown language support - `@codemirror/state` (^6.5.2) - Editor state management - `@codemirror/view` (^6.38.6) - Editor view layer - `@codemirror/theme-one-dark` (^6.1.3) - Dark theme - `@replit/codemirror-vim` (^6.2.2) - Vim mode integration - `vite` (^7.2.2) - 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 (`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 `