diff --git a/apps/calendar/apps/web/package.json b/apps/calendar/apps/web/package.json index dfd8724e3..f9f78a82f 100644 --- a/apps/calendar/apps/web/package.json +++ b/apps/calendar/apps/web/package.json @@ -19,6 +19,7 @@ "@tailwindcss/vite": "^4.1.7", "@types/d3-force": "^3.0.0", "@types/node": "^20.0.0", + "@types/suncalc": "^1.9.2", "prettier": "^3.1.1", "prettier-plugin-svelte": "^3.1.2", "svelte": "^5.0.0", @@ -31,7 +32,6 @@ "dependencies": { "@calendar/shared": "workspace:*", "@manacore/shared-auth": "workspace:*", - "@manacore/shared-types": "workspace:*", "@manacore/shared-auth-ui": "workspace:*", "@manacore/shared-branding": "workspace:*", "@manacore/shared-feedback-service": "workspace:*", @@ -44,12 +44,14 @@ "@manacore/shared-tailwind": "workspace:*", "@manacore/shared-theme": "workspace:*", "@manacore/shared-theme-ui": "workspace:*", + "@manacore/shared-types": "workspace:*", "@manacore/shared-ui": "workspace:*", "@manacore/shared-utils": "workspace:*", "@neodrag/svelte": "^2.3.3", "d3-force": "^3.0.0", "date-fns": "^4.1.0", "lucide-svelte": "^0.559.0", + "suncalc": "^1.9.0", "svelte-dnd-action": "^0.9.68", "svelte-i18n": "^4.0.1" }, diff --git a/apps/calendar/apps/web/src/lib/components/calendar/DateStrip.svelte b/apps/calendar/apps/web/src/lib/components/calendar/DateStrip.svelte index cb3defb5d..c5cb6daa4 100644 --- a/apps/calendar/apps/web/src/lib/components/calendar/DateStrip.svelte +++ b/apps/calendar/apps/web/src/lib/components/calendar/DateStrip.svelte @@ -11,6 +11,42 @@ } from 'date-fns'; import { de } from 'date-fns/locale'; import { onMount, tick } from 'svelte'; + import SunCalc from 'suncalc'; + + // Moon phase emojis (8 phases) + const MOON_EMOJIS = ['🌑', '🌒', '🌓', '🌔', '🌕', '🌖', '🌗', '🌘']; + + // Get moon emoji for a date + function getMoonEmoji(date: Date): string { + const moonData = SunCalc.getMoonIllumination(date); + // phase: 0 = new moon, 0.25 = first quarter, 0.5 = full moon, 0.75 = last quarter + const phaseIndex = Math.floor(moonData.phase * 8) % 8; + return MOON_EMOJIS[phaseIndex]; + } + + // Check if this is a significant moon phase (new, first quarter, full, last quarter) + function isSignificantMoonPhase(date: Date): { significant: boolean; emoji: string } { + const moonData = SunCalc.getMoonIllumination(date); + const phase = moonData.phase; + // Lunar cycle is ~29.53 days, so 1 day = ~0.0339 + // Use half a day tolerance (~0.017) to ensure only 1 day is marked + const tolerance = 0.017; + + if (phase < tolerance || phase > 1 - tolerance) { + return { significant: true, emoji: '🌑' }; // New moon + } + if (Math.abs(phase - 0.25) < tolerance) { + return { significant: true, emoji: '🌓' }; // First quarter + } + if (Math.abs(phase - 0.5) < tolerance) { + return { significant: true, emoji: '🌕' }; // Full moon + } + if (Math.abs(phase - 0.75) < tolerance) { + return { significant: true, emoji: '🌗' }; // Last quarter + } + + return { significant: false, emoji: '' }; + } // Reactive view range - needed to trigger re-renders let viewRange = $derived(viewStore.viewRange); @@ -166,25 +202,14 @@