diff --git a/AGENT_GUIDE.md b/AGENT_GUIDE.md index e566673..519dcc1 100644 --- a/AGENT_GUIDE.md +++ b/AGENT_GUIDE.md @@ -523,6 +523,9 @@ Desktop and mobile do not have separate editor implementations. Both use the sam ### PWA shell Cache shell/static assets only. Do not cache note/resource/API responses in ways that break shared-browser safety. +### Zoom, touch and hover CSS +One `viewportMeta` constant (`app/templates/shared.js`) is used by every page: `width=device-width, initial-scale=1.0, viewport-fit=cover, interactive-widget=resizes-content`. Zoom is allowed (WCAG 1.4.4; `user-scalable=no` also blocks pinch zoom on Android; iOS Safari ignores it anyway). Double-tap zoom on controls is off through `touch-action: manipulation`, and `public/viewport.js` does not pin the page scroll while the user is pinch-zoomed (`shouldPin`). `interactive-widget=resizes-content` only affects Android Chrome. The end of `styles.css` has the touch block (`@media (hover: none) and (pointer: coarse)`: no text-size inflation, tap flash, selection or callout on controls; selection stays on fields and `.cm-content`) and a `prefers-reduced-motion` block (the loading ring keeps turning). Pure `:hover` feedback rules must live inside `@media (hover: hover)`: iOS emulates hover on tap and leaves it stuck (a tapped toolbar button stayed highlighted). Reveal-on-hover affordances that mix `:hover` with another state have `(hover: none)` fallbacks and are exempt. `tests/touchCss.test.js` lints this. `/diag` can reproduce the old meta (`?zoom=0`, `?iw=0`) for comparison. + ### Service worker policy (`public/service-worker.js`) Deliberately small. Navigations go to the network; if that fails the worker serves the static `/offline.html` (inline styles, a Try again button that checks `/health` and reloads, auto-retry on the `online` event). Versioned `.js`/`.css` URLs (`?v=`, immutable by construction) are cache-first in a cache named `joplock-shell-`; any other Joplock cache is deleted on activate, other apps' caches are left alone. Nothing else is intercepted: **no HTML page, API response, fragment, note or attachment is ever cached** (shared-browser safety; the app has no offline notes). The install step precaches only `/offline.html` and an icon. It used to precache bare URLs that the `?v=` requests never matched (so assets were stored twice) and never cached `app.js`/TinyMCE. Covered by `tests/serviceWorker.test.js` (the real file in a sandbox) and `playwright-tests/pwa-offline.spec.js` (real browser: cached assets, offline launch, recovery). Not done: an in-app "new version, reload" prompt; the new worker takes over immediately (`skipWaiting` + `claim`). diff --git a/app/routes/resources.js b/app/routes/resources.js index 2f6050a..d6d5b7c 100644 --- a/app/routes/resources.js +++ b/app/routes/resources.js @@ -1,5 +1,6 @@ 'use strict'; +const { viewportMeta } = require('../templates/shared'); const { send, sendJson, readRawBody, parseMultipart, contentDispositionFilename, shouldInlineResource } = require('./_helpers'); // Joplin Server rejects files over its formidable maxFileSize (200MB) with an @@ -82,7 +83,7 @@ const handle = async (url, request, response, ctx) => { - + ${title}