@sailingrotevista/rotevista-dash 7.0.45 โ†’ 8.0.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.

Potentially problematic release.


This version of @sailingrotevista/rotevista-dash might be problematic. Click here for more details.

package/README.md CHANGED
@@ -1,90 +1,48 @@
1
- # @sailingrotevista/rotevista-dash
1
+ # Sailing Dashboard Pro (React + TypeScript)
2
2
 
3
- ## Sailing Dashboard Pro (v6.0)
4
-
5
- A tactical marine dashboard and advanced monitoring system for sailboats, designed to run as a Web App (PWA) fully integrated into the **Signal K** ecosystem. Specifically optimized for precision rendering on boat tablets (such as iPads and Samsung Galaxy Tab Active) and built to run efficiently on low-resource gateways like the **Victron Cerbo GX** (running Venus OS Large) or Raspberry Pi.
3
+ A tactical marine dashboard and advanced monitoring system for sailboats, built with React, TypeScript, and Vite. Designed to run seamlessly as a Progressive Web App (PWA) with Signal K integration, optimized for precision rendering on boat tablets (such as iPads and rugged Android tablets) and running on onboard systems (Victron Cerbo GX, Venus OS, or Raspberry Pi).
6
4
 
7
5
  ---
8
6
 
9
7
  ## Core Features
10
8
 
11
- ### ๐Ÿ“ˆ Server-Side RAM-Buffered History
12
- Unlike traditional dashboards that store history in the volatile memory of the client browser (wiping it whenever the screen locks or the browser reloads), this dashboard performs continuous background logging in RAM directly within the Signal K server process. Upon startup, the client instantly loads up to 60 minutes of pre-populated history via a fast JSON API endpoint, ensuring fully drawn, stable charts from the very first second the page is opened.
13
-
14
- ### ๐Ÿ”‹ Extreme Low-Power Architecture (Battery Saver)
15
- Engineered for long-range cruising without overheating your mobile devices or draining the boat's house batteries:
16
- * **Event-Driven SVG Redraw:** Complex SVG chart elements are not redrawn on a blind timer. Instead, they are redrawn *only* when a new historical data point is appended to the buffer (reducing the browser's active SVG layout and paint cycles by **93%**).
17
- * **Virtual-DOM Text Protection (`safeSetText`):** Digital text readouts are updated only if their numerical value has actually changed since the previous second, bypassing expensive browser style and layout calculations (*reflow/repaint*).
18
- * **Zero-Overhead Analog Needles:** Pointers (AWA, TWA, Leeway) snap instantly to their new positions at 1Hz instead of forcing continuous 60fps style interpolations, allowing the tablet's GPU to rest in a low-power idle state.
19
-
20
- ### ๐Ÿงญ Weather Compass & True Wind Direction (TWD) Trends
21
- * **Tactical Trend (Short-term):** Instantly detects lift or header shifts by comparing the 2-second moving average of True Wind Angle (TWA) with the 10-second average.
22
- * **Strategic Trend (Long-term):** Flashing green (CW) and red (CCW) dots on the TWD mini-compass indicate meteorological wind rotations, comparing the last 1 minute of wind with a stable strategic baseline of **15 minutes** under sail or **60 minutes** at anchor.
23
- * **Absolute UTC Epoch Snapping:** Time windows on both the server and any connected clients are locked to absolute UTC second boundaries (00, 10, 20, 30, 40, 50). This eliminates phase offsets and ensures identical tactical trends across all devices on the boat.
24
-
25
- ### ๐Ÿ›ก๏ธ Shallow Water Alarms & Geometric Safety Lines
26
- * Automatically draws high-visibility dashed safety lines across the depth chart background to indicate **Warning (Yellow, e.g. 3.5m)** and **Danger (Red, e.g. 2.5m)** thresholds.
27
- * **Non-Scaling Vector Effects:** By using `vector-effect="non-scaling-stroke"`, these lines and the active depth curve remain perfectly sharp, thin, and professional regardless of whether the widget is small or zoomed to full screen.
28
- * **Adaptive 0-Baseline:** In shallow water, the chart minimum is locked to 0 for keel safety; in deep water, the scale floats freely (e.g., 98m to 102m) to reveal the fine contour details of the seabed.
29
-
30
- ### ๐Ÿ”Œ Smart Source Locking (Zero-Config)
31
- An intelligent background algorithm assigns priority scores to active hardware sources on your boat (e.g. Yacht Devices N2K GPS vs a generic USB GPS) and locks onto the highest-quality sensor, switching to a backup only if a signal dropout occurs.
32
-
33
- ### โฐ Sleep & Power-Save Watchdog
34
- Detects when the tablet is unlocked or the browser wakes up a tab from *Power Save* mode, automatically closing orphaned WebSockets, reconnecting, and downloading the latest server-logged history in milliseconds to resume fluid, gap-free operation.
9
+ ### ๐Ÿ“ˆ Real-Time & Historical Sparklines
10
+ - **Adaptive Scales:** Automatic standard or Hercules zoom mode with snap-to-grid resolution.
11
+ - **Dynamic Reef & Danger Thresholds:** Visual warnings (Orange 1st Reef, Red 2nd Reef, Purple storm) and customizable depth safety lines with acoustic alerts.
12
+ - **Elastic History Gap Handling:** Smooth data rendering without synthetic gaps.
35
13
 
36
- ### ๐ŸŽฎ Smart Local Development Auto-Detection
37
- The dashboard automatically detects if it is running locally on your Mac/PC via the `file://` protocol or on `localhost`. In this mode, it still connects to your boat's Cerbo GX over Wi-Fi to stream real-time data, but **bypasses the server's configuration, preserving your local `CONFIG` edits** so you can test scales, alarms, and steps instantly.
14
+ ### ๐Ÿงญ Dual Central Instruments: Analog Gauge & Weather Radar
15
+ - **Analog Wind Gauge:** Real-time Apparent Wind Angle (AWA), True Wind Angle (TWA), Apparent Wind Speed (AWS), Leeway angle, and Ground Track vector.
16
+ - **Tactical & Gybe Alarms:** Visual and acoustic alarms for deep downwind gybe danger with hysteresis anti-chatter state machine.
17
+ - **Historical Weather Radar:** 8-ring historical true wind direction (TWD) and true wind speed (TWS) radar with Open-Meteo future forecast interpolation and live boat heading alignment.
18
+ - **Quick Switch:** 1-second hold on the TWD mini-compass switches to the historical radar; click anywhere on the radar to return to the analog gauge.
38
19
 
39
- ---
40
-
41
- ## System Requirements
20
+ ### ๐ŸŒ™ Night Mode & Tactical Focus Mode
21
+ - **Tactical Red Night Mode:** 1-second press on the central hotspot toggles full tactical red night mode to preserve night vision.
22
+ - **Fullscreen Mode:** Tap the central hotspot to toggle browser fullscreen.
23
+ - **Tactical Focus Mode:** 1-second press on any data box expands that instrument into high-visibility split-screen mode.
42
24
 
43
- * **Server:** Signal K Node Server (v1.20.0 or higher) installed on a Victron Cerbo GX, Raspberry Pi, or onboard PC.
44
- * **Client:** Any modern PWA-compliant browser supporting ES6 (Safari on iPadOS/iOS, Samsung Internet, Chrome on Android, Chrome/Safari on macOS/Windows).
25
+ ### ๐Ÿ“ก Signal K & Simulation Engine
26
+ - Automatically connects to Signal K WebSocket (`ws://${host}/signalk/v1/stream`) when running aboard.
27
+ - Automatically falls back to an integrated dynamic simulation engine when running standalone or in browser previews, keeping all instruments, graphs, and tactical trends active.
45
28
 
46
29
  ---
47
30
 
48
- ## Signal K Data Requirements
49
-
50
- ### Required Foundational Paths
51
- To derive, calculate, and display the entire suite of tactical data, the dashboard must receive the following raw paths from your boat's NMEA network:
31
+ ## Development
52
32
 
53
- * `navigation.speedThroughWater` (STW - Speed through water)
54
- * `navigation.speedOverGround` (SOG - Speed over ground)
55
- * `navigation.courseOverGroundTrue` (COG - Course over ground)
56
- * `navigation.headingTrue` (True Heading - *If missing, the system automatically falls back to Magnetic Heading*)
57
- * `environment.wind.speedApparent` (AWS - Apparent wind speed)
58
- * `environment.wind.angleApparent` (AWA - Apparent wind angle)
59
- * `environment.depth.belowTransducer` (Depth below keel/transducer)
60
- * `navigation.position` (GPS Latitude and Longitude - used for hemisphere detection and CSV logging)
33
+ ```bash
34
+ # Install dependencies
35
+ npm install
61
36
 
62
- ### Optional / Native Fallback Paths (Surgically Supported)
63
- The dashboard operates on a **"Native First, Fallback Second"** architecture. If any of the following calculated paths are transmitted natively by your boat's instruments, the dashboard will utilize them directly to preserve their calibration. If they are missing, the system silently activates its own local vector formulas:
37
+ # Start Vite development server (Port 3000)
38
+ npm run dev
64
39
 
65
- * `environment.wind.speedTrue` (**Optional** - Native True Wind Speed in m/s; falls back to local calculation if missing)
66
- * `environment.wind.directionTrue` (**Optional** - Native True Wind Direction in radians; falls back to local calculation if missing)
67
- * `navigation.headingMagnetic` (**Optional** - Magnetic Heading; automatically used if True Heading is missing on the network)
68
- * `navigation.magneticVariation` (**Optional** - Magnetic Variation; used to calibrate the Magnetic Heading fallback to True)
40
+ # Production build
41
+ npm run build
42
+ ```
69
43
 
70
44
  ---
71
45
 
72
- ## Installation
73
-
74
- The dashboard is distributed as a standard Signal K Node Server plugin and Web App.
75
-
76
- ### Installation via Signal K App Store (Recommended)
77
- 1. Access your Signal K admin panel (`http://<boat-ip>:3000`).
78
- 2. Navigate to **App Store** -> **Available**.
79
- 3. Search for `@sailingrotevista/rotevista-dash` and click **Install**.
80
- 4. Go to **Plugins Config**, activate the **Rotevista Dash Configuration** plugin, and configure your boat's safety thresholds (Draft, alarm offsets, and strategic chart timelines).
81
- 5. Click **Save**. The Signal K server will restart, apply the configurations, and initiate background history logging in RAM.
82
-
83
- ### Accessing the Dashboard
84
- Once installed, the dashboard is accessible on your local network by typing into your tablet or computer browser:
85
- ```text
86
- http://<boat-ip>:3000/@sailingrotevista/rotevista-dash/
87
- For the best experience in the cockpit, tap your browser's share button and select "Add to Home Screen" to run it as a standalone, distraction-free application.
46
+ ## License
88
47
 
89
- License
90
48
  This project is licensed under the MIT License - see the LICENSE file for details.
package/bun.lock ADDED
@@ -0,0 +1,157 @@
1
+ {
2
+ "lockfileVersion": 2,
3
+ "configVersion": 1,
4
+ "workspaces": {
5
+ "": {
6
+ "name": "@sailingrotevista/rotevista-dash",
7
+ "dependencies": {
8
+ "react": "^19.3.1",
9
+ "react-dom": "^19.3.1",
10
+ },
11
+ "devDependencies": {
12
+ "@types/react": "^19.3.0",
13
+ "@types/react-dom": "^19.3.0",
14
+ "@vitejs/plugin-react": "^6.1.2",
15
+ "typescript": "^7.0.2",
16
+ "vite": "^8.3.4",
17
+ },
18
+ },
19
+ },
20
+ "packages": {
21
+ "@oxc-project/types": ["@oxc-project/types@0.154.0", "", {}, "sha512-KYOUGQFlLWR+s4QMV2Q9ko38TZm7EdO87Qm7rvZLqk/webse/cDjtyxKhg/47l0byCbs/k46LT8qc4PKjAikzA=="],
22
+
23
+ "@rolldown/binding-android-arm-eabi": ["@rolldown/binding-android-arm-eabi@1.2.14", "", { "os": "android", "cpu": "arm" }, "sha512-aWR+XKH9+rW7m5fWq/5360J3mHycrx/o6xZM2DQFdJnTMdyP7/WzxPjhtLbVWdKo2y612/4U1bWqb3HQhiF7vg=="],
24
+
25
+ "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.2.14", "", { "os": "android", "cpu": "arm64" }, "sha512-QiVxHofl6A8lQU7WAtOUY4QMVZXohxwz7WJJoeh8NBEUyKfIkMRKhYyq8az/1RypNiIXzNTtiS6KwHz0erxYRQ=="],
26
+
27
+ "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.2.14", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lTQOzHuBIooh3jIr5XINQkjPGEuGy+Dsy0meXDr6Sjty0b99kYgGh4uBC/8mJX1T0kC8d6S0aPP1Y+bHkh6aBw=="],
28
+
29
+ "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.2.14", "", { "os": "darwin", "cpu": "x64" }, "sha512-hyMKEGatjmBYCs/Q4sbIcL/GqpFEqsX3wc+HYVQ/85A4rxc1o7rPm4DUI4GV4k/h0ITddwFSGpqQunI14GXjTA=="],
30
+
31
+ "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.2.14", "", { "os": "freebsd", "cpu": "x64" }, "sha512-u371GbXoVXrEgP20PwzoGNOdAOqAMhzo19JzzY+V4YmmnC+DTOoqdbMu+O+54emM59Mik/UdrtWgcuEDKWRPKA=="],
32
+
33
+ "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.2.14", "", { "os": "linux", "cpu": "arm" }, "sha512-BqwKsdZQf6EqVWr8fv2MrDYzAvOZ7+NYFSJESRGs87fkseEhs0TU9TXgRJTkZoRRkxTizyEg1LKUXfU3/78uqg=="],
34
+
35
+ "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.2.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-SD9kY8M009x/7s57Z1dgUVlP5+uYBgKQK0Tkr1WIWngrtUJmRCW1RJFMY7QDla2khXhmg2i5Io7MUjcmZNZ/KQ=="],
36
+
37
+ "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.2.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-BJ2CL19M6uuWesMgnqJzgxoI+N+eV7b0A9O9vLV42y+gitC41fNa8KElau1tHVzuLWX5GzEuxMPhAO0tQXQMkg=="],
38
+
39
+ "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.2.14", "", { "os": "linux", "cpu": "ppc64" }, "sha512-4+Kl8daPV8NDHaD7mfCcc1pRM+Qg7ab8DyMm5FG5WoZgv4h2i3KFN1CybYBNbQqmuYm7ftJLvmMI1eHYJ22S/Q=="],
40
+
41
+ "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.2.14", "", { "os": "linux", "cpu": "s390x" }, "sha512-V/h8ePq1fKDrmtFMz6xcCN3DKvuvMRrwwD1q0PkBeeo4DpK64biaKvqWGVxqCeYKqeRk7gE7/iKPxLCn08O97A=="],
42
+
43
+ "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.2.14", "", { "os": "linux", "cpu": "x64" }, "sha512-WxalXzgrLVpKC93xfpaJ3gCXUWzeCr+FRCbDVD2mUAy4L6Vo3lNVz0epUEUpxPWJkLNYUwM4OSIikv36OJ9mLg=="],
44
+
45
+ "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.2.14", "", { "os": "linux", "cpu": "x64" }, "sha512-3G3tnAmTpdk9jO3GQtyHXqeFD2SlecfTws/Y3M2mmj9nMBwwuLT4kur8sZKeu7ENUHSpVO3cIsm5A52LEWWwlg=="],
46
+
47
+ "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.2.14", "", { "os": "none", "cpu": "arm64" }, "sha512-qyr5XiNGxm2ytx17vvtcJ9nXn6v1gENETi503bJx3OGwZ1tSonj7BosxbMa/xeAfSl5iS9X6KjEWYXLu6v1L7Q=="],
48
+
49
+ "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.2.14", "", { "os": "win32", "cpu": "arm64" }, "sha512-oA4o5o0S2yB199YyYXDzKUv1Z56Rc+mgOjwm7Z/vJ9Z8igiDPYvoOuDyo6g7AK/ZaF09nPhDKVnGBcYHO11GXA=="],
50
+
51
+ "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.2.14", "", { "os": "win32", "cpu": "x64" }, "sha512-9mE94bf2GZUaR/fUSXCMpBoWkctu421Mi0ORpm4zqfbkjKep7RxzPFa5etcGs5h+2KApoWBOA/hAJa/7saGgEQ=="],
52
+
53
+ "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="],
54
+
55
+ "@types/react": ["@types/react@19.3.0", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg=="],
56
+
57
+ "@types/react-dom": ["@types/react-dom@19.3.0", "", { "peerDependencies": { "@types/react": "^19.3.0" } }, "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q=="],
58
+
59
+ "@typescript/typescript-aix-ppc64": ["@typescript/typescript-aix-ppc64@7.0.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ=="],
60
+
61
+ "@typescript/typescript-darwin-arm64": ["@typescript/typescript-darwin-arm64@7.0.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA=="],
62
+
63
+ "@typescript/typescript-darwin-x64": ["@typescript/typescript-darwin-x64@7.0.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA=="],
64
+
65
+ "@typescript/typescript-freebsd-arm64": ["@typescript/typescript-freebsd-arm64@7.0.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ=="],
66
+
67
+ "@typescript/typescript-freebsd-x64": ["@typescript/typescript-freebsd-x64@7.0.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw=="],
68
+
69
+ "@typescript/typescript-linux-arm": ["@typescript/typescript-linux-arm@7.0.2", "", { "os": "linux", "cpu": "arm" }, "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ=="],
70
+
71
+ "@typescript/typescript-linux-arm64": ["@typescript/typescript-linux-arm64@7.0.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ=="],
72
+
73
+ "@typescript/typescript-linux-loong64": ["@typescript/typescript-linux-loong64@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ=="],
74
+
75
+ "@typescript/typescript-linux-mips64el": ["@typescript/typescript-linux-mips64el@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA=="],
76
+
77
+ "@typescript/typescript-linux-ppc64": ["@typescript/typescript-linux-ppc64@7.0.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA=="],
78
+
79
+ "@typescript/typescript-linux-riscv64": ["@typescript/typescript-linux-riscv64@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ=="],
80
+
81
+ "@typescript/typescript-linux-s390x": ["@typescript/typescript-linux-s390x@7.0.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw=="],
82
+
83
+ "@typescript/typescript-linux-x64": ["@typescript/typescript-linux-x64@7.0.2", "", { "os": "linux", "cpu": "x64" }, "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A=="],
84
+
85
+ "@typescript/typescript-netbsd-arm64": ["@typescript/typescript-netbsd-arm64@7.0.2", "", { "os": "none", "cpu": "arm64" }, "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA=="],
86
+
87
+ "@typescript/typescript-netbsd-x64": ["@typescript/typescript-netbsd-x64@7.0.2", "", { "os": "none", "cpu": "x64" }, "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA=="],
88
+
89
+ "@typescript/typescript-openbsd-arm64": ["@typescript/typescript-openbsd-arm64@7.0.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ=="],
90
+
91
+ "@typescript/typescript-openbsd-x64": ["@typescript/typescript-openbsd-x64@7.0.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg=="],
92
+
93
+ "@typescript/typescript-sunos-x64": ["@typescript/typescript-sunos-x64@7.0.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g=="],
94
+
95
+ "@typescript/typescript-win32-arm64": ["@typescript/typescript-win32-arm64@7.0.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ=="],
96
+
97
+ "@typescript/typescript-win32-x64": ["@typescript/typescript-win32-x64@7.0.2", "", { "os": "win32", "cpu": "x64" }, "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g=="],
98
+
99
+ "@vitejs/plugin-react": ["@vitejs/plugin-react@6.1.2", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.1" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "oxc-transform-react": "^0.152.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler", "oxc-transform-react"] }, "sha512-fhJNGUdscQa4HkI0xFxwonWyhnVReCPDbXI7FH7jmzpnVCrFQI7PRrhWecO263610vOrs9tr2Y4xcf8uAdFxbw=="],
100
+
101
+ "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
102
+
103
+ "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
104
+
105
+ "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
106
+
107
+ "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
108
+
109
+ "lightningcss": ["lightningcss@1.33.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.33.0", "lightningcss-darwin-arm64": "1.33.0", "lightningcss-darwin-x64": "1.33.0", "lightningcss-freebsd-x64": "1.33.0", "lightningcss-linux-arm-gnueabihf": "1.33.0", "lightningcss-linux-arm64-gnu": "1.33.0", "lightningcss-linux-arm64-musl": "1.33.0", "lightningcss-linux-x64-gnu": "1.33.0", "lightningcss-linux-x64-musl": "1.33.0", "lightningcss-win32-arm64-msvc": "1.33.0", "lightningcss-win32-x64-msvc": "1.33.0" } }, "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA=="],
110
+
111
+ "lightningcss-android-arm64": ["lightningcss-android-arm64@1.33.0", "", { "os": "android", "cpu": "arm64" }, "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg=="],
112
+
113
+ "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.33.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg=="],
114
+
115
+ "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.33.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ=="],
116
+
117
+ "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.33.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg=="],
118
+
119
+ "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.33.0", "", { "os": "linux", "cpu": "arm" }, "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ=="],
120
+
121
+ "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.33.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg=="],
122
+
123
+ "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.33.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ=="],
124
+
125
+ "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.33.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg=="],
126
+
127
+ "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.33.0", "", { "os": "linux", "cpu": "x64" }, "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw=="],
128
+
129
+ "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.33.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA=="],
130
+
131
+ "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.33.0", "", { "os": "win32", "cpu": "x64" }, "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA=="],
132
+
133
+ "nanoid": ["nanoid@3.3.20", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ=="],
134
+
135
+ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
136
+
137
+ "picomatch": ["picomatch@4.0.7", "", {}, "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA=="],
138
+
139
+ "postcss": ["postcss@8.5.30", "", { "dependencies": { "nanoid": "^3.3.20", "picocolors": "^1.1.1", "source-map-js": "^1.2.2" } }, "sha512-zkd+e9S1AQHhmaBKye9zlFHWjOwVgqXl1q3ECYy8561K7qxR2cREf/STLskulBFd78k6AOFeA+ff1TtEzk9bAA=="],
140
+
141
+ "react": ["react@19.3.1", "", {}, "sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw=="],
142
+
143
+ "react-dom": ["react-dom@19.3.1", "", { "dependencies": { "scheduler": "^0.28.0" }, "peerDependencies": { "react": "^19.3.1" } }, "sha512-Y0bARz53lhEC1NMCASs0/j+faB/zqt3R8hpvg1e8+61ZVJ4UL0UdC4cBbXfZMcIuhrfgeILTLe+ZeBCavQPtKQ=="],
144
+
145
+ "rolldown": ["rolldown@1.2.14", "", { "dependencies": { "@oxc-project/types": "=0.154.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm-eabi": "1.2.14", "@rolldown/binding-android-arm64": "1.2.14", "@rolldown/binding-darwin-arm64": "1.2.14", "@rolldown/binding-darwin-x64": "1.2.14", "@rolldown/binding-freebsd-x64": "1.2.14", "@rolldown/binding-linux-arm-gnueabihf": "1.2.14", "@rolldown/binding-linux-arm64-gnu": "1.2.14", "@rolldown/binding-linux-arm64-musl": "1.2.14", "@rolldown/binding-linux-ppc64-gnu": "1.2.14", "@rolldown/binding-linux-s390x-gnu": "1.2.14", "@rolldown/binding-linux-x64-gnu": "1.2.14", "@rolldown/binding-linux-x64-musl": "1.2.14", "@rolldown/binding-openharmony-arm64": "1.2.14", "@rolldown/binding-win32-arm64-msvc": "1.2.14", "@rolldown/binding-win32-x64-msvc": "1.2.14" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-eTBTTqMR6uHKLgcUqpJP8ntzDI6V4LYA8negAMBEI7gkgVvOdrx6VsvS2Ym+83KhOPdLCHWJu0w7UQFJZhHKNw=="],
146
+
147
+ "scheduler": ["scheduler@0.28.0", "", {}, "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw=="],
148
+
149
+ "source-map-js": ["source-map-js@1.2.2", "", {}, "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw=="],
150
+
151
+ "tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="],
152
+
153
+ "typescript": ["typescript@7.0.2", "", { "optionalDependencies": { "@typescript/typescript-aix-ppc64": "7.0.2", "@typescript/typescript-darwin-arm64": "7.0.2", "@typescript/typescript-darwin-x64": "7.0.2", "@typescript/typescript-freebsd-arm64": "7.0.2", "@typescript/typescript-freebsd-x64": "7.0.2", "@typescript/typescript-linux-arm": "7.0.2", "@typescript/typescript-linux-arm64": "7.0.2", "@typescript/typescript-linux-loong64": "7.0.2", "@typescript/typescript-linux-mips64el": "7.0.2", "@typescript/typescript-linux-ppc64": "7.0.2", "@typescript/typescript-linux-riscv64": "7.0.2", "@typescript/typescript-linux-s390x": "7.0.2", "@typescript/typescript-linux-x64": "7.0.2", "@typescript/typescript-netbsd-arm64": "7.0.2", "@typescript/typescript-netbsd-x64": "7.0.2", "@typescript/typescript-openbsd-arm64": "7.0.2", "@typescript/typescript-openbsd-x64": "7.0.2", "@typescript/typescript-sunos-x64": "7.0.2", "@typescript/typescript-win32-arm64": "7.0.2", "@typescript/typescript-win32-x64": "7.0.2" }, "bin": { "tsc": "bin/tsc" } }, "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA=="],
154
+
155
+ "vite": ["vite@8.3.4", "", { "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.7", "postcss": "^8.5.29", "rolldown": "~1.2.12", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.7.1", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-IftLE8phJhdNRIGNEV4JCCvfNScoZQVfcXaN8TQropWGJtOHcu+iAB9pHcuGvmEb/kddw0LBe/jS8ztdC6xdiQ=="],
156
+ }
157
+ }
package/index.html CHANGED
@@ -1,264 +1,22 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="it">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
- <meta name="apple-mobile-web-app-capable" content="yes">
7
- <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
8
- <link rel="manifest" href="manifest.json">
9
-
10
- <!-- Icona standard per la scheda del browser (Favicon) -->
11
- <link rel="icon" type="image/png" href="icons/dash.png">
12
-
13
- <!-- Icona per Bookmark e schermata Home su Apple iPad, iPhone e Mac -->
14
- <link rel="apple-touch-icon" href="icons/dash.png">
15
- <title>Sailing Dashboard Pro</title>
16
- <link rel="stylesheet" href="style.css">
17
- </head>
18
- <body>
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
6
+ <meta name="apple-mobile-web-app-capable" content="yes" />
7
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
8
+ <meta name="description" content="Tactical marine wind dashboard with navigation and course aids, analog wind gauge, historical weather radar, and depth alerts." />
9
+ <meta property="og:title" content="Sailing Dashboard Pro" />
10
+ <meta property="og:description" content="Tactical marine wind dashboard with navigation and course aids, analog wind gauge, historical weather radar, and depth alerts." />
11
+ <link rel="manifest" href="./manifest.json" />
19
12
 
20
- <div class="main-container">
21
-
22
- <!-- COLONNA SINISTRA -->
23
- <div class="data-box box-stw">
24
- <div class="label-row"><span class="label">STW</span><span class="unit">kts</span></div>
25
- <span class="value" id="stw">0.0</span>
26
- <div class="graph-wrapper">
27
- <svg class="sparkline" id="stw-graph" viewBox="0 0 200 40" preserveAspectRatio="none"></svg>
28
- <div class="scale-labels" id="stw-scale"></div>
29
- </div>
30
- </div>
31
-
32
- <div class="data-box box-sog">
33
- <div class="label-row"><span class="label" id="sog-vmg-label">SOG</span><span class="unit">kts</span></div>
34
- <span class="value" id="sog">0.0</span>
35
- <div class="graph-wrapper">
36
- <svg class="sparkline" id="sog-graph" viewBox="0 0 200 40" preserveAspectRatio="none"></svg>
37
- <div class="scale-labels" id="sog-scale"></div>
38
- </div>
39
- </div>
40
-
41
- <div class="data-box box-hdg">
42
- <div class="label-row"><span class="label">HEADING (MEAN)</span></div>
43
- <span class="value-large" id="hdg">000&deg;</span>
44
- </div>
45
-
46
- <div class="data-box box-cog">
47
- <div class="label-row"><span class="label">COG (MEAN)</span></div>
48
- <span class="value-large" id="cog">000&deg;</span>
49
- </div>
50
-
51
- <div class="data-box box-tack">
52
- <div class="label-row"><span class="label" id="tack-label">TACK</span></div>
53
- <div class="dual-value-container">
54
- <div class="dual-value-col tack-hdg-col">
55
- <span class="dual-label">HDG</span>
56
- <span class="value dual-val" id="tack-hdg">---&deg;</span>
57
- </div>
58
- <div class="dual-value-col tack-cog-col">
59
- <span class="dual-label">COG</span>
60
- <span class="value dual-val" id="tack-cog">---&deg;</span>
61
- </div>
62
- </div>
63
- </div>
64
-
65
- <!-- BUSSOLA CENTRALE -->
66
- <div class="box-gauge">
67
- <!-- STATO CONNESSIONE -->
68
- <div id="status" class="offline">OFFLINE</div>
69
-
70
- <!-- BUSSOLA 1: ANALOGICA STANDARD (Visibile all'avvio) -->
71
- <svg id="wind-gauge" viewBox="35 38 330 395" preserveAspectRatio="xMidYMid meet">
72
- <defs>
73
- <clipPath id="boat-clip"><circle cx="200" cy="200" r="50" /></clipPath>
74
- <linearGradient id="axiom-grad" x1="0%" y1="0%" x2="0%" y2="100%">
75
- <stop offset="0%" style="stop-color:#ffffff;stop-opacity:1" />
76
- <stop offset="100%" style="stop-color:#888888;stop-opacity:1" />
77
- </linearGradient>
78
- <linearGradient id="leeway-grad" x1="0%" y1="0%" x2="100%" y2="0%">
79
- <stop offset="0%" style="stop-color:#ff0000;stop-opacity:1" />
80
- <stop offset="25%" style="stop-color:#ff8800;stop-opacity:1" />
81
- <stop offset="50%" style="stop-color:#00ff00;stop-opacity:1" />
82
- <stop offset="75%" style="stop-color:#ff8800;stop-opacity:1" />
83
- <stop offset="100%" style="stop-color:#ff0000;stop-opacity:1" />
84
- </linearGradient>
85
- <linearGradient id="leeway-night-grad" x1="0%" y1="0%" x2="100%" y2="0%">
86
- <stop offset="0%" style="stop-color:#ff0000;stop-opacity:1" />
87
- <stop offset="50%" style="stop-color:#330000;stop-opacity:1" />
88
- <stop offset="100%" style="stop-color:#ff0000;stop-opacity:1" />
89
- </linearGradient>
90
- <clipPath id="leeway-clip">
91
- <rect id="leeway-mask-rect" x="125" y="0" width="0" height="12" rx="2" />
92
- </clipPath>
93
- <filter id="center-glow" x="-20%" y="-20%" width="140%" height="140%">
94
- <feDropShadow dx="0" dy="0" stdDeviation="8" flood-color="#aaaaaa" flood-opacity="0.5" />
95
- </filter>
96
- </defs>
97
-
98
- <circle cx="200" cy="200" r="160" fill="#050505" />
99
- <circle cx="200" cy="200" r="125" fill="#121212" />
100
-
101
- <path d="M 82.0 101.0 A 154 154 0 0 1 142.3 57.2" fill="none" stroke="#ff0000" stroke-width="12" />
102
- <path d="M 257.7 57.2 A 154 154 0 0 1 318.0 101.0" fill="none" stroke="#00ff00" stroke-width="12" />
103
- <path d="M 265.1 339.6 A 154 154 0 0 1 134.9 339.6" fill="none" stroke="#ff8800" stroke-width="12" />
104
-
105
- <g id="ticks"></g>
106
-
107
- <g id="tick-labels" fill="#bbb" text-anchor="middle" dominant-baseline="middle" font-family="Arial" font-weight="bold">
108
- <text font-size="16" transform="translate(200, 74)">0</text>
109
- <text font-size="16" transform="translate(326, 200) rotate(90)">90</text>
110
- <text font-size="16" transform="translate(74, 200) rotate(-90)">90</text>
111
- <text font-size="16" transform="translate(200, 326) rotate(180)">180</text>
112
- <text font-size="11" transform="translate(262.5, 91.7) rotate(30)">30</text>
113
- <text font-size="11" transform="translate(308.3, 137.5) rotate(60)">60</text>
114
- <text font-size="11" transform="translate(308.3, 262.5) rotate(120)">120</text>
115
- <text font-size="11" transform="translate(262.5, 308.3) rotate(150)">150</text>
116
- <text font-size="11" transform="translate(137.5, 91.7) rotate(-30)">30</text>
117
- <text font-size="11" transform="translate(91.7, 137.5) rotate(-60)">60</text>
118
- <text font-size="11" transform="translate(91.7, 262.5) rotate(-120)">120</text>
119
- <text font-size="11" transform="translate(137.5, 308.3) rotate(-150)">150</text>
120
- </g>
121
-
122
- <g id="track-pointer" transform="rotate(0, 200, 200)">
123
- <path d="M200,42 L194,58 L206,58 Z" fill="#007aff" stroke="#fff" stroke-width="0.5" />
124
- </g>
125
-
126
- <circle id="fullscreen-hotspot" cx="200" cy="200" r="55" fill="#181818" stroke="#333" stroke-width="1" filter="url(#center-glow)" cursor="pointer" />
127
-
128
- <path id="boat-icon" d="M200,150 Q165,185 170,250 Q165,190 200,173 Q235,190 230,250 Q235,185 200,150 Z"
129
- fill="url(#axiom-grad)" transform="translate(0, 5)" clip-path="url(#boat-clip)" style="pointer-events: none;" />
130
-
131
- <g id="aws-display-group" transform="translate(200, 265)">
132
- <text x="0" y="0" fill="#777" font-size="10" font-weight="bold" text-anchor="middle" text-transform="uppercase">Apparent Wind kts</text>
133
- <text id="aws-val-svg" x="0" y="42" fill="#fff" font-size="52" font-weight="bold" text-anchor="middle">0.0</text>
134
- </g>
135
-
136
- <g id="awa-pointer" transform="rotate(0, 200, 200)" opacity="0.9">
137
- <path d="M 200,70 L 213,95 L 200,145 L 187,95 Z" fill="#ff8c00" stroke="#000" stroke-width="1" />
138
- <text x="200" y="90" fill="#000" font-size="10" font-weight="900" text-anchor="middle" font-family="Arial Black">A</text>
139
- </g>
140
-
141
- <g id="twa-pointer" transform="rotate(0, 200, 200)" opacity="0.9">
142
- <path d="M 200,92 A 8,8 0 0 1 208,100 C 208,108 200,128 200,128 C 200,128 192,108 192,100 A 8,8 0 0 1 200,92 Z"
143
- fill="#ffff00" stroke="#000" stroke-width="0.8" />
144
- <text x="200" y="106" fill="#000" font-size="9" font-weight="900" text-anchor="middle" font-family="Arial Black">T</text>
145
- <circle id="trend-gauge-cw" cx="215" cy="110" r="4" fill="#bbb" />
146
- <circle id="trend-gauge-ccw" cx="185" cy="110" r="4" fill="#bbb" />
147
- </g>
148
-
149
- <g transform="translate(75, 395)">
150
- <text x="125" y="-12" id="leeway-val" fill="#aaa" font-size="11" text-anchor="middle" font-weight="bold">LEEWAY: 0.0&deg;</text>
151
- <rect x="0" y="0" width="250" height="12" fill="#222" rx="3" />
152
- <rect x="0" y="0" width="250" height="12" fill="url(#leeway-grad)" clip-path="url(#leeway-clip)" rx="3" />
153
- <g stroke="#555" stroke-width="1">
154
- <line x1="0" y1="-2" x2="0" y2="14" /><line x1="62.5" y1="2" x2="62.5" y2="10" />
155
- <line x1="125" y1="-2" x2="125" y2="14" /><line x1="187.5" y1="2" x2="187.5" y2="10" />
156
- <line x1="250" y1="-2" x2="250" y2="14" />
157
- </g>
158
- <g fill="#555" font-size="8" text-anchor="middle" font-weight="bold">
159
- <text x="0" y="24">-20&deg;</text><text x="62.5" y="24">-10</text>
160
- <text x="125" y="24">0&deg;</text><text x="187.5" y="24">10</text>
161
- <text x="250" y="24">20&deg;</text>
162
- </g>
163
- </g>
164
- </svg>
165
-
166
- <!-- BUSSOLA 2: RADAR HISTORICAL (Nascosto all'avvio) -->
167
- <svg id="wind-radar" viewBox="35 38 330 395" preserveAspectRatio="xMidYMid meet" style="display: none;">
168
- <defs id="radar-gradients">
169
- <clipPath id="radar-boat-clip"><circle cx="200" cy="200" r="50" /></clipPath>
170
- <filter id="radar-center-glow" x="-20%" y="-20%" width="140%" height="140%">
171
- <feDropShadow dx="0" dy="0" stdDeviation="8" flood-color="#aaaaaa" flood-opacity="0.5" />
172
- </filter>
173
- </defs>
174
-
175
- <circle cx="200" cy="200" r="160" fill="rgb(252, 252, 252)" />
176
- <circle cx="200" cy="200" r="125" fill="rgb(240, 240, 240)" />
177
-
178
- <g id="radar-ticks"></g>
179
-
180
- <g id="radar-tick-labels" fill="#000000" text-anchor="middle" dominant-baseline="middle" font-family="Arial" font-weight="bold">
181
- <text font-size="16" transform="translate(200, 74)">N</text>
182
- <text font-size="16" transform="translate(326, 200) rotate(90)">E</text>
183
- <text font-size="16" transform="translate(74, 200) rotate(-90)">W</text>
184
- <text font-size="16" transform="translate(200, 326) rotate(180)">S</text>
185
-
186
- <text font-size="11" transform="translate(262.5, 91.7) rotate(30)">30</text>
187
- <text font-size="11" transform="translate(308.3, 137.5) rotate(60)">60</text>
188
- <text font-size="11" transform="translate(308.3, 262.5) rotate(120)">120</text>
189
- <text font-size="11" transform="translate(262.5, 308.3) rotate(150)">150</text>
190
- <text font-size="11" transform="translate(137.5, 91.7) rotate(-30)">330</text>
191
- <text font-size="11" transform="translate(91.7, 137.5) rotate(-60)">300</text>
192
- <text font-size="11" transform="translate(91.7, 262.5) rotate(-120)">240</text>
193
- <text font-size="11" transform="translate(137.5, 308.3) rotate(-150)">210</text>
194
- </g>
195
-
196
- <circle id="ora-orbit" cx="200" cy="200" r="67.2" fill="none" stroke="#cfd8dc" stroke-width="1.2" stroke-dasharray="4, 4" />
197
- <g id="radar-rings"></g>
198
-
199
- <circle id="radar-hotspot" cx="200" cy="200" r="55" fill="rgb(238, 238, 238)" stroke="#e0e0e0" stroke-width="1" filter="url(#radar-center-glow)" cursor="pointer" />
200
- <path id="radar-boat-icon" d="M200,150 Q165,185 170,250 Q165,190 200,173 Q235,190 230,250 Q235,185 200,150 Z"
201
- fill="#000000" transform="translate(0, 5)" clip-path="url(#radar-boat-clip)" style="pointer-events: none;" />
202
- </svg>
203
- </div>
204
-
205
- <!-- COLONNA DESTRA -->
206
- <!-- NOTA: Etichetta a SX e Unitร  a DX nel codice, il CSS usa row-reverse per specchiarli sul bordo -->
207
- <div class="data-box box-depth">
208
- <div class="label-row"><span class="label">DEPTH</span><span class="unit">m</span></div>
209
- <span class="value" id="depth">--.-</span>
210
- <div class="graph-wrapper">
211
- <div class="scale-labels" id="depth-scale"></div>
212
- <svg class="sparkline" id="depth-graph" viewBox="0 0 200 40" preserveAspectRatio="none"></svg>
213
- </div>
214
- </div>
215
-
216
- <div class="data-box box-tws">
217
- <div class="label-row">
218
- <span class="label" id="tws-aws-label">TWS</span>
219
- <span class="unit">kts</span>
220
- </div>
221
- <span class="value" id="tws">0.0</span>
222
- <div class="graph-wrapper">
223
- <div class="scale-labels" id="tws-scale"></div>
224
- <svg class="sparkline" id="tws-graph" viewBox="0 0 200 40" preserveAspectRatio="none"></svg>
225
- </div>
226
- </div>
227
-
228
- <div class="data-box box-twa">
229
- <div class="label-row"><span class="label">TWA (MEAN)</span></div>
230
- <span class="value-large" id="twa-avg">---&deg;</span>
231
- </div>
232
-
233
- <div class="data-box box-awa">
234
- <div class="label-row"><span class="label">AWA (MEAN)</span></div>
235
- <span class="value-large" id="awa-avg">---&deg;</span>
236
- </div>
237
-
238
- <div class="data-box box-twd">
239
- <div class="label-row"><span class="label">TWD (MEAN)</span></div>
240
- <div class="value-with-compass">
241
- <svg class="mini-compass" viewBox="0 0 40 40">
242
- <circle cx="20" cy="20" r="18.5" fill="none" stroke="rgba(0,0,0,0.1)" stroke-width="1.2"/>
243
- <text x="20" y="8" fill="#e74c3c" font-size="5.5" text-anchor="middle" font-weight="900">N</text>
244
- <text x="20" y="36" fill="#000" font-size="5.5" text-anchor="middle" font-weight="900">S</text>
245
- <g id="twd-boat-wrap" transform="rotate(0, 20, 20)">
246
- <path d="M 20,17 L 17,26 L 20,24.5 L 23,26 Z" fill="#000" opacity="0.3" />
247
- </g>
248
- <g id="twd-arrow" transform="rotate(0, 20, 20)">
249
- <path id="twd-wind-chevron" d="M 17,5 L 20,7.5 L 23,5" fill="none" stroke="#000" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" />
250
- <circle id="trend-dot-cw" cx="24" cy="7.5" r="1.5" fill="#bbb" />
251
- <circle id="trend-dot-ccw" cx="16" cy="7.5" r="1.5" fill="#bbb" />
252
- </g>
253
- </svg>
254
- <span class="value-large" id="twd-avg">---&deg;</span>
255
- </div>
256
- </div>
257
- </div>
258
- <script src="utils.js"></script>
259
- <script src="charts.js"></script>
260
- <script src="gauge.js"></script>
261
- <script src="weather-radar.js"></script>
262
- <script src="app.js?v=6.0"></script>
263
- </body>
13
+ <!-- Favicon & Home screen bookmark icons -->
14
+ <link rel="icon" type="image/png" href="./icons/dash.png" />
15
+ <link rel="apple-touch-icon" href="./icons/dash.png" />
16
+ <title>Sailing Dashboard Pro</title>
17
+ </head>
18
+ <body>
19
+ <div id="root"></div>
20
+ <script type="module" src="/src/main.tsx"></script>
21
+ </body>
264
22
  </html>