ocearo-ui 0.1.21 → 2.0.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.
Files changed (147) hide show
  1. package/404.html +1 -1
  2. package/README.fr.md +123 -133
  3. package/README.md +120 -134
  4. package/__next.__PAGE__.txt +19 -5
  5. package/__next._full.txt +11 -11
  6. package/__next._tree.txt +2 -2
  7. package/_next/static/chunks/0-c2-u5p6bxzo.js +1 -0
  8. package/_next/static/chunks/01atsfq_4opab.js +1 -0
  9. package/_next/static/chunks/0cx639tthxlz4.js +1 -0
  10. package/_next/static/chunks/0h2qsuyze9ds1.js +1 -0
  11. package/_next/static/chunks/0iqdztntvxh3c.js +119 -0
  12. package/_next/static/chunks/0iztd835j5p1n.js +700 -0
  13. package/_next/static/chunks/{2nxsyowmuwpjy.js → 0jfox92up1jvy.js} +1 -1
  14. package/_next/static/chunks/0jpa5m78l0ib0.js +1 -0
  15. package/_next/static/chunks/0kpstejrka-7k.js +1 -0
  16. package/_next/static/chunks/0n95joti2n2py.js +3 -0
  17. package/_next/static/chunks/0oaqe70syw_b5.js +1 -0
  18. package/_next/static/chunks/0ohleay4roayh.js +353 -0
  19. package/_next/static/chunks/0on1skx9l54-9.js +1 -0
  20. package/_next/static/chunks/0w_skktmh3e48.js +1 -0
  21. package/_next/static/chunks/0y-dctk-kmqss.js +1 -0
  22. package/_next/static/chunks/0yiqocv54ogoq.js +1 -0
  23. package/_next/static/chunks/0zlk2mlkvbtj1.js +1 -0
  24. package/_next/static/chunks/0zwc9m4qgtwle.js +1 -0
  25. package/_next/static/chunks/11awvgcou7v4r.js +1 -0
  26. package/_next/static/chunks/144mjqdrx42lq.js +1 -0
  27. package/_next/static/chunks/1d81m3g9jqkob.js +1 -0
  28. package/_next/static/chunks/1ed7ayfg19cjb.js +1 -0
  29. package/_next/static/chunks/1epya-yh2wvj2.js +1 -0
  30. package/_next/static/chunks/1exvcc71cgr4t.js +446 -0
  31. package/_next/static/chunks/1fzzb4jtgv5gg.js +9 -0
  32. package/_next/static/chunks/1g8fbzv0hu0i2.js +1 -0
  33. package/_next/static/chunks/1gaiqhzy_0d7s.js +1 -0
  34. package/_next/static/chunks/1gq4t2dyr7rq3.js +1 -0
  35. package/_next/static/chunks/1l-jew0-1-3eq.js +30 -0
  36. package/_next/static/chunks/1ptj3mv8b_tzs.js +1 -0
  37. package/_next/static/chunks/1w1xks0hp_tlr.js +346 -0
  38. package/_next/static/chunks/1xekc18cyi4sx.js +1 -0
  39. package/_next/static/chunks/1y35unwvy8p98.js +1 -0
  40. package/_next/static/chunks/2aq8orcir5h03.js +1 -0
  41. package/_next/static/chunks/2fdpvhx4oo81h.js +1 -0
  42. package/_next/static/chunks/2g-xpe7xnropn.js +409 -0
  43. package/_next/static/chunks/2kmptoy43h0mx.js +1 -0
  44. package/_next/static/chunks/2n86yq1wz1yx7.js +1 -0
  45. package/_next/static/chunks/2r_h93rt76vka.js +1 -0
  46. package/_next/static/chunks/2rebcvig176xk.js +564 -0
  47. package/_next/static/chunks/2w71oj1brox5z.js +1 -0
  48. package/_next/static/chunks/2wayx8t5r5fi9.js +1 -0
  49. package/_next/static/chunks/2y3d8zopa8r5r.css +3 -0
  50. package/_next/static/chunks/3--qzv1u6c7_7.js +1 -0
  51. package/_next/static/chunks/31lugryguappr.js +1 -0
  52. package/_next/static/chunks/32j_j396qs7be.js +1 -0
  53. package/_next/static/chunks/32yn_fc-es66m.js +22 -0
  54. package/_next/static/chunks/34_oyzp8_pi_m.js +1 -0
  55. package/_next/static/chunks/34wvn5zw0a69w.js +1 -0
  56. package/_next/static/chunks/37t6vg8fb1-z8.js +1 -0
  57. package/_next/static/chunks/3k5itcabq5oqs.js +9 -0
  58. package/_next/static/chunks/3ktj0iemsr1m-.js +1 -0
  59. package/_next/static/chunks/3nm7o0eq5abhg.js +1 -0
  60. package/_next/static/chunks/3s6nzrbk-8mnv.js +31 -0
  61. package/_next/static/chunks/3td9mnhvcj9cq.js +1 -0
  62. package/_next/static/chunks/3xjnsskh0tayo.js +1 -0
  63. package/_next/static/chunks/3zqx8h9yg5-ch.js +1 -0
  64. package/_next/static/chunks/turbopack-1h111qg8l92rw.js +1 -0
  65. package/_not-found/__next._full.txt +12 -12
  66. package/_not-found/__next._not-found.__PAGE__.txt +19 -2
  67. package/_not-found/__next._tree.txt +2 -2
  68. package/_not-found.html +1 -1
  69. package/_not-found.txt +12 -12
  70. package/boats/boats.json +40 -5
  71. package/index.html +1 -1
  72. package/index.txt +11 -11
  73. package/package.json +14 -14
  74. package/settings/__next._full.txt +14 -14
  75. package/settings/__next._tree.txt +2 -2
  76. package/settings/__next.settings.__PAGE__.txt +22 -5
  77. package/settings.html +1 -1
  78. package/settings.txt +14 -14
  79. package/__next._head.txt +0 -6
  80. package/__next._index.txt +0 -5
  81. package/_next/static/chunks/009rlietvsaq2.js +0 -1
  82. package/_next/static/chunks/070stlm97b9a2.js +0 -1
  83. package/_next/static/chunks/08gn5_gd6amkq.js +0 -1
  84. package/_next/static/chunks/0abo2oxxw3hpi.js +0 -1
  85. package/_next/static/chunks/0jzbiie0nzar2.js +0 -1
  86. package/_next/static/chunks/0l9s4rhr00adq.js +0 -1
  87. package/_next/static/chunks/0ri_um14-5x7u.js +0 -1
  88. package/_next/static/chunks/0utifqhyv2cuc.js +0 -1
  89. package/_next/static/chunks/0uvr7xkt_5uno.js +0 -1
  90. package/_next/static/chunks/0wzr1__so6xme.js +0 -1
  91. package/_next/static/chunks/0zjxnzbxh76mt.js +0 -1
  92. package/_next/static/chunks/11ln9lzzbo35q.js +0 -1
  93. package/_next/static/chunks/1bz0tcmhjmjgh.js +0 -1
  94. package/_next/static/chunks/1d4h-sglyo8ft.js +0 -1
  95. package/_next/static/chunks/1gizigk3w6q7j.js +0 -31
  96. package/_next/static/chunks/1porlo35ga4zu.js +0 -1
  97. package/_next/static/chunks/1ptwltjb3h6jf.js +0 -4
  98. package/_next/static/chunks/1sq_733weprvs.js +0 -1
  99. package/_next/static/chunks/1w1ua0-p5lk1e.js +0 -38
  100. package/_next/static/chunks/1wv5e5qomv-7g.css +0 -3
  101. package/_next/static/chunks/27hn45pv56pv7.js +0 -393
  102. package/_next/static/chunks/28y-j215vt42m.js +0 -1
  103. package/_next/static/chunks/29-ag86gvlc40.js +0 -9
  104. package/_next/static/chunks/2a0yjir_3mk1m.js +0 -1
  105. package/_next/static/chunks/2atijj_pkc4uc.js +0 -2
  106. package/_next/static/chunks/2baz6j1q913qw.js +0 -1
  107. package/_next/static/chunks/2ccpehidlaq88.js +0 -1
  108. package/_next/static/chunks/2lez3uww9j4t0.js +0 -135
  109. package/_next/static/chunks/2lvvt2f1l5sfu.js +0 -1
  110. package/_next/static/chunks/2nd5amcst4lhp.js +0 -1
  111. package/_next/static/chunks/2pwmb4pkqygy_.js +0 -1
  112. package/_next/static/chunks/2rsdqsatgq9ta.js +0 -1
  113. package/_next/static/chunks/2uao1hfsz-oy8.js +0 -1
  114. package/_next/static/chunks/2urimbxg5bqv3.js +0 -1
  115. package/_next/static/chunks/2yghvponatk1x.js +0 -135
  116. package/_next/static/chunks/31i14-jemhnic.js +0 -863
  117. package/_next/static/chunks/32cbrrsp-x35l.js +0 -1
  118. package/_next/static/chunks/36ra6xjie0dsv.js +0 -9
  119. package/_next/static/chunks/3dt63tw_9r0jt.js +0 -38
  120. package/_next/static/chunks/3iuz3f0429awx.js +0 -1
  121. package/_next/static/chunks/3jh1u-s3k0bkk.js +0 -1
  122. package/_next/static/chunks/3jupxlga7r_do.js +0 -1
  123. package/_next/static/chunks/3m57lnggu30tr.js +0 -243
  124. package/_next/static/chunks/3rhgquok36e79.js +0 -1
  125. package/_next/static/chunks/3sgrkzk20mxs6.js +0 -54
  126. package/_next/static/chunks/42dfiu6iby3fq.js +0 -1
  127. package/_next/static/chunks/43ps2cu5ahf8n.js +0 -3
  128. package/_next/static/chunks/turbopack-0pirmko-w-775.js +0 -1
  129. package/_not-found/__next._head.txt +0 -6
  130. package/_not-found/__next._index.txt +0 -5
  131. package/_not-found/__next._not-found.txt +0 -5
  132. package/boats/ais/ais-30.glb +0 -0
  133. package/boats/ais/ais-31.glb +0 -0
  134. package/boats/ais/ais-35.glb +0 -0
  135. package/boats/ais/ais-36.glb +0 -0
  136. package/boats/ais/ais-37.glb +0 -0
  137. package/boats/ais/ais-40.glb +0 -0
  138. package/boats/ais/ais-50.glb +0 -0
  139. package/boats/ais/ais-60.glb +0 -0
  140. package/boats/ais/ais-70.glb +0 -0
  141. package/boats/ais/ais-80.glb +0 -0
  142. package/settings/__next._head.txt +0 -6
  143. package/settings/__next._index.txt +0 -5
  144. package/settings/__next.settings.txt +0 -5
  145. /package/_next/static/{hlKDicTyNGfd7dmHqo5m5 → baa0rBM5TIECz8JV_tLT6}/_buildManifest.js +0 -0
  146. /package/_next/static/{hlKDicTyNGfd7dmHqo5m5 → baa0rBM5TIECz8JV_tLT6}/_clientMiddlewareManifest.js +0 -0
  147. /package/_next/static/{hlKDicTyNGfd7dmHqo5m5 → baa0rBM5TIECz8JV_tLT6}/_ssgManifest.js +0 -0
package/README.md CHANGED
@@ -1,202 +1,188 @@
1
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
2
+ [![npm](https://img.shields.io/npm/v/ocearo-ui.svg)](https://www.npmjs.com/package/ocearo-ui)
3
+ [![Signal K](https://img.shields.io/badge/Signal%20K-webapp-0a7ea4.svg)](https://signalk.org)
1
4
  [![GitHub Issues](https://img.shields.io/github/issues/laborima/ocearo-ui.svg)](https://github.com/laborima/ocearo-ui/issues)
2
- [![Contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg)](CONTRIBUTING.md)
3
- [![License](https://img.shields.io/badge/License-Apache%202.0-brightgreen.svg)](https://opensource.org/licenses/Apache-2.0)
4
5
 
5
6
  [Français 🇫🇷](README.fr.md)
6
7
 
7
8
  # Ocearo UI
8
9
 
9
- **Sailing Made Smarter**
10
+ **The open-source sailing display for Signal K.** A clear, real-time 3D picture of the boat, the sea and the traffic around it — sail trim, rules of the road, man overboard, anchor watch, bathymetry and weather — in a calm interface inspired by Tesla's driving visualisation. It runs as a Signal K webapp on the boat's server (a Raspberry Pi is enough) and in any browser on board: chart-table screen, cockpit tablet or phone.
10
11
 
11
- **Ocean Robot** is set to transform sailing with an intuitive and visually engaging user interface (UI) designed for the OpenPlotter boat project. Powered by the **Signal K** platform, Ocean Robot gathers and stores boat data to provide real-time insights.
12
+ ![Under spinnaker leaving La Rochelle: realistic sea from the true wind, sail trim stripes and telltales, compass and tide](docs/screenshots/hero.jpg)
12
13
 
13
- Inspired by Tesla's autopilot UI, this system delivers a futuristic and streamlined experience tailored for sailors.
14
+ > Every image on this page is a screenshot of the app, fed with real data for La Rochelle on 4 October 2026: wind from Open-Meteo, tides, the SHOM 20 m bathymetry and scripted AIS traffic.
14
15
 
15
16
  ---
16
17
 
17
- ## **Latest Updates (v0.1.19)**
18
+ ## Contents
18
19
 
19
- - **Engine Maintenance Schedule**: New maintenance tab with manufacturer service intervals (Volvo Penta D1/D2, Yanmar YM, generic diesel), last-done tracking and overdue/due-soon statuses.
20
- - **Engine Alarms**: The 24 NMEA2000 discrete engine notifications (overheat, oil pressure, coolant level…) are now surfaced with a persistent alarm banner and tab badge, plus configurable temperature zones raising audible SignalK alerts.
21
- - **Harbor-detail 3D Chart**: Adaptive level-of-detail map plane — pontoons visible at berth (OSM z19 + OpenSeaMap seamarks), wide area when zoomed out, true-to-scale with AIS targets.
22
- - **Real Weather Map**: Meteo mode combines the base map with the Windy wind overlay and live RainViewer precipitation radar over a ~40 km area.
23
- - **Living Ocean & Sky**: Wind-driven geometric swell (significant wave height from wind speed), forecast-driven clouds and rain particles, ocean sky shared by all map modes.
24
- - **Air Quality from VOC**: Dashboard tile driven by the BME680 gas resistance (kΩ) with qualitative scale when no CO₂/PM2.5 sensors are fitted.
25
- - **Fuel Analytics**: Consumption-range estimation (worst–average), full-tank assumption after refills, engine hours persisted with engine off, complete refill history.
26
- - **RPi5 Performance**: Chart/meteo modes render a lite ocean without the mirror-reflection pass — the scene is no longer drawn twice per frame.
27
- - **Previous highlights (v0.1.16)**: full 3D sails with physically-based trim, rigging tensions, sail trim HUD, autopilot view, dashboard, configurable units, Next.js 16 / React 19 / Tailwind v4 stack.
20
+ - [Highlights](#highlights)
21
+ - [The 3D view](#the-3d-view)
22
+ - [Traffic and the rules of the road](#traffic-and-the-rules-of-the-road)
23
+ - [Safety: man overboard and anchor watch](#safety-man-overboard-and-anchor-watch)
24
+ - [Instruments and boat systems](#instruments-and-boat-systems)
25
+ - [Offline at sea](#offline-at-sea)
26
+ - [Installation](#installation)
27
+ - [Configuration](#configuration)
28
+ - [Development](#development)
29
+ - [Data sources and credits](#data-sources-and-credits)
30
+ - [Contributing](#contributing) · [Licence](#licence) · [Disclaimer](#navigation-disclaimer)
28
31
 
29
32
  ---
30
33
 
31
- ## **Key Features**
34
+ ## Highlights
32
35
 
33
- ### **Dynamic 3D Environment**
34
- - **Physically-based Sails**: Real-time 3D representation of mainsail and jib/genoa with dynamic camber, twist, and reefing based on wind conditions.
35
- - **Interactive Rigging**: Visualization of rigging lines (backstay, vang, cunningham, outhaul) with tension-based color gradients.
36
- - **Smart Compass**: Integrated **Sail Trim HUD** showing recommended car positions for optimal performance.
37
- - **Day/Night Skybox**: Dynamic lighting and ocean environment that synchronizes with vessel time.
36
+ - **A boat you can trim from the screen.** Main, headsails and asymmetric spinnaker drawn from the wind (camber, twist, reefs, sail changes), with draft stripes, telltales and the traveller and jib cars where they should be.
37
+ - **The sea around you, five ways.** A realistic sea built from the true wind, the live chart with buoyage, the seabed in 3D, the wind forecast, or a clean FSD-style ground for racing.
38
+ - **Traffic you can read at a glance.** AIS targets drawn as real ship types and sizes, moving smoothly between reports, coloured by who must give way under COLREG — with the manoeuvre to make.
39
+ - **Tactics.** Laylines to the waypoint, isochrones from your polars (where you will be in 5, 10 and 15 minutes), VMG advice and a polar-speed ghost boat.
40
+ - **Safety first.** Man-overboard marker with drift prediction, anchor watch with the real swing track, shallow-water warnings against your draft.
41
+ - **Everything else on board.** Dashboard, engine and maintenance, energy, tanks, logbook with voyage replay, autopilot, media and documents — in 12 languages, metric, imperial or nautical units, day, dark and red night themes.
42
+ - **Built for the boat.** Works offline once the area is downloaded, adapts to a Raspberry Pi, reads standard Signal K paths only.
38
43
 
39
- ### **Navigation & Awareness**
40
- - **AIS Radar**: Real-time 3D visualization of nearby vessels with detailed information panels.
41
- - **Course & Routing**: Dedicated widget for Signal K routes, waypoints, and real-time course calculations.
42
- - **Environmental Context**: Integrated tide levels, weather forecasts (Signal K Weather API), and depth monitoring with vessel attitude.
43
- - **Laylines**: High-precision 3D laylines to assist in tactical navigation.
44
+ ---
44
45
 
45
- ### **Vessel Systems**
46
- - **Autopilot Control**: Fully integrated autopilot interface for seamless vessel management.
47
- - **Engine Monitoring**: Comprehensive gauges for temperature, pressure, and fuel consumption with refill tracking.
48
- - **Tesla-style Indicators**: Modern, high-visibility bars for battery status and tank levels.
49
- - **Customizable UI**: Support for 12 languages and configurable units (metric, imperial, nautical).
46
+ ## The 3D view
50
47
 
51
- ---
48
+ The heart of Ocearo is a live 3D scene centred on your boat. A button cycles between five representations of the world around it.
49
+
50
+ | | |
51
+ |---|---|
52
+ | ![Live chart with buoyage and AIS](docs/screenshots/chart.jpg) | ![SHOM bathymetry as a survey grid](docs/screenshots/bathymetry.jpg) |
53
+ | **Chart.** OpenStreetMap with OpenSeaMap buoyage and lights, true to scale with your boat and the AIS targets. Uses a Signal K chart provider (MBTiles) when one is installed. | **Bathymetry.** The seabed as a survey grid: relief exaggerated, isobaths, soundings at the current tide, a sounder line from the keel, and water too shallow for your draft hatched in orange. 5–20 m SHOM surveys on the French coast, global relief elsewhere. |
54
+ | ![Wind forecast layer](docs/screenshots/weather.jpg) | ![Isochrones and laylines](docs/screenshots/polars.jpg) |
55
+ | **Weather.** The 48-hour wind forecast around the boat (Open-Meteo) in Windy colours, with a time slider. | **Polars and laylines.** Where the polars put you in 5, 10 and 15 minutes on every heading, the laylines to the waypoint and the VMG to steer. |
56
+
57
+ The **sea** itself is generated from the true wind: wave height, length and direction of a coastal wind sea plus a swell, whitecaps from about 7 knots, the sky's reflection and the sun where it really is, and your own wake following the track you actually sailed. The sails are drawn from the apparent wind with their trim: draft stripes with the depth and position of maximum camber, telltales on the luff and leech, and the sheet cars on their tracks.
52
58
 
53
- ## **Core Views**
54
-
55
- ### **Cruising View**
56
- - **3D Visualization**: Provides a dynamic 3D view of the vessel, displaying critical elements such as:
57
- - Wind direction with **active laylines**
58
- - High-precision 3D Compass
59
- - Depth level & vessel attitude
60
- - Nearby vessels represented in 3D using AIS data
61
- - **Sail Trim HUD**: Real-time arc indicators for mainsail traveller and jib cars at compass level.
62
- - Physically-based sail shape (camber & twist) reflecting current wind conditions.
63
-
64
- ![Cruising View](docs/cruising.png)
65
-
66
- ### **Anchored View**
67
- - Simplified 3D representation of the vessel with key at-anchor data, including:
68
- - GPS position
69
- - Time
70
- - Tide levels
71
- - Depth
72
- - Battery status
73
-
74
- ![Anchored View](docs/anchored.png)
75
-
76
- ### **Park Assist View**
77
- - Leverages camera and sensor data to simplify docking by:
78
- - Displaying trajectory predictions based on wind and rudder angle
79
- - Showing speed indications and live feeds from the front camera
80
- - Highlighting available berthing spots
81
-
82
- *Currently in progress.*
83
-
84
- ![Anchored View](docs/parking.png)
85
-
86
- ### **Other Views**
87
- Additional visuals enhance the system's functionality:
88
-
89
- ![Other Views](docs/ais.png)
90
- ![Other Views](docs/ocean.png)
91
- ![Other Views](docs/meteo.png)
92
- ![Other Views](docs/navigation.png)
93
- ![Other Views](docs/instruments.png)
94
- ![Other Views](docs/webcam.png)
95
- ![Other Views](docs/battery.png)
96
- ![Other Views](docs/performances.png)
97
- ![Other Views](docs/dashboard1.png)
98
- ![Other Views](docs/dashboard2.png)
99
- ![Other Views](docs/dashboard3.png)
100
- ![Other Views](docs/engine.png)
59
+ Boats: a 10.8 m racer (default), a 14 m catamaran and simpler models, chosen in the settings.
101
60
 
102
61
  ---
103
62
 
104
- ## **Vision for the Future**
105
- Ocean Robot's roadmap includes advanced features aimed at enhancing safety and efficiency for sailors:
63
+ ## Traffic and the rules of the road
64
+
65
+ | | |
66
+ |---|---|
67
+ | ![We must give way to a fishing vessel](docs/screenshots/colregs.jpg) | ![A port-tack yacht must keep clear of us](docs/screenshots/standon.jpg) |
68
+ | **Our move.** A vessel engaged in fishing crosses ahead: under rule 18 the sailing yacht keeps clear. The boat and the banner turn orange-red, the advice names the rule and the alteration (“Avoid: 15° to port”). | **Their move.** A port-tack yacht closing from starboard must keep clear (rule 12): it turns violet with the manoeuvre expected of it. If it does not act, rule 17(b) advice appears. |
69
+
70
+ Colours mean the same thing everywhere: **red** — risk of collision and it is our move; **violet** — risk of collision and it is theirs; **orange** — close but no collision course; grey — nothing to report. CPA and TCPA come from both vessels' course and speed; thresholds are set in the settings. AIS targets are drawn by type and length (sailing yachts, catamarans, ferries, cargo ships, tugs, fishing vessels, lifeboats…) and dead-reckoned between reports, so they glide instead of jumping.
106
71
 
107
- - **AI-powered object detection** for floating debris and obstacles
108
- - Integration with advanced camera systems (e.g., **see.ai**) to detect floating objects
109
- - **Future Enhancements**:
110
- - Camera-based monitoring of sail indicators (*penons*) for optimal sail trim suggestions
111
- - 3D Bathymetry visualization
112
- - 3D Start lines visualization
113
-
114
72
  ---
115
73
 
116
- Check out the live demo: https://laborima.github.io/ocearo-ui/
74
+ ## Safety: man overboard and anchor watch
75
+
76
+ | | |
77
+ |---|---|
78
+ | ![Man overboard](docs/screenshots/mob.jpg) | ![Anchor watch](docs/screenshots/anchor.jpg) |
79
+ | **Man overboard.** One button (or any Signal K MOB notification) raises the alarm: drop point, the person's estimated position now from current and leeway, the drift line and the bearing and distance to steer. | **Anchor watch.** Alarm radius and 80 % watch ring centred on the anchor, the rode, and the track the boat has actually described around it: a veer or a dragging anchor shows in its shape long before the alarm. Kept by [ocearo-core](https://github.com/laborima/ocearo-core), so it survives a reload. |
80
+
81
+ ![Parking assist](docs/screenshots/parking.jpg)
117
82
 
118
- Or install it in Signal K using the NPM package: https://www.npmjs.com/package/ocearo-ui
83
+ **Parking assist.** Choose the berth (bow-in, stern-in, alongside, mooring buoy) and follow the predicted track with wind and current.
119
84
 
120
85
  ---
121
86
 
122
- ## **Contributing**
87
+ ## Instruments and boat systems
123
88
 
124
- Your support and involvement make Ocearo UI better! Here are some ways you can contribute:
89
+ | | |
90
+ |---|---|
91
+ | ![Dashboard](docs/screenshots/dashboard.jpg) | ![Dashboard, dark theme](docs/screenshots/dashboard-dark.jpg) |
92
+ | **Dashboard.** AIS radar, weather, course to the waypoint, tanks… next to the 3D view, in the day theme… | …and the dark theme, in the spirit of Tesla's FSD display: charcoal, light shapes, luminous accents. |
93
+ | ![Engine](docs/screenshots/engine.jpg) | ![Energy](docs/screenshots/energy.jpg) |
94
+ | **Engine.** Speed, hours, temperatures, pressures, gear and alarms (the 24 NMEA 2000 engine notifications), plus a maintenance schedule. | **Energy.** Batteries, charge and consumption, solar, and the health of the Raspberry Pi running the stack. |
95
+ | ![Logbook](docs/screenshots/logbook.jpg) | ![Settings](docs/screenshots/settings.jpg) |
96
+ | **Logbook.** Automatic and manual entries, mission timeline, and recorded voyages you can replay in the 3D view. | **Settings.** Boat, units, language, themes, 3D quality (including a Raspberry Pi profile), alarm thresholds and offline data. |
125
97
 
126
- - Report bugs: Help me squash issues by letting me know when something isn't working as expected.
127
- - Suggest features: Share your ideas for new features or improvements your feedback shapes the project!
128
- - Contribute code: Submit pull requests to add features, fix bugs, or improve documentation.
129
- - Support the project: Help fund the purchase of webcams, and sensors by buying me a coffee.
98
+ ![Night theme](docs/screenshots/dashboard-night.jpg)
130
99
 
131
- [![Buy Me A Coffee](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/laborima)
100
+ **Night theme.** Red and red-orange only, so the screen keeps your night vision, with values brighter than labels so it stays easy to read. The theme can switch itself with the sun.
132
101
 
133
102
  ---
134
103
 
135
- ## **Development and Deployment**
104
+ ## Offline at sea
136
105
 
137
- ### Building
106
+ At sea there is rarely internet. Whenever there is (marina Wi-Fi, 4G), Ocearo keeps what it will need:
138
107
 
139
- Clone sources:
108
+ - **Chart tiles** you have viewed, **wind forecasts** and **bathymetry** are cached in the display's browser; *Settings → Offline data* downloads the bathymetry and forecast for 5, 10 or 20 NM around the boat in one go.
109
+ - With **[ocearo-core](https://github.com/laborima/ocearo-core)** on the server, the **SHOM surveys** (5–20 m digital elevation models of the French coast, open data) are downloaded by the server itself and shared with every screen on board.
110
+ - OpenStreetMap's tile servers do not allow bulk downloads: for complete offline charts, add MBTiles charts to the Signal K server, which Ocearo then uses automatically.
140
111
 
141
- ```bash
142
- git clone https://github.com/laborima/ocearo-ui.git
143
- cd ocearo-ui
144
- ```
112
+ ---
145
113
 
146
- Install dependencies:
114
+ ## Installation
147
115
 
148
- ```bash
149
- npm install
150
- ```
116
+ Ocearo UI is a Signal K **webapp**.
151
117
 
152
- Start the development server:
118
+ 1. On your Signal K server (≥ 2.x), open **Appstore → Available**, search for **ocearo-ui** and install it (or `npm install ocearo-ui` in `~/.signalk`).
119
+ 2. Restart the server and open `http://<signalk-server>:3000/ocearo-ui/` on any screen on board.
120
+ 3. Recommended: install **[ocearo-core](https://www.npmjs.com/package/ocearo-core)** for the logbook, anchor watch, SHOM bathymetry, system metrics and the AI copilot.
153
121
 
154
- ```bash
155
- npm run dev
156
- ```
122
+ A live demo with simulated data: <https://laborima.github.io/ocearo-ui/>
157
123
 
158
- Access the UI at [http://localhost:3000](http://localhost:3000) in your browser.
124
+ ### Signal K prerequisites
159
125
 
160
- Edit the page by modifying `app/page.js`. Changes update automatically as you save.
126
+ Ocearo reads standard Signal K paths; when a path is missing, its display is empty rather than wrong. On a typical NMEA 2000 boat these plugins publish what it needs:
161
127
 
162
- ### Boat Models
128
+ | Plugin | Provides | Used by |
129
+ |--------|----------|---------|
130
+ | [`signalk-derived-data`](https://www.npmjs.com/package/signalk-derived-data) | True wind and true heading (enable `heading`, `angleTrueWater`, `directionTrue`) | Wind, sail trim, polars, compass |
131
+ | [`@meri-imperiumi/signalk-autostate`](https://www.npmjs.com/package/@meri-imperiumi/signalk-autostate) | `navigation.state` | Sail visibility, alert priorities |
132
+ | [`@signalk/set-system-time`](https://www.npmjs.com/package/@signalk/set-system-time) | System clock from GPS | Tides, day/night, logbook |
133
+ | [`@signalk/signalk-autopilot`](https://www.npmjs.com/package/@signalk/signalk-autopilot) | Signal K v2 autopilot API | Autopilot view |
134
+ | [`ocearo-core`](https://www.npmjs.com/package/ocearo-core) | Logbook, anchor, bathymetry, system metrics, AI copilot | Logbook, anchor watch, bathymetry, Raspberry Pi tab |
163
135
 
164
- All 3D boat models have a waterline length of 10 meters and are positioned at 0 on the Y-axis. Use Blender to make any adjustments.
136
+ Hardware: a Raspberry Pi 4 or 5 runs the server and a display comfortably; select the *Raspberry Pi* 3D quality on the Pi's own screen. Any recent browser works as a remote display.
165
137
 
166
138
  ---
167
- ### Setup Tides Data
168
139
 
169
- Create JSON files and stores tide data under the following path:
140
+ ## Configuration
170
141
 
171
- `public/tides/${harbor}/${MM}_${yyyy}.json`
142
+ Everything is set in the app (**Settings**): Signal K server address and authentication, boat model, draft and polars, units, language, theme (manual or following the sun), 3D quality, AIS scale and collision thresholds (CPA, TCPA), and offline data. Settings are stored per display.
172
143
 
173
- A sample script allows downloading tide data for La Rochelle.
144
+ To install the app on a tablet or phone as a full-screen app (PWA), serve Signal K over HTTPS on the boat's network: see [docs/ssl.md](docs/ssl.md).
174
145
 
175
146
  ---
176
147
 
177
- ## **Deploying to OpenPlotter**
178
-
179
- The recommended way is to use the npm published package.
180
- If you want to deploy your own build:
148
+ ## Development
181
149
 
182
150
  ```bash
183
151
  git clone https://github.com/laborima/ocearo-ui.git
184
152
  cd ocearo-ui
185
153
  npm install
186
- NODE_ENV=production npm run build
187
- scp -r ./out/* pi@openplotter.local:/home/pi/.signalk/node_modules/ocearo-ui
154
+ npm run dev # http://localhost:3000 — point it at your Signal K server in Settings
155
+ npm run lint
156
+ npm run build # static export in out/
188
157
  ```
189
158
 
159
+ To try a build on a Signal K server, link it as a webapp: `npm run link` (symlinks `out/` into `~/.signalk/node_modules/ocearo-ui`), then restart the server.
160
+
161
+ Stack: Next.js 16 (static export), React 19, Three.js with React Three Fiber, Tailwind CSS 4, i18next. The 3D boats, sails and AIS fleet are procedural (no model files to download); see [AGENTS.md](AGENTS.md) for the project conventions.
162
+
163
+ Tide tables for offline use live in `public/tides/<harbour>/<MM>_<yyyy>.json`.
164
+
190
165
  ---
191
166
 
192
- ⚠ Navigation Disclaimer
167
+ ## Data sources and credits
168
+
169
+ - Charts © [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors (ODbL); buoyage © [OpenSeaMap](https://openseamap.org) (CC BY-SA).
170
+ - Bathymetry: [SHOM](https://data.shom.fr) digital elevation models (Licence Ouverte Etalab 2.0), served by ocearo-core; global relief from the [Terrarium tiles](https://registry.opendata.aws/terrain-tiles/) on AWS Open Data (GEBCO, ETOPO, SRTM and others).
171
+ - Wind forecast: [Open-Meteo](https://open-meteo.com) (CC BY 4.0).
172
+ - Sun position: NOAA solar calculator equations.
173
+
174
+ ---
175
+
176
+ ## Contributing
177
+
178
+ Bug reports, ideas and pull requests are welcome — see the [issues](https://github.com/laborima/ocearo-ui/issues). Changes are listed in the [CHANGELOG](CHANGELOG.md).
179
+
180
+ [![Buy Me A Coffee](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/laborima)
193
181
 
194
- Use with Caution – Not a Substitute for Official Navigation Systems
182
+ ## Licence
195
183
 
196
- Ocearo UI is designed to enhance sailing awareness and provide real-time data visualization. However, this software is not a certified navigation or safety system and should not be relied upon as the sole source of navigational information.
184
+ [Apache 2.0](LICENSE).
197
185
 
198
- - Always cross-check data with official marine charts, GPS devices, and other navigation aids.
199
- - Maintain situational awareness and follow maritime safety regulations.
200
- - The developers of Ocearo UI are not liable for any incidents, accidents, or navigation errors that may arise from using this software.
186
+ ## Navigation disclaimer
201
187
 
202
- By using Ocearo UI, you acknowledge and accept the inherent risks of relying on non-certified navigation tools. Always navigate responsibly!
188
+ Ocearo UI improves situational awareness; it is **not a certified navigation or safety system** and must not be the only source of navigational information. Always cross-check with official charts and instruments, keep a proper lookout and follow the rules of the road. The authors accept no liability for incidents arising from its use.
@@ -1,9 +1,23 @@
1
1
  1:"$Sreact.fragment"
2
- 2:I[47257,["/ocearo-ui/_next/static/chunks/1d4h-sglyo8ft.js","/ocearo-ui/_next/static/chunks/3jupxlga7r_do.js"],"ClientPageRoot"]
3
- 3:I[81694,["/ocearo-ui/_next/static/chunks/1d4h-sglyo8ft.js","/ocearo-ui/_next/static/chunks/3jupxlga7r_do.js","/ocearo-ui/_next/static/chunks/2baz6j1q913qw.js","/ocearo-ui/_next/static/chunks/009rlietvsaq2.js","/ocearo-ui/_next/static/chunks/08gn5_gd6amkq.js","/ocearo-ui/_next/static/chunks/0wzr1__so6xme.js","/ocearo-ui/_next/static/chunks/1bz0tcmhjmjgh.js"],"default"]
4
- 6:I[97367,["/ocearo-ui/_next/static/chunks/1d4h-sglyo8ft.js","/ocearo-ui/_next/static/chunks/3jupxlga7r_do.js"],"OutletBoundary"]
2
+ 2:I[47257,["/ocearo-ui/_next/static/chunks/0h2qsuyze9ds1.js"],"ClientPageRoot"]
3
+ 3:I[81694,["/ocearo-ui/_next/static/chunks/0h2qsuyze9ds1.js","/ocearo-ui/_next/static/chunks/31lugryguappr.js","/ocearo-ui/_next/static/chunks/32j_j396qs7be.js","/ocearo-ui/_next/static/chunks/2r_h93rt76vka.js","/ocearo-ui/_next/static/chunks/1gaiqhzy_0d7s.js","/ocearo-ui/_next/static/chunks/0kpstejrka-7k.js"],"default"]
4
+ 6:I[97367,["/ocearo-ui/_next/static/chunks/0h2qsuyze9ds1.js"],"OutletBoundary"]
5
5
  7:"$Sreact.suspense"
6
- 0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/ocearo-ui/_next/static/chunks/2baz6j1q913qw.js","async":true}],["$","script","script-1",{"src":"/ocearo-ui/_next/static/chunks/009rlietvsaq2.js","async":true}],["$","script","script-2",{"src":"/ocearo-ui/_next/static/chunks/08gn5_gd6amkq.js","async":true}],["$","script","script-3",{"src":"/ocearo-ui/_next/static/chunks/0wzr1__so6xme.js","async":true}],["$","script","script-4",{"src":"/ocearo-ui/_next/static/chunks/1bz0tcmhjmjgh.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"hlKDicTyNGfd7dmHqo5m5"}
6
+ b:I[97367,["/ocearo-ui/_next/static/chunks/0h2qsuyze9ds1.js"],"ViewportBoundary"]
7
+ c:I[97367,["/ocearo-ui/_next/static/chunks/0h2qsuyze9ds1.js"],"MetadataBoundary"]
8
+ d:I[27201,["/ocearo-ui/_next/static/chunks/0h2qsuyze9ds1.js"],"IconMark"]
9
+ f:I[39756,["/ocearo-ui/_next/static/chunks/0h2qsuyze9ds1.js"],"default"]
10
+ 10:I[37457,["/ocearo-ui/_next/static/chunks/0h2qsuyze9ds1.js"],"default"]
11
+ :HL["/ocearo-ui/_next/static/chunks/2y3d8zopa8r5r.css","style"]
12
+ a:X
13
+ 0:{"buildId":"baa0rBM5TIECz8JV_tLT6","data":[{"rsc":["$","$1","c",{"children":[["$","$L2",null,{"Component":"$3","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@4","$@5"]}}],[["$","script","script-0",{"src":"/ocearo-ui/_next/static/chunks/31lugryguappr.js","async":true}],["$","script","script-1",{"src":"/ocearo-ui/_next/static/chunks/32j_j396qs7be.js","async":true}],["$","script","script-2",{"src":"/ocearo-ui/_next/static/chunks/2r_h93rt76vka.js","async":true}],["$","script","script-3",{"src":"/ocearo-ui/_next/static/chunks/1gaiqhzy_0d7s.js","async":true}],["$","script","script-4",{"src":"/ocearo-ui/_next/static/chunks/0kpstejrka-7k.js","async":true}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],"isPartial":"$@9","staleTime":"$a","varyParams":null},{"rsc":["$","$1","h",{"children":[null,["$","$Lb",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"}],["$","meta","2",{"name":"theme-color","content":"#4bbcd8","media":"(prefers-color-scheme: light)"}],["$","meta","3",{"name":"theme-color","content":"#0a2e3d","media":"(prefers-color-scheme: dark)"}]]}],["$","div",null,{"hidden":true,"children":["$","$Lc",null,{"children":["$","$7",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"Ocearo"}],["$","meta","1",{"name":"description","content":"Sailing made smarter"}],["$","link","2",{"rel":"manifest","href":"./manifest.json"}],["$","meta","3",{"name":"format-detection","content":"telephone=no"}],["$","meta","4",{"name":"mobile-web-app-capable","content":"yes"}],["$","meta","5",{"name":"apple-mobile-web-app-title","content":"Ocearo"}],["$","meta","6",{"name":"apple-mobile-web-app-status-bar-style","content":"black-translucent"}],["$","link","7",{"rel":"shortcut icon","href":"./favicon.ico"}],["$","link","8",{"rel":"icon","href":"./favicon.ico","type":"image/x-icon"}],["$","link","9",{"rel":"icon","href":"./favicon.svg","type":"image/svg+xml","sizes":"any"}],["$","link","10",{"rel":"icon","href":"./favicon-96x96.png","type":"image/png","sizes":"96x96"}],["$","link","11",{"rel":"apple-touch-icon","href":"./apple-touch-icon.png"}],["$","$Ld","12",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":"$@e","staleTime":"$a","varyParams":null},{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/ocearo-ui/_next/static/chunks/2y3d8zopa8r5r.css","precedence":"next"}],["$","script","script-0",{"src":"/ocearo-ui/_next/static/chunks/0h2qsuyze9ds1.js","async":true}]],["$","html",null,{"lang":"en","children":[["$","head",null,{"children":["$","meta",null,{"name":"description","content":"Sailing made smarter with Ocearo: touchscreen-first sailing dashboard with navigation, weather, and instrumentation."}]}],["$","body",null,{"className":"geistsans_fce11f57-module__b_bU6G__variable geistmono_ed61a5f9-module__6fgesq__variable antialiased","children":["$","$Lf",null,{"parallelRouterKey":"children","template":["$","$L10",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]]}]]}],"isPartial":"$@11","staleTime":"$a","varyParams":null}],"isUpgradeableISRFallback":false,"a":"$@12","rootVaryParams":null,"needsRuntimeRequest":"$@13"}
7
14
  4:{}
8
- 5:"$0:rsc:props:children:0:props:serverProvidedParams:params"
15
+ 5:"$0:data:0:rsc:props:children:0:props:serverProvidedParams:params"
9
16
  8:null
17
+ a:300
18
+ 13:true
19
+ a:C
20
+ 12:0
21
+ e:"$undefined"
22
+ 11:"$undefined"
23
+ 9:"$undefined"
package/__next._full.txt CHANGED
@@ -1,19 +1,19 @@
1
1
  1:"$Sreact.fragment"
2
- 2:I[39756,["/ocearo-ui/_next/static/chunks/1d4h-sglyo8ft.js","/ocearo-ui/_next/static/chunks/3jupxlga7r_do.js"],"default"]
3
- 3:I[37457,["/ocearo-ui/_next/static/chunks/1d4h-sglyo8ft.js","/ocearo-ui/_next/static/chunks/3jupxlga7r_do.js"],"default"]
4
- 4:I[47257,["/ocearo-ui/_next/static/chunks/1d4h-sglyo8ft.js","/ocearo-ui/_next/static/chunks/3jupxlga7r_do.js"],"ClientPageRoot"]
5
- 5:I[81694,["/ocearo-ui/_next/static/chunks/1d4h-sglyo8ft.js","/ocearo-ui/_next/static/chunks/3jupxlga7r_do.js","/ocearo-ui/_next/static/chunks/2baz6j1q913qw.js","/ocearo-ui/_next/static/chunks/009rlietvsaq2.js","/ocearo-ui/_next/static/chunks/08gn5_gd6amkq.js","/ocearo-ui/_next/static/chunks/0wzr1__so6xme.js","/ocearo-ui/_next/static/chunks/1bz0tcmhjmjgh.js"],"default"]
6
- 8:I[97367,["/ocearo-ui/_next/static/chunks/1d4h-sglyo8ft.js","/ocearo-ui/_next/static/chunks/3jupxlga7r_do.js"],"OutletBoundary"]
2
+ 2:I[39756,["/ocearo-ui/_next/static/chunks/0h2qsuyze9ds1.js"],"default"]
3
+ 3:I[37457,["/ocearo-ui/_next/static/chunks/0h2qsuyze9ds1.js"],"default"]
4
+ 4:I[47257,["/ocearo-ui/_next/static/chunks/0h2qsuyze9ds1.js"],"ClientPageRoot"]
5
+ 5:I[81694,["/ocearo-ui/_next/static/chunks/0h2qsuyze9ds1.js","/ocearo-ui/_next/static/chunks/31lugryguappr.js","/ocearo-ui/_next/static/chunks/32j_j396qs7be.js","/ocearo-ui/_next/static/chunks/2r_h93rt76vka.js","/ocearo-ui/_next/static/chunks/1gaiqhzy_0d7s.js","/ocearo-ui/_next/static/chunks/0kpstejrka-7k.js"],"default"]
6
+ 8:I[97367,["/ocearo-ui/_next/static/chunks/0h2qsuyze9ds1.js"],"OutletBoundary"]
7
7
  9:"$Sreact.suspense"
8
- b:I[97367,["/ocearo-ui/_next/static/chunks/1d4h-sglyo8ft.js","/ocearo-ui/_next/static/chunks/3jupxlga7r_do.js"],"ViewportBoundary"]
9
- d:I[97367,["/ocearo-ui/_next/static/chunks/1d4h-sglyo8ft.js","/ocearo-ui/_next/static/chunks/3jupxlga7r_do.js"],"MetadataBoundary"]
10
- f:I[68027,["/ocearo-ui/_next/static/chunks/1d4h-sglyo8ft.js","/ocearo-ui/_next/static/chunks/3jupxlga7r_do.js"],"default",1]
11
- :HL["/ocearo-ui/_next/static/chunks/1wv5e5qomv-7g.css","style"]
8
+ b:I[97367,["/ocearo-ui/_next/static/chunks/0h2qsuyze9ds1.js"],"ViewportBoundary"]
9
+ d:I[97367,["/ocearo-ui/_next/static/chunks/0h2qsuyze9ds1.js"],"MetadataBoundary"]
10
+ f:I[68027,["/ocearo-ui/_next/static/chunks/0h2qsuyze9ds1.js"],"default",1]
11
+ :HL["/ocearo-ui/_next/static/chunks/2y3d8zopa8r5r.css","style"]
12
12
  :HC["/",""]
13
- 0:{"P":null,"c":["",""],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/ocearo-ui/_next/static/chunks/1wv5e5qomv-7g.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/ocearo-ui/_next/static/chunks/1d4h-sglyo8ft.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/ocearo-ui/_next/static/chunks/3jupxlga7r_do.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":[["$","head",null,{"children":["$","meta",null,{"name":"description","content":"Sailing made smarter with Ocearo: touchscreen-first sailing dashboard with navigation, weather, and instrumentation."}]}],["$","body",null,{"className":"geistsans_fce11f57-module__b_bU6G__variable geistmono_ed61a5f9-module__6fgesq__variable antialiased","children":["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]]}],{"children":[["$","$1","c",{"children":[["$","$L4",null,{"Component":"$5","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@6","$@7"]}}],[["$","script","script-0",{"src":"/ocearo-ui/_next/static/chunks/2baz6j1q913qw.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/ocearo-ui/_next/static/chunks/009rlietvsaq2.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/ocearo-ui/_next/static/chunks/08gn5_gd6amkq.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/ocearo-ui/_next/static/chunks/0wzr1__so6xme.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/ocearo-ui/_next/static/chunks/1bz0tcmhjmjgh.js","async":true,"nonce":"$undefined"}]],["$","$L8",null,{"children":["$","$9",null,{"name":"Next.MetadataOutlet","children":"$@a"}]}]]}],{},null,false,null]},null,false,null],["$","$1","h",{"children":[null,["$","$Lb",null,{"children":"$Lc"}],["$","div",null,{"hidden":true,"children":["$","$Ld",null,{"children":["$","$9",null,{"name":"Next.Metadata","children":"$Le"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$f",[["$","link","0",{"rel":"stylesheet","href":"/ocearo-ui/_next/static/chunks/1wv5e5qomv-7g.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"hlKDicTyNGfd7dmHqo5m5"}
13
+ 0:{"P":null,"c":["",""],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{},"$undefined","$undefined",4608]},"$undefined","$undefined",4624],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/ocearo-ui/_next/static/chunks/2y3d8zopa8r5r.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/ocearo-ui/_next/static/chunks/0h2qsuyze9ds1.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":[["$","head",null,{"children":["$","meta",null,{"name":"description","content":"Sailing made smarter with Ocearo: touchscreen-first sailing dashboard with navigation, weather, and instrumentation."}]}],["$","body",null,{"className":"geistsans_fce11f57-module__b_bU6G__variable geistmono_ed61a5f9-module__6fgesq__variable antialiased","children":["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]]}],{"children":[["$","$1","c",{"children":[["$","$L4",null,{"Component":"$5","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@6","$@7"]}}],[["$","script","script-0",{"src":"/ocearo-ui/_next/static/chunks/31lugryguappr.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/ocearo-ui/_next/static/chunks/32j_j396qs7be.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/ocearo-ui/_next/static/chunks/2r_h93rt76vka.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/ocearo-ui/_next/static/chunks/1gaiqhzy_0d7s.js","async":true,"nonce":"$undefined"}],["$","script","script-4",{"src":"/ocearo-ui/_next/static/chunks/0kpstejrka-7k.js","async":true,"nonce":"$undefined"}]],["$","$L8",null,{"children":["$","$9",null,{"name":"Next.MetadataOutlet","children":"$@a"}]}]]}],{},null,false,null]},null,false,null],["$","$1","h",{"children":[null,["$","$Lb",null,{"children":"$Lc"}],["$","div",null,{"hidden":true,"children":["$","$Ld",null,{"children":["$","$9",null,{"name":"Next.Metadata","children":"$Le"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$f",[["$","link","0",{"rel":"stylesheet","href":"/ocearo-ui/_next/static/chunks/2y3d8zopa8r5r.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"r":"$undefined","s":"$undefined","a":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"baa0rBM5TIECz8JV_tLT6"}
14
14
  6:{}
15
15
  7:"$0:f:0:1:1:children:0:props:children:0:props:serverProvidedParams:params"
16
16
  c:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"}],["$","meta","2",{"name":"theme-color","content":"#4bbcd8","media":"(prefers-color-scheme: light)"}],["$","meta","3",{"name":"theme-color","content":"#0a2e3d","media":"(prefers-color-scheme: dark)"}]]
17
- 10:I[27201,["/ocearo-ui/_next/static/chunks/1d4h-sglyo8ft.js","/ocearo-ui/_next/static/chunks/3jupxlga7r_do.js"],"IconMark"]
17
+ 10:I[27201,["/ocearo-ui/_next/static/chunks/0h2qsuyze9ds1.js"],"IconMark"]
18
18
  a:null
19
19
  e:[["$","title","0",{"children":"Ocearo"}],["$","meta","1",{"name":"description","content":"Sailing made smarter"}],["$","link","2",{"rel":"manifest","href":"./manifest.json","crossOrigin":"$undefined"}],["$","meta","3",{"name":"format-detection","content":"telephone=no"}],["$","meta","4",{"name":"mobile-web-app-capable","content":"yes"}],["$","meta","5",{"name":"apple-mobile-web-app-title","content":"Ocearo"}],["$","meta","6",{"name":"apple-mobile-web-app-status-bar-style","content":"black-translucent"}],["$","link","7",{"rel":"shortcut icon","href":"./favicon.ico"}],["$","link","8",{"rel":"icon","href":"./favicon.ico","type":"image/x-icon"}],["$","link","9",{"rel":"icon","href":"./favicon.svg","type":"image/svg+xml","sizes":"any"}],["$","link","10",{"rel":"icon","href":"./favicon-96x96.png","type":"image/png","sizes":"96x96"}],["$","link","11",{"rel":"apple-touch-icon","href":"./apple-touch-icon.png"}],["$","$L10","12",{}]]
package/__next._tree.txt CHANGED
@@ -1,3 +1,3 @@
1
- :HL["/ocearo-ui/_next/static/chunks/1wv5e5qomv-7g.css","style"]
1
+ :HL["/ocearo-ui/_next/static/chunks/2y3d8zopa8r5r.css","style"]
2
2
  :HC["/",""]
3
- 0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}},"staleTime":300,"buildId":"hlKDicTyNGfd7dmHqo5m5"}
3
+ 0:{"tree":{"name":"","param":null,"prefetchHints":4176,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":4256,"slots":null}}},"staleTime":300,"buildId":"baa0rBM5TIECz8JV_tLT6"}
@@ -0,0 +1 @@
1
+ (globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,28843,t=>{"use strict";var e=t.i(43476),a=t.i(71645),s=t.i(46932),l=t.i(85709);t.s(["default",0,()=>{let t=(0,l.useSignalKPath)("steering.rudderAngle",0),i=(0,a.useMemo)(()=>180*t/Math.PI,[t]);return(0,e.jsx)("div",{style:{width:"400px"},className:"mx-auto p-4 transition-all duration-300 group",children:(0,e.jsxs)("div",{className:"w-full bg-hud-elevated h-1.5 rounded-full relative",children:[(()=>{let t=[];for(let a=-60;a<=60;a+=10){let s=a/120*100;t.push((0,e.jsx)("div",{className:"absolute h-1.5 w-[1px] bg-hud-muted opacity-20",style:{left:`${50+s}%`,bottom:"0"},children:(0,e.jsx)("span",{className:"absolute -top-5 left-1/2 -translate-x-1/2 text-caption font-semibold uppercase tracking-tighter text-hud-muted",children:Math.abs(a)})},a))}return t})(),(0,e.jsx)(s.motion.div,{initial:!1,animate:{width:`${Math.abs(i)/120*100}%`,left:i<0?`${50-Math.abs(i)/120*100}%`:"50%"},transition:{type:"spring",stiffness:300,damping:30},className:`absolute top-0 h-full rounded-full ${i<0?"bg-oRed":"bg-oGreen"} shadow-[0_0_15px_var(--hud-text-main)] shadow-opacity-20`}),(0,e.jsx)("div",{className:"absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-1 h-3 bg-hud-muted opacity-20 rounded-full z-10"})]})})}])},50226,function(t){t.n(t.i(28843))}]);
@@ -0,0 +1 @@
1
+ (globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,54784,e=>{"use strict";var t=e.i(43476),a=e.i(71645),s=e.i(46932),n=e.i(49721),i=e.i(68757);e.i(85269);var l=e.i(62588),o=e.i(77291),r=e.i(83646),c=e.i(85709),d=e.i(83402);let u=({title:e,icon:s,iconClass:i="text-oBlue",action:l,className:o="",children:r,...c})=>(0,t.jsxs)("div",{className:`tesla-card flex flex-col min-w-0 ${o}`,...c,children:[(e||s||l)&&(0,t.jsxs)("div",{className:"flex items-center gap-2 mb-3 shrink-0",children:[s&&(a.default.isValidElement(s)?(0,t.jsx)("span",{className:`${i} flex items-center`,children:s}):(0,t.jsx)(n.FontAwesomeIcon,{icon:s,className:`${i} text-sm opacity-80`})),e&&(0,t.jsx)("span",{className:"text-caption font-semibold uppercase tracking-widest text-hud-secondary truncate",children:e}),l&&(0,t.jsx)("div",{className:"ml-auto flex items-center gap-2",children:l})]}),r]}),m=({title:e,icon:a,iconColorClass:s="text-oBlue",hasData:n=!0,noDataMessage:i="No data available",children:l,className:o=""})=>{let r="function"==typeof a?a():a;return n?(0,t.jsx)(u,{title:e,icon:r,iconClass:s,className:`h-full overflow-hidden ${o}`,children:(0,t.jsx)("div",{className:"flex-1 flex flex-col justify-center min-h-0 overflow-hidden",children:l})}):(0,t.jsx)(u,{title:e,icon:r,iconClass:s,className:`h-full overflow-hidden ${o}`,children:(0,t.jsx)("div",{className:"flex-1 flex items-center justify-center min-h-0",children:(0,t.jsxs)("div",{className:"text-center",children:[(0,t.jsx)("div",{className:"text-hero font-medium text-hud-dim mb-2",children:"N/A"}),(0,t.jsx)("div",{className:"text-caption font-semibold uppercase text-hud-muted",children:i})]})})})};var x=e.i(22831);let h=[1,2,5,10,20],p=a.default.memo(()=>{let e,{t:s}=(0,x.useTranslation)(),{targets:u}=(0,l.useAIS)(),{statuses:p}=(0,o.default)({always:!0}),{scene:g}=(0,r.default)(),[f,v]=(0,a.useState)(5),y=d.default.get("debugMode"),w=(0,a.useCallback)(e=>{e.preventDefault(),v(t=>{let a=h.indexOf(t);return -1===a?t:e.deltaY<0&&a>0?h[a-1]:e.deltaY>0&&a<h.length-1?h[a+1]:t})},[]),b=(0,c.useSignalKPath)("navigation.position"),N=(0,c.useSignalKPath)("navigation.headingTrue"),j=(0,c.useSignalKPath)("navigation.headingMagnetic"),k=(0,c.useSignalKPath)("navigation.courseOverGroundTrue"),C=N??j??k??0,S=(0,a.useMemo)(()=>u.length&&b?u.filter(e=>e.distanceMeters&&e.distanceMeters<=1852*f).map(e=>{let t=e.distanceMeters/1852,a=((180*Math.atan2(e.sceneX,-e.sceneZ)/Math.PI+360)%360-180*C/Math.PI+360)%360,s=null===e.cpaMeters?null:e.cpaMeters/1852;return{id:e.mmsi,name:e.name||`MMSI ${e.mmsi}`,distance:Math.round(10*t)/10,bearing:Math.round(a),type:e.shipType||"unknown",cpa:null===s?null:Math.round(10*s)/10}}).sort((e,t)=>e.distance-t.distance).slice(0,10):[],[u,f,b,C]),M={giveWay:g.vesselDanger,yields:g.vesselYields,close:g.vesselClose},F=e=>M[p[e.id]]||"var(--hud-text-secondary, #8a9097)",W=e=>"giveWay"===p[e.id]||"yields"===p[e.id],$=y||S.length>0;return(0,t.jsxs)(m,{title:s("widgets.aisTacticalRadar"),icon:i.faTowerBroadcast,hasData:$,noDataMessage:s("widgets.signalLossAIS"),children:[(0,t.jsx)("div",{className:"absolute top-4 right-4 z-10",children:(0,t.jsxs)("select",{value:f,onChange:e=>v(Number(e.target.value)),className:"bg-hud-elevated text-hud-secondary text-caption font-semibold uppercase tracking-widest rounded-sm px-3 py-1 border border-hud shadow-soft focus:outline-none focus:ring-1 focus:ring-oBlue/50 hover:bg-hud-bg hover:text-hud-main transition-all duration-500",children:[(0,t.jsx)("option",{value:1,children:"1 NM"}),(0,t.jsx)("option",{value:2,children:"2 NM"}),(0,t.jsx)("option",{value:5,children:"5 NM"}),(0,t.jsx)("option",{value:10,children:"10 NM"}),(0,t.jsx)("option",{value:20,children:"20 NM"})]})}),(0,t.jsxs)("div",{className:"flex-1 relative min-h-0 bg-hud-bg rounded-sm overflow-hidden border border-hud shadow-inner group mt-2",onWheel:w,children:[(0,t.jsxs)("svg",{className:"w-full h-full p-2 transition-transform duration-700 group-hover:scale-[1.02]",viewBox:"0 0 200 200",children:[[1,2,3,4].map(e=>(0,t.jsx)("circle",{cx:"100",cy:"100",r:20*e,fill:"none",stroke:"var(--color-oBlue)",strokeOpacity:"0.28",strokeWidth:"0.5"},e)),(0,t.jsx)("line",{x1:"100",y1:"20",x2:"100",y2:"180",stroke:"var(--color-oBlue)",strokeOpacity:"0.16",strokeWidth:"0.5"}),(0,t.jsx)("line",{x1:"20",y1:"100",x2:"180",y2:"100",stroke:"var(--color-oBlue)",strokeOpacity:"0.16",strokeWidth:"0.5"}),(0,t.jsxs)("g",{children:[(0,t.jsx)("line",{x1:"100",y1:"100",x2:"100",y2:"20",stroke:"var(--color-oGreen)",strokeWidth:"1",strokeOpacity:"0.4"}),(0,t.jsx)("animateTransform",{attributeName:"transform",type:"rotate",from:"0 100 100",to:"360 100 100",dur:"3s",repeatCount:"indefinite"})]}),(e=[],S.map(t=>{let a=t.distance/f*80,s=100+a*Math.cos((t.bearing-90)*Math.PI/180),n=100+a*Math.sin((t.bearing-90)*Math.PI/180),i=n-5;for(;e.some(e=>26>Math.abs(e.x-s)&&6>Math.abs(e.y-i));)i+=6;return e.push({x:s,y:i}),{target:t,x:s,y:n,ly:i}})).map(({target:e,x:a,y:s,ly:n})=>{let i=W(e);return(0,t.jsxs)("g",{className:i?"animate-soft-pulse":"",children:[(0,t.jsx)("circle",{cx:a,cy:s,r:i?"3":"2",fill:F(e),className:"transition-all duration-1000"}),(0,t.jsx)("text",{x:a+5,y:n,fill:"currentColor",fontSize:"5",className:"font-black pointer-events-none uppercase tracking-tighter opacity-60 text-hud-main",children:e.name.substring(0,8)})]},e.id)}),(0,t.jsx)("polygon",{points:"100,95 105,105 100,102 95,105",fill:"var(--color-oBlue)",stroke:"currentColor",strokeWidth:"0.5",opacity:"0.8",className:"text-hud-main"})]}),(0,t.jsxs)("div",{className:"absolute top-2 left-2 text-caption text-hud-muted font-semibold pointer-events-none uppercase tracking-widest space-y-1",children:[(0,t.jsxs)("div",{className:"bg-hud-bg/40 px-1.5 py-0.5 rounded-sm",children:[s("widgets.range")," ",f," NM"]}),(0,t.jsxs)("div",{className:"bg-hud-bg/40 px-1.5 py-0.5 rounded-sm",children:[s("widgets.targets")," ",S.length]})]})]}),S.length>0&&(0,t.jsxs)("div",{className:"mt-4 space-y-2",children:[(0,t.jsx)("div",{className:"text-caption text-hud-muted font-semibold uppercase tracking-[0.2em] px-1",children:s("widgets.tacticalAnalysis")}),S.slice(0,2).map(e=>{let a;return(0,t.jsxs)("div",{className:"flex items-center justify-between text-caption tesla-card px-3 py-2 tesla-hover",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-3 min-w-0",children:[(0,t.jsx)(n.FontAwesomeIcon,{icon:30===(a=Number(e.type))||a>=70&&a<=89?i.faShip:i.faLocationDot,style:{color:F(e)},className:`text-xs opacity-80 ${W(e)?"animate-soft-pulse":""}`}),(0,t.jsx)("span",{className:"text-hud-main truncate font-black uppercase tracking-tight",children:e.name})]}),(0,t.jsxs)("div",{className:"flex space-x-4 text-hud-secondary font-black tracking-tighter",children:[(0,t.jsxs)("span",{className:"gliding-value",children:[e.distance.toFixed(1)," NM"]}),(0,t.jsxs)("span",{className:"gliding-value",style:{color:F(e)},children:["CPA: ",null===e.cpa?"--":e.cpa.toFixed(1)]})]})]},e.id)})]})]})});function g(){let{t:e}=(0,x.useTranslation)(),s=(0,c.useSignalKPaths)(["tanks.fuel.0.currentLevel","tanks.fuel.0.capacity","tanks.fuel.0.name","tanks.fuel.0.type","tanks.fuel.0.temperature","tanks.freshWater.0.currentLevel","tanks.freshWater.0.capacity","tanks.freshWater.0.name","tanks.freshWater.0.type","tanks.freshWater.0.temperature","tanks.wasteWater.0.currentLevel","tanks.wasteWater.0.capacity","tanks.wasteWater.0.name","tanks.wasteWater.0.type","tanks.wasteWater.0.temperature","tanks.oil.0.currentLevel","tanks.oil.0.capacity","tanks.oil.0.name","tanks.oil.0.type","tanks.oil.0.temperature"]),l=(0,a.useMemo)(()=>{let t=(e,t,a,n,i)=>({level:s[`tanks.${e}.0.currentLevel`]??null,capacity:s[`tanks.${e}.0.capacity`]??null,tankName:s[`tanks.${e}.0.name`],tankType:s[`tanks.${e}.0.type`],temperature:s[`tanks.${e}.0.temperature`],icon:a,color:n,bgColor:i,displayName:t}),a={fuel:t("fuel",e("widgets.tankFuel"),i.faGasPump,"text-oYellow","bg-oYellow"),freshWater:t("freshWater",e("widgets.tankFreshWater"),i.faTint,"text-oBlue","bg-oBlue"),wasteWater:t("wasteWater",e("widgets.tankWasteWater"),i.faToilet,"text-hud-secondary","bg-hud-elevated"),oil:t("oil",e("widgets.tankEngineOil"),i.faOilCan,"text-oYellow","bg-oYellow")};return{hasData:Object.values(a).some(e=>null!==e.level),...a}},[s,e]);return(0,t.jsx)(m,{title:e("widgets.storageSystems"),icon:i.faGasPump,hasData:l.hasData,noDataMessage:e("widgets.signalLossLevelSensors"),children:(0,t.jsx)("div",{className:"flex-1 flex flex-col justify-center min-h-0 space-y-4",children:Object.entries(l).filter(([e])=>"hasData"!==e).map(([e,a])=>{if(null===a.level||null===a.capacity)return null;let s=Math.round(100*a.level),i=Math.round(a.level*a.capacity),l="wasteWater"===e,o=l?a.level>.8:a.level<.1;return(0,t.jsxs)("div",{className:"space-y-2",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-3",children:[(0,t.jsx)("div",{className:"flex items-center justify-center w-6 h-6 bg-hud-elevated rounded-full",children:(0,t.jsx)(n.FontAwesomeIcon,{icon:a.icon,className:`${a.color} text-xs opacity-80`})}),(0,t.jsx)("span",{className:"text-hud-main text-caption font-semibold uppercase tracking-widest",children:a.displayName})]}),(0,t.jsxs)("div",{className:"flex items-center space-x-4",children:[(0,t.jsxs)("span",{className:"text-hud-secondary text-caption font-semibold opacity-60",children:[i,"/",a.capacity,"L"]}),(0,t.jsxs)("span",{className:`text-label font-semibold ${((e,t=!1)=>t?e>.8?"text-oRed":e>.6?"text-oYellow":"text-oGreen":e<.1?"text-oRed":e<.25?"text-oYellow":"text-oGreen")(a.level,l)} ${o?"animate-soft-pulse":""}`,children:[s,"%"]})]})]}),(0,t.jsx)("div",{className:"bg-hud-elevated rounded-full h-1.5 overflow-hidden shadow-inner",children:(0,t.jsx)("div",{className:`h-full transition-all duration-1000 ${l?a.level>.8?"bg-oRed":a.level>.6?"bg-oYellow":"bg-oGreen":a.level<.1?"bg-oRed":a.level<.25?"bg-oYellow":"bg-oGreen"}`,style:{width:`${s}%`}})})]},e)})})})}p.displayName="AISRadarWidget";var f=e.i(46991),v=e.i(83934),y=e.i(95430);function w(){let{t:e}=(0,x.useTranslation)(),s=d.default.get("debugMode"),{forecasts:l,isLoading:o,weatherApiAvailable:r,fetchWeatherForecast:c,getCurrentWeather:u}=(0,v.useWeather)(),h=(0,a.useMemo)(()=>{let e=u(),t=(0,f.convertTemperatureUnit)(e.temperature),a=null!==(0,f.finite)(e.humidity)?Math.round(100*e.humidity):null,n=null!==(0,f.convertPressure)(e.pressure)?Math.round(e.pressure/100):null,i=(0,f.convertSpeedUnit)(e.windSpeed),o=(0,f.toDegrees)(e.windDirection),r=e.description,c=e.source,d=null!==t||null!==a||null!==n||null!==i;if(!d&&!s)return{hasData:!1,dataSource:null};let m=t,x=a,h=n,p=i,g=o;!d&&s&&(m=15,x=65,h=1013,p=5.2,g=120,c="debug");let v="sunny";if(r){let e=r.toLowerCase();e.includes("rain")||e.includes("shower")?v="rainy":e.includes("snow")?v="snow":e.includes("partly")||e.includes("few clouds")||e.includes("scattered")?v="partly-cloudy":e.includes("cloud")||e.includes("overcast")?v="cloudy":(e.includes("clear")||e.includes("sun"))&&(v="sunny")}else h&&(v=h<1e3?x&&x>80?"rainy":"cloudy":h>1020?"sunny":"partly-cloudy");return{hasData:!0,dataSource:c,description:r,temperature:m,humidity:x,pressure:h,windSpeed:p,windDirection:g,condition:v,forecast:l}},[u,l,s]),p=e=>{switch(e){case"sunny":return i.faSun;case"partly-cloudy":default:return i.faCloudSun;case"cloudy":return i.faCloud;case"rainy":return i.faCloudRain;case"heavy-rain":return i.faCloudShowersHeavy;case"snow":return i.faSnowflake}},g=e=>{switch(e){case"sunny":return"text-oYellow";case"partly-cloudy":case"rainy":case"heavy-rain":default:return"text-oBlue";case"cloudy":return"text-hud-muted";case"snow":return"text-hud-main"}},w=e=>null===(0,f.finite)(e)?"":["N","NNE","NE","ENE","E","ESE","SE","SSE","S","SSW","SW","WSW","W","WNW","NW","NNW"][(Math.round(e/22.5)%16+16)%16];return(0,t.jsxs)(m,{title:e("widgets.meteorologicalData"),icon:p(h.condition),hasData:h.hasData,noDataMessage:o?e("widgets.acquiringSatellite"):e("widgets.signalLossWeather"),children:[(0,t.jsxs)("div",{className:"absolute top-4 right-4 z-10 flex items-center space-x-3",children:[h.dataSource&&(0,t.jsx)("span",{className:`text-caption px-2 py-0.5 rounded-sm uppercase font-semibold tracking-widest text-hud-main shadow-soft ${"sensors"===h.dataSource?"bg-oGreen/40 border border-oGreen/30":"forecast"===h.dataSource?"bg-oBlue/40 border border-oBlue/30":"bg-hud-elevated border border-hud"}`,children:(()=>{switch(h.dataSource){case"sensors":return e("widgets.dataSourceLive");case"forecast":return e("widgets.dataSourceForecast");case"debug":return e("widgets.dataSourceDemo");default:return""}})()}),!1!==r&&(0,t.jsx)("button",{onClick:c,disabled:o,className:"w-7 h-7 rounded-full bg-hud-elevated flex items-center justify-center text-hud-secondary hover:text-hud-main transition-all duration-500 tesla-hover",title:e("widgets.updateForecast"),children:(0,t.jsx)(n.FontAwesomeIcon,{icon:i.faSync,className:`text-xs ${o?"animate-spin":""}`})})]}),(0,t.jsxs)("div",{className:"flex-1 flex flex-col min-h-0",children:[(0,t.jsx)("div",{className:"flex-1 flex flex-col justify-center",children:(0,t.jsxs)("div",{className:"text-center mb-6 group",children:[(0,t.jsx)("div",{className:`text-hero mb-4 transition-transform duration-700 group-hover:scale-110 ${g(h.condition)}`,children:(0,t.jsx)(n.FontAwesomeIcon,{icon:p(h.condition),className:"sunny"!==h.condition?"animate-soft-pulse":""})}),(0,t.jsx)("div",{className:"text-hero font-medium text-hud-main leading-none gliding-value tracking-tight",children:null!==h.temperature?`${h.temperature}${(0,f.getTemperatureUnitLabel)()}`:e("common.na")}),(0,t.jsx)("div",{className:"text-caption font-semibold text-hud-secondary uppercase tracking-[0.3em] mt-3 opacity-60",children:h.description||(t=>{switch(t){case"sunny":return e("widgets.clearSkies");case"partly-cloudy":return e("widgets.partlyCloudy");case"cloudy":return e("widgets.overcast");case"rainy":return e("widgets.lightRain");case"heavy-rain":return e("widgets.heavyRain");case"snow":return e("widgets.snow");default:return e("widgets.variable")}})(h.condition)})]})}),h.forecast&&h.forecast.length>1&&(0,t.jsx)("div",{className:"space-y-2 shrink-0",children:h.forecast.slice(1,4).map((e,a)=>{let s=new Date(e.date?e.date:(0,y.vesselNow)().getTime()+(a+1)*864e5),l=isNaN(s.getTime())?e.time||`+${a+1}d`:s.toLocaleDateString("en-GB",{weekday:"short"}),o=e.description?e.description.toLowerCase().includes("rain")?"rainy":e.description.toLowerCase().includes("cloud")?"cloudy":e.description.toLowerCase().includes("snow")?"snow":"sunny":"partly-cloudy",r=null!==(0,f.finite)(e.wind?.speed)?Math.round(10*e.wind.speed)/10:null,c=w(e.wind?.direction)||null;return(0,t.jsxs)("div",{className:"flex items-center justify-between py-1.5 border-b border-hud/30 last:border-0",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-3",children:[(0,t.jsx)("span",{className:"text-hud-muted text-caption font-semibold uppercase tracking-widest w-8",children:l}),(0,t.jsx)(n.FontAwesomeIcon,{icon:p(o),className:`text-sm w-5 ${g(o)}`}),(0,t.jsx)("span",{className:"text-hud-main text-label font-semibold gliding-value w-8",children:null!==e.temperature?`${Math.round(e.temperature)}\xb0`:"--"})]}),(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)(n.FontAwesomeIcon,{icon:i.faWind,className:"text-oBlue text-xs opacity-50"}),(0,t.jsx)("span",{className:"text-hud-main text-caption font-semibold gliding-value",children:null!==r?`${r} ${(0,f.getSpeedUnitLabel)()}`:"--"}),c&&(0,t.jsx)("span",{className:"text-hud-muted text-caption font-semibold tracking-widest opacity-60",children:c})]})]},a)})}),(0,t.jsxs)("div",{className:"flex items-center justify-between shrink-0 mt-2",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)(n.FontAwesomeIcon,{icon:i.faWind,className:"text-oBlue text-sm opacity-50"}),(0,t.jsx)("span",{className:"text-hud-secondary text-caption uppercase font-semibold tracking-widest",children:e("widgets.now")})]}),(0,t.jsxs)("div",{className:"text-right",children:[(0,t.jsx)("span",{className:"text-hud-main text-label font-semibold gliding-value tracking-tight",children:null!==h.windSpeed?`${h.windSpeed} ${(0,f.getSpeedUnitLabel)()}`:e("common.na")}),(0,t.jsx)("span",{className:"text-hud-muted text-caption uppercase font-semibold tracking-widest ml-2 opacity-60",children:null!==h.windDirection?w(h.windDirection):""})]})]})]})]})}var b=e.i(66799);let N=(0,a.createContext)(),j=({children:e})=>{let[s,n]=(0,a.useState)({}),[i,l]=(0,a.useState)({}),[o,r]=(0,a.useState)({}),[c,d]=(0,a.useState)(!1),[u,m]=(0,a.useState)(null),[x,h]=(0,a.useState)(null),[p,g]=(0,a.useState)(null),[f,v]=(0,a.useState)(!1),[y,w]=(0,a.useState)(null),j=(0,a.useRef)(null),k=(0,a.useRef)(null),C=(0,a.useCallback)(async()=>{d(!0),m(null);try{let[e,t,a]=await Promise.all([b.default.getRoutes().catch(e=>("NetworkError"!==e.name&&console.error("NavigationContext: Failed to fetch routes:",e),{})),b.default.getWaypoints().catch(e=>("NetworkError"!==e.name&&console.error("NavigationContext: Failed to fetch waypoints:",e),{})),b.default.getCharts().catch(e=>("NetworkError"!==e.name&&console.error("NavigationContext: Failed to fetch charts:",e),{}))]);n(e||{}),l(t||{}),r(a||{})}catch(e){"NetworkError"!==e.name&&(console.error("NavigationContext: Failed to fetch resources:",e),m(e.message))}finally{d(!1)}},[]),S=(0,a.useCallback)(async()=>{try{let e=await b.default.getRoutes();return n(e||{}),e}catch(e){throw console.error("NavigationContext: Failed to fetch routes:",e),e}},[]),M=(0,a.useCallback)(async()=>{try{let e=await b.default.getWaypoints();return l(e||{}),e}catch(e){throw console.error("NavigationContext: Failed to fetch waypoints:",e),e}},[]),F=(0,a.useCallback)(async()=>{try{let[e,t]=await Promise.all([b.default.getCourse().catch(()=>null),b.default.getCourseCalculations().catch(()=>null)]);h(e),g(t)}catch(e){console.error("NavigationContext: Failed to fetch course:",e),w(e.message)}},[]),W=(0,a.useCallback)(async e=>{v(!0),w(null);try{await b.default.setDestinationWaypoint(e),await F()}catch(e){throw console.error("NavigationContext: Failed to set waypoint destination:",e),w(e.message),e}finally{v(!1)}},[F]),$=(0,a.useCallback)(async(e,t)=>{v(!0),w(null);try{await b.default.setDestinationPosition(e,t),await F()}catch(e){throw console.error("NavigationContext: Failed to set position destination:",e),w(e.message),e}finally{v(!1)}},[F]),T=(0,a.useCallback)(async(e,t=0,a=!1)=>{v(!0),w(null);try{await b.default.activateRoute(e,t,a),await F()}catch(e){throw console.error("NavigationContext: Failed to activate route:",e),w(e.message),e}finally{v(!1)}},[F]),D=(0,a.useCallback)(async()=>{v(!0),w(null);try{await b.default.clearCourse(),h(null),g(null)}catch(e){throw console.error("NavigationContext: Failed to clear course:",e),w(e.message),e}finally{v(!1)}},[]),A=(0,a.useCallback)(async()=>{try{await b.default.nextWaypoint(),await F()}catch(e){throw console.error("NavigationContext: Failed to go to next waypoint:",e),e}},[F]),I=(0,a.useCallback)(async()=>{try{await b.default.previousWaypoint(),await F()}catch(e){throw console.error("NavigationContext: Failed to go to previous waypoint:",e),e}},[F]),R=(0,a.useCallback)(async(e,t,a,s="")=>{try{let n=b.default.generateUUID(),i=b.default.createWaypointFeature(t,e,a,s);return await b.default.saveWaypoint(n,i),await M(),n}catch(e){throw console.error("NavigationContext: Failed to create waypoint:",e),e}},[M]),P=(0,a.useCallback)(async e=>{try{await b.default.deleteWaypoint(e),await M()}catch(e){throw console.error("NavigationContext: Failed to delete waypoint:",e),e}},[M]),G=(0,a.useCallback)(async(e,t,a="")=>{try{let s=b.default.generateUUID(),n=e.map(e=>[e.longitude,e.latitude]),i=b.default.createRouteFeature(n,t,a);return await b.default.saveRoute(s,i),await S(),s}catch(e){throw console.error("NavigationContext: Failed to create route:",e),e}},[S]),L=(0,a.useCallback)(async e=>{try{await b.default.deleteRoute(e),await S()}catch(e){throw console.error("NavigationContext: Failed to delete route:",e),e}},[S]),E=(0,a.useCallback)(e=>{let t=s[e];return t?b.default.parseRouteCoordinates(t):[]},[s]),B=(0,a.useCallback)(e=>{let t=i[e];return t?b.default.parseWaypointPosition(t):null},[i]),O=(0,a.useCallback)(()=>Object.entries(s).map(([e,t])=>({id:e,name:t.name||"Unnamed Route",description:t.description||"",pointCount:t.feature?.geometry?.coordinates?.length||0})),[s]),Y=(0,a.useCallback)(()=>Object.entries(i).map(([e,t])=>{let a=b.default.parseWaypointPosition(t);return{id:e,name:t.name||"Unnamed Waypoint",description:t.description||"",latitude:a?.latitude,longitude:a?.longitude}}),[i]),U=(0,a.useCallback)(()=>x?.nextPoint?.position!=null||x?.activeRoute?.href!=null,[x]),K=(0,a.useCallback)(()=>x?{position:x.nextPoint?.position,waypointHref:x.nextPoint?.href,routeHref:x.activeRoute?.href,pointIndex:x.activeRoute?.pointIndex,isRoute:!!x.activeRoute?.href,arrivalCircle:x.arrivalCircle}:null,[x]);(0,a.useEffect)(()=>(C(),F(),j.current=setInterval(()=>{F()},5e3),k.current=setInterval(()=>{C()},3e5),()=>{j.current&&clearInterval(j.current),k.current&&clearInterval(k.current)}),[C,F]);let z=(0,a.useMemo)(()=>({routes:s,waypoints:i,charts:o,isLoadingResources:c,resourcesError:u,activeCourse:x,courseCalculations:p,isLoadingCourse:f,courseError:y,fetchResources:C,fetchRoutes:S,fetchWaypoints:M,fetchCourse:F,navigateToWaypoint:W,navigateToPosition:$,activateRoute:T,clearCourse:D,goToNextWaypoint:A,goToPreviousWaypoint:I,createWaypoint:R,deleteWaypoint:P,createRoute:G,deleteRoute:L,getRouteCoordinates:E,getWaypointPosition:B,getRoutesList:O,getWaypointsList:Y,hasActiveDestination:U,getDestinationInfo:K}),[s,i,o,c,u,x,p,f,y,C,S,M,F,W,$,T,D,A,I,R,P,G,L,E,B,O,Y,U,K]);return(0,t.jsx)(N.Provider,{value:z,children:e})};function k(){var e,s;let{t:l}=(0,x.useTranslation)(),{activeCourse:o,courseCalculations:r,waypoints:u,routes:h,isLoadingCourse:p,navigateToWaypoint:g,activateRoute:v,clearCourse:y,goToNextWaypoint:w,goToPreviousWaypoint:b,getWaypointsList:j,getRoutesList:k,hasActiveDestination:C}=(()=>{let e=(0,a.useContext)(N);if(!e)throw Error("useNavigation must be used within a NavigationContextProvider");return e})(),S=d.default.get("debugMode"),M=(0,c.useSignalKPath)("navigation.courseGreatCircle.nextPoint.distance"),F=(0,c.useSignalKPath)("navigation.courseGreatCircle.nextPoint.bearingTrue"),W=(0,c.useSignalKPath)("navigation.courseGreatCircle.crossTrackError"),$=(0,c.useSignalKPath)("navigation.courseGreatCircle.velocityMadeGood"),T=(0,c.useSignalKPath)("steering.autopilot.state"),D=(0,c.useSignalKPath)("steering.autopilot.mode"),A=(0,c.useSignalKPath)("steering.autopilot.target.headingTrue"),[I,R]=(0,a.useState)(!1),[P,G]=(0,a.useState)(!1),L=a.default.useMemo(()=>{let e=r||{},t=M??e.distance??null,a=F??e.bearingTrue??null;return null!==t||null!==a||S?{hasData:!0,distance:t??(S?5e3:null),bearingTrue:a??(S?.52:null),bearingMagnetic:e.bearingMagnetic??null,crossTrackError:W??e.crossTrackError??(S?50:null),velocityMadeGood:$??e.velocityMadeGood??(S?5.5:null),timeToGo:e.timeToGo??null,estimatedTimeOfArrival:e.estimatedTimeOfArrival??null,calcMethod:e.calcMethod}:{hasData:!1}},[r,M,F,W,$,S]),E=async e=>{try{await g(e),R(!1)}catch(e){console.error("Failed to navigate to waypoint:",e)}},B=async e=>{try{await v(e),G(!1)}catch(e){console.error("Failed to activate route:",e)}},O=j(),Y=k(),U=C();return(0,t.jsxs)(m,{title:l("widgets.course"),icon:i.faRoute,iconColorClass:"text-oGreen",hasData:L.hasData||U||O.length>0||Y.length>0,noDataMessage:l("widgets.noCourseData"),children:[(0,t.jsxs)("div",{className:"absolute top-4 right-4 z-10 flex space-x-2",children:[(0,t.jsx)("button",{onClick:()=>{R(!I),G(!1)},className:`w-8 h-8 rounded-full flex items-center justify-center transition-all duration-500 shadow-soft ${I?"bg-oGreen text-hud-main":"bg-hud-elevated text-hud-secondary tesla-hover"}`,title:l("widgets.waypoints"),children:(0,t.jsx)(n.FontAwesomeIcon,{icon:i.faLocationDot,className:"text-xs"})}),(0,t.jsx)("button",{onClick:()=>{G(!P),R(!1)},className:`w-8 h-8 rounded-full flex items-center justify-center transition-all duration-500 shadow-soft ${P?"bg-oGreen text-hud-main":"bg-hud-elevated text-hud-secondary tesla-hover"}`,title:l("widgets.routes"),children:(0,t.jsx)(n.FontAwesomeIcon,{icon:i.faList,className:"text-xs"})})]}),(0,t.jsxs)("div",{className:"flex-1 flex flex-col min-h-0",children:[I&&(0,t.jsxs)("div",{className:"mb-4 max-h-40 overflow-y-auto tesla-card space-y-1",children:[(0,t.jsx)("div",{className:"text-caption text-hud-muted mb-2 uppercase font-semibold tracking-[0.2em] px-2 pt-1",children:l("widgets.storedWaypoints")}),0===O.length?(0,t.jsx)("div",{className:"text-caption text-hud-secondary italic px-2 py-4 text-center font-semibold uppercase",children:l("widgets.noActiveNodes")}):O.map(e=>(0,t.jsxs)("button",{onClick:()=>E(e.id),className:"w-full text-left p-2 tesla-hover rounded-sm text-caption text-hud-main flex items-center group font-semibold uppercase tracking-tight",children:[(0,t.jsx)(n.FontAwesomeIcon,{icon:i.faLocationDot,className:"text-oYellow mr-3 text-xs opacity-50 group-hover:opacity-100"}),(0,t.jsx)("span",{className:"truncate",children:e.name})]},e.id))]}),P&&(0,t.jsxs)("div",{className:"mb-4 max-h-40 overflow-y-auto tesla-card space-y-1",children:[(0,t.jsx)("div",{className:"text-caption text-hud-muted mb-2 uppercase font-semibold tracking-[0.2em] px-2 pt-1",children:l("widgets.missionRoutes")}),0===Y.length?(0,t.jsx)("div",{className:"text-caption text-hud-secondary italic px-2 py-4 text-center font-semibold uppercase",children:l("widgets.noRoutesLoaded")}):Y.map(e=>(0,t.jsxs)("button",{onClick:()=>B(e.id),className:"w-full text-left p-2 tesla-hover rounded-sm text-caption text-hud-main flex items-center justify-between group font-semibold uppercase tracking-tight",children:[(0,t.jsxs)("div",{className:"flex items-center",children:[(0,t.jsx)(n.FontAwesomeIcon,{icon:i.faRoute,className:"text-oGreen mr-3 text-xs opacity-50 group-hover:opacity-100"}),(0,t.jsx)("span",{className:"truncate",children:e.name})]}),(0,t.jsxs)("span",{className:"text-caption text-hud-muted font-semibold",children:[e.pointCount," pts"]})]},e.id))]}),U&&(0,t.jsxs)("div",{className:"tesla-card bg-oYellow/5 mb-6 border-l-2 border-oYellow/30 animate-soft-pulse",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between mb-2",children:[(0,t.jsxs)("div",{className:"flex items-center space-x-3",children:[(0,t.jsx)(n.FontAwesomeIcon,{icon:i.faCrosshairs,className:"text-oYellow text-sm"}),(0,t.jsx)("span",{className:"text-hud-main text-caption font-semibold uppercase tracking-widest truncate",children:(()=>{if(!o)return l("widgets.noDestination");if(o.activeRoute?.href){let e=h[o.activeRoute.href.split("/").pop()],t=o.activeRoute.pointIndex||0;return e?.name?`${e.name} (${t+1})`:l("widgets.activeRoute")}if(o.nextPoint?.href){let e=u[o.nextPoint.href.split("/").pop()];return e?.name||l("widgets.waypoint")}if(o.nextPoint?.position){let e=o.nextPoint.position;return`${e.latitude?.toFixed(4)}\xb0, ${e.longitude?.toFixed(4)}\xb0`}return l("widgets.noDestination")})()})]}),(0,t.jsx)("button",{onClick:y,className:"text-hud-secondary hover:text-oRed transition-colors p-1",title:l("widgets.abortNavigation"),children:(0,t.jsx)(n.FontAwesomeIcon,{icon:i.faTimes,className:"text-sm"})})]}),o?.activeRoute?.href&&(0,t.jsxs)("div",{className:"flex justify-center space-x-3 mt-4",children:[(0,t.jsxs)("button",{onClick:b,className:"bg-hud-elevated hover:bg-hud-bg text-hud-main px-4 py-1.5 rounded text-caption font-semibold uppercase tracking-widest transition-all shadow-soft",children:[(0,t.jsx)(n.FontAwesomeIcon,{icon:i.faArrowLeft,className:"mr-2"}),l("widgets.previous")]}),(0,t.jsxs)("button",{onClick:w,className:"bg-oBlue hover:bg-oBlue/80 text-hud-main px-4 py-1.5 rounded text-caption font-semibold uppercase tracking-widest transition-all shadow-lg shadow-oBlue/20",children:[l("widgets.proceed"),(0,t.jsx)(n.FontAwesomeIcon,{icon:i.faArrowRight,className:"ml-2"})]})]})]}),T&&(0,t.jsxs)("div",{className:`tesla-card px-3 py-2 mb-3 flex items-center justify-between ${"enabled"===T?"border-l-2 border-oGreen/30":""}`,children:[(0,t.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,t.jsx)(n.FontAwesomeIcon,{icon:i.faRobot,className:`text-xs ${"enabled"===T?"text-oGreen":"text-hud-muted"}`}),(0,t.jsx)("span",{className:`text-caption font-semibold uppercase capitalize gliding-value ${"enabled"===T?"text-oGreen":"text-hud-secondary"}`,children:T})]}),(0,t.jsxs)("div",{className:"flex items-center space-x-4 text-caption font-semibold uppercase",children:[(0,t.jsx)("span",{className:"text-hud-main capitalize gliding-value",children:D||"—"}),(0,t.jsx)("span",{className:"text-oYellow gliding-value",children:null!==A?`${Math.round((0,f.toDegrees)(A))}\xb0`:"—"})]})]}),L.hasData&&(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,t.jsxs)("div",{className:"tesla-card text-center tesla-hover ",children:[(0,t.jsx)(n.FontAwesomeIcon,{icon:i.faRuler,className:"text-oGreen text-xs mb-2 opacity-50"}),(0,t.jsx)("div",{className:"text-hud-main text-value font-semibold gliding-value tracking-tight",children:null==(e=L.distance)?l("common.na"):e<1e3?`${Math.round(e)} m`:`${(0,f.convertDistanceUnit)(e)} ${(0,f.getDistanceUnitLabel)()}`}),(0,t.jsx)("div",{className:"text-hud-secondary text-caption uppercase font-semibold mt-2 tracking-[0.2em]",children:l("widgets.distance")})]}),(0,t.jsxs)("div",{className:"tesla-card text-center tesla-hover ",children:[(0,t.jsx)(n.FontAwesomeIcon,{icon:i.faCompass,className:"text-oBlue text-xs mb-2 opacity-50"}),(0,t.jsx)("div",{className:"text-hud-main text-value font-semibold gliding-value tracking-tight",children:null!==L.bearingTrue?`${Math.round((0,f.toDegrees)(L.bearingTrue))}\xb0`:l("common.na")}),(0,t.jsx)("div",{className:"text-hud-secondary text-caption uppercase font-semibold mt-2 tracking-[0.2em]",children:l("widgets.bearing")})]}),(0,t.jsxs)("div",{className:"tesla-card text-center tesla-hover ",children:[(0,t.jsx)(n.FontAwesomeIcon,{icon:i.faArrowRight,className:`${L.crossTrackError>50||L.crossTrackError<-50?"text-oRed animate-soft-pulse":"text-oGreen opacity-50"} text-xs mb-2`}),(0,t.jsx)("div",{className:"text-hud-main text-value font-semibold gliding-value tracking-tight",children:null!==L.crossTrackError?`${Math.abs(Math.round(L.crossTrackError))}m`:l("common.na")}),(0,t.jsx)("div",{className:"text-hud-secondary text-caption uppercase font-semibold mt-2 tracking-[0.2em]",children:"XTE"})]}),(0,t.jsxs)("div",{className:"tesla-card text-center tesla-hover ",children:[(0,t.jsx)(n.FontAwesomeIcon,{icon:i.faPlay,className:"text-oGreen text-xs mb-2 opacity-50"}),(0,t.jsxs)("div",{className:"text-hud-main text-value font-semibold gliding-value tracking-tight",children:[null!==L.velocityMadeGood?`${(0,f.convertSpeedUnit)(L.velocityMadeGood)}`:l("common.na"),(0,t.jsx)("span",{className:"text-caption text-hud-secondary ml-1",children:(0,f.getSpeedUnitLabel)()})]}),(0,t.jsx)("div",{className:"text-hud-secondary text-caption uppercase font-semibold mt-2 tracking-[0.2em]",children:"VMG"})]}),null!==L.timeToGo&&(0,t.jsxs)("div",{className:"tesla-card text-center tesla-hover ",children:[(0,t.jsx)(n.FontAwesomeIcon,{icon:i.faClock,className:"text-oGreen text-xs mb-2 opacity-50"}),(0,t.jsx)("div",{className:"text-hud-main text-value font-semibold gliding-value tracking-tight",children:(e=>{if(null==e)return l("common.na");let t=Math.floor(e/3600),a=Math.floor(e%3600/60);return t>0?`${t}h ${a}m`:`${a}m`})(L.timeToGo)}),(0,t.jsx)("div",{className:"text-hud-secondary text-caption uppercase font-semibold mt-2 tracking-[0.2em]",children:"TTG"})]}),L.estimatedTimeOfArrival&&(0,t.jsxs)("div",{className:"tesla-card text-center tesla-hover ",children:[(0,t.jsx)(n.FontAwesomeIcon,{icon:i.faClock,className:"text-oYellow text-xs mb-2 opacity-50"}),(0,t.jsx)("div",{className:"text-hud-main text-value font-semibold gliding-value tracking-tight",children:(s=L.estimatedTimeOfArrival)?new Date(s).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}):l("common.na")}),(0,t.jsx)("div",{className:"text-hud-secondary text-caption uppercase font-semibold mt-2 tracking-[0.2em]",children:"ETA"})]})]})]})]})}e.i(67561);let C=a.default.memo(({children:e,widgetName:a,className:l="",fullscreenWidget:o,toggleFullscreen:r})=>(0,t.jsxs)(s.motion.div,{layout:!0,variants:{hidden:{opacity:0,y:20},show:{opacity:1,y:0}},transition:{duration:.5,ease:[.4,0,.2,1]},className:`relative group ${l}`,children:[e,(0,t.jsx)("button",{onClick:()=>r(a),className:"absolute top-3 right-3 opacity-0 group-hover:opacity-100 transition-all duration-500 bg-hud-bg backdrop-blur-md hover:bg-hud-elevated rounded-full w-8 h-8 flex items-center justify-center text-hud-muted hover:text-hud-main z-10 shadow-soft",title:o===a?"Exit fullscreen":"Fullscreen",children:(0,t.jsx)(n.FontAwesomeIcon,{icon:o===a?i.faCompress:i.faExpand,className:"text-xs"})})]}));C.displayName="WidgetWrapper";let S={hidden:{opacity:0},show:{opacity:1,transition:{staggerChildren:.05}}};e.s(["default",0,function(){let[e,n]=(0,a.useState)(null),i=a.default.useCallback(e=>{n(t=>t===e?null:e)},[]),l=(a,s,n="")=>(0,t.jsx)(C,{widgetName:a,fullscreenWidget:e,toggleFullscreen:i,className:e&&e!==a?"hidden":e===a?"w-full h-full":n,children:s});return(0,t.jsx)("div",{className:"flex flex-col h-full bg-rightPaneBg overflow-hidden",children:(0,t.jsx)("div",{className:"flex-1 p-4 min-h-0 overflow-auto scrollbar-hide",children:(0,t.jsxs)(s.motion.div,{variants:S,initial:"hidden",animate:"show",className:e?"flex h-full":"card-grid",children:[l("aisradar",(0,t.jsx)(p,{}),"sm:col-span-2 sm:row-span-2"),l("weather",(0,t.jsx)(w,{})),l("course",(0,t.jsx)(j,{children:(0,t.jsx)(k,{})})),l("tanks",(0,t.jsx)(g,{}))]})})})}],54784)},25294,function(e){e.n(e.i(54784))}]);