@signalk/app-dock 1.4.1 → 1.4.2
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/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:c,cache:l,cacheSignal:u,cloneElement:d,createContext:f,createElement:p,createRef:m,forwardRef:h,isValidElement:g,lazy:_,memo:v,startTransition:y,use:b,useActionState:x,useCallback:S,useContext:C,useDebugValue:w,useDeferredValue:T,useEffect:E,useEffectEvent:D,useId:O,useImperativeHandle:k,useInsertionEffect:A,useLayoutEffect:j,useMemo:M,useOptimistic:N,useReducer:ee,useRef:P,useState:F,useSyncExternalStore:I,useTransition:L,version:R}=e,z=window.__SK_REACT_JSX_RUNTIME__;if(!z)throw Error(`App Dock: window.__SK_REACT_JSX_RUNTIME__ is not set by the host.`);var B=z,{Fragment:V,jsx:H,jsxs:U}=B;B.jsxDEV;function W(e){return e instanceof Error?e.message:String(e)}var G={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 K({icon:e,label:t,color:n,size:r}){let i=r||36,a={...G.iconBox,width:i,height:i,borderRadius:i*.22,...n?{background:n}:{}},o=e||``;return o.startsWith(`/`)||o.startsWith(`http`)?H(`div`,{style:a,children:H(`img`,{src:o,alt:t,style:{...G.iconImg,padding:i*.12},onError:e=>{let n=e.currentTarget;n.style.display=`none`,n.parentElement&&(n.parentElement.textContent=(t||`??`).slice(0,2).toUpperCase())}})}):H(`div`,{style:a,children:o||(t||`??`).slice(0,2).toUpperCase()})}function q({label:e,value:t,options:n,onChange:r,hint:i}){return U(`div`,{style:G.fieldRow,children:[H(`span`,{style:G.label,children:e}),H(`select`,{style:G.select,value:t,onChange:e=>r(e.target.value),children:n.map(e=>H(`option`,{value:e.value,children:e.label},e.value))}),i&&H(`span`,{style:G.hint,children:i})]})}function J({label:e,value:t,onChange:n,hint:r}){return U(`div`,{style:G.fieldRow,children:[H(`span`,{style:G.label,children:e}),H(`input`,{style:G.input,type:`number`,value:t,onChange:e=>n(Number(e.target.value))}),r&&H(`span`,{style:G.hint,children:r})]})}function Y({label:e,checked:t,onChange:n,hint:r}){return U(`div`,{style:G.fieldRow,children:[H(`span`,{style:G.label,children:e}),H(`input`,{type:`checkbox`,style:G.checkboxField,checked:t,onChange:e=>n(e.target.checked)}),r&&H(`span`,{style:G.hint,children:r})]})}function X({configuration:e,save:t}){let n=e||{},[r,i]=F(()=>n.apps||[]),[a,o]=F(n.position||`bottom`),[s,c]=F(n.iframeMode||`keep-alive`),[l,u]=F(n.iconSize||56),[d,f]=F(n.magnification!==!1),[p,m]=F(n.magnificationScale||1.7),[h,g]=F(n.showNightModeButton||!1),[_,v]=F(n.showFullscreenButton||!1),[y,b]=F(n.showExitButton||!1),x=n.tourDismissed||!1,[C,w]=F(``),[T,D]=F(!1),[O,k]=F(!1),[A,j]=F(!1),[M,N]=F(null),[ee,I]=F(null),L=P(r);E(()=>{L.current=r},[r]);let R=P(null);E(()=>()=>{R.current&&clearTimeout(R.current)},[]);let z=S(e=>({position:a,iframeMode:s,iconSize:l,magnification:d,magnificationScale:p,showNightModeButton:h,showFullscreenButton:_,showExitButton:y,tourDismissed:x,apps:e}),[a,s,l,d,p,h,_,y,x]),B=S(async()=>{R.current&&clearTimeout(R.current),j(!0),w(`Saving…`),D(!1);try{await t(z(r)),w(`Configuration saved — plugin will restart`),D(!1),R.current=setTimeout(()=>w(``),5e3)}catch(e){w(`Save failed: `+W(e)),D(!0)}finally{j(!1)}},[r,z,t]),X=async()=>{k(!0),w(``),D(!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){w(`No webapps found. Is the server fully started?`),D(!0),k(!1);return}let t=L.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),w(r>0?`Found ${e.length} webapps, added ${r} new.`:`All ${e.length} webapps already in list.`),D(!1)}catch(e){w(`Discovery failed: `+W(e)),D(!0)}k(!1)},Z=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})),te=e=>i(r.filter((t,n)=>n!==e)),ne=e=>N(e),re=(e,t)=>{e.preventDefault(),I(t)},ie=()=>I(null),ae=(e,t)=>{if(e.preventDefault(),I(null),M!==null&&M!==t){let e=[...r],[n]=e.splice(M,1);e.splice(t,0,n),i(e)}N(null)},oe=()=>{N(null),I(null)},$=r.filter(e=>e.enabled!==!1);return U(`div`,{style:G.root,children:[H(`div`,{style:G.sectionTitle,children:`Dock Settings`}),H(q,{label:`Dock position`,value:a,onChange:o,options:[{value:`bottom`,label:`Bottom`},{value:`top`,label:`Top`},{value:`left`,label:`Left`},{value:`right`,label:`Right`}]}),H(q,{label:`iFrame lifecycle`,value:s,onChange:c,options:[{value:`keep-alive`,label:`Keep-alive (faster switching, more RAM)`},{value:`destroy`,label:`Destroy (reload each time, less RAM)`}]}),H(J,{label:`Icon size`,value:l,onChange:u,hint:`px`}),H(Y,{label:`Magnification effect`,checked:d,onChange:f}),d&&H(J,{label:`Magnification scale`,value:p,onChange:m,hint:`1.0 - 2.5`}),H(`div`,{style:G.sectionTitle,children:`Utility Buttons`}),H(Y,{label:`Night/Day mode toggle`,checked:h,onChange:g,hint:`Sun/moon button to toggle environment.mode`}),H(Y,{label:`Fullscreen toggle`,checked:_,onChange:v,hint:`Toggles browser fullscreen. Hidden on devices without Fullscreen API support (e.g. iPadOS Safari).`}),H(Y,{label:`Exit button`,checked:y,onChange:b,hint:`X button to return to Signal K admin UI`}),H(`div`,{style:G.sectionTitle,children:`Webapps`}),H(`button`,{style:{...G.btn,...G.btnPrimary,...O?{opacity:.6}:{}},onClick:()=>void X(),disabled:O,children:O?`Discovering…`:`Discover Installed Webapps`}),C&&H(`div`,{style:{...G.status,color:T?`#ef4444`:`#10b981`},children:C}),H(`div`,{style:G.sectionTitle,children:`Dock Apps (drag to reorder)`}),r.length===0?H(`div`,{style:G.empty,children:`No apps yet. Click Discover above.`}):H(`div`,{children:r.map((e,t)=>U(`div`,{draggable:!0,onDragStart:()=>ne(t),onDragOver:e=>re(e,t),onDragLeave:ie,onDrop:e=>ae(e,t),onDragEnd:oe,style:{...G.item,...e.enabled===!1?G.itemDisabled:{},...ee===t?G.dragOver:{},...M===t?{opacity:.4}:{}},children:[H(`span`,{style:G.handle,children:`≡`}),H(K,{icon:e.icon,label:e.label||e.url,color:e.color}),U(`div`,{style:G.info,children:[H(`div`,{style:G.name,children:e.label||e.url}),H(`div`,{style:G.url,children:e.url})]}),H(`div`,{style:G.toggle,title:`Enabled`,children:H(`input`,{type:`checkbox`,checked:e.enabled!==!1,onChange:()=>Z(t),style:G.checkbox})}),H(`div`,{style:{...G.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:`▶`}),H(`button`,{style:{...G.btn,...G.btnDanger},onClick:()=>te(t),children:`Remove`})]},e.url+t))}),($.length>0||h||_||y)&&U(V,{children:[H(`div`,{style:G.sectionTitle,children:`Preview`}),U(`div`,{style:G.preview,children:[h&&H(`div`,{style:{...G.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:`☀️`}),_&&H(`div`,{style:{...G.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:`⛶`}),(h||_)&&H(`div`,{style:{width:1,height:24,background:`rgba(255,255,255,0.15)`,alignSelf:`center`}}),$.map((e,t)=>H(K,{icon:e.icon,label:e.label||e.url,color:e.color,size:40},t)),y&&U(V,{children:[H(`div`,{style:{width:1,height:24,background:`rgba(255,255,255,0.15)`,alignSelf:`center`}}),H(`div`,{style:{...G.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(`div`,{style:G.actions,children:[H(`button`,{style:{...G.btn,...G.btnSave,...A?{opacity:.6}:{}},onClick:()=>void B(),disabled:A,children:A?`Saving…`:`Save Configuration`}),C&&H(`div`,{style:{...G.status,color:T?`#ef4444`:`#10b981`},children:C})]})]})}export{X 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||`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};
|
package/public/dock.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
*,:before,:after{box-sizing:border-box;margin:0;padding:0}html,body{-webkit-font-smoothing:antialiased;touch-action:none;background:#000;width:100%;height:100%;font-family:-apple-system,BlinkMacSystemFont,SF Pro Display,Helvetica Neue,Arial,sans-serif;overflow:hidden}#iframe-container{z-index:1;position:fixed;inset:0}#iframe-container iframe{touch-action:auto;border:none;width:100%;height:100%;display:none;position:absolute;inset:0}#iframe-container iframe.active{display:block}#idle-hint{z-index:2;pointer-events:none;opacity:0;flex-direction:column;justify-content:center;align-items:center;gap:12px;animation:1.2s .6s forwards idle-fade-in;display:flex;position:fixed;inset:0}#idle-hint.hidden{display:none}#idle-logo{opacity:.85;border-radius:16px;width:72px;height:72px;margin-bottom:4px}#idle-title{color:#ffffffb3;letter-spacing:.02em;font-size:22px;font-weight:700}#idle-version{color:#ffffff59;letter-spacing:.05em;margin-top:-8px;font-size:11px;font-weight:500}#idle-hint-text{color:#ffffff8c;letter-spacing:.02em;text-align:center;max-width:480px;margin-top:12px;padding:0 32px;font-size:15px;font-weight:500;line-height:1.6}@keyframes idle-fade-in{to{opacity:1}}#dock-handle{z-index:5;appearance:none;cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent;background:0 0;border:none;padding:12px 18px;display:none;position:fixed}#dock-handle.visible{display:block}#dock-handle:before{content:"";background:#fff9;border-radius:3px;width:56px;height:6px;display:block;box-shadow:0 0 0 1px #00000059}#dock-handle.pos-bottom{bottom:env(safe-area-inset-bottom,0px);left:50%;transform:translate(-50%)}#dock-handle.pos-top{top:env(safe-area-inset-top,0px);left:50%;transform:translate(-50%)}#dock-handle.pos-left{left:env(safe-area-inset-left,0px);padding:18px 12px;top:50%;transform:translateY(-50%)}#dock-handle.pos-right{right:env(safe-area-inset-right,0px);padding:18px 12px;top:50%;transform:translateY(-50%)}#dock-handle.pos-left:before,#dock-handle.pos-right:before{width:6px;height:56px}#dock-backdrop{z-index:10;opacity:0;pointer-events:none;-webkit-backdrop-filter:blur(2px);background:#00000073;transition:opacity .28s;position:fixed;inset:0}#dock-backdrop.visible{opacity:1;pointer-events:auto}#dock{z-index:40;opacity:0;pointer-events:none;transition:transform .42s cubic-bezier(.34,1.45,.64,1),opacity .25s;position:fixed}#dock.visible{opacity:1;pointer-events:auto}#dock.pos-bottom{bottom:max(16px, env(safe-area-inset-bottom,16px));left:50%;transform:translate(-50%)translateY(calc(100% + 32px))scale(.92)}#dock.pos-bottom.visible{transform:translate(-50%)translateY(0)scale(1)}#dock.pos-top{top:max(16px, env(safe-area-inset-top,16px));left:50%;transform:translate(-50%)translateY(calc(-100% - 32px))scale(.92)}#dock.pos-top.visible{transform:translate(-50%)translateY(0)scale(1)}#dock.pos-left{left:max(16px, env(safe-area-inset-left,16px));top:50%;transform:translateY(-50%)translate(calc(-100% - 32px))scale(.92)}#dock.pos-left.visible{transform:translateY(-50%)translate(0)scale(1)}#dock.pos-right{right:max(16px, env(safe-area-inset-right,16px));top:50%;transform:translateY(-50%)translate(calc(100% + 32px))scale(.92)}#dock.pos-right.visible{transform:translateY(-50%)translate(0)scale(1)}#dock-inner{-webkit-backdrop-filter:blur(60px)saturate(180%);background:#1e1e208c;border:1px solid #ffffff2e;border-radius:26px;flex-direction:row;gap:10px;padding:12px 18px;display:flex;box-shadow:0 16px 48px #00000080,0 2px 6px #00000040,inset 0 .5px #ffffff26}#dock-inner.align-bottom{align-items:flex-end}#dock-inner.align-top{align-items:flex-start}#dock-inner.align-left,#dock-inner.align-right{align-items:center}#dock-inner.layout-vertical{flex-direction:column}.dock-item{cursor:pointer;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;user-select:none;touch-action:manipulation;flex-direction:column;align-items:center;gap:5px;display:flex;position:relative}.dock-icon{will-change:width, height;background:#3a3a3ee6;justify-content:center;align-items:center;transition:width 80ms,height 80ms,border-radius 80ms,box-shadow .18s,filter .18s;display:flex;position:relative;overflow:hidden;box-shadow:0 2px 10px #0006,inset 0 1px #ffffff14}.dock-icon.settling{transition:width .28s cubic-bezier(.34,1.2,.64,1),height .28s cubic-bezier(.34,1.2,.64,1),border-radius .28s,box-shadow .18s,filter .18s}.dock-icon:after{content:"";pointer-events:none;opacity:0;background:radial-gradient(#ffffff0f 0%,#0000 70%);height:6px;transition:opacity .2s;position:absolute;bottom:-4px;left:15%;right:15%}.dock-item.active .dock-icon:after,.dock-item.mag-closest .dock-icon:after{opacity:1}.dock-icon img{object-fit:contain;pointer-events:none;width:100%;height:100%;padding:10px}.dock-item.pressing .dock-icon{filter:brightness(.88)}.dock-item.active .dock-icon{box-shadow:0 4px 18px #00000080,0 0 0 2px #ffffff38,inset 0 1px #ffffff1f}.dock-dot{opacity:0;background:#ffffffd9;border-radius:50%;flex-shrink:0;width:4px;height:4px;transition:opacity .2s}.dock-item.active .dock-dot{opacity:1}.dock-label{color:#fffffff2;letter-spacing:.02em;white-space:nowrap;pointer-events:none;opacity:0;z-index:1;background:#161618eb;border:1px solid #ffffff1a;border-radius:7px;padding:5px 10px;font-size:11px;font-weight:600;transition:opacity .12s;position:absolute;bottom:calc(100% + 10px);left:50%;transform:translate(-50%);box-shadow:0 4px 12px #0006}.dock-item.pressing .dock-label,.dock-item.mag-closest .dock-label{opacity:1}.dock-label-below{top:calc(100% + 10px);bottom:auto}.dock-label-right{top:50%;bottom:auto;left:calc(100% + 10px);transform:translateY(-50%)}.dock-label-left{inset:50% calc(100% + 10px) auto auto;transform:translateY(-50%)}.dock-separator{background:#ffffff26;border-radius:1px;flex-shrink:0;align-self:center;width:1px;height:32px;margin:0 2px}.layout-vertical .dock-separator{width:32px;height:1px;margin:2px 0}.dock-icon-utility{box-shadow:none;background:#ffffff14;border:1px solid #ffffff1f}.dock-item-utility .dock-dot{display:none}.dock-item-utility:hover .dock-icon-utility,.dock-item-utility.pressing .dock-icon-utility{background:#ffffff26}.dock-item-exit:hover .dock-icon-utility{background:#ef444440}#loading-overlay{z-index:8;color:#ffffffbf;opacity:0;pointer-events:none;background:#0009;flex-direction:column;justify-content:center;align-items:center;gap:16px;font-size:14px;font-weight:500;transition:opacity .2s;display:flex;position:fixed;inset:0}#loading-overlay.visible{opacity:1;pointer-events:auto}.spinner{border:3px solid #ffffff26;border-top-color:#ffffffbf;border-radius:50%;width:32px;height:32px;animation:.75s linear infinite spin}@keyframes spin{to{transform:rotate(360deg)}}#tour-overlay{z-index:30;-webkit-backdrop-filter:blur(4px);opacity:0;pointer-events:none;background:#0009;justify-content:center;align-items:center;padding:24px;transition:opacity .3s;display:flex;position:fixed;inset:0}#tour-overlay.visible{opacity:1;pointer-events:auto}#tour-card{-webkit-backdrop-filter:blur(40px)saturate(180%);color:#ffffffeb;background:#1e1e20e6;border:1px solid #ffffff2e;border-radius:22px;width:100%;max-width:520px;margin-bottom:180px;padding:32px;box-shadow:0 24px 64px #0009,inset 0 .5px #ffffff26}#tour-title{letter-spacing:.01em;margin-bottom:16px;font-size:22px;font-weight:700}#tour-body{color:#ffffffc7;font-size:15px;line-height:1.55}#tour-body p{margin-bottom:12px}#tour-body p:last-child{margin-bottom:0}#tour-link{color:#60a5fa;font-weight:600;text-decoration:none}#tour-link:hover{text-decoration:underline}#tour-buttons{justify-content:flex-end;gap:10px;margin-top:24px;display:flex}.tour-btn{cursor:pointer;border:none;border-radius:10px;padding:10px 20px;font-family:inherit;font-size:14px;font-weight:600;transition:background .15s,transform .1s}.tour-btn-primary{color:#fff;background:#3b82f6}.tour-btn-primary:hover{background:#2563eb}.tour-btn-dismiss{color:#fffc;background:#ffffff14}.tour-btn-dismiss:hover{background:#ffffff26}.tour-btn:active{transform:scale(.97)}
|
|
1
|
+
*,:before,:after{box-sizing:border-box;margin:0;padding:0}html,body{-webkit-font-smoothing:antialiased;touch-action:none;background:#000;width:100%;height:100%;font-family:-apple-system,BlinkMacSystemFont,SF Pro Display,Helvetica Neue,Arial,sans-serif;overflow:hidden}#iframe-container{z-index:1;position:fixed;inset:0}#iframe-container iframe{touch-action:auto;border:none;width:100%;height:100%;display:none;position:absolute;inset:0}#iframe-container iframe.active{display:block}#idle-hint{z-index:2;pointer-events:none;opacity:0;flex-direction:column;justify-content:center;align-items:center;gap:12px;animation:1.2s .6s forwards idle-fade-in;display:flex;position:fixed;inset:0}#idle-hint.hidden{display:none}#idle-logo{opacity:.85;border-radius:16px;width:72px;height:72px;margin-bottom:4px}#idle-title{color:#ffffffb3;letter-spacing:.02em;font-size:22px;font-weight:700}#idle-version{color:#ffffff59;letter-spacing:.05em;margin-top:-8px;font-size:11px;font-weight:500}#idle-hint-text{color:#ffffff8c;letter-spacing:.02em;text-align:center;max-width:480px;margin-top:12px;padding:0 32px;font-size:15px;font-weight:500;line-height:1.6}@keyframes idle-fade-in{to{opacity:1}}#dock-handle{z-index:5;appearance:none;cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent;background:0 0;border:none;padding:12px 18px;display:none;position:fixed}#dock-handle.visible{display:block}#dock-handle:before{content:"";background:#fff9;border-radius:3px;width:56px;height:6px;display:block;box-shadow:0 0 0 1px #00000059}#dock-handle.pos-bottom{bottom:env(safe-area-inset-bottom,0px);left:50%;transform:translate(-50%)}#dock-handle.pos-top{top:env(safe-area-inset-top,0px);left:50%;transform:translate(-50%)}#dock-handle.pos-left{left:env(safe-area-inset-left,0px);padding:18px 12px;top:50%;transform:translateY(-50%)}#dock-handle.pos-right{right:env(safe-area-inset-right,0px);padding:18px 12px;top:50%;transform:translateY(-50%)}#dock-handle.pos-left:before,#dock-handle.pos-right:before{width:6px;height:56px}#dock-backdrop{z-index:10;opacity:0;pointer-events:none;-webkit-backdrop-filter:blur(2px);background:#00000073;transition:opacity .28s;position:fixed;inset:0}#dock-backdrop.visible{opacity:1;pointer-events:auto}#dock{z-index:40;opacity:0;pointer-events:none;transition:transform .42s cubic-bezier(.34,1.45,.64,1),opacity .25s;position:fixed}#dock.visible{opacity:1;pointer-events:auto}#dock.pos-bottom{bottom:max(16px, env(safe-area-inset-bottom,16px));left:50%;transform:translate(-50%)translateY(calc(100% + 32px))scale(.92)}#dock.pos-bottom.visible{transform:translate(-50%)translateY(0)scale(1)}#dock.pos-top{top:max(16px, env(safe-area-inset-top,16px));left:50%;transform:translate(-50%)translateY(calc(-100% - 32px))scale(.92)}#dock.pos-top.visible{transform:translate(-50%)translateY(0)scale(1)}#dock.pos-left{left:max(16px, env(safe-area-inset-left,16px));top:50%;transform:translateY(-50%)translate(calc(-100% - 32px))scale(.92)}#dock.pos-left.visible{transform:translateY(-50%)translate(0)scale(1)}#dock.pos-right{right:max(16px, env(safe-area-inset-right,16px));top:50%;transform:translateY(-50%)translate(calc(100% + 32px))scale(.92)}#dock.pos-right.visible{transform:translateY(-50%)translate(0)scale(1)}#dock-inner{-webkit-backdrop-filter:blur(60px)saturate(180%);background:#1e1e208c;border:1px solid #ffffff2e;border-radius:26px;flex-direction:row;gap:10px;padding:12px 18px;display:flex;box-shadow:0 16px 48px #00000080,0 2px 6px #00000040,inset 0 .5px #ffffff26}#dock-inner.align-bottom{align-items:flex-end}#dock-inner.align-top{align-items:flex-start}#dock-inner.align-left,#dock-inner.align-right{align-items:center}#dock-inner.layout-vertical{flex-direction:column}.dock-item{cursor:pointer;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;user-select:none;touch-action:manipulation;flex-direction:column;align-items:center;gap:5px;display:flex;position:relative}.dock-icon{will-change:width, height;background:#3a3a3ee6;justify-content:center;align-items:center;transition:width 80ms,height 80ms,border-radius 80ms,box-shadow .18s,filter .18s;display:flex;position:relative;overflow:hidden;box-shadow:0 2px 10px #0006,inset 0 1px #ffffff14}.dock-icon.settling{transition:width .28s cubic-bezier(.34,1.2,.64,1),height .28s cubic-bezier(.34,1.2,.64,1),border-radius .28s,box-shadow .18s,filter .18s}.dock-icon:after{content:"";pointer-events:none;opacity:0;background:radial-gradient(#ffffff0f 0%,#0000 70%);height:6px;transition:opacity .2s;position:absolute;bottom:-4px;left:15%;right:15%}.dock-item.active .dock-icon:after,.dock-item.mag-closest .dock-icon:after{opacity:1}.dock-icon img{object-fit:contain;pointer-events:none;width:100%;height:100%;padding:10px}.dock-item.pressing .dock-icon{filter:brightness(.88)}.dock-item.active .dock-icon{box-shadow:0 4px 18px #00000080,0 0 0 2px #ffffff38,inset 0 1px #ffffff1f}.dock-dot{opacity:0;background:#ffffffd9;border-radius:50%;flex-shrink:0;width:4px;height:4px;transition:opacity .2s}.dock-item.active .dock-dot{opacity:1}.dock-label{color:#fffffff2;letter-spacing:.02em;white-space:nowrap;pointer-events:none;opacity:0;z-index:1;background:#161618eb;border:1px solid #ffffff1a;border-radius:7px;padding:5px 10px;font-size:11px;font-weight:600;transition:opacity .12s;position:absolute;bottom:calc(100% + 10px);left:50%;transform:translate(-50%);box-shadow:0 4px 12px #0006}.dock-item.pressing .dock-label,.dock-item.mag-closest .dock-label{opacity:1}.dock-label-below{top:calc(100% + 10px);bottom:auto}.dock-label-right{top:50%;bottom:auto;left:calc(100% + 10px);transform:translateY(-50%)}.dock-label-left{inset:50% calc(100% + 10px) auto auto;transform:translateY(-50%)}.dock-separator{background:#ffffff26;border-radius:1px;flex-shrink:0;align-self:center;width:1px;height:32px;margin:0 2px}.layout-vertical .dock-separator{width:32px;height:1px;margin:2px 0}.dock-icon-utility{box-shadow:none;background:#ffffff14;border:1px solid #ffffff1f}.dock-item-utility .dock-dot{display:none}.dock-item-utility:hover .dock-icon-utility,.dock-item-utility.pressing .dock-icon-utility{background:#ffffff26}.dock-item-exit:hover .dock-icon-utility{background:#ef444440}.dock-svg-icon{color:#ffffffe6;flex-shrink:0;width:55%;height:55%}.dock-svg-sun{color:#fbbf24}.dock-svg-moon{color:#c7d2fe}#loading-overlay{z-index:8;color:#ffffffbf;opacity:0;pointer-events:none;background:#0009;flex-direction:column;justify-content:center;align-items:center;gap:16px;font-size:14px;font-weight:500;transition:opacity .2s;display:flex;position:fixed;inset:0}#loading-overlay.visible{opacity:1;pointer-events:auto}.spinner{border:3px solid #ffffff26;border-top-color:#ffffffbf;border-radius:50%;width:32px;height:32px;animation:.75s linear infinite spin}@keyframes spin{to{transform:rotate(360deg)}}#tour-overlay{z-index:30;-webkit-backdrop-filter:blur(4px);opacity:0;pointer-events:none;background:#0009;justify-content:center;align-items:center;padding:24px;transition:opacity .3s;display:flex;position:fixed;inset:0}#tour-overlay.visible{opacity:1;pointer-events:auto}#tour-card{-webkit-backdrop-filter:blur(40px)saturate(180%);color:#ffffffeb;background:#1e1e20e6;border:1px solid #ffffff2e;border-radius:22px;width:100%;max-width:520px;margin-bottom:180px;padding:32px;box-shadow:0 24px 64px #0009,inset 0 .5px #ffffff26}#tour-title{letter-spacing:.01em;margin-bottom:16px;font-size:22px;font-weight:700}#tour-body{color:#ffffffc7;font-size:15px;line-height:1.55}#tour-body p{margin-bottom:12px}#tour-body p:last-child{margin-bottom:0}#tour-link{color:#60a5fa;font-weight:600;text-decoration:none}#tour-link:hover{text-decoration:underline}#tour-buttons{justify-content:flex-end;gap:10px;margin-top:24px;display:flex}.tour-btn{cursor:pointer;border:none;border-radius:10px;padding:10px 20px;font-family:inherit;font-size:14px;font-weight:600;transition:background .15s,transform .1s}.tour-btn-primary{color:#fff;background:#3b82f6}.tour-btn-primary:hover{background:#2563eb}.tour-btn-dismiss{color:#fffc;background:#ffffff14}.tour-btn-dismiss:hover{background:#ffffff26}.tour-btn:active{transform:scale(.97)}
|
package/public/dock.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(e){let t=document.getElementById(e);if(!t)throw Error(`[Dock] #${e} is missing from index.html`);return t}function t(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 n={position:`bottom`,iframeMode:`keep-alive`,iconSize:56,magnification:!0,magnificationScale:1.7,showNightModeButton:!0,showFullscreenButton:!1,showExitButton:!1,apps:[]},r={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`}]},i=!1,a=!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;i=!n&&r!==`admin`,a=n||r===`admin`||r===`readwrite`;break}}catch{}async function o(){try{let e=await fetch(`/plugins/signalk-app-dock/settings`);if(!e.ok)return null;let t=await e.json();return Array.isArray(t.apps)?{...n,...t}:null}catch{return null}}let s={...n},c=!i;if(i){let e=await o();c=e!==null,s=e||{...n,...r}}else try{let[e,t]=await Promise.all([fetch(`/plugins/signalk-app-dock/config`),fetch(`/plugins/signalk-app-dock/settings`)]);if(e.ok){let t=await e.json(),r=t.configuration||t;s={...n,...r}}if(t.ok){let e=await t.json();Array.isArray(e.apps)&&e.apps.length>0&&(s.apps=e.apps)}}catch(e){console.warn(`[Dock] Could not load config, using defaults.`,e)}(!Array.isArray(s.apps)||s.apps.length===0)&&console.warn(`[Dock] No apps configured — open Plugin Config to add webapps.`);let l=e(`iframe-container`),u=e(`dock-backdrop`),d=e(`dock`),f=e(`dock-inner`),p=e(`loading-overlay`),ee=e(`loading-label`),te=document.getElementById(`idle-hint`),m=e(`idle-hint-text`),h=e(`dock-handle`),g=!1,_={},v=`day`,y=c&&a;async function b(){if(c)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.textContent=v===`night`?`🌙`:`☀️`;let t=document.querySelector(`.dock-item-nightmode .dock-label`);t&&(t.textContent=v===`night`?`Day mode`:`Night mode`)}async function ne(){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 re(){S()&&(C()?document.exitFullscreen().catch(e=>console.warn(`[Dock] exitFullscreen`,e)):document.documentElement.requestFullscreen().catch(e=>console.warn(`[Dock] requestFullscreen`,e)))}function ie(){let e=document.querySelector(`.dock-item-fullscreen .dock-icon`);if(!e)return;e.textContent=C()?`⤢`:`⛶`;let t=document.querySelector(`.dock-item-fullscreen .dock-label`);t&&(t.textContent=C()?`Exit fullscreen`:`Fullscreen`)}let w=s.position;d.classList.add(`pos-${w}`),f.classList.add(`align-${w}`),h.classList.add(`pos-${w}`);let T=w===`left`||w===`right`;T&&f.classList.add(`layout-vertical`);let ae=s.magnification!==!1,oe=Math.max(1,Math.min(s.magnificationScale||1.7,2.5)),se=s.iconSize*3.5;function ce(){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(()=>{}),!s.apps||s.apps.length===0?i?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`+(i?`<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 E(){te&&te.classList.add(`hidden`)}function le(){switch(w){case`top`:return`dock-label-below`;case`left`:return`dock-label-right`;case`right`:return`dock-label-left`;default:return``}}function D(e,t,n,r){let i=s.iconSize,a=Math.round(i*.25)+`px`,o=le(),c=document.createElement(`div`);c.className=`dock-item dock-item-utility `+e;let l=document.createElement(`div`);l.className=`dock-icon dock-icon-utility`,l.style.width=i+`px`,l.style.height=i+`px`,l.style.borderRadius=a,l.style.fontSize=Math.round(i*.48)+`px`,l.textContent=t;let u=document.createElement(`div`);u.className=`dock-dot`;let d=document.createElement(`div`);return d.className=`dock-label`+(o?` `+o:``),d.textContent=n,c.appendChild(l),c.appendChild(u),c.appendChild(d),c.addEventListener(`touchstart`,()=>c.classList.add(`pressing`),{passive:!0}),c.addEventListener(`touchend`,()=>c.classList.remove(`pressing`),{passive:!0}),c.addEventListener(`touchcancel`,()=>c.classList.remove(`pressing`),{passive:!0}),c.addEventListener(`click`,e=>{A?e.preventDefault():r()}),c}function ue(){let e=document.createElement(`div`);return e.className=`dock-separator`,e}function O(){f.innerHTML=``;let e=s.iconSize,t=Math.round(e*.25)+`px`,n=le(),r=s.showNightModeButton&&y;r&&f.appendChild(D(`dock-item-nightmode`,`☀️`,`Night mode`,()=>void ne())),s.showFullscreenButton&&S()&&f.appendChild(D(`dock-item-fullscreen`,`⛶`,`Fullscreen`,re)),(r||s.showFullscreenButton&&S())&&f.appendChild(ue()),s.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=>{A?e.preventDefault():ve(i)}),f.appendChild(a)}),s.showExitButton&&(f.appendChild(ue()),f.appendChild(D(`dock-item-exit`,`✕`,`Exit to Admin`,()=>{window.location.href=`/admin/`}))),s.showNightModeButton&&x(),s.showFullscreenButton&&S()&&ie()}let k=!1,A=!1,j=0;function M(e){let t=f.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(),c=T?o.top+o.height/2:o.left+o.width/2,l=T?e.y:e.x,u=Math.abs(l-c);u<r&&(r=u,n=i);let d=Math.min(u/se,1),f=1+(oe-1)*(1-d*d),p=Math.round(s.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 N(){let e=f.querySelectorAll(`.dock-item`),t=s.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`))})}ae&&(f.addEventListener(`mousemove`,e=>{if(k)return;k=!0;let t={x:e.clientX,y:e.clientY};requestAnimationFrame(()=>{M(t),k=!1})}),f.addEventListener(`mouseleave`,()=>{k=!1,N()}),f.addEventListener(`touchstart`,e=>{let t=e.touches[0];A=!1,j=T?t.clientY:t.clientX},{passive:!0}),f.addEventListener(`touchmove`,e=>{let t=e.touches[0],n=T?t.clientY:t.clientX;if(Math.abs(n-j)>10&&(A=!0),k)return;k=!0;let r={x:t.clientX,y:t.clientY};requestAnimationFrame(()=>{M(r),k=!1})},{passive:!0}),f.addEventListener(`touchend`,()=>{k=!1,N(),setTimeout(()=>{A=!1},50)},{passive:!0}));let de=0;function fe(){g||(g=!0,de=Date.now(),d.classList.add(`visible`),u.classList.add(`visible`),Y())}function P(){g&&(X&&X.classList.contains(`visible`)||(g=!1,d.classList.remove(`visible`),u.classList.remove(`visible`),N(),Y()))}u.addEventListener(`click`,e=>{Date.now()-de<500?e.stopPropagation():P()});function pe(e){ee.textContent=`Loading ${e}\u2026`,p.classList.add(`visible`)}function me(){p.classList.remove(`visible`)}let he=`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){pe(t.label),e.addEventListener(`load`,me,{once:!0});try{e.contentWindow?.location.reload()}catch{e.src=t.url}}function ve(e){let t=s.apps[e];if(!t)return;E(),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&&s.iframeMode!==`destroy`&&_[t.url].classList.contains(`active`))_e(_[t.url],t),Y(),P();else{if(s.iframeMode===`destroy`?(l.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||pe(t.label);let e=document.createElement(`iframe`);e.allow=`fullscreen; geolocation`,e.setAttribute(`sandbox`,he),e.style.touchAction=`auto`,e.addEventListener(`load`,me,{once:!0}),l.appendChild(e),e.src=t.url,_[t.url]=e}_[t.url].classList.add(`active`),Y(),P()}}let F=0,I=0,L=0,R=null;function z(){return performance.timeOrigin+performance.now()}function B(e,t){let n=e.defaultView&&e.defaultView.performance;return n?n.timeOrigin+t.timeStamp:z()}function V(){if(H&&console.log(`[Dock] triggerDock called, dockVisible=`,g),!g){if(fe(),H){let e=d.getBoundingClientRect();console.log(`[Dock] after showDock: dock rect`,e,`classes`,d.className)}navigator.vibrate&&navigator.vibrate(8)}}let H=new URLSearchParams(location.search).has(`debugDock`);function U(e,t,n=null,r=z()){if(g)return;let i=e-I,a=t-L,o=Math.round(Math.sqrt(i*i+a*a)),s=o<=40&&n===R,c=r-F,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?(F=0,V()):(F=r,I=e,L=t,R=n)}let ye=new WeakSet;function be(e,t){if(ye.has(e))return;ye.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,B(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,B(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=Ce(e);n&&we(n,e,t!==null),t&&!W.has(t)&&(W.add(t),be(t,e),xe(t))};e.addEventListener(`load`,t),t()}function xe(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,Se=-1/0;function Ce(e){let t=e;for(;t&&t.parentElement!==l;){let e=t.ownerDocument&&t.ownerDocument.defaultView;t=e?e.frameElement:null}return t}function we(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=l.querySelector(`iframe.active`),t=!1;if(!g)for(let n of J(e))q.has(n)||(t=!0);h.classList.toggle(`visible`,t)}function Te(e){let t=0,n=0;for(let r=e;r;){let e=r.getBoundingClientRect();if(t+=e.left,n+=e.top,r.parentElement===l)break;let i=r.ownerDocument.defaultView;r=i?i.frameElement:null}return{x:t,y:n}}function Ee(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 n=e.data;if(!n||n.type!==`signalk-app-dock:pointerdown`||!t(n.x)||!t(n.y))return;let r=l.querySelector(`iframe.active`);if(!r||!e.source)return;let i=null;for(let t of J(r))Ee(e.source,t.contentWindow)&&(i=t);if(!i)return;e.source===i.contentWindow&&(q.add(i),Y());let a=z(),o=t(n.t)&&n.t<=a?n.t:a;if(!(Math.abs(o-Se)<50)){if(Se=o,e.source===i.contentWindow){let e=Te(i);U(n.x+e.x,n.y+e.y,null,o)}else U(n.x,n.y,e.source,o)}}),h.addEventListener(`click`,V),be(document,null),W.add(document),xe(document),O(),s.showNightModeButton&&y&&(b(),setInterval(b,5e3)),s.showFullscreenButton&&S()&&document.addEventListener(`fullscreenchange`,ie);let De=[`position`,`iframeMode`,`iconSize`,`magnification`,`magnificationScale`,`showNightModeButton`,`showFullscreenButton`,`showExitButton`];async function Oe(){try{let e=s,t=s;if(i)t=await o()||e;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;t={...n,...a,apps:e.apps}}if(i.ok){let e=await i.json();Array.isArray(e.apps)&&e.apps.length>0&&(t={...t,apps:e.apps})}}if(De.some(n=>e[n]!==t[n])){window.location.reload();return}JSON.stringify(t.apps)===JSON.stringify(e.apps)?s=t:(s=t,O())}catch{}}c&&setInterval(Oe,5e3);let X=document.getElementById(`tour-overlay`),ke=document.getElementById(`tour-btn-got-it`),Z=document.getElementById(`tour-btn-dismiss`);function Q(){X?.classList.remove(`visible`),P()}function Ae(){X?.classList.add(`visible`),fe()}ke&&ke.addEventListener(`click`,Q),!c||!a?(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`);$&&(i?$.replaceWith(document.createTextNode(`Plugin Config (admin only)`)):$.addEventListener(`click`,e=>{e.preventDefault();let t=`/admin/#/serverConfiguration/plugins/signalk-app-dock`;E();let n=document.createElement(`iframe`);n.allow=`fullscreen; geolocation`,n.setAttribute(`sandbox`,he),n.style.touchAction=`auto`,n.classList.add(`active`),l.innerHTML=``,Object.keys(_).forEach(e=>delete _[e]),l.appendChild(n),n.src=t,_[t]=n,Q()})),s.tourDismissed||Ae();let je=s.apps.findIndex(e=>e.autostart);je>=0?ve(je):ce()})().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:`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));
|