@signalk/app-dock 1.1.0-beta.1 → 1.1.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/README.md CHANGED
@@ -13,6 +13,8 @@ A macOS-style app dock for switching between Signal K webapps on touch screens.
13
13
  - **Autostart** -- optionally load a default app immediately on open
14
14
  - **keep-alive or destroy** iframe lifecycle
15
15
  - **Night/day mode toggle** -- optional sun/moon button that flips `environment.mode` via a PUT handler
16
+ - **Fullscreen toggle** -- optional button that hides browser chrome via the Fullscreen API; feature-detected, so hidden on devices without API support (notably iPadOS Safari -- use Add to Home Screen there)
17
+ - **Installable as an app** -- ships a web app manifest and apple-touch-icon so iOS, Android, and desktop browsers can install the dock to the home screen and launch it in standalone mode (no browser chrome). Works over plain HTTP -- no SSL required
16
18
  - **Active dot indicator**, label tooltip, haptic feedback
17
19
  - **Embedded config panel** in the admin UI with webapp discovery, drag-to-reorder, and live preview
18
20
 
@@ -35,6 +37,18 @@ Tip: double-tapping a clickable element in the underlying webapp will activate t
35
37
 
36
38
  Config changes made in **Plugin Config** apply automatically within ~5 seconds: structural changes (position, icon size, etc.) reload the dock page; changes to the app list hot-update in place.
37
39
 
40
+ ## Install as an app
41
+
42
+ The dock ships a web app manifest, so iOS and Android can install it to the home screen and launch it like a native app -- no Safari/Chrome chrome, no URL bar.
43
+
44
+ - **iPad / iPhone (Safari)**: open the dock URL, tap the Share icon, choose **Add to Home Screen**.
45
+ - **Android (Chrome)**: open the dock URL, tap the ⋮ menu, choose **Install app** (or **Add to Home Screen**).
46
+ - **Desktop (Chrome, Edge)**: an install icon appears in the address bar; click it.
47
+
48
+ HTTPS is **not required** for Add-to-Home-Screen -- the dock installs cleanly over plain `http://your-sk-server:3000/`. It works equally over HTTPS with a self-signed or local-CA cert, provided the device has been configured to trust that CA (the usual iPad profile flow: Settings > General > VPN & Device Management > trust the profile > Settings > General > About > Certificate Trust Settings > enable full trust).
49
+
50
+ If the installed dock ever displays a blank screen after a long idle period (rare; only if the server's caching layer ignores the dock's `no-cache` directives), remove the home-screen icon and re-add it from Safari.
51
+
38
52
  ## Configuration
39
53
 
40
54
  Open **Plugin Config > App Dock** in the admin UI. The embedded configurator provides:
@@ -47,15 +61,16 @@ Open **Plugin Config > App Dock** in the admin UI. The embedded configurator pro
47
61
 
48
62
  ### Settings
49
63
 
50
- | Setting | Default | Description |
51
- | --------------------- | ------------ | ------------------------------------------------------------------------------------------------ |
52
- | `position` | `bottom` | Dock edge: `bottom`, `top`, `left`, `right` |
53
- | `iframeMode` | `keep-alive` | `keep-alive` loads each app once and hides/shows (faster, more RAM); `destroy` reloads each time |
54
- | `iconSize` | `56` | Base icon size in px |
55
- | `magnification` | `true` | Enable macOS-style magnification effect |
56
- | `magnificationScale` | `1.7` | Max icon scale (1.0-2.5) |
57
- | `showNightModeButton` | `true` | Show sun/moon button in dock; PUT-writes `environment.mode` |
58
- | `showExitButton` | `false` | Show X button that returns to the Signal K admin UI |
64
+ | Setting | Default | Description |
65
+ | ---------------------- | ------------ | ------------------------------------------------------------------------------------------------ |
66
+ | `position` | `bottom` | Dock edge: `bottom`, `top`, `left`, `right` |
67
+ | `iframeMode` | `keep-alive` | `keep-alive` loads each app once and hides/shows (faster, more RAM); `destroy` reloads each time |
68
+ | `iconSize` | `56` | Base icon size in px |
69
+ | `magnification` | `true` | Enable macOS-style magnification effect |
70
+ | `magnificationScale` | `1.7` | Max icon scale (1.0-2.5) |
71
+ | `showNightModeButton` | `true` | Show sun/moon button in dock; PUT-writes `environment.mode` |
72
+ | `showFullscreenButton` | `false` | Show fullscreen toggle in dock; hidden automatically where the Fullscreen API is unavailable |
73
+ | `showExitButton` | `false` | Show X button that returns to the Signal K admin UI |
59
74
 
60
75
  ## Development
61
76
 
@@ -87,6 +102,16 @@ The repo ships three cooperating surfaces:
87
102
  | `npm run lint` | ESLint check |
88
103
  | `npm run build:config` | Rebuild the admin UI config panel (required after `src/` changes) |
89
104
 
105
+ ### Icon assets
106
+
107
+ If `public/app-icon.svg` changes, regenerate the 180-px apple-touch-icon:
108
+
109
+ ```bash
110
+ rsvg-convert -w 180 -h 180 public/app-icon.svg -o public/app-icon-180.png
111
+ ```
112
+
113
+ `public/app-icon-maskable.svg` is a hand-authored wrapper that places the artwork inside Android's 80% safe zone; if the main icon's layout changes meaningfully, update it by hand to match.
114
+
90
115
  ## License
91
116
 
92
117
  Apache-2.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signalk/app-dock",
3
- "version": "1.1.0-beta.1",
3
+ "version": "1.1.0",
4
4
  "description": "macOS-style configurable app dock for switching between SignalK webapps on touch screens",
5
5
  "repository": {
6
6
  "url": "https://github.com/SignalK/app-dock"
Binary file
@@ -0,0 +1,24 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
2
+ <defs>
3
+ <linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
4
+ <stop offset="0%" stop-color="#60a5fa"/>
5
+ <stop offset="100%" stop-color="#2563eb"/>
6
+ </linearGradient>
7
+ </defs>
8
+ <rect width="100" height="100" fill="url(#bg)"/>
9
+ <g transform="translate(14 14)">
10
+ <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"/>
11
+ <rect x="14" y="51" width="10" height="10" rx="2.5" fill="#3b82f6" opacity="0.9"/>
12
+ <rect x="31" y="51" width="10" height="10" rx="2.5" fill="#10b981" opacity="0.9"/>
13
+ <rect x="48" y="51" width="10" height="10" rx="2.5" fill="#f59e0b" opacity="0.9"/>
14
+ <rect x="28" y="34" width="16" height="16" rx="3.5" fill="#10b981" opacity="0.95"/>
15
+ <circle cx="36" cy="66" r="1.5" fill="#ffffff" fill-opacity="0.8"/>
16
+ <circle cx="36" cy="22" r="10" fill="none" stroke="#ffffff" stroke-opacity="0.15" stroke-width="1"/>
17
+ <line x1="36" y1="14" x2="36" y2="18" stroke="#ffffff" stroke-opacity="0.3" stroke-width="1" stroke-linecap="round"/>
18
+ <line x1="36" y1="26" x2="36" y2="30" stroke="#ffffff" stroke-opacity="0.15" stroke-width="1" stroke-linecap="round"/>
19
+ <line x1="28" y1="22" x2="32" y2="22" stroke="#ffffff" stroke-opacity="0.15" stroke-width="1" stroke-linecap="round"/>
20
+ <line x1="40" y1="22" x2="44" y2="22" stroke="#ffffff" stroke-opacity="0.15" stroke-width="1" stroke-linecap="round"/>
21
+ <polygon points="36,15 34,22 36,20 38,22" fill="#ef4444" fill-opacity="0.7"/>
22
+ <polygon points="36,29 34,22 36,24 38,22" fill="#ffffff" fill-opacity="0.25"/>
23
+ </g>
24
+ </svg>
package/public/index.html CHANGED
@@ -4,6 +4,15 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, viewport-fit=cover" />
6
6
  <meta name="mobile-web-app-capable" content="yes" />
7
+ <link rel="manifest" href="manifest.webmanifest" />
8
+ <link rel="icon" type="image/svg+xml" href="app-icon.svg" />
9
+ <link rel="apple-touch-icon" href="app-icon-180.png" />
10
+ <meta name="apple-mobile-web-app-capable" content="yes" />
11
+ <meta name="apple-mobile-web-app-title" content="App Dock" />
12
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
13
+ <meta name="theme-color" content="#000000" />
14
+ <meta http-equiv="Cache-Control" content="no-cache, must-revalidate" />
15
+ <meta http-equiv="Pragma" content="no-cache" />
7
16
  <title>SK Dock</title>
8
17
  <link rel="stylesheet" href="dock.css" />
9
18
  </head>
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "Signal K App Dock",
3
+ "short_name": "App Dock",
4
+ "description": "macOS-style dock to switch between Signal K webapps",
5
+ "start_url": "/@signalk/app-dock/",
6
+ "scope": "/",
7
+ "display": "standalone",
8
+ "orientation": "any",
9
+ "background_color": "#000000",
10
+ "theme_color": "#000000",
11
+ "icons": [
12
+ {
13
+ "src": "app-icon.svg",
14
+ "sizes": "any",
15
+ "type": "image/svg+xml",
16
+ "purpose": "any"
17
+ },
18
+ {
19
+ "src": "app-icon-180.png",
20
+ "sizes": "180x180",
21
+ "type": "image/png",
22
+ "purpose": "any"
23
+ },
24
+ {
25
+ "src": "app-icon-maskable.svg",
26
+ "sizes": "any",
27
+ "type": "image/svg+xml",
28
+ "purpose": "maskable"
29
+ }
30
+ ]
31
+ }
@@ -521,4 +521,22 @@ describe('public files', () => {
521
521
  it('icon-settings.svg exists', () => {
522
522
  assert.ok(fs.existsSync(path.join(publicDir, 'icon-settings.svg')))
523
523
  })
524
+
525
+ it('manifest.webmanifest exists and is valid JSON with required PWA fields', () => {
526
+ const manifestPath = path.join(publicDir, 'manifest.webmanifest')
527
+ assert.ok(fs.existsSync(manifestPath))
528
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'))
529
+ assert.equal(typeof manifest.name, 'string')
530
+ assert.equal(typeof manifest.start_url, 'string')
531
+ assert.equal(manifest.display, 'standalone')
532
+ assert.ok(Array.isArray(manifest.icons) && manifest.icons.length > 0)
533
+ })
534
+
535
+ it('app-icon-180.png exists (apple-touch-icon)', () => {
536
+ assert.ok(fs.existsSync(path.join(publicDir, 'app-icon-180.png')))
537
+ })
538
+
539
+ it('app-icon-maskable.svg exists', () => {
540
+ assert.ok(fs.existsSync(path.join(publicDir, 'app-icon-maskable.svg')))
541
+ })
524
542
  })