842 lines
34 KiB
Markdown
842 lines
34 KiB
Markdown
# 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)
|
|
<div id="file-tree" hx-swap-oob="innerHTML">
|
|
<!-- Updated file tree HTML -->
|
|
</div>
|
|
```
|
|
|
|
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/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` (^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 (`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 `<script type="module">`
|
|
|
|
**Development Workflow**:
|
|
```bash
|
|
cd frontend
|
|
npm run build # Build for production
|
|
npm run build -- --watch # Watch mode for development
|
|
```
|
|
|
|
The Go server does not need to be restarted after frontend builds - simply refresh the browser.
|
|
|
|
## Key Implementation Details
|
|
|
|
### Front Matter Management
|
|
|
|
The system automatically manages front matter when saving notes:
|
|
- `title`: Auto-generated from filename if missing (converts hyphens to spaces, title-cases)
|
|
- `date`: Set to creation date for new files, preserved for existing files
|
|
- `last_modified`: Always updated to current timestamp on save (format: `DD-MM-YYYY:HH:MM`)
|
|
- `tags`: Preserved from user input; defaults to `["default"]` for new notes
|
|
|
|
Front matter extraction happens in two places:
|
|
- `indexer.ExtractFrontMatterAndBody()` - For file paths
|
|
- `indexer.ExtractFrontMatterAndBodyFromReader()` - For `io.Reader` (used when parsing form content)
|
|
|
|
### Indexing and Search
|
|
|
|
The indexer maintains two data structures:
|
|
- `tags map[string][]string` - Maps tags to file paths for tag-based lookup
|
|
- `docs map[string]*Document` - Stores full document metadata for rich search
|
|
|
|
Re-indexing happens:
|
|
- On server startup
|
|
- When files are created/modified/deleted (via watcher with 200ms debounce)
|
|
- After POST/DELETE/MOVE operations (background goroutine)
|
|
|
|
Rich search supports multiple query formats:
|
|
- General terms: Search across title, tags, path, and body (AND logic)
|
|
- `tag:value` - Filter by specific tag (case-insensitive)
|
|
- `title:value` - Filter by title content
|
|
- `path:value` - Filter by file path
|
|
- Quoted phrases: `"exact phrase"` preserves spaces
|
|
- Results are scored and ranked by relevance (title matches score highest)
|
|
|
|
### REST API (v1)
|
|
|
|
The application includes a full REST API for programmatic access:
|
|
|
|
**Implementation**: `internal/api/rest_handler.go`
|
|
|
|
**Endpoints**:
|
|
- `GET /api/v1/notes` - List all notes with metadata (JSON)
|
|
- `GET /api/v1/notes/{path}` - Get a specific note (JSON or Markdown based on Accept header)
|
|
- `PUT /api/v1/notes/{path}` - Create or update a note (accepts JSON or raw Markdown)
|
|
- `DELETE /api/v1/notes/{path}` - Delete a note
|
|
|
|
**Content Negotiation**:
|
|
- `Accept: application/json` → Returns JSON with full metadata
|
|
- `Accept: text/markdown` → Returns raw Markdown content
|
|
- `Content-Type: application/json` → Accepts structured JSON request
|
|
- `Content-Type: text/markdown` → Accepts raw Markdown body
|
|
|
|
**Key Features**:
|
|
- Automatic front matter generation for new notes
|
|
- Front matter update (last_modified) on PUT operations
|
|
- Background re-indexing after modifications
|
|
- Path validation (same security as HTML endpoints)
|
|
- Supports nested folders (creates parent directories automatically)
|
|
|
|
**Documentation**: See `API.md` for full REST API documentation with examples.
|
|
|
|
### Search Modal
|
|
|
|
A modern command-palette style search modal is available:
|
|
|
|
**Implementation**: `frontend/src/search.js`
|
|
|
|
**Features**:
|
|
- Keyboard shortcut: `Ctrl/Cmd+K` to open anywhere
|
|
- Real-time search with 300ms debounce
|
|
- Keyboard navigation: `↑`/`↓` to navigate, `Enter` to open, `Esc` to close
|
|
- Highlighting of search terms in results
|
|
- Uses existing search API (`/api/search`)
|
|
- Displays results with icons, titles, paths, snippets, tags, and dates
|
|
|
|
**Styling**: Custom styles in `static/theme.css` with Material Darker theme integration.
|
|
|
|
### Theme and Font Customization
|
|
|
|
**Implementation**: `frontend/src/theme-manager.js` and `frontend/src/font-manager.js`
|
|
|
|
The application supports extensive UI customization:
|
|
|
|
#### Themes
|
|
8 dark themes available via Settings (⚙️ icon):
|
|
- **Material Dark** (default) - Material Design inspired
|
|
- **Monokai** - Classic Monokai colors
|
|
- **Dracula** - Popular purple-tinted theme
|
|
- **One Dark** - Atom/VS Code inspired
|
|
- **Solarized Dark** - Precision colors by Ethan Schoonover
|
|
- **Nord** - Arctic, north-bluish color palette
|
|
- **Catppuccin** - Soothing pastel theme
|
|
- **Everforest** - Comfortable greenish theme
|
|
|
|
Themes are applied via CSS custom properties and persist in localStorage.
|
|
|
|
#### Fonts
|
|
8 font options with 4 size presets (small, medium, large, extra-large):
|
|
- JetBrains Mono (default)
|
|
- Fira Code
|
|
- Inter
|
|
- IBM Plex Mono
|
|
- Source Code Pro
|
|
- Cascadia Code
|
|
- Roboto Mono
|
|
- Ubuntu Mono
|
|
|
|
Font settings apply to both the editor and preview pane.
|
|
|
|
### Vim Mode
|
|
|
|
**Implementation**: `frontend/src/vim-mode-manager.js` using `@replit/codemirror-vim`
|
|
|
|
Optional Vim keybindings for power users:
|
|
- **Enable/Disable**: Toggle via Settings (⚙️ icon)
|
|
- **Full Vim Support**: hjkl navigation, visual mode, operators, text objects
|
|
- **Mode Indicator**: Shows current Vim mode (Normal/Insert/Visual) in editor
|
|
- **Persistence**: Vim mode preference saved to localStorage
|
|
- **CodeMirror Integration**: Native Vim extension with excellent compatibility
|
|
|
|
Vim mode users get full modal editing while maintaining CodeMirror features like syntax highlighting and auto-save.
|
|
|
|
### Keyboard Shortcuts
|
|
|
|
**Implementation**: `frontend/src/keyboard-shortcuts.js`
|
|
|
|
The application provides 10+ global keyboard shortcuts for efficient navigation:
|
|
|
|
**Essential Shortcuts**:
|
|
- `Ctrl/Cmd+D` - Create or open today's daily note
|
|
- `Ctrl/Cmd+K` - Open search modal
|
|
- `Ctrl/Cmd+S` - Save current note (also triggers auto-save)
|
|
- `Ctrl/Cmd+B` - Toggle sidebar visibility
|
|
- `Ctrl/Cmd+/` - Show keyboard shortcuts help modal
|
|
|
|
**Editor Shortcuts**:
|
|
- `Tab` - Indent (when in editor)
|
|
- `Shift+Tab` - Outdent (when in editor)
|
|
- `Ctrl/Cmd+Enter` - Save note (alternative to Cmd+S)
|
|
|
|
**Navigation**:
|
|
- `↑`/`↓` - Navigate search results or command palette
|
|
- `Enter` - Select/confirm action
|
|
- `Esc` - Close modals, cancel actions, clear search
|
|
|
|
All shortcuts are non-blocking and work across the application. The shortcuts help modal (triggered by `Ctrl/Cmd+/`) provides a quick reference guide.
|
|
|
|
For complete documentation, see `docs/KEYBOARD_SHORTCUTS.md`.
|
|
|
|
### Security Considerations
|
|
|
|
File path validation in `handler.go` and `rest_handler.go`:
|
|
- `filepath.Clean()` to normalize paths
|
|
- Reject paths starting with `..` or absolute paths (directory traversal prevention)
|
|
- Enforce `.md` extension for notes
|
|
- Use `filepath.Join()` to construct safe paths within notes directory
|
|
- DOMPurify sanitizes Markdown-rendered HTML to prevent XSS attacks
|
|
|
|
**REST API Security**:
|
|
- No authentication currently implemented
|
|
- Recommend using reverse proxy (nginx, Caddy) with auth for public exposure
|
|
- CORS not configured (same-origin only)
|
|
- No rate limiting (add middleware if needed)
|
|
|
|
### Template System
|
|
|
|
Templates are pre-parsed at startup. The API handler returns HTML fragments that htmx inserts into the page. Out-of-band swaps update the file tree sidebar without full page reload.
|
|
|
|
### Concurrency
|
|
|
|
- Indexer uses `sync.RWMutex` for thread-safe access (read locks for searches, write locks for re-indexing)
|
|
- Re-indexing after saves/deletes/moves happens in background goroutines to avoid blocking HTTP responses
|
|
- Watcher runs in separate goroutine with proper context cancellation
|
|
- Server shutdown uses 5-second graceful shutdown timeout with context
|
|
|
|
### File Organization
|
|
|
|
The application supports hierarchical note organization:
|
|
- Notes can be organized in subdirectories within the `notes/` directory
|
|
- The file tree displays folders and files in a hierarchical view
|
|
- Folders are displayed before files, both sorted alphabetically (case-insensitive)
|
|
- API endpoints for folder creation (`/api/folders/create`) and file moving (`/api/files/move`)
|
|
- Watcher recursively monitors all subdirectories for changes
|
|
|
|
### CodeMirror 6 Editor Implementation
|
|
|
|
The editor is implemented in `frontend/src/editor.js` with two main classes:
|
|
|
|
#### MarkdownEditor Class
|
|
Manages the CodeMirror 6 instance and its interactions:
|
|
|
|
**Initialization**:
|
|
```javascript
|
|
EditorState.create({
|
|
doc: content,
|
|
extensions: [
|
|
basicSetup, // Line numbers, search, fold gutter
|
|
markdown(), // Markdown syntax support
|
|
oneDark, // One Dark theme
|
|
keymap.of([indentWithTab]),
|
|
EditorView.updateListener.of(...), // Preview & auto-save
|
|
keymap.of([{ key: "Mod-s", run: saveFunction }])
|
|
]
|
|
});
|
|
```
|
|
|
|
**Key Features**:
|
|
- **Live Preview**: Updates preview pane with 150ms debounce on editor changes
|
|
- **Auto-Save**: Triggers form submission after 2 seconds of inactivity
|
|
- **Scroll Sync**: Bidirectional scroll synchronization between editor and preview (50ms debounce)
|
|
- **Front Matter Stripping**: Removes YAML front matter from preview rendering
|
|
- **View Modes**: Three modes (split/editor-only/preview-only) saved to localStorage
|
|
- **Height Management**: Dynamic height calculation (`window.innerHeight - 180px`) with resize handling
|
|
- **Cleanup**: Proper destruction of editor instances to prevent memory leaks
|
|
|
|
**Integration**:
|
|
- Replaces the hidden textarea element
|
|
- HTMX events trigger editor initialization (`htmx:afterSwap`)
|
|
- Form submission uses `requestSubmit()` to trigger HTMX POST
|
|
|
|
#### SlashCommands Class
|
|
Provides command palette for quick Markdown insertion:
|
|
|
|
**Trigger**: Type `/` at the beginning of a line to open the palette
|
|
|
|
**Implementation**:
|
|
- Monitors editor state changes for `/` character
|
|
- Filters commands in real-time as user types
|
|
- Positions palette dynamically using CodeMirror's `coordsAtPos()`
|
|
- Keyboard navigation with arrow keys, Enter/Tab to select
|
|
- Inserts Markdown text using CodeMirror transactions
|
|
- 13 built-in commands with text templates
|
|
|
|
**UI/UX**:
|
|
- Styled palette with gradient selection indicator
|
|
- Smooth animations and transitions
|
|
- Accessible keyboard navigation
|
|
- Auto-closes on selection or Escape key
|
|
|
|
### Slash Commands
|
|
|
|
The editor includes a slash command system integrated with CodeMirror 6:
|
|
- Type `/` at the start of a line to trigger the command palette
|
|
- Available commands (14 total):
|
|
- **Headings**: h1, h2, h3 - Insert Markdown headers
|
|
- **Formatting**: bold, italic, code - Text formatting
|
|
- **Blocks**: codeblock, quote, hr, table - Block-level elements
|
|
- **Lists**: list - Unordered list
|
|
- **Dynamic**: date - Insert current date in French format (DD/MM/YYYY)
|
|
- **Links**:
|
|
- `link` - Insert standard Markdown link `[texte](url)`
|
|
- `ilink` - Open internal note linking modal (see Note Linking below)
|
|
- Navigate with Arrow Up/Down, select with Enter/Tab, cancel with Escape
|
|
- Commands are filtered in real-time as you type after the `/`
|
|
- The palette is positioned dynamically near the cursor using CodeMirror coordinates
|
|
- Implementation in `frontend/src/editor.js` with the `SlashCommands` class
|
|
- Styled command palette with gradient selection indicator
|
|
|
|
### Note Linking
|
|
|
|
**Implementation**: `frontend/src/link-inserter.js`
|
|
|
|
The note linking system allows users to create Markdown links between notes without leaving the editor:
|
|
|
|
**Activation**: Type `/ilink` (internal link) in the editor and select the command from the slash palette
|
|
|
|
**Features**:
|
|
- **Fuzzy Search**: Real-time search across all notes with 200ms debounce
|
|
- **Keyboard Navigation**: Navigate with ↑/↓, select with Enter, cancel with Esc
|
|
- **Search Integration**: Reuses existing `/api/search` endpoint (no new backend code)
|
|
- **Rich Results**: Shows note title, path, tags, and metadata
|
|
- **Instant Insertion**: Inserts `[Note Title](path/to/note.md)` format at cursor position
|
|
|
|
**Architecture**:
|
|
- `LinkInserter` class manages the search modal and selection
|
|
- Opens via `SlashCommands.openLinkInserter()` when `/ilink` is triggered
|
|
- Uses HTMX search API for consistency
|
|
- Modal styled to match `SearchModal` design language
|
|
|
|
**Workflow**:
|
|
1. User types `/ilink` → slash palette appears
|
|
2. User selects "ilink" → modal opens with search input
|
|
3. User types search query → fuzzy search filters notes
|
|
4. User selects note (Enter/click) → Markdown link inserted
|
|
5. Modal closes → editor regains focus at end of inserted link
|
|
|
|
**Standard Links**: For external URLs, use `/link` to insert the standard Markdown template `[texte](url)`
|
|
|
|
**Link Format**: Links are inserted as HTML with HTMX attributes:
|
|
```html
|
|
<a href="#" hx-get="/api/notes/path/to/note.md" hx-target="#editor-container" hx-swap="innerHTML">Note Title</a>
|
|
```
|
|
|
|
This format:
|
|
- **Clickable in preview**: Links open the note directly in the editor when clicked
|
|
- **HTMX-powered**: Uses existing HTMX infrastructure (no new backend code)
|
|
- **Inline HTML**: Marked.js renders the HTML as-is, DOMPurify sanitizes it, HTMX processes it
|
|
- **Editable**: Plain HTML in the source, can be manually edited if needed
|
|
|
|
**Note**: This format is specific to this application. For compatibility with other Markdown tools, use standard Markdown links with `/link` command.
|
|
|
|
## Frontend Libraries
|
|
|
|
The application uses a mix of npm packages (for the editor) and CDN-loaded libraries (for utilities):
|
|
|
|
### NPM Packages (Built with Vite)
|
|
Managed in `frontend/package.json`:
|
|
- **@codemirror/basic-setup (^0.20.0)**: Base editor functionality (line numbers, search, etc.)
|
|
- **@codemirror/lang-markdown (^6.5.0)**: Markdown language support and syntax highlighting
|
|
- **@codemirror/state (^6.5.2)**: Editor state management
|
|
- **@codemirror/view (^6.38.6)**: Editor view layer and rendering
|
|
- **@codemirror/theme-one-dark (^6.1.3)**: Dark theme for CodeMirror
|
|
- **@replit/codemirror-vim (^6.2.2)**: Vim mode integration for CodeMirror
|
|
- **vite (^7.2.2)**: Build tool for bundling frontend modules
|
|
|
|
### CDN Libraries
|
|
Loaded in `templates/index.html`:
|
|
- **htmx (1.9.10)**: AJAX interactions and dynamic content loading
|
|
- **marked.js**: Markdown to HTML conversion for preview
|
|
- **DOMPurify**: HTML sanitization to prevent XSS attacks
|
|
- **Highlight.js (11.9.0)**: Syntax highlighting for code blocks in preview with Atom One Dark theme
|
|
|
|
### Styling
|
|
- **8 Dark Themes**: Switchable themes in `static/theme.css`
|
|
- Material Dark, Monokai, Dracula, One Dark, Solarized, Nord, Catppuccin, Everforest
|
|
- **Color System**: CSS custom properties for consistent theming
|
|
- Background colors: `--bg-primary`, `--bg-secondary`, `--bg-tertiary`, `--bg-elevated`
|
|
- Text colors: `--text-primary`, `--text-secondary`, `--text-muted`
|
|
- Accent colors: `--accent-blue`, `--accent-violet`
|
|
- **Font Customization**: 8 font families with 4 size presets
|
|
- **No CSS Framework**: All styles hand-crafted with CSS Grid and Flexbox
|
|
- **Responsive Design**: Adaptive layout for different screen sizes
|
|
- **Custom Scrollbars**: Styled scrollbars matching the current theme
|
|
|
|
### 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)
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
project-notes/
|
|
├── cmd/
|
|
│ └── server/
|
|
│ └── main.go # Server entry point
|
|
├── internal/
|
|
│ ├── api/
|
|
│ │ ├── handler.go # HTTP handlers for CRUD operations
|
|
│ │ ├── rest_handler.go # REST API v1 endpoints
|
|
│ │ ├── daily_notes.go # Daily notes functionality
|
|
│ │ └── favorites.go # Favorites management
|
|
│ ├── indexer/
|
|
│ │ ├── indexer.go # Note indexing and search
|
|
│ │ └── indexer_test.go # Indexer tests
|
|
│ └── watcher/
|
|
│ └── watcher.go # Filesystem watcher with fsnotify
|
|
├── frontend/ # Frontend build system
|
|
│ ├── src/
|
|
│ │ ├── main.js # Entry point - imports all modules
|
|
│ │ ├── editor.js # CodeMirror 6 editor with slash commands
|
|
│ │ ├── vim-mode-manager.js # Vim mode integration
|
|
│ │ ├── search.js # Search modal (Ctrl/Cmd+K)
|
|
│ │ ├── file-tree.js # Drag-and-drop file tree
|
|
│ │ ├── favorites.js # Favorites system
|
|
│ │ ├── daily-notes.js # Daily notes and calendar widget
|
|
│ │ ├── keyboard-shortcuts.js # Global keyboard shortcuts
|
|
│ │ ├── theme-manager.js # Theme switching
|
|
│ │ ├── font-manager.js # Font customization
|
|
│ │ └── ui.js # Sidebar toggle
|
|
│ ├── package.json # NPM dependencies
|
|
│ ├── package-lock.json
|
|
│ └── vite.config.js # Vite build configuration
|
|
├── static/
|
|
│ ├── dist/ # Compiled frontend (generated)
|
|
│ │ ├── project-notes-frontend.es.js
|
|
│ │ └── project-notes-frontend.umd.js
|
|
│ └── theme.css # Material Darker theme
|
|
├── templates/
|
|
│ ├── index.html # Main page layout
|
|
│ ├── editor.html # Editor component
|
|
│ ├── file-tree.html # File tree sidebar
|
|
│ ├── search-results.html # Search results
|
|
│ └── new-note-prompt.html # New note modal
|
|
├── notes/ # Note storage directory
|
|
│ ├── *.md # Markdown files with YAML front matter
|
|
│ ├── daily/ # Daily notes (YYYY-MM-DD.md)
|
|
│ ├── .favorites.json # Favorites list (auto-generated)
|
|
│ └── daily-note-template.md # Optional daily note template
|
|
├── docs/ # Documentation
|
|
│ ├── KEYBOARD_SHORTCUTS.md # Keyboard shortcuts reference
|
|
│ ├── DAILY_NOTES.md # Daily notes guide
|
|
│ ├── USAGE_GUIDE.md # Complete usage guide
|
|
│ └── FREEBSD_BUILD.md # FreeBSD build guide
|
|
├── go.mod # Go dependencies
|
|
├── go.sum
|
|
├── API.md # REST API documentation
|
|
└── CLAUDE.md # This file
|
|
```
|
|
|
|
### Key Files to Edit
|
|
|
|
**Backend Development**:
|
|
- `cmd/server/main.go` - Server initialization and routing
|
|
- `internal/api/handler.go` - Main HTML endpoints and request handling
|
|
- `internal/api/rest_handler.go` - REST API v1 endpoints
|
|
- `internal/api/daily_notes.go` - Daily notes and calendar functionality
|
|
- `internal/api/favorites.go` - Favorites management
|
|
- `internal/indexer/indexer.go` - Search and indexing logic
|
|
- `internal/watcher/watcher.go` - Filesystem monitoring
|
|
|
|
**Frontend Development**:
|
|
- `frontend/src/editor.js` - CodeMirror editor, preview, slash commands
|
|
- `frontend/src/vim-mode-manager.js` - Vim mode integration
|
|
- `frontend/src/search.js` - Search modal functionality
|
|
- `frontend/src/link-inserter.js` - Note linking modal for `/link` command
|
|
- `frontend/src/file-tree.js` - File tree interactions and drag-and-drop
|
|
- `frontend/src/favorites.js` - Favorites system
|
|
- `frontend/src/daily-notes.js` - Daily notes creation and calendar widget
|
|
- `frontend/src/keyboard-shortcuts.js` - Global keyboard shortcuts
|
|
- `frontend/src/theme-manager.js` - Theme switching logic
|
|
- `frontend/src/font-manager.js` - Font customization logic
|
|
- `frontend/src/ui.js` - UI utilities (sidebar toggle)
|
|
- `static/theme.css` - Styling and theming (8 themes)
|
|
- `templates/*.html` - HTML templates (Go template syntax)
|
|
|
|
**Configuration**:
|
|
- `frontend/vite.config.js` - Frontend build configuration
|
|
- `frontend/package.json` - NPM dependencies and scripts
|
|
- `go.mod` - Go dependencies
|
|
|
|
### Important Notes
|
|
|
|
1. **Frontend builds are required**: The application will not work without compiled JavaScript in `static/dist/`
|
|
2. **No hot reload for frontend**: Changes to `frontend/src/` require running `npm run build` and refreshing the browser
|
|
3. **Backend changes**: Require restarting the Go server (`go run ./cmd/server`)
|
|
4. **Template changes**: Require restarting the Go server (templates are pre-parsed at startup)
|
|
5. **CSS changes**: Only require browser refresh (loaded via `<link>` tag)
|
|
6. **Note changes**: Automatically detected by filesystem watcher, trigger re-indexing |