signalk-binnacle 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1322 -0
- package/README.md +115 -47
- package/package.json +16 -5
- package/public/assets/TrendCharts-BajKdV4q.js +10 -0
- package/public/assets/TrendCharts-DHXnahl6.css +1 -0
- package/public/assets/{index-Djb4rsNY.css → index-CHwGT9zB.css} +1 -1
- package/public/assets/index-CuQ3O73e.js +854 -0
- package/public/assets/route-edit-D936jsBL.js +1 -0
- package/public/assets/{sk.worker-BkjA9p00.js → sk.worker-C09pYRNp.js} +1 -1
- package/public/index.html +2 -2
- package/public/screenshots/01-chart.png +0 -0
- package/public/screenshots/02-routes.png +0 -0
- package/public/screenshots/03-anchorage.png +0 -0
- package/public/screenshots/04-profiles.png +0 -0
- package/public/screenshots/05-weather.png +0 -0
- package/public/sw.js +1 -1
- package/public/workbox-362dab98.js +1 -0
- package/public/assets/index-D-VwfKcE.js +0 -849
- package/public/workbox-a85f4708.js +0 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,1322 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Binnacle are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project aims to follow
|
|
5
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
<a id="v061"></a>
|
|
8
|
+
|
|
9
|
+
## [0.6.1] - 2026-06-12
|
|
10
|
+
|
|
11
|
+
Quick access from community feedback: the chart actions a navigator reaches for stay within one
|
|
12
|
+
or two taps, and the weather panel's layer row scrolls honestly instead of clipping its last pill.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Measure from the chart: the long-press and right-click menu gains "Measure from here", arming
|
|
17
|
+
the measure tool with its first point at the pressed position, so measuring starts where you
|
|
18
|
+
are looking instead of via the app menu. Re-arming mid-measurement deliberately starts fresh;
|
|
19
|
+
extending an in-progress measurement is a plain chart tap.
|
|
20
|
+
- A Charts pill on the bottom status strip opens Layers and charts in one tap, beside Center,
|
|
21
|
+
Follow, and Forecast, so switching charts no longer goes through the app menu.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- The weather panel's layer pills no longer render the last label clipped: the edge fade shows
|
|
26
|
+
only while there is actually more to scroll, lifts at the end of the scroll, and the pills
|
|
27
|
+
keep their natural width instead of compressing when the panel narrows, so the row genuinely
|
|
28
|
+
scrolls.
|
|
29
|
+
- The chart context menu sizes itself to its longest label, so its edge-clamp math matches the
|
|
30
|
+
rendered box.
|
|
31
|
+
- Voice control can activate the Charts pill by its visible word, its expanded state is not
|
|
32
|
+
announced while it is still disabled during chart load, and its tooltip says the chart is
|
|
33
|
+
loading while it is.
|
|
34
|
+
|
|
35
|
+
### Internal
|
|
36
|
+
|
|
37
|
+
- One armMeasure helper replaces the duplicated reveal-then-arm sequence, new measure tests pin
|
|
38
|
+
the seed-after-arm contract and the deliberate reset on re-arm, and CI test flakes from cold
|
|
39
|
+
ICU loading are prevented by a per-worker warm-up rather than per-test timeouts.
|
|
40
|
+
|
|
41
|
+
<a id="v060"></a>
|
|
42
|
+
|
|
43
|
+
## [0.6.0] - 2026-06-12
|
|
44
|
+
|
|
45
|
+
A reliability and correctness pass across the whole app: course following, the collision and anchor
|
|
46
|
+
watches, weather, charts, tides, and profiles, with the safety alarms now holding up in a
|
|
47
|
+
backgrounded browser tab. Plus: the app menu is a new tile launcher, every readout follows the
|
|
48
|
+
server's imperial-or-metric unit preference, and route editing loads on demand.
|
|
49
|
+
|
|
50
|
+
### Added
|
|
51
|
+
|
|
52
|
+
- Imperial and metric display units across the whole app, following the Signal K server's unit
|
|
53
|
+
preferences (Server Config, Unit Preferences) with a per-profile local fallback on older
|
|
54
|
+
servers. Depth, anchor distances and radius, MOB range, measured legs, tide heights and station
|
|
55
|
+
range, temperatures, pressure, precipitation, wave heights, and visibility all convert; knots,
|
|
56
|
+
nautical miles, bearings, and the hPa isobar convention stay nautical.
|
|
57
|
+
- The app menu is now a launcher: large icon tiles grouped Navigate, Conditions, Safety, and
|
|
58
|
+
Settings over a dimming scrim, bottom-anchored on phones for one-handed reach, with Forecast
|
|
59
|
+
now findable in the menu. Both alarm mutes moved into a new Alarms panel beside the collision
|
|
60
|
+
thresholds.
|
|
61
|
+
- The measure layer supports opacity like every other overlay, starting Measure re-shows a hidden
|
|
62
|
+
measure layer, the Tides panel cross-links its stations layer with a show-on-chart toggle, and
|
|
63
|
+
layer opacity sliders have a floor so a checked safety layer can never be dimmed invisible.
|
|
64
|
+
- The Terra Draw route editor loads on first use instead of at startup, trimming the initial
|
|
65
|
+
bundle by about 137 kB for faster cold loads on Pi-class displays.
|
|
66
|
+
- Standard waypoints: drop one from a long press on the chart, see them as named markers, and
|
|
67
|
+
locate, go to, rename, or delete them from the new Waypoints panel. They live in the server's
|
|
68
|
+
own waypoint resources, so they interoperate with Freeboard-SK and every other client.
|
|
69
|
+
- An Active alerts list in the Alarms panel: every notification on the boat (engine, NMEA2000,
|
|
70
|
+
autopilot, or any plugin) surfaces with severity, time, and one-tap Silence and Acknowledge
|
|
71
|
+
that propagate to every station on a 2.28 server.
|
|
72
|
+
- Collision and MOB alerts ride the server's v2 Notifications API when available (server-managed
|
|
73
|
+
ids; muting locally silences the boat-wide alert), with the v1 delta publish kept for older
|
|
74
|
+
servers. Server capabilities are detected once from the features endpoint.
|
|
75
|
+
- The anchor watch speaks the standard Anchor API the moment a server ships it (the proposal's
|
|
76
|
+
drop, raise, radius, and reposition routes, feature-detected), ahead of the existing
|
|
77
|
+
anchoralarm-plugin path and the client-local watch.
|
|
78
|
+
- Custom chart symbols from the signalk-symbol-manager plugin: a note whose icon reference
|
|
79
|
+
resolves to a managed symbol renders that symbol (scale and anchor honored), and a provided
|
|
80
|
+
"waypoint" symbol replaces the built-in waypoint marker. At night-red, user artwork is remapped
|
|
81
|
+
into the red band so the theme's no-color rule holds. Without the plugin, every icon stays
|
|
82
|
+
built-in.
|
|
83
|
+
|
|
84
|
+
- Worldwide tides through the signalk-tides plugin when the server runs it (NOAA, Neaps,
|
|
85
|
+
WorldTides, or StormGlass per its configuration), with the NOAA CO-OPS path unchanged as the
|
|
86
|
+
fallback; the Tides panel says which source served.
|
|
87
|
+
- AIS target trails from the tracks plugin: faded wakes behind moving targets, themed for all
|
|
88
|
+
three themes, fetched only when the plugin is present and the layer is visible.
|
|
89
|
+
- Offline charts that actually work: PMTiles archives are cached as blocks in browser storage at
|
|
90
|
+
the protocol layer, so previously viewed chart areas render offline in every context, including
|
|
91
|
+
the plain-http default where no service worker can run (the old service-worker chart cache
|
|
92
|
+
provably never stored anything: range responses cannot enter the Cache API). Plugin-served
|
|
93
|
+
raster chart tiles, the seamark, bathymetry, boundary, and ice overlays, the base-map style,
|
|
94
|
+
and CO-OPS predictions gain service-worker caching over https, with per-cache bounds and quota
|
|
95
|
+
protection; opaque cross-origin responses are no longer cached (each one padded several MB of
|
|
96
|
+
quota).
|
|
97
|
+
- Tide stations and predictions, chart notes, and the vessel conditions panel now persist in
|
|
98
|
+
browser storage, so a reload with no signal replays the last data for the area, each item
|
|
99
|
+
declaring its own age, over plain http as well as https.
|
|
100
|
+
- When the base map style itself is unreachable (plain http at sea with no internet), the map
|
|
101
|
+
starts on a minimal water-colored fallback instead of staying blank, so cached charts and
|
|
102
|
+
every overlay still load. The real base map returns on the next load with connectivity.
|
|
103
|
+
- A Trends panel: depth, apparent wind, barometric pressure, and speed over the last 24 hours
|
|
104
|
+
as themed graphs, served by the server's v2 History API when a history provider runs
|
|
105
|
+
(signalk-questdb, signalk-to-influxdb2, or signalk-parquet), with provider fallback when the
|
|
106
|
+
default provider has no data. Without one, the graphs show the current session, sampled live.
|
|
107
|
+
- A "Track history (24 h)" chart layer: the vessel's server-recorded last day as a dashed line
|
|
108
|
+
under the live track, gap-split across stops, opt-in from the Layers panel and only queried
|
|
109
|
+
while shown.
|
|
110
|
+
|
|
111
|
+
### Removed
|
|
112
|
+
|
|
113
|
+
- The browser-local PMTiles file upload. Chart files belong on the server: install the
|
|
114
|
+
signalk-pmtiles-plugin and drop .pmtiles files in its charts folder, and they appear in
|
|
115
|
+
Binnacle on every device automatically. Adding a chart by URL is unchanged and still syncs to
|
|
116
|
+
the server. Previously uploaded browser-local charts are dropped cleanly at upgrade.
|
|
117
|
+
|
|
118
|
+
### Changed
|
|
119
|
+
|
|
120
|
+
- Delta batching in the stream worker now runs on a timer instead of requestAnimationFrame, and
|
|
121
|
+
AIS staleness pruning runs on a wall clock instead of the render loop, so live data keeps
|
|
122
|
+
flowing and the collision and anchor alarms keep evaluating while the tab is hidden.
|
|
123
|
+
- An AIS target that stops reporting is now dropped after seven minutes, so anchored traffic with
|
|
124
|
+
a slow AIS refresh no longer flickers in and out of the target list.
|
|
125
|
+
- Track recording no longer accumulates points while the boat sits at anchor: session gaps are
|
|
126
|
+
detected from fix continuity, not motion.
|
|
127
|
+
- Cancelling MOB and raising the anchor are now two-tap confirms, and deleting a profile or a
|
|
128
|
+
saved track asks first, matching the route delete.
|
|
129
|
+
- Escape handling is one shared topmost stack across the panels, the menu, and the measure tool,
|
|
130
|
+
so Escape always closes the surface on top and never one underneath.
|
|
131
|
+
- Layer drag-to-reorder now stays within the layer's own category instead of crossing into the
|
|
132
|
+
next section.
|
|
133
|
+
- Opening the Tides panel on a cold start is faster: the tide predictions and the current-station
|
|
134
|
+
lookup now fetch concurrently instead of back to back.
|
|
135
|
+
|
|
136
|
+
### Fixed
|
|
137
|
+
|
|
138
|
+
- Editing a route no longer strips its waypoint names.
|
|
139
|
+
- The nav strip no longer shows the next waypoint's arrival time as the whole-route ETA.
|
|
140
|
+
- An active course now survives a page reload: the course state hydrates on first connect, and
|
|
141
|
+
the route's Active badge tracks the server, including courses started or cleared from another
|
|
142
|
+
station.
|
|
143
|
+
- Arrival no longer re-alarms from GPS jitter at the arrival circle; the alarm latches until the
|
|
144
|
+
boat clearly leaves the circle.
|
|
145
|
+
- A failed waypoint skip and a partially failed GPX import are now reported instead of passing
|
|
146
|
+
silently.
|
|
147
|
+
- At night-red the own vessel, the AIS targets, and the note icons are no longer hidden along
|
|
148
|
+
with the base map's sprite icons.
|
|
149
|
+
- An acknowledged collision alert re-arms once the situation clears, and a contact's severity
|
|
150
|
+
downgrade has hysteresis, so the alarm can neither stay silently dismissed nor flap between
|
|
151
|
+
danger and warning.
|
|
152
|
+
- A target reporting speed without a course is no longer modeled as steaming due north, and
|
|
153
|
+
contacts with provider-supplied CPA keep classifying during an own-fix dropout.
|
|
154
|
+
- The anchor watch announces a degraded state when GPS is lost, and an anchor-marker drag the
|
|
155
|
+
system cancels no longer silently relocates the anchor.
|
|
156
|
+
- The MOB strip dashes out bearing and range on a stale fix instead of presenting frozen numbers
|
|
157
|
+
as live.
|
|
158
|
+
- Wind particle colors now match the legend's absolute scale.
|
|
159
|
+
- Radar frames refetch on schedule: the cache no longer extends its own expiry on every read.
|
|
160
|
+
- A partial forecast no longer stretches stale wave pixels over a new viewport, and overlapping
|
|
161
|
+
forecast loads can no longer finish out of order.
|
|
162
|
+
- The conditions panel's forecast section falls back to the free grid when a provider returns an
|
|
163
|
+
empty series, and the weather panel's clock notes stay live during a long open.
|
|
164
|
+
- Deleting a user chart no longer leaves it in the persisted layer state, and renaming one
|
|
165
|
+
updates its Layers row and its server resource.
|
|
166
|
+
- Tide times are correct when the browser's time zone differs from the station's (predictions
|
|
167
|
+
are now requested in GMT), tide data refetches after midnight at anchor, and the on-chart tide
|
|
168
|
+
label no longer shows past events.
|
|
169
|
+
- Tide fetches are skipped entirely while nothing displays them.
|
|
170
|
+
- Note markers recover after a failed or superseded fetch instead of freezing until reload.
|
|
171
|
+
- A transient network failure at startup no longer wipes the stored auth token, and a failed
|
|
172
|
+
access request retries instead of hanging at "Requesting access".
|
|
173
|
+
- Profiles no longer show "unsaved changes" on every launch, deleting all profiles no longer
|
|
174
|
+
resurrects the starter profiles, and a synced device no longer marks a profile active without
|
|
175
|
+
applying it. A failed profile import shows an error.
|
|
176
|
+
- A refused alarm Silence or Acknowledge now shows an error in the Alarms panel instead of the
|
|
177
|
+
alarm just continuing to sound, and a collision alert whose server notification was cleared
|
|
178
|
+
from another station is re-raised instead of going silent.
|
|
179
|
+
- Cleared notifications no longer linger in the Active alerts list, and an unchanged
|
|
180
|
+
notification broadcast no longer re-renders the panel.
|
|
181
|
+
- AIS wakes now clear after a few minutes of failed refreshes instead of freezing in place, and
|
|
182
|
+
waypoint markers have their own color in each theme.
|
|
183
|
+
- The weather panel's layer pills stay on one scrollable row at every window width instead of
|
|
184
|
+
wrapping into a second header row.
|
|
185
|
+
- A trend history load that resolves out of order can no longer overwrite a newer result, and a
|
|
186
|
+
failed load shows its failure note instead of loading forever.
|
|
187
|
+
- A trend metric requested twice on one path with different aggregates now maps to its own
|
|
188
|
+
column instead of mirroring the first.
|
|
189
|
+
- A tide reading replayed from the offline cache remeasures the station distance from the
|
|
190
|
+
current position, so a reading cached a few kilometers away cannot misjudge the coverage
|
|
191
|
+
radius or misstate the range.
|
|
192
|
+
- Muting the collision alarm from the danger strip now reports a refused boat-wide silence in
|
|
193
|
+
the Alarms panel, matching the panel's own Silence and Acknowledge.
|
|
194
|
+
- Losing authorization mid-session no longer makes the collision notifier abandon its server
|
|
195
|
+
notification id; the v1 delta fallback carries the change until the server accepts again.
|
|
196
|
+
- A unit preset changed on the server while the link was down is picked up on reconnect.
|
|
197
|
+
- The offline cache's third-party host matchers accept only the real weather and radar domains
|
|
198
|
+
and their subdomains, not lookalike hostnames that merely end in the same letters.
|
|
199
|
+
|
|
200
|
+
### Internal
|
|
201
|
+
|
|
202
|
+
- Map tile, WebGL shader, and PMTiles resources are released on teardown, dead exports and the
|
|
203
|
+
unused weather view persistence were removed, and assorted hot-path allocations were trimmed.
|
|
204
|
+
- Shared bbox helpers, a shared test fetch stub, the shared input primitive, and one global
|
|
205
|
+
segmented-control rule replaced per-feature copies.
|
|
206
|
+
- The notification mirror compares the four status flags directly instead of serializing the
|
|
207
|
+
status object on every delta, a coordinate-cell quantizer shared by the tides and weather
|
|
208
|
+
caches replaced two copies, an IndexedDB store that degrades to memory now logs one
|
|
209
|
+
diagnostic breadcrumb, and the alarm mute rows sit on the shared button base. New tests cover
|
|
210
|
+
the session trend recorder, the notification dedup, the refused silence and acknowledge
|
|
211
|
+
paths, and the history provider fallbacks.
|
|
212
|
+
|
|
213
|
+
<a id="v050"></a>
|
|
214
|
+
|
|
215
|
+
## [0.5.0] - 2026-06-11
|
|
216
|
+
|
|
217
|
+
A safety-focused redesign of the man-overboard confirm, a broad weather-panel upgrade (more
|
|
218
|
+
decision data, honest provenance, and accessibility), and a new app icon.
|
|
219
|
+
|
|
220
|
+
### Added
|
|
221
|
+
|
|
222
|
+
- **Gusts without a provider.** The free forecast grid now carries wind gusts, so the reefing
|
|
223
|
+
number shows in the tap readout and the vessel conditions panel even with no weather provider
|
|
224
|
+
configured.
|
|
225
|
+
- **Barometric tendency.** The conditions panel shows the trend a sailor decides by ("falling
|
|
226
|
+
1.2 hPa/3 h"): the provider's own tendency when it reports one, otherwise computed from the
|
|
227
|
+
trailing three hours of the forecast grid.
|
|
228
|
+
- **More conditions data.** Wave and swell direction (labeled "from"), visibility, and water
|
|
229
|
+
temperature appear when the source carries them, and the current block is tagged Observed or
|
|
230
|
+
Forecast with its valid time and zone.
|
|
231
|
+
- **Forecast provenance.** A footer states the source and fetch time, the stale note says how old
|
|
232
|
+
the shown forecast is, and a grid missing its requested wave fields is qualified rather than
|
|
233
|
+
passed off as complete.
|
|
234
|
+
- **Radar honesty.** The legend names the frame the loop is painting (for example "frame
|
|
235
|
+
-40 min"), extrapolated nowcast frames are labeled as such, cached radar is flagged when
|
|
236
|
+
offline, and the radar hides while the time slider is away from now instead of painting live
|
|
237
|
+
rain over a three-day-out wind field.
|
|
238
|
+
- **Slider orientation.** A tick marks now on the forecast slider, the label carries Past or
|
|
239
|
+
Forecast plus the time zone, and a one-shot note explains the zoom cap the first time you
|
|
240
|
+
pinch into it.
|
|
241
|
+
- **New app icon**, aligned with the rest of the plugin family: a compass rose on a white
|
|
242
|
+
compass-card badge over the shared ocean-wave mark.
|
|
243
|
+
|
|
244
|
+
### Changed
|
|
245
|
+
|
|
246
|
+
- **Man overboard confirm.** Pressing MOB now opens a centered dialog. The position is captured
|
|
247
|
+
at the press, so the seconds spent confirming can no longer carry the mark away from the
|
|
248
|
+
person; the confirm only gates the alarm. One full-width Mark man overboard button sits in the
|
|
249
|
+
one-handed thumb zone with a quiet Cancel stacked above it, the dialog self-dismisses after 15
|
|
250
|
+
seconds with a visible countdown (a re-press shortly after reuses the earlier press-time fix),
|
|
251
|
+
and without a GPS fix the boat-wide alarm still raises, position-less, with a clear warning.
|
|
252
|
+
The recovery strip adds the wall-clock Marked time for the log and the VHF relay.
|
|
253
|
+
- **Weather opens at now.** The time slider seeds to the forecast step nearest now instead of
|
|
254
|
+
the start of the series, which begins up to a day in the past.
|
|
255
|
+
- **Conditions track the slider.** With a weather provider configured, the Here panel re-picks
|
|
256
|
+
the forecast step as the slider moves, and it falls back to the free grid when the provider
|
|
257
|
+
fails instead of freezing a one-shot sample.
|
|
258
|
+
- **Warnings.** Sorted most severe first, with the issuing source and the validity window, at a
|
|
259
|
+
readable size; free mode now says warnings are unavailable instead of showing a silently empty
|
|
260
|
+
list.
|
|
261
|
+
- **Legends.** Wind in whole 10-knot bands, cloud in whole percent, and weather readouts in
|
|
262
|
+
whole knots.
|
|
263
|
+
- **Night-red.** The map attribution control follows the theme on both maps instead of rendering
|
|
264
|
+
as a bright white bar.
|
|
265
|
+
|
|
266
|
+
### Fixed
|
|
267
|
+
|
|
268
|
+
- The tapped readout blends the two forecast steps exactly as the drawn fields do, so the number
|
|
269
|
+
can no longer disagree with the picture under the finger by a full step.
|
|
270
|
+
- Weather provider detection re-runs when the auth token arrives or the stream reconnects; one
|
|
271
|
+
failed probe no longer locks the whole session onto the free sources.
|
|
272
|
+
- The latest observation is picked by date rather than response order, and a provider's last
|
|
273
|
+
forecast step no longer answers for a time days past its horizon.
|
|
274
|
+
- A rate-limited marine (waves) endpoint no longer blocks the healthy atmospheric fetch, so
|
|
275
|
+
turning waves off recovers immediately.
|
|
276
|
+
- Provider precipitation is labeled as the accumulation it is (mm), not a rate.
|
|
277
|
+
- The course strip no longer covers the weather panel's slider and legend while a route is
|
|
278
|
+
active; the strips lift to the panel's top edge instead.
|
|
279
|
+
- Accessibility: the forecast slider announces real times instead of epoch milliseconds, manual
|
|
280
|
+
time changes are announced, the floating map notes are reliable live regions that stack
|
|
281
|
+
instead of overlapping, scrubbing stops playback so the thumb is not yanked back mid-drag,
|
|
282
|
+
Enter on the focused mini-map samples the center, and the tap readout can be pinned (hover or
|
|
283
|
+
focus) and dismissed.
|
|
284
|
+
|
|
285
|
+
<a id="v040"></a>
|
|
286
|
+
|
|
287
|
+
## [0.4.0] - 2026-06-10
|
|
288
|
+
|
|
289
|
+
Four new at-sea features (an anchor watch, a man-overboard button, a measure tool, and an AIS
|
|
290
|
+
target list) plus shell refinements from helm feedback.
|
|
291
|
+
|
|
292
|
+
### Added
|
|
293
|
+
|
|
294
|
+
- **Anchor watch.** Drop the anchor at the boat, set the swing radius by hand or capture it from
|
|
295
|
+
the live distance plus a margin, and get a drag alarm after three consecutive fixes outside the
|
|
296
|
+
circle. The alarm latches until acknowledged, so a boat that swings back inside cannot silently
|
|
297
|
+
clear an alarm you never saw, and the watch survives a reload. When the signalk-anchoralarm-plugin
|
|
298
|
+
is installed, Binnacle drives it instead, so the alarm keeps running with the browser closed; the
|
|
299
|
+
panel says which mode is watching. On the chart: the swing circle, a rode line, and a drop-point
|
|
300
|
+
marker you can drag to where the hook actually lies.
|
|
301
|
+
- **Man overboard.** An always-visible MOB button centered in the top bar. One tap pops out a
|
|
302
|
+
large confirm (so a stray tap can never raise the alarm, and the window self-dismisses); the
|
|
303
|
+
confirm marks the spot, publishes the boat-wide `notifications.mob` alarm so every station sees
|
|
304
|
+
it, flies the chart to the mark, and raises a recovery strip with live bearing, range, and
|
|
305
|
+
elapsed time. Steering to the mark stays a deliberate second tap (Steer to MOB) through the
|
|
306
|
+
course system, never automatic, since a coupled autopilot may follow the course. An MOB raised
|
|
307
|
+
by another station shows here too, and the mark survives a reload.
|
|
308
|
+
- **Measure tool.** Arm it from the menu, tap points on the chart, and read each leg's range and
|
|
309
|
+
bearing plus the running total, with the total labeled at the last point. Undo, Clear, Done, or
|
|
310
|
+
Escape.
|
|
311
|
+
- **AIS target list.** Every tracked target as a tappable card with name or MMSI, live range and
|
|
312
|
+
bearing, SOG, and CPA and TCPA when available, sortable by range, CPA, or name, with the
|
|
313
|
+
lookout's severity coloring risky contacts and a tap flying the chart to the target.
|
|
314
|
+
- A depth readout in the anchor panel when a sounder publishes `environment.depth.belowTransducer`.
|
|
315
|
+
|
|
316
|
+
### Changed
|
|
317
|
+
|
|
318
|
+
- The footer's "Connected" text is now a compact status dot: green by day and dusk, a calm dim red
|
|
319
|
+
in night-red (which forbids green), and the caution color while the stream is down. The label
|
|
320
|
+
remains for screen readers and the hover title, and the dot stays on phones where the word used
|
|
321
|
+
to be hidden.
|
|
322
|
+
- The trailing position cluster's numerals now take the same instrument-readout size as the
|
|
323
|
+
leading AIS, SOG, and COG readouts, so the footer reads as one instrument row.
|
|
324
|
+
- The four feature alarms now share one edge-triggered core (`GatedAlarm`), with the collision
|
|
325
|
+
alarm keeping its escalation-overrides-mute policy on top; tones are unchanged, and each alarm
|
|
326
|
+
remains audibly distinct (MOB above the collision two-beep, anchor between collision and
|
|
327
|
+
arrival).
|
|
328
|
+
|
|
329
|
+
### Fixed
|
|
330
|
+
|
|
331
|
+
- The MOB trigger's boat-wide notification never actually left the browser: the position object
|
|
332
|
+
read from the live store is a reactive proxy, which cannot be structured-cloned into the stream
|
|
333
|
+
worker, so the publish threw and was lost while the local strip looked fine. The mark is now
|
|
334
|
+
snapshotted into a plain object, with a regression test, and the round trip is verified against
|
|
335
|
+
a live server.
|
|
336
|
+
|
|
337
|
+
<a id="v031"></a>
|
|
338
|
+
|
|
339
|
+
## [0.3.1] - 2026-06-10
|
|
340
|
+
|
|
341
|
+
A pass over the existing features for safety, honesty under failure, performance on modest hardware,
|
|
342
|
+
and accessibility, plus a few navigation additions.
|
|
343
|
+
|
|
344
|
+
### Added
|
|
345
|
+
|
|
346
|
+
- Honest data-staleness signals. When the position feed stops, the footer shows a calm "No GPS fix"
|
|
347
|
+
note and dashes SOG and COG instead of presenting a frozen speed and course as if they were live,
|
|
348
|
+
and the collision watch and the course guidance stop computing against the stale fix. The connection
|
|
349
|
+
badge turns to a caution color and reads "Reconnecting" or "Not connected" during an outage instead
|
|
350
|
+
of staying "Connected".
|
|
351
|
+
- A cross-track deviation needle (a CDI) on the nav strip, so steering to track is a glance rather than
|
|
352
|
+
a number read, with a caution color when it pegs at full scale.
|
|
353
|
+
- An on-screen arrival banner paired with the arrival tone, for a helm with the volume low.
|
|
354
|
+
- A footer "AIS" chip showing how many targets the collision watch is tracking, so an empty danger
|
|
355
|
+
strip reads as all-clear rather than as a possible failure.
|
|
356
|
+
- A status note in the weather panel (loading, offline, or showing the last forecast) instead of a
|
|
357
|
+
blank or silently outdated map.
|
|
358
|
+
|
|
359
|
+
### Changed
|
|
360
|
+
|
|
361
|
+
- The collision-alarm mute is now session-only and auto-expires after ten minutes, then re-arms; it is
|
|
362
|
+
no longer persisted across a reload or carried by a profile, so a mute set in a crowded anchorage
|
|
363
|
+
cannot silently follow you into the next passage. A close, imminent contact (inside about 0.1 nm and
|
|
364
|
+
two minutes) overrides both mute and acknowledge, so a real emergency always sounds. Acknowledging a
|
|
365
|
+
danger now keeps the strip on screen, dimmed, with its CPA and TCPA, while the target is still
|
|
366
|
+
closing, instead of hiding it.
|
|
367
|
+
- Deleting a route now asks to confirm, with distinct wording when the delete will also stop active
|
|
368
|
+
navigation. The nav strip disables waypoint-skip at the first and last points and keeps a gutter
|
|
369
|
+
before Stop, so a mis-tap cannot end navigation.
|
|
370
|
+
- The footer SOG and COG step up to the full instrument-readout size, the numbers a helmsman glances
|
|
371
|
+
at most.
|
|
372
|
+
- Performance on the Raspberry Pi: the chart's overlays no longer sync at full frame rate while idle
|
|
373
|
+
(they update on real map repaints plus a low-frequency tick and pause when the tab is hidden), the
|
|
374
|
+
weather fields and the wind particle field stop forcing continuous GPU work, and a long track
|
|
375
|
+
simplifies incrementally rather than re-processing the whole track on every fix.
|
|
376
|
+
- Signal K conformance: TCPA accepts the spec's ISO-8601 duration form, the nav strip prefers the
|
|
377
|
+
server's estimated time of arrival when a provider supplies it, the client-side course fallback uses
|
|
378
|
+
consistent rhumb-line geometry, and a target that stops reporting is dropped after three minutes
|
|
379
|
+
rather than six.
|
|
380
|
+
- Resilience: in-app requests time out instead of hanging on a half-open link, the local chart and
|
|
381
|
+
track stores recover after a transient IndexedDB failure instead of dropping to memory for the
|
|
382
|
+
session, the stream reconnects immediately when the network returns, and persisted charts and
|
|
383
|
+
profiles are validated on load so a drifted entry is dropped rather than trusted.
|
|
384
|
+
- Accessibility: opening a panel moves focus into it, the Forecast and Here toggles use aria-expanded,
|
|
385
|
+
in-place confirm and review steps move focus to their new control, a failed chart import is
|
|
386
|
+
announced, and the app menu stays open when a mute toggle is flipped.
|
|
387
|
+
|
|
388
|
+
### Fixed
|
|
389
|
+
|
|
390
|
+
- The wind particle field no longer freezes after switching away from and back to the browser tab.
|
|
391
|
+
|
|
392
|
+
<a id="v030"></a>
|
|
393
|
+
|
|
394
|
+
## [0.3.0] - 2026-06-09
|
|
395
|
+
|
|
396
|
+
### Added
|
|
397
|
+
|
|
398
|
+
- Profiles: named bundles of your settings (theme, which layers are on, their opacity and order, the
|
|
399
|
+
weather layers, the collision thresholds, the track and planning settings, and the alarm mutes) that
|
|
400
|
+
you save, switch between, rename, delete, and set a default for. A switcher pill in the top bar shows
|
|
401
|
+
the active profile and opens a Profiles panel; applying a profile updates the chart live, and tweaking
|
|
402
|
+
a setting marks the profile as edited so you can save the change or discard it by switching away.
|
|
403
|
+
Three starter profiles (Coastal day, Night passage, and At anchor) seed on first run. Profiles are
|
|
404
|
+
stored locally, and when you are logged in to a secured SignalK server they also sync through the
|
|
405
|
+
server's applicationData store so they follow you across devices. The sync degrades cleanly: an
|
|
406
|
+
unsecured server, or a login that cannot use the applicationData store, keeps profiles local, and a
|
|
407
|
+
login that can read the store but not write it stops after one rejected write rather than retrying on
|
|
408
|
+
every edit, so it never floods the console. You can also export a profile to a JSON file and import
|
|
409
|
+
profiles from one, to back them up or share them between boats.
|
|
410
|
+
|
|
411
|
+
- Course planning on the chart. Long-press (touch) or right-click (desktop) a point and choose "Go to
|
|
412
|
+
here" to navigate straight to it via the Course API, with the destination shown on the nav strip.
|
|
413
|
+
- Route interchange via GPX. Export any saved route to a GPX file other plotters, MFDs, and
|
|
414
|
+
Freeboard-SK read, and import routes from a GPX file back into Binnacle, closing the round trip.
|
|
415
|
+
- Passage planning in the route editor. A persisted plan speed turns the leg table into a passage
|
|
416
|
+
plan, showing the cumulative time to reach each waypoint and a whole-route Time alongside the
|
|
417
|
+
distance, plus a per-leg distance and bearing table that updates live as waypoints are dragged.
|
|
418
|
+
- Track-to-route workflows. Save the current track as a reusable route, reverse a saved route for the
|
|
419
|
+
return leg, navigate home by retracing the current track, and skip the active route's waypoint
|
|
420
|
+
forward or back from the nav strip. The nav strip also shows the whole-route distance and arrival
|
|
421
|
+
time when a multi-leg route is active.
|
|
422
|
+
- A minimize control on the Routes panel. On a phone the panel is a bottom sheet that covers the chart,
|
|
423
|
+
so a chevron in the header collapses it to just the header bar while it stays open, freeing the chart
|
|
424
|
+
to tap waypoints into a route. The control only appears at phone widths.
|
|
425
|
+
|
|
426
|
+
### Changed
|
|
427
|
+
|
|
428
|
+
- The Layers panel now leads with "My routes and tracks" above "Traffic and live data". The panel
|
|
429
|
+
order is kept aligned with the map stack so drag-to-reorder lands coherently, so this also raises
|
|
430
|
+
the routes and track layers above AIS and the reference overlays on the chart; the own vessel and
|
|
431
|
+
the collision rings stay pinned on top.
|
|
432
|
+
- The Tracks panel now renders saved tracks as the same elevated cards as the Routes panel, each
|
|
433
|
+
showing the track's distance and duration, and the current-track stats line was tightened to a
|
|
434
|
+
label, value, and unit grid that removes the trailing whitespace and aligns the values in a column.
|
|
435
|
+
- At night-red, the base map's pre-colored sprite icons (road and transit shields, aerodrome marks)
|
|
436
|
+
are now hidden along with the POI dots, so the chart stays pure red on black with no stray blue,
|
|
437
|
+
green, or white icons. The text labels stay visible.
|
|
438
|
+
- The route line, the note selection ring, and the AIS target triangles gained a dark casing or halo,
|
|
439
|
+
so they keep their bright color but no longer sit low-contrast against the light day water. The
|
|
440
|
+
casing is invisible on the dark dusk and night-red maps, where the bright shape reads on its own.
|
|
441
|
+
|
|
442
|
+
### Fixed
|
|
443
|
+
|
|
444
|
+
- Importing a GPX route no longer aborts on a malformed numeric character entity: a code point outside
|
|
445
|
+
the Unicode range now stays literal instead of throwing an uncaught error that ended the whole import.
|
|
446
|
+
- The Routes opacity slider now dims the waypoint labels along with the route line and markers, the way
|
|
447
|
+
the tides and notes overlays already did.
|
|
448
|
+
- The active-route strip's previous and next waypoint buttons are now a full 44px touch target, so they
|
|
449
|
+
are usable underway.
|
|
450
|
+
- The precipitation legend now reads up to 40 mm/h, matching the range the precipitation field paints.
|
|
451
|
+
|
|
452
|
+
### Internal
|
|
453
|
+
|
|
454
|
+
- A simplification pass over the overlay work: each new overlay slice (seamarks, protected areas,
|
|
455
|
+
boundaries, ocean conditions) now exposes a band-owning factory, so the chart widget no longer
|
|
456
|
+
hardcodes which map band each draws into (matching the depth-charts sibling). Deduplicated the
|
|
457
|
+
chart zoom-range expression and the tides upcoming-events computation, moved the station-distance
|
|
458
|
+
formatter into the tides display module, and tidied a handful of comments.
|
|
459
|
+
- A simplification pass over the plotter and UI changes: the saved-item card list moved to one global
|
|
460
|
+
`.saved` system in `app.css` consumed by both panels, a shared `downloadBlob` helper backs the
|
|
461
|
+
track and route exporters, the GPX escape and unescape pair moved to one `xml-entities` module, the
|
|
462
|
+
distance-over-speed time uses the shared `etaSeconds` everywhere, the meters-per-degree constant is
|
|
463
|
+
shared from `$shared/nav`, the nav-strip `RouteProgress` type has one definition, the plan-speed
|
|
464
|
+
field uses the shared `.input`, and the whole-route distance derives from the leg table so the total
|
|
465
|
+
and the per-leg numbers cannot drift.
|
|
466
|
+
- A simplification pass over the contrast work: the route, selection-ring, and AIS dark contrast aids share
|
|
467
|
+
one `DARK_SCRIM` constant and an `rgbaCss` helper in `$shared/map` instead of three drifting literals,
|
|
468
|
+
and the SlideOver minimize takes one `{ collapsed, onToggle }` object so the state and its toggle are
|
|
469
|
+
always supplied together.
|
|
470
|
+
- A simplification pass over the profiles feature: the active-card accent edge-bar and "Active" badge moved
|
|
471
|
+
to the one global `.saved` system in `app.css` consumed by the Routes, Tracks, and Profiles panels, a
|
|
472
|
+
shared `pickTextFile` helper in `$shared/ui` backs both the route GPX import and the profile JSON
|
|
473
|
+
import instead of two hidden-input handlers, the profile importer validates the theme against the
|
|
474
|
+
shared `THEMES` list, and the default profile now auto-applies on a fresh device that has a default
|
|
475
|
+
but no active profile. The server sync also stops pushing after one rejected write, so a read-only
|
|
476
|
+
token attaches for reads but does not fire a doomed write on every later edit.
|
|
477
|
+
- A project-wide modularization pass. The labeled current-item stats grid that the
|
|
478
|
+
Routes editor and the Tracks panel duplicated verbatim is now one global `.stat-grid` system in
|
|
479
|
+
`app.css`. A shared `downloadText(filename, text, type)` helper backs the route GPX, track GeoJSON,
|
|
480
|
+
and profile JSON exporters, so the Blob construction lives in one place. The four weather colormaps
|
|
481
|
+
(wind, waves, precipitation, and cloud) build on a new `themedRamp(day, night)` factory in
|
|
482
|
+
`color-ramp.ts`, so the night-red ramp swap is defined once instead of repeated per colormap. Two
|
|
483
|
+
dependency-cruiser rules, `no-cross-slice-shared` and `no-cross-slice-entities`, now enforce that a
|
|
484
|
+
shared or entities slice reaches a sibling only through its index public API, matching the existing
|
|
485
|
+
`no-cross-feature` rule.
|
|
486
|
+
- The deferred modularization items. A shared `fetchJsonOrUndefined` helper backs the free-API weather
|
|
487
|
+
clients and the course REST hydration; a shared `MemoryCache` backs the weather grid cache and the
|
|
488
|
+
tides per-station caches; the `SlideOver` shell gained a subtitle and a footer so the last hand-rolled
|
|
489
|
+
panel (the note detail) folds into it; a shared `SavedList` and `VisibilityToggle` back the Routes,
|
|
490
|
+
Tracks, and Profiles cards; the coordinate guards moved to a `shared/geo` slice so geometry no longer
|
|
491
|
+
imports the SignalK transport slice for a type; and the portable profile settings are a single
|
|
492
|
+
declarative registry typed so a forgotten setting fails the build.
|
|
493
|
+
- A whole-repo cleanup. Correctness fixes (the GPX entity guard, the route label opacity, a
|
|
494
|
+
marine-merge step-count guard, a deferred object-URL revoke, and a wind-arrow bounds guard), a round
|
|
495
|
+
of dead index re-export trimming, the four repeated course hydrate-and-seed sites folded into one
|
|
496
|
+
helper, and assorted comment and legend fixes.
|
|
497
|
+
|
|
498
|
+
<a id="v021"></a>
|
|
499
|
+
|
|
500
|
+
## [0.2.1] - 2026-06-09
|
|
501
|
+
|
|
502
|
+
### Fixed
|
|
503
|
+
|
|
504
|
+
- Points of interest (Crow's Nest and ActiveCaptain) no longer flicker. A slow or rate-limited
|
|
505
|
+
provider response made the markers vanish and reappear, because a failed fetch was treated as
|
|
506
|
+
"no POIs" and cleared them. A failed fetch now keeps the markers on screen, and the overlay
|
|
507
|
+
caches fetched sets by area, so panning back, panning a little, or zooming in reuses a recent
|
|
508
|
+
fetch instead of re-hitting the network.
|
|
509
|
+
- Active marine warnings (gale, storm, and small-craft advisories) no longer flicker off the
|
|
510
|
+
conditions panel when a weather-provider request transiently fails: the last warnings are kept
|
|
511
|
+
until a real update replaces them.
|
|
512
|
+
- An active route's guidance (the nav strip, arrival circle, and auto-advance) no longer freezes on
|
|
513
|
+
stale values after a stream reconnect. The v2 course data is re-hydrated on reconnect, since
|
|
514
|
+
resubscribing cannot redeliver it under subscribe=none, and the route list is refreshed too.
|
|
515
|
+
- Server charts no longer blank on a transient failure at map load: the fetch now distinguishes a
|
|
516
|
+
failed request from a reachable server with no charts, matching routes and points of interest.
|
|
517
|
+
- Imported-chart storage is reclaimed at startup: a PMTiles blob left behind by a failed save or a
|
|
518
|
+
delete that ran while storage was degraded is now swept once its descriptor is gone, so orphaned
|
|
519
|
+
blobs cannot accumulate on disk.
|
|
520
|
+
- The Points-of-interest layer no longer fetches from the provider or re-clusters while it is toggled
|
|
521
|
+
off: a hidden layer now does no network or rendering work until it is shown again.
|
|
522
|
+
- Silenced a stream of "styleimagemissing" console warnings: the base map style references a few
|
|
523
|
+
sprite icons and landuse patterns its published sprite does not contain, so a transparent
|
|
524
|
+
placeholder is supplied for each. The map is unaffected; the console stays clean.
|
|
525
|
+
|
|
526
|
+
### Internal
|
|
527
|
+
|
|
528
|
+
- Bounded two more caches to a fixed size: the note-detail memo, and the persistent weather-grid
|
|
529
|
+
store (now matching its in-memory tier).
|
|
530
|
+
- A whole-codebase cleanup pass: deduplicated the local-date computation shared by
|
|
531
|
+
the tides fetch window and its session-cache key, sourced the vessel and AIS default colors from
|
|
532
|
+
the day theme token, dropped over-broad slice exports, reset the stream reconnect backoff on an
|
|
533
|
+
explicit disconnect, and tidied several comments, an accessibility region, and a redundant live
|
|
534
|
+
region. No user-facing behavior change beyond the fixes above.
|
|
535
|
+
|
|
536
|
+
<a id="v020"></a>
|
|
537
|
+
|
|
538
|
+
## [0.2.0] - 2026-06-08
|
|
539
|
+
|
|
540
|
+
### Added
|
|
541
|
+
|
|
542
|
+
- A Tides panel (US waters), opened from the app menu, showing the nearest NOAA tide station's next
|
|
543
|
+
high and low with heights in meters and feet, a 48-hour tide curve with a "now" marker, and the
|
|
544
|
+
nearest tidal-current station's next flood or ebb with its rate and set. The nearest tide and
|
|
545
|
+
current stations are also markable on the chart from the Layers panel. It degrades to a clear
|
|
546
|
+
message outside US coverage, and the data is cached for the session.
|
|
547
|
+
- New built-in chart overlays, all free, key-free, and verified against the live services. Each
|
|
548
|
+
starts hidden, toggles from the Layers panel, and carries its source attribution:
|
|
549
|
+
- OpenSeaMap seamarks: a global overlay of navigation aids (buoys, beacons, lights, and harbors).
|
|
550
|
+
- Marine protected areas: EMODnet protected areas with Natura 2000 nested under them (EU), and the
|
|
551
|
+
NOAA MPA Inventory (US).
|
|
552
|
+
- Maritime boundaries: the inter-country jurisdiction lines and the territorial sea (12 nm), so you
|
|
553
|
+
can see when a passage crosses into another country's waters.
|
|
554
|
+
- Ocean conditions: NASA GIBS sea-surface temperature and sea ice concentration (global, daily),
|
|
555
|
+
which appear in a new Ocean conditions section of the Layers panel and default to translucent.
|
|
556
|
+
- A review step when importing a chart: after the file or URL is read, you can rename it and check
|
|
557
|
+
its type, zoom range, and size before saving, instead of it saving immediately.
|
|
558
|
+
- URL-based imported charts now register on the Signal K server as a chart resource, so other
|
|
559
|
+
devices on the same server discover them, and deleting the chart removes the server resource too.
|
|
560
|
+
The chart stays manageable locally (a synced chart is shown once, not twice). File-based imports
|
|
561
|
+
stay on the importing device, since a stock server cannot host the file bytes.
|
|
562
|
+
|
|
563
|
+
### Changed
|
|
564
|
+
|
|
565
|
+
- The Layers panel is reorganized into collapsible categories (Traffic and live data, Navigation
|
|
566
|
+
aids, Areas and boundaries, My routes and tracks, Ocean conditions, and Charts and depth), each
|
|
567
|
+
with a row count, so a long flat list reads as a few sections. The two most-used categories open by
|
|
568
|
+
default and the rest collapse to cut clutter; each category remembers whether you left it open or
|
|
569
|
+
closed. Per-layer toggles, opacity, the facet-group cards, and drag-to-reorder are unchanged.
|
|
570
|
+
- A better default chart order: the US NOAA ENC nautical chart leads, then US BlueTopo bathymetry,
|
|
571
|
+
then EMODnet (Europe), then GEBCO (global), so the most detailed free coverage is on top. You can
|
|
572
|
+
still drag any layer to reorder it.
|
|
573
|
+
- Cleaner, consistent layer names: sentence case throughout, plural for collection layers (Track is
|
|
574
|
+
now Tracks), and a unified "source, type, region" format for the single-layer bathymetry overlays
|
|
575
|
+
(for example "GEBCO bathymetry (global)"). The weather "Cloud cover" layer is now "Cloud" to match
|
|
576
|
+
the other single-word weather layers.
|
|
577
|
+
- A denser, more consistent app menu and Layers panel: list rows use a compact row size while action
|
|
578
|
+
buttons keep the larger touch target, the opacity sliders sit in shorter rows, and the gaps are
|
|
579
|
+
snapped to one spacing scale, so more layers and menu items fit without scrolling.
|
|
580
|
+
- Three depth sources now present as labeled groups in the Layers panel, each with a base facet and a
|
|
581
|
+
nested survey-quality facet: "NOAA ENC (US)" (Base chart, Data quality (ZOC)), "EMODnet (Europe)"
|
|
582
|
+
(Bathymetry, Quality index), and "BlueTopo (US)" (Bathymetry, Uncertainty). The quality facets show
|
|
583
|
+
how reliable each cell is: ZOC zones for the ENC, EMODnet's combined quality index, and BlueTopo's
|
|
584
|
+
per-cell vertical uncertainty. Within a group the facet toggles are aligned in one column, a single
|
|
585
|
+
drag handle and opacity slider serve the whole group, and the quality facet only enables while the
|
|
586
|
+
base facet is on (turning the base off hides it). A generic sub-layer grouping mechanism backs this,
|
|
587
|
+
so any future multi-facet chart can group its facets the same way.
|
|
588
|
+
|
|
589
|
+
### Fixed
|
|
590
|
+
|
|
591
|
+
- Deleting a user-imported chart now actually removes it. The layer row, the map overlay, and the
|
|
592
|
+
stored descriptor were all left in place because the delete handler read the chart id after the
|
|
593
|
+
panel had already cleared its selection, so the removal threw and never ran.
|
|
594
|
+
- A chart imported while a dark theme (dusk or night-red) is active now takes the theme immediately,
|
|
595
|
+
instead of staying in day colors until the next theme change. Overlays registered after the first
|
|
596
|
+
recolor are now themed at registration.
|
|
597
|
+
- The nearest tide and tidal-current readings no longer briefly go stale around local midnight: the
|
|
598
|
+
session cache now rolls over on the same local day the NOAA forecast window uses, rather than on
|
|
599
|
+
the UTC day.
|
|
600
|
+
|
|
601
|
+
### Internal
|
|
602
|
+
|
|
603
|
+
- A whole-codebase cleanup pass: named the shared millisecond constants once (MINUTE_MS, HOUR_MS,
|
|
604
|
+
DAY_MS), added a placeholder-aware nautical-miles readout, deduplicated the base-theme style
|
|
605
|
+
lookups, removed a dead weather-provider field and an empty re-export shim, tightened the
|
|
606
|
+
storage and chart-adapter export surface, and aligned the route and wave overlay label font and
|
|
607
|
+
arrow color with the other overlays. No user-facing behavior change beyond the fixes above.
|
|
608
|
+
|
|
609
|
+
<a id="v013"></a>
|
|
610
|
+
|
|
611
|
+
## [0.1.3] - 2026-06-08
|
|
612
|
+
|
|
613
|
+
### Added
|
|
614
|
+
|
|
615
|
+
- A fully keyboard-navigable app menu: arrow keys move between items, Home and End jump to the ends,
|
|
616
|
+
and it follows the WAI-ARIA menu pattern with grouped sections (Navigation and Alarms), roving
|
|
617
|
+
focus, and announced toggle states.
|
|
618
|
+
- Reduced-motion support: when the system prefers reduced motion, the chart's center-on-boat,
|
|
619
|
+
fly-to, and fit-to-bounds camera moves jump instantly instead of animating.
|
|
620
|
+
- An opt-in NOAA ENC data-quality overlay. The NOAA ENC chart is now one clean chart layer plus a
|
|
621
|
+
separate, default-hidden "data quality" layer carrying the Zones of Confidence (CATZOC) ratings,
|
|
622
|
+
so the chart no longer always paints the data-quality triangles and overscale patterns on top.
|
|
623
|
+
- One-tap alarm muting from the danger strip, a muted-alarm badge in the top bar, and a spoken
|
|
624
|
+
collision summary written to a live region for assistive technology.
|
|
625
|
+
- Motion and depth across the interface: the slide-over panels, the app menu, and the weather panel
|
|
626
|
+
now reveal with a short reduced-motion-aware transition, the theme toggle animates its icon on each
|
|
627
|
+
cycle, and the panels and menu carry a layered shadow. A more confident day palette and a larger
|
|
628
|
+
instrument-readout type tier make the hero numbers (SOG, the nav metrics, the conditions) dominate
|
|
629
|
+
their labels.
|
|
630
|
+
|
|
631
|
+
### Changed
|
|
632
|
+
|
|
633
|
+
- Bearings are now labeled true (123 degrees T) on the COG, BTW, and wind readouts, time-to-go shows
|
|
634
|
+
hours and minutes past an hour (2h 05m) instead of a bare minute count, and the collision strip and
|
|
635
|
+
its spoken summary are graded danger versus caution by the worst contact rather than always
|
|
636
|
+
sounding full danger.
|
|
637
|
+
- The design system was consolidated: one shared lit-toggle, input, slider,
|
|
638
|
+
and button-row vocabulary replaces the per-component copies, the danger and nav strips now stack
|
|
639
|
+
instead of overlapping so course guidance survives a close-quarters contact, and the danger strip
|
|
640
|
+
stays above the weather panel so Mute and Acknowledge are always reachable.
|
|
641
|
+
|
|
642
|
+
- The app menu is redesigned. Tracks, Routes, and Layers are now edge-docked slide-over panels
|
|
643
|
+
promoted from inline accordions, each with a back-to-menu button so you can move between panels
|
|
644
|
+
without reopening the menu, and the menu groups its items under section headers.
|
|
645
|
+
- Center, Follow, and Forecast now sit together in the bottom status strip as three matching labeled
|
|
646
|
+
pill buttons, in that order. Follow and Forecast show a clear lit on-state, kept dim enough for
|
|
647
|
+
night-red.
|
|
648
|
+
- A whole-codebase cleanup pass with no change to behavior beyond the fixes below: the Signal K
|
|
649
|
+
frame pipeline hands the per-frame value map straight to the store instead of rebuilding it each
|
|
650
|
+
frame, the active-route readouts compute each leg's geometry once per change rather than several
|
|
651
|
+
times per render, the Layers drag measures row positions once at drag start instead of on every
|
|
652
|
+
pointer move, and duplicated formatting, geometry, WMS, and map-image helpers were consolidated.
|
|
653
|
+
|
|
654
|
+
### Fixed
|
|
655
|
+
|
|
656
|
+
- The bottom status strip no longer overlaps or wraps unevenly on a phone. It stacks into a clean
|
|
657
|
+
layout: the live readouts above, and the Center, Follow, and Forecast controls on one row below.
|
|
658
|
+
- The Tracks panel's statistics now align in a single value column.
|
|
659
|
+
- The collision danger strip no longer double-announces to screen readers. The app keeps a single
|
|
660
|
+
concise spoken summary of the danger, and the on-screen contact list is now a silent visual
|
|
661
|
+
landmark, so assistive technology reads the danger once instead of twice.
|
|
662
|
+
- The animated wind field now honors the system reduced-motion preference, falling back to the static
|
|
663
|
+
wind arrows instead of running a continuous particle animation.
|
|
664
|
+
- The active-route strip no longer re-reads its whole readout line to a screen reader every second;
|
|
665
|
+
only the destination name announces, when a waypoint advances.
|
|
666
|
+
- On a phone the note detail and a leading panel no longer overlap as stacked bottom sheets (they are
|
|
667
|
+
mutually exclusive at narrow widths), the brand drops its version string so the top-bar controls
|
|
668
|
+
keep room, and the weather "Here" conditions open as a full-width sheet rather than covering the
|
|
669
|
+
small map.
|
|
670
|
+
- Form inputs theme their placeholder text, the day caution color is darker for contrast and is
|
|
671
|
+
clearly distinct from the alarm red, and the Forecast control exposes its dialog to assistive tech.
|
|
672
|
+
|
|
673
|
+
<a id="v012"></a>
|
|
674
|
+
|
|
675
|
+
## [0.1.2] - 2026-06-05
|
|
676
|
+
|
|
677
|
+
### Fixed
|
|
678
|
+
|
|
679
|
+
- Importing a chart now flies the map to the chart's bounds, so a PMTiles archive (by file or URL)
|
|
680
|
+
is immediately visible instead of staying off-screen when it covers a different area than the
|
|
681
|
+
current view. Previously the chart was added to the Layers panel but the map did not move, so it
|
|
682
|
+
looked like nothing happened. (A new fitBounds map command, fired only on a user import, never on
|
|
683
|
+
the charts restored at startup.)
|
|
684
|
+
|
|
685
|
+
### Changed
|
|
686
|
+
|
|
687
|
+
- The layer-toggle checkbox no longer shrinks when a layer name is long: it stays square (the name
|
|
688
|
+
ellipsizes instead).
|
|
689
|
+
|
|
690
|
+
<a id="v011"></a>
|
|
691
|
+
|
|
692
|
+
## [0.1.1] - 2026-06-05
|
|
693
|
+
|
|
694
|
+
### Changed
|
|
695
|
+
|
|
696
|
+
- App Store polish, reviewed against the Signal K AppStore publishing doc. The appIcon is now
|
|
697
|
+
256x256 (the previous 72x72 was below the documented 128x128 minimum). The README is scannable,
|
|
698
|
+
since the server's Webapps view renders it: the screenshots gallery that showed as raw HTML there
|
|
699
|
+
is removed (the screenshots stay in `signalk.screenshots` for the App Store detail page), and the
|
|
700
|
+
duplicate feature inventory is collapsed into one concise list. The title is now "WebGL chart
|
|
701
|
+
plotter for Signal K" across the README, the PWA manifest, and the repository description.
|
|
702
|
+
|
|
703
|
+
<a id="v010"></a>
|
|
704
|
+
|
|
705
|
+
## [0.1.0] - 2026-06-05
|
|
706
|
+
|
|
707
|
+
### Added
|
|
708
|
+
|
|
709
|
+
- Routes: plan a passage and follow it. Open Routes from the menu, draw a route on the chart (tap to
|
|
710
|
+
add waypoints, drag a point to move it, tap a midpoint to insert one), and watch the leg count and
|
|
711
|
+
total distance update live as you draw. Save it to the Signal K server (`/resources/routes` as a
|
|
712
|
+
GeoJSON LineString), and it syncs to every device and lists with show or hide, edit, activate, and
|
|
713
|
+
delete. Activating a route hands it to the Signal K v2 Course API, and a nav strip shows the active
|
|
714
|
+
waypoint, cross-track error with a steer-left or steer-right side, distance and bearing to the
|
|
715
|
+
waypoint, velocity made good, and time to go, with an arrival alarm at the arrival circle. The
|
|
716
|
+
guidance prefers the server's course calculations and computes them on the client when the
|
|
717
|
+
course-provider plugin is absent (a "computing locally" badge says when), the same graceful
|
|
718
|
+
degrade as the collision CPA. A failed save keeps the route under edit so nothing is lost, and the
|
|
719
|
+
on-chart editing line is themed for day, dusk, and night-red. Route editing uses Terra Draw.
|
|
720
|
+
|
|
721
|
+
- The weather forecast is cached in IndexedDB, so it survives a reload and a return to a recent view
|
|
722
|
+
reuses it instead of re-fetching. Unlike the service-worker cache, which browsers expose only in a
|
|
723
|
+
secure context, IndexedDB works over plain HTTP, so this is the offline-leaning weather cache for
|
|
724
|
+
the many users without SSL. Each grid is stored with a one-hour expiry, expiries are kept apart from
|
|
725
|
+
the grids so pruning never loads them, and the store degrades to memory and never throws when
|
|
726
|
+
IndexedDB is unavailable. Verified over https: after a reload, opening the forecast served the grid
|
|
727
|
+
from IndexedDB with zero Open-Meteo requests.
|
|
728
|
+
|
|
729
|
+
- Weather. A dedicated weather mini-map, opened by the Forecast button centered in the status strip,
|
|
730
|
+
keeps the navigation chart clean and the weather within its data resolution. The mini-map is capped
|
|
731
|
+
at zoom 7 (RainViewer's real radar resolution) and panned independently of the chart, so weather can
|
|
732
|
+
never be zoomed past what the data supports: no "zoom not supported" tiles, no pretending a coarse
|
|
733
|
+
grid has street-level detail. In the panel you toggle Wind, Pressure, Waves, Precipitation, Cloud
|
|
734
|
+
cover, or Rain radar, scrub the coming days with a time slider, read a per-layer color-ramp legend,
|
|
735
|
+
and tap anywhere for the wind, pressure, sea state, and rain at that point and time. Wind draws as
|
|
736
|
+
speed-colored arrows, mean-sea-level pressure as labeled isobar contours (marching squares, 4 hPa),
|
|
737
|
+
significant wave height, precipitation, and cloud cover as smooth color fields, and precipitation
|
|
738
|
+
radar as an animated RainViewer loop. The four area fills (waves, precipitation, cloud, and radar)
|
|
739
|
+
are mutually exclusive, one at a time, so they never stack into mud; wind arrows and pressure
|
|
740
|
+
isobars stay freely combinable on top. A "Here" panel shows the current conditions, a short
|
|
741
|
+
forecast, and any gale or storm warnings for the vessel's own position.
|
|
742
|
+
- Weather data prefers a configured Signal K weather provider (for example AccuWeather) for point
|
|
743
|
+
data: the tap readout and the "Here" conditions and warnings come from the provider when one is
|
|
744
|
+
set, and fall back automatically to the free, browser-only sources when none is configured. Area
|
|
745
|
+
data is always free, because no provider exposes gridded fields through Signal K: the atmospheric
|
|
746
|
+
grid and the marine wave field come from Open-Meteo, and radar from RainViewer, with no key and no
|
|
747
|
+
server plugin. Results are cached by viewport in memory so panning reuses a recent fetch, and the
|
|
748
|
+
Open-Meteo responses, the RainViewer frame index, and the radar tiles are cached by the service
|
|
749
|
+
worker for offline use. Layers beyond wind and waves are off on first open, themed for day, dusk,
|
|
750
|
+
and night-red (a deep, low-brightness red on black at night, no blue; the radar raster is
|
|
751
|
+
desaturated and dimmed).
|
|
752
|
+
- Wind draws as an animated WebGL particle field, the glanceable signature layer: thousands of
|
|
753
|
+
particles stream through the forecast wind with fading trails, colored by speed (the day ramp, and
|
|
754
|
+
a pure red-on-black ramp at night). It is a custom MapLibre layer running a GPU particle simulation
|
|
755
|
+
over the forecast u/v, projected so pan and zoom only reproject the particles and the trails reset
|
|
756
|
+
cleanly on a move. It falls back to the speed-colored arrow layer when WebGL is unavailable, and
|
|
757
|
+
the animation runs only while the Wind layer is on.
|
|
758
|
+
|
|
759
|
+
- Approving Binnacle's Signal K access is now self-explanatory and recognizable. The request uses a
|
|
760
|
+
named client id (`binnacle-<short>`) instead of a bare UUID, so it is easy to spot in the Signal K
|
|
761
|
+
access-requests list, and the "Requesting access" banner shows that id plus a one-click "Approve in
|
|
762
|
+
Signal K" shortcut that opens the admin access-requests page. A legacy bare-UUID client id is
|
|
763
|
+
upgraded to the named form on load, keeping any existing token.
|
|
764
|
+
|
|
765
|
+
- Follow boat: a "Follow boat" item in the menu locks the chart to the vessel, recentering on each
|
|
766
|
+
new position fix at your current zoom. It centers immediately when turned on, and a manual pan of
|
|
767
|
+
the chart releases the lock (a scroll-zoom keeps it). It is off by default and does not persist
|
|
768
|
+
across reloads. The one-shot "Center on boat" remains for a quick recenter that also zooms in when
|
|
769
|
+
you are zoomed far out.
|
|
770
|
+
|
|
771
|
+
- A Layers panel and drag-to-reorder for every layer. The old inline "Layers" submenu is now a
|
|
772
|
+
"Layers and charts" launcher that opens a left-docked slide-over listing every layer top of the map
|
|
773
|
+
first, grouped into "Charts and Depth" and "Overlays" with the own vessel and active alarms pinned on
|
|
774
|
+
top. Each row toggles, sets opacity, and drags (by pointer or keyboard) to restack the z-order, and
|
|
775
|
+
the order persists across visits. The panel docks opposite the note detail so both can be open at
|
|
776
|
+
once, and it themes for day, dusk, and night-red.
|
|
777
|
+
|
|
778
|
+
- Streaming depth charts. Four free hosted bathymetry and chart sources toggle on from the Charts
|
|
779
|
+
and Depth section, off by default and cached as you pan: GEBCO global bathymetry, EMODnet (Europe), the
|
|
780
|
+
NOAA ENC chart (US), and NOAA BlueTopo (US). They are reference overlays, not certified for
|
|
781
|
+
navigation. A raster source cannot be recolored, so at night it is desaturated and dimmed (no blue,
|
|
782
|
+
low brightness) rather than left full-color.
|
|
783
|
+
|
|
784
|
+
- Import your own charts. Add a PMTiles archive by URL or by dropping a file into a themed drop zone;
|
|
785
|
+
Binnacle reads its name, bounds, zoom, and whether it is vector or raster, lists it under Charts
|
|
786
|
+
and Depth as a normal reorderable layer, and stores an uploaded file in the browser for offline use. A
|
|
787
|
+
per-chart detail view renames it, shows its metadata, and deletes it (stating the storage freed).
|
|
788
|
+
Both vector and raster PMTiles render; full S-52 styling of converted ENC depth features is a later
|
|
789
|
+
spec.
|
|
790
|
+
|
|
791
|
+
- Note detail panel: tapping a point of interest now opens a slide-in side panel with native,
|
|
792
|
+
structured detail instead of a plain-text popup that bounced you to an external viewer. Binnacle
|
|
793
|
+
consumes Crow's Nest's presentation-neutral `properties.crowsNest` sections from
|
|
794
|
+
`/resources/notes/{id}`, rendering each item by kind (measures with units, availability badges,
|
|
795
|
+
rating stars, flag toggles, links, and notes), and falls back cleanly to the plain-text
|
|
796
|
+
description for any other notes provider or schema version. The marker icon now uses the
|
|
797
|
+
explicit POI type when present, the structured values render as text with scheme-checked links
|
|
798
|
+
(no HTML injection), and the panel is themed for day, dusk, and night-red and becomes a bottom
|
|
799
|
+
sheet on a narrow screen.
|
|
800
|
+
|
|
801
|
+
- Tracks: Binnacle now records and shows where you have been. The active track is drawn behind the
|
|
802
|
+
boat as you move, colored by speed (dark for slow, bright for fast) or a single solid color, and a
|
|
803
|
+
break in the line marks a GPS dropout or a gap between sessions. The whole voyage is kept in the
|
|
804
|
+
browser (IndexedDB) and reappears after a refresh. A "Tracks" submenu in the menu pauses and
|
|
805
|
+
resumes recording, shows live voyage stats (distance, duration, and average and maximum speed),
|
|
806
|
+
saves the current track to the Signal K server (`/resources/tracks` as GeoJSON), clears it, and
|
|
807
|
+
toggles the color mode. Saved tracks list with show or hide on the chart, delete, and a GeoJSON
|
|
808
|
+
export you can download. Track speeds are stored in SI (m/s) and converted to knots only at the
|
|
809
|
+
display edge; the track layer is a normal layer, so it toggles and fades from the Layers submenu.
|
|
810
|
+
|
|
811
|
+
- Lookout collision thresholds are now editable (differentiator step 6). A "Collision thresholds"
|
|
812
|
+
submenu in the menu sets the danger and warning CPA (nautical miles) and TCPA (minutes); changes
|
|
813
|
+
apply live to the assessment and persist across visits, with a reset to defaults. Values are stored
|
|
814
|
+
in SI and edited at the display edge in nm and minutes.
|
|
815
|
+
|
|
816
|
+
- Lookout publishes its collision alert to Signal K (differentiator step 5). When the assessment
|
|
817
|
+
crosses a threshold, Binnacle writes `notifications.navigation.collision` over the streaming API
|
|
818
|
+
(state alarm for danger, warn for warning, with the appropriate method) so other Signal K clients
|
|
819
|
+
and devices share the alarm; it clears to normal when the risk passes. It is published only when
|
|
820
|
+
the state or worst contact changes, not on every per-second tick.
|
|
821
|
+
|
|
822
|
+
- Lookout now sounds an audible collision alarm (differentiator step 4). When an AIS contact
|
|
823
|
+
crosses the danger CPA/TCPA threshold, a repeating two-beep tone plays, synthesized with the Web
|
|
824
|
+
Audio API so nothing is downloaded. Acknowledging the contact on the danger strip silences it, and
|
|
825
|
+
a new or more severe contact re-arms it; a "Mute alarm" toggle in the menu turns sound off entirely
|
|
826
|
+
and persists. The audio primes on your first interaction with the page (browsers block sound until
|
|
827
|
+
a gesture). Warnings stay visual only.
|
|
828
|
+
|
|
829
|
+
- An app menu in the top bar gives app-wide options a home. It stays a single button until
|
|
830
|
+
opened, then drops a themed popout, and closes on selection, Escape, or a click outside; the
|
|
831
|
+
trigger is a labeled disclosure (aria-haspopup, aria-expanded, aria-controls). The menu is generic:
|
|
832
|
+
it renders whatever action items it is given plus optional collapsible submenus, so adding an
|
|
833
|
+
option is one `MenuItem` (or one `MenuSubmenu`) in the app shell, never a change to the menu
|
|
834
|
+
itself. It hosts a "Center on boat" action that flies the map to the vessel and a "Layers"
|
|
835
|
+
submenu.
|
|
836
|
+
|
|
837
|
+
- Binnacle now remembers your session across a page refresh. The map reopens at the last center and
|
|
838
|
+
zoom, and each layer's visibility and opacity are restored, alongside the theme that was already
|
|
839
|
+
persisted. The view is saved to local storage after panning settles (one write per gesture, not
|
|
840
|
+
per frame), layer changes are saved as they happen, and a corrupt or out-of-range saved view is
|
|
841
|
+
ignored in favor of the default world view.
|
|
842
|
+
|
|
843
|
+
- Points-of-interest markers now use per-category icons and a rich detail popup. Each note is sorted
|
|
844
|
+
into a category (anchorage, marina, fuel, services, inlet, boat ramp, bridge, hazard, navaid,
|
|
845
|
+
structure, or a generic point of interest), matched from the provider's skIcon against the live
|
|
846
|
+
Crow's Nest / ActiveCaptain vocabulary with a keyword fallback for unfamiliar variants, so
|
|
847
|
+
navigation lights and channel buoys read as navaids, creek inlets as inlets, and boat ramps and
|
|
848
|
+
bridges as themselves instead of plain pins. Each category draws as a themed disc with a glyph:
|
|
849
|
+
Lucide glyphs (anchor, sailboat, fuel pump, wrench, waves, landmark, triangle-alert, map-pin) per
|
|
850
|
+
the spec's chosen app icon family, plus custom slipway and bridge marks. Hazards take the alarm hue,
|
|
851
|
+
navaids the caution hue, the rest the POI hue; all recolor with the theme (night-red stays in the
|
|
852
|
+
red band).
|
|
853
|
+
Clicking a marker opens a themed popup with the name, category, any description and source
|
|
854
|
+
attribution, and an http(s)-only link to the provider's detail page, and the selected marker gets a
|
|
855
|
+
highlight ring.
|
|
856
|
+
|
|
857
|
+
- Navaids now render type-specific symbols instead of one generic marker. The note name is parsed
|
|
858
|
+
into a kind (lighthouse, light, buoy, daybeacon, or generic) and, for buoys and daybeacons, a
|
|
859
|
+
lateral side from the aid's number using the US IALA-B convention (even = red, starboard hand;
|
|
860
|
+
odd = green, port hand). Lights draw as a magenta flare, lighthouses as a lantern-topped tower,
|
|
861
|
+
starboard marks as a red cone or triangle, port marks as a green cylinder or square, so a channel
|
|
862
|
+
reads at a glance. The side is carried by shape as well as color, so it survives night-red (where
|
|
863
|
+
red and green collapse to two red shades). This infers symbols from the note text; full S-52
|
|
864
|
+
symbology keyed off S-57 ENC attributes (shape, color, category) remains the later vector-chart
|
|
865
|
+
spec, since notes carry no such attributes.
|
|
866
|
+
|
|
867
|
+
- Points-of-interest markers cluster at lower zoom and split apart as you zoom in, so a busy harbor
|
|
868
|
+
shows a single counted disc instead of a stack of overlapping markers; clicking a cluster zooms to
|
|
869
|
+
expand it. Marker size scales gently with zoom.
|
|
870
|
+
|
|
871
|
+
- Points-of-interest overlay: Binnacle now renders Signal K `notes` resources on the map, so POI
|
|
872
|
+
providers like signalk-crows-nest (Active Captain, OpenSeaMap, NOAA, USCG light list) show up.
|
|
873
|
+
The overlay fetches notes scoped to the current viewport (`?bbox=...`, no `provider` so every
|
|
874
|
+
notes provider merges, which is how Freeboard-SK retrieves them), refetched as the map moves and
|
|
875
|
+
gated below zoom 9. POIs draw as themed dots with names at zoom 12 and up, and toggle from the
|
|
876
|
+
layers panel. Earlier nothing showed because Binnacle had no consumer for `notes` resources; the
|
|
877
|
+
data was being served correctly all along.
|
|
878
|
+
|
|
879
|
+
- Lookout collision chart highlight (differentiator step 3): dangerous AIS contacts now get a graded
|
|
880
|
+
ring on the chart in the safety z-band, danger and warning colored from the theme (day, dusk, and
|
|
881
|
+
night-red, which keeps both in the red band with danger brighter). The overlay is dirty-checked
|
|
882
|
+
against the assessment so it only rebuilds when a contact's id, severity, or position changes, is
|
|
883
|
+
theme-aware through the layer manager's applyTheme broadcast, and toggles from the layers panel.
|
|
884
|
+
|
|
885
|
+
- Lookout danger strip: collision danger now surfaces on screen. A strip floats at the bottom of
|
|
886
|
+
the chart listing the most dangerous AIS contacts with their closest point of approach in nautical
|
|
887
|
+
miles and time to closest approach in minutes, color-graded by severity, with an acknowledge
|
|
888
|
+
control and a "computing locally" note when the values are the client-side fallback rather than a
|
|
889
|
+
Signal K provider. The strip is absent when nothing is dangerous, so a calm night watch stays dark,
|
|
890
|
+
and it updates as traffic moves. This is the first on-screen slice of the active-safety Lookout
|
|
891
|
+
feature; the chart highlight, audible alarm, notifications, and thresholds panel follow.
|
|
892
|
+
- Offline and PWA caching: Binnacle is now an installable progressive web app. A service worker
|
|
893
|
+
precaches the app shell, runtime-caches the OpenFreeMap base map and the Signal K PMTiles charts
|
|
894
|
+
cache-first (range-request aware) as they are viewed, and never caches the live Signal K stream or
|
|
895
|
+
REST API, so anywhere the navigator has looked renders offline while live data stays fresh. The
|
|
896
|
+
top bar offers an update when a new build is published, and the status strip shows an offline
|
|
897
|
+
indicator. Service workers require a secure context, so this activates when the Signal K server is
|
|
898
|
+
served over HTTPS; over plain HTTP the app degrades cleanly to online-only with no errors.
|
|
899
|
+
- The status strip shows the map's center latitude and longitude and the zoom level, updating as
|
|
900
|
+
the chart is panned and zoomed, formatted at the display edge with hemisphere suffixes.
|
|
901
|
+
- Lookout (active-safety, first slice): the headless collision data layer behind the upcoming
|
|
902
|
+
danger strip. A pure, test-first closest-point-of-approach module computes CPA and TCPA from the
|
|
903
|
+
own vessel and a target's position and velocity, a persisted-settings helper holds
|
|
904
|
+
user-configurable danger and warning thresholds with sensible defaults, and a collision
|
|
905
|
+
assessment ranks AIS contacts by severity, preferring the server's `navigation.closestApproach`
|
|
906
|
+
when a provider supplies it and falling back to the computed values otherwise. The danger strip,
|
|
907
|
+
chart highlight, audible alarm, and Signal K notification publishing follow in later slices.
|
|
908
|
+
- Theming: a design-token system with day, dusk, and night-red palettes, switched by a single
|
|
909
|
+
theme controller that sets `data-theme` on the document and persists the choice. Every surface
|
|
910
|
+
recolors from CSS custom properties, a top-bar toggle cycles the themes, and the map base
|
|
911
|
+
recolors via `setPaintProperty` (keeping tiles and overlays). Night-red is pure red on true
|
|
912
|
+
black with no blue, and a dedicated alarm token stays distinguishable in every palette.
|
|
913
|
+
- Identity: self-hosted Inter (UI) and JetBrains Mono (tabular numeric readouts) typography
|
|
914
|
+
bundled for offline use, Lucide icons for the theme toggle and the layers panel, the own-ship
|
|
915
|
+
and AIS symbols recolored per theme so the chart shows no blue on the night-red theme (the own
|
|
916
|
+
ship turns red and AIS a night-safe amber), and the build version shown in the top bar.
|
|
917
|
+
- AIS targets: the worker learns the self vessel from the `hello` handshake and routes other
|
|
918
|
+
vessels' deltas into a per-context AIS stream, the store accumulates each target and prunes
|
|
919
|
+
ones that go silent past a six-minute window, an `AisTargets` entity interprets each target
|
|
920
|
+
into display units, and an AIS overlay renders them as GPU symbols in the traffic band that
|
|
921
|
+
rotate with course and skip rebuilding when nothing changed. The app subscribes `vessels.*` at
|
|
922
|
+
a controlled rate, and CPA and TCPA are read from `navigation.closestApproach` when a provider
|
|
923
|
+
supplies them.
|
|
924
|
+
- Charts: a generic chart-source adapter turns any Signal K chart resource into MapLibre source
|
|
925
|
+
and layer specs, branching on the chart type (raster tilelayer, WMS, WMTS, and S-57, plus
|
|
926
|
+
vector tileJSON with PMTiles resolved to the `pmtiles://` protocol) and honoring bounds and
|
|
927
|
+
zoom limits. Each chart wraps as a basemap-band overlay on the existing layer manager, the
|
|
928
|
+
charts client discovers them from `/resources/charts` (v2, falling back to v1, degrading to an
|
|
929
|
+
empty list offline), and a layers panel gives each chart a visibility toggle and an opacity
|
|
930
|
+
slider.
|
|
931
|
+
- Verify-before-push git hooks (`.githooks/`, installed via `npm run hooks`): a fast format,
|
|
932
|
+
lint, and boundary check before each commit, and the full type-check, test, and build gate
|
|
933
|
+
before each push, so a broken tree cannot be committed or pushed.
|
|
934
|
+
|
|
935
|
+
### Changed
|
|
936
|
+
|
|
937
|
+
- A final whole-codebase cleanup before the 0.1.0 release, no feature change. The
|
|
938
|
+
panel, button, icon, label, and instrument-strip styling moved into shared `app.css` utilities
|
|
939
|
+
(`.icon-btn` with accent and danger modifiers, `.btn-ghost`, `.btn-pill`, a shared bottom-strip
|
|
940
|
+
metrics row, a `.caps-label` for the uppercase section labels, and a 4px-based `--space-*` spacing
|
|
941
|
+
scale for the common padding, gap, and margin values): the Routes panel now renders the same
|
|
942
|
+
slide-over shell as the Layers and note panels instead of having the app shell hand-roll its dock
|
|
943
|
+
chrome, every panel header reads the shared `.panel-title`, and the row-action icon buttons and
|
|
944
|
+
ghost buttons stop being re-declared per component. The Signal K
|
|
945
|
+
resource clients (routes, charts, tracks, and course) now share one `fetchKeyedResource` plus
|
|
946
|
+
`putResource` and `deleteResource` instead of three copies of the v2-then-v1 fetch and five copies
|
|
947
|
+
of the PUT and DELETE wrapper; the three IndexedDB stores share one `openIdbDatabase` opener and one
|
|
948
|
+
`degradeToMemory` policy; the weather grid blends through the shared `lerp`; user-chart ids and the
|
|
949
|
+
save-name prompt use shared helpers; the store iterates own keys with `Object.entries`; the
|
|
950
|
+
longitude-delta normalize is total over any input; and the unused `routeLegs` was removed.
|
|
951
|
+
|
|
952
|
+
- The Signal K webapp manifest is complete for the App Store and the 0.1.0 release: five screenshots
|
|
953
|
+
(the chart with AIS, route planning, charts and depth, an anchorage point-of-interest detail, and
|
|
954
|
+
the weather mini-map) and a "Works well with" list (Crow's Nest for the points of interest Binnacle
|
|
955
|
+
renders, signalk-ssl for the HTTPS its offline cache needs, and signalk-virtual-weather-sensors as a
|
|
956
|
+
weather provider it reads). A cross-platform webapp CI builds, tests, and packs on Linux, macOS, and
|
|
957
|
+
Windows on Node 22 and 24, and a release publishes to npm with a provenance attestation.
|
|
958
|
+
|
|
959
|
+
- A UI consistency pass covering design tokens, layout, typography,
|
|
960
|
+
accessibility, and marine HMI conventions brought the whole interface to one standard. New tokens defined for all three themes (a large
|
|
961
|
+
radius, a shared hover and press timing, a caution-tier warning color, and an alarm tint) replace
|
|
962
|
+
the values components used to hand-code. Panel titles are consistent headings, the numeric readouts
|
|
963
|
+
share the mono instrument font, the bottom-strip titles match the panel title scale, and the
|
|
964
|
+
night-red border is deepened so panels stay separated where the shadow is dropped. The on-chart
|
|
965
|
+
route editing color now reads the one map-theme source instead of a duplicated table. The four
|
|
966
|
+
slide-over and overlay panels share one dismiss behavior (Escape closes the topmost, and focus
|
|
967
|
+
returns to the control that opened it).
|
|
968
|
+
|
|
969
|
+
- Routing cleanup pass covering geodesy and the route domain, course guidance and the resource
|
|
970
|
+
clients, the overlay, editor, and chart wiring, and the routing UI and app wiring, no feature
|
|
971
|
+
change. The Earth-radius constant and the antimeridian longitude-delta normalize are now
|
|
972
|
+
shared by the rhumb-line geometry and the collision CPA projection instead of duplicated, a
|
|
973
|
+
`steerSide` helper centralizes the port-versus-starboard cross-track convention, a `clientId` helper
|
|
974
|
+
folds the two copies of the secure-context id fallback, and the route distance no longer allocates a
|
|
975
|
+
leg array just to sum it. Stopping an active course now clears every course cell, where before it
|
|
976
|
+
left the previous point, the active route, and the arrival circle stale, and the seeding and
|
|
977
|
+
clearing of those cells moved onto the course entity that owns them. Dead route-editor methods and a
|
|
978
|
+
redundant overlay visibility flag were removed. Tests went from 412 to 415.
|
|
979
|
+
|
|
980
|
+
- The weather mini-map opens centered on the navigation chart's current view, so the forecast is for
|
|
981
|
+
the area you are looking at, rather than reopening at its own last position. The zoom is still capped
|
|
982
|
+
to the mini-map's maximum so weather never zooms past its data resolution.
|
|
983
|
+
|
|
984
|
+
- Second whole-repo cleanup pass covering weather, the Signal K data layer, map and charts, notes
|
|
985
|
+
and tracks, safety and chrome, and app and build infrastructure, no feature change.
|
|
986
|
+
The wind particle field caches its GPU uniform and attribute locations once at setup instead of
|
|
987
|
+
re-querying them every frame, the layer manager applies the stacking order once per batch when the
|
|
988
|
+
chart and overlays first load rather than restacking after each of a dozen-plus registrations, and
|
|
989
|
+
shared helpers fold repeated logic: a `DEG_TO_RAD` constant for hot numeric loops, an `HOUR_MS`
|
|
990
|
+
constant, an `applyRasterTheme` for the night-red raster treatment shared by the chart, depth, and
|
|
991
|
+
radar layers, an `asKeyedObject` guard shared by the chart, note, track, and weather resource
|
|
992
|
+
clients, a `toLonLat` mapper for the track coordinate builders, and one `RAIN_VISIBLE_MM_H`
|
|
993
|
+
threshold. Dead Signal K path constants were removed.
|
|
994
|
+
|
|
995
|
+
- A new app build now surfaces an Update control instead of silently reloading. The progressive web
|
|
996
|
+
app uses prompt registration rather than auto-update, so a fresh build never reloads the chart out
|
|
997
|
+
from under you mid-passage; the Update control lets the navigator choose when to apply it.
|
|
998
|
+
|
|
999
|
+
- Whole-repo cleanup pass, weather-weighted, no behavior change. One shared
|
|
1000
|
+
`emptyFeatureCollection` in `$shared/map` replaces the per-overlay copies (vessel, track, ais,
|
|
1001
|
+
notes, and weather), a shared `headingDegrees` helper folds the vessel and AIS heading fallback,
|
|
1002
|
+
the weather mini-map's viewport cache is now bounded, the wind overlay reports both its candidate
|
|
1003
|
+
layer ids so a rare WebGL fallback still restacks, the notes cluster click no longer double-fires,
|
|
1004
|
+
the radar opacity is one constant, and dead surface was removed (the unused `cellIndex` export, the
|
|
1005
|
+
unused weather `type` field and `weatherCacheKey` export, and a pass-through wrapper).
|
|
1006
|
+
|
|
1007
|
+
- Points of interest cluster later and say what they hold. Markers now uncluster from zoom 12 (up
|
|
1008
|
+
from 14), so the zoom you usually navigate at shows individual POIs instead of group circles, while
|
|
1009
|
+
the wider view (zoom 9 to 11) still clusters so it does not turn into a mash of overlapping pins. A
|
|
1010
|
+
cluster no longer reads as a generic purple circle: it shows the colored icon of its most important
|
|
1011
|
+
member (a red hazard disc if it holds any hazard, the amber navaid disc otherwise the point-of-
|
|
1012
|
+
interest disc), inside a ring that marks it as a group, with a count badge. Clicking a cluster still
|
|
1013
|
+
zooms it apart.
|
|
1014
|
+
|
|
1015
|
+
- Tidied the Signal K auth flow internals, with no behavior change. The focus and cross-tab
|
|
1016
|
+
storage listeners now live inside `AuthController` (like `OnlineStatus` owns its own listeners)
|
|
1017
|
+
instead of the app shell parsing the stored auth JSON itself, a single in-flight guard stops a
|
|
1018
|
+
duplicate access-request poll when a tab return fires focus and visibilitychange together, and
|
|
1019
|
+
the own-vessel and AIS subscriptions are issued in one call.
|
|
1020
|
+
|
|
1021
|
+
- Cleanup pass over the depth-charts work. The two IndexedDB stores now
|
|
1022
|
+
share one open-and-transaction helper; the unused PMTiles store list and total-size methods were
|
|
1023
|
+
dropped; byte-size formatting moved to a shared `formatBytes`; and a few small dead guards, a
|
|
1024
|
+
redundant array copy, and duplicated layer-id lists were tidied.
|
|
1025
|
+
|
|
1026
|
+
- Whole-repo cleanup pass. The collision assessment is memoized with
|
|
1027
|
+
`$derived`, so the O(targets) CPA loop runs once per real change instead of several times per
|
|
1028
|
+
frame (it was recomputed on every animation frame by the overlay and twice per alarm tick). CPA
|
|
1029
|
+
and TCPA display formatting is centralized in `shared/lib` (`formatCpaNm`, `formatTcpaMin`). The
|
|
1030
|
+
Signal K socket gates every handler on still being the current socket, so a superseded socket
|
|
1031
|
+
cannot inject a delta or schedule a second reconnect. POI classification is case-insensitive, the
|
|
1032
|
+
notes overlay skips per-frame work when the map is idle, the layers view updates one item in place
|
|
1033
|
+
instead of rebuilding the list on every slider tick, the menu submenu is tied to its content with
|
|
1034
|
+
`aria-controls`, and the empty-spec chart overlay no longer installs a dangling listener. Renamed
|
|
1035
|
+
`radiansToDegrees` to `radiansToBearing` (it normalizes to 0..360), exported `SELF_CONTEXT`, and
|
|
1036
|
+
added `nauticalMilesToMeters`. No behavior change beyond the perf and robustness fixes.
|
|
1037
|
+
|
|
1038
|
+
- Second whole-repo cleanup pass. The vessel, AIS, and collision
|
|
1039
|
+
entities now expose speed and course in SI (m/s and radians); knots and compass-bearing
|
|
1040
|
+
conversion moved to the display edge (the status strip, the vessel and AIS overlays, and a shared
|
|
1041
|
+
`formatKnots`), removing a knots-to-m/s and degrees-to-radians round-trip in the collision math.
|
|
1042
|
+
The worker hands the AIS batch to the main thread as a nested `Map` across the Comlink boundary,
|
|
1043
|
+
dropping a per-frame object rebuild on each side. The collision overlay dirty-checks the
|
|
1044
|
+
assessment by reference instead of building a per-frame signature string. Gap-splitting for track
|
|
1045
|
+
simplification and export is one shared `splitAtGaps` helper, the theme and settings localStorage
|
|
1046
|
+
writes are guarded against quota and private-mode failures, the menu-icon, connection-phase, and
|
|
1047
|
+
theme label maps are typed to their unions, and dead code (an unused `metersToNauticalMiles`
|
|
1048
|
+
export, a `LatLon` re-export, the entry-module default export, and a redundant callback wrapper)
|
|
1049
|
+
was removed.
|
|
1050
|
+
|
|
1051
|
+
- The layers controls (per-layer visibility and opacity) moved off the chart into the app menu.
|
|
1052
|
+
They were a panel floating over the top-left of the map; they now live in a collapsible "Layers"
|
|
1053
|
+
submenu inside the menu, so the chart is unobstructed and the controls share one place with other
|
|
1054
|
+
app options. Each layer's visibility and opacity still persist across refreshes.
|
|
1055
|
+
|
|
1056
|
+
- The own-vessel marker is now a boat hull instead of a flat triangle. It is drawn with the 2D
|
|
1057
|
+
canvas (filled hull, darker outline, sharp bow, flat transom) at 2x for retina crispness, rotates
|
|
1058
|
+
to `headingTrue` (falling back to `courseOverGroundTrue`), and recolors with the theme (blue by
|
|
1059
|
+
day, red at night). The pointed bow makes the heading unambiguous. The symbol-overlay factory
|
|
1060
|
+
gained an optional `pixelRatio` so an icon can be drawn at 2x.
|
|
1061
|
+
|
|
1062
|
+
- A tiled chart now hands off to the base map when you zoom past its native detail. Each chart's
|
|
1063
|
+
draw layers are capped one zoom level beyond the source's native maximum (read from the loaded
|
|
1064
|
+
tile metadata, so it is archive-agnostic), so zooming in past the chart's scale reveals the sharp
|
|
1065
|
+
base map instead of a blocky overzoomed chart. The chart stays authoritative within its own zoom
|
|
1066
|
+
range and aligned with the base beyond it.
|
|
1067
|
+
|
|
1068
|
+
- The collision danger strip's Acknowledge control now works: acknowledging suppresses the current
|
|
1069
|
+
worst contact, and a new or more severe contact automatically re-arms the alert. The full
|
|
1070
|
+
mute/alarm lifecycle remains a later Lookout step.
|
|
1071
|
+
|
|
1072
|
+
- Whole-repo cleanup pass: a chart now themes its own draw layers through an `applyTheme` broadcast
|
|
1073
|
+
from the layer manager, so the widget no longer reaches into chart layers by id (the source-layer
|
|
1074
|
+
to color mapping lives in one place); the vessel and AIS overlays are built from a shared
|
|
1075
|
+
`createSymbolOverlay` factory instead of duplicated scaffolding; the vector draw order and per
|
|
1076
|
+
source-layer styling are a single ordered list; a `mapstyleJSON` chart is a clean no-op pending
|
|
1077
|
+
the style pipeline rather than a broken source; AIS target views are memoized by version so
|
|
1078
|
+
own-vessel motion no longer rebuilds the list; the AIS staleness scan is throttled off the
|
|
1079
|
+
per-frame path; the subscription registry gained a refcounted `remove` and the worker routes
|
|
1080
|
+
unsubscribe through it so a dropped path is not resurrected on reconnect; `PersistedValue` reports
|
|
1081
|
+
`fromStorage` by key presence rather than a value compare; coordinate formatting and the
|
|
1082
|
+
AIS-target field extractors were de-duplicated; the connecting state is a shared
|
|
1083
|
+
`INITIAL_CONNECTION_STATE`; dead code was removed (`PathCell.receivedAt`, the unused `worst`
|
|
1084
|
+
getter, the `kelvinToCelsius` and `metersToFeet` helpers, identity arithmetic in the icons, and
|
|
1085
|
+
several unreachable null-guards); and the danger strip shows a "+N more" cue instead of silently
|
|
1086
|
+
truncating the contact list.
|
|
1087
|
+
|
|
1088
|
+
- Whole-repo cleanup pass: the chart source and layer ids derive from a single `chartSourceId`
|
|
1089
|
+
helper, the own-vessel overlay skips its per-frame `setData` when position and heading are
|
|
1090
|
+
unchanged, the vessel icon is built once and cached, the connection clears its reconnect timer
|
|
1091
|
+
on disconnect, malformed delta frames and chart fetch errors now warn instead of failing
|
|
1092
|
+
silently, MapLibre source and layer specs are properly typed (no `as never` casts), the layers
|
|
1093
|
+
panel hides the opacity slider for layers that do not support it, the unit converters accept
|
|
1094
|
+
`null`, the connection wakes a single shared own-vessel instead of two, and the shared test
|
|
1095
|
+
fakes (`FakeWebSocket`, `createFakeMap`) live in `src/shared/testing/` rather than being
|
|
1096
|
+
redefined per test. The dependency-cruiser ruleset now covers every Feature-Sliced Design
|
|
1097
|
+
layer direction, including the cross-feature public-API boundary.
|
|
1098
|
+
|
|
1099
|
+
- The map: a MapLibre GL map with a vector base, rendered in the chart area. A framework-free
|
|
1100
|
+
`LayerManager` gives every overlay an independent toggle, opacity, and deterministic z-order
|
|
1101
|
+
via sentinel layers and `beforeId`, so a new overlay later is a new file plus one
|
|
1102
|
+
registration. The own vessel renders as a GPU symbol layer that rotates with heading (falling
|
|
1103
|
+
back to course over ground), updated from the Signal K store each animation frame. Includes
|
|
1104
|
+
the PMTiles protocol registration for future offline tiles.
|
|
1105
|
+
|
|
1106
|
+
- Real-time data layer: a Web Worker hosts the Signal K WebSocket client, bridged to the main
|
|
1107
|
+
thread with Comlink, delivering one batched frame per animation frame. A path-keyed runes
|
|
1108
|
+
store of independently reactive cells lets a component bound to one Signal K path avoid
|
|
1109
|
+
re-running when an unrelated path changes. Includes delta reconciliation, a per-frame
|
|
1110
|
+
last-write-wins batcher, a refcounted subscription registry, full-jitter reconnection with
|
|
1111
|
+
resubscribe on open, and an own-vessel entity that converts SI values to display units at the
|
|
1112
|
+
edge. The shell shows live connection state and own-vessel SOG and COG.
|
|
1113
|
+
|
|
1114
|
+
- Project floor (Phase 1 of the foundation): a Svelte 5, Vite, and TypeScript application
|
|
1115
|
+
that builds as a Signal K webapp, serving static files from `public/` at `/binnacle/`.
|
|
1116
|
+
- Feature-Sliced Design layout (`app`, `views`, `widgets`, `features`, `entities`, and
|
|
1117
|
+
`shared`) with module boundaries enforced by dependency-cruiser.
|
|
1118
|
+
- SI unit-conversion module in `shared`, built test-first, covering meters per second to
|
|
1119
|
+
knots, radians to a normalized degree bearing, Kelvin to Celsius, meters to feet, and
|
|
1120
|
+
meters to nautical miles.
|
|
1121
|
+
- Verification toolchain: Biome for lint and format, svelte-check for type-checking, Vitest
|
|
1122
|
+
for unit tests, Playwright for an end-to-end smoke test, and a CI workflow running the
|
|
1123
|
+
full gate on Node 24.
|
|
1124
|
+
- Foundation design spec and Phase 1 implementation plan under `docs/superpowers`.
|
|
1125
|
+
- README, an Apache-2.0 LICENSE, and a Buy Me a Coffee funding link (README badge,
|
|
1126
|
+
`.github/FUNDING.yml`, and the `package.json` funding field).
|
|
1127
|
+
|
|
1128
|
+
### Fixed
|
|
1129
|
+
|
|
1130
|
+
- The active-route Stop button and the collision-alarm Acknowledge button on the bottom strips were
|
|
1131
|
+
inert. When both strips moved to the shared `.bottom-strip` class, the app shell's `pointer-events`
|
|
1132
|
+
override still targeted their old `.nav-strip` and `.danger-strip` selectors, so each slot's
|
|
1133
|
+
`pointer-events: none` reached the button. The override now targets `.bottom-strip`, restoring both
|
|
1134
|
+
safety-critical actions.
|
|
1135
|
+
|
|
1136
|
+
- Night-red contract violations are corrected: the AIS target was orange (it is
|
|
1137
|
+
now in the red band, with a test guarding it), and the rain-radar legend showed literal blue and
|
|
1138
|
+
green chips at night (now a red intensity ramp). The collision warning severity and an empty track's
|
|
1139
|
+
stats are no longer misleading (a distinct warning color in the danger strip and the thresholds, and
|
|
1140
|
+
a placeholder instead of a zero), and the weather panel no longer renders with square corners
|
|
1141
|
+
because a referenced radius token was undefined.
|
|
1142
|
+
|
|
1143
|
+
- Accessibility fixes: the slide-over panels close on Escape and restore focus, the
|
|
1144
|
+
layer visibility checkboxes and the threshold inputs have explicit names, the note-detail and
|
|
1145
|
+
weather conditions async states announce as status or alert, the add-chart URL field is labeled, the
|
|
1146
|
+
chart rename commits on Enter, the layer-reorder handle advertises its arrow-key shortcut, and the
|
|
1147
|
+
menu popout uses dvh so a long menu stays reachable on a phone.
|
|
1148
|
+
|
|
1149
|
+
- Saving a route now works. The Signal K server validates the standard route resource, and two
|
|
1150
|
+
things failed that validation silently: an unnamed waypoint wrote an empty metadata entry the
|
|
1151
|
+
schema rejects (every entry must carry a name), and over plain HTTP the route id fell back to a
|
|
1152
|
+
non-UUID string that the resources API refuses for standard types. Routes now omit per-waypoint
|
|
1153
|
+
metadata when no waypoint is named (and name the gaps by position otherwise), and route and track
|
|
1154
|
+
ids are always real v4 UUIDs, generated from `crypto.getRandomValues` where `crypto.randomUUID` is
|
|
1155
|
+
unavailable. Verified end to end against the server: a drawn, unnamed route now saves, lists, and
|
|
1156
|
+
survives a reload.
|
|
1157
|
+
|
|
1158
|
+
- The on-chart route editing line is easier to see and the Routes controls read as actions. The
|
|
1159
|
+
editing line was a blue that blended into the water; it now uses the bright selection accent (amber
|
|
1160
|
+
by day, a light red at night) and a heavier stroke, and the New route and Save buttons are filled
|
|
1161
|
+
with the accent instead of flat gray. Each saved route now sits in its own elevated card: the name
|
|
1162
|
+
is a title on its own line, a mono distance and waypoint-count readout sits beneath it in the same
|
|
1163
|
+
instrument style as the navigation strip, the actions form a clean cluster with the delete pushed to
|
|
1164
|
+
the trailing edge, and the active route is marked with an accent edge bar, an accent tint, and an
|
|
1165
|
+
"Active" pill so the live route is obvious at a glance in every theme.
|
|
1166
|
+
|
|
1167
|
+
- In the menu, Routes now sits above Layers and charts.
|
|
1168
|
+
|
|
1169
|
+
- A vector chart that declares a coverage extent now honors it. The raster chart paths already passed
|
|
1170
|
+
the declared `bounds` to MapLibre, but the vector path dropped it, so a regional vector chart
|
|
1171
|
+
requested and 404'd tiles across the whole world instead of only within its coverage. The vector
|
|
1172
|
+
source now carries `bounds` the same way the raster sources do.
|
|
1173
|
+
|
|
1174
|
+
- A provider-supplied collision contact is now gated the same as a locally computed one at the exact
|
|
1175
|
+
instant of closest approach. The provider branch treated a TCPA of zero (closest approach right now)
|
|
1176
|
+
as a live danger while the computed branch treated the same geometry as no longer closing; both now
|
|
1177
|
+
require a positive TCPA, so the two CPA sources agree and a just-passed contact cannot flicker as a
|
|
1178
|
+
danger from one source but not the other.
|
|
1179
|
+
|
|
1180
|
+
- Weather values now read consistently to one decimal. The legend low and high labels and the wave
|
|
1181
|
+
period readout previously mixed bare integers ("0", "9") with decimals ("0.0", "0.5"); wind, waves,
|
|
1182
|
+
precipitation, and cloud now all show one decimal place ("X.X"). Bearing, pressure, and temperature
|
|
1183
|
+
stay whole numbers, as those units are conventionally integers.
|
|
1184
|
+
|
|
1185
|
+
- Weather caching now fits the data. Forecasts are cached for an hour rather than 30 minutes (Open-Meteo
|
|
1186
|
+
model runs are hours apart, and the time slider already shows the right hour from the cached 5-day
|
|
1187
|
+
window), roughly halving request volume. When only the marine (waves) endpoint fails, commonly an
|
|
1188
|
+
Open-Meteo 429 on its separate host, the forecast grid (wind and pressure) is still shown but is not
|
|
1189
|
+
cached and the loader backs off, so panning no longer re-hits the rate-limited endpoint on every move.
|
|
1190
|
+
|
|
1191
|
+
- The weather mini-map no longer freezes blank when Rain radar is on. The RainViewer raster source
|
|
1192
|
+
starts with no frame URL (real frames arrive later), and MapLibre loads tiles for a layer in the
|
|
1193
|
+
style regardless of its visibility, so an empty tiles array crashed its tile-URL builder and then
|
|
1194
|
+
the raster render program, freezing the panel. The source now seeds a transparent placeholder tile
|
|
1195
|
+
so tile loading always succeeds, and the layer stays hidden until a real frame is applied so it is
|
|
1196
|
+
never drawn empty. Real frames replace the placeholder as before.
|
|
1197
|
+
|
|
1198
|
+
- Weather is gentler on the free data sources and correct over fresh water. The atmospheric forecast
|
|
1199
|
+
no longer forces Open-Meteo's sea cell selection, which picked wrong or missing cells over inland
|
|
1200
|
+
and freshwater areas such as the Great Lakes; sea selection now applies only to the marine wave
|
|
1201
|
+
request. A failed grid fetch backs off for a minute instead of retrying on every pan, the per-load
|
|
1202
|
+
grid is sampled to fewer points so a load fits a single request, and the viewport cache is capped.
|
|
1203
|
+
The "Here" conditions panel keys its lookups on a position rounded to about 110 meters, so GPS
|
|
1204
|
+
jitter no longer refetches (and no longer spams a Signal K weather provider with point requests) on
|
|
1205
|
+
every fix.
|
|
1206
|
+
|
|
1207
|
+
- The Forecast button is no longer clipped at the bottom of the status strip, and wind readouts show
|
|
1208
|
+
one decimal place (matching waves), even at zero.
|
|
1209
|
+
|
|
1210
|
+
- Signal K access approval now connects on its own. Previously, after you approved Binnacle in the
|
|
1211
|
+
Signal K UI and returned to the tab, it kept polling a stale request and only a second tab would
|
|
1212
|
+
connect. Binnacle now rechecks the pending request the moment the tab regains focus (background
|
|
1213
|
+
tabs throttle the poll timer), re-requests a fresh one if the old request expired, connects the
|
|
1214
|
+
stream reactively the instant access is granted (no reload), and adopts a token approved in another
|
|
1215
|
+
tab via the storage event. The old one-shot blocking connect that required a reload or a second tab
|
|
1216
|
+
is gone.
|
|
1217
|
+
|
|
1218
|
+
- A raster chart layer now follows the night-red theme (desaturated and dimmed) instead of staying
|
|
1219
|
+
full-saturation and full-brightness, matching the streaming depth layers.
|
|
1220
|
+
|
|
1221
|
+
- The track and PMTiles stores no longer lose records when IndexedDB degrades mid-session: every
|
|
1222
|
+
write is mirrored to the in-memory fallback, so data written before the failure survives.
|
|
1223
|
+
|
|
1224
|
+
- Switching the theme from night-red or dusk back to day no longer leaves the base map broken. The
|
|
1225
|
+
day restore brings the water and land fills back to their real colors and clears the dark label
|
|
1226
|
+
halo from street names. `fill-pattern` is a paint property in MapLibre, not a layout one, so the
|
|
1227
|
+
day-restore snapshot had silently dropped every fill layer, and the label halo was reset only when
|
|
1228
|
+
the source style already defined one.
|
|
1229
|
+
|
|
1230
|
+
- In the day theme, an unchecked layer checkbox no longer renders as a solid black square. The day
|
|
1231
|
+
theme declared a dark `color-scheme` despite being a light theme, so native controls rendered in
|
|
1232
|
+
dark mode; day now uses a light `color-scheme`, and checkboxes follow the theme accent (no blue at
|
|
1233
|
+
night).
|
|
1234
|
+
|
|
1235
|
+
- The base map now recolors fully for the theme. Previously only the background and water were
|
|
1236
|
+
themed, so over land at higher zoom the OpenFreeMap roads stayed white and the parks and landcover
|
|
1237
|
+
green even in night-red, breaking the pure-red-on-black contract. Every base layer is now recolored
|
|
1238
|
+
from its source layer (water, landcover, landuse, transportation, building, boundary, and text
|
|
1239
|
+
labels) per theme, fill patterns are cleared so the flat themed color shows, and label text gets a
|
|
1240
|
+
background-colored halo. Day and dusk gain a calmer palette consistent with the app; night-red is
|
|
1241
|
+
red-on-black across the whole map.
|
|
1242
|
+
|
|
1243
|
+
- A vector (PMTiles) chart could drop tiles to blank gaps at low-to-mid zoom (around z9) on some
|
|
1244
|
+
GPUs, even though the tiles fetched fine (HTTP 206) and decoded correctly. The cause was render
|
|
1245
|
+
load: the archive ships `landuse` un-simplified from low zoom, so a single z9 tile can carry
|
|
1246
|
+
roughly 1700 polygons that are invisible at that scale but heavy to draw over the full base map,
|
|
1247
|
+
which a weaker or high-DPI GPU silently fails to render. The chart now holds `landuse` until z12,
|
|
1248
|
+
where it is actually legible, cutting the low-zoom chart draw load sharply. Each chart layer's
|
|
1249
|
+
minzoom is preserved through the max-zoom cap.
|
|
1250
|
+
|
|
1251
|
+
- A vector (PMTiles) chart could show blank gaps over a real network. The archive is read
|
|
1252
|
+
uncached (`cache: 'no-store'`, to dodge a Chrome disk-cache write failure), so each chart tile
|
|
1253
|
+
depends on a live HTTP range read; a transient drop or a server hiccup under a burst of reads (a
|
|
1254
|
+
zoom that pulls in new tiles) blanked that tile until a later zoom re-requested it. The PMTiles
|
|
1255
|
+
source now retries a failed or 5xx range read (short backoff, up to two tries) while still
|
|
1256
|
+
honoring a caller abort, so a transient failure no longer leaves a hole.
|
|
1257
|
+
|
|
1258
|
+
- AIS targets disappeared from the chart after about six minutes of page uptime. The worker stamped
|
|
1259
|
+
each frame's epoch with 0 (`requestAnimationFrame` is absent in the worker, so the batcher's
|
|
1260
|
+
fallback passed 0) while the overlay pruned staleness against `performance.now()`, so pruning
|
|
1261
|
+
tracked uptime, not real staleness. The worker now stamps a wall clock (`Date.now`) and the
|
|
1262
|
+
overlay prunes with the same clock.
|
|
1263
|
+
- The AIS change counter bumped on every worker frame, not only on AIS changes, because the worker
|
|
1264
|
+
always emits an `ais` object (empty when only the own vessel moved). It now bumps only when a
|
|
1265
|
+
context actually updates, so the AIS overlay and the collision assessment no longer rebuild every
|
|
1266
|
+
frame.
|
|
1267
|
+
- The stored-token auth probe now omits credentials, so a live session cookie cannot mask a stale
|
|
1268
|
+
token and leave the WebSocket streaming nothing.
|
|
1269
|
+
|
|
1270
|
+
- Vector charts (MVT/PMTiles) rendered nothing on the map. A vector tile source paints nothing on
|
|
1271
|
+
its own: MapLibre needs a draw layer per source-layer, and the chart adapter both routed these
|
|
1272
|
+
charts to a raster source and, on the vector path, emitted no draw layers. The adapter now routes
|
|
1273
|
+
any chart marked `mvt`/`pbf`, typed `tileJSON`/`mapstyleJSON`, or ending in `.pmtiles` to a vector
|
|
1274
|
+
source and generates themed fill and line draw layers per source-layer. It covers the two dominant
|
|
1275
|
+
vector base-map schemas (Protomaps and OpenMapTiles) and, because Signal K's charts API often
|
|
1276
|
+
returns an empty layer list for an archive, falls back to the full known set when none are
|
|
1277
|
+
declared; MapLibre silently ignores a draw layer whose source-layer is absent. The chart layers
|
|
1278
|
+
recolor with the day, dusk, and night-red themes, and per-layer opacity now uses the correct paint
|
|
1279
|
+
property for fill, line, and raster layers.
|
|
1280
|
+
|
|
1281
|
+
- PMTiles vector charts failed to render with `ERR_CACHE_WRITE_FAILURE`: a large archive served
|
|
1282
|
+
with a weak ETag over range requests makes Chrome fail the HTTP disk-cache write, which rejects
|
|
1283
|
+
the whole fetch and blanks the chart. Binnacle now registers each PMTiles archive with a source
|
|
1284
|
+
that fetches ranges with `cache: 'no-store'`, bypassing the browser cache for these reads. Durable
|
|
1285
|
+
offline caching of these archives is a later spec.
|
|
1286
|
+
|
|
1287
|
+
- Collision assessment no longer raises a false alarm on an opening or already-passed AIS target:
|
|
1288
|
+
the provider closest-approach path now drops a contact whose time to closest approach is negative
|
|
1289
|
+
(the closest approach is in the past), matching the computed path's behavior.
|
|
1290
|
+
- Closest-point-of-approach math now normalizes the longitude difference, so a vessel pair
|
|
1291
|
+
straddling the antimeridian computes a real short range instead of a bogus near-360-degree offset.
|
|
1292
|
+
- The AIS change counter is now reactive state, so a future reactive consumer is notified rather
|
|
1293
|
+
than only the per-frame poll, removing a latent reactivity trap.
|
|
1294
|
+
|
|
1295
|
+
- Own-vessel readouts (SOG and COG) stayed blank while live data flowed. The store creates a
|
|
1296
|
+
path cell lazily on first access; the first access was the shell's reactive readout, so a
|
|
1297
|
+
brand-new `$state` source was created during the effect's tracking pass and never subscribed,
|
|
1298
|
+
and later updates did not re-render. `OwnVessel` now creates its cells at construction, before
|
|
1299
|
+
the readout runs, so the reactive read tracks an existing cell.
|
|
1300
|
+
- The auth probe could mistake a secured server for an unsecured one. It checked a REST path that
|
|
1301
|
+
a browser session cookie satisfies, so it concluded "unsecured" and connected the WebSocket
|
|
1302
|
+
stream without a token, which the cookie does not authenticate, leaving no live data. The probe
|
|
1303
|
+
now checks a stored token first and runs the anonymous check with credentials omitted, so a
|
|
1304
|
+
cookie cannot mask the need for a token.
|
|
1305
|
+
- The Signal K worker crashed at load with "Class extends value undefined" because the worker
|
|
1306
|
+
graph imported the server-side `@signalk/server-api` package, whose entry re-exports a
|
|
1307
|
+
`FullSignalK` class extending Node's `EventEmitter`; bundled into the browser worker with
|
|
1308
|
+
`events` externalized, that base class resolved to `undefined`. The worker now mirrors the few
|
|
1309
|
+
Signal K wire types it needs locally and no longer imports the package, dropping the worker
|
|
1310
|
+
bundle from about 164 KB to about 7 KB and removing the dependency entirely.
|
|
1311
|
+
- The chart area rendered all blue offshore because the base map was fetched from a CDN
|
|
1312
|
+
(`tiles.openfreemap.org`), which is unreachable on a boat with no internet, leaving an empty
|
|
1313
|
+
map that showed the page background through it. Binnacle now ships a bundled, offline base
|
|
1314
|
+
style that the theme recolors, with Signal K charts layered on top. Bundled vector base tiles
|
|
1315
|
+
are a later spec; this removes the CDN dependency in line with the offline-first rule.
|
|
1316
|
+
|
|
1317
|
+
### Security
|
|
1318
|
+
|
|
1319
|
+
- The points-of-interest popup's "View details" link now follows only `http:` and `https:` URLs.
|
|
1320
|
+
A note's link comes from a resource provider Binnacle does not control, so a `javascript:` or
|
|
1321
|
+
`data:` URL would otherwise execute when clicked; non-http schemes and unparseable URLs are now
|
|
1322
|
+
dropped.
|