@signalk/app-dock 0.3.0-beta.1 → 0.3.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.
@@ -1,28 +1,28 @@
1
1
  name: 'Release on tag'
2
+
2
3
  on:
3
4
  push:
4
5
  tags:
5
- - '*'
6
+ - 'v*'
7
+
8
+ concurrency:
9
+ group: release-${{ github.ref }}
10
+ cancel-in-progress: false
6
11
 
7
12
  jobs:
8
13
  release:
9
14
  permissions:
10
15
  contents: write
11
- if: startsWith(github.ref, 'refs/tags/')
12
16
  runs-on: ubuntu-latest
17
+ timeout-minutes: 10
13
18
  steps:
14
- - name: Build Changelog
15
- id: github_release
16
- uses: mikepenz/release-changelog-builder-action@v1
17
- env:
18
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
19
-
20
19
  - name: Create Release
21
- uses: actions/create-release@v1
20
+ uses: softprops/action-gh-release@v3
22
21
  with:
23
- tag_name: ${{ github.ref }}
24
- release_name: ${{ github.ref }}
25
- body: ${{steps.github_release.outputs.changelog}}
22
+ tag_name: ${{ github.ref_name }}
23
+ name: ${{ github.ref_name }}
24
+ generate_release_notes: true
25
+ prerelease: ${{ contains(github.ref_name, 'beta') || contains(github.ref_name, 'alpha') || contains(github.ref_name, 'rc') }}
26
26
  env:
27
27
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28
28
 
@@ -32,25 +32,26 @@ jobs:
32
32
  id-token: write
33
33
  contents: read
34
34
  runs-on: ubuntu-latest
35
+ timeout-minutes: 10
35
36
  steps:
36
- - uses: actions/checkout@v6
37
- - uses: actions/setup-node@v6
38
- with:
39
- node-version: '24.x'
40
- registry-url: 'https://registry.npmjs.org'
37
+ - uses: actions/checkout@v6
38
+
39
+ - uses: actions/setup-node@v6
40
+ with:
41
+ node-version: '24.x'
42
+ registry-url: 'https://registry.npmjs.org'
41
43
 
42
- - name: Install
43
- run: |
44
- npm install
44
+ - run: npm ci
45
45
 
46
- - name: Publish
47
- run: |
48
- npm install -g npm@latest
49
- npm --version
50
- tag="${GITHUB_REF#refs/tags/}"
51
- if [[ "$tag" == *beta* ]];
52
- then
46
+ - name: Publish to npm
47
+ run: |
48
+ tag="${GITHUB_REF#refs/tags/}"
49
+ if [[ "$tag" == *alpha* ]]; then
50
+ npm publish --provenance --access public --tag alpha
51
+ elif [[ "$tag" == *beta* ]]; then
53
52
  npm publish --provenance --access public --tag beta
53
+ elif [[ "$tag" == *rc* ]]; then
54
+ npm publish --provenance --access public --tag rc
54
55
  else
55
56
  npm publish --provenance --access public
56
- fi
57
+ fi
package/eslint.config.js CHANGED
@@ -45,7 +45,9 @@ module.exports = [
45
45
  Promise: 'readonly',
46
46
  Set: 'readonly',
47
47
  Infinity: 'readonly',
48
- MutationObserver: 'readonly'
48
+ MutationObserver: 'readonly',
49
+ URLSearchParams: 'readonly',
50
+ location: 'readonly'
49
51
  }
50
52
  },
51
53
  rules: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signalk/app-dock",
3
- "version": "0.3.0-beta.1",
3
+ "version": "0.3.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"
package/public/dock.js CHANGED
@@ -399,9 +399,13 @@
399
399
  }
400
400
 
401
401
  // ─── Show / hide dock ────────────────────────────────────────────────────────
402
+ let dockShownAt = 0
403
+ const DOCK_CLICK_IGNORE_MS = 500
404
+
402
405
  function showDock() {
403
406
  if (dockVisible) return
404
407
  dockVisible = true
408
+ dockShownAt = Date.now()
405
409
  $dock.classList.add('visible')
406
410
  $backdrop.classList.add('visible')
407
411
  }
@@ -415,7 +419,17 @@
415
419
  resetMagnification()
416
420
  }
417
421
 
418
- $backdrop.addEventListener('click', hideDock)
422
+ $backdrop.addEventListener('click', (e) => {
423
+ // Windows touch synthesizes a click on whatever's under the finger after
424
+ // pointerdown. If that click lands on the just-shown backdrop, it would
425
+ // immediately close the dock. Ignore clicks within a short window after
426
+ // the dock opens.
427
+ if (Date.now() - dockShownAt < DOCK_CLICK_IGNORE_MS) {
428
+ e.stopPropagation()
429
+ return
430
+ }
431
+ hideDock()
432
+ })
419
433
 
420
434
  // ─── Loading overlay helpers ──────────────────────────────────────────────────
421
435
  function showLoading(label) {
@@ -474,28 +488,39 @@
474
488
  // tap/click normally. A double-tap anywhere opens the dock. Side effect:
475
489
  // double-tapping a button fires that button once before the dock opens —
476
490
  // users are expected to aim double-taps at non-interactive regions.
477
- const DOUBLE_TAP_MS = 280
478
- const DOUBLE_TAP_MIN_GAP_MS = 60
479
- const DOUBLE_TAP_SLOP = 24
491
+ const DOUBLE_TAP_MS = 450
492
+ const DOUBLE_TAP_SLOP = 40
480
493
 
481
494
  let lastTapTime = 0
482
495
  let lastTapX = 0
483
496
  let lastTapY = 0
484
497
 
485
498
  function triggerDock() {
499
+ if (DEBUG) console.log('[Dock] triggerDock called, dockVisible=', dockVisible)
486
500
  if (dockVisible) return
487
501
  showDock()
502
+ if (DEBUG) {
503
+ const r = $dock.getBoundingClientRect()
504
+ console.log('[Dock] after showDock: dock rect', r, 'classes', $dock.className)
505
+ }
488
506
  if (navigator.vibrate) navigator.vibrate(8)
489
507
  }
490
508
 
509
+ const DEBUG = new URLSearchParams(location.search).has('debugDock')
510
+
491
511
  function handleTap(clientX, clientY) {
492
512
  if (dockVisible) return
493
513
  const now = Date.now()
494
514
  const dx = clientX - lastTapX
495
515
  const dy = clientY - lastTapY
496
- const close = dx * dx + dy * dy <= DOUBLE_TAP_SLOP * DOUBLE_TAP_SLOP
516
+ const dist = Math.round(Math.sqrt(dx * dx + dy * dy))
517
+ const close = dist <= DOUBLE_TAP_SLOP
497
518
  const gap = now - lastTapTime
498
- if (gap >= DOUBLE_TAP_MIN_GAP_MS && gap < DOUBLE_TAP_MS && close) {
519
+ const match = gap < DOUBLE_TAP_MS && close
520
+ if (DEBUG) {
521
+ console.log('[Dock] tap', { x: Math.round(clientX), y: Math.round(clientY), gap, dist, close, match })
522
+ }
523
+ if (match) {
499
524
  lastTapTime = 0
500
525
  triggerDock()
501
526
  } else {
@@ -515,11 +540,39 @@
515
540
  return { x: r.left, y: r.top }
516
541
  }
517
542
 
543
+ // Dedupe: touchstart and pointerdown often both fire for the same gesture.
544
+ // We accept whichever arrives first and ignore the other if it's within
545
+ // 50 ms (the browser's synthesized-event coalescence window).
546
+ let lastEventAt = 0
547
+ const COALESCE_MS = 50
548
+
549
+ const onDown = (x, y) => {
550
+ const now = Date.now()
551
+ if (now - lastEventAt < COALESCE_MS) return
552
+ lastEventAt = now
553
+ handleTap(x, y)
554
+ }
555
+
518
556
  doc.addEventListener(
519
557
  'pointerdown',
520
558
  (e) => {
521
559
  const o = offset()
522
- handleTap(e.clientX + o.x, e.clientY + o.y)
560
+ onDown(e.clientX + o.x, e.clientY + o.y)
561
+ },
562
+ { passive: true, capture: true }
563
+ )
564
+
565
+ // Fallback for Windows touchscreens + some browsers where the root
566
+ // document does not receive pointerdown for finger input (observed on
567
+ // W11 with touch-action: none). touchstart is the universally-supported
568
+ // event for direct touch.
569
+ doc.addEventListener(
570
+ 'touchstart',
571
+ (e) => {
572
+ if (!e.touches || e.touches.length === 0) return
573
+ const t = e.touches[0]
574
+ const o = offset()
575
+ onDown(t.clientX + o.x, t.clientY + o.y)
523
576
  },
524
577
  { passive: true, capture: true }
525
578
  )