fix: polish pass for traffic, nav, icons, contact email reveal and dashcam robustness

This commit is contained in:
2026-10-11 15:11:44 +03:00
parent ca12cfda44
commit 1326cee886
49 changed files with 809 additions and 226 deletions

View File

@@ -1,5 +1,6 @@
import { bucharestClock } from '@/lib/operations';
import { maintenanceClock } from '@/journey/maintenance-progress';
import { quantiseRoute } from '@/journey/tab-feed';
/**
* DASHCAM telemetry and the postcard (lazy dashcam chunk only). Every value
@@ -27,10 +28,9 @@ export const DASHCAM_COORDINATES = "N44°26' E26°06'";
export const POSTCARD_WIDTH = 1600;
export const POSTCARD_HEIGHT = 900;
export const routePercent = (progress: number): number =>
Math.round(Math.min(1, Math.max(0, Number.isFinite(progress) ? progress : 0)) * 100);
export const chapterLabel = (chapter: string): string => chapter.replaceAll('-', ' ').toUpperCase();
/** Whole percent on the tab title's 5% step, so the frame, the dashcam's
* title and the route title never disagree (47% vs 45%). */
export const routePercent = (progress: number): number => Math.round(quantiseRoute(progress) * 100);
/** Live Bucharest time; inside Infrastructure the maintenance window's clock. */
export function dashcamClock(sectionId: string, maintenance: number | null, now: Date): string {
@@ -52,8 +52,9 @@ export function postcardFilename(sectionId: string, clock: string): string {
export const postcardCredit = (name: string, city: string): string =>
`NIGHT SHIFT · ${name.toUpperCase()} · ${city.toUpperCase()}`;
export const frameTopLeft = (t: DashcamTelemetry): string =>
`DASHCAM · ${chapterLabel(t.chapter)} · EXIT ${t.exit}`;
/* One vocabulary in the frame: the navigation's exit and chapter name (the
scene's internal chapter ids, e.g. "pre-dawn", are not shown). */
export const frameTopLeft = (t: DashcamTelemetry): string => `DASHCAM · EXIT ${t.exit}`;
export const frameBottomLeft = (t: DashcamTelemetry): string =>
`ROUTE ${t.routePercent}% · ${t.sectionLabel.toUpperCase()}`;
@@ -66,6 +67,20 @@ export function coverCrop(sw: number, sh: number, dw: number, dh: number): { sx:
return { sx: (sw - cropW) / 2, sy: (sh - cropH) / 2, sw: cropW, sh: cropH };
}
/** Widest postcard written: a 2x buffer at 1440 is downsampled to this. */
export const POSTCARD_MAX_WIDTH = 2560;
/**
* The postcard's size for a sw x sh drawing buffer: the largest 16:9 crop of
* the buffer at its own pixels, never upscaled (a 1440 x 900 buffer stretched
* to 1600 read soft), downsampled only beyond POSTCARD_MAX_WIDTH.
*/
export function postcardSize(sw: number, sh: number): { w: number; h: number } {
if (!(sw > 0 && sh > 0)) return { w: POSTCARD_WIDTH, h: POSTCARD_HEIGHT };
const w = Math.min(POSTCARD_MAX_WIDTH, sw, (sh * 16) / 9);
return { w: Math.round(w), h: Math.round((w * 9) / 16) };
}
/** Frame chrome geometry for a w x h postcard (proportional to 1600 x 900). */
export function postcardLayout(w = POSTCARD_WIDTH, h = POSTCARD_HEIGHT) {
const unit = Math.min(w / POSTCARD_WIDTH, h / POSTCARD_HEIGHT);
@@ -180,11 +195,13 @@ export function savePostcard(source: Source, t: DashcamTelemetry): Promise<Postc
let canvas: HTMLCanvasElement;
try {
canvas = document.createElement('canvas');
canvas.width = POSTCARD_WIDTH;
canvas.height = POSTCARD_HEIGHT;
const { w, h } = postcardSize(source.width, source.height);
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext('2d');
if (ctx === null) return Promise.resolve('failed');
drawPostcard(ctx as unknown as PostcardContext, source, t);
ctx.imageSmoothingQuality = 'high';
drawPostcard(ctx as unknown as PostcardContext, source, t, w, h);
} catch {
return Promise.resolve('failed');
}