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: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ is a Tabata whether or not anything is labelled "Tabata".
- **Offline-first** — precached service worker, no backend, no accounts, no analytics. Installable as
**Timer**; waiting updates apply immediately when no Routine is active.
- **Automatic updates** — an active or paused Routine defers a waiting worker until Done or Reset. The
new build reloads automatically, then the bottom version shows green **NEW vx.x.x** for that reload only.
new build reloads automatically; releases show green **NEW vx.x.x** at right, while dev previews append
green **NEW** to the PR/deployment clue at left.
- **Progressive enhancement** — audio session, Wake Lock and vibration are feature-detected and no-ops
where absent. The app is fully correct without any of them.
- **Semantic and accessible** — [Pico CSS](https://picocss.com/) classless, WCAG 2.2 AA in every slice.
Expand Down Expand Up @@ -88,7 +89,8 @@ published, served at [timer.marcosgilf.com](https://timer.marcosgilf.com).

Release Please opens Release PRs from Conventional Commits. Merging a Release PR bumps
`package.json`, updates `CHANGELOG.md`, creates a `vX.Y.Z` Git tag and a GitHub Release. Publishing
that release triggers the production deploy. The app shows that version in the bottom-right corner.
that release triggers the production deploy. The app shows that version in the bottom-right corner;
production update highlights stay there, while preview highlights use the left deployment clue.

Use commit prefixes intentionally:

Expand Down
11 changes: 6 additions & 5 deletions docs/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -431,9 +431,10 @@ layer below is feature-detected, adds capability where present, and is a no-op w
- No update button, confirmation prompt, toast dependency, popover/polyfill, polling or custom
service-worker manager. Standalone Home Screen PWAs use the same service-worker lifecycle: on launch,
registration checks for an update; `updateSW(true)` activates it and reloads the page.
- Before activation, store a session marker. The new build consumes the marker on first render, prefixes
the bottom version with green **NEW vX.X.X**, then clears it. Version highlighting is optional; storage
failures do not block update activation or timer behavior.
- Before activation, store a session marker. The new build consumes the marker on first render, then clears
it. Production releases prefix the bottom version with green **NEW vX.X.X**. Dev deployments append green
**NEW** to the left PR/deployment clue. Version highlighting is optional; storage failures do not block
update activation or timer behavior.
- Registration errors are swallowed; offline use of the current version remains unaffected.

### Install and manifest
Expand Down Expand Up @@ -594,8 +595,8 @@ Genuinely undecided. An implementer should raise these rather than assume.
- **Default values for `Prefs`** — only `prepareMs` has a stated default (10s). Starting `muted`,
`volume`, `ticks` and `vibrate` values are unspecified.
- ~~**Update-prompt UI**~~ — resolved: no update control. A waiting worker activates immediately when
idle, or after an active Routine reaches Done/Reset; the new build labels its bottom version **NEW vX.X.X**
in green for that reload only.
idle, or after an active Routine reaches Done/Reset; production labels its version **NEW vX.X.X** in
green, while dev deployments append **NEW** to the left PR/deployment clue.
- **Install guidance copy for iOS** — decided that it must be static copy (no
`beforeinstallprompt`), but not what it says or which screen hosts it.
- ~~**App name, iconography, favicon/manifest icon set, `theme_color`**~~ — resolved: app name
Expand Down
18 changes: 13 additions & 5 deletions src/layouts/AppLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const deployLabel =
<body data-routine-active="false">
<slot />
<footer class="app-footer">
{deployLabel && <small class="deploy-clue" aria-label={`Deployment context ${deployLabel}`}>{deployLabel}</small>}
{deployLabel && <small class="deploy-clue" data-deploy-clue aria-label={`Deployment context ${deployLabel}`}>{deployLabel}</small>}
<small class="app-version" data-app-version aria-label={`Timer version ${version}`}>v{version}</small>
</footer>

Expand All @@ -47,15 +47,23 @@ const deployLabel =
import { createPwaUpdateController, type RegisterSW } from "../client/pwa-update.ts";

const appVersion = document.querySelector<HTMLElement>("[data-app-version]")!;
const deployClue = document.querySelector<HTMLElement>("[data-deploy-clue]");
const updateMarker = "timer:update-activated";

try {
if (sessionStorage.getItem(updateMarker) === "true") {
sessionStorage.removeItem(updateMarker);
const versionLabel = appVersion.getAttribute("aria-label") ?? "Timer version";
appVersion.textContent = `NEW ${appVersion.textContent ?? ""}`;
appVersion.dataset.updated = "true";
appVersion.setAttribute("aria-label", `New ${versionLabel}`);
if (deployClue) {
const deployLabel = deployClue.getAttribute("aria-label") ?? "Deployment context";
deployClue.textContent = `${deployClue.textContent ?? ""} NEW`;
deployClue.dataset.updated = "true";
deployClue.setAttribute("aria-label", `${deployLabel} new`);
} else {
const versionLabel = appVersion.getAttribute("aria-label") ?? "Timer version";
appVersion.textContent = `NEW ${appVersion.textContent ?? ""}`;
appVersion.dataset.updated = "true";
appVersion.setAttribute("aria-label", `New ${versionLabel}`);
}
}
} catch {
// Version highlighting is optional. Timer behavior stays independent.
Expand Down
21 changes: 18 additions & 3 deletions src/styles/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

:root {
--pico-background-color: #0b0b0c;
scroll-padding-block-start: calc(3.75rem + env(safe-area-inset-top));
--digit-colour: #ff2b2b;
--digit-glow: rgb(255 40 40 / 45%);
--pico-primary: var(--digit-colour);
Expand All @@ -22,19 +23,28 @@ body > main {
min-height: 100dvh;
width: 100%;
max-width: none;
padding: 1rem max(0.5rem, env(safe-area-inset-left)) 1rem max(0.5rem, env(safe-area-inset-right));
padding: calc(1rem + 2.75rem + env(safe-area-inset-top)) max(0.5rem, env(safe-area-inset-left))
1rem max(0.5rem, env(safe-area-inset-right));
padding-bottom: calc(3rem + env(safe-area-inset-bottom));
overflow-x: hidden;
text-align: center;
}

.app-bar {
position: fixed;
z-index: 3;
inset-block-start: 0;
inset-inline-start: 50%;
display: grid;
grid-template-columns: 1fr auto 1fr;
align-items: center;
width: min(100%, 48rem);
min-height: 2.75rem;
min-height: calc(2.75rem + env(safe-area-inset-top));
margin: 0;
padding-block-start: env(safe-area-inset-top);
padding-inline: max(0.5rem, env(safe-area-inset-left)) max(0.5rem, env(safe-area-inset-right));
transform: translateX(-50%);
background: var(--pico-background-color);
}

.app-bar h1 {
Expand Down Expand Up @@ -250,7 +260,12 @@ body > main {
user-select: none;
}

.app-version[data-updated="true"] {
.app-footer .app-version {
margin-inline-start: auto;
}

.app-version[data-updated="true"],
.deploy-clue[data-updated="true"] {
color: #39d353;
font-weight: 700;
opacity: 1;
Expand Down