# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview Calendar Plantempus is a professional TypeScript calendar component with offline-first architecture, drag-and-drop functionality, and real-time synchronization capabilities. ## Build & Development Commands ```bash # Build the project (bundles to wwwroot/js/calendar.js) npm run build # Watch mode for development npm run watch # Clean build output npm run clean # Type check only npx tsc --noEmit # Run all tests npm test # Run tests in watch mode npm run test # Run tests once and exit npm run test:run # Run tests with UI npm run test:ui # CSS Development npm run css:build # Build CSS npm run css:watch # Watch and rebuild CSS npm run css:build:prod # Build minified production CSS npm run css:analyze # Analyze CSS metrics ``` ## Architecture ### Core Design Pattern: Dependency Injection with NovaDI The application uses **NovaDI** (@novadi/core) for dependency injection. All managers, services, and repositories are registered in `src/index.ts` and resolved through the DI container. **Key principle**: Never instantiate managers or services directly with `new`. Always use constructor injection and register types in the container. ### Event-Driven Architecture The application uses a **centralized EventBus** (`src/core/EventBus.ts`) built on DOM CustomEvents for all inter-component communication. This is the ONLY way components should communicate. - All event types are defined in `src/constants/CoreEvents.ts` (reduced from 102+ to ~20 core events) - Components emit events via `eventBus.emit(CoreEvents.EVENT_NAME, payload)` - Components subscribe via `eventBus.on(CoreEvents.EVENT_NAME, handler)` - Never call methods directly between managers - always use events ### Manager Hierarchy **CalendarManager** (`src/managers/CalendarManager.ts`) - Top-level coordinator - Manages calendar state (current view, current date) - Orchestrates initialization sequence - Coordinates other managers via EventBus **Key Managers**: - **EventManager** - Event CRUD operations, data loading from repository - **GridManager** - Renders time grid structure - **ViewManager** - Handles view switching (day/week/month) - **NavigationManager** - Date navigation and period calculations - **DragDropManager** - Advanced drag-and-drop with smooth animations, type conversion (timed ↔ all-day), scroll compensation - **ResizeHandleManager** - Event resizing with visual feedback - **AllDayManager** - All-day event layout and rendering - **HeaderManager** - Date headers and all-day event container - **ScrollManager** - Scroll behavior and position management - **EdgeScrollManager** - Automatic scrolling at viewport edges during drag ### Repository Pattern Event data access is abstracted through the **IEventRepository** interface (`src/repositories/IEventRepository.ts`): - **IndexedDBEventRepository** - Primary: Local storage with offline support - **ApiEventRepository** - Sends changes to backend API - **MockEventRepository** - Legacy: Loads from JSON file All repository methods accept an `UpdateSource` parameter ('local' | 'remote') to distinguish user actions from remote updates. ### Offline-First Sync Architecture **SyncManager** (`src/workers/SyncManager.ts`) provides background synchronization: 1. Local changes are written to **IndexedDB** immediately 2. Operations are queued in **OperationQueue** 3. SyncManager processes queue when online (5-second polling) 4. Failed operations retry with exponential backoff (max 5 retries) 5. Events have `syncStatus`: 'synced' | 'pending' | 'error' ### Rendering Strategy Pattern **EventRenderingService** (`src/renderers/EventRendererManager.ts`) uses strategy pattern: - **IEventRenderer** interface defines rendering contract - **DateEventRenderer** - Renders timed events in day columns - **AllDayEventRenderer** - Renders all-day events in header - Strategies can be swapped without changing core logic ### Layout Engines **EventStackManager** (`src/managers/EventStackManager.ts`) - Uses CSS flexbox for overlapping events: - Groups overlapping events into stacks - Calculates flex positioning (basis, grow, shrink) - Handles multi-column spanning events **AllDayLayoutEngine** (`src/utils/AllDayLayoutEngine.ts`) - Row-based layout for all-day events: - Detects overlaps and assigns row positions - Supports collapsed view (max 4 rows) with "+N more" indicator - Calculates container height dynamically ### Configuration System Configuration is loaded from `wwwroot/data/calendar-config.json` via **ConfigManager**: - **GridSettings** - Hour height, work hours, snap interval - **DateViewSettings** - Period type, first day of week - **TimeFormatConfig** - Timezone, locale, 12/24-hour format - **WorkWeekSettings** - Configurable work week presets - **Interaction** - Enable/disable drag, resize, create Access via injected `Configuration` instance, never load config directly. ## Important Patterns & Conventions ### Event Type Conversion (Drag & Drop) When dragging events between timed grid and all-day area: - **Timed → All-day**: `DragDropManager` emits `drag:mouseenter-header`, `AllDayManager` creates all-day clone - **All-day → Timed**: `DragDropManager` emits `drag:mouseenter-column`, `EventRenderingService` creates timed clone - Original element is marked with `data-conversion-source="true"` - Clone is marked with `data-converted-clone="true"` ### Scroll Compensation During Drag `DragDropManager` tracks scroll delta during edge-scrolling: 1. Listens to `edge-scroll:scrolling` events 2. Accumulates `scrollDeltaY` from scroll events 3. Compensates dragged element position: `targetY = mouseY - scrollDeltaY - mouseOffset.y` 4. Prevents visual "jumping" during scroll ### Grid Snapping When dropping events, snap to time grid: 1. Get mouse Y position relative to column 2. Convert to time using `PositionUtils.getTimeAtPosition()` 3. Account for `mouseOffset.y` (click position within event) 4. Snap to nearest `snapInterval` (default 15 minutes) ### Testing with Vitest Tests use **Vitest** with **jsdom** environment: - Setup file: `test/setup.ts` - Test helpers: `test/helpers/dom-helpers.ts` - Run single test: `npm test -- ` ## Key Files to Know - `src/index.ts` - DI container setup and initialization - `src/core/EventBus.ts` - Central event dispatcher - `src/constants/CoreEvents.ts` - All event type constants - `src/types/CalendarTypes.ts` - Core type definitions - `src/managers/CalendarManager.ts` - Main coordinator - `src/managers/DragDropManager.ts` - Detailed drag-drop architecture docs - `src/configurations/CalendarConfig.ts` - Configuration schema - `wwwroot/data/calendar-config.json` - Runtime configuration ## Common Tasks ### Adding a New Event Type to CoreEvents 1. Add constant to `src/constants/CoreEvents.ts` 2. Define payload type in `src/types/EventTypes.ts` 3. Emit with `eventBus.emit(CoreEvents.NEW_EVENT, payload)` 4. Subscribe with `eventBus.on(CoreEvents.NEW_EVENT, handler)` ### Adding a New Manager 1. Create in `src/managers/` 2. Inject dependencies via constructor (EventBus, Configuration, other managers) 3. Register in DI container in `src/index.ts`: `builder.registerType(NewManager).as()` 4. Communicate via EventBus only, never direct method calls 5. Initialize in CalendarManager if needed ### Modifying Event Data Always go through EventManager: - Create: `eventManager.createEvent(eventData)` - Update: `eventManager.updateEvent(id, updates)` - Delete: `eventManager.deleteEvent(id)` EventManager handles repository calls, event emission, and UI updates. ### Debugging Debug mode is enabled in development: ```javascript eventBus.setDebug(true); // In src/index.ts ``` Access debug interface in browser console: ```javascript window.calendarDebug.eventBus.getEventLog() window.calendarDebug.calendarManager window.calendarDebug.eventManager ``` ## Dependencies - **@novadi/core** - Dependency injection framework - **date-fns** / **date-fns-tz** - Date manipulation and timezone support - **fuse.js** - Fuzzy search for event filtering - **esbuild** - Fast bundler for development - **vitest** - Testing framework - **postcss** - CSS processing and optimization