The V-viewfinder was approved during the favicon-v2 picker at
/v2/, then deployed prematurely (e7b9756) and reverted (81effca) after
the 'design pick is not a deploy command' lesson. Deploying it now with
explicit go-ahead.
Files: yellow V outline with the Camogli harbor visible inside, navy
field outside. Replaces the split-W (two Vs forming a W) across:
- favicon-16/32/64
- apple-touch-icon (180)
- icon-192 + icon-512 manifest icons
- icon-512-maskable (V at 65% safe zone)
- favicon.svg vector
- favicon.ico multi-res
- root-level apple-touch-icon{,-precomposed}.png for iOS fallback paths
Cache-bust query bumped to ?v=20260515-vviewfinder so browsers refetch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
iOS Safari caches the apple-touch-icon per origin and ignores byte-level
changes on the same URL. Adding a version query forces a refetch on
fresh visits without renaming the source files. Buttressed across all
standalone Twig templates and the SPA index plus the manifest icons so
Chrome desktop also refetches.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lets users opt into the new atmospheric design without affecting users on v1.
Adds a beta-flag toggle in Settings → Design. Server-side preference persists
across devices; a cookie mirrors it so unauthenticated Twig pages do correct
first-paint without an extra DB roundtrip.
Backend:
- User.designVersion column (nullable VARCHAR(10); null defaults to 'v1')
- Migration Version20260515120000
- PATCH /api/user/design endpoint accepting 'v1'|'v2', sets wevisto_design cookie
- SpaController injects data-design on <html> + refreshes the cookie on every
SPA load (keeps cross-device pref in sync)
- Twig templates (base, login, register, help, setup, token-*) read the
cookie via {{ app.request.cookies.get('wevisto_design')|default('v1') }}
so login/setup pages also respect the user's design choice
Frontend:
- design-v2.scss — opt-in overlay scoped under [data-design="v2"]. Overrides
--color-* tokens to dusk variants per theme (warm-craft → amber, ocean-dusk
stays, etc.), adds harbor photo backdrop via body::before with theme tint
via body::after. Glass-card blur on existing surfaces. v1 untouched.
- harbor.jpg shipped as a public asset (270KB, single-fetch, cached)
- User type gains designVersion ('v1' | 'v2')
- SettingsView toggle (Original / Atmospheric) calls the API, updates the
data-design attribute optimistically, reverts on failure
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
iOS Safari's Add-to-Home-Screen flow probes /apple-touch-icon.png at the
site root in addition to the <link rel> on the page. Those root paths
currently 302 through Symfony's auth firewall to /login, so iOS gets HTML
where it expects a PNG and falls back to whatever it cached from earlier
installs (the 1 KB placeholder icon). Dropping the real PNG (and the
-precomposed alias) directly in public/ makes nginx serve them as static
files, ahead of the firewall.
Also adds favicon.svg and a multi-size favicon.ico at the root for
browsers/bots that probe / paths instead of reading <link>, and adds
sizes="180x180" to every apple-touch-icon link so iOS doesn't have to
guess.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Login, register, help, token-approve/decline, and setup pages each have
their own <head> (don't extend base.html.twig), so updating base alone
left them without favicon refs. Adds the same four <link> tags after the
viewport meta in each standalone template.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- frontend/public/logo.svg: Camogli photo with We[V]isto knockout wordmark
(yellow V accent), embedded base64 so the SVG is self-contained
- brand/: raw source (15.7MB Camogli original) + 900x900 crop used in the
SVG, plus a short README documenting both
- Login, register, setup index/configure, help: linked logo badge above
the page heading
- Email template: logo bumped to 64x64 (was 30 tall — wordmark unreadable)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Updated: SPA <title>, PWA manifest name/short_name, iOS web-app title,
"Install"/"Pin to home screen" copy, HomeView empty state, all Twig page
titles (login/register/setup/token/help), and the share-notification
email header. Left alone: the firmware-broadcast SSID PictureFrame-XXXX
(coordinated firmware change needed) and internal code/comment refs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- crop: invert overlay shading; the destination-out trick on a
semi-transparent fill was leaving the *inside* of the crop more
transparent than the outside, so the keep-area read as darker
than the discard-area. Replace with 4 explicit dim-strips.
- stickers: floating trash handle now glues to the selected
sticker's top-right corner instead of an off-canvas X that
testers missed.
- stickers: replace the curated grid with an emoji-keyboard
picker — recently-used row, custom-sprite row (santa hat as
inline SVG), then an input that pops the OS emoji keyboard.
Recents persist in localStorage; legacy stickers fall back to
the old STICKERS table.
- pwa-install modal: drop "browser chrome" — beta tester read it
as the literal Chrome browser.
- /setup landing page: tighten "Set up your frame" copy.
Use case: old owner sells the device to a friend. Friend holds the BOOT
button to wipe NVS, joins the device's AP, sets new WiFi. The old
owner's account is still bound to the MAC server-side, so without
explicit consent the friend would silently take over (or, worse, the
old owner's photos would keep displaying until claim).
Flow now:
- GET /setup/{mac} detects MAC bound to anyone and renders a
"Claim this frame as my own" checkbox + a banner explaining what
the takeover wipes. Both register and login panels carry the
checkbox; submitting either form without it bounces back through
the index with a session-flashed error.
- DeviceService::linkToUser now requires allowClaim=true to
transfer ownership. Without it, throws DeviceClaimRequiredException
that the controller catches and turns into the bounce-with-error.
- On a successful claim, the takeover wipes:
* old image-device approvals
* device_image_history rows for the device
* name, wakeTimes, currentImage*, lockedImage, nextPollExpectedAt
so the new owner starts from a fresh slate, not inheriting the
seller's "Living Room / 4:30 AM" preset.
- Already-logged-in user visiting /setup/{mac} for someone else's
device falls through to the form (instead of silently transferring
on page load) so the checkbox is the only path.
Test matrix:
- SetupControllerTest: 5 new functional cases — checkbox renders for
bound MACs, register/login without checkbox bounce + retain old
ownership, register WITH checkbox transfers + purges, logged-in
other-user falls through to form.
- DeviceServiceTest: 3 new unit cases — throw without consent,
isClaimedByAnotherUser true/false matrix, takeover resets device
state.
Coverage: 99.70% lines / 98.19% methods backend, 333 frontend tests
green via ddev tests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
RepeatedType renders as a wrapper div when called as a single widget,
producing unstyled inputs with no mismatch error handling.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>