Skip to content

4.2.0 - drag mode Beta - #423

Draft
TheAngryRaven wants to merge 26 commits into
mainfrom
BETA
Draft

4.2.0 - drag mode Beta#423
TheAngryRaven wants to merge 26 commits into
mainfrom
BETA

Conversation

@TheAngryRaven

Copy link
Copy Markdown
Owner

No description provided.

claude and others added 5 commits August 28, 2026 03:29
A GPS log from a drag strip matches no known track, and worse, the return
road loops back near the staging lanes so waypoint mode falsely detects
out-and-back passes as laps. This adds the pure detection layer for drag
mode: a staged/launch/run state machine that finds standing-start passes,
times every traditional mark (60/330/660/1000/1320 ft) with sub-sample
interpolation, and gates on "at least one straight, fast 660" so unknown
circuits and autocross keep falling through to waypoint mode. Orchestration
and UI land in follow-up commits on this plan.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012urKN2QdPpfSfjQ4Buux3g
dragRunsToLaps scores detected runs at a chosen distance and expresses them
as ordinary Laps (splits in sectorTimes/sectorBoundaries under the circuit
timing-line contract), so the whole lap UI works unchanged. A run that
lifted before the scoring mark keeps its reached splits but is flagged with
the new Lap.incomplete: its lapTimeMs is a data window that can undercut a
real ET, so ranking goes through the new fastestRankedLap helper and
calculateOptimalLap's deltaToFastest now skips incomplete laps — while
their partial splits still feed the optimal, since the best 60 ft of the
day is valid from any pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012urKN2QdPpfSfjQ4Buux3g
useDataLoader now consults detectDragRuns before accepting a waypoint
result — the drag false-positive fix, since a strip's return road loops
back near staging and waypoint mode would mis-time passes as laps. Detected
runs pre-apply at the suggested distance (unpersisted, mirroring the
waypoint branch) pending the user's prompt choice; applying persists the
new FileMetadata.dragDistanceFt (feet, matching the lengthFt convention)
so the session restores silently on reopen, and assigning a real course
clears it. Distance switching re-maps the held runs without re-detection.
Fastest-lap picks in useLapManagement and useReferenceLap now route
through fastestRankedLap so incomplete runs can't be ranked.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012urKN2QdPpfSfjQ4Buux3g
The no-track prompt grows a "Drag Runs Detected" branch (precedence:
course step > drag > waypoint > no-track) with a scoring-distance picker
pre-seeded from the longest distance a run actually covered; the session
header gains a 1/8 / 1000 ft / 1/4 switcher while drag mode is active. The
lap table renders the classic time slip — cumulative 60/330/660/1000 ft
columns with best-per-mark highlights — labels rows "Run N" through the
existing lapLabels plumbing, hides Min Speed (meaningless at a standing
start), badges incomplete passes instead of showing their window as an ET,
and its footer forks to Best ET with the optimal built from the best
splits of the day. The crop-to-segment dropdown self-derives drag mode
(only drag laps carry sectorBoundaries without a course). All new strings
ship in the seven locales.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012urKN2QdPpfSfjQ4Buux3g
…-j5vmu1

Drag mode: standing-start run detection + time slips at unknown venues (plan 0022)
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 29, 2026

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 4037d02 Commit Preview URL

Branch Preview URL
Sep 08 2026, 02:23 AM

@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown

Coverage Summary

Lines: 60.55% (8439/13937) · Statements: 59.58% · Functions: 56.54% · Branches: 57.14%

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 93.33% 100% 79.09%
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 13.95% 20% 14.28%
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/useOfflineReadiness.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 97.87% 94.44% 94.69%
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 57.69% 50% 58.33%
src/lib/courseDetection.ts 97.34% 100% 82.22%
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/deviceCourseOverrides.ts 100% 100% 97.29%
src/lib/deviceCoursePicker.ts 100% 100% 100%
src/lib/deviceCourseSelection.ts 100% 100% 100%
src/lib/deviceGeneratedNames.ts 100% 100% 100%
src/lib/deviceSettingsSchema.ts 97.87% 100% 97.4%
src/lib/deviceSyncFetch.ts 100% 100% 100%
src/lib/deviceSyncNames.ts 100% 100% 100%
src/lib/deviceSyncOps.ts 100% 100% 93.33%
src/lib/deviceSyncPlan.ts 100% 100% 100%
src/lib/deviceSyncRunner.ts 95.65% 100% 90%
src/lib/deviceSyncWizard.ts 100% 100% 70.96%
src/lib/deviceTimezones.ts 89.28% 92.85% 90.9%
src/lib/deviceTrackBudget.ts 100% 100% 100%
src/lib/deviceTrackSync.ts 100% 100% 96.4%
src/lib/doveParser.ts 89.6% 72.72% 77.27%
src/lib/dovexParser.ts 88.31% 100% 82.85%
src/lib/dragRunDetection.ts 100% 100% 92.2%
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/insta360/ipc.ts 0% 0% 0%
src/lib/insta360/nativePlayer.ts 90.52% 82.14% 79.41%
src/lib/insta360/playerClock.ts 100% 100% 90%
src/lib/insta360/pose.ts 100% 100% 100%
src/lib/insta360/types.ts 100% 100% 100%
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.31% 100% 91.09%
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 75% 73.33% 100%
src/lib/loggers/doveslogger/firmwareInfo.ts 100% 100% 100%
src/lib/loggers/doveslogger/ipc.ts 100% 91.66% 85.71%
src/lib/loggers/downloadFlow.ts 100% 100% 100%
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/nativeBytes.ts 89.47% 100% 62.5%
src/lib/nativeVideoExport.ts 0% 0% 0%
src/lib/nativeVideoStore.ts 54.54% 75% 45.83%
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/offlineReadiness.ts 100% 100% 100%
src/lib/offlineWarmup.ts 73.68% 57.14% 61.11%
src/lib/overlayCanvasRenderer.ts 63.43% 71.42% 50%
src/lib/parseReport.ts 69.56% 87.5% 74.35%
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/persistentStorage.ts 100% 100% 90.9%
src/lib/platform.ts 100% 88.88% 89.65%
src/lib/profanity.ts 100% 100% 75%
src/lib/pwaInstall.ts 75% 80% 79.16%
src/lib/referenceUtils.ts 100% 100% 90.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/supportTrackData.ts 100% 100% 75%
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/native-storage/deviceVideos.ts 100% 100% 100%
src/plugins/native-storage/DeviceVideosPanel.tsx 0% 0% 0%
src/plugins/native-storage/index.ts 0% 0% 0%
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%

@supabase

supabase Bot commented Aug 29, 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 ↗︎.

claude and others added 21 commits August 31, 2026 04:59
…ls, data-time sweep

The export renderer becomes the single drawing surface: per-type draw
functions are exported with an OverlayLayout, measureOverlay() shares the
draws' box math, OverlayLabels carries the user-facing strings (translated
by React callers, English defaults elsewhere), and the sector state machine
moves to sectorUtils.computeSectorDisplayStates — including completedAtMs,
which drives the completion sweep in DATA time so it lands on the same
frames in preview and export. Fidelity that existed on only one side (graph
grid lines, needle/dot glow, bubble center dot, pace SLOW/FAST labels,
preview's map framing and pace-bar stacking) is now in the shared draws.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013LzfeVvASfX1hefekpHPUq
…omponents

OverlayCanvas is the one preview host — a canvas sized by measureOverlay and
drawn by the same per-type functions the export uses, with the theme glow
filter and (for the formerly backdrop-blurred DOM widgets) a backdrop blur
as the only CSS extras. VideoPlayer's per-type switch collapses to it; the
nine overlay components and the wall-clock sparkle CSS are gone. The export
context now threads the caller's translated labels, so exported videos stop
being English-only. Changelog + plan doc.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013LzfeVvASfX1hefekpHPUq
…enderer-unification

plan 0023: one overlay scene renderer for preview + export (native phase 0)
startNativeVideoExport stages the job over LapWing's video_export_* IPC —
begin (same quality mapping as the web path), the source blob in 8 MB
raw-body chunks, overlay layers from the plan-0023 unified renderer at
15 Hz telemetry cadence (PNG at output resolution, timestamps relative to
export start) — then drives the hardware transcode with streamed progress
and hands the MP4 to the existing save/share flows. Resolves null (and
the WebView exporter takes over unchanged) off the shell, on the desktop
stub's `unsupported:` sentinel, on an older shell, or for multi-chunk
playlists. VideoPlayer tries native first; web/desktop byte-identical.

The renderer's per-frame invariant work is now memoized per session via
WeakMap on array identity: per-source ranges (validated against the
pace/braking arrays for special sources), map bounds, the pace scale, and
the digital widget's double value-resolve. Speeds up preview, the WebView
exporter, and native layer generation alike; draw output is bit-identical
(existing tests unchanged, +1 counting-fixture memo test).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013LzfeVvASfX1hefekpHPUq
…port-bridge

plan 0024: native export bridge — hardware-speed overlay export in the shell
"Save to device" inside the shell fell into downloadBlob() — an
<a download href="blob:"> click the Android WebView has no download
handler for — so the export completed and the file went nowhere. On native,
destination "device" now means the gallery: the bridge calls
video_export_save and the shell copies the finished MP4 straight out of
its job dir into MediaStore Movies/LapWing (never through the WebView);
the dialog's button reads "Save to Gallery" on native and VideoPlayer
toasts export.savedToGallery. New ExportCallbacks.onSavedToDevice; the
web path is untouched.

The web remembers a session's video via a Chrome-only FileSystemFileHandle;
the shell had nothing. nativeVideoStore.ts is its equivalent over the
shell's video_store_* IPC: loadRecording copies a single-file recording
into the shell's store in the background (8 MB raw-body chunks; the blob
URL keeps playing meanwhile), and the restore effect tries the store before
the IndexedDB fallback — the <video> then streams from app storage over the
asset protocol. The key rides VideoSyncState.nativeStoredKey and the export
bridge passes it as sourceKey, so exporting a remembered video skips the
source upload entirely (a stale key silently falls back to uploading).

i18n keys in all seven locales; plan 0024 + changelog updated. Gates:
2994 tests, tsc, eslint, build.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013LzfeVvASfX1hefekpHPUq
…tore-gallery

plan 0024 follow-up: save to gallery + the remembered video in the shell
…) + visible export failures

"Save to Gallery" on-device stuttered for a second and went idle — no
file, no message. The shell's upload commands took raw invoke bodies
(invoke(cmd, bytes, { headers })), and Tauri never uses that IPC on
Android: the WebView can't hand the app a request body, so Tauri falls
back to window.ipc.postMessage and serializes a Uint8Array payload as a
JSON array of numbers, which the raw-body commands rejected. The first
chunk of every export (and of every video-store copy) failed, and
VideoPlayer.onError only console.error'd it.

- src/lib/nativeBytes.ts: bulk bytes now cross the bridge as base64
  strings in ordinary JSON args, in 4 MB chunks — blobToBase64 via
  FileReader.readAsDataURL (native, off the main thread), with a pure
  chunked-btoa fallback (bytesToBase64). Unit-tested, including the
  FileReader path through a stubbed reader.
- nativeVideoExport.ts / nativeVideoStore.ts send { jobId, offset|tMs,
  data } / { key, offset, data } to the matching LapWing shell. Staging
  progress now weights an overlay layer like one chunk push, which is
  what it costs.
- Export failures are visible: onError toasts export.failed (7 locales)
  with the shell's message, destructive variant — the WebView exporter's
  errors get the same toast.
- Plan 0024 doc: follow-up 2 section; CHANGELOG under 4.2.0 Fixed.

Gates: tsc -b, eslint, vitest (3002 tests, +7).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013LzfeVvASfX1hefekpHPUq
…chunk-transport

plan 0024 follow-up 2: base64 chunk bridge (Android has no raw bodies) + visible export failures
The shell's copy per session's video (the remembered video) is the one
thing in the app that grows by gigabytes, and nothing pruned it — the
delete command existed but no UI called it.

- New first-party plugin src/plugins/native-storage: on the native shell it
  contributes a "Videos on this device" panel to the Profile tab (order 3,
  between the account meters and lap snapshots) — every stored copy with
  file name, owning session, size and date, a per-copy delete with inline
  confirm, and a clear-all, each toasting the bytes freed. On a shell that
  predates the listing the card says it needs a newer build. Contributing
  the panel is also what makes the Profile tab exist in a native build
  without the cloud plugin.
- nativeVideoStore.ts: listNativeStoredVideos / removeNativeStoredVideo /
  clearNativeVideoStore over LapWing's video_store_list / _remove / _clear;
  removals fire a native-video-store-changed window event.
- useVideoSync listens for it: a session playing from the deleted copy (a
  restore) unloads its video exactly as deleting the in-app stored video
  does; one still holding the picked blob only loses the export shortcut.
- Pure parts (ordering, totals, session label, applying a removal, byte
  formatting) in deviceVideos.ts with tests; the bridge functions tested
  against a mocked invoke. 7 locales. Plan doc + changelog.

tsc/eslint clean; 3018 tests pass (+24).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013LzfeVvASfX1hefekpHPUq
Android app only: "From Insta360 camera" joins the camera's Wi-Fi, lists its
recordings and plays one without downloading it — the LapWing shell's
native player streams the file off the camera and serves the frames as an
MJPEG stream the player shows in an <img>. NativePlayerElement implements
the <video> subset useVideoSync relies on (extrapolated clock between the
shell's reports, seek watchdog), so sync lock, offset, scrubbing and the
overlay canvas work on the stream unchanged. 360° recordings get a
drag-to-point layer with lock/reset; export is disabled for streams.

- lib/insta360: types, ipc (insta360_* over the lazy Tauri loader), pose +
  playerClock (pure, tested), nativePlayer (tested with a mocked bridge)
- components/insta360: import dialog (Wi-Fi prefix/password, connect, list,
  load, disconnect), 360° view layer
- useVideoSync: source-agnostic VideoSurface ref, loadCameraRecording,
  unloadVideo, nativeSource; VideoPlayer: <img> surface, camera buttons,
  360° lock/reset; video namespace strings in all seven languages
- docs/plans/0025, CHANGELOG, CLAUDE.md map

Companion: LapWing plan 0002 (tauri-plugin-insta360 + docs/insta360.md).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019sK5Tb8pa8bZEtUNiprYiH
…ore-ui

plan 0024 follow-up 3: see and clear the videos the native app keeps
…camera-preview

plan 0025: stream an Insta360 camera recording into the player (native)
…ne cache

Demoing at a track on an iPhone with no signal came up empty. The service
worker is fine — the whole app shell is precached and a warm Safari tab does
run offline. Two WebKit policies take it away:

1. Safari deletes all script-writable storage for a site not interacted with
   for ~7 days of browser use. That sweep takes the SW registration, the Cache
   API precache AND our IndexedDB sessions, silently. A phone that last opened
   LapWing before the previous race weekend arrives with nothing cached.
2. iOS never fires `beforeinstallprompt`, and InstallPrompt was built entirely
   around that event — so on the one platform that most needs the nudge, the
   card rendered nothing at all.

A Home Screen install is the documented escape hatch (far larger quota, and an
idle counter that only advances on days the app is opened), and it has its own
storage partition — so the instructions must end with "open it once while you
still have signal", or the user installs an icon that is just as empty.

- lib/pwaInstall.ts: pure detection of installed / ios-manual / prompt-capable,
  including the iPad's desktop-class UA (MacIntel + maxTouchPoints > 1) without
  catching real Macs. Dismissal moves from a session-only flag to a 30-day
  localStorage snooze — this hint is worth repeating, not silencing.
- lib/persistentStorage.ts: request the Storage API's persistent mode once at
  startup so the cache and IndexedDB stop being evictable. Fire-and-forget;
  every outcome is a return value, never a throw.
- InstallPrompt: same card, plus the iOS share-sheet steps and a one-line why.
  Strings moved to common:install.* and translated across all six locales.

Vitest covers both new modules (23 cases). Also noted in the plan: the
`app-html` NetworkFirst route is dead code — Workbox matches the precache
NavigationRoute first — left as-is since precache-first is the more
offline-robust behaviour, but the config reads misleadingly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KKLfticLxeFfKK2WFDNYdd
The reported bug — load the app at a track, airplane mode, refresh, nothing
loads — is not iOS-specific and not eviction. Workbox's precache install is
all-or-nothing: one failed request rejects `install`, the service worker is
discarded, and NOTHING is cached. Reproduced in Chromium against a real dist
build by cutting the network 4s into the first load:

  SW state at cut:      { installing: 'installing', active: null }
  cache entries:        53 of 267
  after install failed: { installing: null, waiting: null, active: null }
  offline reload →      net::ERR_INTERNET_DISCONNECTED

The install was 10.16 MB across 267 entries, and the bundled demo datalogs
alone were 44% of it. Seven entries were precached twice on top of that
(includeAssets listed files globPatterns already matched), so every install
re-downloaded the 1.25 MB sample .nmea. On a congested cell connection that is
a long window in which any hiccup costs the entire offline cache, silently.

- vite.config.ts: DEFERRED_ASSET_DIRS (samples/, loggers/) is one source of
  truth feeding globIgnores, a CacheFirst runtime route, and a build-emitted
  offline-assets.json (generated from the filesystem, so a new logger photo
  needs no code change). Install drops to 257 entries / 4.98 MB, almost
  entirely the app itself. includeAssets cut to robots.txt — everything else
  was already covered, which is where the duplicates came from.
- lib/offlineWarmup.ts: pulls the deferred assets in afterwards, per-URL, so a
  failure costs that asset and retries next visit instead of everything. Writes
  via `cache.add`, NOT fetch: on a first visit the worker is registered but not
  yet controlling the page, so page-initiated requests bypass it and cached
  nothing at all — the integration test caught exactly that. `addAll` would
  have reintroduced the all-or-nothing behaviour this plan removes.
- lib/offlineReadiness.ts + useOfflineReadiness + a Settings row: the deeper
  failure was that none of this was visible. You can now check readiness in the
  paddock, and finish the download on demand, instead of finding out on track.

Verified end-to-end (scratchpad harness, real dist over a throttled server):
killing every deferred-asset request outright now leaves the worker activated,
the shell cached (254 entries) and the offline reload rendering — the same
failures that previously discarded the whole install. 18 new Vitest cases.
lint, typecheck, 3064 tests and build all green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KKLfticLxeFfKK2WFDNYdd
…i-cache-x5ny5i

plan 0026: iOS install hint + persistent storage (offline hardening, part 1)
…resilience

plan 0027: stop a flaky connection from wiping the whole offline cache
… no "any network" picker

The dialog offered "leave empty to pick from every network in range", but
Android rejects a Wi-Fi specifier that matches everything, so pressing
Connect with the field empty failed with "cannot set match-all pattern for
specifier". The shell now reads an empty field as "bind to the Wi-Fi this
phone is already on" (LapWing side), which is the mode that actually
works — so the hints have to tell the user to join the camera's network in
the phone's settings first, and that a typed name is what brings up the
Android picker. The password only applies to the typed-name path.

Strings only, in all seven locales, plus the matching sentence in the
unreleased 4.2.0 entry.

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

Insta360: correct the Wi-Fi hints — Android has no "any network" picker
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