@signalk/app-dock 1.2.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/release.yml +37 -0
- package/.github/workflows/label-by-title.yml +67 -0
- package/.github/workflows/release-please.yml +109 -0
- package/.github/workflows/release_on_tag.yml +25 -0
- package/.release-please-manifest.json +3 -0
- package/AGENTS.md +134 -0
- package/CLAUDE.md +1 -0
- package/package.json +1 -1
- package/public/app-icon-180.png +0 -0
- package/public/app-icon.svg +2 -4
- package/release-please-config.json +11 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Categories for the release notes GitHub generates for each tag, built from
|
|
2
|
+
# the titles of the PRs merged since the previous one. release-please asks
|
|
3
|
+
# GitHub for these notes (changelog-type: github in
|
|
4
|
+
# release-please-config.json), and so does release_on_tag.yml for a
|
|
5
|
+
# pre-release tag pushed by hand.
|
|
6
|
+
#
|
|
7
|
+
# A PR lands in the first category it matches, so the catch-all is last.
|
|
8
|
+
# workflows/label-by-title.yml sets the label from the PR title's type, and
|
|
9
|
+
# this file and that workflow name the same labels. A label set by hand
|
|
10
|
+
# places a PR the same way until its title is edited; an unlabelled one
|
|
11
|
+
# still appears, under Other.
|
|
12
|
+
changelog:
|
|
13
|
+
exclude:
|
|
14
|
+
labels:
|
|
15
|
+
- skip-changelog
|
|
16
|
+
# release-please's own release PR, merged right before each tag, which
|
|
17
|
+
# would otherwise list itself.
|
|
18
|
+
- 'autorelease: pending'
|
|
19
|
+
- 'autorelease: tagged'
|
|
20
|
+
categories:
|
|
21
|
+
- title: 🚀 Features
|
|
22
|
+
labels:
|
|
23
|
+
- feature
|
|
24
|
+
- enhancement
|
|
25
|
+
- title: 🐛 Fixes
|
|
26
|
+
labels:
|
|
27
|
+
- bug
|
|
28
|
+
- fix
|
|
29
|
+
- title: 📖 Documentation
|
|
30
|
+
labels:
|
|
31
|
+
- documentation
|
|
32
|
+
- title: 📦 Dependencies
|
|
33
|
+
labels:
|
|
34
|
+
- dependencies
|
|
35
|
+
- title: Other
|
|
36
|
+
labels:
|
|
37
|
+
- '*'
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
name: label-by-title
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request_target:
|
|
5
|
+
types: [opened, edited, reopened]
|
|
6
|
+
|
|
7
|
+
permissions: {}
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
# .github/release.yml sorts the release notes by label, and a pull request
|
|
11
|
+
# opened from the command line carries none. The label follows the title's
|
|
12
|
+
# conventional-commit type. Only the title is read, with no checkout and
|
|
13
|
+
# nothing from the pull request run here, which is what makes
|
|
14
|
+
# pull_request_target safe to use and what lets a pull request from a fork
|
|
15
|
+
# be labelled.
|
|
16
|
+
#
|
|
17
|
+
# `edited` also fires for a body or base-branch edit; those leave the labels
|
|
18
|
+
# alone, so a label set by hand stays until the title changes.
|
|
19
|
+
label:
|
|
20
|
+
if: ${{ github.event.action != 'edited' || github.event.changes.title }}
|
|
21
|
+
# One run per pull request at a time: two overlapping runs would each read
|
|
22
|
+
# the labels, then each edit, and could leave two managed labels behind.
|
|
23
|
+
# Set on the job, after the condition above, so that a body edit, which
|
|
24
|
+
# skips the job, cannot cancel a title edit's run.
|
|
25
|
+
concurrency:
|
|
26
|
+
group: label-by-title-${{ github.event.pull_request.number }}
|
|
27
|
+
cancel-in-progress: true
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
timeout-minutes: 5
|
|
30
|
+
permissions:
|
|
31
|
+
pull-requests: write
|
|
32
|
+
steps:
|
|
33
|
+
- env:
|
|
34
|
+
GH_TOKEN: ${{ github.token }}
|
|
35
|
+
REPO: ${{ github.repository }}
|
|
36
|
+
NUMBER: ${{ github.event.pull_request.number }}
|
|
37
|
+
TITLE: ${{ github.event.pull_request.title }}
|
|
38
|
+
run: |
|
|
39
|
+
# The labels this workflow owns: one of them at a time, so a title
|
|
40
|
+
# edited from feat to fix swaps the label rather than adding one.
|
|
41
|
+
# A type not listed gets none of them and lands under Other.
|
|
42
|
+
managed="enhancement bug documentation skip-changelog"
|
|
43
|
+
conventional='^([a-z]+)(\([^)]*\))?!?: '
|
|
44
|
+
type=""
|
|
45
|
+
if [[ "$TITLE" =~ $conventional ]]; then
|
|
46
|
+
type="${BASH_REMATCH[1]}"
|
|
47
|
+
fi
|
|
48
|
+
case "$type" in
|
|
49
|
+
feat|perf) wanted=enhancement ;;
|
|
50
|
+
fix) wanted=bug ;;
|
|
51
|
+
docs) wanted=documentation ;;
|
|
52
|
+
build|ci|test|chore|refactor|style) wanted=skip-changelog ;;
|
|
53
|
+
*) wanted="" ;;
|
|
54
|
+
esac
|
|
55
|
+
current=$(gh pr view "$NUMBER" --repo "$REPO" --json labels --jq '[.labels[].name] | join(" ")')
|
|
56
|
+
args=()
|
|
57
|
+
for label in $managed; do
|
|
58
|
+
case " $current " in *" $label "*) has=1 ;; *) has=0 ;; esac
|
|
59
|
+
if [ "$label" = "$wanted" ] && [ "$has" = 0 ]; then args+=(--add-label "$label"); fi
|
|
60
|
+
if [ "$label" != "$wanted" ] && [ "$has" = 1 ]; then args+=(--remove-label "$label"); fi
|
|
61
|
+
done
|
|
62
|
+
if [ "${#args[@]}" = 0 ]; then
|
|
63
|
+
echo "#$NUMBER '$TITLE': nothing to change (${wanted:-no managed label})"
|
|
64
|
+
else
|
|
65
|
+
gh pr edit "$NUMBER" --repo "$REPO" "${args[@]}"
|
|
66
|
+
echo "#$NUMBER '$TITLE': ${args[*]}"
|
|
67
|
+
fi
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
name: release-please
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
# The gate below runs release-please only for a push that carries a commit
|
|
7
|
+
# users get, so a release pull request it has already opened is not
|
|
8
|
+
# refreshed by a docs or ci merge, and a config change is exactly such a
|
|
9
|
+
# merge. This trigger re-runs it on demand, which is what rewrites an
|
|
10
|
+
# outstanding release pull request against the current configuration.
|
|
11
|
+
workflow_dispatch:
|
|
12
|
+
|
|
13
|
+
permissions: {}
|
|
14
|
+
|
|
15
|
+
# One release-please run at a time: a push and a manual run, or two pushes
|
|
16
|
+
# landing close together, would each read the release pull request's state
|
|
17
|
+
# and each write it, and the later write could undo the earlier one.
|
|
18
|
+
# cancel-in-progress is false so a run already creating a tag or dispatching
|
|
19
|
+
# the publish is never killed part-way. A run waiting behind it can still be
|
|
20
|
+
# replaced by a newer one, which costs nothing here: release-please derives
|
|
21
|
+
# the release pull request from the commit range, so the newest run produces
|
|
22
|
+
# the same result the superseded one would have.
|
|
23
|
+
#
|
|
24
|
+
# The group is a constant rather than keyed on github.ref: the action is
|
|
25
|
+
# given no target-branch, so every run, including a dispatch launched from
|
|
26
|
+
# some other branch, acts on the default branch's release pull request.
|
|
27
|
+
concurrency:
|
|
28
|
+
group: release-please
|
|
29
|
+
cancel-in-progress: false
|
|
30
|
+
|
|
31
|
+
jobs:
|
|
32
|
+
# GitHub's release notes are never empty, so release-please would propose
|
|
33
|
+
# a release after every merge, a docs or ci change included. Only a push
|
|
34
|
+
# carrying a change users get, or the merge of the release PR itself,
|
|
35
|
+
# which is what creates the tag, goes on to it.
|
|
36
|
+
#
|
|
37
|
+
# Every commit in the push is checked, so a merge commit's PR commits count
|
|
38
|
+
# through their own subjects. A breaking change counts whatever its type, a
|
|
39
|
+
# `revert:` because what it undoes has shipped, and a `Release-As:` footer,
|
|
40
|
+
# matched in any case as release-please does, because it asks for a release
|
|
41
|
+
# on any subject. GitHub's own `Revert "…"` subject is not conventional, so
|
|
42
|
+
# release-please ignores it and it is not let through here either. The last
|
|
43
|
+
# alternative is the release PR's own title, pull-request-title-pattern in
|
|
44
|
+
# release-please-config.json, and must change with it, or the release PR's
|
|
45
|
+
# merge is judged non-releasable and no tag is ever created. The payload
|
|
46
|
+
# holds at most 2048 commits, so a push that fills it may hide more and is
|
|
47
|
+
# let through.
|
|
48
|
+
gate:
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
timeout-minutes: 5
|
|
51
|
+
outputs:
|
|
52
|
+
releasable: ${{ steps.check.outputs.releasable }}
|
|
53
|
+
steps:
|
|
54
|
+
# A manual run carries no commits to judge, and asking for one is the
|
|
55
|
+
# statement that it should happen, so it bypasses the check below
|
|
56
|
+
# rather than being skipped by it for having an empty payload.
|
|
57
|
+
- id: check
|
|
58
|
+
env:
|
|
59
|
+
DISPATCHED: ${{ github.event_name == 'workflow_dispatch' }}
|
|
60
|
+
MESSAGES: ${{ toJSON(github.event.commits.*.message) }}
|
|
61
|
+
RELEASABLE: '^((feat|fix|perf|revert)(\([^)]*\))?!?: |[a-z]+(\([^)]*\))?!: |chore\(release\): v?[0-9])'
|
|
62
|
+
run: |
|
|
63
|
+
if [ "$DISPATCHED" = true ]; then
|
|
64
|
+
echo "releasable=true" >> "$GITHUB_OUTPUT"
|
|
65
|
+
echo "Run requested manually; release-please not gated."
|
|
66
|
+
exit 0
|
|
67
|
+
fi
|
|
68
|
+
if jq -e --arg re "$RELEASABLE" '(length >= 2048) or ([.[] | (split("\n")[0] | test($re)) or test("\nBREAKING[- ]CHANGE: ") or test("\nrelease-as: "; "i")] | any)' <<< "$MESSAGES" > /dev/null; then
|
|
69
|
+
echo "releasable=true" >> "$GITHUB_OUTPUT"
|
|
70
|
+
else
|
|
71
|
+
echo "releasable=false" >> "$GITHUB_OUTPUT"
|
|
72
|
+
echo "No releasable commit in this push; release-please not run."
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
release-please:
|
|
76
|
+
needs: gate
|
|
77
|
+
if: ${{ needs.gate.outputs.releasable == 'true' }}
|
|
78
|
+
runs-on: ubuntu-latest
|
|
79
|
+
timeout-minutes: 10
|
|
80
|
+
permissions:
|
|
81
|
+
contents: write
|
|
82
|
+
pull-requests: write
|
|
83
|
+
# release-please creates its `autorelease: pending` / `autorelease:
|
|
84
|
+
# tagged` labels through the issues API the first time it needs them.
|
|
85
|
+
issues: write
|
|
86
|
+
outputs:
|
|
87
|
+
releases_created: ${{ steps.rp.outputs.releases_created }}
|
|
88
|
+
tag_name: ${{ steps.rp.outputs.tag_name }}
|
|
89
|
+
steps:
|
|
90
|
+
- uses: googleapis/release-please-action@v5
|
|
91
|
+
id: rp
|
|
92
|
+
|
|
93
|
+
# The tag release-please just pushed does not start release_on_tag.yml by
|
|
94
|
+
# itself: an event raised with GITHUB_TOKEN starts no workflow, with
|
|
95
|
+
# workflow_dispatch as the exception. So release_on_tag.yml is dispatched on
|
|
96
|
+
# the tag. It has to stay the file that publishes, since npm's trusted
|
|
97
|
+
# publishing is bound to that workflow file. This is its own job, so that
|
|
98
|
+
# the right to start workflows is held by nothing but this one command.
|
|
99
|
+
publish:
|
|
100
|
+
needs: release-please
|
|
101
|
+
if: ${{ needs.release-please.outputs.releases_created == 'true' }}
|
|
102
|
+
runs-on: ubuntu-latest
|
|
103
|
+
timeout-minutes: 5
|
|
104
|
+
permissions:
|
|
105
|
+
actions: write
|
|
106
|
+
steps:
|
|
107
|
+
- env:
|
|
108
|
+
GH_TOKEN: ${{ github.token }}
|
|
109
|
+
run: gh workflow run release_on_tag.yml --repo "$GITHUB_REPOSITORY" --ref "${{ needs.release-please.outputs.tag_name }}"
|
|
@@ -1,16 +1,27 @@
|
|
|
1
|
+
# Publishes the tag it runs on to npm. Release tags come from release-please:
|
|
2
|
+
# merging its release PR creates the tag and the GitHub Release, then
|
|
3
|
+
# release-please.yml dispatches this workflow on the tag. Pre-release tags
|
|
4
|
+
# (-beta.N, -rc.N, -alpha.N) are pushed by hand, and only a pushed tag gets
|
|
5
|
+
# its GitHub Release created here.
|
|
1
6
|
name: 'Release on tag'
|
|
2
7
|
|
|
3
8
|
on:
|
|
4
9
|
push:
|
|
5
10
|
tags:
|
|
6
11
|
- 'v*'
|
|
12
|
+
# How release-please.yml starts this on a tag it created: that tag, pushed
|
|
13
|
+
# with GITHUB_TOKEN, raises no push event.
|
|
14
|
+
workflow_dispatch:
|
|
7
15
|
|
|
8
16
|
concurrency:
|
|
9
17
|
group: release-${{ github.ref }}
|
|
10
18
|
cancel-in-progress: false
|
|
11
19
|
|
|
12
20
|
jobs:
|
|
21
|
+
# A dispatched run is on a tag release-please made, and release-please made
|
|
22
|
+
# its Release too; creating one here would be a second for the same tag.
|
|
13
23
|
release:
|
|
24
|
+
if: ${{ github.event_name == 'push' }}
|
|
14
25
|
permissions:
|
|
15
26
|
contents: write
|
|
16
27
|
runs-on: ubuntu-latest
|
|
@@ -28,6 +39,10 @@ jobs:
|
|
|
28
39
|
|
|
29
40
|
publish:
|
|
30
41
|
needs: release
|
|
42
|
+
# Runs after the Release on a pushed tag, and without it on a dispatched
|
|
43
|
+
# one, where that job is skipped. A dispatch can name any ref; only a
|
|
44
|
+
# release tag is published.
|
|
45
|
+
if: ${{ !cancelled() && needs.release.result != 'failure' && startsWith(github.ref, 'refs/tags/v') }}
|
|
31
46
|
permissions:
|
|
32
47
|
id-token: write
|
|
33
48
|
contents: read
|
|
@@ -43,6 +58,16 @@ jobs:
|
|
|
43
58
|
|
|
44
59
|
- run: npm ci
|
|
45
60
|
|
|
61
|
+
- name: Verify the tag matches package.json
|
|
62
|
+
run: |
|
|
63
|
+
TAG="${GITHUB_REF_NAME#v}"
|
|
64
|
+
PKG="$(node -p "require('./package.json').version")"
|
|
65
|
+
if [ "$TAG" != "$PKG" ]; then
|
|
66
|
+
echo "::error::tag $GITHUB_REF_NAME does not match package.json version $PKG"
|
|
67
|
+
exit 1
|
|
68
|
+
fi
|
|
69
|
+
echo "tag and package.json agree on $PKG"
|
|
70
|
+
|
|
46
71
|
- name: Publish to npm
|
|
47
72
|
run: |
|
|
48
73
|
tag="${GITHUB_REF#refs/tags/}"
|
package/AGENTS.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# @signalk/app-dock
|
|
2
|
+
|
|
3
|
+
A Signal K server plugin plus webapp: a macOS-style dock for switching between Signal K webapps on touch screens. The
|
|
4
|
+
dock page holds each app in an iframe, and a double-tap anywhere brings up the dock. User-facing behaviour is documented
|
|
5
|
+
in `README.md`; this file covers what a contributor needs to keep intact.
|
|
6
|
+
|
|
7
|
+
## Architecture
|
|
8
|
+
|
|
9
|
+
Three surfaces ship together from one package:
|
|
10
|
+
|
|
11
|
+
- **`plugin/index.js`**: the server plugin (CommonJS). It holds the schema, seeds the default apps on first start, serves
|
|
12
|
+
the dock's HTTP routes, and registers the `environment.mode` PUT handler for the night-mode button.
|
|
13
|
+
- **`public/dock.js`, `dock.css`, `index.html`**: the dock itself, served as-is at `/@signalk/app-dock/`. Plain browser
|
|
14
|
+
JavaScript with **no build step**: edit and reload.
|
|
15
|
+
- **`src/configpanel/`**: the React 19 config panel that the admin UI loads through Webpack Module Federation (keyword
|
|
16
|
+
`signalk-plugin-configurator`). `npm run build:config` writes `public/remoteEntry.js` and its chunks.
|
|
17
|
+
|
|
18
|
+
`public/config.html` is an older standalone configurator that the schema description links to; the admin UI shows the
|
|
19
|
+
React panel.
|
|
20
|
+
|
|
21
|
+
## Rules to keep in mind
|
|
22
|
+
|
|
23
|
+
- **Seed once, never rewrite.** `start()` seeds `DEFAULT_APPS` only when the saved `apps` is not an array. An app list
|
|
24
|
+
that already exists, even an empty one, is the user's and is never changed.
|
|
25
|
+
- **The default app list lives in three places.** `DEFAULT_APPS` in `plugin/index.js`, `NO_ADMIN_DEFAULTS` in
|
|
26
|
+
`public/dock.js` (served to visitors the plugin's `/settings` refuses), and the welcome-tour text in
|
|
27
|
+
`public/index.html`. Change them together.
|
|
28
|
+
- **`savePluginOptions` replaces the whole saved configuration.** Build every save from a complete configuration
|
|
29
|
+
(`running ? pluginSettings : savedSettings()`), never from a partial object.
|
|
30
|
+
- **Routes must work while the plugin is stopped.** signalk-server registers the router of every loaded plugin, enabled
|
|
31
|
+
or not. A handler that needs the configuration reads `app.readPluginOptions()` when `running` is false.
|
|
32
|
+
- **Access is opt-in per route.** Routes on `router` are admin-only. `router.access('readonly' | 'readwrite')`
|
|
33
|
+
(signalk-server 2.31 and later) opens a route to more users; feature-detect it (`typeof router.access === 'function'`)
|
|
34
|
+
so older servers keep working admin-only. `/config` stays admin-only on every server. When access changes, update the
|
|
35
|
+
access detection at the top of `dock.js` and the **Access** table in `README.md` with it.
|
|
36
|
+
- **`/settings` is readable by every visitor with read access.** Never put credentials or anything secret in it; that
|
|
37
|
+
includes app URLs.
|
|
38
|
+
- **Taps pass through.** The double-tap listener is passive, never calls `preventDefault()`, and there is no overlay
|
|
39
|
+
element. It attaches to the dock document and, recursively, to every same-origin iframe document, so apps that embed
|
|
40
|
+
other apps still open the dock.
|
|
41
|
+
- **One iframe sandbox string.** `switchToApp()` and the tour's Plugin Config link in `dock.js` create iframes with the
|
|
42
|
+
same `sandbox` value; keep them identical. `allow-top-navigation-by-user-activation` is what lets a dock opened inside
|
|
43
|
+
a dock break out to the top window (the guard at the top of `dock.js`).
|
|
44
|
+
- **Live config reload.** The dock polls the plugin every 5 s. A setting that changes how the dock is built belongs in
|
|
45
|
+
`STRUCTURAL_KEYS` in `dock.js` (the page reloads); app-list changes rebuild the dock in place.
|
|
46
|
+
- **New settings touch four places:** the schema in `plugin/index.js`, `DEFAULTS` in `dock.js`, the config panel in
|
|
47
|
+
`src/configpanel/`, and the **Settings** table in `README.md`.
|
|
48
|
+
- **Browser globals are declared.** ESLint lints `dock.js` as a classic script with an explicit globals list in
|
|
49
|
+
`eslint.config.js`; add a browser API there when `dock.js` starts using it.
|
|
50
|
+
|
|
51
|
+
## Build output is committed
|
|
52
|
+
|
|
53
|
+
`public/remoteEntry.js`, `public/main.js`, `public/540.js`, `public/805.js` and `public/540.js.LICENSE.txt` are Webpack
|
|
54
|
+
output and are tracked in git. After changing `src/configpanel/`, run `npm run build:config` and commit the regenerated
|
|
55
|
+
files with the source change. `prepublishOnly` rebuilds them at publish time. Prettier and ESLint skip them, and ESLint
|
|
56
|
+
also skips `src/`.
|
|
57
|
+
|
|
58
|
+
## Icons
|
|
59
|
+
|
|
60
|
+
`public/app-icon.svg` is the source icon: the `signalk.appIcon` shown by the admin UI and the App Store, the favicon,
|
|
61
|
+
and the dock's own logo. `public/app-icon-180.png` (apple-touch-icon) is generated from it and
|
|
62
|
+
`public/app-icon-maskable.svg` is maintained by hand; see **Icon assets** in `README.md`.
|
|
63
|
+
|
|
64
|
+
## Tests
|
|
65
|
+
|
|
66
|
+
`npm test` runs `test/plugin.test.js` with `node:test` against a mock `app` (`createMockApp()`). Cover new routes,
|
|
67
|
+
settings and seeding behaviour there. `dock.js` has no automated tests; check dock changes in a browser against a
|
|
68
|
+
running server (see **Development** in `README.md`).
|
|
69
|
+
|
|
70
|
+
## Workflow conventions
|
|
71
|
+
|
|
72
|
+
This repo is maintained by Dirk Wahrheit.
|
|
73
|
+
|
|
74
|
+
- Branch names use **hyphens**, never slashes.
|
|
75
|
+
- Angular conventional commits: `<type>(<scope>): <subject>`. PR titles use the same format; the generated release
|
|
76
|
+
notes are built from them.
|
|
77
|
+
- The PR title's type also picks its section in the release notes. `.github/workflows/label-by-title.yml` labels each
|
|
78
|
+
PR when it is opened or its title edited: `feat`/`perf` → `enhancement` (🚀 Features), `fix` → `bug` (🐛 Fixes),
|
|
79
|
+
`docs` → `documentation` (📖 Documentation), `build`/`ci`/`test`/`chore`/`refactor`/`style` → `skip-changelog`
|
|
80
|
+
(left out). Any other type lands under Other. A label set by hand stays until the title is next edited. Keep the
|
|
81
|
+
workflow and `.github/release.yml` naming the same labels.
|
|
82
|
+
- One logical change per commit.
|
|
83
|
+
- No `Co-Authored-By` lines. No "Generated with Claude Code" attribution.
|
|
84
|
+
- Never commit directly to `main`. Every change goes through a PR.
|
|
85
|
+
- PR descriptions: no checkboxes. "Tested" lists what actually ran, not what was planned.
|
|
86
|
+
- No hand-written CHANGELOG; GitHub Releases carry generated notes.
|
|
87
|
+
|
|
88
|
+
### Before opening a PR
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npm run format # prettier --write + eslint --fix
|
|
92
|
+
npm run lint
|
|
93
|
+
npm test
|
|
94
|
+
npm run build:config # only when src/configpanel/ changed; commit the output
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
CI (`.github/workflows/ci.yml`) calls signalk-server's shared `plugin-ci.yml`: install, `npm test`, plugin lifecycle
|
|
98
|
+
and schema checks, npm-pack contents, and an App Store style `--ignore-scripts` install. It does **not** run Prettier or
|
|
99
|
+
ESLint, so run them locally.
|
|
100
|
+
|
|
101
|
+
### Releases
|
|
102
|
+
|
|
103
|
+
Releases are cut by release-please. Every releasable push to `main` updates a standing release PR titled
|
|
104
|
+
`chore(release): X.Y.Z` that bumps `version` in `package.json` and `package-lock.json`. Merging it creates the tag and
|
|
105
|
+
the GitHub Release, and `.github/workflows/release-please.yml` then dispatches `release_on_tag.yml` on the tag, which
|
|
106
|
+
publishes to npm with provenance (OIDC trusted publishing, no token).
|
|
107
|
+
|
|
108
|
+
- **The version follows the commits:** `feat` → minor, `fix`, `perf` and `revert` → patch, `!` or a
|
|
109
|
+
`BREAKING CHANGE:` footer → major. A `Release-As: X.Y.Z` footer overrides it. Do not bump the version in an ordinary
|
|
110
|
+
PR.
|
|
111
|
+
- **A push with nothing releasable leaves the release PR alone.** The `gate` job in `release-please.yml` decides what
|
|
112
|
+
counts; its comment lists the cases. Revert with a conventional `revert:` subject, since release-please ignores
|
|
113
|
+
GitHub's `Revert "…"`. Change the gate's last alternative together with `pull-request-title-pattern` in
|
|
114
|
+
`release-please-config.json`, or the release PR's merge never creates a tag.
|
|
115
|
+
- **Merge the release PR once it lists your change.** release-please refreshes it a moment after each merge; a
|
|
116
|
+
release PR merged before that still ships the change (the tag is on top of it) but its notes leave it out.
|
|
117
|
+
- **Pre-releases are cut by hand:** a `chore(release): X.Y.Z-beta.N` PR, then an annotated tag `vX.Y.Z-beta.N` pushed
|
|
118
|
+
on its merge commit. `release_on_tag.yml` creates their Release itself and publishes under the npm dist-tag `alpha`,
|
|
119
|
+
`beta` or `rc` that the tag contains.
|
|
120
|
+
|
|
121
|
+
## File layout
|
|
122
|
+
|
|
123
|
+
| Path | Purpose |
|
|
124
|
+
| ----------------------------- | ------------------------------------------------------------------------------------- |
|
|
125
|
+
| `plugin/index.js` | Plugin entry: schema, default-app seeding, routes, night-mode PUT handler. |
|
|
126
|
+
| `public/index.html` | Dock page: idle screen, dock, backdrop, loading overlay, welcome tour. |
|
|
127
|
+
| `public/dock.js` | Dock logic: access detection, config loading and polling, iframes, double-tap, dock. |
|
|
128
|
+
| `public/dock.css` | Dock styling, magnification, positions. |
|
|
129
|
+
| `public/manifest.webmanifest` | PWA manifest for Add to Home Screen. |
|
|
130
|
+
| `public/config.html` | Older standalone configurator. |
|
|
131
|
+
| `src/configpanel/` | React config panel for the admin UI (Module Federation remote). |
|
|
132
|
+
| `webpack.config.js` | Builds `src/configpanel/` into `public/`; shares React as a singleton with the admin. |
|
|
133
|
+
| `test/plugin.test.js` | Plugin tests (`node:test`). |
|
|
134
|
+
| `docs/screenshots/` | App Store screenshots, listed in `package.json` under `signalk.screenshots`. |
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@AGENTS.md
|
package/package.json
CHANGED
package/public/app-icon-180.png
CHANGED
|
Binary file
|
package/public/app-icon.svg
CHANGED
|
@@ -5,10 +5,8 @@
|
|
|
5
5
|
<stop offset="100%" stop-color="#2563eb"/>
|
|
6
6
|
</linearGradient>
|
|
7
7
|
</defs>
|
|
8
|
-
<!--
|
|
9
|
-
<rect width="72" height="72" fill="#
|
|
10
|
-
<!-- Background -->
|
|
11
|
-
<rect width="72" height="72" rx="16" fill="url(#bg)"/>
|
|
8
|
+
<!-- Background, full bleed: whatever displays the icon rounds the corners -->
|
|
9
|
+
<rect width="72" height="72" fill="url(#bg)"/>
|
|
12
10
|
<!-- Dock pill -->
|
|
13
11
|
<rect x="8" y="48" width="56" height="16" rx="8" fill="#ffffff" fill-opacity="0.25" stroke="#ffffff" stroke-opacity="0.4" stroke-width="0.5"/>
|
|
14
12
|
<!-- Dock icons - 3 squares in the pill -->
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
|
|
3
|
+
"release-type": "node",
|
|
4
|
+
"include-component-in-tag": false,
|
|
5
|
+
"changelog-type": "github",
|
|
6
|
+
"skip-changelog": true,
|
|
7
|
+
"pull-request-title-pattern": "chore(release): ${version}",
|
|
8
|
+
"packages": {
|
|
9
|
+
".": {}
|
|
10
|
+
}
|
|
11
|
+
}
|