diff --git a/docs/overnight/LOG.md b/docs/overnight/LOG.md index 6c9aaa4..b98f92a 100644 --- a/docs/overnight/LOG.md +++ b/docs/overnight/LOG.md @@ -95,3 +95,10 @@ Run: `npm run build:preview` then serve `dist` (dev server shows a blank page). - **Verdict: SHIP with fixes** (Fable, hardware GL 1440x900, wheel-driven drive, frames at eleven section-local points, reverse drive, 1920x1080, 1024x700, 390x844 low tier, reduced motion, perf run): the builder's yard sat on a fixed x -28 parallel to the road, so it converged into the vanishing point under the reading panel and was mostly visible only at the chapter entry (steel also darkened late, timing unwatched). Fixes made during review: (1) **the rails are now a radial siding**: the yard group pivots on the chapter lens and its -z axis is turned by `railYaw(aspect)` so the rail line projects to NDC -0.86 (the middle of the 15% left margin) once the chapter's lens shift is applied, for any viewport aspect; every stop, plate and the crane therefore sit at the same screen x inside the margin at 1440x900 and 1920x1080 and the crane rides *up* the margin, growing, from the far stop to the near one; (2) **the stacks step down toward the camera** (4, 3, 2, 1 containers, far to near) so no stack hides the plate or the crane behind it, and the spreader lands on whichever stack it is over; (3) a raised concrete apron (quay at y 5, 12 m deep, accent bay lines) under the yard so the rolling terrain swell no longer buries the far stacks; (4) span 18 -> 12 m so the near crane fits the 216 px margin with the lens settled; (5) floods x 0.42 -> 0.5 and 9 -> 13 m lamp glows, the road pool dropped (two apron pools remain). Observed: the plates light exactly at the stops forward and back, pink on mauve-grey steel with the night palette (nothing blown: structure stays under the 0.85 bloom threshold, only the lamp glows bloom, softly); the entry is under the EXIT 06 deck with the crane screen-doored and the yard already standing (01); the crane fades before the EXIT 07 deck and never crosses a deck, sign, billboard, relay mast or milestone (the yard is 40-95 m off the road); reverse drive runs the crane back and dims plates in the same places; rAF p50 16.7 / p95 16.8 ms, 0 frames over 34 ms through the whole chapter; no console errors or warnings in any run; reduced motion renders the static poster with nothing; 390x844 (low tier) gets the lite yard, which the full-width panel hides except for pink slivers at the left edge. - **Known weaknesses:** the lens eases into the chapter over ~1 s (camera time constant 1.05 s), so for the first second of the chapter the whole yard slides left into the margin with everything else in the world (the first stop at 0.1 can be reached before it settles on a fast scroll, when the crane's right leg sits under the panel edge); at 1024x700 the compact-rail panel is 90% wide with a 5% margin (51 px), so the yard is legible only at the chapter entry and in the gaps between cards (05), not in the margin; the 4-tier far stack is seen at a 30 degree yaw and reads as leaning at a glance; the floods light the stacks as a flat uniform term (no shadows); both work lamps share one strike; the crane's exit run (-80 -> -62 m) is short, so the last 20% of the chapter is a crane standing over the near stack while the yard fades; the yard's screen placement is tuned to the chapter composition constants, so a composition change needs RAIL_NDC rechecked. - Screenshots: docs/overnight/round11/ (01 entry under the EXIT 06 deck, crane screen-doored; 02 second stop with the panel up, pink plate in the margin; 03 last stop, the near one-container stack with the work lamps; 04 1920x1080 third stop; 05 1024x700 chapter entry; 06 390x844 low tier; 07 contact sheet of the left margin across the drive, entry to Toolkit). + +## Polish pass (owner feedback) +- **What:** traffic as real vehicles (dark low-poly cars and box trucks, paired tail lamps, a centre brake lamp, amber clearance lamps on trucks, fog-coloured sheen, one pool per vehicle with its own tint); shoulder reflectors became delineator plates on the guardrail posts, and the amber road studs were removed; the owner's name left the navbar (it stays in the hero); icon buttons centred (the play/eq tile inherited `justify-content: space-between`); the "Email me" preview is a flex item after the button, so it clears it at any width; DASHCAM opens in two phases (the page folds only once the frame has mounted), retries its chunk once, abandons after 4 s, always has an enabled Close, recovers from an unanswered capture, closes on scene loss, motion withdrawal or a resize below 768, no click-to-capture on the empty frame, and keeps focus inside the dialog on such a click. +- **Weaknesses addressed:** postcard at native resolution (largest 16:9 crop up to 2560 wide) and one chapter vocabulary (EXIT nn); dashcam percent quantised like the tab (5%); reduced-motion SceneCanvas writes html[data-dawn] only on change; SHIFT OVER now turns over on the HUD sub-line at full day; the reduced-motion dawn masks the poster's sunset sky for a blue-zenith, gold-horizon daybreak; mile-marker plates stay in the chapter accent (below the tonemap shoulder) and do not flash while swept past by a navigator jump or a fling (nav-click peak frame mean 91-101 -> 58-62, no rising step over 12); html[data-underpass] waits for the shade to hold 120 ms; lite billboard keeps both floods on (phone board brighter). +- **Budgets:** initial JS 115.43 KB gz (from 119.08: IconGlyph draws the six Phosphor glyphs from their regular-weight paths instead of importing the package components, which ship all six weights each), CSS 26.93 KB gz (+0.24), scene chunk 222.95 KB gz (+1.6), DashcamFrame 3.74 KB gz. 925 tests (79 files), violations []. +- **Left:** relay lamp cores (a saturated violet cannot pass the luminance bloom threshold, so a violet core loses its glow); mast girth at 1024/phones (bounded by the panel-margin layout test); cold-GPU first frame; underpass pier vs billboard edge; yard lens slide and far-stack lean. Headless hardware-GL Chromium loses its GPU process when a project video starts (SharedImage BGRA failure), also at ca12cfd; not an app fault. +- Screenshots: docs/overnight/polish/ (WEBP). diff --git a/docs/overnight/polish/01-hero-traffic-crop.webp b/docs/overnight/polish/01-hero-traffic-crop.webp new file mode 100644 index 0000000..4a25be9 Binary files /dev/null and b/docs/overnight/polish/01-hero-traffic-crop.webp differ diff --git a/docs/overnight/polish/02-nav-and-controls.webp b/docs/overnight/polish/02-nav-and-controls.webp new file mode 100644 index 0000000..506d59c Binary files /dev/null and b/docs/overnight/polish/02-nav-and-controls.webp differ diff --git a/docs/overnight/polish/03-contact-email-hover-1440.webp b/docs/overnight/polish/03-contact-email-hover-1440.webp new file mode 100644 index 0000000..dfe9da0 Binary files /dev/null and b/docs/overnight/polish/03-contact-email-hover-1440.webp differ diff --git a/docs/overnight/polish/04-reduced-motion-dawn.webp b/docs/overnight/polish/04-reduced-motion-dawn.webp new file mode 100644 index 0000000..babc851 Binary files /dev/null and b/docs/overnight/polish/04-reduced-motion-dawn.webp differ diff --git a/docs/overnight/polish/05-dashcam-open.webp b/docs/overnight/polish/05-dashcam-open.webp new file mode 100644 index 0000000..296fa54 Binary files /dev/null and b/docs/overnight/polish/05-dashcam-open.webp differ diff --git a/docs/overnight/polish/06-dashcam-postcard-native.webp b/docs/overnight/polish/06-dashcam-postcard-native.webp new file mode 100644 index 0000000..436f909 Binary files /dev/null and b/docs/overnight/polish/06-dashcam-postcard-native.webp differ diff --git a/docs/overnight/polish/07-nav-click-flare-before-top-after-bottom.webp b/docs/overnight/polish/07-nav-click-flare-before-top-after-bottom.webp new file mode 100644 index 0000000..1ed3935 Binary files /dev/null and b/docs/overnight/polish/07-nav-click-flare-before-top-after-bottom.webp differ diff --git a/docs/overnight/polish/08-phone-390-start-infra-builds-rm-contact.webp b/docs/overnight/polish/08-phone-390-start-infra-builds-rm-contact.webp new file mode 100644 index 0000000..87d5138 Binary files /dev/null and b/docs/overnight/polish/08-phone-390-start-infra-builds-rm-contact.webp differ diff --git a/docs/overnight/polish/after-1440-0.03.webp b/docs/overnight/polish/after-1440-0.03.webp new file mode 100644 index 0000000..2e43df2 Binary files /dev/null and b/docs/overnight/polish/after-1440-0.03.webp differ diff --git a/docs/overnight/polish/after-1440-0.25.webp b/docs/overnight/polish/after-1440-0.25.webp new file mode 100644 index 0000000..7b2b817 Binary files /dev/null and b/docs/overnight/polish/after-1440-0.25.webp differ diff --git a/docs/overnight/polish/after-1440-0.5.webp b/docs/overnight/polish/after-1440-0.5.webp new file mode 100644 index 0000000..e24015a Binary files /dev/null and b/docs/overnight/polish/after-1440-0.5.webp differ diff --git a/docs/overnight/polish/after-1440-0.webp b/docs/overnight/polish/after-1440-0.webp new file mode 100644 index 0000000..6d9aab1 Binary files /dev/null and b/docs/overnight/polish/after-1440-0.webp differ diff --git a/docs/overnight/polish/before-1440-0.03.webp b/docs/overnight/polish/before-1440-0.03.webp new file mode 100644 index 0000000..5b91434 Binary files /dev/null and b/docs/overnight/polish/before-1440-0.03.webp differ diff --git a/docs/overnight/polish/before-1440-0.25.webp b/docs/overnight/polish/before-1440-0.25.webp new file mode 100644 index 0000000..12c7cbf Binary files /dev/null and b/docs/overnight/polish/before-1440-0.25.webp differ diff --git a/docs/overnight/polish/before-1440-0.5.webp b/docs/overnight/polish/before-1440-0.5.webp new file mode 100644 index 0000000..feea979 Binary files /dev/null and b/docs/overnight/polish/before-1440-0.5.webp differ diff --git a/docs/overnight/polish/before-1440-0.webp b/docs/overnight/polish/before-1440-0.webp new file mode 100644 index 0000000..8869fe4 Binary files /dev/null and b/docs/overnight/polish/before-1440-0.webp differ diff --git a/docs/overnight/polish/review-all-icons.webp b/docs/overnight/polish/review-all-icons.webp new file mode 100644 index 0000000..06390d8 Binary files /dev/null and b/docs/overnight/polish/review-all-icons.webp differ diff --git a/docs/overnight/polish/review-chapters-traffic.webp b/docs/overnight/polish/review-chapters-traffic.webp new file mode 100644 index 0000000..6886a15 Binary files /dev/null and b/docs/overnight/polish/review-chapters-traffic.webp differ diff --git a/docs/overnight/polish/review-dashcam-open-1440.webp b/docs/overnight/polish/review-dashcam-open-1440.webp new file mode 100644 index 0000000..3f196df Binary files /dev/null and b/docs/overnight/polish/review-dashcam-open-1440.webp differ diff --git a/docs/overnight/polish/review-email-hover-1180.webp b/docs/overnight/polish/review-email-hover-1180.webp new file mode 100644 index 0000000..d6f3142 Binary files /dev/null and b/docs/overnight/polish/review-email-hover-1180.webp differ diff --git a/docs/overnight/polish/review-end-shift-over-rm-phone.webp b/docs/overnight/polish/review-end-shift-over-rm-phone.webp new file mode 100644 index 0000000..cfc0ed3 Binary files /dev/null and b/docs/overnight/polish/review-end-shift-over-rm-phone.webp differ diff --git a/docs/overnight/polish/review-hero-traffic-1440.webp b/docs/overnight/polish/review-hero-traffic-1440.webp new file mode 100644 index 0000000..300e62b Binary files /dev/null and b/docs/overnight/polish/review-hero-traffic-1440.webp differ diff --git a/docs/overnight/polish/review-icons-1440-1024-390.webp b/docs/overnight/polish/review-icons-1440-1024-390.webp new file mode 100644 index 0000000..af8c894 Binary files /dev/null and b/docs/overnight/polish/review-icons-1440-1024-390.webp differ diff --git a/docs/overnight/polish/review-nav-flare.webp b/docs/overnight/polish/review-nav-flare.webp new file mode 100644 index 0000000..252578e Binary files /dev/null and b/docs/overnight/polish/review-nav-flare.webp differ diff --git a/src/components/AppShell.vue b/src/components/AppShell.vue index 32dfdda..dfcb693 100644 --- a/src/components/AppShell.vue +++ b/src/components/AppShell.vue @@ -414,12 +414,13 @@ onUnmounted(() => removePageListeners()); /> diff --git a/src/components/DashcamFrame.vue b/src/components/DashcamFrame.vue index 798a062..1fd9c98 100644 --- a/src/components/DashcamFrame.vue +++ b/src/components/DashcamFrame.vue @@ -35,7 +35,7 @@ const props = defineProps<{ city: string; sceneReady: boolean; }>(); -const emit = defineEmits<{ close: [] }>(); +const emit = defineEmits<{ close: []; ready: [] }>(); /** REC dot sampling rate while open. */ const PULSE_HZ = 12; @@ -43,15 +43,15 @@ const PULSE_HZ = 12; const CAPTURE_TIMEOUT_MS = 1_200; const SHUTTER_MS = 120; -const surface = ref(null); const saveButton = ref(null); const closeButton = ref(null); const dot = ref(null); const now = shallowRef(new Date()); const busy = shallowRef(false); -const lost = shallowRef(false); const status = shallowRef(''); -const unavailable = computed(() => lost.value || !props.sceneReady); +/* The shell closes the frame when the scene leaves `running`; this only + covers the frames in between. A capture timeout is not a lost scene. */ +const unavailable = computed(() => !props.sceneReady); const telemetry = computed(() => ({ sectionId: props.journey.activeSectionId, @@ -85,8 +85,8 @@ const finish = (): void => { window.clearTimeout(captureTimer); busy.value = false; // The button was disabled while busy, which drops focus to the body. - const lost = document.activeElement === null || document.activeElement === document.body; - if (lost) void nextTick(() => saveButton.value?.focus()); + const focusDropped = document.activeElement === null || document.activeElement === document.body; + if (focusDropped) void nextTick(() => saveButton.value?.focus()); }; /* The page stays where it was: a scrollbar drag (the only input not @@ -113,18 +113,15 @@ const shoot = (): void => { finish(); }); }); - // Context loss mid-capture: the scene never answers; drop the request. + // The scene did not answer (a stalled or lost frame): drop the request and + // let the visitor try again; the button comes straight back. captureTimer = window.setTimeout(() => { cancelDashcamCapture(); - lost.value = true; + status.value = 'Postcard not taken, try again'; finish(); }, CAPTURE_TIMEOUT_MS); }; -const onSurfaceClick = (event: MouseEvent): void => { - if (event.target === surface.value) shoot(); -}; - const onPointerMove = (event: PointerEvent): void => { if (event.pointerType !== 'mouse' || !readDashcam().drift) return; setDashcamPointer( @@ -157,7 +154,6 @@ const CAPTURE = { capture: true } as const; const ACTIVE = { capture: true, passive: false } as const; watch(title, (value) => { document.title = value; }); -watch(() => props.sceneReady, (ready) => { if (ready) lost.value = false; }); onMounted(() => { previousTitle = document.title; @@ -170,7 +166,8 @@ onMounted(() => { window.addEventListener('wheel', suppress, ACTIVE); window.addEventListener('touchmove', suppress, ACTIVE); window.addEventListener('keydown', onKeydown, CAPTURE); - saveButton.value?.focus(); + (saveButton.value?.disabled ? closeButton.value : saveButton.value)?.focus(); + emit('ready'); }); onBeforeUnmount(() => { @@ -190,13 +187,12 @@ onBeforeUnmount(() => {