signalk-pypilot-newui 2.11.0 → 2.12.0

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 CHANGED
@@ -1,5 +1,251 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.12.0 — 2026-10-08 — Virtual-tack audit cycle + Profile UX overhaul + safety aproado/empopado
4
+
5
+ Rolls up Rev410 through Rev430 (21 revs). Driven by two sea trials
6
+ (2026-10-03 and 2026-10-04) that exposed critical virtual-tack bugs
7
+ and a batch of UX problems around profiles and the mode selector.
8
+ Six audit MDs accompany the fixes (`docs/_audit_commit[2-6]_pre_rev*.md`
9
+ + `docs/_sea_trial_2026-10-04.md`) following the "audit before fix"
10
+ rule that paid for itself after three failed attempts at the drag/drop
11
+ bug.
12
+
13
+ ### Added
14
+
15
+ - **Virtual-tack audit cycle (Commits 2–6 of the Rev407 plan)**.
16
+ - *Commit 2 (Rev410)* — Mando trace enrichment (`ap.tack.state` and
17
+ `ap.tack.direction` deltas now log heading + AWA + mode + vtPhase),
18
+ VT `remainingDeg` on the mirror HUD (shows "N°" while turning,
19
+ "settling" during the handover phase), explicit "VT fallida: X"
20
+ toast when the backend publishes a terminal `failed` snapshot.
21
+ - *Commit 3 (Rev411)* — Writes propagate pypilot refusal through
22
+ `PUT /raw`, the SignalK v2 PUT handler and the `/ap/*` fallbacks
23
+ (503 instead of a false 200), and `dodge()` throws so KIP sees
24
+ the failure.
25
+ - *Commit 5 (Rev412)* — Empopado hot-gains that **actually apply**
26
+ (silent no-op since Rev320: the code read `catalog[path].value`
27
+ which never existed). Reads from `state.pypilotValues[path]`
28
+ now. New `data-hot={on|off|failed}` badge inside the active HUD
29
+ makes the PID state visible.
30
+ - *Commit 6 (Rev413)* — Yellow (warn) toast when settling times out
31
+ but the compass rotation finished: "Virada completada, viento sin
32
+ converger". Target is still applied; sailor sees it did not fully
33
+ confirm.
34
+ - *Commit 4 (Rev414)* — AWA settling EMA with τ ≈ 2 s applied to the
35
+ noisy raw apparent-wind sample. Fixes the "settling-timeout" seen
36
+ on light-wind sea trials. 4 unit tests cover seed / convergence /
37
+ ±π wrap-around / α = 0 identity.
38
+ - **Virtual-tack final-angle override** (Rev416, fix #12). Backend
39
+ accepts optional `finalAngleRad` on `/virtual-tack/start`; the visor
40
+ forwards whatever is set in `localStorage.pypilotnewui.tackFinalAngleDeg`.
41
+ Sailor can trim the destination AWA ±170° without having to nudge
42
+ after the tack. Backend validates (`|rad| ≤ π`) and falls back to
43
+ the AWA mirror on any invalid value.
44
+ - **EMA staleness guard** (Rev415). The settling loop now reads
45
+ `p.timestamp` from the SK path; samples older than
46
+ `STALE_SAMPLE_MS = 3000` are ignored, the dwell counter resets, and
47
+ the EMA state is cleared so a frozen value (e.g. after a WiFi
48
+ dropout) cannot sneak into the "ok" window.
49
+ - **Nudge target trace** (Rev415, fix #1 instrumentation).
50
+ `apSetTargetRad` logs the current target / heading / AWA / TWA /
51
+ mode / engaged BEFORE each PUT and the status / ok AFTER, so a
52
+ stale-UI vs. real-refusal diagnosis is trivial on the next sea trial.
53
+ - **Mode selector custom modal** (Rev425 → Rev430, fix #13 iterated).
54
+ Replaces the native `<select>` with a centered modal built lazily
55
+ under `<body>` (`.mode-select-modal-backdrop`). Contents:
56
+ - "MODO" label + list; the active mode highlighted in accent blue.
57
+ - "PERFIL" label + autowrapping button (1–2 lines so long names
58
+ like "Ceñida fondo medio viento" fit).
59
+ - Nested list opens **upward** (position: absolute; bottom: 100%)
60
+ so expanding profiles does not force the modal to scroll.
61
+ - Idempotent repaint — SK deltas never flash the open modal.
62
+ - **Doctor suggestions sorted + numbered** (Rev417, fix #8). Visor
63
+ pre-sorts by the same priority map the backend `applyAll` uses
64
+ (`bias < authority < oscillation < noise`), stamps a 1-based
65
+ ordinal prefix ("1.", "2.", "3.") on each card and gives the first
66
+ pending suggestion a `.top-priority` accent border so the sailor
67
+ can see which card the "Apply top priority" button will target.
68
+ - **Unlock prompt** (Rev421, fix #7). Flipping a group's unlock
69
+ checkbox opens a 3-choice modal ("Editar en perfil actual" /
70
+ "Cambiar a 'basic' primero" / "Cancelar") so the sailor explicitly
71
+ chooses the edit scope before touching sliders. Modal body answers
72
+ the second half of Carlos's question: "Los cambios se aplican en el
73
+ acto, no hace falta cerrar la pantalla."
74
+
75
+ ### Changed
76
+
77
+ - **Safety: aproado/empopado restoreAll default = false** (Rev415,
78
+ fix #11). Carlos: "hay que anular el restituir rumbo cuando salimos
79
+ de aproado o empopado porque es peligroso". Pre-Rev415 the finish
80
+ path unconditionally restored mode + target + disengage; now it
81
+ leaves the pilot where it is unless the caller opts in. The
82
+ "lost stern" auto-cancel in empopado passes `restoreAll:false` too.
83
+ - **Fork modal wording** (Rev418, fix #6). The "Keep in '{pilot}'"
84
+ button was filling `{pilot}` with `state.pilot` (always "basic"),
85
+ misleading when the active profile was called something else. Now
86
+ reads "Guardar en el perfil activo" + an info line showing the
87
+ actual profile + pilot pair. Cancel label clarified to
88
+ "Cancelar (deshacer último cambio)".
89
+ - **VT mirror watchdog raised 60 s → 120 s** (Rev410, fix U.2) so it
90
+ does not race the backend's own `PYPILOT_STUCK_MS = 60 s`.
91
+ - **Pypilot-silent watchdog gated on VT state** (Rev410, fix U.1).
92
+ During a VT the backend owns the authoritative stuck detection;
93
+ the legacy 40 s time-since-delta heuristic only fires on native
94
+ tack paths (no VT active) to avoid false "virada cancelada" toasts.
95
+ - **Fork modal strings fully translated** (Rev419, fix #6 follow-up).
96
+ Modal title, body and name label were English fallbacks because
97
+ `data-i18n` is not walked on dynamic `innerHTML`; now set
98
+ explicitly from `t()` after creation.
99
+
100
+ ### Fixed
101
+
102
+ - **Profile Manager drag/drop ping-pong flicker** (Rev420 after Rev418
103
+ and Rev419 attempts). The live-reorder approach was intrinsically
104
+ unstable — after each `insertBefore` the next `pointermove`
105
+ re-triggered a swap. Rewritten with the deferred-commit pattern:
106
+ only `translateY` the dragged item during the gesture, mark the
107
+ nearest drop-target with a visual line, commit one `insertBefore`
108
+ at the end. Also moved listeners to `window` + pointerId filter
109
+ (was on the handle, failed silently on tablet browsers) and gave
110
+ `.pm-item.dragging { touch-action: none }` so the gesture is not
111
+ interpreted as a scroll. Flash animation `.just-moved` on commit
112
+ confirms the move landed.
113
+ - **Tack stats HUD "dejaron de salir" + z-index + X not clickable**
114
+ (Rev416, fix #10). The HUD was only wired to the native pypilot
115
+ tack path (`_tackCountdownTick` arrived); VT (wind mode) never
116
+ triggered it. Terminal `virtualTack` snapshots now call
117
+ `_tackStatsOnTackCompleted` (completed) or
118
+ `_tackStatsOnTackCancelled` (cancelled/failed). Corner tiles had
119
+ `z-index:3` and the SVG wind-rose had no stacking context, so the
120
+ HUD painted behind corners; added a `stats-hud-up` class on
121
+ `.dash-rose` that raises `.rose-wrap` to `z-index:5` while the HUD
122
+ is open and drops corners to `z-index:1` with
123
+ `pointer-events:none`. Close × hit area widened to 34 × 34 so
124
+ off-centre taps still land.
125
+ - **Mode modal "se carga dos veces"** (Rev428). `refreshPypilotValues`
126
+ called `_rebuildModeSelect()` twice back-to-back (once when
127
+ `profiles` landed, once when `profile` landed) and each call
128
+ wiped-and-repopulated the modal's sub-select, producing a visible
129
+ double flash. Consolidated into a single `needsModeRebuild` flag
130
+ + made `_rebuildModePopupProfiles` idempotent (diffs the current
131
+ option list and skips the repaint when it matches).
132
+ - **Mode popup viewport cropping** (Rev426 before the Rev427 full
133
+ redesign). Pre-Rev427 the anchored popup overflowed the viewport
134
+ bottom on 80 %-zoom laptops and phone portrait; added
135
+ viewport-aware placement + internal scroll before deciding to
136
+ switch to a centered modal anyway.
137
+ - **Doctor "Apply top priority" button targets the right card**
138
+ (Rev417 side-effect of #8). The visor now mirrors the backend's
139
+ priority sort so the highlighted card is always the one the button
140
+ will apply.
141
+
142
+ ### Known limitations / pending real-water verification
143
+
144
+ - Rev410 U.2 / U.3 — 120 s watchdog + "VT fallida" toast on backend
145
+ `outcomeReason` need a real pypilot-dead moment to confirm.
146
+ - Rev411 J.1 / J.2 / J-1.5 — 503 propagation on `PUT /raw`, SK v2 PUT
147
+ handler and `dodge()` need pypilot genuinely offline to exercise.
148
+ - Rev412 — "aggressive PID" on empopado now ACTUALLY writes gains to
149
+ pypilot (dormant since Rev320). Validate in water that the new
150
+ P × 1.5 / D × 1.7 / servo-slew × 1.3 does not cause oscillation.
151
+ - Rev414 — AWA EMA τ = 2 s needs confirmation in light wind that the
152
+ dampened sample reaches the ±10° / 2 s dwell to produce
153
+ `completed:ok` instead of `settling-timeout`.
154
+ - Rev416 — final-angle override is exposed via `localStorage` only; a
155
+ slider UI will come in a later rev.
156
+ - Observation #2 from the 2026-10-04 sea trial (pilot behaves as if
157
+ tracking true wind in `wind` mode) is still open and awaits the
158
+ next sea trial's nav-session data with the Rev415 instrumentation
159
+ to confirm it as a real bug vs. a PID-tuning symptom.
160
+ - Observation #9 — AIS collision overlay on the rose — is planned for
161
+ the next **major (3.0.0)** bump; the design is drafted in
162
+ `docs/_audit_feature9_ais_overlay.md` but not implemented yet.
163
+
164
+ ---
165
+
166
+ ## 2.11.0 — 2026-09-30 — Sea-trial Sprint K fixes + severity override + portrait layout
167
+
168
+ Rolls up Rev324 through Rev376. Between 2.10.0 (Rev323) and 2.11.0 the
169
+ plugin went through a second sea trial (2026-09-28, 27 min on Tunatunes)
170
+ that surfaced 13 bugs. The ones not requiring open water are fixed in
171
+ this release; the remainder (aproado/empopado true-wind, mando-físico
172
+ HUD mirror) are implemented and awaiting sea-trial QA.
173
+
174
+ ### Added
175
+
176
+ - **Per-rule alarm severity override** (Rev342 backend + Rev374 frontend).
177
+ Each rule in Setup > Alarms now has an inline `🔴 Alarma / 🟡 Aviso /
178
+ 🔵 Info` dropdown. Changes are persisted via `props.alarmSeverityOverrides`
179
+ so they survive restarts. The shipped default is marked with `★` so the
180
+ sailor can see which option is the factory setting before changing it.
181
+ Endpoint: `POST /alarms/severity/:id {severity}`.
182
+ - **Mute countdown mm:ss** (Rev375). The muted-rule status pill now shows
183
+ real-time `MM:SS` (updated every second) instead of coarse `N minutes`.
184
+ - **Maneuver HUD mirror for external tacks** (Rev348 → Rev355). When a
185
+ tack is triggered from the pypilot native UI or physical remote, the
186
+ visor opens the overlay, animates the orange buttons, and emits the
187
+ post-tack stats panel — the same experience as a visor-initiated tack.
188
+ - **Tack cancellation stats** (Rev355). Cancelling mid-tack shows the HUD
189
+ immediately with `pre` + "CANCELADO" and no post window.
190
+ - **Pypilot-silence watchdog** (Rev367). If pypilot stops sending `ap.tack.state`
191
+ deltas for more than 60 s during a maneuver, the visor closes the mirror
192
+ overlay itself so the UI doesn't hang indefinitely on a lost connection.
193
+ - **Observability instrumentation** (Rev346). `maneuver-trace` now logs
194
+ stage events (`wallTs + monoTs`) for frontier debugging, plus `mtLog`
195
+ calls in `pluginRaw` and the `ap.tack.state/direction` handleDelta
196
+ branch tag the event source as `external` vs visor-local.
197
+ - **Heap diagnostics** sidecar (preserved from Rev885-equivalent work):
198
+ internal structure sizes under `/api/diagnostic.heapAudit` for
199
+ long-term RSS tracking.
200
+
201
+ ### Changed
202
+
203
+ - **Tack stats post window shortened** 60 s → 15 s (Rev371). Was 30 s
204
+ wait + 30 s measurement; now 5 s wait + 10 s measurement. 10 samples at
205
+ 1 Hz still yield a stable TWS/AWA/SOG average.
206
+ - **Rate-of-turn fallback more conservative** (Rev345). Minimum completion
207
+ threshold raised to `max(20°, total × 0.4)` so brief rotations don't
208
+ trigger a premature "tack completed" verdict.
209
+ - **Portrait tablet layout** (Rev370). `font-size` media queries at
210
+ 900 / 1200 / 1600 / 2000 px heights cascade through `em` units to
211
+ scale both the rose and the surrounding buttons on tall tablets.
212
+ Confirmed working on Opera Mobile (where `zoom` had no effect).
213
+ - **Button labels in wind mode** (Rev343). `±100°` becomes `VIRAR ← / →`
214
+ when the pilot is in `wind` or `truewind`, matching the actual
215
+ semantics.
216
+ - **Cancel labels during active maneuver** (Rev343). The orange buttons
217
+ show `CANCELAR VIRADA` or `CANCELAR TRASLUCHADA` instead of their
218
+ normal text so the sailor knows what tapping them will do.
219
+
220
+ ### Fixed
221
+
222
+ - **Diamond target stuck on external tacks** (Rev347). The
223
+ `_countdownPlannedTargetRad` variable now only masks `state.target`
224
+ when the countdown overlay is actually visible; stale values from
225
+ previously aborted visor tacks are cleared belt-and-suspenders.
226
+ - **Severity dropdown reverted to default on every poll** (Rev375,
227
+ `alarms.ts` `describe()`). Was returning `rule.severity` (the factory
228
+ default) instead of `s.severity` (the runtime value with override
229
+ applied). Fixed to return `s.severity`.
230
+ - **Visor countdown mask over authoritative state** (Rev347 lesson,
231
+ documented pattern). Any JS variable that fronts an authoritative
232
+ bus value must be gated by a visible UI signal, not by a cleanup
233
+ that may not fire on every abort path.
234
+
235
+ ### Known limitations
236
+
237
+ - Aproado / empopado true-wind flow (`apSetMode("wind")` with
238
+ `await state.mode = "wind"` before `apSetTargetRad`) is implemented
239
+ but awaits sea-trial QA.
240
+ - Physical-remote tack → HUD mirror hook (bug #7 from the 2026-09-28
241
+ trial) is implemented via `observeTackTransition` + `external` flag
242
+ but awaits sea-trial validation.
243
+ - The pypilot-core direction rewrite for short-arc wind tacks is
244
+ upstream (not our plugin) and remains open; a GitHub issue is drafted
245
+ but not yet posted to Sean D'Epagnier.
246
+
247
+ ---
248
+
3
249
  ## 2.10.0 — 2026-09-27 — Sea-trial wind-mode fixes + maneuver forensics
4
250
 
5
251
  Rolls up Rev309 through Rev323. Between 2.9.0 (Rev280) and 2.10.0 the
package/README.md CHANGED
@@ -316,6 +316,7 @@ that isolated TinyPilot.
316
316
  | `POST` | `/plugins/signalk-pypilot-newui/alarms/ack/:id` | Acknowledge one active alarm (silences the sound, banner stays visible). |
317
317
  | `POST` | `/plugins/signalk-pypilot-newui/alarms/mute/:id` | Mute the RULE for N minutes (query `?min=15`). |
318
318
  | `POST` | `/plugins/signalk-pypilot-newui/alarms/enable/:id` | Enable / disable a rule (query `?on=1\|0`). |
319
+ | `POST` | `/plugins/signalk-pypilot-newui/alarms/severity/:id` | Override the severity of one rule. Body: `{ "severity": "info\|warn\|alarm" }`. Persists via `props.alarmSeverityOverrides` so it survives restart. |
319
320
  | `GET` | `/plugins/signalk-pypilot-newui/prechecks` | Pre-departure autopilot check verdict: `ready` / `ready-with-caveats` / `do-not-engage` + per-item detail. |
320
321
  | `POST` | `/plugins/signalk-pypilot-newui/doctor/start` | Start a diagnostic session (query `?duration=180`). Requires AP engaged. |
321
322
  | `POST` | `/plugins/signalk-pypilot-newui/doctor/cancel` | Cancel the current session. |