Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions server/tests/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ test("public information and discovery pages are accessible without an authentic
assert.match(soundtrack.body, /href="\/privacy"/);
assert.match(soundtrack.body, /https:\/\/apps\.apple\.com\/us\/app\/journeydeck\/id6806502526/);
assert.match(soundtrack.body, /aria-current="page">Soundtracks<\/a>/);
assert.match(soundtrack.body, /\/assets\/v2\/soundtrack\.jpg/);
assert.match(soundtrack.body, /\/assets\/v2\/soundtrack\.webp/);
assert.match(soundtrack.body, /Identify Song uses ShazamKit/);
assert.match(soundtrack.body, /rel="preload" as="image"/);
assert.match(soundtrack.body, /grand-touring-home-800\.webp\?v=1/);
Expand Down Expand Up @@ -260,8 +260,8 @@ test("hosted root serves the Grand Touring launch page while private routes stay
["/login-grand-touring.css?v=grand-tour-1", /text\/css/],
["/beta.js?v=v4-1", /javascript/],
["/campaign.js?v=4", /javascript/],
["/medallions/medallions.css?v=medallions-1", /text\/css/],
["/medallions/medallions.js?v=medallions-1", /javascript/],
["/medallions/medallions.css?v=medallions-2", /text\/css/],
["/medallions/medallions.js?v=medallions-2", /javascript/],
["/medallions/catalog.js", /javascript/],
["/medallions/viewer.js?v=medallions-1", /javascript/],
["/assets/medallions/soundtrack-100-dark.webp", /image\/webp/],
Expand Down
60 changes: 60 additions & 0 deletions server/tests/public-images.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import assert from "node:assert/strict";
import test from "node:test";
import { createApp } from "../src/app.js";
import { fixtureDatabase, root } from "./helpers.js";

test("home page hero and scenic photos use versioned responsive srcset", async () => {
const fixture = fixtureDatabase();
const runtime = await createApp({ databasePath: fixture.filename, root, allowTestAuth: true, legacyUpstream: "", mode: "web" });
try {
const home = await runtime.app.inject({ method: "GET", url: "/" });
assert.equal(home.statusCode, 200, home.body);
assert.match(home.body, /rel="preload" as="image"/);
assert.match(home.body, /01-today-400\.webp\?v=1/);
assert.match(home.body, /01-today\.webp\?v=1 660w/);
assert.match(home.body, /imagesrcset="/);
assert.match(home.body, /imagesizes="/);
assert.match(home.body, /fetchpriority="high"/);
assert.match(home.body, /srcset="\/assets\/v4\/01-today-400\.webp\?v=1 400w/);
assert.doesNotMatch(home.body, /data-feature="Today"[^>]*<img[^>]*loading="lazy"/);
assert.match(home.body, /grand-touring-home-800\.webp\?v=1 800w/);
assert.match(home.body, /grand-touring-home-1600\.webp\?v=1 1600w/);
assert.match(home.body, /favicon-32\.png\?v=app-logo-1/);
assert.match(home.body, /\/assets\/favicon\.png\?v=app-logo-1/);

for (const [url, mime] of [
["/assets/v4/01-today-400.webp?v=1", /image\/webp/],
["/assets/v4/01-today.webp?v=1", /image\/webp/],
["/assets/beta/grand-touring-home-800.webp?v=1", /image\/webp/],
["/assets/beta/grand-touring-home-1600.webp?v=1", /image\/webp/],
["/assets/v2/soundtrack.webp?v=1", /image\/webp/],
["/assets/favicon-32.png?v=app-logo-1", /image\/png/],
["/favicon.ico", /image\/x-icon|image\/vnd\.microsoft\.icon|image\/png|application\/octet-stream/],
["/assets/medallions/soundtrack-100-redline-pick.webp", /image\/webp/],
["/assets/medallions/first-note-dark-pick.webp", /image\/webp/]
] as const) {
const asset = await runtime.app.inject({ method: "GET", url });
assert.equal(asset.statusCode, 200, url);
assert.match(String(asset.headers["content-type"]), mime, url);
}

for (const url of [
"/assets/v4/01-today-400.webp?v=1",
"/assets/beta/grand-touring-home-800.webp?v=1",
"/assets/beta/grand-touring-home-1600.webp?v=1",
"/assets/v2/soundtrack.webp?v=1",
"/assets/favicon-32.png?v=app-logo-1"
]) {
const versioned = await runtime.app.inject({ method: "GET", url });
assert.equal(versioned.headers["cache-control"], "public, max-age=31536000, immutable", url);
}

const soundtrack = await runtime.app.inject({ method: "GET", url: "/apple-music-soundtrack" });
assert.equal(soundtrack.statusCode, 200, soundtrack.body);
assert.match(soundtrack.body, /\/assets\/v2\/soundtrack\.webp\?v=1/);
assert.doesNotMatch(soundtrack.body, /\/assets\/v2\/soundtrack\.jpg"/);
} finally {
await runtime.app.close();
fixture.cleanup();
}
});
1 change: 1 addition & 0 deletions web/404.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<meta name="theme-color" content="#081832">
<meta name="robots" content="noindex, nofollow">
<meta name="description" content="The requested JourneyDeck page could not be found.">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon-32.png?v=app-logo-1">
<link rel="icon" type="image/png" sizes="192x192" href="/assets/favicon.png?v=app-logo-1">
<link rel="apple-touch-icon" href="/assets/journeydeck-cinematic-180.png">
<link rel="stylesheet" href="/public-page.css?v=grand-tour-3">
Expand Down
3 changes: 2 additions & 1 deletion web/apple-music-soundtrack.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<meta name="twitter:site" content="@JourneyDeck">
<meta name="twitter:image" content="https://journeydeck.me/assets/beta/share-card.png?v=1">
<meta name="twitter:image:alt" content="JourneyDeck: every route, every song, every memory. A phone showing a Summer Vacation Memory.">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon-32.png?v=app-logo-1">
<link rel="icon" type="image/png" sizes="192x192" href="/assets/favicon.png?v=app-logo-1">
<link rel="apple-touch-icon" href="/assets/journeydeck-cinematic-180.png">
<link rel="stylesheet" href="/public-page.css?v=hero-lcp-1">
Expand Down Expand Up @@ -78,7 +79,7 @@ <h1>Record drives on iPhone with an optional Apple Music soundtrack</h1>
</section>
<section class="soundtrack-proof" aria-labelledby="soundtrack-proof-title">
<div><p class="eyebrow">THE SOUNDTRACK / JOURNEYDECK 4.0</p><h2 id="soundtrack-proof-title">The songs beside the road.</h2><p>See the soundtrack saved with a journey in the released iPhone app. Connect Apple Music for optional matching, or use Identify Song yourself when a track catches your ear.</p><p>Identify Song uses ShazamKit when you ask it to listen; JourneyDeck does not keep an audio recording.</p></div>
<figure><img src="/assets/v2/soundtrack.jpg" width="1000" height="1500" loading="lazy" alt="JourneyDeck 4.0 App Store screenshot of the Soundtracks view with songs saved for a drive"><figcaption>Soundtracks in JourneyDeck 4.0</figcaption></figure>
<figure><img src="/assets/v2/soundtrack.webp?v=1" width="600" height="1304" loading="lazy" alt="JourneyDeck 4.0 App Store screenshot of the Soundtracks view with songs saved for a drive"><figcaption>Soundtracks in JourneyDeck 4.0</figcaption></figure>
</section>
<section class="content" aria-label="Apple Music soundtrack details">
<article class="card">
Expand Down
Binary file added web/assets/favicon-32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/assets/medallions/first-note-dark-pick.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/assets/medallions/first-note-light-pick.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/assets/medallions/first-note-sakura-pick.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/assets/medallions/first-track-dark-pick.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/assets/medallions/first-track-light-pick.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/assets/medallions/grand-tourer-dark-pick.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/assets/medallions/long-play-dark-pick.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/assets/medallions/long-play-light-pick.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/assets/medallions/long-play-redline-pick.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/assets/medallions/long-play-sakura-pick.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/assets/medallions/memory-maker-dark-pick.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/assets/medallions/picture-this-dark-pick.webp
Binary file added web/assets/v2/soundtrack.webp
Binary file added web/assets/v4/01-today-400.webp
12 changes: 7 additions & 5 deletions web/beta.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,18 @@
<meta name="twitter:site" content="@JourneyDeck">
<meta name="twitter:image" content="https://journeydeck.me/assets/beta/share-card.png?v=1">
<meta name="twitter:image:alt" content="JourneyDeck: every route, every song, every memory. A phone showing a Summer Vacation Memory.">
<link rel="icon" type="image/png" href="/assets/favicon.png?v=app-logo-1">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon-32.png?v=app-logo-1">
<link rel="icon" type="image/png" sizes="192x192" href="/assets/favicon.png?v=app-logo-1">
<link rel="apple-touch-icon" href="/assets/journeydeck-cinematic-180.png">
<link rel="stylesheet" href="/medallions/medallions.css?v=medallions-1">
<link rel="stylesheet" href="/medallions/medallions.css?v=medallions-2">
<link rel="stylesheet" href="/beta.css?v=grand-tour-4">
<link rel="stylesheet" href="/v2-features.css?v=1">
<link rel="stylesheet" href="/site-mobile-nav.css?v=1">
<link rel="preload" as="image" href="/assets/v4/01-today-400.webp?v=1" imagesrcset="/assets/v4/01-today-400.webp?v=1 400w, /assets/v4/01-today.webp?v=1 660w" imagesizes="(max-width: 800px) 228px, 237px">
<script defer src="https://cloud.umami.is/script.js" data-website-id="a5f66e11-ec7d-4133-8e1a-f8e6c08593af" data-domains="journeydeck.me"></script>
<script type="module" src="/campaign.js?v=4"></script>
<script type="module" src="/beta.js?v=v4-1"></script>
<script type="module" src="/medallions/medallions.js?v=medallions-1"></script>
<script type="module" src="/medallions/medallions.js?v=medallions-2"></script>
<title>JourneyDeck: Take the road. Keep the story.</title>
<script type="application/ld+json">
{
Expand Down Expand Up @@ -79,7 +81,7 @@ <h1 id="hero-title">Take the road.<br><span class="gold">Keep the story.</span><
<div class="tour-visual" tabindex="0" aria-label="App screenshots. Use left and right arrow keys to explore.">
<div class="orbit orbit-one" aria-hidden="true"></div><div class="orbit orbit-two" aria-hidden="true"></div><span class="tour-side-label" aria-hidden="true">YOUR WORLD, WELL KEPT.</span>
<div class="phone-stack"><div class="stack-page stack-page-back" aria-hidden="true"></div><div class="stack-page stack-page-middle" aria-hidden="true"></div><div class="phone-frame"><div class="screen-clip">
<figure class="tour-slide is-current" data-feature="Today" role="group" aria-roledescription="slide" aria-label="1 of 5: Today"><img src="/assets/v4/01-today.webp" width="660" height="1434" alt="JourneyDeck 4.0 Today with the journey recorder" fetchpriority="high"></figure>
<figure class="tour-slide is-current" data-feature="Today" role="group" aria-roledescription="slide" aria-label="1 of 5: Today"><img src="/assets/v4/01-today-400.webp?v=1" srcset="/assets/v4/01-today-400.webp?v=1 400w, /assets/v4/01-today.webp?v=1 660w" sizes="(max-width: 800px) 228px, 237px" width="660" height="1434" alt="JourneyDeck 4.0 Today with the journey recorder" fetchpriority="high" decoding="async"></figure>
<figure class="tour-slide" data-feature="Soundtrack" role="group" aria-roledescription="slide" aria-label="2 of 5: Soundtrack" hidden><img src="/assets/v4/05-soundtrack.webp" width="660" height="1434" alt="JourneyDeck 4.0 Soundtrack with saved listening history" loading="lazy"></figure>
<figure class="tour-slide" data-feature="Memories" role="group" aria-roledescription="slide" aria-label="3 of 5: Memories" hidden><img src="/assets/v4/03-memories.webp" width="660" height="1434" alt="JourneyDeck 4.0 Memories with photos" loading="lazy"></figure>
<figure class="tour-slide" data-feature="Ask JourneyDeck" role="group" aria-roledescription="slide" aria-label="4 of 5: Ask JourneyDeck" hidden><img src="/assets/v4/07-ask.webp" width="660" height="1434" alt="Ask JourneyDeck answering a question from the archive" loading="lazy"></figure>
Expand All @@ -106,7 +108,7 @@ <h1 id="hero-title">Take the road.<br><span class="gold">Keep the story.</span><
<div class="showcase-copy" data-reveal><p class="eyebrow">ROUTE REPLAY / JOURNEYDECK 4.0</p><h2 id="replay-title">See the drive unfold again.</h2><p>Replay a journey you recorded on iPhone. Move through its route at your own pace and revisit the soundtrack moments saved along the way.</p><p class="showcase-detail">Start and finish recording intentionally. When the drive is over, the road and its music are there to explore together.</p><a class="feature-cta" href="/road-trip-tracker-app">See the road trip tracker app <span aria-hidden="true">↗</span></a></div>
<figure class="showcase-visual" data-reveal><img src="/assets/v4/02-journey.webp" width="660" height="1434" loading="lazy" alt="JourneyDeck 4.0 screenshot showing a saved journey, route map, soundtrack, and replay control"><figcaption>JourneyDeck 4.0 journey detail on iPhone</figcaption></figure>
</section>
<section class="scenic-break" aria-label="Make a little room for the long way home"><img src="/assets/beta/grand-touring-home.webp" width="1536" height="1024" loading="lazy" alt="A blue touring car on a quiet forest road in the rain" data-parallax><div class="scenic-shade"></div><div class="scenic-copy section-shell" data-reveal><p class="eyebrow">NO ORDINARY MILES.</p><p class="scenic-title">The long way home<br>has its rewards.</p><a class="text-link" href="#medallions">Meet the collection <span aria-hidden="true">↓</span></a></div></section>
<section class="scenic-break" aria-label="Make a little room for the long way home"><img src="/assets/beta/grand-touring-home-800.webp?v=1" srcset="/assets/beta/grand-touring-home-800.webp?v=1 800w, /assets/beta/grand-touring-home-1600.webp?v=1 1600w" sizes="100vw" width="1536" height="1024" loading="lazy" alt="A blue touring car on a quiet forest road in the rain" data-parallax decoding="async"><div class="scenic-shade"></div><div class="scenic-copy section-shell" data-reveal><p class="eyebrow">NO ORDINARY MILES.</p><p class="scenic-title">The long way home<br>has its rewards.</p><a class="text-link" href="#medallions">Meet the collection <span aria-hidden="true">↓</span></a></div></section>
<section class="medallions section-shell section-space" id="medallions" aria-labelledby="medallions-heading">
<div class="section-heading split-heading" data-reveal><div><p class="eyebrow">THE MEDALLION COLLECTION / TRY IT HERE</p><h2 id="medallions-heading">A little milestone.<br><span class="gold">A little gold.</span></h2></div><p>Ten medallions for the roads, songs, and memories that make your story. Pick one. Turn it over. Find your favorite finish.</p></div>
<div class="medallion-explorer" data-theme="redline">
Expand Down
Binary file added web/favicon.ico
Binary file not shown.
7 changes: 5 additions & 2 deletions web/medallions/medallions.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ let revision = 0;
let visible = false;
let started = false;
let rendererModule;
const imageUrl = (id, themeId, thumb = false) => `/assets/medallions/${id}-${themeId}${thumb ? '-thumb' : ''}.webp`;
const imageUrl = (id, themeId, variant = false) => {
const suffix = variant === "pick" ? "-pick" : variant ? "-thumb" : "";
return `/assets/medallions/${id}-${themeId}${suffix}.webp`;
};
const motion = () => ({ reduceMotion: preference.matches, active: visible && !document.hidden });

for (const medal of medallions) {
Expand Down Expand Up @@ -45,7 +48,7 @@ function updateCopy(medal, themeId) {
fallback.alt = `${medal.name} in ${theme.selectedOptions[0].textContent}`;
for (const button of collection.children) {
button.setAttribute('aria-pressed', String(button.dataset.id === medal.id));
button.querySelector('img').src = imageUrl(button.dataset.id, themeId, true);
button.querySelector('img').src = imageUrl(button.dataset.id, themeId, "pick");
}
}

Expand Down
1 change: 1 addition & 0 deletions web/privacy.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<meta name="twitter:site" content="@JourneyDeck">
<meta name="twitter:image" content="https://journeydeck.me/assets/beta/share-card.png?v=1">
<meta name="twitter:image:alt" content="JourneyDeck: every route, every song, every memory. A phone showing a Summer Vacation Memory.">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon-32.png?v=app-logo-1">
<link rel="icon" type="image/png" sizes="192x192" href="/assets/favicon.png?v=app-logo-1">
<link rel="apple-touch-icon" href="/assets/journeydeck-cinematic-180.png">
<link rel="stylesheet" href="/public-page.css?v=grand-tour-3">
Expand Down
1 change: 1 addition & 0 deletions web/private-driving-journal.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<meta name="twitter:site" content="@JourneyDeck">
<meta name="twitter:image" content="https://journeydeck.me/assets/beta/share-card.png?v=1">
<meta name="twitter:image:alt" content="JourneyDeck: every route, every song, every memory. A phone showing a Summer Vacation Memory.">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon-32.png?v=app-logo-1">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add the small favicon to the road-trip page

When a user visits the public /road-trip-tracker-app landing page, web/road-trip-tracker-app.html:23 still offers only the 53,692-byte 192×192 favicon, so it misses the roughly 51 KB saving applied here to every other public marketing page. Add the same 32×32 favicon link to that page and include it in the public-image coverage.

Useful? React with 👍 / 👎.

<link rel="icon" type="image/png" sizes="192x192" href="/assets/favicon.png?v=app-logo-1">
<link rel="apple-touch-icon" href="/assets/journeydeck-cinematic-180.png">
<link rel="stylesheet" href="/public-page.css?v=hero-lcp-1">
Expand Down
1 change: 1 addition & 0 deletions web/support.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<meta name="twitter:site" content="@JourneyDeck">
<meta name="twitter:image" content="https://journeydeck.me/assets/beta/share-card.png?v=1">
<meta name="twitter:image:alt" content="JourneyDeck: every route, every song, every memory. A phone showing a Summer Vacation Memory.">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon-32.png?v=app-logo-1">
<link rel="icon" type="image/png" sizes="192x192" href="/assets/favicon.png?v=app-logo-1">
<link rel="apple-touch-icon" href="/assets/journeydeck-cinematic-180.png">
<link rel="stylesheet" href="/public-page.css?v=grand-tour-3">
Expand Down
1 change: 1 addition & 0 deletions web/terms.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<meta name="twitter:site" content="@JourneyDeck">
<meta name="twitter:image" content="https://journeydeck.me/assets/beta/share-card.png?v=1">
<meta name="twitter:image:alt" content="JourneyDeck: every route, every song, every memory. A phone showing a Summer Vacation Memory.">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon-32.png?v=app-logo-1">
<link rel="icon" type="image/png" sizes="192x192" href="/assets/favicon.png?v=app-logo-1">
<link rel="apple-touch-icon" href="/assets/journeydeck-cinematic-180.png">
<link rel="stylesheet" href="/public-page.css?v=grand-tour-3">
Expand Down