- CoreComponents.safe_row_id: four function heads dispatch on the row
shape (atom-key map, string-key map, other map fallback, non-map)
instead of a cond with Map.has_key? checks.
- Api.V1.CallsignController.validate_callsign: check_callsign_format/2
splits the validation success/failure branches into two clauses; the
binary-vs-everything-else dispatch stays in validate_callsign/1.
Adds 5 integration tests for the CallsignController covering invalid,
not-found, success, case-normalization, and length-limit paths. The
test file is async:false because it shares the rate-limiter ETS with
other API tests and could otherwise trip the 100/min per-IP cap.
OSM tile servers require a Referer header. Cloudflare appears to be
stripping the Referrer-Policy HTTP header, so add a meta tag that
the browser will always respect.
Only the 7 SVGs actually used are vendored into priv/heroicons/.
Also fixes the icon component path resolution (was missing the 24/
size directory) and corrects hero-x-mark/hero-map-pin icon names
that included an erroneous hero- prefix.
Make the map and panel CSS default to "sidebar open" on desktop
(>= 1024px) without requiring the slideover-open class. Only the
slideover-closed class now hides the sidebar. This prevents a blank
gap when the map and panel states get out of sync after LiveView
reconnects.
Dark navbar header with radio icon, refined slideover control panel
with branded header and section cards, polished cards/tables/badges
on info, packets, and about pages using ring borders and shadow-xs.
- Remove CSS `bottom: 50px !important` on popups that pushed them too
high above markers
- Hide trails when markers are clustered (zoom < 11) so trails don't
point to invisible markers inside cluster bubbles
- Fix locate button overlap with zoom controls and keep it always
light mode
- Remove duplicate slideover/toggle CSS from layout files (single
source of truth in component)
- Remove dead CSS classes and standardize breakpoints to 1024px
- Add isDestroyed guards on async callbacks to prevent race conditions
- Escape callsigns in marker HTML to prevent XSS
- Replace Leaflet _container private API with getContainer()
- Clean up about page placeholder text
Remove overly aggressive CSS overrides that set height: auto and
overflow: auto on html/body. These forced the html element to expand
to full content height, preventing the browser's native viewport
scrolling. The browser handles non-map page scrolling correctly by
default — only map pages need explicit overflow: hidden.
Replace all DaisyUI component classes with pure Tailwind CSS utility
classes following TailwindUI conventions. Use dark: variants for dark
mode instead of DaisyUI's data-theme semantic color system.
- Remove DaisyUI plugin and theme vendor files
- Configure Tailwind v4 custom dark variant for data-theme attribute
- Migrate all components: cards, tables, badges, buttons, forms,
navigation, dropdowns, modals, alerts, star ratings, spinners
- Update all auth pages (login, register, settings, password reset,
confirmation) to TailwindUI card layout
- Update content pages (about, API docs, status, packets, bad packets,
info, weather) to TailwindUI patterns
- Replace opacity-based text styling with semantic gray colors
- Update test assertions to match new class names
- Remove BodyClassHook: use CSS :has() selectors on data-map-page attribute
instead of JS toggling body.map-page class
- Remove ResponsiveSlideoverHook: pass viewport_width via LiveSocket connect
params and determine initial slideover state server-side
- Convert theme selector from data-set-theme attributes with DOMContentLoaded
listeners to JS.dispatch("phx:set-theme") with a single event handler
- Fix buggy reRenderAllCharts that incorrectly used `new MapAPRSMap()` as a
Map constructor; charts now re-render via themeChanged event directly
- Add minimum movement threshold (100m) to skip stationary stations
- Add max hop distance (10km) to break trails at igate-hopping artifacts
- Require 3+ points per segment to filter noise from 2-point jumps
- Remove duplicate slideover panel that was hidden behind the controls panel
- Fix toggle button positioning (was rendered above viewport at top:-9px)
- Remove conflicting high-specificity CSS that prevented mobile display:none
- Remove width:100vw on map so it properly resizes when panel opens
- Use theme-aware colors for panel background and navigation links
- Wrap navigation links in proper ul.menu for valid HTML and DaisyUI styling
- Combine Chart.js and date adapter into single bundle to ensure proper loading order
- Add validation to skip chart rendering when insufficient weather data (< 2 points)
- Update vendor loader to not load date adapter separately
- Prevents date adapter errors when weather stations have limited data
- Optimize LiveView to only update position data when coordinates actually change (>100m threshold)
- Hide loading spinner after map initialization to prevent flash on updates
- Add position change detection logic with proper nil/Decimal handling
- Add comprehensive test coverage for position change detection
- Maintain smooth map animations and existing functionality
Resolves map flashing and unnecessary reloads when new packets arrive on /info/:call pages.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Set map height to 100% instead of fixed 280px
- Removed card-body padding to eliminate gap
- Added h-full class to ensure cards fill available height
- Added min-height of 320px for better UX
- Removed redundant styling from map container
The map now properly fills the entire card height and matches
the position information card height on larger screens.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Fixed GeoUtils.significant_movement? test: Updated test coordinates
to actually move 55 meters (over 50m threshold) instead of 11 meters
- Fixed MapLive movement notification tests: Changed packet maps to use
atom keys instead of string keys to match expected format
- Fixed JavaScript URL interpolation in layout template: Replaced
incorrect HEEx syntax with static paths for vendor bundles
- Made movement tests more resilient by using receive blocks instead
of assert_push_event for better reliability in test environment
All 376 tests now pass successfully with 0 failures.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Download external libraries locally to reduce CDN dependencies
- Create combined vendor bundles for JavaScript and CSS
- Replace CDN script tags with local bundles in layout template
- Add build scripts for vendor bundle creation and optimization
- Configure esbuild to process vendor bundles separately
- Total bundle size: ~512KB JS + 17KB CSS (optimized)
Benefits:
- Reduced external requests and CDN dependencies
- Better caching control and reliability
- Eliminated CORS and SRI integrity issues
- Faster page loads with bundled resources
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Use the correct entry point from package.json (dist/oms.js) instead
of oms.min.js, and switch back to unpkg with the verified working
path to resolve content corruption issues.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Switch from unpkg.com to jsdelivr.net CDN to resolve MIME type
mismatch error. The unpkg CDN was serving the JavaScript file
with 'text/plain' content type instead of 'application/javascript'.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add comprehensive mobile viewport meta tags for better mobile experience
- Implement touch gesture support with long-press to show station info
- Create mobile-specific CSS with touch target optimization (44px minimum)
- Add safe area insets support for notched devices
- Position zoom controls at bottom-right on mobile for thumb accessibility
- Optimize map performance on mobile with canvas renderer
- Fix desktop sidebar toggle button clipping when slideover is open
- Improve responsive design for slideover panels and popups
- Add landscape mode and high DPI screen optimizations
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
The zoom controls were not displaying because the vendor.css file
containing Leaflet styles was not being included in the HTML template.
Added the missing stylesheet link to load vendor.css.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed the esbuild errors during Docker build where npm packages couldn't
be resolved. The issue was caused by node_modules being excluded from
the Docker build context in .dockerignore.
Implemented a proper vendor bundle strategy following Phoenix 1.7+ patterns:
- Created vendor.js that bundles all npm dependencies (Leaflet, Chart.js,
topbar) and their CSS files into a single pre-built bundle
- Added vendor esbuild profile in config.exs with proper CSS/image loaders
- Updated mix.exs to build vendor bundle before app bundle in assets.deploy
- Modified JavaScript imports to use globally loaded libraries from vendor
bundle instead of importing from node_modules
- Added vendor.js script tag to root.html.heex before app.js
This approach avoids Docker build failures by pre-bundling all vendor
dependencies, eliminating the need for node_modules during production builds.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
This change eliminates all external CDN dependencies by bundling JavaScript
libraries locally using esbuild. All vendor files are now served from the
application's own assets, improving reliability and privacy.
Changes:
- Created assets/js/vendor.js to bundle all external libraries
- Added vendor bundle configuration to esbuild in config files
- Updated all templates to use local vendor.js instead of CDN scripts
- Created download-vendor-files.sh script to fetch vendor libraries
- Added vendor files to .gitignore (they're downloaded during setup)
- Updated CSS imports to include vendor stylesheets
- Added vendor build step to assets.deploy mix task
Bundled libraries:
- Leaflet 1.9.4 (mapping library)
- Leaflet.heat 0.2.0 (heatmap plugin)
- Leaflet.markercluster 1.5.3 (marker clustering)
- OverlappingMarkerSpiderfier 0.2.6 (overlapping marker handling)
- Chart.js 4.5.0 (charting library)
- chartjs-adapter-date-fns 3.0.0 (date adapter for Chart.js)
All functionality tested and working correctly with local bundles.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Replaced direct Sentry SDK script tag with the Sentry Loader
- The loader handles CORS/network failures gracefully without breaking the app
- Moved Sentry configuration to use the onLoad callback
- Maintains the same configuration (100% sampling, BrowserTracing, etc.)
The Sentry Loader is a small inline script that:
1. Captures errors immediately (even before SDK loads)
2. Loads the SDK asynchronously
3. Handles load failures gracefully
4. Replays captured errors once SDK is loaded
This approach avoids CORS errors breaking the page load while still
providing error tracking when the SDK is accessible.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
The frontend Sentry script was causing CORS and Content Security Policy errors.
Since backend error tracking is already configured with Sentry, the frontend
SDK is not essential and has been removed to prevent these errors.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Mark gleam_stdlib and gleeunit with compile: false in mix.exs
- Copy pre-compiled Gleam BEAM files in Dockerfile
- Add explicit mix gleam_compile step before asset compilation
- Ensure priv/gleam directory is included in Docker build
This fixes the "Could not compile :gleam_stdlib" error during deployment.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>