@signalk/app-dock 1.4.2 → 1.5.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 +10 -10
- package/dist/plugin/schema.js +3 -3
- package/package.json +1 -1
- package/public/configpanel/PluginConfigurationPanel.js +1 -1
- package/public/dock.js +1 -1
package/README.md
CHANGED
|
@@ -91,16 +91,16 @@ Open **Plugin Config > App Dock** in the admin UI. The embedded configurator pro
|
|
|
91
91
|
|
|
92
92
|
### Settings
|
|
93
93
|
|
|
94
|
-
| Setting | Default
|
|
95
|
-
| ---------------------- |
|
|
96
|
-
| `position` | `bottom`
|
|
97
|
-
| `iframeMode` | `
|
|
98
|
-
| `iconSize` | `56`
|
|
99
|
-
| `magnification` | `true`
|
|
100
|
-
| `magnificationScale` | `1.7`
|
|
101
|
-
| `showNightModeButton` | `true`
|
|
102
|
-
| `showFullscreenButton` | `false`
|
|
103
|
-
| `showExitButton` | `false`
|
|
94
|
+
| Setting | Default | Description |
|
|
95
|
+
| ---------------------- | --------- | ---------------------------------------------------------------------------------------------- |
|
|
96
|
+
| `position` | `bottom` | Dock edge: `bottom`, `top`, `left`, `right` |
|
|
97
|
+
| `iframeMode` | `destroy` | `destroy` reloads an app each time it opens; `keep-alive` keeps hidden apps loaded and running |
|
|
98
|
+
| `iconSize` | `56` | Base icon size in px |
|
|
99
|
+
| `magnification` | `true` | Enable macOS-style magnification effect |
|
|
100
|
+
| `magnificationScale` | `1.7` | Max icon scale (1.0-2.5) |
|
|
101
|
+
| `showNightModeButton` | `true` | Show sun/moon button in dock; PUT-writes `environment.mode` |
|
|
102
|
+
| `showFullscreenButton` | `false` | Show fullscreen toggle in dock; hidden automatically where the Fullscreen API is unavailable |
|
|
103
|
+
| `showExitButton` | `false` | Show X button that returns to the Signal K admin UI |
|
|
104
104
|
|
|
105
105
|
In either `iframeMode`, the server's admin UI (an app whose URL is under `/admin/`, such as the Settings tile) opens fresh every time, at the URL its tile points to. A webapp or page opened from inside the admin UI does not come back the next time Settings is tapped.
|
|
106
106
|
|
package/dist/plugin/schema.js
CHANGED
|
@@ -20,9 +20,9 @@ export const ConfigSchema = Type.Object({
|
|
|
20
20
|
iframeMode: Type.Optional(Type.Enum(['keep-alive', 'destroy'], {
|
|
21
21
|
type: 'string',
|
|
22
22
|
title: 'iFrame lifecycle',
|
|
23
|
-
description: '
|
|
24
|
-
'
|
|
25
|
-
default: '
|
|
23
|
+
description: 'destroy: reload on every switch, so only the app on screen runs (slower switching, minimal RAM and CPU). ' +
|
|
24
|
+
'keep-alive: load once and hide/show; hidden apps keep running (faster switching, more RAM and CPU).',
|
|
25
|
+
default: 'destroy'
|
|
26
26
|
})),
|
|
27
27
|
iconSize: Type.Optional(Type.Number({ title: 'Icon size (px)', default: 56 })),
|
|
28
28
|
magnification: Type.Optional(Type.Boolean({ title: 'Enable dock magnification effect', default: true })),
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var e=window.__SK_REACT__;if(!e)throw Error(`App Dock: window.__SK_REACT__ is not set. The host signalk-server admin UI must expose React on window for plugin federation. Requires signalk-server 2.27.0 or newer.`);var{Activity:t,Children:n,Component:r,Fragment:i,Profiler:a,PureComponent:o,StrictMode:s,Suspense:ee,cache:c,cacheSignal:l,cloneElement:u,createContext:te,createElement:d,createRef:f,forwardRef:p,isValidElement:m,lazy:h,memo:g,startTransition:_,use:v,useActionState:y,useCallback:b,useContext:x,useDebugValue:S,useDeferredValue:C,useEffect:w,useEffectEvent:T,useId:E,useImperativeHandle:D,useInsertionEffect:O,useLayoutEffect:k,useMemo:A,useOptimistic:j,useReducer:M,useRef:N,useState:P,useSyncExternalStore:F,useTransition:I,version:L}=e,R=(e,t)=>`<svg class="dock-svg-icon dock-svg-${e}" width="100%" height="100%" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${t}</svg>`,z={sun:R(`sun`,`<circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/>`),moon:R(`moon`,`<path d="M20.5 14A9 9 0 1 1 10 3.5a7.5 7.5 0 0 0 10.5 10.5z"/>`),fullscreen:R(`fullscreen`,`<path d="M4 9V4h5M15 4h5v5M20 15v5h-5M9 20H4v-5"/>`),exitFullscreen:R(`exit-fullscreen`,`<path d="M9 4v5H4M20 9h-5V4M15 20v-5h5M4 15h5v5"/>`),close:R(`close`,`<path d="M6 6l12 12M18 6L6 18"/>`)},B=window.__SK_REACT_JSX_RUNTIME__;if(!B)throw Error(`App Dock: window.__SK_REACT_JSX_RUNTIME__ is not set by the host.`);var V=B,{Fragment:H,jsx:U,jsxs:W}=V;V.jsxDEV;function G(e){return e instanceof Error?e.message:String(e)}var K={root:{fontFamily:`-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif`,color:`#333`,padding:`16px 0`},sectionTitle:{fontSize:13,fontWeight:600,color:`#888`,textTransform:`uppercase`,letterSpacing:`0.05em`,marginBottom:10,marginTop:24},btn:{display:`inline-flex`,alignItems:`center`,gap:8,padding:`8px 16px`,border:`none`,borderRadius:6,fontSize:13,fontWeight:600,cursor:`pointer`},btnPrimary:{background:`#3b82f6`,color:`#fff`},btnSave:{background:`#10b981`,color:`#fff`},btnDanger:{background:`#ef4444`,color:`#fff`,padding:`4px 10px`,fontSize:11},status:{marginTop:8,fontSize:12,minHeight:18},item:{display:`flex`,alignItems:`center`,gap:10,padding:`8px 12px`,background:`#f8f9fa`,border:`1px solid #e0e0e0`,borderRadius:8,marginBottom:6,cursor:`grab`},itemDisabled:{opacity:.4},dragOver:{borderColor:`#3b82f6`,background:`#eef4ff`},handle:{color:`#bbb`,fontSize:16,cursor:`grab`,userSelect:`none`,flexShrink:0,width:18,textAlign:`center`},iconBox:{width:36,height:36,borderRadius:8,display:`flex`,alignItems:`center`,justifyContent:`center`,background:`#e2e8f0`,overflow:`hidden`,flexShrink:0,fontSize:18},iconImg:{width:`100%`,height:`100%`,objectFit:`contain`,padding:5},info:{flex:1,minWidth:0},name:{fontSize:13,fontWeight:600,color:`#333`},url:{fontSize:10,color:`#999`,overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`},toggle:{flexShrink:0},checkbox:{width:16,height:16,cursor:`pointer`,accentColor:`#3b82f6`},preview:{display:`flex`,alignItems:`flex-end`,justifyContent:`center`,gap:6,padding:`12px 18px`,background:`rgba(30,30,32,0.85)`,border:`1px solid rgba(255,255,255,0.18)`,borderRadius:18,margin:`16px auto`,width:`fit-content`},actions:{display:`flex`,gap:10,alignItems:`center`,marginTop:16},empty:{textAlign:`center`,padding:`30px 16px`,color:`#999`,fontSize:13},fieldRow:{display:`flex`,alignItems:`center`,gap:12,marginBottom:10},label:{fontSize:13,fontWeight:500,color:`#555`,width:160,flexShrink:0},select:{padding:`6px 10px`,borderRadius:6,border:`1px solid #ccc`,fontSize:13,background:`#fff`,color:`#333`},input:{padding:`6px 10px`,borderRadius:6,border:`1px solid #ccc`,fontSize:13,width:80,background:`#fff`,color:`#333`},checkboxField:{width:16,height:16,accentColor:`#3b82f6`},hint:{fontSize:11,color:`#aaa`,marginLeft:8}};function q({icon:e,label:t,color:n,size:r}){let i=r||36,a={...K.iconBox,width:i,height:i,borderRadius:i*.22,...n?{background:n}:{}},o=e||``;return o.startsWith(`/`)||o.startsWith(`http`)?U(`div`,{style:a,children:U(`img`,{src:o,alt:t,style:{...K.iconImg,padding:i*.12},onError:e=>{let n=e.currentTarget;n.style.display=`none`,n.parentElement&&(n.parentElement.textContent=(t||`??`).slice(0,2).toUpperCase())}})}):U(`div`,{style:a,children:o||(t||`??`).slice(0,2).toUpperCase()})}function J({name:e,color:t}){return U(`span`,{style:{display:`flex`,width:`55%`,height:`55%`,color:t||`#fff`},dangerouslySetInnerHTML:{__html:z[e]}})}function Y({label:e,value:t,options:n,onChange:r,hint:i}){return W(`div`,{style:K.fieldRow,children:[U(`span`,{style:K.label,children:e}),U(`select`,{style:K.select,value:t,onChange:e=>r(e.target.value),children:n.map(e=>U(`option`,{value:e.value,children:e.label},e.value))}),i&&U(`span`,{style:K.hint,children:i})]})}function X({label:e,value:t,onChange:n,hint:r}){return W(`div`,{style:K.fieldRow,children:[U(`span`,{style:K.label,children:e}),U(`input`,{style:K.input,type:`number`,value:t,onChange:e=>n(Number(e.target.value))}),r&&U(`span`,{style:K.hint,children:r})]})}function Z({label:e,checked:t,onChange:n,hint:r}){return W(`div`,{style:K.fieldRow,children:[U(`span`,{style:K.label,children:e}),U(`input`,{type:`checkbox`,style:K.checkboxField,checked:t,onChange:e=>n(e.target.checked)}),r&&U(`span`,{style:K.hint,children:r})]})}function Q({configuration:e,save:t}){let n=e||{},[r,i]=P(()=>n.apps||[]),[a,o]=P(n.position||`bottom`),[s,ee]=P(n.iframeMode||`keep-alive`),[c,l]=P(n.iconSize||56),[u,te]=P(n.magnification!==!1),[d,f]=P(n.magnificationScale||1.7),[p,m]=P(n.showNightModeButton||!1),[h,g]=P(n.showFullscreenButton||!1),[_,v]=P(n.showExitButton||!1),y=n.tourDismissed||!1,[x,S]=P(``),[C,T]=P(!1),[E,D]=P(!1),[O,k]=P(!1),[A,j]=P(null),[M,F]=P(null),I=N(r);w(()=>{I.current=r},[r]);let L=N(null);w(()=>()=>{L.current&&clearTimeout(L.current)},[]);let R=b(e=>({position:a,iframeMode:s,iconSize:c,magnification:u,magnificationScale:d,showNightModeButton:p,showFullscreenButton:h,showExitButton:_,tourDismissed:y,apps:e}),[a,s,c,u,d,p,h,_,y]),z=b(async()=>{L.current&&clearTimeout(L.current),k(!0),S(`Saving…`),T(!1);try{await t(R(r)),S(`Configuration saved — plugin will restart`),T(!1),L.current=setTimeout(()=>S(``),5e3)}catch(e){S(`Save failed: `+G(e)),T(!0)}finally{k(!1)}},[r,R,t]),B=async()=>{D(!0),S(``),T(!1);try{let e=(await(await fetch(`/skServer/webapps`)).json()).filter(e=>e.name!==`@signalk/app-dock`).map(e=>e.name===`@signalk/server-admin-ui`?{name:e.name,label:`Settings`,url:`/admin/`,icon:`/@signalk/app-dock/icon-settings.svg`,isAdmin:!0}:{name:e.name,label:e.signalk?.displayName||e.name,url:`/${e.name}/`,icon:e.signalk?.appIcon?`/${e.name}/${e.signalk.appIcon}`:null});if(e.length===0){S(`No webapps found. Is the server fully started?`),T(!0),D(!1);return}let t=I.current,n=new Set(t.map(e=>e.url)),r=0,a=[...t];for(let t of e)n.has(t.url)||(a.push({enabled:!0,autostart:!1,url:t.url,label:t.label||``,icon:t.icon||``,color:t.isAdmin?`#78788c`:``}),r++);i(a),S(r>0?`Found ${e.length} webapps, added ${r} new.`:`All ${e.length} webapps already in list.`),T(!1)}catch(e){S(`Discovery failed: `+G(e)),T(!0)}D(!1)},V=e=>i(r.map((t,n)=>n===e?{...t,enabled:!t.enabled}:t)),Q=e=>i(r.map((t,n)=>n===e?{...t,autostart:!t.autostart}:{...t,autostart:!1})),ne=e=>i(r.filter((t,n)=>n!==e)),re=e=>j(e),ie=(e,t)=>{e.preventDefault(),F(t)},ae=()=>F(null),oe=(e,t)=>{if(e.preventDefault(),F(null),A!==null&&A!==t){let e=[...r],[n]=e.splice(A,1);e.splice(t,0,n),i(e)}j(null)},se=()=>{j(null),F(null)},$=r.filter(e=>e.enabled!==!1);return W(`div`,{style:K.root,children:[U(`div`,{style:K.sectionTitle,children:`Dock Settings`}),U(Y,{label:`Dock position`,value:a,onChange:o,options:[{value:`bottom`,label:`Bottom`},{value:`top`,label:`Top`},{value:`left`,label:`Left`},{value:`right`,label:`Right`}]}),U(Y,{label:`iFrame lifecycle`,value:s,onChange:ee,options:[{value:`keep-alive`,label:`Keep-alive (faster switching, more RAM)`},{value:`destroy`,label:`Destroy (reload each time, less RAM)`}]}),U(X,{label:`Icon size`,value:c,onChange:l,hint:`px`}),U(Z,{label:`Magnification effect`,checked:u,onChange:te}),u&&U(X,{label:`Magnification scale`,value:d,onChange:f,hint:`1.0 - 2.5`}),U(`div`,{style:K.sectionTitle,children:`Utility Buttons`}),U(Z,{label:`Night/Day mode toggle`,checked:p,onChange:m,hint:`Sun/moon button to toggle environment.mode`}),U(Z,{label:`Fullscreen toggle`,checked:h,onChange:g,hint:`Toggles browser fullscreen. Hidden on devices without Fullscreen API support (e.g. iPadOS Safari).`}),U(Z,{label:`Exit button`,checked:_,onChange:v,hint:`X button to return to Signal K admin UI`}),U(`div`,{style:K.sectionTitle,children:`Webapps`}),U(`button`,{style:{...K.btn,...K.btnPrimary,...E?{opacity:.6}:{}},onClick:()=>void B(),disabled:E,children:E?`Discovering…`:`Discover Installed Webapps`}),x&&U(`div`,{style:{...K.status,color:C?`#ef4444`:`#10b981`},children:x}),U(`div`,{style:K.sectionTitle,children:`Dock Apps (drag to reorder)`}),r.length===0?U(`div`,{style:K.empty,children:`No apps yet. Click Discover above.`}):U(`div`,{children:r.map((e,t)=>W(`div`,{draggable:!0,onDragStart:()=>re(t),onDragOver:e=>ie(e,t),onDragLeave:ae,onDrop:e=>oe(e,t),onDragEnd:se,style:{...K.item,...e.enabled===!1?K.itemDisabled:{},...M===t?K.dragOver:{},...A===t?{opacity:.4}:{}},children:[U(`span`,{style:K.handle,children:`≡`}),U(q,{icon:e.icon,label:e.label||e.url,color:e.color}),W(`div`,{style:K.info,children:[U(`div`,{style:K.name,children:e.label||e.url}),U(`div`,{style:K.url,children:e.url})]}),U(`div`,{style:K.toggle,title:`Enabled`,children:U(`input`,{type:`checkbox`,checked:e.enabled!==!1,onChange:()=>V(t),style:K.checkbox})}),U(`div`,{style:{...K.toggle,cursor:`pointer`,fontSize:16,opacity:e.autostart?1:.25},onClick:()=>Q(t),title:e.autostart?`Autostart: ON (click to disable)`:`Click to set as autostart app`,children:`▶`}),U(`button`,{style:{...K.btn,...K.btnDanger},onClick:()=>ne(t),children:`Remove`})]},e.url+t))}),($.length>0||p||h||_)&&W(H,{children:[U(`div`,{style:K.sectionTitle,children:`Preview`}),W(`div`,{style:K.preview,children:[p&&U(`div`,{style:{...K.iconBox,width:40,height:40,background:`rgba(255,255,255,0.08)`,border:`1px solid rgba(255,255,255,0.12)`,fontSize:20,borderRadius:10},children:U(J,{name:`sun`,color:`#fbbf24`})}),h&&U(`div`,{style:{...K.iconBox,width:40,height:40,background:`rgba(255,255,255,0.08)`,border:`1px solid rgba(255,255,255,0.12)`,fontSize:20,borderRadius:10,color:`#fff`},children:U(J,{name:`fullscreen`})}),(p||h)&&U(`div`,{style:{width:1,height:24,background:`rgba(255,255,255,0.15)`,alignSelf:`center`}}),$.map((e,t)=>U(q,{icon:e.icon,label:e.label||e.url,color:e.color,size:40},t)),_&&W(H,{children:[U(`div`,{style:{width:1,height:24,background:`rgba(255,255,255,0.15)`,alignSelf:`center`}}),U(`div`,{style:{...K.iconBox,width:40,height:40,background:`rgba(255,255,255,0.08)`,border:`1px solid rgba(255,255,255,0.12)`,fontSize:16,borderRadius:10,color:`#fff`},children:U(J,{name:`close`})})]})]})]}),W(`div`,{style:K.actions,children:[U(`button`,{style:{...K.btn,...K.btnSave,...O?{opacity:.6}:{}},onClick:()=>void z(),disabled:O,children:O?`Saving…`:`Save Configuration`}),x&&U(`div`,{style:{...K.status,color:C?`#ef4444`:`#10b981`},children:x})]})]})}export{Q as default};
|
|
1
|
+
var e=window.__SK_REACT__;if(!e)throw Error(`App Dock: window.__SK_REACT__ is not set. The host signalk-server admin UI must expose React on window for plugin federation. Requires signalk-server 2.27.0 or newer.`);var{Activity:t,Children:n,Component:r,Fragment:i,Profiler:a,PureComponent:o,StrictMode:s,Suspense:ee,cache:c,cacheSignal:l,cloneElement:u,createContext:te,createElement:d,createRef:f,forwardRef:p,isValidElement:m,lazy:h,memo:g,startTransition:_,use:v,useActionState:y,useCallback:b,useContext:x,useDebugValue:S,useDeferredValue:C,useEffect:w,useEffectEvent:T,useId:E,useImperativeHandle:D,useInsertionEffect:O,useLayoutEffect:k,useMemo:A,useOptimistic:j,useReducer:M,useRef:N,useState:P,useSyncExternalStore:F,useTransition:I,version:L}=e,R=(e,t)=>`<svg class="dock-svg-icon dock-svg-${e}" width="100%" height="100%" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${t}</svg>`,z={sun:R(`sun`,`<circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/>`),moon:R(`moon`,`<path d="M20.5 14A9 9 0 1 1 10 3.5a7.5 7.5 0 0 0 10.5 10.5z"/>`),fullscreen:R(`fullscreen`,`<path d="M4 9V4h5M15 4h5v5M20 15v5h-5M9 20H4v-5"/>`),exitFullscreen:R(`exit-fullscreen`,`<path d="M9 4v5H4M20 9h-5V4M15 20v-5h5M4 15h5v5"/>`),close:R(`close`,`<path d="M6 6l12 12M18 6L6 18"/>`)},B=window.__SK_REACT_JSX_RUNTIME__;if(!B)throw Error(`App Dock: window.__SK_REACT_JSX_RUNTIME__ is not set by the host.`);var V=B,{Fragment:H,jsx:U,jsxs:W}=V;V.jsxDEV;function G(e){return e instanceof Error?e.message:String(e)}var K={root:{fontFamily:`-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif`,color:`#333`,padding:`16px 0`},sectionTitle:{fontSize:13,fontWeight:600,color:`#888`,textTransform:`uppercase`,letterSpacing:`0.05em`,marginBottom:10,marginTop:24},btn:{display:`inline-flex`,alignItems:`center`,gap:8,padding:`8px 16px`,border:`none`,borderRadius:6,fontSize:13,fontWeight:600,cursor:`pointer`},btnPrimary:{background:`#3b82f6`,color:`#fff`},btnSave:{background:`#10b981`,color:`#fff`},btnDanger:{background:`#ef4444`,color:`#fff`,padding:`4px 10px`,fontSize:11},status:{marginTop:8,fontSize:12,minHeight:18},item:{display:`flex`,alignItems:`center`,gap:10,padding:`8px 12px`,background:`#f8f9fa`,border:`1px solid #e0e0e0`,borderRadius:8,marginBottom:6,cursor:`grab`},itemDisabled:{opacity:.4},dragOver:{borderColor:`#3b82f6`,background:`#eef4ff`},handle:{color:`#bbb`,fontSize:16,cursor:`grab`,userSelect:`none`,flexShrink:0,width:18,textAlign:`center`},iconBox:{width:36,height:36,borderRadius:8,display:`flex`,alignItems:`center`,justifyContent:`center`,background:`#e2e8f0`,overflow:`hidden`,flexShrink:0,fontSize:18},iconImg:{width:`100%`,height:`100%`,objectFit:`contain`,padding:5},info:{flex:1,minWidth:0},name:{fontSize:13,fontWeight:600,color:`#333`},url:{fontSize:10,color:`#999`,overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`},toggle:{flexShrink:0},checkbox:{width:16,height:16,cursor:`pointer`,accentColor:`#3b82f6`},preview:{display:`flex`,alignItems:`flex-end`,justifyContent:`center`,gap:6,padding:`12px 18px`,background:`rgba(30,30,32,0.85)`,border:`1px solid rgba(255,255,255,0.18)`,borderRadius:18,margin:`16px auto`,width:`fit-content`},actions:{display:`flex`,gap:10,alignItems:`center`,marginTop:16},empty:{textAlign:`center`,padding:`30px 16px`,color:`#999`,fontSize:13},fieldRow:{display:`flex`,alignItems:`center`,gap:12,marginBottom:10},label:{fontSize:13,fontWeight:500,color:`#555`,width:160,flexShrink:0},select:{padding:`6px 10px`,borderRadius:6,border:`1px solid #ccc`,fontSize:13,background:`#fff`,color:`#333`},input:{padding:`6px 10px`,borderRadius:6,border:`1px solid #ccc`,fontSize:13,width:80,background:`#fff`,color:`#333`},checkboxField:{width:16,height:16,accentColor:`#3b82f6`},hint:{fontSize:11,color:`#aaa`,marginLeft:8}};function q({icon:e,label:t,color:n,size:r}){let i=r||36,a={...K.iconBox,width:i,height:i,borderRadius:i*.22,...n?{background:n}:{}},o=e||``;return o.startsWith(`/`)||o.startsWith(`http`)?U(`div`,{style:a,children:U(`img`,{src:o,alt:t,style:{...K.iconImg,padding:i*.12},onError:e=>{let n=e.currentTarget;n.style.display=`none`,n.parentElement&&(n.parentElement.textContent=(t||`??`).slice(0,2).toUpperCase())}})}):U(`div`,{style:a,children:o||(t||`??`).slice(0,2).toUpperCase()})}function J({name:e,color:t}){return U(`span`,{style:{display:`flex`,width:`55%`,height:`55%`,color:t||`#fff`},dangerouslySetInnerHTML:{__html:z[e]}})}function Y({label:e,value:t,options:n,onChange:r,hint:i}){return W(`div`,{style:K.fieldRow,children:[U(`span`,{style:K.label,children:e}),U(`select`,{style:K.select,value:t,onChange:e=>r(e.target.value),children:n.map(e=>U(`option`,{value:e.value,children:e.label},e.value))}),i&&U(`span`,{style:K.hint,children:i})]})}function X({label:e,value:t,onChange:n,hint:r}){return W(`div`,{style:K.fieldRow,children:[U(`span`,{style:K.label,children:e}),U(`input`,{style:K.input,type:`number`,value:t,onChange:e=>n(Number(e.target.value))}),r&&U(`span`,{style:K.hint,children:r})]})}function Z({label:e,checked:t,onChange:n,hint:r}){return W(`div`,{style:K.fieldRow,children:[U(`span`,{style:K.label,children:e}),U(`input`,{type:`checkbox`,style:K.checkboxField,checked:t,onChange:e=>n(e.target.checked)}),r&&U(`span`,{style:K.hint,children:r})]})}function Q({configuration:e,save:t}){let n=e||{},[r,i]=P(()=>n.apps||[]),[a,o]=P(n.position||`bottom`),[s,ee]=P(n.iframeMode||`destroy`),[c,l]=P(n.iconSize||56),[u,te]=P(n.magnification!==!1),[d,f]=P(n.magnificationScale||1.7),[p,m]=P(n.showNightModeButton||!1),[h,g]=P(n.showFullscreenButton||!1),[_,v]=P(n.showExitButton||!1),y=n.tourDismissed||!1,[x,S]=P(``),[C,T]=P(!1),[E,D]=P(!1),[O,k]=P(!1),[A,j]=P(null),[M,F]=P(null),I=N(r);w(()=>{I.current=r},[r]);let L=N(null);w(()=>()=>{L.current&&clearTimeout(L.current)},[]);let R=b(e=>({position:a,iframeMode:s,iconSize:c,magnification:u,magnificationScale:d,showNightModeButton:p,showFullscreenButton:h,showExitButton:_,tourDismissed:y,apps:e}),[a,s,c,u,d,p,h,_,y]),z=b(async()=>{L.current&&clearTimeout(L.current),k(!0),S(`Saving…`),T(!1);try{await t(R(r)),S(`Configuration saved — plugin will restart`),T(!1),L.current=setTimeout(()=>S(``),5e3)}catch(e){S(`Save failed: `+G(e)),T(!0)}finally{k(!1)}},[r,R,t]),B=async()=>{D(!0),S(``),T(!1);try{let e=(await(await fetch(`/skServer/webapps`)).json()).filter(e=>e.name!==`@signalk/app-dock`).map(e=>e.name===`@signalk/server-admin-ui`?{name:e.name,label:`Settings`,url:`/admin/`,icon:`/@signalk/app-dock/icon-settings.svg`,isAdmin:!0}:{name:e.name,label:e.signalk?.displayName||e.name,url:`/${e.name}/`,icon:e.signalk?.appIcon?`/${e.name}/${e.signalk.appIcon}`:null});if(e.length===0){S(`No webapps found. Is the server fully started?`),T(!0),D(!1);return}let t=I.current,n=new Set(t.map(e=>e.url)),r=0,a=[...t];for(let t of e)n.has(t.url)||(a.push({enabled:!0,autostart:!1,url:t.url,label:t.label||``,icon:t.icon||``,color:t.isAdmin?`#78788c`:``}),r++);i(a),S(r>0?`Found ${e.length} webapps, added ${r} new.`:`All ${e.length} webapps already in list.`),T(!1)}catch(e){S(`Discovery failed: `+G(e)),T(!0)}D(!1)},V=e=>i(r.map((t,n)=>n===e?{...t,enabled:!t.enabled}:t)),Q=e=>i(r.map((t,n)=>n===e?{...t,autostart:!t.autostart}:{...t,autostart:!1})),ne=e=>i(r.filter((t,n)=>n!==e)),re=e=>j(e),ie=(e,t)=>{e.preventDefault(),F(t)},ae=()=>F(null),oe=(e,t)=>{if(e.preventDefault(),F(null),A!==null&&A!==t){let e=[...r],[n]=e.splice(A,1);e.splice(t,0,n),i(e)}j(null)},se=()=>{j(null),F(null)},$=r.filter(e=>e.enabled!==!1);return W(`div`,{style:K.root,children:[U(`div`,{style:K.sectionTitle,children:`Dock Settings`}),U(Y,{label:`Dock position`,value:a,onChange:o,options:[{value:`bottom`,label:`Bottom`},{value:`top`,label:`Top`},{value:`left`,label:`Left`},{value:`right`,label:`Right`}]}),U(Y,{label:`iFrame lifecycle`,value:s,onChange:ee,options:[{value:`destroy`,label:`Destroy (reload each time, less RAM and CPU)`},{value:`keep-alive`,label:`Keep-alive (faster switching, hidden apps keep running)`}]}),U(X,{label:`Icon size`,value:c,onChange:l,hint:`px`}),U(Z,{label:`Magnification effect`,checked:u,onChange:te}),u&&U(X,{label:`Magnification scale`,value:d,onChange:f,hint:`1.0 - 2.5`}),U(`div`,{style:K.sectionTitle,children:`Utility Buttons`}),U(Z,{label:`Night/Day mode toggle`,checked:p,onChange:m,hint:`Sun/moon button to toggle environment.mode`}),U(Z,{label:`Fullscreen toggle`,checked:h,onChange:g,hint:`Toggles browser fullscreen. Hidden on devices without Fullscreen API support (e.g. iPadOS Safari).`}),U(Z,{label:`Exit button`,checked:_,onChange:v,hint:`X button to return to Signal K admin UI`}),U(`div`,{style:K.sectionTitle,children:`Webapps`}),U(`button`,{style:{...K.btn,...K.btnPrimary,...E?{opacity:.6}:{}},onClick:()=>void B(),disabled:E,children:E?`Discovering…`:`Discover Installed Webapps`}),x&&U(`div`,{style:{...K.status,color:C?`#ef4444`:`#10b981`},children:x}),U(`div`,{style:K.sectionTitle,children:`Dock Apps (drag to reorder)`}),r.length===0?U(`div`,{style:K.empty,children:`No apps yet. Click Discover above.`}):U(`div`,{children:r.map((e,t)=>W(`div`,{draggable:!0,onDragStart:()=>re(t),onDragOver:e=>ie(e,t),onDragLeave:ae,onDrop:e=>oe(e,t),onDragEnd:se,style:{...K.item,...e.enabled===!1?K.itemDisabled:{},...M===t?K.dragOver:{},...A===t?{opacity:.4}:{}},children:[U(`span`,{style:K.handle,children:`≡`}),U(q,{icon:e.icon,label:e.label||e.url,color:e.color}),W(`div`,{style:K.info,children:[U(`div`,{style:K.name,children:e.label||e.url}),U(`div`,{style:K.url,children:e.url})]}),U(`div`,{style:K.toggle,title:`Enabled`,children:U(`input`,{type:`checkbox`,checked:e.enabled!==!1,onChange:()=>V(t),style:K.checkbox})}),U(`div`,{style:{...K.toggle,cursor:`pointer`,fontSize:16,opacity:e.autostart?1:.25},onClick:()=>Q(t),title:e.autostart?`Autostart: ON (click to disable)`:`Click to set as autostart app`,children:`▶`}),U(`button`,{style:{...K.btn,...K.btnDanger},onClick:()=>ne(t),children:`Remove`})]},e.url+t))}),($.length>0||p||h||_)&&W(H,{children:[U(`div`,{style:K.sectionTitle,children:`Preview`}),W(`div`,{style:K.preview,children:[p&&U(`div`,{style:{...K.iconBox,width:40,height:40,background:`rgba(255,255,255,0.08)`,border:`1px solid rgba(255,255,255,0.12)`,fontSize:20,borderRadius:10},children:U(J,{name:`sun`,color:`#fbbf24`})}),h&&U(`div`,{style:{...K.iconBox,width:40,height:40,background:`rgba(255,255,255,0.08)`,border:`1px solid rgba(255,255,255,0.12)`,fontSize:20,borderRadius:10,color:`#fff`},children:U(J,{name:`fullscreen`})}),(p||h)&&U(`div`,{style:{width:1,height:24,background:`rgba(255,255,255,0.15)`,alignSelf:`center`}}),$.map((e,t)=>U(q,{icon:e.icon,label:e.label||e.url,color:e.color,size:40},t)),_&&W(H,{children:[U(`div`,{style:{width:1,height:24,background:`rgba(255,255,255,0.15)`,alignSelf:`center`}}),U(`div`,{style:{...K.iconBox,width:40,height:40,background:`rgba(255,255,255,0.08)`,border:`1px solid rgba(255,255,255,0.12)`,fontSize:16,borderRadius:10,color:`#fff`},children:U(J,{name:`close`})})]})]})]}),W(`div`,{style:K.actions,children:[U(`button`,{style:{...K.btn,...K.btnSave,...O?{opacity:.6}:{}},onClick:()=>void z(),disabled:O,children:O?`Saving…`:`Save Configuration`}),x&&U(`div`,{style:{...K.status,color:C?`#ef4444`:`#10b981`},children:x})]})]})}export{Q as default};
|
package/public/dock.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var e=(e,t)=>`<svg class="dock-svg-icon dock-svg-${e}" width="100%" height="100%" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${t}</svg>`,t={sun:e(`sun`,`<circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/>`),moon:e(`moon`,`<path d="M20.5 14A9 9 0 1 1 10 3.5a7.5 7.5 0 0 0 10.5 10.5z"/>`),fullscreen:e(`fullscreen`,`<path d="M4 9V4h5M15 4h5v5M20 15v5h-5M9 20H4v-5"/>`),exitFullscreen:e(`exit-fullscreen`,`<path d="M9 4v5H4M20 9h-5V4M15 20v-5h5M4 15h5v5"/>`),close:e(`close`,`<path d="M6 6l12 12M18 6L6 18"/>`)};function n(e){let t=document.getElementById(e);if(!t)throw Error(`[Dock] #${e} is missing from index.html`);return t}function r(e){return typeof e==`number`&&Number.isFinite(e)}(async function(){if(window.self!==window.top){let e=window.location.href;try{window.top?.location.replace(e)}catch{}document.body.innerHTML=`<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;height:100vh;color:#fff;background:#000;font-family:-apple-system,BlinkMacSystemFont,sans-serif;text-align:center;padding:16px"><img src="app-icon.svg" alt="App Dock" style="width:72px;height:72px;border-radius:16px;opacity:0.85" /><a href="`+e+`" target="_top" style="color:#fff;text-decoration:underline;font-size:16px">Open App Dock</a></div>`;return}let e={position:`bottom`,iframeMode:`keep-alive`,iconSize:56,magnification:!0,magnificationScale:1.7,showNightModeButton:!0,showFullscreenButton:!1,showExitButton:!1,apps:[]},i={showNightModeButton:!0,apps:[{enabled:!0,autostart:!0,url:`/@signalk/freeboard-sk/`,label:`Freeboard-SK`,icon:`/@signalk/freeboard-sk/assets/icons/icon-72x72.png`,color:``},{enabled:!0,autostart:!1,url:`/@halos-org/skip/`,label:`Skip`,icon:`/@halos-org/skip/assets/icon-72x72.png`,color:``},{enabled:!0,autostart:!1,url:`/admin/`,label:`Settings`,icon:`/@signalk/app-dock/icon-settings.svg`,color:`#78788c`}]},a=!1,o=!0;for(let e of[`/skServer/loginStatus`,`/loginStatus`])try{let t=await fetch(e);if(t.ok){let e=await t.json(),n=e.authenticationRequired===!1,r=e.status===`loggedIn`?e.userLevel:void 0;a=!n&&r!==`admin`,o=n||r===`admin`||r===`readwrite`;break}}catch{}async function s(){try{let t=await fetch(`/plugins/signalk-app-dock/settings`);if(!t.ok)return null;let n=await t.json();return Array.isArray(n.apps)?{...e,...n}:null}catch{return null}}let c={...e},l=!a;if(a){let t=await s();l=t!==null,c=t||{...e,...i}}else try{let[t,n]=await Promise.all([fetch(`/plugins/signalk-app-dock/config`),fetch(`/plugins/signalk-app-dock/settings`)]);if(t.ok){let n=await t.json(),r=n.configuration||n;c={...e,...r}}if(n.ok){let e=await n.json();Array.isArray(e.apps)&&e.apps.length>0&&(c.apps=e.apps)}}catch(e){console.warn(`[Dock] Could not load config, using defaults.`,e)}(!Array.isArray(c.apps)||c.apps.length===0)&&console.warn(`[Dock] No apps configured — open Plugin Config to add webapps.`);let u=n(`iframe-container`),d=n(`dock-backdrop`),f=n(`dock`),p=n(`dock-inner`),ee=n(`loading-overlay`),te=n(`loading-label`),ne=document.getElementById(`idle-hint`),m=n(`idle-hint-text`),h=n(`dock-handle`),g=!1,_={},v=`day`,y=l&&o;async function b(){if(l)try{let e=await fetch(`/plugins/signalk-app-dock/mode`);e.ok&&(v=(await e.json()).value||`day`,x())}catch(e){console.warn(`[Dock] Could not fetch environment.mode`,e)}}function x(){let e=document.querySelector(`.dock-item-nightmode .dock-icon`);if(!e)return;e.innerHTML=t[v===`night`?`moon`:`sun`];let n=document.querySelector(`.dock-item-nightmode .dock-label`);n&&(n.textContent=v===`night`?`Day mode`:`Night mode`)}async function re(){if(!y)return;await b();let e=v===`night`?`day`:`night`;try{(await fetch(`/signalk/v1/api/vessels/self/environment/mode`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:JSON.stringify({value:e})})).ok&&(v=e,x())}catch(e){console.warn(`[Dock] Failed to toggle mode`,e)}}function S(){return!!(document.fullscreenEnabled&&document.documentElement.requestFullscreen)}function C(){return!!document.fullscreenElement}function ie(){S()&&(C()?document.exitFullscreen().catch(e=>console.warn(`[Dock] exitFullscreen`,e)):document.documentElement.requestFullscreen().catch(e=>console.warn(`[Dock] requestFullscreen`,e)))}function ae(){let e=document.querySelector(`.dock-item-fullscreen .dock-icon`);if(!e)return;e.innerHTML=t[C()?`exitFullscreen`:`fullscreen`];let n=document.querySelector(`.dock-item-fullscreen .dock-label`);n&&(n.textContent=C()?`Exit fullscreen`:`Fullscreen`)}let w=c.position;f.classList.add(`pos-${w}`),p.classList.add(`align-${w}`),h.classList.add(`pos-${w}`);let T=w===`left`||w===`right`;T&&p.classList.add(`layout-vertical`);let oe=c.magnification!==!1,se=Math.max(1,Math.min(c.magnificationScale||1.7,2.5)),ce=c.iconSize*3.5;function le(){let e=document.getElementById(`idle-version`);e&&fetch(`/skServer/webapps`).then(e=>e.json()).then(t=>{let n=t.find(e=>e.name===`@signalk/app-dock`);n&&(e.textContent=`v`+n.version)}).catch(()=>{}),!c.apps||c.apps.length===0?a?m.innerHTML=`App Dock can only be customised by an administrator.<br>Ask an admin to set up apps in <strong>Plugin Config</strong>.`:m.innerHTML=`No apps configured yet.<br>Open <strong>Admin UI → Plugin Config → App Dock</strong><br>and click <strong>Discover Installed Webapps</strong> to get started.`:m.innerHTML=`Double-tap <strong>anywhere</strong> to open the dock`+(a?`<br><br><span style="font-size:12px;opacity:0.6">Customisation is admin-only</span>`:`<br><br><span style="font-size:12px;opacity:0.6">Configure in Admin UI → Plugin Config → App Dock</span>`)}function ue(){ne&&ne.classList.add(`hidden`)}function de(){switch(w){case`top`:return`dock-label-below`;case`left`:return`dock-label-right`;case`right`:return`dock-label-left`;default:return``}}function E(e,n,r,i){let a=c.iconSize,o=Math.round(a*.25)+`px`,s=de(),l=document.createElement(`div`);l.className=`dock-item dock-item-utility `+e;let u=document.createElement(`div`);u.className=`dock-icon dock-icon-utility`,u.style.width=a+`px`,u.style.height=a+`px`,u.style.borderRadius=o,u.innerHTML=t[n];let d=document.createElement(`div`);d.className=`dock-dot`;let f=document.createElement(`div`);return f.className=`dock-label`+(s?` `+s:``),f.textContent=r,l.appendChild(u),l.appendChild(d),l.appendChild(f),l.addEventListener(`touchstart`,()=>l.classList.add(`pressing`),{passive:!0}),l.addEventListener(`touchend`,()=>l.classList.remove(`pressing`),{passive:!0}),l.addEventListener(`touchcancel`,()=>l.classList.remove(`pressing`),{passive:!0}),l.addEventListener(`click`,e=>{O?e.preventDefault():i()}),l}function fe(){let e=document.createElement(`div`);return e.className=`dock-separator`,e}function pe(){p.innerHTML=``;let e=c.iconSize,t=Math.round(e*.25)+`px`,n=de(),r=c.showNightModeButton&&y;r&&p.appendChild(E(`dock-item-nightmode`,`sun`,`Night mode`,()=>void re())),c.showFullscreenButton&&S()&&p.appendChild(E(`dock-item-fullscreen`,`fullscreen`,`Fullscreen`,ie)),(r||c.showFullscreenButton&&S())&&p.appendChild(fe()),c.apps.forEach((r,i)=>{let a=document.createElement(`div`);a.className=`dock-item`,a.dataset.index=String(i);let o=document.createElement(`div`);o.className=`dock-icon`,o.style.width=e+`px`,o.style.height=e+`px`,o.style.borderRadius=t,r.color&&(o.style.background=r.color);let s=r.icon||``;if(s.startsWith(`/`)||s.startsWith(`http`)){let t=document.createElement(`img`);t.src=s,t.alt=r.label,t.onerror=()=>{o.removeChild(t),o.style.fontSize=Math.round(e*.42)+`px`,o.textContent=r.label.slice(0,2).toUpperCase()},o.appendChild(t)}else o.style.fontSize=Math.round(e*.48)+`px`,o.textContent=s||r.label.slice(0,2).toUpperCase();let c=document.createElement(`div`);c.className=`dock-dot`;let l=document.createElement(`div`);l.className=`dock-label`+(n?` `+n:``),l.textContent=r.label,a.appendChild(o),a.appendChild(c),a.appendChild(l),a.addEventListener(`touchstart`,()=>a.classList.add(`pressing`),{passive:!0}),a.addEventListener(`touchend`,()=>a.classList.remove(`pressing`),{passive:!0}),a.addEventListener(`touchcancel`,()=>a.classList.remove(`pressing`),{passive:!0}),a.addEventListener(`click`,e=>{O?e.preventDefault():I(i)}),p.appendChild(a)}),c.showExitButton&&(p.appendChild(fe()),p.appendChild(E(`dock-item-exit`,`close`,`Exit to Admin`,()=>{window.location.href=`/admin/`}))),c.showNightModeButton&&x(),c.showFullscreenButton&&S()&&ae()}let D=!1,O=!1,me=0;function he(e){let t=p.querySelectorAll(`.dock-item`),n=-1,r=1/0;t.forEach((t,i)=>{let a=t.querySelector(`.dock-icon`);if(!a)return;let o=t.getBoundingClientRect(),s=T?o.top+o.height/2:o.left+o.width/2,l=T?e.y:e.x,u=Math.abs(l-s);u<r&&(r=u,n=i);let d=Math.min(u/ce,1),f=1+(se-1)*(1-d*d),p=Math.round(c.iconSize*f);a.classList.remove(`settling`),a.style.width=p+`px`,a.style.height=p+`px`,a.style.borderRadius=Math.round(p*.25)+`px`,a.querySelector(`img`)||(a.style.fontSize=Math.round(p*.48)+`px`)}),t.forEach((e,t)=>{e.classList.toggle(`mag-closest`,t===n)})}function k(){let e=p.querySelectorAll(`.dock-item`),t=c.iconSize;e.forEach(e=>{let n=e.querySelector(`.dock-icon`);n&&(n.classList.add(`settling`),n.style.width=t+`px`,n.style.height=t+`px`,n.style.borderRadius=Math.round(t*.25)+`px`,n.querySelector(`img`)||(n.style.fontSize=Math.round(t*.48)+`px`),e.classList.remove(`mag-closest`))})}oe&&(p.addEventListener(`mousemove`,e=>{if(D)return;D=!0;let t={x:e.clientX,y:e.clientY};requestAnimationFrame(()=>{he(t),D=!1})}),p.addEventListener(`mouseleave`,()=>{D=!1,k()}),p.addEventListener(`touchstart`,e=>{let t=e.touches[0];O=!1,me=T?t.clientY:t.clientX},{passive:!0}),p.addEventListener(`touchmove`,e=>{let t=e.touches[0],n=T?t.clientY:t.clientX;if(Math.abs(n-me)>10&&(O=!0),D)return;D=!0;let r={x:t.clientX,y:t.clientY};requestAnimationFrame(()=>{he(r),D=!1})},{passive:!0}),p.addEventListener(`touchend`,()=>{D=!1,k(),setTimeout(()=>{O=!1},50)},{passive:!0}));let A=0;function j(){g||(g=!0,A=Date.now(),f.classList.add(`visible`),d.classList.add(`visible`),Y())}function M(){g&&(X&&X.classList.contains(`visible`)||(g=!1,f.classList.remove(`visible`),d.classList.remove(`visible`),k(),Y()))}d.addEventListener(`click`,e=>{Date.now()-A<500?e.stopPropagation():M()});function N(e){te.textContent=`Loading ${e}\u2026`,ee.classList.add(`visible`)}function P(){ee.classList.remove(`visible`)}let F=`allow-same-origin allow-scripts allow-forms allow-popups allow-pointer-lock allow-top-navigation-by-user-activation`;function ge(e){try{let t=new URL(e.url,window.location.href);return t.origin===window.location.origin&&/^\/admin(\/|$)/.test(t.pathname)}catch{return!1}}function _e(e,t){N(t.label),e.addEventListener(`load`,P,{once:!0});try{e.contentWindow?.location.reload()}catch{e.src=t.url}}function I(e){let t=c.apps[e];if(!t)return;ue(),document.querySelectorAll(`.dock-item`).forEach(t=>{t.classList.toggle(`active`,t.dataset.index===String(e))});let n=ge(t),r=!n&&!!_[t.url];if(r&&c.iframeMode!==`destroy`&&_[t.url].classList.contains(`active`))_e(_[t.url],t),Y(),M();else{if(c.iframeMode===`destroy`?(u.innerHTML=``,Object.keys(_).forEach(e=>delete _[e])):Object.values(_).forEach(e=>e.classList.remove(`active`)),n&&_[t.url]&&(_[t.url].remove(),delete _[t.url]),!_[t.url]){r||N(t.label);let e=document.createElement(`iframe`);e.allow=`fullscreen; geolocation`,e.setAttribute(`sandbox`,F),e.style.touchAction=`auto`,e.addEventListener(`load`,P,{once:!0}),u.appendChild(e),e.src=t.url,_[t.url]=e}_[t.url].classList.add(`active`),Y(),M()}}let L=0,R=0,z=0,B=null;function V(){return performance.timeOrigin+performance.now()}function ve(e,t){let n=e.defaultView&&e.defaultView.performance;return n?n.timeOrigin+t.timeStamp:V()}function ye(){if(H&&console.log(`[Dock] triggerDock called, dockVisible=`,g),!g){if(j(),H){let e=f.getBoundingClientRect();console.log(`[Dock] after showDock: dock rect`,e,`classes`,f.className)}navigator.vibrate&&navigator.vibrate(8)}}let H=new URLSearchParams(location.search).has(`debugDock`);function U(e,t,n=null,r=V()){if(g)return;let i=e-R,a=t-z,o=Math.round(Math.sqrt(i*i+a*a)),s=o<=40&&n===B,c=r-L,l=c>=0&&c<450&&s;H&&console.log(`[Dock] tap`,{x:Math.round(e),y:Math.round(t),gap:c,dist:o,close:s,match:l}),l?(L=0,ye()):(L=r,R=e,z=t,B=n)}let be=new WeakSet;function xe(e,t){if(be.has(e))return;be.add(e);let n=()=>{if(!t)return{x:0,y:0};let e=t.getBoundingClientRect();return{x:e.left,y:e.top}},r=-1/0,i=(e,t,n)=>{Math.abs(n-r)<50||(r=n,U(e,t,null,n))};e.addEventListener(`pointerdown`,t=>{let r=n();i(t.clientX+r.x,t.clientY+r.y,ve(e,t))},{passive:!0,capture:!0}),e.addEventListener(`touchstart`,t=>{if(!t.touches||t.touches.length===0)return;let r=t.touches[0],a=n();i(r.clientX+a.x,r.clientY+a.y,ve(e,t))},{passive:!0,capture:!0})}let W=new WeakSet;function G(e){let t=()=>{let t=null;try{t=e.contentDocument}catch{}let n=we(e);n&&Te(n,e,t!==null),t&&!W.has(t)&&(W.add(t),xe(t,e),Se(t))};e.addEventListener(`load`,t),t()}function Se(e){e.querySelectorAll(`iframe`).forEach(e=>G(e));let t=e.documentElement||e.body||e;new MutationObserver(e=>{for(let t of e)t.addedNodes.forEach(e=>{if(!e||e.nodeType!==Node.ELEMENT_NODE)return;let t=e;t.tagName===`IFRAME`&&G(t),t.querySelectorAll(`iframe`).forEach(e=>G(e))}),t.removedNodes.length>0&&Y()}).observe(t,{childList:!0,subtree:!0})}let K=new WeakMap,q=new WeakSet,Ce=-1/0;function we(e){let t=e;for(;t&&t.parentElement!==u;){let e=t.ownerDocument&&t.ownerDocument.defaultView;t=e?e.frameElement:null}return t}function Te(e,t,n){q.delete(t);let r=K.get(e);(t===e||!r)&&(r=new Set,K.set(e,r)),n?r.delete(t):r.add(t),Y()}function J(e){let t=e&&K.get(e)||new Set;for(let e of t)(!e.isConnected||!e.ownerDocument.defaultView)&&t.delete(e);return t}function Y(){let e=u.querySelector(`iframe.active`),t=!1;if(!g)for(let n of J(e))q.has(n)||(t=!0);h.classList.toggle(`visible`,t)}function Ee(e){let t=0,n=0;for(let r=e;r;){let e=r.getBoundingClientRect();if(t+=e.left,n+=e.top,r.parentElement===u)break;let i=r.ownerDocument.defaultView;r=i?i.frameElement:null}return{x:t,y:n}}function De(e,t){for(let n=e;n;n=n.parent){if(n===t)return!0;if(n===n.parent)return!1}return!1}window.addEventListener(`message`,e=>{let t=e.data;if(!t||t.type!==`signalk-app-dock:pointerdown`||!r(t.x)||!r(t.y))return;let n=u.querySelector(`iframe.active`);if(!n||!e.source)return;let i=null;for(let t of J(n))De(e.source,t.contentWindow)&&(i=t);if(!i)return;e.source===i.contentWindow&&(q.add(i),Y());let a=V(),o=r(t.t)&&t.t<=a?t.t:a;if(!(Math.abs(o-Ce)<50)){if(Ce=o,e.source===i.contentWindow){let e=Ee(i);U(t.x+e.x,t.y+e.y,null,o)}else U(t.x,t.y,e.source,o)}}),h.addEventListener(`click`,ye),xe(document,null),W.add(document),Se(document),pe(),c.showNightModeButton&&y&&(b(),setInterval(b,5e3)),c.showFullscreenButton&&S()&&document.addEventListener(`fullscreenchange`,ae);let Oe=[`position`,`iframeMode`,`iconSize`,`magnification`,`magnificationScale`,`showNightModeButton`,`showFullscreenButton`,`showExitButton`];async function ke(){try{let t=c,n=c;if(a)n=await s()||t;else{let[r,i]=await Promise.all([fetch(`/plugins/signalk-app-dock/config`),fetch(`/plugins/signalk-app-dock/settings`)]);if(r.ok){let i=await r.json(),a=i.configuration||i;n={...e,...a,apps:t.apps}}if(i.ok){let e=await i.json();Array.isArray(e.apps)&&e.apps.length>0&&(n={...n,apps:e.apps})}}if(Oe.some(e=>t[e]!==n[e])){window.location.reload();return}JSON.stringify(n.apps)===JSON.stringify(t.apps)?c=n:(c=n,pe())}catch{}}l&&setInterval(ke,5e3);let X=document.getElementById(`tour-overlay`),Ae=document.getElementById(`tour-btn-got-it`),Z=document.getElementById(`tour-btn-dismiss`);function Q(){X?.classList.remove(`visible`),M()}function je(){X?.classList.add(`visible`),j()}Ae&&Ae.addEventListener(`click`,Q),!l||!o?(Z&&Z.remove(),document.getElementById(`tour-buttons`)?.style.setProperty(`justify-content`,`center`)):Z&&Z.addEventListener(`click`,()=>{Q(),fetch(`/plugins/signalk-app-dock/dismiss-tour`,{method:`POST`}).catch(()=>{})});let $=document.getElementById(`tour-link`);$&&(a?$.replaceWith(document.createTextNode(`Plugin Config (admin only)`)):$.addEventListener(`click`,e=>{e.preventDefault();let t=`/admin/#/serverConfiguration/plugins/signalk-app-dock`;ue();let n=document.createElement(`iframe`);n.allow=`fullscreen; geolocation`,n.setAttribute(`sandbox`,F),n.style.touchAction=`auto`,n.classList.add(`active`),u.innerHTML=``,Object.keys(_).forEach(e=>delete _[e]),u.appendChild(n),n.src=t,_[t]=n,Q()})),c.tourDismissed||je();let Me=c.apps.findIndex(e=>e.autostart);Me>=0?I(Me):le()})().catch(e=>console.error(`[Dock] failed to start`,e));
|
|
1
|
+
var e=(e,t)=>`<svg class="dock-svg-icon dock-svg-${e}" width="100%" height="100%" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${t}</svg>`,t={sun:e(`sun`,`<circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/>`),moon:e(`moon`,`<path d="M20.5 14A9 9 0 1 1 10 3.5a7.5 7.5 0 0 0 10.5 10.5z"/>`),fullscreen:e(`fullscreen`,`<path d="M4 9V4h5M15 4h5v5M20 15v5h-5M9 20H4v-5"/>`),exitFullscreen:e(`exit-fullscreen`,`<path d="M9 4v5H4M20 9h-5V4M15 20v-5h5M4 15h5v5"/>`),close:e(`close`,`<path d="M6 6l12 12M18 6L6 18"/>`)};function n(e){let t=document.getElementById(e);if(!t)throw Error(`[Dock] #${e} is missing from index.html`);return t}function r(e){return typeof e==`number`&&Number.isFinite(e)}(async function(){if(window.self!==window.top){let e=window.location.href;try{window.top?.location.replace(e)}catch{}document.body.innerHTML=`<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;height:100vh;color:#fff;background:#000;font-family:-apple-system,BlinkMacSystemFont,sans-serif;text-align:center;padding:16px"><img src="app-icon.svg" alt="App Dock" style="width:72px;height:72px;border-radius:16px;opacity:0.85" /><a href="`+e+`" target="_top" style="color:#fff;text-decoration:underline;font-size:16px">Open App Dock</a></div>`;return}let e={position:`bottom`,iframeMode:`destroy`,iconSize:56,magnification:!0,magnificationScale:1.7,showNightModeButton:!0,showFullscreenButton:!1,showExitButton:!1,apps:[]},i={showNightModeButton:!0,apps:[{enabled:!0,autostart:!0,url:`/@signalk/freeboard-sk/`,label:`Freeboard-SK`,icon:`/@signalk/freeboard-sk/assets/icons/icon-72x72.png`,color:``},{enabled:!0,autostart:!1,url:`/@halos-org/skip/`,label:`Skip`,icon:`/@halos-org/skip/assets/icon-72x72.png`,color:``},{enabled:!0,autostart:!1,url:`/admin/`,label:`Settings`,icon:`/@signalk/app-dock/icon-settings.svg`,color:`#78788c`}]},a=!1,o=!0;for(let e of[`/skServer/loginStatus`,`/loginStatus`])try{let t=await fetch(e);if(t.ok){let e=await t.json(),n=e.authenticationRequired===!1,r=e.status===`loggedIn`?e.userLevel:void 0;a=!n&&r!==`admin`,o=n||r===`admin`||r===`readwrite`;break}}catch{}async function s(){try{let t=await fetch(`/plugins/signalk-app-dock/settings`);if(!t.ok)return null;let n=await t.json();return Array.isArray(n.apps)?{...e,...n}:null}catch{return null}}let c={...e},l=!a;if(a){let t=await s();l=t!==null,c=t||{...e,...i}}else try{let[t,n]=await Promise.all([fetch(`/plugins/signalk-app-dock/config`),fetch(`/plugins/signalk-app-dock/settings`)]);if(t.ok){let n=await t.json(),r=n.configuration||n;c={...e,...r}}if(n.ok){let e=await n.json();Array.isArray(e.apps)&&e.apps.length>0&&(c.apps=e.apps)}}catch(e){console.warn(`[Dock] Could not load config, using defaults.`,e)}(!Array.isArray(c.apps)||c.apps.length===0)&&console.warn(`[Dock] No apps configured — open Plugin Config to add webapps.`);let u=n(`iframe-container`),d=n(`dock-backdrop`),f=n(`dock`),p=n(`dock-inner`),ee=n(`loading-overlay`),te=n(`loading-label`),ne=document.getElementById(`idle-hint`),m=n(`idle-hint-text`),h=n(`dock-handle`),g=!1,_={},v=`day`,y=l&&o;async function b(){if(l)try{let e=await fetch(`/plugins/signalk-app-dock/mode`);e.ok&&(v=(await e.json()).value||`day`,x())}catch(e){console.warn(`[Dock] Could not fetch environment.mode`,e)}}function x(){let e=document.querySelector(`.dock-item-nightmode .dock-icon`);if(!e)return;e.innerHTML=t[v===`night`?`moon`:`sun`];let n=document.querySelector(`.dock-item-nightmode .dock-label`);n&&(n.textContent=v===`night`?`Day mode`:`Night mode`)}async function re(){if(!y)return;await b();let e=v===`night`?`day`:`night`;try{(await fetch(`/signalk/v1/api/vessels/self/environment/mode`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:JSON.stringify({value:e})})).ok&&(v=e,x())}catch(e){console.warn(`[Dock] Failed to toggle mode`,e)}}function S(){return!!(document.fullscreenEnabled&&document.documentElement.requestFullscreen)}function C(){return!!document.fullscreenElement}function ie(){S()&&(C()?document.exitFullscreen().catch(e=>console.warn(`[Dock] exitFullscreen`,e)):document.documentElement.requestFullscreen().catch(e=>console.warn(`[Dock] requestFullscreen`,e)))}function ae(){let e=document.querySelector(`.dock-item-fullscreen .dock-icon`);if(!e)return;e.innerHTML=t[C()?`exitFullscreen`:`fullscreen`];let n=document.querySelector(`.dock-item-fullscreen .dock-label`);n&&(n.textContent=C()?`Exit fullscreen`:`Fullscreen`)}let w=c.position;f.classList.add(`pos-${w}`),p.classList.add(`align-${w}`),h.classList.add(`pos-${w}`);let T=w===`left`||w===`right`;T&&p.classList.add(`layout-vertical`);let oe=c.magnification!==!1,se=Math.max(1,Math.min(c.magnificationScale||1.7,2.5)),ce=c.iconSize*3.5;function le(){let e=document.getElementById(`idle-version`);e&&fetch(`/skServer/webapps`).then(e=>e.json()).then(t=>{let n=t.find(e=>e.name===`@signalk/app-dock`);n&&(e.textContent=`v`+n.version)}).catch(()=>{}),!c.apps||c.apps.length===0?a?m.innerHTML=`App Dock can only be customised by an administrator.<br>Ask an admin to set up apps in <strong>Plugin Config</strong>.`:m.innerHTML=`No apps configured yet.<br>Open <strong>Admin UI → Plugin Config → App Dock</strong><br>and click <strong>Discover Installed Webapps</strong> to get started.`:m.innerHTML=`Double-tap <strong>anywhere</strong> to open the dock`+(a?`<br><br><span style="font-size:12px;opacity:0.6">Customisation is admin-only</span>`:`<br><br><span style="font-size:12px;opacity:0.6">Configure in Admin UI → Plugin Config → App Dock</span>`)}function ue(){ne&&ne.classList.add(`hidden`)}function de(){switch(w){case`top`:return`dock-label-below`;case`left`:return`dock-label-right`;case`right`:return`dock-label-left`;default:return``}}function E(e,n,r,i){let a=c.iconSize,o=Math.round(a*.25)+`px`,s=de(),l=document.createElement(`div`);l.className=`dock-item dock-item-utility `+e;let u=document.createElement(`div`);u.className=`dock-icon dock-icon-utility`,u.style.width=a+`px`,u.style.height=a+`px`,u.style.borderRadius=o,u.innerHTML=t[n];let d=document.createElement(`div`);d.className=`dock-dot`;let f=document.createElement(`div`);return f.className=`dock-label`+(s?` `+s:``),f.textContent=r,l.appendChild(u),l.appendChild(d),l.appendChild(f),l.addEventListener(`touchstart`,()=>l.classList.add(`pressing`),{passive:!0}),l.addEventListener(`touchend`,()=>l.classList.remove(`pressing`),{passive:!0}),l.addEventListener(`touchcancel`,()=>l.classList.remove(`pressing`),{passive:!0}),l.addEventListener(`click`,e=>{O?e.preventDefault():i()}),l}function fe(){let e=document.createElement(`div`);return e.className=`dock-separator`,e}function pe(){p.innerHTML=``;let e=c.iconSize,t=Math.round(e*.25)+`px`,n=de(),r=c.showNightModeButton&&y;r&&p.appendChild(E(`dock-item-nightmode`,`sun`,`Night mode`,()=>void re())),c.showFullscreenButton&&S()&&p.appendChild(E(`dock-item-fullscreen`,`fullscreen`,`Fullscreen`,ie)),(r||c.showFullscreenButton&&S())&&p.appendChild(fe()),c.apps.forEach((r,i)=>{let a=document.createElement(`div`);a.className=`dock-item`,a.dataset.index=String(i);let o=document.createElement(`div`);o.className=`dock-icon`,o.style.width=e+`px`,o.style.height=e+`px`,o.style.borderRadius=t,r.color&&(o.style.background=r.color);let s=r.icon||``;if(s.startsWith(`/`)||s.startsWith(`http`)){let t=document.createElement(`img`);t.src=s,t.alt=r.label,t.onerror=()=>{o.removeChild(t),o.style.fontSize=Math.round(e*.42)+`px`,o.textContent=r.label.slice(0,2).toUpperCase()},o.appendChild(t)}else o.style.fontSize=Math.round(e*.48)+`px`,o.textContent=s||r.label.slice(0,2).toUpperCase();let c=document.createElement(`div`);c.className=`dock-dot`;let l=document.createElement(`div`);l.className=`dock-label`+(n?` `+n:``),l.textContent=r.label,a.appendChild(o),a.appendChild(c),a.appendChild(l),a.addEventListener(`touchstart`,()=>a.classList.add(`pressing`),{passive:!0}),a.addEventListener(`touchend`,()=>a.classList.remove(`pressing`),{passive:!0}),a.addEventListener(`touchcancel`,()=>a.classList.remove(`pressing`),{passive:!0}),a.addEventListener(`click`,e=>{O?e.preventDefault():I(i)}),p.appendChild(a)}),c.showExitButton&&(p.appendChild(fe()),p.appendChild(E(`dock-item-exit`,`close`,`Exit to Admin`,()=>{window.location.href=`/admin/`}))),c.showNightModeButton&&x(),c.showFullscreenButton&&S()&&ae()}let D=!1,O=!1,me=0;function he(e){let t=p.querySelectorAll(`.dock-item`),n=-1,r=1/0;t.forEach((t,i)=>{let a=t.querySelector(`.dock-icon`);if(!a)return;let o=t.getBoundingClientRect(),s=T?o.top+o.height/2:o.left+o.width/2,l=T?e.y:e.x,u=Math.abs(l-s);u<r&&(r=u,n=i);let d=Math.min(u/ce,1),f=1+(se-1)*(1-d*d),p=Math.round(c.iconSize*f);a.classList.remove(`settling`),a.style.width=p+`px`,a.style.height=p+`px`,a.style.borderRadius=Math.round(p*.25)+`px`,a.querySelector(`img`)||(a.style.fontSize=Math.round(p*.48)+`px`)}),t.forEach((e,t)=>{e.classList.toggle(`mag-closest`,t===n)})}function k(){let e=p.querySelectorAll(`.dock-item`),t=c.iconSize;e.forEach(e=>{let n=e.querySelector(`.dock-icon`);n&&(n.classList.add(`settling`),n.style.width=t+`px`,n.style.height=t+`px`,n.style.borderRadius=Math.round(t*.25)+`px`,n.querySelector(`img`)||(n.style.fontSize=Math.round(t*.48)+`px`),e.classList.remove(`mag-closest`))})}oe&&(p.addEventListener(`mousemove`,e=>{if(D)return;D=!0;let t={x:e.clientX,y:e.clientY};requestAnimationFrame(()=>{he(t),D=!1})}),p.addEventListener(`mouseleave`,()=>{D=!1,k()}),p.addEventListener(`touchstart`,e=>{let t=e.touches[0];O=!1,me=T?t.clientY:t.clientX},{passive:!0}),p.addEventListener(`touchmove`,e=>{let t=e.touches[0],n=T?t.clientY:t.clientX;if(Math.abs(n-me)>10&&(O=!0),D)return;D=!0;let r={x:t.clientX,y:t.clientY};requestAnimationFrame(()=>{he(r),D=!1})},{passive:!0}),p.addEventListener(`touchend`,()=>{D=!1,k(),setTimeout(()=>{O=!1},50)},{passive:!0}));let A=0;function j(){g||(g=!0,A=Date.now(),f.classList.add(`visible`),d.classList.add(`visible`),Y())}function M(){g&&(X&&X.classList.contains(`visible`)||(g=!1,f.classList.remove(`visible`),d.classList.remove(`visible`),k(),Y()))}d.addEventListener(`click`,e=>{Date.now()-A<500?e.stopPropagation():M()});function N(e){te.textContent=`Loading ${e}\u2026`,ee.classList.add(`visible`)}function P(){ee.classList.remove(`visible`)}let F=`allow-same-origin allow-scripts allow-forms allow-popups allow-pointer-lock allow-top-navigation-by-user-activation`;function ge(e){try{let t=new URL(e.url,window.location.href);return t.origin===window.location.origin&&/^\/admin(\/|$)/.test(t.pathname)}catch{return!1}}function _e(e,t){N(t.label),e.addEventListener(`load`,P,{once:!0});try{e.contentWindow?.location.reload()}catch{e.src=t.url}}function I(e){let t=c.apps[e];if(!t)return;ue(),document.querySelectorAll(`.dock-item`).forEach(t=>{t.classList.toggle(`active`,t.dataset.index===String(e))});let n=ge(t),r=!n&&!!_[t.url];if(r&&c.iframeMode!==`destroy`&&_[t.url].classList.contains(`active`))_e(_[t.url],t),Y(),M();else{if(c.iframeMode===`destroy`?(u.innerHTML=``,Object.keys(_).forEach(e=>delete _[e])):Object.values(_).forEach(e=>e.classList.remove(`active`)),n&&_[t.url]&&(_[t.url].remove(),delete _[t.url]),!_[t.url]){r||N(t.label);let e=document.createElement(`iframe`);e.allow=`fullscreen; geolocation`,e.setAttribute(`sandbox`,F),e.style.touchAction=`auto`,e.addEventListener(`load`,P,{once:!0}),u.appendChild(e),e.src=t.url,_[t.url]=e}_[t.url].classList.add(`active`),Y(),M()}}let L=0,R=0,z=0,B=null;function V(){return performance.timeOrigin+performance.now()}function ve(e,t){let n=e.defaultView&&e.defaultView.performance;return n?n.timeOrigin+t.timeStamp:V()}function ye(){if(H&&console.log(`[Dock] triggerDock called, dockVisible=`,g),!g){if(j(),H){let e=f.getBoundingClientRect();console.log(`[Dock] after showDock: dock rect`,e,`classes`,f.className)}navigator.vibrate&&navigator.vibrate(8)}}let H=new URLSearchParams(location.search).has(`debugDock`);function U(e,t,n=null,r=V()){if(g)return;let i=e-R,a=t-z,o=Math.round(Math.sqrt(i*i+a*a)),s=o<=60&&n===B,c=r-L,l=c>=0&&c<600&&s;H&&console.log(`[Dock] tap`,{x:Math.round(e),y:Math.round(t),gap:c,dist:o,close:s,match:l}),l?(L=0,ye()):(L=r,R=e,z=t,B=n)}let be=new WeakSet;function xe(e,t){if(be.has(e))return;be.add(e);let n=()=>{if(!t)return{x:0,y:0};let e=t.getBoundingClientRect();return{x:e.left,y:e.top}},r=-1/0,i=(e,t,n)=>{Math.abs(n-r)<50||(r=n,U(e,t,null,n))};e.addEventListener(`pointerdown`,t=>{let r=n();i(t.clientX+r.x,t.clientY+r.y,ve(e,t))},{passive:!0,capture:!0}),e.addEventListener(`touchstart`,t=>{if(!t.touches||t.touches.length===0)return;let r=t.touches[0],a=n();i(r.clientX+a.x,r.clientY+a.y,ve(e,t))},{passive:!0,capture:!0})}let W=new WeakSet;function G(e){let t=()=>{let t=null;try{t=e.contentDocument}catch{}let n=we(e);n&&Te(n,e,t!==null),t&&!W.has(t)&&(W.add(t),xe(t,e),Se(t))};e.addEventListener(`load`,t),t()}function Se(e){e.querySelectorAll(`iframe`).forEach(e=>G(e));let t=e.documentElement||e.body||e;new MutationObserver(e=>{for(let t of e)t.addedNodes.forEach(e=>{if(!e||e.nodeType!==Node.ELEMENT_NODE)return;let t=e;t.tagName===`IFRAME`&&G(t),t.querySelectorAll(`iframe`).forEach(e=>G(e))}),t.removedNodes.length>0&&Y()}).observe(t,{childList:!0,subtree:!0})}let K=new WeakMap,q=new WeakSet,Ce=-1/0;function we(e){let t=e;for(;t&&t.parentElement!==u;){let e=t.ownerDocument&&t.ownerDocument.defaultView;t=e?e.frameElement:null}return t}function Te(e,t,n){q.delete(t);let r=K.get(e);(t===e||!r)&&(r=new Set,K.set(e,r)),n?r.delete(t):r.add(t),Y()}function J(e){let t=e&&K.get(e)||new Set;for(let e of t)(!e.isConnected||!e.ownerDocument.defaultView)&&t.delete(e);return t}function Y(){let e=u.querySelector(`iframe.active`),t=!1;if(!g)for(let n of J(e))q.has(n)||(t=!0);h.classList.toggle(`visible`,t)}function Ee(e){let t=0,n=0;for(let r=e;r;){let e=r.getBoundingClientRect();if(t+=e.left,n+=e.top,r.parentElement===u)break;let i=r.ownerDocument.defaultView;r=i?i.frameElement:null}return{x:t,y:n}}function De(e,t){for(let n=e;n;n=n.parent){if(n===t)return!0;if(n===n.parent)return!1}return!1}window.addEventListener(`message`,e=>{let t=e.data;if(!t||t.type!==`signalk-app-dock:pointerdown`||!r(t.x)||!r(t.y))return;let n=u.querySelector(`iframe.active`);if(!n||!e.source)return;let i=null;for(let t of J(n))De(e.source,t.contentWindow)&&(i=t);if(!i)return;e.source===i.contentWindow&&(q.add(i),Y());let a=V(),o=r(t.t)&&t.t<=a?t.t:a;if(!(Math.abs(o-Ce)<50)){if(Ce=o,e.source===i.contentWindow){let e=Ee(i);U(t.x+e.x,t.y+e.y,null,o)}else U(t.x,t.y,e.source,o)}}),h.addEventListener(`click`,ye),xe(document,null),W.add(document),Se(document),pe(),c.showNightModeButton&&y&&(b(),setInterval(b,5e3)),c.showFullscreenButton&&S()&&document.addEventListener(`fullscreenchange`,ae);let Oe=[`position`,`iframeMode`,`iconSize`,`magnification`,`magnificationScale`,`showNightModeButton`,`showFullscreenButton`,`showExitButton`];async function ke(){try{let t=c,n=c;if(a)n=await s()||t;else{let[r,i]=await Promise.all([fetch(`/plugins/signalk-app-dock/config`),fetch(`/plugins/signalk-app-dock/settings`)]);if(r.ok){let i=await r.json(),a=i.configuration||i;n={...e,...a,apps:t.apps}}if(i.ok){let e=await i.json();Array.isArray(e.apps)&&e.apps.length>0&&(n={...n,apps:e.apps})}}if(Oe.some(e=>t[e]!==n[e])){window.location.reload();return}JSON.stringify(n.apps)===JSON.stringify(t.apps)?c=n:(c=n,pe())}catch{}}l&&setInterval(ke,5e3);let X=document.getElementById(`tour-overlay`),Ae=document.getElementById(`tour-btn-got-it`),Z=document.getElementById(`tour-btn-dismiss`);function Q(){X?.classList.remove(`visible`),M()}function je(){X?.classList.add(`visible`),j()}Ae&&Ae.addEventListener(`click`,Q),!l||!o?(Z&&Z.remove(),document.getElementById(`tour-buttons`)?.style.setProperty(`justify-content`,`center`)):Z&&Z.addEventListener(`click`,()=>{Q(),fetch(`/plugins/signalk-app-dock/dismiss-tour`,{method:`POST`}).catch(()=>{})});let $=document.getElementById(`tour-link`);$&&(a?$.replaceWith(document.createTextNode(`Plugin Config (admin only)`)):$.addEventListener(`click`,e=>{e.preventDefault();let t=`/admin/#/serverConfiguration/plugins/signalk-app-dock`;ue();let n=document.createElement(`iframe`);n.allow=`fullscreen; geolocation`,n.setAttribute(`sandbox`,F),n.style.touchAction=`auto`,n.classList.add(`active`),u.innerHTML=``,Object.keys(_).forEach(e=>delete _[e]),u.appendChild(n),n.src=t,_[t]=n,Q()})),c.tourDismissed||je();let Me=c.apps.findIndex(e=>e.autostart);Me>=0?I(Me):le()})().catch(e=>console.error(`[Dock] failed to start`,e));
|