speedandcurrent 1.6.0 → 1.7.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/public/index.html CHANGED
@@ -4,127 +4,288 @@
4
4
  <head>
5
5
  <meta charset="UTF-8">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>Speed and current</title>
7
+ <title>Speed and Current</title>
8
+ <script>
9
+ (async () => {
10
+ try {
11
+ const res = await fetch('/');
12
+ const html = await res.text();
13
+ const doc = new DOMParser().parseFromString(html, 'text/html');
14
+ const link = doc.querySelector('link[rel="stylesheet"]');
15
+ if (link) {
16
+ const el = document.createElement('link');
17
+ el.rel = 'stylesheet';
18
+ el.href = new URL(link.getAttribute('href'), res.url).href;
19
+ document.head.appendChild(el);
20
+ }
21
+ } catch (e) {
22
+ console.warn('[webapp] Could not load SignalK admin stylesheet:', e);
23
+ }
24
+ })();
25
+ </script>
8
26
  <link rel="stylesheet" href="main.css">
9
27
  </head>
10
28
 
11
- <body>
12
-
13
- <header class="menu-bar">
14
- <!-- Speed Unit Selection -->
15
- <div class="menu-item dropdown">
16
- <label for="speed-unit">Speed:</label>
17
- <select id="speed-unit" onchange="handleSpeedUnitChange(this.value)">
18
- <option value="ms">m/s</option>
19
- <option value="knots" selected>knots</option>
20
- <option value="kmh">km/h</option>
21
- </select>
22
- </div>
29
+ <body class="app header-fixed sidebar-fixed aside-menu-fixed aside-menu-hidden">
23
30
 
24
- <!-- Angle Unit Selection -->
25
- <div class="menu-item dropdown">
26
- <label for="angle-unit">Angle:</label>
27
- <select id="angle-unit" onchange="handleAngleUnitChange(this.value)">
28
- <option value="radians">radians</option>
29
- <option value="degrees" selected>degrees</option>
30
- </select>
31
+ <header class="app-header navbar navbar-light bg-white">
32
+ <div class="d-flex align-items-center">
33
+ <a class="d-flex align-items-center gap-2 me-0 text-decoration-none" href="#">
34
+ <img src="icon.png" alt="" style="height:48px;width:auto">
35
+ <span class="app-title">Speed &amp; Current</span>
36
+ </a>
37
+ <button class="navbar-toggler sidebar-toggler d-md-down-none ms-2" type="button" id="sidebarToggler">
38
+ <span class="navbar-toggler-icon"></span>
39
+ </button>
31
40
  </div>
41
+ <span id="message" class="small ms-auto me-3"></span>
42
+ </header>
32
43
 
33
- <!-- Metrics Selection -->
34
- <div class="menu-item" id="metrics-group">
35
- <span>Show:</span>
36
- <label><input type="checkbox" id="show-correction" onchange="handleMetricsChange()"> correction</label>
37
- <label><input type="checkbox" id="show-factor" onchange="handleMetricsChange()"> factor</label>
38
- <label><input type="checkbox" id="show-leeway" onchange="handleMetricsChange()"> leeway</label>
39
- <label><input type="checkbox" id="show-trace" onchange="handleMetricsChange()"> trace</label>
40
- <label><input type="checkbox" id="show-N" onchange="handleMetricsChange()"> N</label>
41
- </div>
44
+ <div class="app-body">
42
45
 
43
- <!-- Color Mode Selection -->
44
- <div class="menu-item dropdown">
45
- <label for="color-mode">Table coloring:</label>
46
- <select id="color-mode" onchange="handleColorModeChange(this.value)">
47
- <option value="none" selected>none</option>
48
- <option value="weight">weight</option>
49
- <option value="leeway">leeway</option>
50
- <option value="factor">factor</option>
51
- <option value="trace">trace</option>
52
- </select>
46
+ <div class="sidebar">
47
+ <nav class="sidebar-nav">
48
+ <ul id="main-nav" class="nav">
49
+ <li class="nav-item">
50
+ <a class="nav-link active" href="#" data-section="inputs">Inputs</a>
51
+ </li>
52
+ <li class="nav-item">
53
+ <a class="nav-link" href="#" data-section="estimation">Boatspeed Estimation</a>
54
+ </li>
55
+ <li class="nav-item">
56
+ <a class="nav-link" href="#" data-section="learning">Correction Table Learning</a>
57
+ </li>
58
+ <li class="nav-item">
59
+ <a class="nav-link" href="#" data-section="table">Correction Table</a>
60
+ </li>
61
+ </ul>
62
+ </nav>
63
+ <button class="sidebar-minimizer brand-minimizer" type="button" id="sidebarMinimizer"></button>
53
64
  </div>
54
65
 
66
+ <main class="main">
67
+ <div class="container-fluid pt-3">
55
68
 
69
+ <!-- ===== INPUTS SECTION ===== -->
70
+ <div id="section-inputs" class="app-section">
71
+ <div class="card">
72
+ <div class="card-header fw-bold text-uppercase">Inputs</div>
73
+ <div class="card-body">
74
+ <h6 class="text-uppercase fw-bold text-muted border-bottom pb-1 mb-1 small">Signal K Sources</h6>
75
+ <p class="text-muted small mb-2">Leave blank / select (any) to accept all sources.</p>
76
+ <table class="table table-sm table-borderless mb-0" id="inputs-sources-table">
77
+ <tbody></tbody>
78
+ </table>
79
+ <h6 class="text-uppercase fw-bold text-muted border-bottom pb-1 mt-3 mb-1 small">Live Values</h6>
80
+ <div id="inputs-values"></div>
81
+ </div>
82
+ </div>
83
+ </div>
56
84
 
85
+ <!-- ===== BOATSPEED ESTIMATION SECTION ===== -->
86
+ <div id="section-estimation" class="app-section" style="display:none">
87
+ <div class="card">
88
+ <div class="card-header d-flex align-items-center justify-content-between">
89
+ <span class="fw-bold text-uppercase">Boatspeed Estimation</span>
90
+ <label class="switch switch-text switch-primary mb-0" id="toggle-estimateBoatSpeed-label" title="Enable boatspeed estimation">
91
+ <input type="checkbox" class="switch-input form-check-input" id="toggle-estimateBoatSpeed">
92
+ <span class="switch-label" data-on="On" data-off="Off"></span>
93
+ <span class="switch-handle"></span>
94
+ </label>
95
+ </div>
96
+ <div class="card-body">
97
+ <h6 class="text-uppercase fw-bold text-muted border-bottom pb-1 mb-1 small">Settings</h6>
98
+ <table class="table table-sm table-borderless mb-0" id="estimation-settings-table">
99
+ <tbody></tbody>
100
+ </table>
101
+ <h6 class="text-uppercase fw-bold text-muted border-bottom pb-1 mt-3 mb-1 small">Inputs</h6>
102
+ <div id="estimation-inputs"></div>
103
+ <h6 class="text-uppercase fw-bold text-muted border-bottom pb-1 mt-3 mb-1 small">Intermediates</h6>
104
+ <div id="estimation-intermediates"></div>
105
+ <h6 class="text-uppercase fw-bold text-muted border-bottom pb-1 mt-3 mb-1 small">Outputs</h6>
106
+ <div id="estimation-outputs"></div>
107
+ </div>
108
+ </div>
109
+ </div>
57
110
 
58
- <!-- Pause Button -->
59
- <button id="toggle-updates" class="menu-item" onclick="toggleUpdates()">Pause</button>
111
+ <!-- ===== CORRECTION TABLE LEARNING SECTION ===== -->
112
+ <div id="section-learning" class="app-section" style="display:none">
113
+ <div class="card">
114
+ <div class="card-header d-flex align-items-center justify-content-between">
115
+ <span class="fw-bold text-uppercase">Correction Table Learning</span>
116
+ <label class="switch switch-text switch-primary mb-0" id="toggle-updateCorrectionTable-label" title="Enable correction table learning">
117
+ <input type="checkbox" class="switch-input form-check-input" id="toggle-updateCorrectionTable">
118
+ <span class="switch-label" data-on="On" data-off="Off"></span>
119
+ <span class="switch-handle"></span>
120
+ </label>
121
+ </div>
122
+ <div class="card-body">
123
+ <h6 class="text-uppercase fw-bold text-muted border-bottom pb-1 mb-1 small">Settings</h6>
124
+ <table class="table table-sm table-borderless mb-0" id="learning-settings-table">
125
+ <tbody></tbody>
126
+ </table>
127
+ <h6 class="text-uppercase fw-bold text-muted border-bottom pb-1 mt-3 mb-1 small">Smoother</h6>
128
+ <table class="table table-sm table-borderless mb-0" id="smoother-settings-table">
129
+ <tbody></tbody>
130
+ </table>
131
+ <h6 class="text-uppercase fw-bold text-muted border-bottom pb-1 mt-3 mb-1 small">Inputs</h6>
132
+ <div id="learning-inputs"></div>
133
+ <h6 class="text-uppercase fw-bold text-muted border-bottom pb-1 mt-3 mb-1 small">Intermediates</h6>
134
+ <div id="learning-intermediates"></div>
135
+ </div>
136
+ </div>
137
+ </div>
60
138
 
61
- <!-- messages -->
139
+ <!-- ===== CORRECTION TABLE SECTION ===== -->
140
+ <div id="section-table" class="app-section" style="display:none">
62
141
 
63
- <div id="message">
64
- </div>
65
-
66
- </header>
142
+ <div class="card">
143
+ <div class="card-header" style="position:relative">
144
+ <span class="fw-bold text-uppercase">Correction Table</span>
145
+ <span id="active-table-name" class="ms-2 text-muted small"></span>
146
+ <div style="position:absolute;right:1rem;top:0;bottom:0;display:flex;align-items:center;gap:0.5rem;">
147
+ <button id="btn-tbl-new" class="btn btn-sm btn-outline-secondary">New</button>
148
+ <button id="btn-tbl-load" class="btn btn-sm btn-outline-secondary">Load</button>
149
+ <button id="btn-tbl-copy" class="btn btn-sm btn-outline-secondary">Copy</button>
150
+ <button id="btn-tbl-resize" class="btn btn-sm btn-outline-secondary">Resize</button>
151
+ </div>
152
+ </div>
153
+ <div class="card-body">
154
+ <div id="table-container"></div>
155
+ </div>
156
+ </div>
157
+ </div>
67
158
 
68
- <!-- Content below the fixed menu -->
69
- <main class="content">
70
-
71
- <section class="graph">
72
- <svg height="300px" width="300px" xmlns="http://www.w3.org/2000/svg" viewbox="-100 -100 200 200" id="canvas">
73
-
74
- <!-- Draw the paths -->
75
-
76
-
77
- <path id="hull" d="M -45 10 Q 15 25 30 0 Q 15 -25 -45 -10 z" stroke="black" fill="none" stroke-width="1"
78
- transform="rotate(-90), scale(1, 1)" />
79
-
80
- <line id="mast" x1="0" y1="0" x2="1" y2="8" stroke="brown" stroke-width="2" transform="rotate(-90)" />
81
-
82
-
83
- Sorry, your browser does not support inline SVG.
84
- </svg>
85
- </section>
86
-
87
-
88
- <section class=" data ">
89
- <h2>Deltas</h2>
90
- <div id="delta-container">
91
- <!-- Speeds table will be populated here -->
92
159
  </div>
93
- </section>
160
+ </main>
161
+ </div>
94
162
 
95
- <section class="data ">
96
- <h2>Vectors</h2>
97
- <div id="speeds-container">
98
- <!-- Vectors table will be populated here -->
99
- </div>
100
- </section>
101
-
102
- <section class="data ">
103
- <h2>Attitude</h2>
104
- <div id="attitude-container">
105
- <!-- attitude table will be populated here -->
106
- </div>
107
- </section>
163
+ <script>
164
+ document.getElementById('sidebarMinimizer').addEventListener('click', () => {
165
+ document.body.classList.toggle('sidebar-minimized');
166
+ document.body.classList.toggle('brand-minimized');
167
+ });
168
+ document.getElementById('sidebarToggler').addEventListener('click', () => {
169
+ document.body.classList.toggle('sidebar-hidden');
170
+ });
171
+ document.querySelectorAll('#main-nav .nav-link').forEach(link => {
172
+ link.addEventListener('click', e => {
173
+ e.preventDefault();
174
+ document.querySelectorAll('#main-nav .nav-link').forEach(l => l.classList.remove('active'));
175
+ link.classList.add('active');
176
+ const target = link.dataset.section;
177
+ document.querySelectorAll('.app-section').forEach(s => s.style.display = 'none');
178
+ document.getElementById('section-' + target).style.display = '';
179
+ });
180
+ });
181
+ </script>
108
182
 
183
+ <!-- ===== TABLE MANAGER MODALS ===== -->
109
184
 
110
- <section class="data ">
111
- <h2>Correction table</h2>
112
- <div id="table-container">
113
- <!-- Steps table will be populated here -->
185
+ <!-- Create table -->
186
+ <div class="modal fade" id="modal-create" tabindex="-1" role="dialog">
187
+ <div class="modal-dialog" role="document">
188
+ <div class="modal-content">
189
+ <div class="modal-header">
190
+ <h5 class="modal-title">New Correction Table</h5>
191
+ <button type="button" class="close" data-dismiss="modal"><span>&times;</span></button>
192
+ </div>
193
+ <div class="modal-body">
194
+ <table class="table table-sm table-borderless">
195
+ <tbody>
196
+ <tr><td>Name</td><td><input type="text" id="create-name" class="form-control form-control-sm" placeholder="e.g. table-2026" style="width:180px"></td></tr>
197
+ <tr><td id="create-maxSpeed-label">Max speed (kn)</td><td><input type="number" id="create-maxSpeed" class="form-control form-control-sm" value="9" min="1" max="30" step="1" style="width:80px"></td></tr>
198
+ <tr><td id="create-speedStep-label">Speed step (kn)</td><td><input type="number" id="create-speedStep" class="form-control form-control-sm" value="1" min="0.1" max="5" step="0.1" style="width:80px"></td></tr>
199
+ <tr><td id="create-maxHeel-label">Max heel (&deg;)</td><td><input type="number" id="create-maxHeel" class="form-control form-control-sm" value="32" min="1" max="90" step="1" style="width:80px"></td></tr>
200
+ <tr><td id="create-heelStep-label">Heel step (&deg;)</td><td><input type="number" id="create-heelStep" class="form-control form-control-sm" value="8" min="1" max="30" step="1" style="width:80px"></td></tr>
201
+ </tbody>
202
+ </table>
203
+ </div>
204
+ <div class="modal-footer">
205
+ <span id="modal-create-status" class="small text-danger mr-auto"></span>
206
+ <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
207
+ <button type="button" class="btn btn-primary" id="btn-create-confirm">Create</button>
208
+ </div>
114
209
  </div>
115
-
116
- </section>
117
-
118
-
119
- </main>
120
-
121
-
210
+ </div>
211
+ </div>
122
212
 
213
+ <!-- Load table -->
214
+ <div class="modal fade" id="modal-load" tabindex="-1" role="dialog">
215
+ <div class="modal-dialog" role="document">
216
+ <div class="modal-content">
217
+ <div class="modal-header">
218
+ <h5 class="modal-title">Load Correction Table</h5>
219
+ <button type="button" class="close" data-dismiss="modal"><span>&times;</span></button>
220
+ </div>
221
+ <div class="modal-body">
222
+ <p class="text-muted small">Select a saved table to load and activate.</p>
223
+ <ul id="table-list" class="list-group"></ul>
224
+ </div>
225
+ <div class="modal-footer">
226
+ <span id="modal-load-status" class="small text-danger mr-auto"></span>
227
+ <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
228
+ <button type="button" class="btn btn-primary" id="btn-load-confirm" disabled>Load</button>
229
+ </div>
230
+ </div>
231
+ </div>
232
+ </div>
123
233
 
234
+ <!-- Copy table -->
235
+ <div class="modal fade" id="modal-copy" tabindex="-1" role="dialog">
236
+ <div class="modal-dialog" role="document">
237
+ <div class="modal-content">
238
+ <div class="modal-header">
239
+ <h5 class="modal-title">Copy Active Table</h5>
240
+ <button type="button" class="close" data-dismiss="modal"><span>&times;</span></button>
241
+ </div>
242
+ <div class="modal-body">
243
+ <p class="text-muted small">Save the active table under a new name and activate it.</p>
244
+ <table class="table table-sm table-borderless">
245
+ <tbody>
246
+ <tr><td>New name</td><td><input type="text" id="copy-name" class="form-control form-control-sm" placeholder="e.g. backup-2026" style="width:180px"></td></tr>
247
+ </tbody>
248
+ </table>
249
+ </div>
250
+ <div class="modal-footer">
251
+ <span id="modal-copy-status" class="small text-danger mr-auto"></span>
252
+ <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
253
+ <button type="button" class="btn btn-primary" id="btn-copy-confirm">Copy</button>
254
+ </div>
255
+ </div>
256
+ </div>
257
+ </div>
124
258
 
259
+ <!-- Resize table -->
260
+ <div class="modal fade" id="modal-resize" tabindex="-1" role="dialog">
261
+ <div class="modal-dialog" role="document">
262
+ <div class="modal-content">
263
+ <div class="modal-header">
264
+ <h5 class="modal-title">Resize Active Table</h5>
265
+ <button type="button" class="close" data-dismiss="modal"><span>&times;</span></button>
266
+ </div>
267
+ <div class="modal-body">
268
+ <p class="text-muted small">Resamples learned corrections onto a new grid. The active table file is overwritten.</p>
269
+ <table class="table table-sm table-borderless">
270
+ <tbody>
271
+ <tr><td id="resize-maxSpeed-label">Max speed (kn)</td><td><input type="number" id="resize-maxSpeed" class="form-control form-control-sm" min="1" max="30" step="1" style="width:80px"></td></tr>
272
+ <tr><td id="resize-speedStep-label">Speed step (kn)</td><td><input type="number" id="resize-speedStep" class="form-control form-control-sm" min="0.1" max="5" step="0.1" style="width:80px"></td></tr>
273
+ <tr><td id="resize-maxHeel-label">Max heel (&deg;)</td><td><input type="number" id="resize-maxHeel" class="form-control form-control-sm" min="1" max="90" step="1" style="width:80px"></td></tr>
274
+ <tr><td id="resize-heelStep-label">Heel step (&deg;)</td><td><input type="number" id="resize-heelStep" class="form-control form-control-sm" min="1" max="30" step="1" style="width:80px"></td></tr>
275
+ </tbody>
276
+ </table>
277
+ </div>
278
+ <div class="modal-footer">
279
+ <span id="modal-resize-status" class="small mr-auto"></span>
280
+ <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
281
+ <button type="button" class="btn btn-primary" id="btn-resize-confirm">Resize</button>
282
+ </div>
283
+ </div>
284
+ </div>
285
+ </div>
125
286
 
126
287
  <script type="module" src="app.js"></script>
127
- <script type="module" src="vectors.js"></script>
288
+
128
289
  </body>
129
290
 
130
291
  </html>
package/public/main.css CHANGED
@@ -1,84 +1,36 @@
1
- /* --- CSS Variables for Colors --- */
1
+ /* ─── CSS Variables for instance colours (used by SVG strokes and table rows) ─ */
2
2
  :root {
3
- /* Data type colors */
4
- --polar-color: #0d47a1; /* darker blue */
5
- --delta-color: #1b5e20; /* darker green */
6
- --attitude-color: #b28704; /* darker yellow/gold */
7
- --table2d-color: #4e342e; /* darker brown */
8
-
9
- /* Instance colors */
10
- --heading-color: #000000; /* black */
11
- --attitude-color-instance: #000000; /* black */
12
- --current-color: #0d47a1; /* darker blue */
13
- --currentDamped-color: #0d47a1; /* darker blue */
14
- --boatSpeed-color: #388e3c; /* darker green */
15
- --boatSpeedDamped-color: #388e3c; /* darker green */
16
- --correctedBoatSpeed-color: #27632a; /* even darker green */
17
- --boatSpeedRefGround-color: #00575b; /* darker teal */
18
- --groundSpeed-color: #5d4037; /* darker brown */
19
- --groundSpeedDamped-color: #5d4037; /* darker brown */
20
- --speedCorrection-color: #ad1457; /* darker pink/red */
21
- --residual-color: #6a1b9a; /* darker purple */
22
- --correctionTable-color: #4e342e; /* darker brown */
3
+ --heading-color: #000000;
4
+ --attitude-color-instance: #000000;
5
+ --current-color: #0d47a1;
6
+ --currentDamped-color: #0d47a1;
7
+ --boatSpeed-color: #388e3c;
8
+ --boatSpeedDamped-color: #388e3c;
9
+ --correctedBoatSpeed-color: #27632a;
10
+ --boatSpeedRefGround-color: #00575b;
11
+ --groundSpeed-color: #5d4037;
12
+ --groundSpeedDamped-color: #5d4037;
13
+ --speedCorrection-color: #ad1457;
14
+ --residual-color: #6a1b9a;
15
+ --correctionTable-color: #4e342e;
23
16
  }
24
17
 
25
- /* --- Data Type Classes --- */
26
-
27
- .data {
28
- color: black;
29
- }
18
+ /* ─── Instance ID colours ──────────────────────────────────────────────────── */
19
+ #heading { color: var(--heading-color); stroke: var(--heading-color); }
20
+ #attitude { color: var(--attitude-color-instance);stroke: var(--attitude-color-instance); }
21
+ #current,
22
+ #currentDamped { color: var(--current-color); stroke: var(--current-color); }
23
+ #boatSpeed,
24
+ #boatSpeedDamped { color: var(--boatSpeed-color); stroke: var(--boatSpeed-color); }
25
+ #correctedBoatSpeed { color: var(--correctedBoatSpeed-color); stroke: var(--correctedBoatSpeed-color); }
26
+ #boatSpeedRefGround { color: var(--boatSpeedRefGround-color); stroke: var(--boatSpeedRefGround-color); }
27
+ #groundSpeed,
28
+ #groundSpeedDamped { color: var(--groundSpeed-color); stroke: var(--groundSpeed-color); }
29
+ #speedCorrection { color: var(--speedCorrection-color); stroke: var(--speedCorrection-color); }
30
+ #residual { color: var(--residual-color); stroke: var(--residual-color); }
31
+ #correctionTable { color: var(--correctionTable-color); stroke: var(--correctionTable-color); }
30
32
 
31
-
32
- /* --- Instance IDs for Specific Colors --- */
33
- #heading {
34
- color: var(--heading-color);
35
- stroke: var(--heading-color);
36
- }
37
- #attitude {
38
- color: var(--attitude-color-instance);
39
- stroke: var(--attitude-color-instance);
40
- }
41
- #current, #currentDamped {
42
- color: var(--current-color);
43
- stroke: var(--current-color);
44
- }
45
- #boatSpeed, #boatSpeedDamped {
46
- color: var(--boatSpeed-color);
47
- stroke: var(--boatSpeed-color);
48
- }
49
- #correctedBoatSpeed {
50
- color: var(--correctedBoatSpeed-color);
51
- stroke: var(--correctedBoatSpeed-color);
52
- }
53
- #boatSpeedRefGround {
54
- color: var(--boatSpeedRefGround-color);
55
- stroke: var(--boatSpeedRefGround-color);
56
- }
57
- #groundSpeed, #groundSpeedDamped {
58
- color: var(--groundSpeed-color);
59
- stroke: var(--groundSpeed-color);
60
- }
61
- #speedCorrection {
62
- color: var(--speedCorrection-color);
63
- stroke: var(--speedCorrection-color);
64
- }
65
- #residual {
66
- color: var(--residual-color);
67
- stroke: var(--residual-color);
68
- }
69
- #correctionTable {
70
- color: var(--correctionTable-color);
71
- stroke: var(--correctionTable-color);
72
- }
73
-
74
- /* --- Table and Cell Styling --- */
75
- table {
76
- border-collapse: collapse;
77
- }
78
- th, td {
79
- border: 1px solid black;
80
- padding: 5px;
81
- }
33
+ /* ─── Correction table cell sizing ────────────────────────────────────────── */
82
34
  .TableCell {
83
35
  width: 60px;
84
36
  height: 60px;
@@ -86,92 +38,61 @@ th, td {
86
38
  vertical-align: middle;
87
39
  font-size: 11px;
88
40
  }
89
- .selectedCell {
90
- color: rgb(17, 204, 0);
91
- }
92
- .emptyCell {
93
- }
94
-
95
- /* --- SVG Styling --- */
96
- svg {
97
- border: 1px solid #ccc;
98
- height: auto;
99
- aspect-ratio: 1 / 1;
100
- svg {
101
- width: 100vmin;
102
- height: 100vmin;
103
- }
104
- }
105
- line {
106
- stroke-width: 2;
107
- }
108
- #hull, #mast {
109
- stroke: purple;
110
- stroke-width: 1;
111
- stroke-linecap: round;
112
- }
41
+ /* corner cell: show axis units */
42
+ .TableCorner { font-size: 10px; color: #888; font-weight: 400; text-align: center; }
43
+ /* cell states */
44
+ .cell--empty { background-color: #f0f0f0; }
45
+ .cell--active { outline: 3px solid #1565c0; outline-offset: -3px; }
46
+ .cell--neighbour { outline: 1px dashed #90a4ae; outline-offset: -2px; }
47
+ .cell-factor { font-weight: 600; font-size: 12px; }
48
+ .cell-leeway { font-weight: 600; font-size: 12px; margin-top: 2px; }
113
49
 
114
- /* --- Menu and Layout --- */
115
- body {
116
- font-family: Arial, sans-serif;
117
- display: flex;
118
- flex-direction: column;
119
- margin: 15px 15px 15px 15px;
120
- }
121
- .menu-bar {
122
- position: fixed;
123
- top: 0;
124
- left: 0;
50
+ /* ─── Stable table column widths inside cards ──────────────────────────────── */
51
+ .card-body .table {
52
+ table-layout: fixed;
125
53
  width: 100%;
126
- background: #6a6a6a;
127
- color: black;
128
- display: flex;
129
- align-items: center;
130
- padding: 10px 20px;
131
- gap: 20px;
132
- box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.2);
133
- z-index: 1000;
134
- }
135
- .menu-item {
136
- display: flex;
137
- align-items: center;
138
- gap: 5px;
139
- cursor: pointer;
140
54
  }
141
- .menu-item label {
142
- font-size: 14px;
143
- color: black;
55
+ .card-body .table td:first-child {
56
+ width: 55%;
57
+ white-space: nowrap;
58
+ overflow: hidden;
59
+ text-overflow: ellipsis;
144
60
  }
145
- .menu-item select {
146
- background: transparent;
147
- border: 1px solid #888;
148
- color: black;
149
- font-size: 14px;
150
- appearance: none;
151
- padding-right: 15px;
152
- cursor: pointer;
61
+ .card-body .table td:first-child small {
62
+ white-space: normal;
63
+ overflow: visible;
64
+ text-overflow: clip;
65
+ display: block;
153
66
  }
154
- .menu-item select::-ms-expand {
155
- display: none;
156
- }
157
- #toggle-updates {
158
- background: transparent;
159
- border: 1px solid #888;
160
- padding: 5px 15px;
161
- border-radius: 4px;
162
- cursor: pointer;
67
+ .card-body .table td:last-child { width: 45%; }
68
+
69
+ /* ─── SVG stroke rules ─────────────────────────────────────────────────────── */
70
+ line { stroke-width: 2; }
71
+ #hull, #mast { stroke: purple; stroke-width: 1; stroke-linecap: round; }
72
+
73
+ /* ─── Surface plot ─────────────────────────────────────────────────────────── */
74
+ .surfacePlot { width: 100%; height: 100%; border: 1px solid black; padding: 0; margin: 0; position: relative; }
75
+
76
+ /* ─── SignalK navbar-brand suppression ─────────────────────────────────────── */
77
+ .app-header .navbar-brand {
78
+ background-image: none !important;
79
+ width: auto !important;
163
80
  }
164
- #toggle-updates:hover {
165
- background: #555;
81
+ .app-header .navbar-brand::before,
82
+ .app-header .navbar-brand::after {
83
+ display: none !important;
84
+ content: none !important;
166
85
  }
167
- .content {
168
- margin-top: 60px;
86
+
87
+ /* ─── App title ────────────────────────────────────────────────────────────── */
88
+ .app-title {
89
+ font-size: 1.1rem;
90
+ font-weight: 500;
91
+ color: #29363D;
169
92
  }
170
- .surfacePlot {
171
- width: 100%;
172
- height: 100%;
173
- border: 1px solid black;
174
- padding: 0;
175
- margin: 0;
176
- position: relative;
177
- }
93
+
94
+ /* ─── Sidebar accent ───────────────────────────────────────────────────────── */
95
+ .sidebar .nav-link.active { border-left-color: #00bcd4 !important; }
96
+
97
+ /* ─── Stale data rows ──────────────────────────────────────────────────────── */
98
+ tr.stale td { color: #aaa; font-style: italic; }