Easy-to-manage touch screens
Free, open-source touch screens. Guided USB setup, a drag-and-drop editor and local control. Made for Home Assistant.
Log your flights and watch your own flight map take shape.
LifestyleTypeScript · CSSGitHub @PozkliiOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
A web app for logging and reviewing flights, built with Next.js, Supabase, and deployed on Vercel.
• Next.js (App Router, TypeScript, Tailwind CSS)
• Supabase for auth and data storage
• Vercel for hosting, deployed from this GitHub repo
2. Create a Supabase project at supabase.com, then copy .env.local.example to .env.local and fill in your project's URL and anon key (Project Settings → API):
3. Create the flights table and its row-level security policies by running supabase/schema.sql in your project's SQL editor (Supabase dashboard → SQL Editor → New query).Read the full README ↗A web app for logging and reviewing flights, built with Next.js, Supabase, and deployed on Vercel.
Install dependencies:
npm install
Create a Supabase project at supabase.com, then copy .env.local.example to .env.local and fill in your project's URL and anon key (Project Settings → API):
cp .env.local.example .env.local
Create the flights and user_preferences tables and their row-level security policies by running supabase/schema.sql in your project's SQL editor (Supabase dashboard → SQL Editor → New query). Already set up from an earlier version? Run supabase/migrations/20260926_ratings_and_favourites.sql instead — it adds the flights.rating column and the user_preferences table, and is safe to run more than once. Do this before deploying the ratings/favourites code, since the flights page selects the new column. Then run supabase/migrations/20261005_flight_details.sql, which adds the optional flight details (takeoff and landing times, landing rate, conditions, weather; its fuel columns are no longer used); until it's run the log still works, just without them (saving a flight with them says so). Then run supabase/migrations/20261006_sharing_and_confirmed_flights.sql, which adds share links and confirmed flights saved with the account (the flight_shares and confirmed_flights tables, with row-level security, and the shared_flight_map function that share pages read through); until it's run, the Stats page says sharing needs it and confirmed flights stay in the browser.
In the Supabase dashboard, under Authentication → URL Configuration, set the Site URL to http://localhost:3000 for local development (and add your Vercel domain once deployed). Email/password sign-up is enabled by default and requires confirming a link sent by email before a user can sign in.
Run the dev server:
npm run dev
Open http://localhost:3000.
Import this repository in Vercel, add the same two environment variables from .env.local in the project's settings, and deploy. Every push to main will trigger a new deployment.
After deploying, add your Vercel URL (and any preview URLs) to Supabase's Authentication → URL Configuration → Redirect URLs so email confirmation links work in production.
Free-tier Supabase projects are paused after a period of inactivity. While paused, the auth API is unreachable and sign-in/sign-up fail with a "can't reach the sign-in service" message — restore the project from the Supabase dashboard (it takes a few minutes to come back up).
/) with a moving fractal-flame background. Each visit it's one of three hand-picked flames (rose and blue veils, orange and blue embers, or a blue and teal vortex). Flames generated in their style (flame-random.ts) only show when asked for: random transforms from the same kinds of variations, in a palette of two related hues on a near-black ground, framed close, each candidate test-rendered on a small grid and rejected if it's a speck, a line, fog or a solid blob. A generated flame follows from a seed, shown faintly in the corner ("Background no. …"), so ?bg=random asks for a new one and ?seed=<n> brings one back. Either kind is rendered live in WebGL2 (flame-gl.ts: the same chaos game the stills in public/sky/flame-*.webp were drawn with, run on the GPU each frame with every transform slowly swaying, so the shapes curl and morph; parameters in flame-params.ts), under a pulsing glow layer and twinkling motes orbiting the middle (flame-motes.tsx). ?sharp=1 previews crisper versions (sharper stills, the live flame at full resolution without blur, no glow layer). ?style=lines previews a sharper line style: the flame tone-mapped so only its densest crests light up, as fine bright filaments over dark space, the whole flame floating (drifting, bobbing and breathing slowly) and the motes rising like dust; it combines with ?bg= and ?seed=. The still shows first and stays wherever the live flame can't run (no WebGL2, a device too slow for it, or prefers-reduced-motion, which also stills everything else). ?bg=veil, ?bg=ember or ?bg=vortex picks one, and ?bg=golden shows the earlier golden-hour sky — a low sun just off the top-left corner, sun-lit cumulus and slowly breathing light shafts — and an animated gold wireframe turbofan that draws itself in, ring by ring from the intake back, grows its fan, spools up and flies nearly level, facing right, with its exhaust streaming off to the left (canvas 2D, engine-fx.ts; shown as a still under prefers-reduced-motion). The engine is the hero's centrepiece, in the middle of the first screen, with the heading (and signing in, and the app's sections) and the feature blurbs to its left, and the example-flight tour on its own to its right (stacked on phones: heading, engine, tour, blurbs). Under the heading are Flight Log, Flight Map and Flight Plan buttons (app-links.tsx): greyed out behind a lock with a "Sign in to use these" hint for visitors, working links once signed in (when the Sign in button also goes). The same three links (Flight Map, Flight Log, Flight Plan, with the page you're on marked; just Map, Log and Plan on phones) sit at the top right of the header when signed in. Signed in, the Flight Map is home: signing in lands there, and the bare landing page () redirects to it (with a query, e.g. a background preview such as or , the landing page still shows). Each is its own page: the Flight Map () has the globe (or 2D map) and the selected flight's summary card, and nothing else; the Flight Log () has logging a new flight, your favourites and flying summary, and the list of flights (searchable by airport, city, aircraft, airline or notes; filterable by aircraft, airline, airport and year; sortable by date, length, rating or softest landing, all kept in the address so a filtered view can be bookmarked; each flight editable in place, every field filled in), with the page's heading centred and the picked flight's summary card under it (choosing one scrolls up to it; until then a line above the list says how) and a "Show on Flight Map" link that opens it there (); Flight Plan is , which opens with a From and To route: the great-circle distance between two airports (nm, km and miles), the initial heading and a flight time (), plus Airline and Aircraft fields: with a known aircraft the flight time is estimated from its cruise speed, otherwise at 450 kt. on a real-world route or a challenging approach loads it into this panel (From, To, airline and aircraft, all still editable), scrolling up to it with a brief highlight; on a live-weather challenge sets it as the destination. Once the route has an aircraft, sets it as your confirmed flight, shown at the top of the planner (kept in this browser, so it's still there after a reload or a session in the sim; confirming another replaces it). From there, opens its Flight Log entry already filled in from the plan (today's date, route, airline, aircraft and the planned time as the hours, with a one-click "time since confirming" option), ready for notes and a rating and to be added to the log; puts it back into the route panel, and drops it (, ). To the engine's right, an example-flight tour shows an endless stream of generated example flights on a summary card, a new one every 7 seconds, with a mini globe beside the figures showing the same Esri World Imagery as the Flight Map ( on a canvas 2D: fetches the imagery's Web Mercator tiles at the detail the zoom needs and projects them onto the globe pixel by pixel, credited under it; until tiles arrive, or if they can't, the earlier flat style shows instead, light blue seas and dark land drawn with from Natural Earth's 1:110m outlines in ) that turns to face each route, zooming in to fit it (a short hop fills a good part of the globe, seen through a round window; a long-haul flight keeps the whole globe, and between two far-apart short hops it zooms out on the way), and draws it in, then fills it in brightly from the departure airport over and over, like the flight's progress; the feature blurbs sit under the heading. The globe is as big as the card and the screen's height allow (up to 184px). They're built from a table of real airlines on routes they're known for, with aircraft types they typically fly there, plus a few private/GA hops (); the generator () randomizes direction, aircraft, date (any day from X-Plane 12's release, 17 December 2022, to the visitor's own today, so the range grows each day — dated in their browser, since the page itself is prerendered) and notes (two sentences from different phases of flight, drawn from a pool of templates in that avoids phrasings already on screen and airline-only details on private flights), and estimates flight time from the real great-circle distance and the aircraft's cruise speed. Airport coordinates come from the bundled OurAirports lookup, and routes crossing the antimeridian are skipped. A summary card (the same one as the flights page, minus thumbnail and close) shows its route, distance, flight time, takeoff and landing times (plain clock times, generated around the flight time, with taxi times that shrink for short hops), date, aircraft, airline and notes. On short desktop screens the heading tightens up so it all still fits. The tour can be paused. On the Flight Map, the globe (or 2D map) fills the whole page under the header, edge to edge with no frame, so zooming in or flying into an airport just fills more of the screen; on a wide screen the header and the selected flight's card sit over its left side, over a fade that keeps them readable, with the globe centred in the space beside them (map padding), and the Globe/2D toggle and route colours top right; on a phone they stack above it. Selecting a flight zooms (looking straight down) to fit its whole route, great-circle bulge and airport labels included, beside the card; a route too long to show any closer than the whole globe (London–Sydney, say) turns the globe to face it instead, and deselecting returns to the whole globe. On a PC everything fits in one screenful with no scrolling; the engine shrinks on shorter screens to make room. It all sits on one continuous sky. The sky is pre-rendered into two images in (the sky itself, and the light shafts, screen-blended on top) rather than generated in the browser, which would cost a noticeable pause on slower devices; on portrait screens it's shown across the top at close to its designed scale and fades into a matching base colour, rather than zooming into the sun. It's a deliberately single theme rather than adapting to light/dark preference.Bundled in src/lib/data/, generated by the scripts in scripts/:
airports.json / airport-details.json — OurAirports (public domain). scripts/augment-airport-data.mjs adds each airport's country and OurAirports ident to the existing lookup (without changing its keys, names or coordinates, so logged flights keep resolving) and writes elevation, type, codes, Wikipedia link and runways to airport-details.json. Re-run it with a fresh airports.csv/runways.csv download to update.airlines.json — airline names and IATA/ICAO codes from OpenFlights (Open Database License), built by scripts/build-airline-data.mjs.src/lib/airport-trivia.ts) but switched off for now: neither the pop-up when a flight is opened nor the planner's "did you know?" box is shown/?bg=ember?style=lines/flights/log/flights?flight=<id>/planroute-planner.tsxconfirmed-flight.tsxconfirmed-flight-store.tsmini-globe.tsxsatellite-tiles.tsland-shapes.tssrc/lib/example-flights/routes.tssrc/lib/example-flights/generate.tsnotes.tspublic/sky//login)/log) and Flight Map (/flights) pages for logging and reviewing your flights (date, airline, aircraft, route, hours, notes, and optional details, always shown in the forms: takeoff and landing times, typed freely (14:05, 1405, 14.05 or 945 all work, tidied to 24-hour HH:MM) with the time between them offered as the hours; landing rate in fpm; day, night or dawn/dusk; and the weather, picked from a list such as clear skies, overcast, thunderstorms or strong crosswind; any of them can be emptied, or all removed with Clear details, and saving removes them from the flight), backed by a flights table scoped to each user via row-level securitySATELLITE_TILES and STYLE_URL in flight-globe.tsx). Idle at the overview zoom, it spins slowly on its own (nudging the center longitude every frame, not the bearing, so it reads as the Earth turning rather than the camera orbiting) — paused for the duration of any drag/rotate/pitch gesture or programmatic camera move, and skipped entirely under prefers-reduced-motion. Flight routes are drawn with deck.gl's ArcLayer, composited into the MapLibre render pass via @deck.gl/mapbox's MapboxOverlay (interleaved: true), styled as glowing arcs in one flat color end to end (a wide, low-opacity layer under a thin, bright one approximates the glow, since deck.gl has no native bloom) — airports have no visible marker (a flat disc on the globe's surface z-fights with it and flickers), but a fully transparent ScatterplotLayer keeps them clickable, since deck.gl picks by its own picking colors rather than fill alpha. The selected route is drawn as its own pair of layers with constant colors/widths rather than per-arc accessor functions — deck.gl only re-runs accessor functions when the data or an updateTriggers entry changes, so a function would keep drawing the old color/selection. Clicking a route highlights it on the globe (with its two airports' city/code labels), highlights the matching entry in the flight log below, and vice versa, and flies the camera in on the two airports with a 55° pitch; deselecting eases back out to the full overview. Clicking an individual airport marker flies in further still (zoom 15, 60° pitch) — close enough for MapLibre's native 3D buildings to render, showing that airport's actual OpenStreetMap building footprints in 3D rather than a generic marker. Airport coordinates come from a bundled ICAO/IATA lookup, derived from OurAirports (public domain) — no coordinates to enter manually. On the flights page, a color swatch above the globe lets you pick the color used for every route in place of the default cyan, applied immediately without moving the camera. Selecting a flight (on the globe or in the log) pulses its route's glow while the camera flies in, and animates a details card into the globe band: a thumbnail, the route's city names, great-circle distance (haversine, in nm and km), logged flight time, date, aircraft, airline and the flight's notes — the numbers count up from zero. An always-visible Add/Change thumbnail button on the card opens a picker to choose one of the flight's uploaded images or upload a new one; the choice is stored as its own copy at {user id}/thumbnails/{flight id}/… in the flight-media bucket (already covered by its per-user storage policies, so no schema change), replacing the previous one. Without a chosen thumbnail the card falls back to the flight's first uploaded image, then to a drawn route graphic. All of it is skipped under prefers-reduced-motion — remembered in localStorage, so it's per-browser rather than synced to your account.
setLayoutProperty(id, "visibility", "none")) except water (so it still reads as a globe), the widened fill-extrusion buildings above, and anything on the OpenMapTiles aeroway / aerodrome_label source-layers — an airport's physical layout (runways, taxiways, aprons/stands) and name label. Everything else (place/road/POI labels, landcover/landuse texture, roads, boundaries, hillshading) is otherwise just clutter at globe scale.maplibre-gl@5, not the current v6 — v6 changed how it bundles its background Web Worker (used to process map data off the main thread), and that pattern doesn't get picked up correctly by Next.js's Turbopack: no worker is ever created, so every GeoJSON layer (the flight routes and airport markers) silently renders nothing, with no error. v5 doesn't hit this. Worth re-testing after a Turbopack or MapLibre update in case it's since been fixed upstream, but don't bump the major version without confirming flights actually render on the globe first.ArcLayer needs parameters: { cullMode: "none" } under the globe projection — GlobeView back-face-culls by default, which otherwise hides most of an arc's tube geometry depending on viewing angle.click event calling overlay.pickObject() directly, rather than a layer's own onClick prop — in interleaved: true mode, MapLibre owns input handling and only forwards a subset of events to deck.gl, so a layer's onClick doesn't reliably fire from real clicks even though picking itself works.cameraForBounds() to fit the two selected airports (falling back to a simple midpoint if it returns nothing, e.g. for antipodal-ish points), rather than hand-computing zoom from the coordinate spread.fill-extrusion building layer(s) have their zoom range explicitly widened on load (map.setLayerZoomRange(id, 0, 24)) so they're not gated behind whatever minzoom the upstream style tuned for street-level browsing — the actual level of detail still depends on how well OpenStreetMap has mapped that specific airport's buildings; some are well-mapped with individual terminal footprints and heights, others may show little more than the runway/tarmac outline.flight-media) scoped to each usersrc/lib/airlines.ts), and logos load from public CDNs keyed by that code (pics.avs.io, then images.kiwi.com), falling back to a monogram. Ambiguous names get the monogram rather than a guessed logo. On the flights page, the selected flight's card is the focus: wider, with a glow in its route colour, and headed by the airline with its logo shown large; a flight without a photo gets a block in its route colour. Choosing a flight from the log scrolls its card into view, and the other routes fade back so the selected one stands out. The flights can be shown on the globe or a flat 2D map with straight route lines (remembered per browser), in one of seven route colours.user_preferences), the airlines you fly with (logos and flight counts, favourite starred) and the countries your flights have touched./plan, signed in only), personalised from your log (most-used airports as quick picks, your favourite or most-flown aircraft, routes you've already flown), with three tabs:
/plan?airport=CODE), or pick one of your most-used airports: its name, codes, and a note if it's a famously challenging airport. Its live METAR is opt-in — Show live METAR reveals it (and only then fetches it), staying on for other airports until hidden — decoded into flight category, wind, visibility, cloud, weather, temperature, QNH and the best-aligned runway's headwind/crosswind. METARs come from NOAA's Aviation Weather Center via /api/metar (cached 5 minutes) and are decoded by src/lib/metar.ts from the raw report text.A20N, B738, Zibo 737-800, Q400…) to see real routes airlines fly with it, from the route table in src/lib/example-flights/routes.ts (also used by the landing page tour), with distance, estimated block time, and badges for your favourite airline, challenging airports and routes you've already flown. Defaults to your favourite (or most-flown) aircraft./api/weather-challenges over the curated airports plus a watchlist of big hubs; and a curated list of famously challenging approaches (Lukla, Paro, Saba, Courchevel, St Barths, Madeira, Gibraltar, Queenstown…) with why, difficulty, elevation and runway length, and a real route in to try. The live list is only fetched once the tab is opened./stats, signed in only) — headline figures (flights, hours, distance, countries, continents), hours per month over the last year (a column chart, its figures on hover and as a table), most-flown aircraft and most-visited airports, the farthest and longest flights, and 15 achievements with progress towards each (src/lib/flight-stats.ts): first flight, 10/50/100 flights, 100 hours, 100 hours in type, all six continents, 10 countries, Lukla landed, five famously challenging airports, a 5,000 nm flight, the Earth's circumference in all, five night flights, a dawn or dusk flight, and a landing of 60 fpm or less./share/<id>, an unguessable id) to your globe, routes and stats, with a name of your choosing shown on it; copy it, replace it with a new one (the old one stops working) or stop sharing. Visitors see flights' dates, routes, aircraft, hours and details, never notes, photos or your account; the page reads them through a database function (shared_flight_map) rather than the tables, and asks search engines not to index it./log/export): every flight and its details, oldest first, ready for a spreadsheet.