v2.9.0 - rebrand and native app prep - #265
Merged
Merged
Conversation
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
lapwing | e00c53a | Commit Preview URL Branch Preview URL |
Jun 22 2026, 03:10 AM |
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
Coverage SummaryLines: 58.96% (6011/10195) · Statements: 58.04% · Functions: 56.37% · Branches: 54.04% Per-file coverage
|
Recolor the brand accent from teal to purple (#8746b3 = HSL 276 44% 49%): set --primary and its mirrors (--ring, --sidebar-primary, --sidebar-ring) in both light and dark (keeping the existing light/dark lightness split). Data-viz tokens (speed/telemetry scales) are untouched. Replace the gauge glyph in the app/page headers (Landing, Index, Login, Register, Forgot/Reset password, Admin) with the new web-logo.png via a small shared BrandLogo component. Functional gauge icons (weather pressure, g-force setting, garage tab, coach, analog-gauge overlay) are left as-is. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WyjporYvTUjrP2a29wKKPo
Purple theme + LapWing header logo
Two mobile/native polish fixes: - Tapping the LapWing logo (top-left) in an open session now closes the session and returns to the landing page, via a new `clearSession` on useSessionData. Added a `header.home` aria-label (seeded to all locales). - App shell now respects device safe-area insets so header content no longer sits under the status bar / notch on native and installed PWAs. Added `viewport-fit=cover` and a reusable `.safe-area-inset` utility applied to the session + landing shells (border-box keeps padding inside the full-height containers; a no-op where there's no safe area). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PpQppp3sdsZXLRUGf1F69g
…rmwrb Mobile UI cleanup: logo → home + safe-area insets
Replace the direct Bluetooth jump behind "Download from logger" with an image-based chooser of supported loggers: - PerchWerks Fledgling (DIY, Bluetooth) -> runs the existing BLE download - AiM MyChron 5/5S/2T (Wi-Fi, native app required) -> explanatory dialog; copy differs on the native shell (placeholder "TBD") vs the web app (native app in development, ~July 2026) - Alfano 6/7 (Bluetooth, coming soon) -> explanatory dialog (~early July '26) Also relabels the file-manager "Download from DovesDataLogger" button to "Download from logger". New `logger` i18n namespace (all 7 locales) and placeholder artwork under public/loggers/ for easy swap-out. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HLRuG1p8vhUXFP5V34WVET
…u-erwc9f Image-based logger picker for "Download from logger"
Three changes building on the logger picker: 1. Fix the home-screen "Download from logger" button. The picker was buried inside the lazy BLE chunk, so on the landing it sat behind a disabled Suspense fallback and clicks did nothing when that chunk stalled. Split the lightweight picker into an eager host (LoggerDownload); the heavy Fledgling BLE flow is mounted on demand once the Fledgling is picked, so the menu opens instantly while lib/ble/* stays off the initial payload. 2. Generic logger-connection layer (src/lib/loggers/). The download UI talks to every logger through one LoggerConnection interface (listLogs / downloadLog / disconnect); createFledglingConnection() adapts the BLE transport. This is the seam MyChron (native/Tauri) and Alfano will plug into. DeviceContext now tracks the connected loggerKind. 3. Device tab is Fledgling-gated: shows a "Fledgling only" notice when the connected logger isn't a Fledgling (groundwork for non-Fledgling connections, which have no settings/tracks/firmware surface). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HLRuG1p8vhUXFP5V34WVET
…u-erwc9f Fix home download button + generic logger-connection layer
The login screen now links to the Privacy Policy and Terms of Service, reusing the existing landing-namespace link labels (no new keys). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TcQYR6dBauo2iSyqt8VkyU
Restyle the Credits dialog trigger to match the Browser Compatibility button so the two read as a matched pair when placed side by side. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TcQYR6dBauo2iSyqt8VkyU
- Primary action is now a 50/50 split: an equal-billing "Download from logger" panel beside the drag-and-drop dropzone (most users pull logs straight off a logger). The separate logger ActionTile is removed. - Native (Tauri/Android) shell hides marketing surfaces it doesn't need: the hero pitch, the feature roadmap, the GitHub repo links and the sponsor button. The DIY "Build your own logger" tile stays. - Move Privacy/Terms out of the mid-page row: on the stable build they now flank the operated-by/version block in the footer. Credits moves up next to the Browser Compatibility button. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TcQYR6dBauo2iSyqt8VkyU
The Privacy and Terms pages hardcoded their back link to the home route, so opening Terms/Privacy from the sign-in or register screen and clicking back dumped the user on the landing page. Use a small goBackOrHome helper that steps back one history entry when there's an in-app page to return to, falling back to home only when the page was opened directly (fresh tab, bookmark, external link). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TcQYR6dBauo2iSyqt8VkyU
…native-hf5ydp UI cleanup: landing page split, leaner native shell, footer/login link tidy-up
Add docs/ble-protocol.md: a transport-agnostic specification of the full BLE connection stack (GATT services/characteristics, every command and response packet across file/battery/settings/track/firmware protocols, DIS version read, and a native-port checklist) so the connection can be replicated in any stack — e.g. the Tauri shell — without reading the TypeScript reference client. Cross-reference it from docs/ble.md and add a CLAUDE.md pointer plus a keep-in-sync rule so future wire-format changes update the spec. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016C8usikqirgjsfH7djYt8Q
When signed in the profile now renders two storage meters — cloud quota and on-device local — instead of only the cloud one. The local bar gets an (i) tooltip (click or hover; works on touch) explaining that the device's real free space isn't exposed to the app, so the bar is drawn against an arbitrary 10 GB advisory marker and can be ignored — local storage has no limit. - localUsage now uses a fixed LOCAL_ADVISORY_LIMIT (10 GB) instead of the browser's storage estimate, matching what the tooltip tells the user. - The cloud and local meters refresh independently so the local one stays accurate offline (no network) and updates live on on-device changes. - The "sign in to back it up" footnote is suppressed on the signed-in local bar, where it would be both redundant and wrong. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TcQYR6dBauo2iSyqt8VkyU
…umentation-fps9eq Document the DovesDataLogger BLE protocol as a portable wire spec
beta-proxy/ (the beta.lapwingdata.com reverse proxy) is a separate Worker from the main app, which Cloudflare's connected-repo CI builds — nothing was deploying the proxy, so it had to be pushed by hand. Add deploy-beta-proxy.yml: deploy on BETA pushes that touch beta-proxy/** (and on demand via workflow_dispatch) using cloudflare/wrangler-action, and dry-run validate it on PRs (no secrets, so fork PRs work too). The wrangler version is pinned for reproducible runs since beta-proxy has no committed lockfile. Deploy needs two repo secrets: CLOUDFLARE_API_TOKEN (Edit Workers) and CLOUDFLARE_ACCOUNT_ID. Documented in beta-proxy/README.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TcQYR6dBauo2iSyqt8VkyU
…s-local Profile: separate cloud + local storage bars with a local-limit tooltip
The limiter called check-login-rate before the login and incremented the IP's counter on EVERY attempt — successes included — so five tries (even correct ones) locked the IP for an hour. It also never reset on success and sub-threshold failures never aged out. Rework it around what actually happened: - pre-login "check" only reports lock state, never mutates; - a failure is recorded only when the password was actually wrong; - a success clears the IP's counter; - failures accumulate over a 15-minute sliding window (new updated_at column + shared trigger) so old stray failures don't pile up. The decision logic is extracted to a pure rateLimit.ts shared by the edge function and a Vitest regression test (vitest now also picks up supabase/functions pure-logic tests; coverage stays src/-only). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TcQYR6dBauo2iSyqt8VkyU
Fix login rate limiter locking out valid sign-ins
The PWA's only update path was the service worker noticing a byte-diff in its own file (registration.update() every 60s -> onNeedRefresh toast). That can silently stall behind HTTP/CDN caching, leaving a tab pinned to a stale build with no prompt. Add an independent freshness signal: vite emits a tiny /version.json next to the bundle from the same build constants as buildInfo, excluded from the Workbox precache. versionCheck.ts fetches it uncached (on start, on an interval, and on focus/reconnect) and compares against the running build; when a genuinely newer deploy is live it surfaces the existing "Update ready" toast. The toast is now shared/deduped between the SW callback and the poller, and its Refresh hard-reboots (updateSW(true) with an unregister+reload fallback for the stale-SW case). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R2ErrGGyzGeQDyq6hwdRYU
…-qxj01o Add deploy-version check independent of the service worker
Registration accepted any 6+ character password with no feedback. Add an offline password-strength evaluator (lib/passwordStrength.ts) that enforces length plus character-class rules, and a PasswordStrengthMeter component that shows a live strength bar and per-rule checklist as the user types. Block sign-up until every rule passes, and translate the new strings across all shipped locales. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rs8GJ1Wsy1izavKFUDvFSM
…rd-security-7q2w7k Add password rules + realtime strength checker to sign-up
CI: auto-deploy the beta-proxy Worker
The safe-area-inset utility was only applied to the main app shell and landing page, leaving other full-screen surfaces drawing edge-to-edge on native/installed-PWA. Their top chrome sat under the device status bar: - File-manager drawer: its garage/profile/device header (title, battery, disconnect, close) is a portal overlay outside the padded app shell. - Full-page routes: login, register, admin, privacy, terms, delete-account, 404 — each owns its own min-h-screen root. Apply the existing reusable .safe-area-inset utility to all of them. No-op on browsers/desktops without a safe area (insets resolve to 0). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FojvNNtPyxoixHn2mR5Txu
Wires the MyChron tile in the logger picker to a real native-only download flow, mirroring the existing BLE (Fledgling) downloader. Single-download scope. - mychron/ipc.ts: typed Tauri IPC client (logger_connect/list/download/disconnect) reaching @tauri-apps/api via a dynamic import so it code-splits out of the web bundle entirely (web build never fetches Tauri). - createMychronConnection(): second LoggerConnection adapter (native, Wi-Fi). - MyChronDownload.tsx: lazy UI mirroring the BLE flow's panels + a Wi-Fi "pick your MyChron" state; imports via the async XRK path (parseDatalogFile) since the device returns binary XRK bytes; owns and always tears down its connection. - Shared file-list/progress panels extracted to components/loggers; pure progress formatters + computeProgress moved to transport-neutral lib/loggers/progress.ts (ble/format.ts re-exports them). - Picker/host wiring, logger i18n keys, docs, Credits, and changelog updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AbxsCSt8grPs75vfJyt8wP
…dding-7k3868 Extend safe-area insets to drawer + full-page routes
Replaces the English fallback for the mychron.flow.* keys with proper translations in es/fr/de/it/pt-BR/ja, reusing established terms (Browse Files, Cancel, Retry, Connecting) for consistency. German uses WLAN; the rest keep Wi-Fi. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AbxsCSt8grPs75vfJyt8wP
…ta-kuv5nu # Conflicts: # CHANGELOG.md
…v5nu Native mychron downloads
Adds a native-only Settings → MyChron text field for the Wi-Fi name prefix the Android system picker filters on, so a logger that broadcasts a different name can still be found. Persists via useSettings (localStorage), defaults to "MYCHRON5" (mirrors MYCHRON_SSID_PREFIX), and is read in MyChronDownload with the constant as fallback. Field is gated on isNativeApp(). Translated in all six locales; docs + changelog updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AbxsCSt8grPs75vfJyt8wP
…v5nu Make MyChron Wi-Fi SSID prefix configurable in Settings
- Sticky landing banner: the landing header is now pinned to the top (login button always visible; content scrolls cleanly under the status bar on mobile). Top safe-area inset moves onto the sticky header. - Shared BrandHeader: extract the logo+name header into one sticky, clickable (→ home) component, reused on login/register/privacy/terms. Privacy/terms now also carry safe-area padding via the shared header. - Native: hide the entire pricing-cards section on the registration page (only the free sign-up form shows). Profile billing buttons were already gated. - External links open in the system browser on native (Tauri): openExternal now routes through @tauri-apps/plugin-opener when the __HTT_NATIVE__ bridge isn't wired, instead of navigating the in-app WebView. Dynamically imported so it stays out of the web bundle. - Add safe-area-x / safe-area-top CSS utilities for the sticky-header layout. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AbxsCSt8grPs75vfJyt8wP
- Swap the placeholder SVGs for the real MyChron/Alfano/Fledgling product photos (public/loggers/*.png). - Mobile: compact horizontal cards (image thumbnail + text) instead of full-height image cards that filled the screen; from sm+ the full image card layout is unchanged. - Cap the dialog height with internal scroll + native safe-area padding so a tall list never pushes the close (X) above the top of the screen on mobile. - Add an explicit Close button on mobile (desktop keeps the corner X). - Add a trademark/ownership note for the MyChron and Alfano brands (translated across all locales). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AbxsCSt8grPs75vfJyt8wP
The earlier safe-area-inset on the picker dialog set padding to a bare env() inset, which collapses to 0 on un-notched devices and overrode the dialog's default p-6 — scrunching the content against the panel border. Replace it with a safe-area-modal utility (max(1.5rem, env()) on every side) so padding never drops below the base while still clearing notches, and add a w-[calc(100%-2rem)] side gutter so the panel isn't edge-to-edge on mobile. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AbxsCSt8grPs75vfJyt8wP
MyChron download only works in the native app, which isn't public yet, so show a "Coming soon" badge to web users (matching Alfano). On native it works, so no badge there. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AbxsCSt8grPs75vfJyt8wP
…weaks Landing/auth UI polish + native gating tweaks
…ture list - Cut the unreleased changelog section as 2.9.0 and bump package.json to 2.9.0. Coach stays on the published npm package (@perchwerks/eye-in-the-sky@0.5.0) — BETA has been on it since the 2.7.0 cut (never flipped back to the git dep), so it's already in release config; no flip needed for the main push. - Move the open-source GitHub repo links off the landing page and into the About dialog, under the "Free & Open Source" section, above the feature list. Shown on all platforms there (consistent with Credits), so the native landing stays lean. - Refresh the About feature list with the headline additions from the last month (7 languages, native Android app + MyChron Wi-Fi download, phone-as-logger GPS timing, optional cloud sync, AI coach), translated across all locales. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AbxsCSt8grPs75vfJyt8wP
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AbxsCSt8grPs75vfJyt8wP
Release 2.9.0: About-dialog GitHub links + feature refresh + version cut
- Shorten logger names to "AiM MyChron 5+" and "Alfano 6+" so they fit on one line and don't pin to specific models (the protocol may cover newer ones). - Add the shared sticky BrandHeader to the account-deletion page. - Remove the "Telemetry Data Viewer" tagline from the landing header for now (locale keys kept for a future replacement). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AbxsCSt8grPs75vfJyt8wP
Picker name tidy, delete-account header, drop landing tagline
TheAngryRaven
marked this pull request as ready for review
June 22, 2026 03:11
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Promotes BETA → main as v2.9.0. Everything since 2.8.0, grouped by theme. Coach ships on the published npm package (
@perchwerks/eye-in-the-sky@0.5.0) — release config.🎨 Rebrand → LapWing
BrandLogo), refreshed app icons + favicon.lapwingdata.com(+beta.lapwingdata.comproxy).📱 Native Android app prep (Tauri)
isNativeApp()gates native-only behavior.__HTT_NATIVE__bridge), not the in-app WebView.🔌 Loggers & downloads
LoggerConnectionlayer (listLogs/downloadLog/disconnect) — one seam for all transports.createMychronConnection(); Android system Wi-Fi picker; native-only Settings → MyChron SSID-prefix field.@tauri-apps/apiis lazy-loaded (off the web bundle).🖥️ Landing & auth/legal UI
BrandHeader(→ home) on login / register / privacy / terms; privacy/terms gain mobile safe-area padding.☁️ Accounts & cloud
version.json).🐛 Fixes
Full detail in
CHANGELOG.mdunder [2.9.0]. Depends on #281 (release cut + About move) landing in BETA first.🤖 Generated with Claude Code