Files
personotes/CLAUDE.md
2025-11-12 17:16:13 +01:00

34 KiB

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:

// ✅ 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:

// ✅ 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.

// 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:

// 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):

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):

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:

---
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.

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

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:

go test ./...

Run specific package tests:

go test ./internal/indexer
go test ./internal/api

Run tests with verbose output:

go test -v ./...

Building the Server Binary

go build ./cmd/server

Backend Dependencies

Update Go dependencies:

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 <script type="module">

Development Workflow:

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)

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:

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:

<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/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

personotes/
├── 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)
│   │   ├── personotes-frontend.es.js
│   │   └── personotes-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