Skip to content

plan 0016 (3/3): prompt for firmware and tracks on connect - #386

Merged
TheAngryRaven merged 12 commits into
BETAfrom
claude/device-connect-prompts
Aug 6, 2026
Merged

plan 0016 (3/3): prompt for firmware and tracks on connect#386
TheAngryRaven merged 12 commits into
BETAfrom
claude/device-connect-prompts

Conversation

@TheAngryRaven

@TheAngryRaven TheAngryRaven commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Summary

Last of three (plan 0016). Replaces #383, which GitHub had locked into a stack. Targets BETA directly — merge after #387 and #385 and this diff shrinks to just its own work.

Merge order: #387#385 → this. All three go to BETA; the branches are chained, so until the other two land this diff also shows their commits.

Connect a logger and two questions run in order:

  1. Firmware, with a new Remind me tomorrow button. First because accepting it reboots the device and drops the link — anything queued behind it would be thrown away — and because it's the rarer of the two.
  2. Tracks, the yes/no from plan 0016 (2/3): the two-screen sync + rename wizard #385, and only when the sync plan actually has rows. A prompt that fires on every connect to say "nothing to sync" is worse than silence.

Declining the track prompt is scoped to the connection: no stored suppression, and the flow only re-runs when a new connection appears. Being asked again after deliberately reconnecting is expected; twice on one connection is nagging.

The snooze

Keyed by device and version, and checked against the version actually on offer rather than up front. So your two loggers snooze independently, and a brand-new release asks again immediately instead of inheriting yesterday's "tomorrow".

Storage is a standalone src/lib/firmwareUpdateReminder.ts in the pendingCheckout.ts shape — private localStorage key, pure parse(raw, now) the getters wrap. Deliberately not AppSettings: that's cloud-synced via accountExport.ts, and "I'll do it tomorrow" is a decision about this browser and this logger, not something to push to every device you own.

Two changes that made it possible

  • checkForUpdates gained silent and suppress, and now returns whether an update was offered. It toasted on every non-update outcome — "up to date", "couldn't read the version", "check failed". Right for a button press, wrong for a check the user didn't ask for.
  • The firmware dialog had to be hoisted. It lived inline in FirmwareUpdateSection, which mounts only with the drawer open, on the Device tab, on the settings sub-tab, over BLE — so an on-connect check had nowhere to render. FirmwareUpdateProvider now owns the single useFirmwareUpdate instance and renders FirmwareUpdateDialog once, high in the tree.

Related Issues

Supersedes #383. Merge after #387 and #385.

Type of Change

  • New feature
  • Refactor / reusability improvement
  • Documentation

Checklist

  • bun run lint passes
  • bun run typecheck passes
  • bun run test:run passes (2719 tests, 190 files)
  • bun run build succeeds
  • Feature works offline — the firmware step is skipped when offline rather than surfaced as a failure; the manifest fetch is the only network call and it's guarded by useOnlineStatus
  • Docs updated — plan 0016 (now marked DONE), CHANGELOG.md
  • 1 new i18n key in all seven locales; parity test green

Notes for Reviewers

The dialog hoist is the riskiest thing in this PR and it's worth a look even though it's mechanical. FirmwareUpdateSection went from ~200 lines to ~55 — everything removed is now in FirmwareUpdateDialog.tsx unchanged apart from the new snooze button. The alternative (a second useFirmwareUpdate instance for the auto-check) would have meant two GATT version reads per connect and two dialogs racing to be on screen, so I took the refactor instead.

Things I'd flag:

  • This is the part I can't test. The whole PR is provider wiring and effects, and the test environment is node with no testing-library — so DeviceConnectFlow and the dialog have no unit tests, only the pure firmwareUpdateReminder module does (14 tests). The connect sequencing genuinely needs hardware to verify. The one behaviour I'd specifically watch: after you dismiss or snooze the firmware dialog, the track prompt should follow — that transition keys off confirmOpen going back down, which is the least robust thing here.
  • DeviceProvider is mounted twice in Index.tsx (landing and main), so FirmwareUpdateProvider + DeviceConnectFlow are mounted twice too — never simultaneously, since the branches are exclusive, but worth knowing.
  • I did not build a firmware capability layer. Per your note it's "not doing that yet, just consider it" — and nothing here needs one, since the object track format predates every shipped release. The existing boolean DeviceDetails.supportsSprintTracks is the seam if you ever want one.

claude added 12 commits August 5, 2026 05:51
buildTrackJsonForUpload emitted a bare JSON array of courses. The firmware
parses that — but its array branch (sd_functions.ino parseTrackFile) blanks
longName, shortName and defaultCourse, and every course falls back to
lengthFt = 0. lengthFt is what CourseDetector ranks courses by, so a track
uploaded from this app could never be course-detected and dropped straight to
Lap Anything, and the blank shortName reached the DOVEX header's short_name
column.

Emit the object form instead — the same shape the app's own track files and
the on-device course creator already write, and one the firmware has parsed
since well before any shipped release, so no version gate is needed.

Also add parseDeviceTrackFile(), which keeps the wrapper's longName/shortName/
type/defaultCourse rather than discarding them; parseDeviceCourseJson stays as
a thin wrapper over it for the callers that only want courses. The rename flow
needs longName, and needs shortName because for a device-authored track the
FILENAME is the 12-char longName (N260803_1432.json) while the 8-char
shortName the sync merge keys on lives inside the file.

The old "emits a JSON array of courses (not a wrapping object)" test asserted
the lossy shape as the contract, which is how this survived review; it is
replaced with assertions on the metadata the device actually consumes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ESkRRtF4vRrANPL6huSgmD
A track the on-device course creator wrote is stored at N260803_1432.json but
declares shortName "08031432" — 8 characters, chosen by the firmware precisely
because that is this app's Track.shortName budget and the key its sync merge
uses. buildMergedTrackList keyed on the FILENAME instead, so a track imported
from the device could never be matched to the file it came from: it stayed
"device_only" forever and the sync kept re-offering it.

Separate the two concepts. DeviceTrackFile.shortName is now the identity (the
declared shortName, falling back to the filename base only for legacy
bare-array files that declare nothing), and the new fileName / deviceFileName
carry the location. deviceTrackFileFrom() owns that rule so it is unit-tested
rather than buried in the tab, and every write path now targets the real file
instead of `shortName + ".json"` — which would otherwise orphan the original
and leave two copies on the card.

Also fixes the other half of the same nag: handleDownloadToApp never passed a
shortName to addTrack, and buildMergedTrackList skips app tracks that have
none, so downloaded tracks were invisible to the merge whatever the key was. It
now carries the shortName over and names the track from the file's longName.

The two course-level writers went through rebuildDeviceTrackJson so editing one
course stops stripping the file's wrapper metadata and resetting every lengthFt
— the same loss the bare-array uploader caused, reached from a different button.

Verified by reverting the identity rule and watching the round-trip test report
2 merged entries instead of 1 — literally the app_only/device_only split that
made the prompt re-fire. The first draft of that test derived its input from the
value under test and passed either way; it now spells the expectation out.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ESkRRtF4vRrANPL6huSgmD
Four modules, all Arduino-free pure logic with no React, because the test
environment is "node" with no testing-library — a dialog cannot be rendered, so
anything worth asserting has to live outside the component.

- deviceGeneratedNames: recognises the on-device course creator's
  N{YYMMDD}_{HHMM} names and MMDDHHMM short names. The date and time parts are
  validated, so a real name that merely looks the part isn't mistaken for a
  placeholder and the user pushed to rename something they already named.

- deviceSyncPlan: decides what a sync would offer and in which direction.
  Synced tracks are dropped; app tracks the user didn't create are never pushed
  (the two we ship are reference data, not "unknown tracks"); a mismatch uploads
  the app's version after importing any course walked on the device.
  Crucially it also refuses to offer rows that could never converge — mixed
  circuit+sprint tracks, tracks past the firmware's MAX_LAYOUTS (whose tail its
  parser silently ignores, so the file can never read back as written), and
  sprint tracks on a transport that can't reach /TRACKS/SPRINT. Each of those
  would otherwise report a difference on every connect forever. They are
  surfaced with a reason rather than trimmed to fit: dropping a user's courses
  to turn a checkmark green is the worse failure.

- deviceSyncNames: the edit rules and the save gate. A short name follows the
  long name until the user takes it over, and editing the long name takes it
  back; a course name follows its track's name the same way. Track names are
  required for both kinds — a venue is permanent. Course names are required for
  circuit only: a sprint venue re-lays its course every event, so the date it
  was walked genuinely is the most useful label.

- deviceSyncOps: the ordered operation list. Put before delete, so a failure
  between them leaves the track on the card twice rather than nowhere; device
  before app, so a failure after the write leaves a correctly-named file the
  next connect offers as a plain download, instead of stranding a renamed app
  track beside its old device file. FAT is case-insensitive, so a case-only
  filename change is not a rename — deleting "the old file" would delete the one
  just written.

The load-bearing tests replay a plan back through deviceTrackFileFrom and
buildMergedTrackList and assert "synced". If that ever fails, the on-connect
prompt re-fires on every connect, which is the whole thing this is avoiding.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ESkRRtF4vRrANPL6huSgmD
Captures why the sync path had four separate ways to produce a track that could
never reach "synced", the identity-vs-location split that fixes the worst of
them, why operation order (put before delete, device before app) is the
load-bearing part, and the decisions taken with the owner — including the ones
about what NOT to build: no truncation, no firmware capability layer, no new
opcodes.

Also records that no capability gate is needed here, with the evidence: the
firmware has parsed the object track format since before any shipped release.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ESkRRtF4vRrANPL6huSgmD
addTrack/addCourse deliberately only ever ADD: they backfill a short name only
when there isn't one, and never remove a course. That is right for "add this
one thing" and wrong for a sync, which has already worked out what the track
must look like for both sides to agree — anything less than a wholesale write
leaves them disagreeing, and the next connect re-offers the same track.

Courses are marked user-defined on the way in because saveUserTracks drops any
that aren't, which would silently discard a course walked on the device.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ESkRRtF4vRrANPL6huSgmD
deviceSyncRunner walks the operation list with injected executors, so the risky
part is testable without a radio or a browser. It keeps going after a failure,
but a failed track ABANDONS ITS OWN remaining operations — once the new file
didn't write, deleting the old one destroys the only copy, which for a course
walked in the field is unrecoverable. Other tracks still run: one track failing
is no reason to leave the other nine untouched, and the plan orders each
track's work contiguously so that split is clean.

deviceSyncFetch owns reading both folders. The Device → Tracks tab now uses it
instead of its own copy of the loop, so there is exactly one place that applies
deviceTrackFileFrom's identity rule — a second copy that keyed files by
filename would quietly reintroduce the bug where an imported track never
matches the file it came from. Behaviour is unchanged: same order, same
per-file error swallowing, same sprint-capability skip.

buildDeviceSyncSnapshot also collects the short names of tracks the plan is NOT
touching, so a rename can't be pointed at an already-synced track's file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ESkRRtF4vRrANPL6huSgmD
Two screens, the selection, and the validation that decides whether "Save &
import" can fire — all as pure functions, because the test environment is node
with no testing-library and logic left in a component is logic nobody checks.

Three rules worth naming:

- Unchecking a row stops it being validated. Otherwise one track you don't want
  to name blocks the entire sync with no way past it.
- A course name follows its track's name until the user types in it, and going
  Back to rename the track re-points every course that is still following. A
  name they typed themselves is never overwritten.
- canSave re-checks the TRACK screen, not just the course screen. Going forward
  and then back and clearing a track name must not leave Save live.

Duplicate short names are caught across the whole selection and against tracks
outside the plan, per kind — two tracks sharing a short name are one file on
the device, so the second write silently overwrites the first, while the same
short name in the circuit and sprint folders is two legitimate files.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ESkRRtF4vRrANPL6huSgmD
Screen 1 names the tracks: a checkbox per row, an upload/download bubble, a
sprint bubble, and — only on rows the logger named itself — a full-name box
with a narrower short-name box beside it that fills in as you type. Screen 2
does the same for course names with a circuit/sprint bubble. Back/Next, then
Back and "Save & import".

Rows the sync refuses to attempt are listed underneath with the reason, rather
than silently missing.

The component holds one useState and the markup; every decision comes from the
pure modules. Problem and skip-reason strings are spelled out in switch
statements because the i18n keys are literal-union typed — a computed key won't
typecheck, which is the point.

37 new keys across all seven locales, matching each language's existing
terminology for track and course. The walked-on date renders in UTC: the name
encodes the GPS clock, so a local-time render would show the wrong minute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ESkRRtF4vRrANPL6huSgmD
… itself

checkForUpdates toasted on every non-update outcome — "up to date", "couldn't
read the version", "check failed". That is right when the user pressed the
button and wrong for a check that fires on connect: they didn't ask, and being
told nothing happened is worse than being told nothing. It now takes a `silent`
option, and returns whether an update was actually offered so an automatic
caller knows whether it still owns the screen.

The snooze is keyed by DEVICE AND VERSION, and checked against the version
actually on offer rather than up front. Two consequences, both wanted: the
owner's two loggers snooze independently, and a NEW release asks again
immediately instead of inheriting yesterday's "tomorrow".

Storage is a standalone module in the pendingCheckout.ts shape — a private
localStorage key and a pure parse(raw, now) the getters wrap, so the 24h rule is
testable without touching the clock. Deliberately NOT AppSettings: that is
cloud-synced, and "I'll do it tomorrow" is a decision about this browser and
this logger, not something to push to every device the user owns. A future
timestamp is treated as live rather than discarded, so winding the clock back
can't silently un-snooze everything.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ESkRRtF4vRrANPL6huSgmD
The firmware dialog used to be inline in FirmwareUpdateSection, which mounts
only when the drawer is open, on the Device tab, on the settings sub-tab, while
connected over BLE. A check that fires on connect had nowhere to show itself.
Split the dialog out and give a FirmwareUpdateProvider ownership of the single
useFirmwareUpdate instance, rendered once high in the tree. Mounting a second
instance instead would mean two device-version reads over GATT and two dialogs
racing to be the one on screen.

DeviceConnectFlow then runs the two questions in order on each new connection:

- FIRMWARE FIRST, because accepting it reboots the device and drops the link —
  anything queued behind it would be thrown away. It is also the rarer of the
  two, and "remind me tomorrow" keeps it rare.
- THEN TRACKS, and only when the sync plan actually has rows. A prompt that
  appears on every connect to say "nothing to sync" is worse than silence, and
  the plan already excludes the rows that could never converge.

Declining the track prompt is scoped to the connection: no stored suppression,
and the flow only re-runs when a new connection appears. Being asked again after
deliberately reconnecting is expected; being asked twice on one connection is
nagging.

Offline, the firmware step is skipped rather than surfaced — the manifest is a
network fetch and that is not this connection's problem. A failure reading the
card is logged, not toasted, for the same reason the auto-check went silent: the
user didn't ask, and the Device → Tracks tab is still there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ESkRRtF4vRrANPL6huSgmD
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
lapwing e59adfd Commit Preview URL

Branch Preview URL
Aug 05 2026, 06:37 AM

@supabase

supabase Bot commented Aug 6, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project tdxloldxzvnzdivdazzd because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

Coverage Summary

Lines: 58.28% (7485/12841) · Statements: 57.43% · Functions: 55.17% · Branches: 55.2%

Per-file coverage
File Lines Functions Branches
src/components/admin/supportAttachment.ts 0% 0% 0%
src/components/map/positionArrowMarker.ts 0% 0% 0%
src/components/video-overlays/dataSourceResolver.ts 85.07% 71.42% 80.82%
src/components/video-overlays/overlayUtils.ts 100% 100% 100%
src/components/video-overlays/registry.ts 100% 100% 100%
src/components/video-overlays/sectorUtils.ts 94.73% 100% 84.37%
src/components/video-overlays/themes.ts 100% 100% 100%
src/components/video-overlays/types.ts 100% 100% 100%
src/hooks/use-mobile.tsx 0% 0% 100%
src/hooks/use-toast.ts 0% 0% 0%
src/hooks/useAuth.ts 100% 100% 100%
src/hooks/useDataLoader.ts 14.11% 20% 17.02%
src/hooks/useDocumentHead.ts 0% 0% 0%
src/hooks/useEngineManager.ts 0% 0% 0%
src/hooks/useFileManager.ts 0% 0% 0%
src/hooks/useFirmwareUpdate.ts 0% 0% 0%
src/hooks/useKartManager.ts 100% 100% 100%
src/hooks/useLapManagement.ts 0% 0% 0%
src/hooks/useLapOverlays.ts 0% 0% 0%
src/hooks/useLapSnapshots.ts 0% 0% 0%
src/hooks/useNativeFirmwareUpdate.ts 0% 0% 0%
src/hooks/useNoteManager.ts 0% 0% 0%
src/hooks/useOnlineStatus.ts 0% 0% 0%
src/hooks/usePlayback.ts 0% 0% 0%
src/hooks/useReferenceLap.ts 0% 0% 0%
src/hooks/useSessionData.ts 0% 0% 0%
src/hooks/useSessionMetadata.ts 0% 0% 0%
src/hooks/useSettings.ts 0% 0% 0%
src/hooks/useSetupManager.ts 0% 0% 100%
src/hooks/useSimPlayback.ts 0% 0% 0%
src/hooks/useStripePrices.ts 0% 0% 0%
src/hooks/useSubscription.ts 0% 0% 0%
src/hooks/useTemplateFields.ts 0% 0% 0%
src/hooks/useTemplateManager.ts 0% 0% 0%
src/hooks/useTrackEditorForm.ts 0% 0% 0%
src/hooks/useVehicleManager.ts 0% 0% 100%
src/hooks/useVideoSync.ts 0% 0% 0%
src/hooks/useWakeLock.ts 0% 0% 0%
src/hooks/useWaybackImagery.ts 0% 0% 0%
src/lib/test/idb.ts 100% 100% 100%
src/lib/aimParser.ts 96.21% 100% 83.05%
src/lib/alfanoParser.ts 93.44% 100% 75.66%
src/lib/appActivity.ts 0% 0% 100%
src/lib/billing.ts 97.36% 100% 98.21%
src/lib/billingClient.ts 0% 0% 0%
src/lib/ble/test/mockBle.ts 96% 90% 50%
src/lib/ble/battery.ts 93.33% 100% 87.5%
src/lib/ble/connection.ts 0% 0% 0%
src/lib/ble/dfu/dfuPackage.ts 96.66% 100% 81.25%
src/lib/ble/dfu/dfuTypes.ts 100% 100% 100%
src/lib/ble/dfu/firmwareImage.ts 100% 100% 100%
src/lib/ble/dfu/firmwareManifest.ts 96.92% 92.85% 93.18%
src/lib/ble/dfu/firmwareUpdateError.ts 100% 100% 100%
src/lib/ble/dfu/index.ts 100% 100% 100%
src/lib/ble/dfu/version.ts 96.77% 100% 83.33%
src/lib/ble/fileTransfer.ts 90.69% 95% 72.91%
src/lib/ble/firmwareCrc.ts 100% 100% 100%
src/lib/ble/firmwareUpload.ts 90.62% 88% 84.31%
src/lib/ble/format.ts 100% 100% 100%
src/lib/ble/index.ts 100% 100% 100%
src/lib/ble/internal.ts 100% 100% 50%
src/lib/ble/settings.ts 93.6% 100% 85.29%
src/lib/ble/trackOpcodes.ts 100% 100% 100%
src/lib/ble/trackSync.ts 89.89% 90.9% 72.72%
src/lib/ble/types.ts 100% 100% 100%
src/lib/bleDatalogger.ts 100% 100% 100%
src/lib/blogPosts.ts 100% 100% 97.22%
src/lib/brakingZones.ts 97.14% 100% 86.11%
src/lib/browserCompat.ts 0% 0% 0%
src/lib/buildInfo.ts 100% 100% 100%
src/lib/canvas2d.ts 100% 100% 91.66%
src/lib/channels.ts 100% 100% 84.61%
src/lib/chartAxis.ts 98% 100% 82.92%
src/lib/chartColors.ts 100% 100% 100%
src/lib/chartUtils.ts 100% 100% 97.05%
src/lib/contactMessage.ts 47.61% 50% 43.75%
src/lib/courseDetection.ts 97.34% 100% 81.11%
src/lib/courseLineStyle.ts 100% 100% 100%
src/lib/courseSectors.ts 100% 100% 94.73%
src/lib/datalogParser.ts 83.56% 80% 81.81%
src/lib/db/index.ts 0% 0% 0%
src/lib/db/submissionMaterialize.ts 100% 100% 97.5%
src/lib/db/supabaseAdapter.ts 0% 0% 0%
src/lib/db/types.ts 100% 100% 100%
src/lib/dbUtils.ts 76.23% 80% 17.2%
src/lib/debugConsole.ts 57.74% 61.11% 47.72%
src/lib/deviceGeneratedNames.ts 100% 100% 100%
src/lib/deviceSettingsSchema.ts 93.33% 100% 96.42%
src/lib/deviceSyncFetch.ts 100% 100% 100%
src/lib/deviceSyncNames.ts 100% 100% 100%
src/lib/deviceSyncOps.ts 100% 100% 92.85%
src/lib/deviceSyncPlan.ts 100% 100% 96.96%
src/lib/deviceSyncRunner.ts 95.65% 100% 90%
src/lib/deviceSyncWizard.ts 100% 100% 68.57%
src/lib/deviceTrackSync.ts 100% 100% 96.94%
src/lib/doveParser.ts 89.6% 72.72% 77.27%
src/lib/dovexParser.ts 88.31% 100% 82.85%
src/lib/driverProfileGroups.ts 100% 100% 83.33%
src/lib/emailValidation.ts 100% 100% 100%
src/lib/engineStorage.ts 100% 75% 100%
src/lib/engineUtils.ts 100% 100% 91.66%
src/lib/fieldResolver.ts 100% 100% 83.33%
src/lib/fileBrowserTree.ts 98.87% 97.5% 89.36%
src/lib/fileLoadingState.ts 100% 100% 100%
src/lib/fileStorage.ts 82.79% 78.12% 72.22%
src/lib/firmwareUpdateReminder.ts 100% 100% 100%
src/lib/fnv1a.ts 100% 100% 100%
src/lib/garageEvents.ts 100% 100% 100%
src/lib/gforceCalculation.ts 100% 100% 97.22%
src/lib/ggDiagram.ts 100% 100% 94.73%
src/lib/gps/customGps.ts 100% 100% 82.05%
src/lib/gps/dovepWriter.ts 100% 100% 92%
src/lib/gps/gpsFix.ts 100% 100% 100%
src/lib/gps/index.ts 100% 100% 100%
src/lib/gps/observationSample.ts 100% 100% 100%
src/lib/gps/realtimeTimer.ts 90.07% 100% 76.04%
src/lib/gps/sessionGate.ts 100% 100% 100%
src/lib/gpsQualityFilter.ts 100% 100% 96.77%
src/lib/graphPrefsStorage.ts 100% 100% 100%
src/lib/i18n/config.ts 100% 100% 100%
src/lib/i18n/format.ts 100% 100% 85.71%
src/lib/i18n/pluginLocales.ts 0% 0% 100%
src/lib/i18n/seedUtils.ts 100% 100% 90.47%
src/lib/imageCrop.ts 12.19% 10% 0%
src/lib/iracingParser.ts 91.93% 80% 76.47%
src/lib/kartStorage.ts 100% 75% 100%
src/lib/lapAlignment.ts 100% 80% 76.92%
src/lib/lapCalculation.ts 96.2% 100% 90.57%
src/lib/lapDelta.ts 99.2% 100% 85.07%
src/lib/lapOverlays.ts 100% 100% 86%
src/lib/lapSnapshot.ts 100% 100% 88.46%
src/lib/lapSnapshotStorage.ts 100% 83.33% 100%
src/lib/leaderboardBrowse.ts 100% 92.3% 80.76%
src/lib/leaderboardHandoff.ts 100% 100% 100%
src/lib/leaderboardSession.ts 97.67% 83.33% 67.85%
src/lib/leaderboardTypes.ts 100% 100% 100%
src/lib/logFileType.ts 100% 100% 100%
src/lib/loggers/alfano/alfanoConnection.ts 100% 100% 100%
src/lib/loggers/alfano/ipc.ts 100% 100% 100%
src/lib/loggers/bleDetails.ts 11.11% 11.11% 100%
src/lib/loggers/doveslogger/dovesloggerConnection.ts 56.25% 53.33% 100%
src/lib/loggers/doveslogger/firmwareInfo.ts 100% 100% 100%
src/lib/loggers/doveslogger/ipc.ts 100% 91.66% 66.66%
src/lib/loggers/errors.ts 100% 100% 100%
src/lib/loggers/fileNaming.ts 100% 100% 100%
src/lib/loggers/fledglingConnection.ts 100% 100% 100%
src/lib/loggers/index.ts 100% 100% 100%
src/lib/loggers/mychron/ipc.ts 100% 100% 100%
src/lib/loggers/mychron/mychronConnection.ts 100% 100% 100%
src/lib/loggers/native/ipc.ts 100% 100% 100%
src/lib/loggers/native/owner.ts 100% 100% 100%
src/lib/loggers/progress.ts 94.73% 100% 96.15%
src/lib/loggers/types.ts 100% 100% 100%
src/lib/mapMarker.ts 100% 100% 100%
src/lib/motecParser.ts 89.61% 81.48% 55.71%
src/lib/navBack.ts 100% 100% 71.42%
src/lib/nmeaParser.ts 85.62% 92.85% 71.22%
src/lib/noteStorage.ts 100% 80% 100%
src/lib/overlayCanvasRenderer.ts 0% 0% 0%
src/lib/parseReport.ts 68.18% 85.71% 72.22%
src/lib/parserUtils.ts 100% 100% 98.93%
src/lib/passwordStrength.ts 100% 100% 100%
src/lib/pendingCheckout.ts 58.82% 25% 100%
src/lib/platform.ts 100% 88.88% 89.65%
src/lib/profanity.ts 100% 100% 75%
src/lib/referenceUtils.ts 98.29% 100% 86.66%
src/lib/rssFeed.ts 0% 100% 100%
src/lib/sampleData.ts 100% 100% 100%
src/lib/satelliteImagery.ts 100% 100% 90%
src/lib/setupHistory.ts 94.17% 100% 72.51%
src/lib/setupRevision.ts 100% 100% 94.11%
src/lib/setupRevisionStorage.ts 84.48% 78.26% 36.84%
src/lib/setupStatus.ts 100% 100% 100%
src/lib/setupStorage.ts 81.35% 58.62% 100%
src/lib/shareSession.ts 100% 100% 100%
src/lib/sim/simClient.ts 0% 0% 0%
src/lib/sim/simPlayback.ts 100% 100% 97.22%
src/lib/sim/simSession.ts 100% 100% 100%
src/lib/speedBounds.ts 94.28% 66.66% 87.17%
src/lib/speedEvents.ts 86.56% 100% 76%
src/lib/speedHeatmap.ts 100% 100% 100%
src/lib/sprintCourse.ts 100% 100% 92.3%
src/lib/submittedTracksStorage.ts 0% 0% 0%
src/lib/templateEdit.ts 97.82% 88.88% 86.95%
src/lib/templateStorage.ts 93.54% 69.23% 100%
src/lib/trackStorage.ts 15.57% 26.78% 17.77%
src/lib/trackSubmission.ts 100% 100% 92.64%
src/lib/trackUtils.ts 100% 100% 97.05%
src/lib/ubxParser.ts 99% 100% 89.58%
src/lib/units.ts 100% 100% 100%
src/lib/updateFlow.ts 100% 100% 100%
src/lib/utils.ts 100% 100% 100%
src/lib/vboParser.ts 91.71% 100% 77.83%
src/lib/vehicleHistory.ts 97.29% 100% 80%
src/lib/vehicleStorage.ts 100% 75% 100%
src/lib/versionCheck.ts 19.44% 12.5% 44.73%
src/lib/videoExport.ts 0% 0% 0%
src/lib/videoExportTarget.ts 100% 100% 84.61%
src/lib/videoFileStorage.ts 100% 76% 61.11%
src/lib/videoPlaylist.ts 95.71% 100% 90%
src/lib/videoStorage.ts 100% 76.92% 77.77%
src/lib/videoTimeline.ts 100% 100% 100%
src/lib/wakeLock.ts 100% 80% 100%
src/lib/weatherCacheStorage.ts 100% 100% 100%
src/lib/weatherService.ts 23.8% 35.29% 25.89%
src/lib/xrk/xrkClient.ts 3.57% 0% 0%
src/lib/xrk/xrkConfig.ts 100% 100% 100%
src/lib/xrk/xrkImporter.ts 70% 75% 80%
src/lib/xrk/xrkMapping.ts 98.7% 100% 87.5%
src/lib/xrk/xrkResample.ts 100% 100% 94%
src/lib/xrk/xrkTypes.ts 100% 100% 100%
src/lib/xrk/xrkWorker.ts 0% 0% 0%
src/plugins/cloud-sync/accountDeletion.ts 100% 100% 100%
src/plugins/cloud-sync/accountExport.ts 0% 0% 0%
src/plugins/cloud-sync/accountImport.ts 13.51% 25% 26.08%
src/plugins/cloud-sync/activeUser.ts 100% 100% 100%
src/plugins/cloud-sync/autoSync.ts 0% 0% 0%
src/plugins/cloud-sync/cloudClient.ts 55% 25% 87.5%
src/plugins/cloud-sync/CloudLogsPanel.tsx 0% 0% 0%
src/plugins/cloud-sync/DataPrivacyPanel.tsx 0% 0% 0%
src/plugins/cloud-sync/exportManifest.ts 100% 100% 100%
src/plugins/cloud-sync/FileDeleteToggle.tsx 0% 0% 0%
src/plugins/cloud-sync/fileSync.ts 91.66% 85.71% 100%
src/plugins/cloud-sync/FileSyncToggle.tsx 0% 0% 0%
src/plugins/cloud-sync/index.ts 0% 0% 0%
src/plugins/cloud-sync/LapSnapshotsPanel.tsx 0% 0% 0%
src/plugins/cloud-sync/leaderboardClient.ts 8.92% 10.52% 1.56%
src/plugins/cloud-sync/leaderboardSubmission.ts 100% 100% 94.11%
src/plugins/cloud-sync/LeaderboardSubmitPanel.tsx 0% 0% 0%
src/plugins/cloud-sync/localUsage.ts 100% 100% 50%
src/plugins/cloud-sync/merge.ts 100% 100% 100%
src/plugins/cloud-sync/pendingSync.ts 100% 100% 100%
src/plugins/cloud-sync/postsClient.ts 0% 0% 0%
src/plugins/cloud-sync/profile.ts 61.53% 50% 63.33%
src/plugins/cloud-sync/publicProfile.ts 6.25% 28.57% 0%
src/plugins/cloud-sync/publicShare.ts 100% 100% 100%
src/plugins/cloud-sync/publicVehicleSync.ts 92.85% 100% 78.57%
src/plugins/cloud-sync/sessionShare.ts 92.15% 76.92% 91.66%
src/plugins/cloud-sync/setupRevisionTombstones.ts 100% 100% 83.33%
src/plugins/cloud-sync/ShareFileButton.tsx 0% 0% 0%
src/plugins/cloud-sync/ShareSessionDialog.tsx 0% 0% 0%
src/plugins/cloud-sync/shareState.ts 100% 100% 100%
src/plugins/cloud-sync/shareToken.ts 100% 100% 100%
src/plugins/cloud-sync/snapshotSync.ts 97.22% 87.5% 75%
src/plugins/cloud-sync/snapshotTombstones.ts 100% 100% 100%
src/plugins/cloud-sync/StoragePanel.tsx 0% 0% 0%
src/plugins/cloud-sync/storageTypes.ts 100% 100% 100%
src/plugins/cloud-sync/storeAccessors.ts 100% 100% 90%
src/plugins/cloud-sync/syncEngine.ts 99.05% 91.3% 80%
src/plugins/cloud-sync/syncStores.ts 100% 100% 100%
src/plugins/cloud-sync/trackAutoSubmit.ts 100% 100% 78.57%
src/plugins/fileSources.ts 0% 0% 100%
src/plugins/index.ts 0% 0% 0%
src/plugins/mounts.ts 71.42% 75% 100%
src/plugins/panels.ts 75% 83.33% 100%
src/plugins/PluginMount.tsx 0% 0% 0%
src/plugins/PluginPanelHost.tsx 0% 0% 0%
src/plugins/registry.ts 77.27% 50% 100%
src/plugins/storage.ts 90.32% 70.83% 66.66%
src/plugins/tools/i18n.ts 0% 0% 100%
src/plugins/tools/index.ts 0% 0% 100%
src/plugins/tools/laptimer/lapTimerSession.ts 89.06% 80% 84.37%
src/plugins/tools/laptimer/LapTimerTool.tsx 0% 0% 0%
src/plugins/tools/laptimer/useLapTimer.ts 0% 0% 0%
src/plugins/tools/pill-alignment/CalibrationPanel.tsx 0% 0% 0%
src/plugins/tools/pill-alignment/envelope.ts 100% 100% 90.9%
src/plugins/tools/pill-alignment/EnvelopePlot.tsx 0% 0% 0%
src/plugins/tools/pill-alignment/FindSetupPanel.tsx 0% 0% 0%
src/plugins/tools/pill-alignment/inverse.ts 95.83% 100% 88.52%
src/plugins/tools/pill-alignment/model.ts 100% 100% 92.85%
src/plugins/tools/pill-alignment/OverheadToeView.tsx 0% 0% 100%
src/plugins/tools/pill-alignment/PillAlignmentTool.tsx 0% 0% 0%
src/plugins/tools/pill-alignment/PillDial.tsx 0% 0% 0%
src/plugins/tools/pill-alignment/profiles.ts 100% 100% 84.61%
src/plugins/tools/pill-alignment/toe.ts 100% 100% 100%
src/plugins/tools/seat-position/model.ts 100% 100% 60%
src/plugins/tools/seat-position/SeatDiagram.tsx 0% 0% 0%
src/plugins/tools/seat-position/SeatPositionTool.tsx 0% 0% 0%
src/plugins/tools/shared/NumRow.tsx 0% 0% 0%
src/plugins/tools/toolList.ts 0% 0% 100%
src/plugins/tools/ToolsLandingTile.tsx 0% 0% 0%
src/plugins/tools/ToolsPanel.tsx 0% 0% 0%
src/plugins/types.ts 100% 100% 100%
src/types/racing.ts 100% 100% 100%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants