signalk-tides 2.1.2 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/dependabot.yml +7 -0
- package/.github/workflows/ci.yml +3 -3
- package/.github/workflows/release.yml +1 -1
- package/.oxlintrc.json +8 -0
- package/README.md +6 -0
- package/dist/index.js +28 -1
- package/dist/plotter-extension.js +35 -0
- package/package.json +21 -19
- package/public/assets/main-68xVY5Ec.js +1 -0
- package/public/assets/maplibre-CcFST_Y2.js +814 -0
- package/public/assets/maplibre-gl-worker-CRiIRpYb.js +15 -0
- package/public/assets/useUnitPreferences-BoZImJ0r.css +1 -0
- package/public/assets/useUnitPreferences-lYEQOWom.js +12 -0
- package/public/assets/widget-DyJmPMCf.js +1 -0
- package/public/index.html +3 -2
- package/public/widget.html +17 -0
- package/tsconfig.node.json +1 -0
- package/tsconfig.test.json +1 -0
- package/vitest.config.browser.ts +5 -0
- package/widget.html +15 -0
- package/public/assets/index-BYJ4l3Sy.js +0 -12
- package/public/assets/index-CityHj4f.css +0 -1
- package/public/assets/maplibre-gl-BOnKX8T3.js +0 -748
package/.github/dependabot.yml
CHANGED
|
@@ -5,6 +5,13 @@ updates:
|
|
|
5
5
|
schedule:
|
|
6
6
|
interval: "weekly"
|
|
7
7
|
open-pull-requests-limit: 100
|
|
8
|
+
# vitest and every @vitest/* package pin each other's exact version, so a
|
|
9
|
+
# bump to one alone fails `npm ci` with ERESOLVE before a test runs.
|
|
10
|
+
groups:
|
|
11
|
+
vitest:
|
|
12
|
+
patterns:
|
|
13
|
+
- "vitest"
|
|
14
|
+
- "@vitest/*"
|
|
8
15
|
- package-ecosystem: "github-actions"
|
|
9
16
|
directory: "/"
|
|
10
17
|
schedule:
|
package/.github/workflows/ci.yml
CHANGED
|
@@ -12,7 +12,7 @@ jobs:
|
|
|
12
12
|
uses: actions/checkout@v7
|
|
13
13
|
|
|
14
14
|
- name: Setup Node.js
|
|
15
|
-
uses: actions/setup-node@
|
|
15
|
+
uses: actions/setup-node@v7
|
|
16
16
|
|
|
17
17
|
- name: Install dependencies
|
|
18
18
|
run: npm install
|
|
@@ -30,7 +30,7 @@ jobs:
|
|
|
30
30
|
uses: actions/checkout@v7
|
|
31
31
|
|
|
32
32
|
- name: Setup Node.js
|
|
33
|
-
uses: actions/setup-node@
|
|
33
|
+
uses: actions/setup-node@v7
|
|
34
34
|
|
|
35
35
|
- name: Install dependencies
|
|
36
36
|
run: npm install
|
|
@@ -53,7 +53,7 @@ jobs:
|
|
|
53
53
|
steps:
|
|
54
54
|
- uses: actions/checkout@v7
|
|
55
55
|
|
|
56
|
-
- uses: actions/setup-node@
|
|
56
|
+
- uses: actions/setup-node@v7
|
|
57
57
|
|
|
58
58
|
- name: Install modules
|
|
59
59
|
run: npm install
|
package/.oxlintrc.json
ADDED
package/README.md
CHANGED
|
@@ -59,6 +59,12 @@ $ curl http://localhost:3000/signalk/v2/api/resources/tides
|
|
|
59
59
|
}
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
+
### Chart plotter widget
|
|
63
|
+
|
|
64
|
+
The plugin offers a tide widget to chart plotters that support Signal K [Plotter Extensions](https://github.com/SignalK/freeboard-sk/blob/master/docs/api/plotter-extensions-api.md), such as [Freeboard-SK](https://github.com/SignalK/freeboard-sk). It shows the tide at the vessel's position: the level now with its trend, and the next high or low water, on the predicted curve. Tapping it opens the full Tides app in a panel beside the chart.
|
|
65
|
+
|
|
66
|
+
In Freeboard-SK, press and hold an empty spot in a corner of the chart (or right-click it and choose *Add widget here*) and pick *Tides*.
|
|
67
|
+
|
|
62
68
|
## License
|
|
63
69
|
|
|
64
70
|
This plugin is a fork of the [signalk-tides-api](https://github.com/joabakk/signalk-tides-api) plugin (which is no longer working) and is licensed under the [Apache License 2.0](LICENSE). Kudos to @joabakk and @sbender9 for the original work.
|
package/dist/index.js
CHANGED
|
@@ -18,6 +18,7 @@ import { findStation, nearestStation, stationsNear } from "neaps";
|
|
|
18
18
|
import { tideStateAt, timeToNextExtreme } from "./calculations.js";
|
|
19
19
|
import FileCache from "./cache.js";
|
|
20
20
|
import { withVesselPosition } from "./middleware.js";
|
|
21
|
+
import { PLOTTER_EXTENSION_ID, plotterExtensionManifest, } from "./plotter-extension.js";
|
|
21
22
|
// Recompute and republish on a fixed interval. Predictions are computed locally
|
|
22
23
|
// by neaps, so this is cheap and needs no configuration.
|
|
23
24
|
const UPDATE_INTERVAL = 60 * 1000; // 1 minute
|
|
@@ -103,7 +104,12 @@ export default function (app) {
|
|
|
103
104
|
const cache = new FileCache(app.getDataDirPath());
|
|
104
105
|
// Mount the Neaps API, with vessel/default resolved to the configured
|
|
105
106
|
// default station (or the nearest one).
|
|
106
|
-
activeRouter = withVesselPosition(
|
|
107
|
+
activeRouter = withVesselPosition(
|
|
108
|
+
// @neaps/api bundles its own Express declarations, so its Router is
|
|
109
|
+
// callable at runtime but not structurally compatible with ours.
|
|
110
|
+
// The OpenAPI server URL comes from req.baseUrl, set by the
|
|
111
|
+
// app.use(API_PATH, ...) mount above.
|
|
112
|
+
createRoutes(), () => lastPosition, () => config.defaultStation ?? null);
|
|
107
113
|
// Register tide predictions as a resource provider
|
|
108
114
|
app.registerResourceProvider({
|
|
109
115
|
type: "tides",
|
|
@@ -126,6 +132,27 @@ export default function (app) {
|
|
|
126
132
|
},
|
|
127
133
|
},
|
|
128
134
|
});
|
|
135
|
+
// Offer the tide widget to chart plotters (src/plotter-extension.ts)
|
|
136
|
+
app.registerResourceProvider({
|
|
137
|
+
type: "plotterExtensions",
|
|
138
|
+
methods: {
|
|
139
|
+
async listResources() {
|
|
140
|
+
return { [PLOTTER_EXTENSION_ID]: plotterExtensionManifest };
|
|
141
|
+
},
|
|
142
|
+
async getResource(id) {
|
|
143
|
+
if (id !== PLOTTER_EXTENSION_ID) {
|
|
144
|
+
throw new Error(`No such plotterExtensions resource: ${id}`);
|
|
145
|
+
}
|
|
146
|
+
return plotterExtensionManifest;
|
|
147
|
+
},
|
|
148
|
+
setResource() {
|
|
149
|
+
throw new Error("Read-only resource");
|
|
150
|
+
},
|
|
151
|
+
deleteResource() {
|
|
152
|
+
throw new Error("Read-only resource");
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
});
|
|
129
156
|
app.subscriptionmanager.subscribe({
|
|
130
157
|
context: ("vessels." + app.selfId),
|
|
131
158
|
subscribe: [
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tide widget this plugin offers to chart plotters that implement the
|
|
3
|
+
* Signal K Plotter Extensions API, such as Freeboard-SK, where the user places
|
|
4
|
+
* it on the chart. Both pages are part of the webapp, which the server serves
|
|
5
|
+
* at /signalk-tides/.
|
|
6
|
+
*/
|
|
7
|
+
const WEBAPP_PATH = "/signalk-tides/";
|
|
8
|
+
export const PLOTTER_EXTENSION_ID = "signalk-tides";
|
|
9
|
+
export const plotterExtensionManifest = {
|
|
10
|
+
name: "Tides",
|
|
11
|
+
description: "The tide at the vessel's position: the level now and the next high or low water.",
|
|
12
|
+
apiVersion: "1",
|
|
13
|
+
requires: ["widgets"],
|
|
14
|
+
// Without them the widget still shows the tide; tapping it opens the panel.
|
|
15
|
+
optional: ["panels.iframe", "ui"],
|
|
16
|
+
widgets: [
|
|
17
|
+
{
|
|
18
|
+
id: "tides",
|
|
19
|
+
title: "Tides",
|
|
20
|
+
type: "iframe",
|
|
21
|
+
url: `${WEBAPP_PATH}widget.html`,
|
|
22
|
+
size: "2x1",
|
|
23
|
+
lifecycle: "whileEnabled",
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
panels: [
|
|
27
|
+
{
|
|
28
|
+
id: "tides",
|
|
29
|
+
title: "Tides",
|
|
30
|
+
type: "iframe",
|
|
31
|
+
url: WEBAPP_PATH,
|
|
32
|
+
lifecycle: "onOpen",
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "signalk-tides",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Tidal predictions for the vessel's position, powered by Neaps.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"scripts": {
|
|
9
9
|
"build": "tsc -b && vite build",
|
|
10
10
|
"dev": "vite",
|
|
11
|
-
"lint": "
|
|
11
|
+
"lint": "oxlint",
|
|
12
12
|
"prepack": "npm run build",
|
|
13
13
|
"preview": "vite preview",
|
|
14
14
|
"test": "npm run test:node && npm run test:browser",
|
|
@@ -34,13 +34,14 @@
|
|
|
34
34
|
],
|
|
35
35
|
"license": "Apache-2.0",
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@neaps/api": "^0.
|
|
38
|
-
"@neaps/react": "^0.
|
|
37
|
+
"@neaps/api": "^0.7.0",
|
|
38
|
+
"@neaps/react": "^0.2.0",
|
|
39
39
|
"@signalk/server-api": "^2.28.0",
|
|
40
40
|
"express": "^5.2.1",
|
|
41
|
-
"neaps": "^0.
|
|
41
|
+
"neaps": "^0.8.0",
|
|
42
42
|
"react": "^19.1.0",
|
|
43
|
-
"react-dom": "^19.1.0"
|
|
43
|
+
"react-dom": "^19.1.0",
|
|
44
|
+
"signalk-plotterext-bus": "^0.15.0"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"@eslint/js": "^9.21.0",
|
|
@@ -54,25 +55,26 @@
|
|
|
54
55
|
"@types/react-dom": "^19.0.4",
|
|
55
56
|
"@types/supertest": "^7.2.0",
|
|
56
57
|
"@vitejs/plugin-react": "^6.0.2",
|
|
57
|
-
"@vitest/browser": "
|
|
58
|
-
"@vitest/browser-playwright": "
|
|
59
|
-
"@vitest/ui": "
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"jsdom": "^29.1.1",
|
|
65
|
-
"maplibre-gl": "^5.19.0",
|
|
66
|
-
"pkg-pr-new": "^0.0.75",
|
|
58
|
+
"@vitest/browser": "5.0.2",
|
|
59
|
+
"@vitest/browser-playwright": "5.0.2",
|
|
60
|
+
"@vitest/ui": "5.0.2",
|
|
61
|
+
"jsdom": "^30.0.1",
|
|
62
|
+
"maplibre-gl": "^6.9.0",
|
|
63
|
+
"oxlint": "^1.74.0",
|
|
64
|
+
"pkg-pr-new": "^0.0.88",
|
|
67
65
|
"playwright": "^1.58.2",
|
|
68
66
|
"react-map-gl": "^8.1.0",
|
|
69
67
|
"signalk-server": "^2.13.5",
|
|
70
68
|
"supertest": "^7.2.2",
|
|
71
69
|
"tailwindcss": "^4.1.3",
|
|
72
|
-
"typescript": "^
|
|
73
|
-
"typescript-eslint": "^8.24.1",
|
|
70
|
+
"typescript": "^7.0.2",
|
|
74
71
|
"vite": "^8.0.16",
|
|
75
|
-
"vitest": "
|
|
72
|
+
"vitest": "5.0.2"
|
|
73
|
+
},
|
|
74
|
+
"overrides": {
|
|
75
|
+
"vite-plus": {
|
|
76
|
+
"@vitest/browser-playwright": "4.1.11"
|
|
77
|
+
}
|
|
76
78
|
},
|
|
77
79
|
"repository": {
|
|
78
80
|
"type": "git",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e,c as t,d as n,f as r,i,l as a,n as o,r as s,s as c,t as l,u}from"./useUnitPreferences-lYEQOWom.js";var d=u(),f=r(n(),1),p=()=>new URLSearchParams(window.location.search).get(`station`)||`vessel/default`;function m(){let[e,t]=(0,f.useState)(p);return(0,f.useEffect)(()=>{let e=()=>t(p());return window.addEventListener(`popstate`,e),()=>window.removeEventListener(`popstate`,e)},[]),[e,(0,f.useCallback)(e=>{let n=e.trim()||`vessel/default`,r=new URL(window.location.href);n===`vessel/default`?r.searchParams.delete(`station`):r.searchParams.set(`station`,n),window.history.pushState(window.history.state,``,r),t(n)},[])]}var h=a(),g={version:8,sources:{osm:{type:`raster`,tiles:[`https://tile.openstreetmap.org/{z}/{x}/{y}.png`],tileSize:256,attribution:`© OpenStreetMap contributors`}},layers:[{id:`osm`,type:`raster`,source:`osm`}]};function _({stationId:n,onSelect:r}){let a=(0,f.useRef)(null),[o,c]=(0,f.useState)(!1),[l,u]=(0,f.useState)(!1),[d,p]=(0,f.useState)(`list`),{data:m}=t(n),_=()=>{u(window.matchMedia(`(min-width: 1024px)`).matches),p(`list`),c(!0),a.current?.showModal()},v=e=>{r(e.id),a.current?.close()};return(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`button`,{type:`button`,onClick:_,"aria-label":`Browse stations`,className:`absolute hidden min-[20rem]:block right-2 top-2 sm:right-4 sm:top-4 md:right-6 md:top-6 lg:right-8 lg:top-8 xl:right-20 xl:top-20 z-10 rounded-md border border-(--neaps-border) bg-(--neaps-bg-subtle) p-2 text-(--neaps-text) hover:bg-(--neaps-bg)`,children:(0,h.jsxs)(`svg`,{width:`20`,height:`20`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,"aria-hidden":`true`,children:[(0,h.jsx)(`line`,{x1:`3`,y1:`6`,x2:`21`,y2:`6`}),(0,h.jsx)(`line`,{x1:`3`,y1:`12`,x2:`21`,y2:`12`}),(0,h.jsx)(`line`,{x1:`3`,y1:`18`,x2:`21`,y2:`18`})]})}),(0,h.jsx)(`dialog`,{ref:a,onClose:()=>c(!1),onClick:e=>{e.target===e.currentTarget&&e.currentTarget.close()},className:`m-auto w-[min(92vw,900px)] max-h-[85vh] rounded-lg border border-(--neaps-border) bg-(--neaps-bg) p-0 text-(--neaps-text) backdrop:bg-black/50`,children:o&&(0,h.jsxs)(`div`,{className:`flex max-h-[85vh] flex-col`,children:[(0,h.jsxs)(`div`,{className:`flex items-center justify-between border-b border-(--neaps-border) p-3`,children:[(0,h.jsx)(`button`,{type:`button`,onClick:()=>a.current?.close(),"aria-label":`Close`,className:`grid size-11 place-items-center rounded-md text-(--neaps-text-muted) hover:text-(--neaps-text)`,children:`✕`}),(0,h.jsx)(`h2`,{className:`text-base font-semibold`,children:`Stations`}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>p(d===`map`?`list`:`map`),"aria-label":d===`map`?`Show list`:`Show map`,className:`grid size-11 place-items-center rounded-md text-(--neaps-text-muted) hover:text-(--neaps-text) lg:invisible`,children:d===`map`?(0,h.jsxs)(`svg`,{width:`20`,height:`20`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,"aria-hidden":`true`,children:[(0,h.jsx)(`line`,{x1:`8`,y1:`6`,x2:`21`,y2:`6`}),(0,h.jsx)(`line`,{x1:`8`,y1:`12`,x2:`21`,y2:`12`}),(0,h.jsx)(`line`,{x1:`8`,y1:`18`,x2:`21`,y2:`18`}),(0,h.jsx)(`line`,{x1:`3`,y1:`6`,x2:`3.01`,y2:`6`}),(0,h.jsx)(`line`,{x1:`3`,y1:`12`,x2:`3.01`,y2:`12`}),(0,h.jsx)(`line`,{x1:`3`,y1:`18`,x2:`3.01`,y2:`18`})]}):(0,h.jsxs)(`svg`,{width:`20`,height:`20`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,"aria-hidden":`true`,children:[(0,h.jsx)(`polygon`,{points:`3 6 9 3 15 6 21 3 21 18 15 21 9 18 3 21 3 6`}),(0,h.jsx)(`line`,{x1:`9`,y1:`3`,x2:`9`,y2:`18`}),(0,h.jsx)(`line`,{x1:`15`,y1:`6`,x2:`15`,y2:`21`})]})})]}),(0,h.jsxs)(`div`,{className:`flex min-h-0 flex-col gap-4 p-3 lg:flex-row`,children:[(0,h.jsxs)(`div`,{className:`w-full flex-col gap-3 overflow-y-auto lg:flex lg:w-80 ${d===`map`?`hidden`:`flex`}`,children:[(0,h.jsx)(i,{onSelect:v}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>v({id:`vessel/default`}),className:`rounded-md border border-(--neaps-border) px-3 py-2 text-left text-sm hover:bg-(--neaps-bg-subtle)`,children:`📍 Vessel position`}),(0,h.jsx)(s,{stationId:n,onStationSelect:v})]}),(l||d===`map`)&&m&&(0,h.jsx)(`div`,{className:`h-[60vh] overflow-hidden rounded-md border border-(--neaps-border) lg:h-auto lg:min-h-100 lg:flex-1`,children:(0,h.jsx)(e,{mapStyle:g,initialViewState:{longitude:m.longitude,latitude:m.latitude,zoom:10},focusStation:m.id,onStationSelect:v})})]})]})})]})}var{VITE_SIGNALK_URL:v=window.location.toString()}={BASE_URL:`./`,DEV:!1,MODE:`production`,PROD:!0,SSR:!1},y=new URL(`/signalk/v2/api`,v).toString();function b(){let{units:e,ready:t}=l(),[n,r]=m();return t?(0,h.jsx)(o,{baseUrl:y,units:e,children:(0,h.jsxs)(`div`,{className:`relative`,children:[(0,h.jsx)(_,{stationId:n,onSelect:r}),(0,h.jsx)(c,{id:n,className:`overflow-y-auto p-2 sm:p-4 md:p-6 lg:p-8 xl:p-20`})]})}):null}(0,d.createRoot)(document.getElementById(`app`)).render((0,h.jsx)(b,{}));
|