diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..d80244e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,27 @@ +--- +name: Bug report +about: Report a reproducible problem +labels: bug +--- + +## Summary +Describe the issue clearly. + +## Steps to Reproduce +1. +2. +3. + +## Expected Behavior +What should happen. + +## Actual Behavior +What happened instead. + +## Environment +- OS: +- Browser/Version: +- Commit/Branch: + +## Additional Context +Logs, screenshots, or other details. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..8005e32 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,2 @@ +blank_issues_enabled: false +contact_links: [] diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..e7c9089 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,21 @@ +--- +name: Feature request +about: Propose an enhancement +labels: enhancement +--- + +## Problem +What problem should this solve? + +## Proposal +Describe the proposed solution. + +## Scope +What is in scope and out of scope? + +## Acceptance Criteria +- [ ] +- [ ] + +## Additional Context +Related links, references, or constraints. diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000..4d873b5 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,15 @@ +# Security Policy + +## Supported Versions +The `main` branch is the supported baseline for security fixes. + +## Reporting a Vulnerability +Please do not disclose vulnerabilities publicly. + +Report security issues privately to the maintainers with: +- A clear description of the issue +- Steps to reproduce +- Impact assessment +- Any suggested mitigation + +We will acknowledge valid reports and provide status updates during triage and remediation. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..efdc481 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,15 @@ +## Summary +Describe what changed and why. + +## Changes +- + +## Validation +- [ ] Relevant checks pass +- [ ] Manual verification completed + +## Related +Issue/Task: + +## Notes +Any deployment, migration, or follow-up notes. diff --git a/.gitignore b/.gitignore index 862e698..ab06e51 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,7 @@ # production /build -.github +.github/agents # misc .DS_Store diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..33ed9a2 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,21 @@ +# Code of Conduct + +## Our Commitment +We are committed to a respectful, inclusive, and constructive environment for everyone participating in this project. + +## Expected Behavior +- Be respectful and professional. +- Provide constructive feedback. +- Focus on technical discussion and project goals. +- Assume good intent and communicate clearly. + +## Unacceptable Behavior +- Harassment, discrimination, or personal attacks. +- Threatening, abusive, or disruptive conduct. +- Sharing private information without consent. + +## Reporting +Report concerns to the maintainers through private repository contact channels. Reports will be reviewed promptly and handled confidentially where possible. + +## Enforcement +Maintainers may remove, edit, or reject contributions and interactions that violate this Code of Conduct. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..572d0d3 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,21 @@ +# Contributing + +## Workflow +1. Create a branch from `main`. +2. Make focused, minimal changes. +3. Run relevant checks locally. +4. Open a pull request with clear context. + +## Pull Request Expectations +- Keep PRs scoped to one topic. +- Include a short summary of what changed and why. +- Reference related issues when applicable. +- Update docs when behavior changes. + +## Code Quality +- Follow existing project style and conventions. +- Avoid unrelated refactors. +- Prefer clear, maintainable implementations. + +## Commit Messages +Use concise, descriptive commit messages (for example: `feat: add airport search index`). diff --git a/public/airline-logos/aegean-airlines.png b/public/airline-logos/aegean-airlines.png new file mode 100644 index 0000000..6d0d175 Binary files /dev/null and b/public/airline-logos/aegean-airlines.png differ diff --git a/public/airline-logos/aer-lingus.png b/public/airline-logos/aer-lingus.png new file mode 100644 index 0000000..512974f Binary files /dev/null and b/public/airline-logos/aer-lingus.png differ diff --git a/public/airline-logos/aeroflot.svg b/public/airline-logos/aeroflot.svg new file mode 100644 index 0000000..c8a2379 --- /dev/null +++ b/public/airline-logos/aeroflot.svg @@ -0,0 +1 @@ +Aeroflot \ No newline at end of file diff --git a/public/airline-logos/aerola-neas-argentinas.png b/public/airline-logos/aerola-neas-argentinas.png new file mode 100644 index 0000000..d2ac328 Binary files /dev/null and b/public/airline-logos/aerola-neas-argentinas.png differ diff --git a/public/airline-logos/aeroma-xico.png b/public/airline-logos/aeroma-xico.png new file mode 100644 index 0000000..7eb17d4 Binary files /dev/null and b/public/airline-logos/aeroma-xico.png differ diff --git a/public/airline-logos/air-algerie.png b/public/airline-logos/air-algerie.png new file mode 100644 index 0000000..9feecfd Binary files /dev/null and b/public/airline-logos/air-algerie.png differ diff --git a/public/airline-logos/air-arabia.png b/public/airline-logos/air-arabia.png new file mode 100644 index 0000000..14501db Binary files /dev/null and b/public/airline-logos/air-arabia.png differ diff --git a/public/airline-logos/air-astana.png b/public/airline-logos/air-astana.png new file mode 100644 index 0000000..927008a Binary files /dev/null and b/public/airline-logos/air-astana.png differ diff --git a/public/airline-logos/air-baltic.png b/public/airline-logos/air-baltic.png new file mode 100644 index 0000000..b009831 Binary files /dev/null and b/public/airline-logos/air-baltic.png differ diff --git a/public/airline-logos/air-berlin.png b/public/airline-logos/air-berlin.png new file mode 100644 index 0000000..bc988a9 Binary files /dev/null and b/public/airline-logos/air-berlin.png differ diff --git a/public/airline-logos/air-canada-jazz.png b/public/airline-logos/air-canada-jazz.png new file mode 100644 index 0000000..fbeab85 Binary files /dev/null and b/public/airline-logos/air-canada-jazz.png differ diff --git a/public/airline-logos/air-canada.svg b/public/airline-logos/air-canada.svg new file mode 100644 index 0000000..392cd34 --- /dev/null +++ b/public/airline-logos/air-canada.svg @@ -0,0 +1 @@ +Air Canada \ No newline at end of file diff --git a/public/airline-logos/air-china.svg b/public/airline-logos/air-china.svg new file mode 100644 index 0000000..b89229d --- /dev/null +++ b/public/airline-logos/air-china.svg @@ -0,0 +1 @@ +Air China \ No newline at end of file diff --git a/public/airline-logos/air-dolomiti.png b/public/airline-logos/air-dolomiti.png new file mode 100644 index 0000000..ed4c572 Binary files /dev/null and b/public/airline-logos/air-dolomiti.png differ diff --git a/public/airline-logos/air-europa.png b/public/airline-logos/air-europa.png new file mode 100644 index 0000000..a9f503d Binary files /dev/null and b/public/airline-logos/air-europa.png differ diff --git a/public/airline-logos/air-france.svg b/public/airline-logos/air-france.svg new file mode 100644 index 0000000..95c28e9 --- /dev/null +++ b/public/airline-logos/air-france.svg @@ -0,0 +1 @@ +Air France \ No newline at end of file diff --git a/public/airline-logos/air-india-express.png b/public/airline-logos/air-india-express.png new file mode 100644 index 0000000..818d246 Binary files /dev/null and b/public/airline-logos/air-india-express.png differ diff --git a/public/airline-logos/air-india.svg b/public/airline-logos/air-india.svg new file mode 100644 index 0000000..8ccd355 --- /dev/null +++ b/public/airline-logos/air-india.svg @@ -0,0 +1 @@ +Air India \ No newline at end of file diff --git a/public/airline-logos/air-mauritius.png b/public/airline-logos/air-mauritius.png new file mode 100644 index 0000000..a146def Binary files /dev/null and b/public/airline-logos/air-mauritius.png differ diff --git a/public/airline-logos/air-new-zealand.png b/public/airline-logos/air-new-zealand.png new file mode 100644 index 0000000..3c6967a Binary files /dev/null and b/public/airline-logos/air-new-zealand.png differ diff --git a/public/airline-logos/air-nostrum.png b/public/airline-logos/air-nostrum.png new file mode 100644 index 0000000..fbeab85 Binary files /dev/null and b/public/airline-logos/air-nostrum.png differ diff --git a/public/airline-logos/air-serbia.svg b/public/airline-logos/air-serbia.svg new file mode 100644 index 0000000..c1b42c4 --- /dev/null +++ b/public/airline-logos/air-serbia.svg @@ -0,0 +1 @@ +Air Serbia \ No newline at end of file diff --git a/public/airline-logos/air-transat.svg b/public/airline-logos/air-transat.svg new file mode 100644 index 0000000..b078a8c --- /dev/null +++ b/public/airline-logos/air-transat.svg @@ -0,0 +1 @@ +Air Transat \ No newline at end of file diff --git a/public/airline-logos/airasia-x.png b/public/airline-logos/airasia-x.png new file mode 100644 index 0000000..5d083e3 Binary files /dev/null and b/public/airline-logos/airasia-x.png differ diff --git a/public/airline-logos/airasia.svg b/public/airline-logos/airasia.svg new file mode 100644 index 0000000..41d1d77 --- /dev/null +++ b/public/airline-logos/airasia.svg @@ -0,0 +1 @@ +AirAsia \ No newline at end of file diff --git a/public/airline-logos/alaska-airlines.png b/public/airline-logos/alaska-airlines.png new file mode 100644 index 0000000..a2eed02 Binary files /dev/null and b/public/airline-logos/alaska-airlines.png differ diff --git a/public/airline-logos/alitalia-ita-airways.png b/public/airline-logos/alitalia-ita-airways.png new file mode 100644 index 0000000..b674de1 Binary files /dev/null and b/public/airline-logos/alitalia-ita-airways.png differ diff --git a/public/airline-logos/alitalia.png b/public/airline-logos/alitalia.png new file mode 100644 index 0000000..b674de1 Binary files /dev/null and b/public/airline-logos/alitalia.png differ diff --git a/public/airline-logos/all-nippon-airways.png b/public/airline-logos/all-nippon-airways.png new file mode 100644 index 0000000..3986e13 Binary files /dev/null and b/public/airline-logos/all-nippon-airways.png differ diff --git a/public/airline-logos/allegiant-air.png b/public/airline-logos/allegiant-air.png new file mode 100644 index 0000000..f8dac3c Binary files /dev/null and b/public/airline-logos/allegiant-air.png differ diff --git a/public/airline-logos/american-airlines.svg b/public/airline-logos/american-airlines.svg new file mode 100644 index 0000000..e981dac --- /dev/null +++ b/public/airline-logos/american-airlines.svg @@ -0,0 +1 @@ +American Airlines \ No newline at end of file diff --git a/public/airline-logos/amerijet.png b/public/airline-logos/amerijet.png new file mode 100644 index 0000000..fbeab85 Binary files /dev/null and b/public/airline-logos/amerijet.png differ diff --git a/public/airline-logos/ana.png b/public/airline-logos/ana.png new file mode 100644 index 0000000..3986e13 Binary files /dev/null and b/public/airline-logos/ana.png differ diff --git a/public/airline-logos/asiana-airlines.png b/public/airline-logos/asiana-airlines.png new file mode 100644 index 0000000..9dba60b Binary files /dev/null and b/public/airline-logos/asiana-airlines.png differ diff --git a/public/airline-logos/atlas-air.png b/public/airline-logos/atlas-air.png new file mode 100644 index 0000000..eb6fb87 Binary files /dev/null and b/public/airline-logos/atlas-air.png differ diff --git a/public/airline-logos/austrian-airlines.png b/public/airline-logos/austrian-airlines.png new file mode 100644 index 0000000..2083bca Binary files /dev/null and b/public/airline-logos/austrian-airlines.png differ diff --git a/public/airline-logos/avianca.svg b/public/airline-logos/avianca.svg new file mode 100644 index 0000000..b8beac3 --- /dev/null +++ b/public/airline-logos/avianca.svg @@ -0,0 +1 @@ +avianca \ No newline at end of file diff --git a/public/airline-logos/azul.png b/public/airline-logos/azul.png new file mode 100644 index 0000000..af22e0f Binary files /dev/null and b/public/airline-logos/azul.png differ diff --git a/public/airline-logos/binter-canarias.png b/public/airline-logos/binter-canarias.png new file mode 100644 index 0000000..bb41caf Binary files /dev/null and b/public/airline-logos/binter-canarias.png differ diff --git a/public/airline-logos/british-airways.svg b/public/airline-logos/british-airways.svg new file mode 100644 index 0000000..b2ddd0a --- /dev/null +++ b/public/airline-logos/british-airways.svg @@ -0,0 +1 @@ +British Airways \ No newline at end of file diff --git a/public/airline-logos/brussels-airlines.png b/public/airline-logos/brussels-airlines.png new file mode 100644 index 0000000..7e93eaf Binary files /dev/null and b/public/airline-logos/brussels-airlines.png differ diff --git a/public/airline-logos/cathay-pacific.png b/public/airline-logos/cathay-pacific.png new file mode 100644 index 0000000..5747ccf Binary files /dev/null and b/public/airline-logos/cathay-pacific.png differ diff --git a/public/airline-logos/cebu-pacific.png b/public/airline-logos/cebu-pacific.png new file mode 100644 index 0000000..018afbe Binary files /dev/null and b/public/airline-logos/cebu-pacific.png differ diff --git a/public/airline-logos/china-airlines.png b/public/airline-logos/china-airlines.png new file mode 100644 index 0000000..7a8b3b5 Binary files /dev/null and b/public/airline-logos/china-airlines.png differ diff --git a/public/airline-logos/china-eastern.png b/public/airline-logos/china-eastern.png new file mode 100644 index 0000000..8175f97 Binary files /dev/null and b/public/airline-logos/china-eastern.png differ diff --git a/public/airline-logos/china-southern.png b/public/airline-logos/china-southern.png new file mode 100644 index 0000000..67c442f Binary files /dev/null and b/public/airline-logos/china-southern.png differ diff --git a/public/airline-logos/condor.png b/public/airline-logos/condor.png new file mode 100644 index 0000000..1d43f8d Binary files /dev/null and b/public/airline-logos/condor.png differ diff --git a/public/airline-logos/copa-airlines.svg b/public/airline-logos/copa-airlines.svg new file mode 100644 index 0000000..73962bd --- /dev/null +++ b/public/airline-logos/copa-airlines.svg @@ -0,0 +1 @@ +Copa Airlines \ No newline at end of file diff --git a/public/airline-logos/croatia-airlines.png b/public/airline-logos/croatia-airlines.png new file mode 100644 index 0000000..7da25d8 Binary files /dev/null and b/public/airline-logos/croatia-airlines.png differ diff --git a/public/airline-logos/delta-air-lines.svg b/public/airline-logos/delta-air-lines.svg new file mode 100644 index 0000000..20aecad --- /dev/null +++ b/public/airline-logos/delta-air-lines.svg @@ -0,0 +1 @@ +Delta \ No newline at end of file diff --git a/public/airline-logos/easyjet-europe.png b/public/airline-logos/easyjet-europe.png new file mode 100644 index 0000000..5c67da5 Binary files /dev/null and b/public/airline-logos/easyjet-europe.png differ diff --git a/public/airline-logos/easyjet.svg b/public/airline-logos/easyjet.svg new file mode 100644 index 0000000..db71d0d --- /dev/null +++ b/public/airline-logos/easyjet.svg @@ -0,0 +1 @@ +easyJet \ No newline at end of file diff --git a/public/airline-logos/egyptair.png b/public/airline-logos/egyptair.png new file mode 100644 index 0000000..0d1cb1d Binary files /dev/null and b/public/airline-logos/egyptair.png differ diff --git a/public/airline-logos/el-al.png b/public/airline-logos/el-al.png new file mode 100644 index 0000000..c992138 Binary files /dev/null and b/public/airline-logos/el-al.png differ diff --git a/public/airline-logos/emirates.svg b/public/airline-logos/emirates.svg new file mode 100644 index 0000000..35c4441 --- /dev/null +++ b/public/airline-logos/emirates.svg @@ -0,0 +1 @@ +Emirates \ No newline at end of file diff --git a/public/airline-logos/envoy-air.png b/public/airline-logos/envoy-air.png new file mode 100644 index 0000000..fbeab85 Binary files /dev/null and b/public/airline-logos/envoy-air.png differ diff --git a/public/airline-logos/ethiopian-airlines.svg b/public/airline-logos/ethiopian-airlines.svg new file mode 100644 index 0000000..4b22a47 --- /dev/null +++ b/public/airline-logos/ethiopian-airlines.svg @@ -0,0 +1 @@ +Ethiopian Airlines \ No newline at end of file diff --git a/public/airline-logos/etihad-airways.svg b/public/airline-logos/etihad-airways.svg new file mode 100644 index 0000000..4489d30 --- /dev/null +++ b/public/airline-logos/etihad-airways.svg @@ -0,0 +1 @@ +Etihad Airways \ No newline at end of file diff --git a/public/airline-logos/eurowings.png b/public/airline-logos/eurowings.png new file mode 100644 index 0000000..fc8beee Binary files /dev/null and b/public/airline-logos/eurowings.png differ diff --git a/public/airline-logos/eva-air.png b/public/airline-logos/eva-air.png new file mode 100644 index 0000000..5771544 Binary files /dev/null and b/public/airline-logos/eva-air.png differ diff --git a/public/airline-logos/fedex-express.png b/public/airline-logos/fedex-express.png new file mode 100644 index 0000000..fbeab85 Binary files /dev/null and b/public/airline-logos/fedex-express.png differ diff --git a/public/airline-logos/fiji-airways.png b/public/airline-logos/fiji-airways.png new file mode 100644 index 0000000..e956ad5 Binary files /dev/null and b/public/airline-logos/fiji-airways.png differ diff --git a/public/airline-logos/finnair.png b/public/airline-logos/finnair.png new file mode 100644 index 0000000..49ba990 Binary files /dev/null and b/public/airline-logos/finnair.png differ diff --git a/public/airline-logos/flydubai.png b/public/airline-logos/flydubai.png new file mode 100644 index 0000000..0192e6a Binary files /dev/null and b/public/airline-logos/flydubai.png differ diff --git a/public/airline-logos/frontier-airlines.png b/public/airline-logos/frontier-airlines.png new file mode 100644 index 0000000..efb1ee0 Binary files /dev/null and b/public/airline-logos/frontier-airlines.png differ diff --git a/public/airline-logos/garuda-indonesia.png b/public/airline-logos/garuda-indonesia.png new file mode 100644 index 0000000..ac945fc Binary files /dev/null and b/public/airline-logos/garuda-indonesia.png differ diff --git a/public/airline-logos/german-air-force.svg b/public/airline-logos/german-air-force.svg new file mode 100644 index 0000000..d6dd52e --- /dev/null +++ b/public/airline-logos/german-air-force.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/airline-logos/gol.png b/public/airline-logos/gol.png new file mode 100644 index 0000000..ec7056a Binary files /dev/null and b/public/airline-logos/gol.png differ diff --git a/public/airline-logos/gulf-air.png b/public/airline-logos/gulf-air.png new file mode 100644 index 0000000..2dcff0b Binary files /dev/null and b/public/airline-logos/gulf-air.png differ diff --git a/public/airline-logos/hainan-airlines.png b/public/airline-logos/hainan-airlines.png new file mode 100644 index 0000000..47a276c Binary files /dev/null and b/public/airline-logos/hainan-airlines.png differ diff --git a/public/airline-logos/hawaiian-airlines.png b/public/airline-logos/hawaiian-airlines.png new file mode 100644 index 0000000..325aeb1 Binary files /dev/null and b/public/airline-logos/hawaiian-airlines.png differ diff --git a/public/airline-logos/hong-kong-airlines.png b/public/airline-logos/hong-kong-airlines.png new file mode 100644 index 0000000..1e6f9f2 Binary files /dev/null and b/public/airline-logos/hong-kong-airlines.png differ diff --git a/public/airline-logos/hong-kong-express.png b/public/airline-logos/hong-kong-express.png new file mode 100644 index 0000000..3feb4ba Binary files /dev/null and b/public/airline-logos/hong-kong-express.png differ diff --git a/public/airline-logos/iberia-express.png b/public/airline-logos/iberia-express.png new file mode 100644 index 0000000..b7c6135 Binary files /dev/null and b/public/airline-logos/iberia-express.png differ diff --git a/public/airline-logos/iberia.svg b/public/airline-logos/iberia.svg new file mode 100644 index 0000000..e049bdc --- /dev/null +++ b/public/airline-logos/iberia.svg @@ -0,0 +1 @@ +Iberia \ No newline at end of file diff --git a/public/airline-logos/icelandair.svg b/public/airline-logos/icelandair.svg new file mode 100644 index 0000000..1355cf1 --- /dev/null +++ b/public/airline-logos/icelandair.svg @@ -0,0 +1 @@ +Iceland \ No newline at end of file diff --git a/public/airline-logos/indigo.svg b/public/airline-logos/indigo.svg new file mode 100644 index 0000000..3c48fc6 --- /dev/null +++ b/public/airline-logos/indigo.svg @@ -0,0 +1 @@ +IndiGo \ No newline at end of file diff --git a/public/airline-logos/interjet.png b/public/airline-logos/interjet.png new file mode 100644 index 0000000..fbeab85 Binary files /dev/null and b/public/airline-logos/interjet.png differ diff --git a/public/airline-logos/ita-airways.png b/public/airline-logos/ita-airways.png new file mode 100644 index 0000000..b674de1 Binary files /dev/null and b/public/airline-logos/ita-airways.png differ diff --git a/public/airline-logos/japan-airlines.svg b/public/airline-logos/japan-airlines.svg new file mode 100644 index 0000000..d5b9664 --- /dev/null +++ b/public/airline-logos/japan-airlines.svg @@ -0,0 +1 @@ +Japan Airlines \ No newline at end of file diff --git a/public/airline-logos/jeju-air.png b/public/airline-logos/jeju-air.png new file mode 100644 index 0000000..6395c40 Binary files /dev/null and b/public/airline-logos/jeju-air.png differ diff --git a/public/airline-logos/jet2.png b/public/airline-logos/jet2.png new file mode 100644 index 0000000..5598c7b Binary files /dev/null and b/public/airline-logos/jet2.png differ diff --git a/public/airline-logos/jetblue.svg b/public/airline-logos/jetblue.svg new file mode 100644 index 0000000..95eeebe --- /dev/null +++ b/public/airline-logos/jetblue.svg @@ -0,0 +1 @@ +JetBlue \ No newline at end of file diff --git a/public/airline-logos/jetstar.png b/public/airline-logos/jetstar.png new file mode 100644 index 0000000..120a8ae Binary files /dev/null and b/public/airline-logos/jetstar.png differ diff --git a/public/airline-logos/juneyao-airlines.png b/public/airline-logos/juneyao-airlines.png new file mode 100644 index 0000000..72fee2d Binary files /dev/null and b/public/airline-logos/juneyao-airlines.png differ diff --git a/public/airline-logos/klm.svg b/public/airline-logos/klm.svg new file mode 100644 index 0000000..974f6b7 --- /dev/null +++ b/public/airline-logos/klm.svg @@ -0,0 +1 @@ +KLM \ No newline at end of file diff --git a/public/airline-logos/korean-air.png b/public/airline-logos/korean-air.png new file mode 100644 index 0000000..64a89ac Binary files /dev/null and b/public/airline-logos/korean-air.png differ diff --git a/public/airline-logos/latam-airlines.png b/public/airline-logos/latam-airlines.png new file mode 100644 index 0000000..60c483a Binary files /dev/null and b/public/airline-logos/latam-airlines.png differ diff --git a/public/airline-logos/latam-brasil.png b/public/airline-logos/latam-brasil.png new file mode 100644 index 0000000..60c483a Binary files /dev/null and b/public/airline-logos/latam-brasil.png differ diff --git a/public/airline-logos/latam-pera.png b/public/airline-logos/latam-pera.png new file mode 100644 index 0000000..60c483a Binary files /dev/null and b/public/airline-logos/latam-pera.png differ diff --git a/public/airline-logos/lot-polish-airlines.svg b/public/airline-logos/lot-polish-airlines.svg new file mode 100644 index 0000000..1395606 --- /dev/null +++ b/public/airline-logos/lot-polish-airlines.svg @@ -0,0 +1 @@ +LOT Polish Airlines \ No newline at end of file diff --git a/public/airline-logos/lufthansa-cityline.png b/public/airline-logos/lufthansa-cityline.png new file mode 100644 index 0000000..fbeab85 Binary files /dev/null and b/public/airline-logos/lufthansa-cityline.png differ diff --git a/public/airline-logos/lufthansa.svg b/public/airline-logos/lufthansa.svg new file mode 100644 index 0000000..923b4b1 --- /dev/null +++ b/public/airline-logos/lufthansa.svg @@ -0,0 +1 @@ +Lufthansa \ No newline at end of file diff --git a/public/airline-logos/luxair.png b/public/airline-logos/luxair.png new file mode 100644 index 0000000..da07c20 Binary files /dev/null and b/public/airline-logos/luxair.png differ diff --git a/public/airline-logos/malaysia-airlines.png b/public/airline-logos/malaysia-airlines.png new file mode 100644 index 0000000..2b2083d Binary files /dev/null and b/public/airline-logos/malaysia-airlines.png differ diff --git a/public/airline-logos/norse-atlantic-airways.png b/public/airline-logos/norse-atlantic-airways.png new file mode 100644 index 0000000..37ff37d Binary files /dev/null and b/public/airline-logos/norse-atlantic-airways.png differ diff --git a/public/airline-logos/norwegian-air-int-l.png b/public/airline-logos/norwegian-air-int-l.png new file mode 100644 index 0000000..6fd89e4 Binary files /dev/null and b/public/airline-logos/norwegian-air-int-l.png differ diff --git a/public/airline-logos/norwegian-air-shuttle.svg b/public/airline-logos/norwegian-air-shuttle.svg new file mode 100644 index 0000000..92f5db3 --- /dev/null +++ b/public/airline-logos/norwegian-air-shuttle.svg @@ -0,0 +1 @@ +Norwegian \ No newline at end of file diff --git a/public/airline-logos/norwegian.svg b/public/airline-logos/norwegian.svg new file mode 100644 index 0000000..92f5db3 --- /dev/null +++ b/public/airline-logos/norwegian.svg @@ -0,0 +1 @@ +Norwegian \ No newline at end of file diff --git a/public/airline-logos/oman-air.png b/public/airline-logos/oman-air.png new file mode 100644 index 0000000..020c59b Binary files /dev/null and b/public/airline-logos/oman-air.png differ diff --git a/public/airline-logos/pakistan-int-l-airlines.png b/public/airline-logos/pakistan-int-l-airlines.png new file mode 100644 index 0000000..4cf4301 Binary files /dev/null and b/public/airline-logos/pakistan-int-l-airlines.png differ diff --git a/public/airline-logos/pakistan-international-airlines.png b/public/airline-logos/pakistan-international-airlines.png new file mode 100644 index 0000000..4cf4301 Binary files /dev/null and b/public/airline-logos/pakistan-international-airlines.png differ diff --git a/public/airline-logos/peach-aviation.png b/public/airline-logos/peach-aviation.png new file mode 100644 index 0000000..04b0658 Binary files /dev/null and b/public/airline-logos/peach-aviation.png differ diff --git a/public/airline-logos/pegasus-airlines.svg b/public/airline-logos/pegasus-airlines.svg new file mode 100644 index 0000000..176a185 --- /dev/null +++ b/public/airline-logos/pegasus-airlines.svg @@ -0,0 +1 @@ +Pegasus Airlines \ No newline at end of file diff --git a/public/airline-logos/philippine-airlines.png b/public/airline-logos/philippine-airlines.png new file mode 100644 index 0000000..2614b46 Binary files /dev/null and b/public/airline-logos/philippine-airlines.png differ diff --git a/public/airline-logos/porter-airlines.png b/public/airline-logos/porter-airlines.png new file mode 100644 index 0000000..c3265b9 Binary files /dev/null and b/public/airline-logos/porter-airlines.png differ diff --git a/public/airline-logos/qantas.svg b/public/airline-logos/qantas.svg new file mode 100644 index 0000000..6beec92 --- /dev/null +++ b/public/airline-logos/qantas.svg @@ -0,0 +1 @@ +Qantas \ No newline at end of file diff --git a/public/airline-logos/qatar-airways.svg b/public/airline-logos/qatar-airways.svg new file mode 100644 index 0000000..f4151ab --- /dev/null +++ b/public/airline-logos/qatar-airways.svg @@ -0,0 +1 @@ +Qatar Airways \ No newline at end of file diff --git a/public/airline-logos/republic-airways.png b/public/airline-logos/republic-airways.png new file mode 100644 index 0000000..8578aed Binary files /dev/null and b/public/airline-logos/republic-airways.png differ diff --git a/public/airline-logos/rossiya.png b/public/airline-logos/rossiya.png new file mode 100644 index 0000000..a8bef69 Binary files /dev/null and b/public/airline-logos/rossiya.png differ diff --git a/public/airline-logos/royal-air-maroc.png b/public/airline-logos/royal-air-maroc.png new file mode 100644 index 0000000..0776558 Binary files /dev/null and b/public/airline-logos/royal-air-maroc.png differ diff --git a/public/airline-logos/royal-jordanian.png b/public/airline-logos/royal-jordanian.png new file mode 100644 index 0000000..7eb02d7 Binary files /dev/null and b/public/airline-logos/royal-jordanian.png differ diff --git a/public/airline-logos/ryanair.svg b/public/airline-logos/ryanair.svg new file mode 100644 index 0000000..03efbd7 --- /dev/null +++ b/public/airline-logos/ryanair.svg @@ -0,0 +1 @@ +Ryanair \ No newline at end of file diff --git a/public/airline-logos/sas.png b/public/airline-logos/sas.png new file mode 100644 index 0000000..d106795 Binary files /dev/null and b/public/airline-logos/sas.png differ diff --git a/public/airline-logos/saudia.svg b/public/airline-logos/saudia.svg new file mode 100644 index 0000000..939703e --- /dev/null +++ b/public/airline-logos/saudia.svg @@ -0,0 +1 @@ +Saudia \ No newline at end of file diff --git a/public/airline-logos/scandinavian-airlines.png b/public/airline-logos/scandinavian-airlines.png new file mode 100644 index 0000000..d106795 Binary files /dev/null and b/public/airline-logos/scandinavian-airlines.png differ diff --git a/public/airline-logos/scoot.png b/public/airline-logos/scoot.png new file mode 100644 index 0000000..a24359d Binary files /dev/null and b/public/airline-logos/scoot.png differ diff --git a/public/airline-logos/shenzhen-airlines.png b/public/airline-logos/shenzhen-airlines.png new file mode 100644 index 0000000..7d8450c Binary files /dev/null and b/public/airline-logos/shenzhen-airlines.png differ diff --git a/public/airline-logos/sichuan-airlines.png b/public/airline-logos/sichuan-airlines.png new file mode 100644 index 0000000..8b4ffa4 Binary files /dev/null and b/public/airline-logos/sichuan-airlines.png differ diff --git a/public/airline-logos/singapore-airlines.svg b/public/airline-logos/singapore-airlines.svg new file mode 100644 index 0000000..169934d --- /dev/null +++ b/public/airline-logos/singapore-airlines.svg @@ -0,0 +1 @@ +Singapore Airlines \ No newline at end of file diff --git a/public/airline-logos/sky-express.png b/public/airline-logos/sky-express.png new file mode 100644 index 0000000..ec7056a Binary files /dev/null and b/public/airline-logos/sky-express.png differ diff --git a/public/airline-logos/skymark-airlines.png b/public/airline-logos/skymark-airlines.png new file mode 100644 index 0000000..55d7263 Binary files /dev/null and b/public/airline-logos/skymark-airlines.png differ diff --git a/public/airline-logos/skywest-airlines.png b/public/airline-logos/skywest-airlines.png new file mode 100644 index 0000000..fbeab85 Binary files /dev/null and b/public/airline-logos/skywest-airlines.png differ diff --git a/public/airline-logos/south-african-airways.png b/public/airline-logos/south-african-airways.png new file mode 100644 index 0000000..8bfd3cc Binary files /dev/null and b/public/airline-logos/south-african-airways.png differ diff --git a/public/airline-logos/southwest-airlines.svg b/public/airline-logos/southwest-airlines.svg new file mode 100644 index 0000000..8bb6fda --- /dev/null +++ b/public/airline-logos/southwest-airlines.svg @@ -0,0 +1 @@ +Southwest Airlines \ No newline at end of file diff --git a/public/airline-logos/spicejet.png b/public/airline-logos/spicejet.png new file mode 100644 index 0000000..9e14d84 Binary files /dev/null and b/public/airline-logos/spicejet.png differ diff --git a/public/airline-logos/spirit-airlines.png b/public/airline-logos/spirit-airlines.png new file mode 100644 index 0000000..fad1cfb Binary files /dev/null and b/public/airline-logos/spirit-airlines.png differ diff --git a/public/airline-logos/spring-airlines.svg b/public/airline-logos/spring-airlines.svg new file mode 100644 index 0000000..11a2af9 --- /dev/null +++ b/public/airline-logos/spring-airlines.svg @@ -0,0 +1 @@ +Spring \ No newline at end of file diff --git a/public/airline-logos/srilankan-airlines.png b/public/airline-logos/srilankan-airlines.png new file mode 100644 index 0000000..3900c2c Binary files /dev/null and b/public/airline-logos/srilankan-airlines.png differ diff --git a/public/airline-logos/starlux-airlines.png b/public/airline-logos/starlux-airlines.png new file mode 100644 index 0000000..e36a1c8 Binary files /dev/null and b/public/airline-logos/starlux-airlines.png differ diff --git a/public/airline-logos/sun-country-airlines.png b/public/airline-logos/sun-country-airlines.png new file mode 100644 index 0000000..b9ebedf Binary files /dev/null and b/public/airline-logos/sun-country-airlines.png differ diff --git a/public/airline-logos/swiss-int-l-air-lines.png b/public/airline-logos/swiss-int-l-air-lines.png new file mode 100644 index 0000000..d206db6 Binary files /dev/null and b/public/airline-logos/swiss-int-l-air-lines.png differ diff --git a/public/airline-logos/swiss.png b/public/airline-logos/swiss.png new file mode 100644 index 0000000..5c026b7 Binary files /dev/null and b/public/airline-logos/swiss.png differ diff --git a/public/airline-logos/tap-air-portugal.png b/public/airline-logos/tap-air-portugal.png new file mode 100644 index 0000000..57faae0 Binary files /dev/null and b/public/airline-logos/tap-air-portugal.png differ diff --git a/public/airline-logos/tarom.svg b/public/airline-logos/tarom.svg new file mode 100644 index 0000000..7db26d3 --- /dev/null +++ b/public/airline-logos/tarom.svg @@ -0,0 +1 @@ +TAROM \ No newline at end of file diff --git a/public/airline-logos/thai-airways.png b/public/airline-logos/thai-airways.png new file mode 100644 index 0000000..b6d3dfd Binary files /dev/null and b/public/airline-logos/thai-airways.png differ diff --git a/public/airline-logos/transavia-france.png b/public/airline-logos/transavia-france.png new file mode 100644 index 0000000..688df6f Binary files /dev/null and b/public/airline-logos/transavia-france.png differ diff --git a/public/airline-logos/transavia.png b/public/airline-logos/transavia.png new file mode 100644 index 0000000..688df6f Binary files /dev/null and b/public/airline-logos/transavia.png differ diff --git a/public/airline-logos/tui-airways.svg b/public/airline-logos/tui-airways.svg new file mode 100644 index 0000000..3af2df1 --- /dev/null +++ b/public/airline-logos/tui-airways.svg @@ -0,0 +1 @@ +TUI \ No newline at end of file diff --git a/public/airline-logos/turkish-airlines.svg b/public/airline-logos/turkish-airlines.svg new file mode 100644 index 0000000..6867d46 --- /dev/null +++ b/public/airline-logos/turkish-airlines.svg @@ -0,0 +1 @@ +Turkish Airlines \ No newline at end of file diff --git a/public/airline-logos/tway-airlines.png b/public/airline-logos/tway-airlines.png new file mode 100644 index 0000000..c41cbb1 Binary files /dev/null and b/public/airline-logos/tway-airlines.png differ diff --git a/public/airline-logos/united-airlines.svg b/public/airline-logos/united-airlines.svg new file mode 100644 index 0000000..ffeef03 --- /dev/null +++ b/public/airline-logos/united-airlines.svg @@ -0,0 +1 @@ +United Airlines \ No newline at end of file diff --git a/public/airline-logos/ups-airlines.svg b/public/airline-logos/ups-airlines.svg new file mode 100644 index 0000000..8dbcd44 --- /dev/null +++ b/public/airline-logos/ups-airlines.svg @@ -0,0 +1 @@ +UPS \ No newline at end of file diff --git a/public/airline-logos/us-airways.png b/public/airline-logos/us-airways.png new file mode 100644 index 0000000..fbeab85 Binary files /dev/null and b/public/airline-logos/us-airways.png differ diff --git a/public/airline-logos/vietjet-air.png b/public/airline-logos/vietjet-air.png new file mode 100644 index 0000000..3262c96 Binary files /dev/null and b/public/airline-logos/vietjet-air.png differ diff --git a/public/airline-logos/vietnam-airlines.png b/public/airline-logos/vietnam-airlines.png new file mode 100644 index 0000000..20aafd4 Binary files /dev/null and b/public/airline-logos/vietnam-airlines.png differ diff --git a/public/airline-logos/virgin-atlantic.svg b/public/airline-logos/virgin-atlantic.svg new file mode 100644 index 0000000..e4b247a --- /dev/null +++ b/public/airline-logos/virgin-atlantic.svg @@ -0,0 +1 @@ +Virgin Atlantic \ No newline at end of file diff --git a/public/airline-logos/virgin-australia.png b/public/airline-logos/virgin-australia.png new file mode 100644 index 0000000..161a75b Binary files /dev/null and b/public/airline-logos/virgin-australia.png differ diff --git a/public/airline-logos/volaris.png b/public/airline-logos/volaris.png new file mode 100644 index 0000000..873aa2e Binary files /dev/null and b/public/airline-logos/volaris.png differ diff --git a/public/airline-logos/volotea.png b/public/airline-logos/volotea.png new file mode 100644 index 0000000..e9621de Binary files /dev/null and b/public/airline-logos/volotea.png differ diff --git a/public/airline-logos/vueling.png b/public/airline-logos/vueling.png new file mode 100644 index 0000000..dca2910 Binary files /dev/null and b/public/airline-logos/vueling.png differ diff --git a/public/airline-logos/westjet.png b/public/airline-logos/westjet.png new file mode 100644 index 0000000..9eedf4b Binary files /dev/null and b/public/airline-logos/westjet.png differ diff --git a/public/airline-logos/widera-e.png b/public/airline-logos/widera-e.png new file mode 100644 index 0000000..be10698 Binary files /dev/null and b/public/airline-logos/widera-e.png differ diff --git a/public/airline-logos/wizz-air.svg b/public/airline-logos/wizz-air.svg new file mode 100644 index 0000000..0cdc7b6 --- /dev/null +++ b/public/airline-logos/wizz-air.svg @@ -0,0 +1 @@ +Wizz Air \ No newline at end of file diff --git a/public/airline-logos/xiamen-airlines.png b/public/airline-logos/xiamen-airlines.png new file mode 100644 index 0000000..a4ee788 Binary files /dev/null and b/public/airline-logos/xiamen-airlines.png differ diff --git a/public/airline-logos/xiamenair.png b/public/airline-logos/xiamenair.png new file mode 100644 index 0000000..a4ee788 Binary files /dev/null and b/public/airline-logos/xiamenair.png differ diff --git a/public/airline-logos/zipair.png b/public/airline-logos/zipair.png new file mode 100644 index 0000000..95f5083 Binary files /dev/null and b/public/airline-logos/zipair.png differ diff --git a/src/components/map/airport-layer.tsx b/src/components/map/airport-layer.tsx index 893a981..3ef1981 100644 --- a/src/components/map/airport-layer.tsx +++ b/src/components/map/airport-layer.tsx @@ -8,6 +8,10 @@ import { CITIES, type City } from "@/lib/cities"; const SOURCE_ID = "airport-markers"; const DOTS_LAYER = "airport-dots"; +const HIT_LAYER = "airport-hit"; +const ACTIVE_SOURCE_ID = "active-airport-marker"; +const ACTIVE_RING_LAYER = "active-airport-ring"; +const ACTIVE_CORE_LAYER = "active-airport-core"; type AirportLayerProps = { activeCity: City; @@ -79,7 +83,6 @@ export function AirportLayer({ isDark, }: AirportLayerProps) { const { map, isLoaded } = useMap(); - const markerRef = useRef(null); const popupRef = useRef(null); const callbackRef = useRef(onSelectAirport); useEffect(() => { @@ -92,45 +95,122 @@ export function AirportLayer({ const m = map; const dotColor = isDark - ? "rgba(167,243,208,0.28)" - : "rgba(15,118,110,0.22)"; + ? "rgba(188,248,221,0.68)" + : "rgba(15,118,110,0.62)"; function addSourceAndLayers() { if (m.getSource(SOURCE_ID)) return; m.addSource(SOURCE_ID, { type: "geojson", data: airportGeoJson }); + m.addLayer({ + id: HIT_LAYER, + type: "circle", + source: SOURCE_ID, + paint: { + "circle-radius": ["step", ["zoom"], 8, 6, 10, 10, 12, 14, 15], + "circle-color": "rgba(255,255,255,0.01)", + "circle-opacity": 0.01, + "circle-pitch-alignment": "map", + "circle-pitch-scale": "map", + }, + }); + m.addLayer({ id: DOTS_LAYER, type: "circle", source: SOURCE_ID, paint: { - "circle-radius": [ - "step", - ["zoom"], - 0.55, - 6, - 0.8, - 10, - 1.05, - 14, - 1.35, - ], + "circle-radius": ["step", ["zoom"], 1.3, 6, 1.8, 10, 2.4, 14, 3], "circle-color": dotColor, "circle-opacity": [ "interpolate", ["linear"], ["zoom"], 2, - 0.14, + 0.44, 8, - 0.22, + 0.56, 14, - 0.34, + 0.68, ], - "circle-stroke-width": 0, + "circle-stroke-color": "rgba(255,255,255,0.18)", + "circle-stroke-width": [ + "interpolate", + ["linear"], + ["zoom"], + 2, + 0.15, + 10, + 0.3, + 14, + 0.5, + ], + "circle-pitch-alignment": "map", + "circle-pitch-scale": "map", }, }); + + if (!m.getSource(ACTIVE_SOURCE_ID)) { + m.addSource(ACTIVE_SOURCE_ID, { + type: "geojson", + data: { + type: "FeatureCollection", + features: [], + }, + }); + } + + if (!m.getLayer(ACTIVE_RING_LAYER)) { + m.addLayer({ + id: ACTIVE_RING_LAYER, + type: "circle", + source: ACTIVE_SOURCE_ID, + paint: { + "circle-radius": [ + "interpolate", + ["linear"], + ["zoom"], + 2, + 4, + 8, + 6, + 14, + 9, + ], + "circle-color": "rgba(255,255,255,0)", + "circle-stroke-color": "rgba(255,255,255,0.26)", + "circle-stroke-width": 1, + "circle-pitch-alignment": "map", + "circle-pitch-scale": "map", + }, + }); + } + + if (!m.getLayer(ACTIVE_CORE_LAYER)) { + m.addLayer({ + id: ACTIVE_CORE_LAYER, + type: "circle", + source: ACTIVE_SOURCE_ID, + paint: { + "circle-radius": [ + "interpolate", + ["linear"], + ["zoom"], + 2, + 1.6, + 8, + 2.2, + 14, + 2.8, + ], + "circle-color": "rgba(255,255,255,0.62)", + "circle-opacity": 0.95, + "circle-pitch-alignment": "map", + "circle-pitch-scale": "map", + }, + }); + } } addSourceAndLayers(); @@ -180,18 +260,22 @@ export function AirportLayer({ } } - m.on("mouseenter", DOTS_LAYER, onMouseEnter); - m.on("mouseleave", DOTS_LAYER, onMouseLeave); - m.on("click", DOTS_LAYER, onClick); + m.on("mouseenter", HIT_LAYER, onMouseEnter); + m.on("mouseleave", HIT_LAYER, onMouseLeave); + m.on("click", HIT_LAYER, onClick); return () => { m.off("style.load", addSourceAndLayers); - m.off("mouseenter", DOTS_LAYER, onMouseEnter); - m.off("mouseleave", DOTS_LAYER, onMouseLeave); - m.off("click", DOTS_LAYER, onClick); + m.off("mouseenter", HIT_LAYER, onMouseEnter); + m.off("mouseleave", HIT_LAYER, onMouseLeave); + m.off("click", HIT_LAYER, onClick); popup.remove(); try { + if (m.getLayer(ACTIVE_CORE_LAYER)) m.removeLayer(ACTIVE_CORE_LAYER); + if (m.getLayer(ACTIVE_RING_LAYER)) m.removeLayer(ACTIVE_RING_LAYER); + if (m.getSource(ACTIVE_SOURCE_ID)) m.removeSource(ACTIVE_SOURCE_ID); if (m.getLayer(DOTS_LAYER)) m.removeLayer(DOTS_LAYER); + if (m.getLayer(HIT_LAYER)) m.removeLayer(HIT_LAYER); if (m.getSource(SOURCE_ID)) m.removeSource(SOURCE_ID); } catch { /* already cleaned up */ @@ -201,26 +285,26 @@ export function AirportLayer({ useEffect(() => { if (!map || !isLoaded) return; - injectCSS(); - - const el = document.createElement("div"); - el.className = "airport-beacon"; - el.innerHTML = - '
' + - '
' + - '
' + - '
'; if (!isValidCoordinates(activeCity.coordinates)) return; - const marker = new maplibregl.Marker({ element: el }) - .setLngLat(activeCity.coordinates) - .addTo(map); - markerRef.current = marker; + const src = map.getSource(ACTIVE_SOURCE_ID) as + | maplibregl.GeoJSONSource + | undefined; + if (!src) return; - return () => { - marker.remove(); - markerRef.current = null; - }; + src.setData({ + type: "FeatureCollection", + features: [ + { + type: "Feature", + geometry: { + type: "Point", + coordinates: activeCity.coordinates, + }, + properties: {}, + }, + ], + }); }, [map, isLoaded, activeCity]); return null; diff --git a/src/components/map/flight-layers.tsx b/src/components/map/flight-layers.tsx index 52bd55b..3573f30 100644 --- a/src/components/map/flight-layers.tsx +++ b/src/components/map/flight-layers.tsx @@ -31,6 +31,66 @@ const TRACK_DAMPING = 0.18; const TRAIL_SMOOTHING_ITERATIONS = 3; const AIRCRAFT_SCENEGRAPH_URL = "/models/airplane.glb"; const AIRCRAFT_PX_PER_UNIT = 0.3; +const BASE_AIRCRAFT_SIZE = 25; +const AIRCRAFT_PICK_RADIUS_PX = 14; + +const CATEGORY_TINT: Record = { + 2: [100, 235, 180], + 3: [120, 225, 235], + 4: [255, 210, 120], + 5: [255, 185, 110], + 6: [255, 160, 120], + 7: [255, 120, 200], + 8: [140, 220, 160], + 9: [170, 210, 255], + 10: [220, 170, 255], + 11: [255, 150, 180], + 12: [180, 230, 160], + 14: [195, 165, 255], +}; + +function categorySizeMultiplier(category: number | null): number { + switch (category) { + case 2: + return 0.88; + case 3: + return 0.96; + case 4: + return 1.08; + case 5: + return 1.18; + case 6: + return 1.28; + case 7: + return 1.04; + case 8: + return 0.86; + case 9: + case 12: + return 0.8; + case 10: + return 1.15; + case 14: + return 0.72; + default: + return 1; + } +} + +function tintAircraftColor( + base: [number, number, number, number], + category: number | null, +): [number, number, number, number] { + const tint = category !== null ? CATEGORY_TINT[category] : undefined; + if (!tint) return base; + + return [ + Math.round(base[0] * 0.58 + tint[0] * 0.42), + Math.round(base[1] * 0.58 + tint[1] * 0.42), + Math.round(base[2] * 0.58 + tint[2] * 0.42), + base[3], + ]; +} const PULSE_PERIOD_MS = 7000; const RING_PERIOD_MS = 5500; @@ -543,7 +603,7 @@ export function FlightLayers({ const picked = (overlay as unknown as DeckGLOverlay).pickObject?.({ x: e.point.x, y: e.point.y, - radius: 10, + radius: AIRCRAFT_PICK_RADIUS_PX, }); if (!picked?.object) { onClick(null); @@ -562,6 +622,7 @@ export function FlightLayers({ if (!overlayRef.current) { overlayRef.current = new MapboxOverlay({ interleaved: false, + pickingRadius: AIRCRAFT_PICK_RADIUS_PX, layers: [], }); map.addControl(overlayRef.current as unknown as maplibregl.IControl); @@ -731,7 +792,7 @@ export function FlightLayers({ data: interpolated, getPosition: (d) => [d.longitude!, d.latitude!, 0], getIcon: () => "aircraft", - getSize: 20, + getSize: (d) => 20 * categorySizeMultiplier(d.category), getColor: [0, 0, 0, 60], getAngle: (d) => 360 - (d.trueTrack ?? 0), iconAtlas: atlasUrl, @@ -747,6 +808,9 @@ export function FlightLayers({ const trailMap = new Map(currentTrails.map((t) => [t.icao24, t])); const handledIds = new Set(); const trailData: TrailEntry[] = []; + const denseSubdivisions = interpolated.length > 140 ? 1 : 2; + const smoothingIterations = + interpolated.length > 220 ? 1 : TRAIL_SMOOTHING_ITERATIONS; const buildVisibleTrailPoints = ( trail: TrailEntry, @@ -777,7 +841,10 @@ export function FlightLayers({ p[1], Math.max(0, altitudeMeters[i] ?? trail.baroAltitude ?? 0), ]) as ElevatedPoint[]; - const denseBasePath = densifyElevatedPath(basePath, 2); + const denseBasePath = densifyElevatedPath( + basePath, + denseSubdivisions, + ); if ( animFlight && @@ -792,7 +859,9 @@ export function FlightLayers({ ]); const smoothed = - clipped.length < 4 ? clipped : smoothElevatedPath(clipped); + clipped.length < 4 + ? clipped + : smoothElevatedPath(clipped, smoothingIterations); return smoothed.map((p) => [p[0], p[1], Math.max(0, p[2])]); } @@ -800,11 +869,23 @@ export function FlightLayers({ const smoothed = denseBasePath.length < 4 ? denseBasePath - : smoothElevatedPath(denseBasePath); + : smoothElevatedPath(denseBasePath, smoothingIterations); return smoothed.map((p) => [p[0], p[1], Math.max(0, p[2])]); }; + const visibleTrailCache = new Map(); + const getVisibleTrailPoints = ( + trail: TrailEntry, + animFlight: FlightState | undefined, + ): ElevatedPoint[] => { + const cached = visibleTrailCache.get(trail.icao24); + if (cached) return cached; + const computed = buildVisibleTrailPoints(trail, animFlight); + visibleTrailCache.set(trail.icao24, computed); + return computed; + }; + for (const f of interpolated) { if (f.longitude == null || f.latitude == null) continue; @@ -844,7 +925,7 @@ export function FlightLayers({ }, getPath: (d) => { const animFlight = interpolatedMap.get(d.icao24); - const visiblePoints = buildVisibleTrailPoints(d, animFlight); + const visiblePoints = getVisibleTrailPoints(d, animFlight); return visiblePoints.map( (p) => [ @@ -859,7 +940,7 @@ export function FlightLayers({ }, getColor: (d) => { const animFlight = interpolatedMap.get(d.icao24); - const visiblePoints = buildVisibleTrailPoints(d, animFlight); + const visiblePoints = getVisibleTrailPoints(d, animFlight); const len = visiblePoints.length; return visiblePoints.map((point, i) => { const tVal = len > 1 ? i / (len - 1) : 1; @@ -988,10 +1069,18 @@ export function FlightLayers({ const yaw = -(d.trueTrack ?? 0); return [pitch, yaw, 90]; }, - getColor: (d) => - altColors ? altitudeToColor(d.baroAltitude) : defaultColor, + getColor: (d) => { + const base = altColors + ? altitudeToColor(d.baroAltitude) + : defaultColor; + return tintAircraftColor(base, d.category); + }, scenegraph: AIRCRAFT_SCENEGRAPH_URL, - sizeScale: 25, + getScale: (d) => { + const scale = categorySizeMultiplier(d.category); + return [scale, scale, scale]; + }, + sizeScale: BASE_AIRCRAFT_SIZE, sizeMinPixels: AIRCRAFT_PX_PER_UNIT, sizeMaxPixels: AIRCRAFT_PX_PER_UNIT, _lighting: "pbr", diff --git a/src/components/ui/flight-card.tsx b/src/components/ui/flight-card.tsx index 5a0d7c1..20bd1c6 100644 --- a/src/components/ui/flight-card.tsx +++ b/src/components/ui/flight-card.tsx @@ -1,5 +1,7 @@ "use client"; +import { useState } from "react"; +import Image from "next/image"; import { motion, AnimatePresence } from "motion/react"; import { Plane, @@ -20,17 +22,39 @@ import { headingToCardinal, } from "@/lib/flight-utils"; import { lookupAirline, parseFlightNumber } from "@/lib/airlines"; +import { aircraftTypeHint } from "@/lib/aircraft"; +import { airlineLogoCandidates } from "@/lib/airline-logos"; type FlightCardProps = { flight: FlightState | null; onClose: () => void; }; +const loadedLogoUrls = new Set(); + export function FlightCard({ flight, onClose }: FlightCardProps) { const airline = flight ? lookupAirline(flight.callsign) : null; const flightNum = flight ? parseFlightNumber(flight.callsign) : null; + const company = + airline ?? (flight ? `${flight.originCountry} operator` : null); + const model = flight ? aircraftTypeHint(flight.category) : null; + const logoCandidates = airlineLogoCandidates(airline); const heading = flight?.trueTrack ?? null; const cardinal = heading !== null ? headingToCardinal(heading) : null; + const [logoIndexByAirline, setLogoIndexByAirline] = useState< + Record + >({}); + const [logoLoadedByKey, setLogoLoadedByKey] = useState< + Record + >({}); + const airlineKey = airline ?? "__none__"; + const logoIndex = logoIndexByAirline[airlineKey] ?? 0; + const logoLoadKey = `${airlineKey}:${logoIndex}`; + const logoUrl = logoCandidates[logoIndex] ?? null; + const logoLoaded = + (logoUrl ? loadedLogoUrls.has(logoUrl) : false) || + (logoLoadedByKey[logoLoadKey] ?? false); + const showLogo = Boolean(logoUrl); return ( @@ -46,17 +70,57 @@ export function FlightCard({ flight, onClose }: FlightCardProps) { damping: 28, mass: 0.8, }} - className="w-64 sm:w-72" + className="w-72 sm:w-80" role="complementary" aria-label="Selected flight details" aria-live="polite" >
-
-
- - +
+
+ {showLogo ? ( + + {!logoLoaded && ( + + ) : ( + + )}

@@ -79,11 +143,14 @@ export function FlightCard({ flight, onClose }: FlightCardProps) {

- {airline && ( + {company && (

- {airline} + {company} + {model ? ( + · {model} + ) : null}

)} diff --git a/src/lib/aircraft.ts b/src/lib/aircraft.ts new file mode 100644 index 0000000..e094bf7 --- /dev/null +++ b/src/lib/aircraft.ts @@ -0,0 +1,36 @@ +const CATEGORY_LABELS: Record = { + 2: "Light aircraft", + 3: "Small aircraft", + 4: "Large aircraft", + 5: "High vortex large", + 6: "Heavy aircraft", + 7: "High-performance aircraft", + 8: "Rotorcraft", + 9: "Glider / sailplane", + 10: "Lighter-than-air", + 11: "Parachutist / skydiver", + 12: "Ultralight / hang-glider", + 13: "Reserved", + 14: "Unmanned aerial vehicle", + 15: "Space / trans-atmospheric", + 16: "Surface emergency vehicle", + 17: "Surface service vehicle", + 18: "Point obstacle", + 19: "Cluster obstacle", + 20: "Line obstacle", +}; + +export function categoryToAircraftLabel(category: number | null): string | null { + if (category === null) return null; + return CATEGORY_LABELS[category] ?? null; +} + +export function aircraftModelHint(category: number | null): string | null { + const label = categoryToAircraftLabel(category); + if (!label) return null; + return `${label} class`; +} + +export function aircraftTypeHint(category: number | null): string | null { + return aircraftModelHint(category); +} diff --git a/src/lib/airline-logos.ts b/src/lib/airline-logos.ts new file mode 100644 index 0000000..4f284b6 --- /dev/null +++ b/src/lib/airline-logos.ts @@ -0,0 +1,77 @@ +function normalizeAirlineText(value: string): string { + return value + .normalize("NFD") + .replace(/[\u0300-\u036f]/g, "") + .toLowerCase() + .trim(); +} + +function toAirlineLogoSlug(airlineName: string): string { + return normalizeAirlineText(airlineName) + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-+|-+$/g, ""); +} + +function toAirlineAliasKey(airlineName: string): string { + return normalizeAirlineText(airlineName).replace(/[^a-z0-9]+/g, ""); +} + +const LOGO_SLUG_ALIASES: Record = { + allnipponairways: "all-nippon-airways", + ana: "all-nippon-airways", + jal: "japan-airlines", + elal: "el-al", + itaairways: "ita-airways", + latam: "latam-airlines", + latamairlines: "latam-airlines", + norwegian: "norwegian-air-shuttle", + swiss: "swiss", + tapairportugal: "tap-air-portugal", + vietjetair: "vietjet-air", + xiamenair: "xiamenair", + pakistaninternationalairlines: "pakistan-international-airlines", + pakistanintlairlines: "pakistan-int-l-airlines", + indigo: "indigo", + indigoairlines: "indigo", + goindigo: "indigo", +}; + +function buildSlugVariants(baseSlug: string): string[] { + if (!baseSlug) return []; + + const variants = new Set([baseSlug]); + variants.add(baseSlug.replace(/-airlines$/, "")); + variants.add(baseSlug.replace(/-airline$/, "")); + variants.add(baseSlug.replace(/-airways$/, "")); + variants.add(baseSlug.replace(/-air$/, "")); + variants.add(baseSlug.replace(/-international$/, "")); + variants.add(baseSlug.replace(/-int-l$/, "")); + variants.add(baseSlug.replace(/-intl$/, "")); + + return Array.from(variants).filter(Boolean); +} + +export function airlineLogoCandidates(airlineName: string | null): string[] { + if (!airlineName) return []; + + const slug = toAirlineLogoSlug(airlineName); + const aliasKey = toAirlineAliasKey(airlineName); + const aliasSlug = LOGO_SLUG_ALIASES[aliasKey] ?? null; + + const orderedSlugs = Array.from( + new Set([ + ...buildSlugVariants(slug), + ...(aliasSlug ? buildSlugVariants(aliasSlug) : []), + ]), + ); + + if (orderedSlugs.length === 0) return []; + + const candidates: string[] = []; + for (const s of orderedSlugs) { + candidates.push(`/airline-logos/${s}.svg`); + candidates.push(`/airline-logos/${s}.png`); + } + + return candidates; +} diff --git a/src/lib/airlines.ts b/src/lib/airlines.ts index eb70723..80f396c 100644 --- a/src/lib/airlines.ts +++ b/src/lib/airlines.ts @@ -5,10 +5,17 @@ type AirlineInfo = { const ICAO_AIRLINES: Record = { AAL: { name: "American Airlines" }, AAR: { name: "Asiana Airlines" }, + AAY: { name: "Allegiant Air" }, + ABY: { name: "Air Arabia" }, ACA: { name: "Air Canada" }, + AEA: { name: "Air Europa" }, AEE: { name: "Aegean Airlines" }, + AFL: { name: "Aeroflot" }, AFR: { name: "Air France" }, AIC: { name: "Air India" }, + AIQ: { name: "AirAsia" }, + ASL: { name: "Air Serbia" }, + ANE: { name: "Air Nostrum" }, AIJ: { name: "Interjet" }, AJT: { name: "Amerijet" }, ALK: { name: "SriLankan Airlines" }, @@ -18,23 +25,40 @@ const ICAO_AIRLINES: Record = { ASA: { name: "Alaska Airlines" }, AUA: { name: "Austrian Airlines" }, AVA: { name: "Avianca" }, + ARG: { name: "Aerolíneas Argentinas" }, AWE: { name: "US Airways" }, + AXM: { name: "AirAsia" }, + AXB: { name: "Air India Express" }, AZA: { name: "Alitalia / ITA Airways" }, + AZU: { name: "Azul" }, BAW: { name: "British Airways" }, BEL: { name: "Brussels Airlines" }, BER: { name: "Air Berlin" }, + BTI: { name: "Air Baltic" }, CAL: { name: "China Airlines" }, CCA: { name: "Air China" }, + CEB: { name: "Cebu Pacific" }, CES: { name: "China Eastern" }, + CHH: { name: "Hainan Airlines" }, + CFG: { name: "Condor" }, CLH: { name: "Lufthansa CityLine" }, CMP: { name: "Copa Airlines" }, CPA: { name: "Cathay Pacific" }, + CRK: { name: "Hong Kong Airlines" }, + CQH: { name: "Spring Airlines" }, + CSC: { name: "Sichuan Airlines" }, CSN: { name: "China Southern" }, + CSZ: { name: "Shenzhen Airlines" }, CTN: { name: "Croatia Airlines" }, CXA: { name: "Xiamen Airlines" }, + DAH: { name: "Air Algerie" }, DAL: { name: "Delta Air Lines" }, + DKH: { name: "Juneyao Airlines" }, + DAT: { name: "Brussels Airlines" }, + DLA: { name: "Air Dolomiti" }, DLH: { name: "Lufthansa" }, EIN: { name: "Aer Lingus" }, + ENY: { name: "Envoy Air" }, EJU: { name: "easyJet Europe" }, ELY: { name: "El Al" }, ETD: { name: "Etihad Airways" }, @@ -42,41 +66,68 @@ const ICAO_AIRLINES: Record = { EVA: { name: "EVA Air" }, EWG: { name: "Eurowings" }, EZY: { name: "easyJet" }, + EXS: { name: "Jet2" }, + FFT: { name: "Frontier Airlines" }, FDX: { name: "FedEx Express" }, + FDB: { name: "flydubai" }, FIN: { name: "Finnair" }, FJI: { name: "Fiji Airways" }, GAF: { name: "German Air Force" }, + GFA: { name: "Gulf Air" }, GIA: { name: "Garuda Indonesia" }, + GLO: { name: "GOL" }, GTI: { name: "Atlas Air" }, HAL: { name: "Hawaiian Airlines" }, + HKE: { name: "Hong Kong Express" }, HVN: { name: "Vietnam Airlines" }, + IGO: { name: "IndiGo" }, IBE: { name: "Iberia" }, IBK: { name: "Norwegian Air Int'l" }, + IBB: { name: "Binter Canarias" }, + IBU: { name: "IndiGo" }, ICE: { name: "Icelandair" }, + IBS: { name: "Iberia Express" }, JAL: { name: "Japan Airlines" }, JBU: { name: "JetBlue" }, + JJA: { name: "Jeju Air" }, + JJP: { name: "Jetstar" }, JST: { name: "Jetstar" }, + JZA: { name: "Air Canada Jazz" }, KAL: { name: "Korean Air" }, KLM: { name: "KLM" }, + KZR: { name: "Air Astana" }, LAN: { name: "LATAM Airlines" }, + LGL: { name: "Luxair" }, + LPE: { name: "LATAM Perú" }, LOT: { name: "LOT Polish Airlines" }, MAU: { name: "Air Mauritius" }, MAS: { name: "Malaysia Airlines" }, MSR: { name: "EgyptAir" }, NAX: { name: "Norwegian Air Shuttle" }, NKS: { name: "Spirit Airlines" }, + OMA: { name: "Oman Air" }, + OZW: { name: "SkyWest Airlines" }, PAL: { name: "Philippine Airlines" }, PIA: { name: "Pakistan Int'l Airlines" }, + PGT: { name: "Pegasus Airlines" }, + POE: { name: "Porter Airlines" }, QFA: { name: "Qantas" }, QTR: { name: "Qatar Airways" }, RAM: { name: "Royal Air Maroc" }, RJA: { name: "Royal Jordanian" }, + RPA: { name: "Republic Airways" }, ROT: { name: "TAROM" }, RYR: { name: "Ryanair" }, SAS: { name: "Scandinavian Airlines" }, + SCO: { name: "Scoot" }, + SDM: { name: "Rossiya" }, + SCX: { name: "Sun Country Airlines" }, + SEJ: { name: "SpiceJet" }, + SEH: { name: "Sky Express" }, SAA: { name: "South African Airways" }, SIA: { name: "Singapore Airlines" }, SKW: { name: "SkyWest Airlines" }, + SKY: { name: "Skymark Airlines" }, SVA: { name: "Saudia" }, SWA: { name: "Southwest Airlines" }, SWR: { name: "Swiss Int'l Air Lines" }, @@ -84,16 +135,26 @@ const ICAO_AIRLINES: Record = { TAP: { name: "TAP Air Portugal" }, THA: { name: "Thai Airways" }, THY: { name: "Turkish Airlines" }, + TOM: { name: "TUI Airways" }, + TRA: { name: "Transavia" }, + TSC: { name: "Air Transat" }, + TWB: { name: "Tway Airlines" }, TUI: { name: "TUI Airways" }, TVF: { name: "Transavia France" }, UAE: { name: "Emirates" }, UAL: { name: "United Airlines" }, + USA: { name: "US Airways" }, UPS: { name: "UPS Airlines" }, + VJC: { name: "VietJet Air" }, VIR: { name: "Virgin Atlantic" }, + VOE: { name: "Volotea" }, + VOI: { name: "Volaris" }, VOZ: { name: "Virgin Australia" }, VLG: { name: "Vueling" }, WJA: { name: "WestJet" }, + WIF: { name: "Widerøe" }, WZZ: { name: "Wizz Air" }, + XAX: { name: "AirAsia X" }, }; export function lookupAirline(callsign: string | null): string | null { diff --git a/src/lib/airports.ts b/src/lib/airports.ts index acd23fb..4e4d396 100644 --- a/src/lib/airports.ts +++ b/src/lib/airports.ts @@ -72510,43 +72510,91 @@ export const AIRPORTS: Airport[] = [ }, ]; +type SearchAirportEntry = { + airport: Airport; + iata: string; + city: string; + name: string; + country: string; +}; + +const AIRPORT_SEARCH_INDEX: SearchAirportEntry[] = AIRPORTS.map((airport) => ({ + airport, + iata: airport.iata.toLowerCase(), + city: airport.city.toLowerCase(), + name: airport.name.toLowerCase(), + country: airport.country.toLowerCase(), +})); + +const IATA_LOOKUP = new Map(AIRPORTS.map((airport) => [airport.iata, airport])); + +const SEARCH_CACHE_LIMIT = 80; +const SEARCH_CACHE = new Map(); + +function getCachedAirportSearch(query: string): Airport[] | undefined { + const cached = SEARCH_CACHE.get(query); + if (!cached) return undefined; + + SEARCH_CACHE.delete(query); + SEARCH_CACHE.set(query, cached); + return cached; +} + +function setCachedAirportSearch(query: string, airports: Airport[]) { + if (SEARCH_CACHE.has(query)) SEARCH_CACHE.delete(query); + SEARCH_CACHE.set(query, airports); + + if (SEARCH_CACHE.size > SEARCH_CACHE_LIMIT) { + const oldest = SEARCH_CACHE.keys().next().value; + if (oldest) SEARCH_CACHE.delete(oldest); + } +} + export function searchAirports(query: string, limit = 20): Airport[] { const q = query.toLowerCase().trim(); if (!q) return []; + const cached = getCachedAirportSearch(q); + if (cached) return cached.slice(0, limit); + const exact: Airport[] = []; const iataPrefix: Airport[] = []; const cityStart: Airport[] = []; const nameStart: Airport[] = []; const contains: Airport[] = []; - for (const a of AIRPORTS) { - const iata = a.iata.toLowerCase(); - const city = a.city.toLowerCase(); - const name = a.name.toLowerCase(); - const country = a.country.toLowerCase(); + for (const entry of AIRPORT_SEARCH_INDEX) { + const { airport, iata, city, name, country } = entry; - if (iata === q) exact.push(a); - else if (iata.startsWith(q)) iataPrefix.push(a); - else if (city.startsWith(q)) cityStart.push(a); - else if (name.startsWith(q)) nameStart.push(a); + if (iata === q) { + if (exact.length < limit) exact.push(airport); + } else if (iata.startsWith(q)) { + if (iataPrefix.length < limit) iataPrefix.push(airport); + } else if (city.startsWith(q)) { + if (cityStart.length < limit) cityStart.push(airport); + } else if (name.startsWith(q)) { + if (nameStart.length < limit) nameStart.push(airport); + } else if (city.includes(q) || name.includes(q) || country.startsWith(q)) - contains.push(a); + if (contains.length < limit) contains.push(airport); } - return [ + const results = [ ...exact, ...iataPrefix, ...cityStart, ...nameStart, ...contains, - ].slice(0, limit); + ]; + + setCachedAirportSearch(q, results); + return results.slice(0, limit); } export function findByIata(iata: string): Airport | undefined { const code = iata.toUpperCase(); - return AIRPORTS.find((a) => a.iata === code); + return IATA_LOOKUP.get(code); } export function airportToCity(airport: Airport): City { diff --git a/src/lib/opensky.ts b/src/lib/opensky.ts index 9b26cb0..f9f21b2 100644 --- a/src/lib/opensky.ts +++ b/src/lib/opensky.ts @@ -18,6 +18,7 @@ export type FlightState = { squawk: string | null; spiFlag: boolean; positionSource: number; + category: number | null; }; type OpenSkyResponse = { @@ -44,6 +45,7 @@ function parseStates(raw: OpenSkyResponse): FlightState[] { squawk: s[14] as string | null, spiFlag: s[15] as boolean, positionSource: s[16] as number, + category: (s[17] as number | null) ?? null, })) .filter( (f) => @@ -75,7 +77,7 @@ export async function fetchFlightsByBbox( const lo0 = clamp(lomin, -180, 180); const lo1 = clamp(lomax, -180, 180); - const url = `${OPENSKY_API}/states/all?lamin=${la0}&lamax=${la1}&lomin=${lo0}&lomax=${lo1}`; + const url = `${OPENSKY_API}/states/all?lamin=${la0}&lamax=${la1}&lomin=${lo0}&lomax=${lo1}&extended=1`; const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);