@signalk/app-dock 1.2.0 → 1.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.
- package/README.md +20 -0
- package/package.json +6 -1
- package/public/app-icon-180.png +0 -0
- package/public/app-icon.svg +2 -4
- package/public/dock.css +62 -0
- package/public/dock.js +141 -4
- package/public/index.html +4 -0
- package/.gitattributes +0 -5
- package/.github/workflows/ci.yml +0 -11
- package/.github/workflows/release_on_tag.yml +0 -57
- package/.github/workflows/stale.yml +0 -42
- package/.prettierignore +0 -6
- package/.prettierrc +0 -6
- package/eslint.config.js +0 -59
- package/src/configpanel/PluginConfigurationPanel.js +0 -549
- package/src/configpanel/index.js +0 -1
- package/test/plugin.test.js +0 -696
- package/webpack.config.js +0 -39
package/README.md
CHANGED
|
@@ -37,6 +37,26 @@ Tip: double-tapping a clickable element in the underlying webapp will activate t
|
|
|
37
37
|
|
|
38
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.
|
|
39
39
|
|
|
40
|
+
## Apps on another origin
|
|
41
|
+
|
|
42
|
+
The dock notices a double-tap by listening inside each app's page, and a browser allows that only for pages on the same origin (scheme, host and port) as the dock. A page from another port or host is out of reach: the app itself, a page the app moves its frame to (such as a radar display opened directly on its own server), or one the app shows in a frame of its own. While any such page in the app on screen does not forward its taps, a small tab at the dock's edge opens the dock with a single tap.
|
|
43
|
+
|
|
44
|
+
An app on another origin can bring back double-tap by forwarding its pointerdowns to the dock:
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
if (window.self !== window.top) {
|
|
48
|
+
document.addEventListener(
|
|
49
|
+
'pointerdown',
|
|
50
|
+
(e) => {
|
|
51
|
+
window.top.postMessage({ type: 'signalk-app-dock:pointerdown', x: e.clientX, y: e.clientY }, '*')
|
|
52
|
+
},
|
|
53
|
+
{ passive: true, capture: true }
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The dock accepts these messages only from a page of the app on screen that it cannot listen to itself, or a frame inside that page, and all they do is count towards a double-tap. The tab goes away once every such page has sent one. A page that loads again starts over: the tab is back until its first tap.
|
|
59
|
+
|
|
40
60
|
## Install as an app
|
|
41
61
|
|
|
42
62
|
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.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signalk/app-dock",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.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"
|
|
@@ -22,6 +22,11 @@
|
|
|
22
22
|
]
|
|
23
23
|
},
|
|
24
24
|
"main": "plugin/index.js",
|
|
25
|
+
"files": [
|
|
26
|
+
"plugin/",
|
|
27
|
+
"public/",
|
|
28
|
+
"docs/screenshots/"
|
|
29
|
+
],
|
|
25
30
|
"scripts": {
|
|
26
31
|
"build:config": "webpack",
|
|
27
32
|
"prepublishOnly": "webpack",
|
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 -->
|
package/public/dock.css
CHANGED
|
@@ -99,6 +99,68 @@ body {
|
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
/* ─── Handle over an app on another origin ─────────────────────────────────── */
|
|
103
|
+
/* The padding is the touch target; the bar itself is drawn by ::before. */
|
|
104
|
+
#dock-handle {
|
|
105
|
+
position: fixed;
|
|
106
|
+
z-index: 5;
|
|
107
|
+
display: none;
|
|
108
|
+
padding: 12px 18px;
|
|
109
|
+
border: none;
|
|
110
|
+
background: transparent;
|
|
111
|
+
-webkit-appearance: none;
|
|
112
|
+
appearance: none;
|
|
113
|
+
cursor: pointer;
|
|
114
|
+
touch-action: manipulation;
|
|
115
|
+
-webkit-tap-highlight-color: transparent;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
#dock-handle.visible {
|
|
119
|
+
display: block;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
#dock-handle::before {
|
|
123
|
+
content: '';
|
|
124
|
+
display: block;
|
|
125
|
+
width: 56px;
|
|
126
|
+
height: 6px;
|
|
127
|
+
border-radius: 3px;
|
|
128
|
+
background: rgba(255, 255, 255, 0.6);
|
|
129
|
+
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.35);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
#dock-handle.pos-bottom {
|
|
133
|
+
bottom: env(safe-area-inset-bottom, 0px);
|
|
134
|
+
left: 50%;
|
|
135
|
+
transform: translateX(-50%);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
#dock-handle.pos-top {
|
|
139
|
+
top: env(safe-area-inset-top, 0px);
|
|
140
|
+
left: 50%;
|
|
141
|
+
transform: translateX(-50%);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
#dock-handle.pos-left {
|
|
145
|
+
left: env(safe-area-inset-left, 0px);
|
|
146
|
+
top: 50%;
|
|
147
|
+
transform: translateY(-50%);
|
|
148
|
+
padding: 18px 12px;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
#dock-handle.pos-right {
|
|
152
|
+
right: env(safe-area-inset-right, 0px);
|
|
153
|
+
top: 50%;
|
|
154
|
+
transform: translateY(-50%);
|
|
155
|
+
padding: 18px 12px;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
#dock-handle.pos-left::before,
|
|
159
|
+
#dock-handle.pos-right::before {
|
|
160
|
+
width: 6px;
|
|
161
|
+
height: 56px;
|
|
162
|
+
}
|
|
163
|
+
|
|
102
164
|
/* ─── Backdrop ──────────────────────────────────────────────────────────────── */
|
|
103
165
|
#dock-backdrop {
|
|
104
166
|
position: fixed;
|
package/public/dock.js
CHANGED
|
@@ -164,6 +164,7 @@
|
|
|
164
164
|
const $loadingLabel = document.getElementById('loading-label')
|
|
165
165
|
const $idleHint = document.getElementById('idle-hint')
|
|
166
166
|
const $idleHintText = document.getElementById('idle-hint-text')
|
|
167
|
+
const $dockHandle = document.getElementById('dock-handle')
|
|
167
168
|
|
|
168
169
|
// ─── State ───────────────────────────────────────────────────────────────────
|
|
169
170
|
let dockVisible = false
|
|
@@ -250,6 +251,7 @@
|
|
|
250
251
|
const pos = cfg.position
|
|
251
252
|
$dock.classList.add(`pos-${pos}`)
|
|
252
253
|
$dockInner.classList.add(`align-${pos}`)
|
|
254
|
+
$dockHandle.classList.add(`pos-${pos}`)
|
|
253
255
|
|
|
254
256
|
const isVertical = pos === 'left' || pos === 'right'
|
|
255
257
|
if (isVertical) $dockInner.classList.add('layout-vertical')
|
|
@@ -576,6 +578,7 @@
|
|
|
576
578
|
dockShownAt = Date.now()
|
|
577
579
|
$dock.classList.add('visible')
|
|
578
580
|
$backdrop.classList.add('visible')
|
|
581
|
+
updateHandle()
|
|
579
582
|
}
|
|
580
583
|
|
|
581
584
|
function hideDock() {
|
|
@@ -585,6 +588,7 @@
|
|
|
585
588
|
$dock.classList.remove('visible')
|
|
586
589
|
$backdrop.classList.remove('visible')
|
|
587
590
|
resetMagnification()
|
|
591
|
+
updateHandle()
|
|
588
592
|
}
|
|
589
593
|
|
|
590
594
|
$backdrop.addEventListener('click', (e) => {
|
|
@@ -649,6 +653,7 @@
|
|
|
649
653
|
}
|
|
650
654
|
|
|
651
655
|
iframes[app.url].classList.add('active')
|
|
656
|
+
updateHandle()
|
|
652
657
|
hideDock()
|
|
653
658
|
}
|
|
654
659
|
|
|
@@ -665,6 +670,9 @@
|
|
|
665
670
|
let lastTapTime = 0
|
|
666
671
|
let lastTapX = 0
|
|
667
672
|
let lastTapY = 0
|
|
673
|
+
// Coordinates are in the dock's viewport unless a tap names another space:
|
|
674
|
+
// two taps pair up only within the same one.
|
|
675
|
+
let lastTapSpace = null
|
|
668
676
|
|
|
669
677
|
function triggerDock() {
|
|
670
678
|
if (DEBUG) console.log('[Dock] triggerDock called, dockVisible=', dockVisible)
|
|
@@ -679,13 +687,13 @@
|
|
|
679
687
|
|
|
680
688
|
const DEBUG = new URLSearchParams(location.search).has('debugDock')
|
|
681
689
|
|
|
682
|
-
function handleTap(clientX, clientY) {
|
|
690
|
+
function handleTap(clientX, clientY, space = null) {
|
|
683
691
|
if (dockVisible) return
|
|
684
692
|
const now = Date.now()
|
|
685
693
|
const dx = clientX - lastTapX
|
|
686
694
|
const dy = clientY - lastTapY
|
|
687
695
|
const dist = Math.round(Math.sqrt(dx * dx + dy * dy))
|
|
688
|
-
const close = dist <= DOUBLE_TAP_SLOP
|
|
696
|
+
const close = dist <= DOUBLE_TAP_SLOP && space === lastTapSpace
|
|
689
697
|
const gap = now - lastTapTime
|
|
690
698
|
const match = gap < DOUBLE_TAP_MS && close
|
|
691
699
|
if (DEBUG) {
|
|
@@ -698,6 +706,7 @@
|
|
|
698
706
|
lastTapTime = now
|
|
699
707
|
lastTapX = clientX
|
|
700
708
|
lastTapY = clientY
|
|
709
|
+
lastTapSpace = space
|
|
701
710
|
}
|
|
702
711
|
}
|
|
703
712
|
|
|
@@ -756,12 +765,14 @@
|
|
|
756
765
|
|
|
757
766
|
function attachToIframe(frame) {
|
|
758
767
|
const wire = () => {
|
|
759
|
-
let doc
|
|
768
|
+
let doc = null
|
|
760
769
|
try {
|
|
761
770
|
doc = frame.contentDocument
|
|
762
771
|
} catch {
|
|
763
|
-
|
|
772
|
+
// cross-origin; can't reach
|
|
764
773
|
}
|
|
774
|
+
const app = appFrameOf(frame)
|
|
775
|
+
if (app) noteFrameReach(app, frame, doc !== null)
|
|
765
776
|
if (!doc || attachedDocs.has(doc)) return
|
|
766
777
|
attachedDocs.add(doc)
|
|
767
778
|
attachTapListener(doc, frame)
|
|
@@ -786,11 +797,137 @@
|
|
|
786
797
|
const nested = n.querySelectorAll && n.querySelectorAll('iframe')
|
|
787
798
|
if (nested) nested.forEach((f) => attachToIframe(f))
|
|
788
799
|
})
|
|
800
|
+
// A removed frame may have been the one keeping the handle up.
|
|
801
|
+
if (r.removedNodes.length > 0) updateHandle()
|
|
789
802
|
}
|
|
790
803
|
})
|
|
791
804
|
obs.observe(root, { childList: true, subtree: true })
|
|
792
805
|
}
|
|
793
806
|
|
|
807
|
+
// ─── Apps on another origin ──────────────────────────────────────────────────
|
|
808
|
+
// The listeners above cannot reach a page from another origin, so a
|
|
809
|
+
// double-tap over one goes unseen. That page may be the app itself or a
|
|
810
|
+
// frame inside it. Such a page can forward its pointerdowns instead, and
|
|
811
|
+
// while any page in the active app is out of reach and not forwarding, a
|
|
812
|
+
// tab at the dock's edge opens the dock with a single tap.
|
|
813
|
+
//
|
|
814
|
+
// A forwarded pointerdown is a message posted to the dock's window:
|
|
815
|
+
// window.top.postMessage({ type: 'signalk-app-dock:pointerdown', x: e.clientX, y: e.clientY }, '*')
|
|
816
|
+
// It counts only from inside a frame of the active app that the listeners
|
|
817
|
+
// cannot reach, so no tap is counted twice, and all it can do is feed the
|
|
818
|
+
// double-tap detection.
|
|
819
|
+
const FORWARDED_POINTERDOWN = 'signalk-app-dock:pointerdown'
|
|
820
|
+
const FORWARD_COALESCE_MS = 50
|
|
821
|
+
// Per app frame, the frames in it (itself included) the listeners cannot
|
|
822
|
+
// reach, and which of those have sent a pointerdown of their own since they
|
|
823
|
+
// loaded.
|
|
824
|
+
const unreachableFrames = new WeakMap()
|
|
825
|
+
const forwardingFrames = new WeakSet()
|
|
826
|
+
let lastForwardedAt = 0
|
|
827
|
+
|
|
828
|
+
// The app frame a frame belongs to: the frame itself, or the one holding
|
|
829
|
+
// the document it sits in, found through same-origin parents.
|
|
830
|
+
function appFrameOf(frame) {
|
|
831
|
+
let f = frame
|
|
832
|
+
while (f && f.parentElement !== $iframeContainer) {
|
|
833
|
+
const win = f.ownerDocument && f.ownerDocument.defaultView
|
|
834
|
+
f = win ? win.frameElement : null
|
|
835
|
+
}
|
|
836
|
+
return f
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// Runs on every load of a frame in an app. A new document starts over:
|
|
840
|
+
// whether it forwards is not known until it does, and an app frame's new
|
|
841
|
+
// document brings its own frames.
|
|
842
|
+
function noteFrameReach(app, frame, reachable) {
|
|
843
|
+
forwardingFrames.delete(frame)
|
|
844
|
+
let frames = unreachableFrames.get(app)
|
|
845
|
+
if (frame === app || !frames) {
|
|
846
|
+
frames = new Set()
|
|
847
|
+
unreachableFrames.set(app, frames)
|
|
848
|
+
}
|
|
849
|
+
if (reachable) frames.delete(frame)
|
|
850
|
+
else frames.add(frame)
|
|
851
|
+
updateHandle()
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// Frames removed from their document, or left in one that has been
|
|
855
|
+
// navigated away, are dropped as they are found.
|
|
856
|
+
function liveUnreachableFrames(app) {
|
|
857
|
+
const frames = (app && unreachableFrames.get(app)) || new Set()
|
|
858
|
+
for (const f of frames) {
|
|
859
|
+
if (!f.isConnected || !f.ownerDocument.defaultView) frames.delete(f)
|
|
860
|
+
}
|
|
861
|
+
return frames
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
function updateHandle() {
|
|
865
|
+
const app = $iframeContainer.querySelector('iframe.active')
|
|
866
|
+
let show = false
|
|
867
|
+
if (!dockVisible) {
|
|
868
|
+
for (const f of liveUnreachableFrames(app)) {
|
|
869
|
+
if (!forwardingFrames.has(f)) show = true
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
$dockHandle.classList.toggle('visible', show)
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// Where a frame's content starts in the dock's viewport, adding up the
|
|
876
|
+
// frames it sits in.
|
|
877
|
+
function offsetInDock(frame) {
|
|
878
|
+
let x = 0
|
|
879
|
+
let y = 0
|
|
880
|
+
for (let f = frame; f; ) {
|
|
881
|
+
const r = f.getBoundingClientRect()
|
|
882
|
+
x += r.left
|
|
883
|
+
y += r.top
|
|
884
|
+
if (f.parentElement === $iframeContainer) break
|
|
885
|
+
const win = f.ownerDocument.defaultView
|
|
886
|
+
f = win ? win.frameElement : null
|
|
887
|
+
}
|
|
888
|
+
return { x, y }
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function isInsideFrame(source, frameWindow) {
|
|
892
|
+
for (let w = source; w; w = w.parent) {
|
|
893
|
+
if (w === frameWindow) return true
|
|
894
|
+
if (w === w.parent) return false
|
|
895
|
+
}
|
|
896
|
+
return false
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
window.addEventListener('message', (e) => {
|
|
900
|
+
const data = e.data
|
|
901
|
+
if (!data || data.type !== FORWARDED_POINTERDOWN) return
|
|
902
|
+
if (!Number.isFinite(data.x) || !Number.isFinite(data.y)) return
|
|
903
|
+
const app = $iframeContainer.querySelector('iframe.active')
|
|
904
|
+
if (!app || !e.source) return
|
|
905
|
+
let from = null
|
|
906
|
+
for (const f of liveUnreachableFrames(app)) {
|
|
907
|
+
if (isInsideFrame(e.source, f.contentWindow)) from = f
|
|
908
|
+
}
|
|
909
|
+
if (!from) return
|
|
910
|
+
// A frame inside the page forwarding says nothing about taps on the page
|
|
911
|
+
// around it, so only the page's own message stops the handle.
|
|
912
|
+
if (e.source === from.contentWindow) {
|
|
913
|
+
forwardingFrames.add(from)
|
|
914
|
+
updateHandle()
|
|
915
|
+
}
|
|
916
|
+
const now = Date.now()
|
|
917
|
+
if (now - lastForwardedAt < FORWARD_COALESCE_MS) return
|
|
918
|
+
lastForwardedAt = now
|
|
919
|
+
// A frame inside the page sits somewhere the dock cannot see, so its
|
|
920
|
+
// taps keep their own coordinates and pair up only with each other.
|
|
921
|
+
if (e.source === from.contentWindow) {
|
|
922
|
+
const o = offsetInDock(from)
|
|
923
|
+
handleTap(data.x + o.x, data.y + o.y)
|
|
924
|
+
} else {
|
|
925
|
+
handleTap(data.x, data.y, e.source)
|
|
926
|
+
}
|
|
927
|
+
})
|
|
928
|
+
|
|
929
|
+
$dockHandle.addEventListener('click', triggerDock)
|
|
930
|
+
|
|
794
931
|
attachTapListener(document, null)
|
|
795
932
|
attachedDocs.add(document)
|
|
796
933
|
scanAndObserveDoc(document)
|
package/public/index.html
CHANGED
|
@@ -28,6 +28,10 @@
|
|
|
28
28
|
<div id="idle-hint-text"></div>
|
|
29
29
|
</div>
|
|
30
30
|
|
|
31
|
+
<!-- Tab that opens the dock over an app on another origin, where a
|
|
32
|
+
double-tap cannot be detected -->
|
|
33
|
+
<button id="dock-handle" type="button" aria-label="Open the dock"></button>
|
|
34
|
+
|
|
31
35
|
<!-- Dimming overlay shown when dock is open; tap to close -->
|
|
32
36
|
<div id="dock-backdrop"></div>
|
|
33
37
|
|
package/.gitattributes
DELETED
package/.github/workflows/ci.yml
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
name: 'Release on tag'
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
tags:
|
|
6
|
-
- 'v*'
|
|
7
|
-
|
|
8
|
-
concurrency:
|
|
9
|
-
group: release-${{ github.ref }}
|
|
10
|
-
cancel-in-progress: false
|
|
11
|
-
|
|
12
|
-
jobs:
|
|
13
|
-
release:
|
|
14
|
-
permissions:
|
|
15
|
-
contents: write
|
|
16
|
-
runs-on: ubuntu-latest
|
|
17
|
-
timeout-minutes: 10
|
|
18
|
-
steps:
|
|
19
|
-
- name: Create Release
|
|
20
|
-
uses: softprops/action-gh-release@v3
|
|
21
|
-
with:
|
|
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
|
-
env:
|
|
27
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
28
|
-
|
|
29
|
-
publish:
|
|
30
|
-
needs: release
|
|
31
|
-
permissions:
|
|
32
|
-
id-token: write
|
|
33
|
-
contents: read
|
|
34
|
-
runs-on: ubuntu-latest
|
|
35
|
-
timeout-minutes: 10
|
|
36
|
-
steps:
|
|
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'
|
|
43
|
-
|
|
44
|
-
- run: npm ci
|
|
45
|
-
|
|
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
|
|
52
|
-
npm publish --provenance --access public --tag beta
|
|
53
|
-
elif [[ "$tag" == *rc* ]]; then
|
|
54
|
-
npm publish --provenance --access public --tag rc
|
|
55
|
-
else
|
|
56
|
-
npm publish --provenance --access public
|
|
57
|
-
fi
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# Marks issues labeled "awaiting response" as stale after 30 days of inactivity
|
|
2
|
-
# and closes them 30 days after that (60 days total). Issues labeled
|
|
3
|
-
# "good first task" or "help wanted" are exempt. Pull requests are never
|
|
4
|
-
# touched. The workflow runs daily and can also be triggered manually.
|
|
5
|
-
name: Stale issues
|
|
6
|
-
|
|
7
|
-
on:
|
|
8
|
-
schedule:
|
|
9
|
-
- cron: '17 1 * * *'
|
|
10
|
-
workflow_dispatch:
|
|
11
|
-
|
|
12
|
-
permissions:
|
|
13
|
-
contents: read
|
|
14
|
-
issues: write
|
|
15
|
-
|
|
16
|
-
concurrency:
|
|
17
|
-
group: stale
|
|
18
|
-
cancel-in-progress: false
|
|
19
|
-
|
|
20
|
-
jobs:
|
|
21
|
-
stale:
|
|
22
|
-
runs-on: ubuntu-latest
|
|
23
|
-
timeout-minutes: 10
|
|
24
|
-
steps:
|
|
25
|
-
- uses: actions/stale@v10
|
|
26
|
-
with:
|
|
27
|
-
days-before-issue-stale: 30
|
|
28
|
-
days-before-issue-close: 30
|
|
29
|
-
stale-issue-label: 'stale'
|
|
30
|
-
any-of-issue-labels: 'awaiting response'
|
|
31
|
-
exempt-issue-labels: 'good first task,help wanted'
|
|
32
|
-
stale-issue-message: >
|
|
33
|
-
This issue is marked as awaiting a response from the reporter and
|
|
34
|
-
has had no activity for 30 days. It will be closed in another 30
|
|
35
|
-
days if there is no further update. If the issue is still
|
|
36
|
-
relevant, please reply with any missing information.
|
|
37
|
-
close-issue-message: >
|
|
38
|
-
Closing this issue because it has been stale for 60 days without
|
|
39
|
-
a response. Please reopen if this is still relevant.
|
|
40
|
-
days-before-pr-stale: -1
|
|
41
|
-
days-before-pr-close: -1
|
|
42
|
-
operations-per-run: 100
|
package/.prettierignore
DELETED
package/.prettierrc
DELETED
package/eslint.config.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
const prettier = require('eslint-config-prettier')
|
|
2
|
-
|
|
3
|
-
module.exports = [
|
|
4
|
-
{ ignores: ['src/**', 'public/remoteEntry.js', 'public/540.js', 'public/805.js', 'public/main.js'] },
|
|
5
|
-
{
|
|
6
|
-
files: ['plugin/**/*.js', 'test/**/*.js'],
|
|
7
|
-
languageOptions: {
|
|
8
|
-
ecmaVersion: 2022,
|
|
9
|
-
sourceType: 'commonjs',
|
|
10
|
-
globals: {
|
|
11
|
-
require: 'readonly',
|
|
12
|
-
module: 'readonly',
|
|
13
|
-
exports: 'readonly',
|
|
14
|
-
__dirname: 'readonly',
|
|
15
|
-
__filename: 'readonly',
|
|
16
|
-
process: 'readonly',
|
|
17
|
-
console: 'readonly',
|
|
18
|
-
setTimeout: 'readonly',
|
|
19
|
-
clearTimeout: 'readonly',
|
|
20
|
-
setInterval: 'readonly',
|
|
21
|
-
clearInterval: 'readonly',
|
|
22
|
-
globalThis: 'readonly'
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
rules: {
|
|
26
|
-
'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
|
|
27
|
-
'no-undef': 'error'
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
files: ['public/dock.js'],
|
|
32
|
-
languageOptions: {
|
|
33
|
-
ecmaVersion: 2022,
|
|
34
|
-
sourceType: 'script',
|
|
35
|
-
globals: {
|
|
36
|
-
window: 'readonly',
|
|
37
|
-
document: 'readonly',
|
|
38
|
-
navigator: 'readonly',
|
|
39
|
-
fetch: 'readonly',
|
|
40
|
-
console: 'readonly',
|
|
41
|
-
setTimeout: 'readonly',
|
|
42
|
-
clearTimeout: 'readonly',
|
|
43
|
-
setInterval: 'readonly',
|
|
44
|
-
requestAnimationFrame: 'readonly',
|
|
45
|
-
Promise: 'readonly',
|
|
46
|
-
Set: 'readonly',
|
|
47
|
-
Infinity: 'readonly',
|
|
48
|
-
MutationObserver: 'readonly',
|
|
49
|
-
URLSearchParams: 'readonly',
|
|
50
|
-
location: 'readonly'
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
rules: {
|
|
54
|
-
'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
|
|
55
|
-
'no-undef': 'error'
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
prettier
|
|
59
|
-
]
|