@mxtommy/kip 4.5.0-beta.1 → 4.5.1
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/CHANGELOG.md +18 -0
- package/README.md +11 -7
- package/package.json +21 -8
- package/plugin/duckdb-parquet-storage.service.js +1182 -0
- package/plugin/history-series.service.js +439 -0
- package/plugin/index.js +705 -30
- package/plugin/openApi.json +253 -3
- package/plugin/plugin-auth.service.js +75 -0
- package/public/assets/help-docs/chartplotter.md +5 -18
- package/public/assets/help-docs/community.md +0 -3
- package/public/assets/help-docs/configuration.md +1 -1
- package/public/assets/help-docs/contact-us.md +0 -4
- package/public/assets/help-docs/dashboards.md +20 -18
- package/public/assets/help-docs/datainspector.md +7 -5
- package/public/assets/help-docs/history-api.md +116 -0
- package/public/assets/help-docs/menu.json +18 -6
- package/public/assets/help-docs/nodered-control-flows.md +125 -0
- package/public/assets/help-docs/putcontrols.md +101 -60
- package/public/assets/help-docs/welcome.md +6 -7
- package/public/assets/help-docs/widget-historical-series.md +66 -0
- package/public/assets/help-docs/zones.md +5 -10
- package/public/chunk-A6DQJFP4.js +16 -0
- package/public/chunk-B75MT7ND.js +1 -0
- package/public/{chunk-T6TFVZVM.js → chunk-CEB42O2C.js} +1 -1
- package/public/chunk-CHGXAEKT.js +2 -0
- package/public/chunk-D7VDX7ZF.js +5 -0
- package/public/{chunk-ZQER6AIQ.js → chunk-DEGYRCMI.js} +1 -1
- package/public/{chunk-M2B5OYGO.js → chunk-DEM56G4S.js} +1 -1
- package/public/chunk-DYTBBUMI.js +4 -0
- package/public/chunk-EQ2N7KDA.js +3 -0
- package/public/chunk-FNF7M3AE.js +1 -0
- package/public/chunk-IHURI4IH.js +5 -0
- package/public/{chunk-YIYYVDFO.js → chunk-IYRLINL7.js} +2 -2
- package/public/{chunk-5FEX27I4.js → chunk-JB4YVVNW.js} +1 -1
- package/public/chunk-JGGMFMY5.js +1 -0
- package/public/chunk-KPHICV76.js +5 -0
- package/public/{chunk-QZKCRH3H.js → chunk-KZ5DUKAX.js} +1 -1
- package/public/{chunk-HMOOTAEA.js → chunk-LQDSU4WS.js} +3 -3
- package/public/{chunk-IXQ7KIFY.js → chunk-MGPPVLZ7.js} +1 -1
- package/public/{chunk-QVCLOCEC.js → chunk-R7RQHWKJ.js} +1 -1
- package/public/chunk-RONXIZ2U.js +9 -0
- package/public/chunk-S72JTJPN.js +6 -0
- package/public/{chunk-KFFAA7DL.js → chunk-VCY32MWT.js} +8 -8
- package/public/chunk-YCEXTKGG.js +1 -0
- package/public/chunk-YKJKIWXO.js +6 -0
- package/public/chunk-ZV7IYYEQ.js +50 -0
- package/public/index.html +1 -1
- package/public/main-FQESQQV6.js +1 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -84
- package/.github/ISSUE_TEMPLATE/config.yml +0 -5
- package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -35
- package/.github/copilot-instructions.md +0 -205
- package/.github/instructions/angular.instructions.md +0 -123
- package/.github/instructions/best-practices.instructions.md +0 -59
- package/.github/instructions/project.instructions.md +0 -432
- package/.github/workflows/ci.yml +0 -37
- package/docs/widget-schematic.md +0 -102
- package/images/ActionSidenav.png +0 -0
- package/images/ChartplotterMode.png +0 -0
- package/images/KIPDemo.png +0 -0
- package/images/KipBrightness-1024.png +0 -0
- package/images/KipConfig-Units-1024.png +0 -0
- package/images/KipConfig-display-1024x488.png +0 -0
- package/images/KipFreeboard-SK-1024.png +0 -0
- package/images/KipGaugeSample1-1024x545.png +0 -0
- package/images/KipGaugeSample2-1024x488.png +0 -0
- package/images/KipGaugeSample3-1024x508.png +0 -0
- package/images/KipNightMode-1024.png +0 -0
- package/images/KipWidgetConfig-layout-1024.png +0 -0
- package/images/KipWidgetConfig-paths-1024x488.png +0 -0
- package/images/Options.png +0 -0
- package/images/exterior_user_installs.png +0 -0
- package/images/formfactor.png +0 -0
- package/public/assets/help-docs/datasets.md +0 -95
- package/public/chunk-2OB7ZJBR.js +0 -3
- package/public/chunk-6GGJZDRE.js +0 -1
- package/public/chunk-6V4GGGXE.js +0 -2
- package/public/chunk-A5BW6BUM.js +0 -1
- package/public/chunk-DGE5YFPU.js +0 -5
- package/public/chunk-G6M3Z3BY.js +0 -53
- package/public/chunk-GMGZLXY7.js +0 -4
- package/public/chunk-GUZ3BDVZ.js +0 -2
- package/public/chunk-ICDGHQFP.js +0 -6
- package/public/chunk-JCNE4QHQ.js +0 -15
- package/public/chunk-K6XYUNG4.js +0 -8
- package/public/chunk-LGCQEN7V.js +0 -4
- package/public/chunk-O3JH7UTR.js +0 -1
- package/public/chunk-Q3USFT4F.js +0 -2
- package/public/chunk-VIKU7BH7.js +0 -1
- package/public/chunk-XMQPXXLW.js +0 -8
- package/public/main-4URMGBQS.js +0 -1
- package/rm-npmjs-beta.sh +0 -50
- package/tools/schematics/collection.json +0 -9
- package/tools/schematics/create-host2-widget/files/readme/README.md.template +0 -109
- package/tools/schematics/create-host2-widget/files/spec/widget-__name@dasherize__.component.spec.ts +0 -38
- package/tools/schematics/create-host2-widget/files/widget/widget-__name@dasherize__.component.html +0 -6
- package/tools/schematics/create-host2-widget/files/widget/widget-__name@dasherize__.component.scss +0 -5
- package/tools/schematics/create-host2-widget/files/widget/widget-__name@dasherize__.component.ts.template +0 -94
- package/tools/schematics/create-host2-widget/index.js +0 -138
- package/tools/schematics/create-host2-widget/schema.json +0 -89
- package/tools/schematics/create-host2-widget/test/create-host2-widget.spec.ts +0 -70
- package/tools/schematics/create-host2-widget/utils/formatting.js +0 -119
package/public/main-4URMGBQS.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as ft}from"./chunk-2OB7ZJBR.js";import{a as Et,b as N,c as at,h as de,j as T,k as me,l as rt,n as fe,o as ue,t as st}from"./chunk-G6M3Z3BY.js";import{a as Ce}from"./chunk-QZKCRH3H.js";import{c as ae}from"./chunk-XMQPXXLW.js";import{a as Se,b as ye,c as we,d as Me,e as xe,f as ke}from"./chunk-QVCLOCEC.js";import{a as be}from"./chunk-IXQ7KIFY.js";import{c as he,f as ge,g as _e,i as ve}from"./chunk-GMGZLXY7.js";import{b as Oe}from"./chunk-HMOOTAEA.js";import{e as pe}from"./chunk-Q3USFT4F.js";import{c as nt,d as ot,e as K,f as B,g as re,h as se,i as ce,j as le}from"./chunk-LGCQEN7V.js";import"./chunk-DGE5YFPU.js";import{a as tt,b as $t,c as P,e as jt,f as qt,g as Gt}from"./chunk-5FEX27I4.js";import{a as oe}from"./chunk-T6TFVZVM.js";import{a as ee,g as ie,u as ne}from"./chunk-K6XYUNG4.js";import{c as Ut,e as Vt,h as F,i as Y,k as I,l as X,m as M}from"./chunk-6V4GGGXE.js";import{a as Zt,k as it,q as Xt,t as te}from"./chunk-GUZ3BDVZ.js";import{A as gt,Ab as b,Ac as Nt,Ba as St,Bb as C,Cb as G,Dc as Dt,De as L,Eb as U,Ee as A,Fb as V,Ga as yt,Gb as w,Ha as y,Hb as o,Hc as At,Ia as z,Ib as s,Ic as J,Jb as x,K as q,Ka as wt,Kc as mt,Lc as Tt,Ld as Wt,Qb as v,Sb as u,U as _t,Ub as g,Ya as f,a as ut,ac as W,ae as Qt,ba as vt,bc as Q,c as ht,cc as kt,ec as Ot,f as R,gd as Rt,hc as l,ic as H,jb as O,jc as It,ke as Jt,la as S,ld as Ft,md as Lt,nc as lt,nd as Kt,oc as dt,od as Bt,oe as et,pc as pt,pe as Yt,qa as a,qd as Z,rd as _,re as D,sa as bt,sd as zt,td as Ht,ub as Mt,va as p,w as $,wa as m,yb as xt,yc as Pt,z as j,za as Ct}from"./chunk-JCNE4QHQ.js";var ct=(i,c)=>{let t=a(M),e=a(P),n=t.getSplitShellEnabled(),r="/"+c.map(k=>k.path).join("/"),d=r.startsWith("/chartplotter"),h=r.startsWith("/dashboard");if(n&&h&&!d){let k=c.length>1?c[1].path:void 0;return e.createUrlTree(["chartplotter",k??""])}if(!n&&d){let k=c.length>1?c[1].path:void 0;return e.createUrlTree(["dashboard",k??""])}return!0};var Ie=[{path:"dashboard/:id",component:ft,canMatch:[ct]},{path:"chartplotter/:id",loadComponent:()=>import("./chunk-O3JH7UTR.js").then(i=>i.SplitShellComponent),canMatch:[ct]},{path:"settings",loadComponent:()=>import("./chunk-YIYYVDFO.js").then(i=>i.SettingsComponent),title:"KIP - Settings"},{path:"options",loadComponent:()=>import("./chunk-KFFAA7DL.js").then(i=>i.TabsComponent),title:"KIP - Options"},{path:"remote",loadComponent:()=>import("./chunk-A5BW6BUM.js").then(i=>i.RemoteControlComponent),title:"KIP - Remote Control"},{path:"help",loadComponent:()=>import("./chunk-6GGJZDRE.js").then(i=>i.AppHelpComponent),title:"KIP - Help"},{path:"data",loadComponent:()=>import("./chunk-ICDGHQFP.js").then(i=>i.DataInspectorComponent),title:"KIP - Data Inspector"},{path:"login",loadComponent:()=>import("./chunk-VIKU7BH7.js").then(i=>i.WidgetLoginComponent),title:"Login"},{path:"**",component:ft,canMatch:[ct]}];var Pe={production:!0};var He=(i,c)=>c.path;function $e(i,c){i&1&&(o(0,"mat-icon"),l(1,"info"),s())}function je(i,c){i&1&&(o(0,"mat-icon"),l(1,"info"),s())}function qe(i,c){i&1&&(o(0,"mat-icon",3),l(1,"report"),s())}function Ge(i,c){i&1&&(o(0,"mat-icon",4),l(1,"warning"),s())}function We(i,c){i&1&&(o(0,"mat-icon",5),l(1,"error"),s())}function Qe(i,c){i&1&&(o(0,"mat-icon",6),l(1,"emergency_home"),s())}function Je(i,c){if(i&1){let t=v();o(0,"div",2),b(1,$e,2,0,"mat-icon")(2,je,2,0,"mat-icon")(3,qe,2,0,"mat-icon",3)(4,Ge,2,0,"mat-icon",4)(5,We,2,0,"mat-icon",5)(6,Qe,2,0,"mat-icon",6),o(7,"div",7),l(8),s(),o(9,"div",8),l(10),Pt(11,"slice"),s(),o(12,"div",9),l(13),s(),o(14,"button",10),u("click",function(){let n=p(t).$implicit,r=g();return m(r.silence(n.path))}),o(15,"mat-icon"),l(16,"music_off"),s()(),o(17,"button",11),u("click",function(){let n=p(t).$implicit,r=g();return m(r.clear(n.path))}),o(18,"mat-icon"),l(19,"published_with_changes"),s()()(),x(20,"mat-divider",12)}if(i&2){let t,e=c.$implicit;f(),C((t=e.value.state)==="nominal"?1:t==="normal"?2:t==="alert"?3:t==="warn"?4:t==="alarm"?5:t==="emergency"?6:-1),f(7),H(e.value.state),f(2),It(" ",Nt(11,5,e.path,14)," "),f(3),H(e.value.message),f(),w("disabled",!(e.value.method!=null&&e.value.method.includes("sound")))}}function Ye(i,c){i&1&&(o(0,"mat-list-item",13)(1,"span",15),l(2,"Notifications Disabled"),s(),o(3,"span")(4,"i"),l(5,"*Enable notifications in Settings."),s()()())}function Ze(i,c){i&1&&(o(0,"mat-list-item",14)(1,"i"),l(2,'"No Notification"'),s()())}function Xe(i,c){if(i&1&&b(0,Ye,6,0,"mat-list-item",13)(1,Ze,3,0,"mat-list-item",14),i&2){let t=g();C(t.notificationConfig().disableNotifications?0:1)}}function ti(i,c){if(i&1){let t=v();o(0,"button",17),u("click",function(){p(t);let n=g(2);return m(n.mutePlayer(!n.isMuted))}),o(1,"span",18),l(2,"volume_up"),s(),l(3," Unmute Audio "),s()}}function ei(i,c){if(i&1){let t=v();o(0,"button",17),u("click",function(){p(t);let n=g(2);return m(n.mutePlayer(!n.isMuted))}),o(1,"span",18),l(2,"volume_off"),s(),l(3," Mute Audio "),s()}}function ii(i,c){if(i&1&&b(0,ti,4,0,"button",16)(1,ei,4,0,"button",16),i&2){let t=g();C(t.isMuted?0:1)}}var Ae=(()=>{class i{_notificationsService=a(T);_notifications$=this._notificationsService.observeNotifications();notificationConfig=_(this._notificationsService.observeNotificationConfiguration(),{requireSync:!0});menuNotifications=_(gt([this._notifications$,this._notificationsService.observeNotificationConfiguration()]).pipe(j(([t,e])=>{let n=[];return e.devices.showNormalState||n.push(nt.Normal),e.devices.showNominalState||n.push(nt.Nominal),t.filter(r=>r.value&&r.value.state&&!n.includes(r.value.state)).filter(r=>{let d=r.value?.method;return d?.length?d.includes(ot.Visual)||d.includes(ot.Sound):!1})})),{requireSync:!0,equal:Vt});isMuted=!1;mutePlayer(t){this.isMuted=t,this._notificationsService.mutePlayer(t)}silence(t){this._notificationsService.setSkMethod(t,[ot.Visual])}clear(t){this._notificationsService.setSkState(t,nt.Normal)}static \u0275fac=function(e){return new(e||i)};static \u0275cmp=O({type:i,selectors:[["menu-notifications"]],decls:6,vars:2,consts:[[1,"menu-item-container"],[1,"actions-container","actions-bottom-container"],[1,"notification-container"],[1,"icon-alert-color"],[1,"icon-warn-color"],[1,"icon-alarm-color"],[1,"icon-emergency-color"],[1,"notification-title"],[1,"notification-path","scrollable-text"],[1,"notification-text"],["mat-icon-button","",1,"notification-action-btn",3,"click","disabled"],["mat-icon-button","",1,"notification-action-btn",3,"click"],[1,"notification-divider"],["lines","3","disabled","true",2,"text-align","center"],["disabled","true",2,"text-align","center"],["matListItemTitle",""],["mat-flat-button","","matTooltip","Temporally toggle all notification audio. To permanently disable/enable notification audio, use the configuration settings option",1,"action-button"],["mat-flat-button","","matTooltip","Temporally toggle all notification audio. To permanently disable/enable notification audio, use the configuration settings option",1,"action-button",3,"click"],[1,"material-icons"]],template:function(e,n){e&1&&(o(0,"mat-list",0),U(1,Je,21,8,null,null,He,!1,Xe,2,1),s(),o(4,"div",1),b(5,ii,2,1),s()),e&2&&(f(),V(n.menuNotifications()),f(4),C(n.notificationConfig().disableNotifications?-1:5))},dependencies:[ve,ge,_e,oe,he,D,et,Jt,rt,le,ce,A,L,Rt],styles:[".notification-container[_ngcontent-%COMP%]{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-evenly;padding:2px 10px 7px;scroll-behavior:smooth}mat-icon[_ngcontent-%COMP%]{margin-right:10px}.scrollable-text[_ngcontent-%COMP%]{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.scrollable-text[_ngcontent-%COMP%]:hover{overflow:auto;white-space:normal;word-wrap:break-word}.notification-title[_ngcontent-%COMP%]{text-transform:capitalize;font-size:var(--mat-sys-headline-small-size);width:calc(100% - 34px)}.notification-path[_ngcontent-%COMP%]{font-size:var(--mat-sys-body-small-size);word-break:break-all;width:100%}.notification-text[_ngcontent-%COMP%]{font-size:var(--mat-sys-body-small-line-height);font-style:italic;width:100%;overflow:auto;white-space:normal;word-wrap:break-word}.notification-action-btn[_ngcontent-%COMP%]{background-color:var(--mat-sys-surface-container)}.notification-btn-container[_ngcontent-%COMP%]{display:inline-block;width:50%;height:48px;text-align:center}.menu-item-container[_ngcontent-%COMP%]{width:230px;overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch;height:calc(100% - 48px)}.notification-bottom-container[_ngcontent-%COMP%]{position:absolute;bottom:0;width:100%;background-color:var(--mat-sys-surface-container)}.actions-container[_ngcontent-%COMP%]{display:grid;grid-auto-flow:column}.actions-bottom-container[_ngcontent-%COMP%]{position:absolute;bottom:0;width:100%;height:48px}.action-button[_ngcontent-%COMP%]{border-radius:0;height:48px;border-bottom-right-radius:var(--mat-sidenav-container-shape, var(--mat-sys-corner-large))}.icon-alert-color[_ngcontent-%COMP%]{color:var(--kip-zone-alert-color)}.icon-warn-color[_ngcontent-%COMP%]{color:var(--kip-zone-warn-color)}.icon-alarm-color[_ngcontent-%COMP%]{color:var(--kip-zone-alarm-color)}.icon-emergency-color[_ngcontent-%COMP%]{color:var(--kip-zone-emergency-color)}.alarm-emergency-blink[_ngcontent-%COMP%]{animation:_ngcontent-%COMP%_blinking-emergency 1.5s infinite}@keyframes _ngcontent-%COMP%_blinking-emergency{0%{background-color:var(--kip-zone-alarm-color)}50%{background-color:transparent}to{background-color:var(--kip-zone-alarm-color)}}.warn[_ngcontent-%COMP%]{color:var(--kip-zone-warn-color)}.notification-divider[_ngcontent-%COMP%]{padding-top:10px}"],changeDetection:0})}return i})();var ni=["badgeButton"],Te=(()=>{class i{badgeButton=J.required("badgeButton");_notifications=a(T);notificationsInfo=_(this._notifications.observerNotificationsInfo());openNotificationMenu(){let t=new Event("openRightSidenav",{bubbles:!0,cancelable:!0});window.document.dispatchEvent(t)}onKeyDown(t){let e=t.key?.toLowerCase();(e==="enter"||e===" "||e==="spacebar")&&(t.preventDefault(),this.openNotificationMenu())}focus(){try{let t=this.badgeButton&&this.badgeButton(),e=t&&t.nativeElement;e&&typeof e.focus=="function"&&e.focus()}catch(t){}}static \u0275fac=function(e){return new(e||i)};static \u0275cmp=O({type:i,selectors:[["notification-badge"]],viewQuery:function(e,n){e&1&&W(n.badgeButton,ni,5),e&2&&Q()},decls:5,vars:7,consts:[["badgeButton",""],["mat-fab","","role","button","aria-label","Open notifications",1,"layout-action-btn",3,"click","keydown"],["aria-hidden","false","matBadgeSize","large","matBadgePosition","after","matBadgeOverlap","true",1,"icon-alert-color",3,"matBadgeHidden","matBadge"]],template:function(e,n){if(e&1){let r=v();o(0,"div")(1,"button",1,0),u("click",function(){return p(r),m(n.openNotificationMenu())})("keydown",function(h){return p(r),m(n.onKeyDown(h))}),o(3,"mat-icon",2),l(4,"notifications "),s()()()}e&2&&(f(),Ot("warn",n.notificationsInfo().isWarn)("alarm-emergency-blink",n.notificationsInfo().isAlarmEmergency),xt("aria-haspopup",!0),f(2),w("matBadgeHidden",!n.notificationsInfo().alarmCount)("matBadge",n.notificationsInfo().alarmCount))},dependencies:[D,Yt,A,L,rt,me],styles:["[_nghost-%COMP%]{display:block}.layout-action-btn[_ngcontent-%COMP%]{color:var(--mat-sys-on-primary);background-color:var(--mat-sys-primary)}.icon-alert-color[_ngcontent-%COMP%]{--mat-list-list-item-leading-icon-color: var(--kip-zone-alert-color)}.icon-warn-color[_ngcontent-%COMP%]{--mat-list-list-item-leading-icon-color: var(--kip-zone-warn-color)}.icon-alarm-color[_ngcontent-%COMP%]{--mat-list-list-item-leading-icon-color: var(--kip-zone-alarm-color)}.icon-emergency-color[_ngcontent-%COMP%]{--mat-list-list-item-leading-icon-color: var(--kip-zone-emergency-color)}.alarm-emergency-blink[_ngcontent-%COMP%]{animation:_ngcontent-%COMP%_blinking-emergency 1.5s infinite}@keyframes _ngcontent-%COMP%_blinking-emergency{0%{background-color:var(--kip-zone-alarm-color)}50%{background-color:var(--mat-sys-primary)}to{background-color:var(--kip-zone-alarm-color)}}.warn[_ngcontent-%COMP%]{color:var(--kip-zone-warn-color)}"]})}return i})();var Ee=(()=>{class i{overlayRef=null;overlay=a(Xt);injector=a(Ct);open(){if(this.overlayRef)return;this.overlayRef=this.overlay.create({hasBackdrop:!1,panelClass:"notification-overlay-panel",positionStrategy:this.overlay.position().global().bottom("20px").left("20px"),scrollStrategy:this.overlay.scrollStrategies.reposition()});let t=new Zt(Te,null,this.injector),e=this.overlayRef.attach(t);try{let n=e&&e.instance;if(n&&typeof n.focus=="function")n.focus();else{let r=this.overlayRef.overlayElement,d=r&&r.querySelector("button[mat-fab], .layout-action-btn");d&&typeof d.focus=="function"&&d.focus()}}catch(n){}}close(){try{this.overlayRef&&this.overlayRef.dispose()}catch(t){console.warn("[NotificationOverlayService] dispose failed",t)}finally{this.overlayRef=null}}toggle(t){t?this.open():this.close()}isOpen(){return!!this.overlayRef}ngOnDestroy(){try{this.overlayRef&&this.overlayRef.dispose()}catch(t){}finally{this.overlayRef=null}}static \u0275fac=function(e){return new(e||i)};static \u0275prov=S({token:i,factory:i.\u0275fac,providedIn:"root"})}return i})();function oi(i,c){if(i&1){let t=v();o(0,"button",12),u("click",function(){p(t);let n=g(2);return m(n.onActionItem("toggleFullScreen"))}),o(1,"span",10),l(2,"fullscreen"),s()()}}function ai(i,c){if(i&1){let t=v();o(0,"button",12),u("click",function(){p(t);let n=g(2);return m(n.onActionItem("toggleFullScreen"))}),o(1,"span",10),l(2,"close_fullscreen"),s()()}}function ri(i,c){if(i&1&&b(0,oi,3,0,"button",11)(1,ai,3,0,"button",11),i&2){let t=g();C(t.uiEvent.fullscreenStatus()?1:0)}}function si(i,c){if(i&1){let t=v();o(0,"button",14),u("click",function(){p(t);let n=g(2);return m(n.onActionItem("nightMode"))}),o(1,"span",10),l(2,"mode_night"),s()()}}function ci(i,c){if(i&1){let t=v();o(0,"button",14),u("click",function(){p(t);let n=g(2);return m(n.onActionItem("nightMode"))}),o(1,"span",10),l(2,"light_mode"),s()()}}function li(i,c){if(i&1&&b(0,si,3,0,"button",13)(1,ci,3,0,"button",13),i&2){let t=g();C(t.app.isNightMode()?1:0)}}function di(i,c){if(i&1){let t=v();o(0,"tile-large-icon",15,1),u("click",function(){let n=p(t).$index,r=g();return m(r.navigateToDashboard(n))}),s()}if(i&2){let t=c.$implicit;w("active",t.active)("svgIcon",t.svgIcon)("iconSize",t.iconSize)("label",t.label)}}var Re=(()=>{class i{actionsSidenav=At.required();_router=a(P);uiEvent=a(st);dashboard=a(N);app=a(ae);_settings=a(M);isAutoNightMode=_(this._settings.getAutoNightModeAsO(),{requireSync:!0});_initialDashboardMatch=(()=>{let e=this._router.parseUrl(this._router.url).root.children.primary,n=e?e.segments.map(r=>r.path):[];return n.length===0||n[0]==="dashboard"||n[0]==="chartplotter"})();isDashboardContext=_(this._router.events.pipe(q(t=>t instanceof tt),vt(null),j(()=>{let e=this._router.parseUrl(this._router.url).root.children.primary,n=e?e.segments.map(r=>r.path):[];return n.length===0||n[0]==="dashboard"||n[0]==="chartplotter"}),_t()),{initialValue:this._initialDashboardMatch});menuItems=Dt(()=>{let t=this.dashboard.dashboards(),e=this.dashboard.activeDashboard();return t.map((n,r)=>({id:Number(n.id),active:r===e,pinned:!1,svgIcon:n.icon||"dashboard-dashboard",iconSize:60,label:n.name||`Dashboard ${r+1}`}))});ngAfterViewInit(){this.uiEvent.addHotkeyListener((t,e)=>this.handleKeyDown(t,e),{ctrlKey:!0,shiftKey:!0,keys:["e","f","n"]})}ngOnDestroy(){this.uiEvent.removeHotkeyListener(this.handleKeyDown.bind(this))}handleKeyDown(t,e){switch(t){case"e":this.onActionItem("layout");break;case"f":this.onActionItem("toggleFullScreen");break;case"n":this.onActionItem("nightMode");break;default:break}}onActionItem(t){switch(this.actionsSidenav().close(),t){case"toggleFullScreen":this.uiEvent.toggleFullScreen();break;case"settings":this._router.navigate(["/settings"]);break;case"layout":this.dashboard.setStaticDashboard(!1);break;case"nightMode":this.app.isNightMode.set(!this.app.isNightMode()),this.app.toggleDayNightMode();break;default:break}}navigateToDashboard(t){this.actionsSidenav().close(),this._router.navigate([`dashboard/${t}`])}static \u0275fac=function(e){return new(e||i)};static \u0275cmp=O({type:i,selectors:[["menu-actions"]],inputs:{actionsSidenav:[1,"actionsSidenav"]},decls:14,vars:3,consts:[["tileContainer",""],["tile",""],[1,"actions-container"],[1,"actions-top-container"],["mat-flat-button","","tabindex","0",1,"action-button","home-btn",3,"click"],["height","24px","width","24px","svgIcon","kip","aria-hidden","false",1,"home-icon",2,"height","24px","width","24px"],[1,"menu-item-container"],[3,"active","svgIcon","iconSize","label"],[1,"actions-bottom-container"],["mat-flat-button","",1,"action-button",2,"width","135px",3,"click","disabled"],[1,"material-icons"],["mat-flat-button","","tabindex","0",1,"action-button"],["mat-flat-button","","tabindex","0",1,"action-button",3,"click"],["mat-flat-button","",1,"action-button"],["mat-flat-button","",1,"action-button",3,"click"],[3,"click","active","svgIcon","iconSize","label"]],template:function(e,n){if(e&1){let r=v();o(0,"div",2)(1,"div",3),b(2,ri,2,1),b(3,li,2,1),o(4,"button",4),u("click",function(){return p(r),m(n.onActionItem("settings"))}),x(5,"mat-icon",5),s()(),o(6,"div",6,0),U(8,di,2,4,"tile-large-icon",7,G),s(),o(10,"div",8)(11,"button",9),u("click",function(){return p(r),m(n.onActionItem("layout"))}),o(12,"span",10),l(13,"lock_open"),s()()()()}e&2&&(f(2),C(n.uiEvent.fullscreenSupported()?2:-1),f(),C(n.isAutoNightMode()?-1:3),f(5),V(n.menuItems()),f(3),w("disabled",!n.isDashboardContext()))},dependencies:[A,L,D,et,be],styles:[".actions-container[_ngcontent-%COMP%]{display:flex;flex-direction:column;height:100%;width:135px}.actions-top-container[_ngcontent-%COMP%]{display:flex;flex-direction:row;flex-wrap:wrap;width:100%}.actions-bottom-container[_ngcontent-%COMP%]{height:48px;width:100%}.action-button[_ngcontent-%COMP%]{border-radius:0;height:48px;flex:1 1 48px;min-height:0;overflow:hidden;border:1px solid var(--mat-sidenav-container-background-color, var(--mat-sys-surface))}.home-btn[_ngcontent-%COMP%]{--mat-icon-button-icon-size: 24px;--mat-button-filled-icon-offset: 0px;--mat-button-filled-icon-spacing: 0px;--mat-button-filled-label-text-size: 0px}.home-icon[_ngcontent-%COMP%] .mat-mdc-unelevated-button[_ngcontent-%COMP%] > .mat-icon[_ngcontent-%COMP%]{font-size:0px}.full-size[_ngcontent-%COMP%]{width:135px}.menu-item-container[_ngcontent-%COMP%]{display:flex;flex-direction:column;flex-wrap:nowrap;padding:3px 1px;gap:5px;width:135px;overflow-y:auto;scrollbar-width:none;overflow-x:hidden;scroll-behavior:smooth;flex:1 1 auto;min-height:0}"]})}return i})();var Ue=(()=>{class i{http=a(Lt);settings=a(M);dashboard=a(N);data=a(B);KIP_UUID=this.settings.KipUUID;ACTIVE_SCREEN_PATH=`self.displays.${this.KIP_UUID}.screenIndex`;CHANGE_SCREEN_PATH=`self.displays.${this.KIP_UUID}.activeScreen`;displayName=_(this.settings.getInstanceNameAsO());isRemoteControl=_(this.settings.getIsRemoteControlAsO());remoteScreenPosition=_(this.data.subscribePath(this.ACTIVE_SCREEN_PATH,"default"));changeDashboardTo=_(this.data.subscribePath(this.CHANGE_SCREEN_PATH,"default"));PLUGIN_URL=this.settings.signalkUrl.url+"/plugins/kip";previousIsRemoteControl=!1;constructor(){this.setActiveDashboardOnRemote(this.KIP_UUID,null),this.setScreensOnRemote(this.KIP_UUID,null),this.clearActiveScreenOnRemote(this.KIP_UUID,null),console.log("[Remote Dashboards] Cleaning paths on server"),y(()=>{let t=this.isRemoteControl(),e=this.displayName();z(()=>{if(!t&&!this.previousIsRemoteControl)return;this.previousIsRemoteControl=t;let n;t?(n=this.getScreensPayload(this.dashboard.dashboards()),this.dashboard.activeDashboard()!==null&&this.setActiveDashboardOnRemote(this.KIP_UUID,this.dashboard.activeDashboard()).catch(d=>{console.error("[Remote Dashboards] Error sharing active dashboard on Remote Control activation:",d)})):(n=null,this.clearActiveDashboardOnServer()),this.shareScreens(n)})}),y(()=>{let t=this.dashboard.dashboards();z(()=>{if(!this.isRemoteControl())return;let e=this.getScreensPayload(t);this.shareScreens(e)})}),y(()=>{let t=this.dashboard.activeDashboard();z(()=>{this.isRemoteControl()&&t!==null&&this.setActiveDashboardOnRemote(this.KIP_UUID,t).then(()=>{console.log(`[Remote Dashboards] Sent new dashboard highlight index ${t} to server.`)}).catch(e=>{console.error("[Remote Dashboards] Error sharing active dashboard:",e)})})}),y(()=>{let t=this.changeDashboardTo();z(()=>{if(!this.isRemoteControl()||t.data.value==null)return;let e=Number(t.data.value);!isNaN(e)&&e>=0&&e<this.dashboard.dashboards().length&&this.dashboard.activeDashboard()!==e&&(this.dashboard.navigateTo(e),console.log(`[Remote Dashboards] Executed remote request to change active dashboard to: ${e}`))})})}clearActiveDashboardOnServer(){this.setActiveDashboardOnRemote(this.KIP_UUID,null).then(()=>{console.log("[Remote Dashboards] Disabled: Cleared active dashboard on server")}).catch(t=>{console.error("[Remote Dashboards] Error clearing active dashboard on server while disabling the feature:",t)})}getScreensPayload(t){let e=this.displayName(),n=t.map(h=>{var k=h,{configuration:r}=k,d=ht(k,["configuration"]);return d});return{displayName:e,screens:n}}shareScreens(t){this.setScreensOnRemote(this.KIP_UUID,t).then(()=>{console.log("[Remote Dashboards] Sending dashboard configurations to server.")}).catch(e=>{console.error("[Remote Dashboards] Error sharing screen configuration:",e)})}setActiveDashboardOnRemote(t,e){return R(this,null,function*(){let n=e===null?null:{screenIdx:e};return $(this.http.put(`${this.PLUGIN_URL}/displays/${t}/screenIndex`,n))})}setScreensOnRemote(t,e){return R(this,null,function*(){let n=e===null?null:ut({},e);return $(this.http.put(`${this.PLUGIN_URL}/displays/${t}`,n))})}clearActiveScreenOnRemote(t,e){return R(this,null,function*(){let n=e===null?null:{screenIdx:e};return $(this.http.put(`${this.PLUGIN_URL}/displays/${t}/activeScreen`,n))})}static \u0275fac=function(e){return new(e||i)};static \u0275prov=S({token:i,factory:i.\u0275fac,providedIn:"root"})}return i})();var pi=["upgradeMessages"];function mi(i,c){if(i&1&&(o(0,"li"),l(1),s()),i&2){let t=c.$implicit;f(),H(t)}}function fi(i,c){if(i&1&&(o(0,"ul",11,1),U(2,mi,2,1,"li",null,G),s()),i&2){let t=g(2);f(2),V(t.upgrade.messages())}}function ui(i,c){if(i&1&&(o(0,"div",7)(1,"div",8),x(2,"mat-progress-spinner",9),o(3,"div",10)(4,"h3"),l(5,"Upgrading Configuration\u2026"),s(),o(6,"p"),l(7,"Please wait a moment while we migrate your dashboards and widgets."),s(),b(8,fi,4,0,"ul",11),s()()()),i&2){let t=g();f(8),C(t.upgrade.messages().length?8:-1)}}var Ve=(()=>{class i{_deltaService=a(K);_connectionStateMachine=a(F);_dashboard=a(N);_remoteControl=a(Ue);toast=a(ee);_notifications=a(T);_uiEvent=a(st);_dialog=a(ue);appSettingsService=a(M);authenticationService=a(I);_dataSet=a(at);_responsive=a(Wt);_destroyRef=a(St);_notificationOverlay=a(Ee);_router=a(P);upgrade=a(fe);upgradeMessagesRef=J("upgradeMessages");_upgradeShown=!1;actionsSidenavOpened=mt(!1);notificationsSidenavOpened=mt(!1);notificationsInfo=_(this._notifications.observerNotificationsInfo());dashboardVisible=yt(!1);isPhonePortrait;scheduledOpen=null;OPEN_DELAY_MS=300;_swipeLeftHandler=()=>this.onSwipeLeft();_swipeRightHandler=()=>this.onSwipeRight();_hotkeyHandler=t=>this.handleKeyDown(t);constructor(){y(()=>{if(this.appSettingsService.configUpgrade()){let t=this.appSettingsService.getConfigVersion();t===11&&this.upgrade.runUpgrade(t),t||this._upgradeShown||(this._upgradeShown=!0,this._dialog.openFrameDialog({title:"Upgrade Instructions",component:"upgrade-config"},!0).pipe(Z(this._destroyRef)).subscribe())}}),y(()=>{let t=this.upgrade.messages();if(this.upgrade.upgrading()&&t.length&&this.upgradeMessagesRef()){let e=this.upgradeMessagesRef().nativeElement;e.scrollTop=e.scrollHeight}});try{this.dashboardVisible.set(this.isUrlDashboard(this._router.url))}catch(t){}this._router.events.pipe(q(t=>t instanceof tt),Z(this._destroyRef)).subscribe(t=>{try{this.dashboardVisible.set(this.isUrlDashboard(t.urlAfterRedirects||t.url))}catch(e){}}),y(()=>{let t=this.dashboardVisible()&&this._dashboard.isDashboardStatic()&&this.notificationsInfo().alarmCount>0;if(this.notificationsSidenavOpened()){this.scheduledOpen&&(clearTimeout(this.scheduledOpen),this.scheduledOpen=null);try{this._notificationOverlay.close()}catch(n){}return}if(t){if(this.scheduledOpen)return;this.scheduledOpen=window.setTimeout(()=>{this.scheduledOpen=null;try{this._notificationOverlay.open()}catch(n){}},this.OPEN_DELAY_MS)}else{this.scheduledOpen&&(clearTimeout(this.scheduledOpen),this.scheduledOpen=null);try{this._notificationOverlay.close()}catch(n){}}}),y(()=>{if(this.notificationsSidenavOpened())try{this._notificationOverlay.close()}catch(e){}}),this.isPhonePortrait=_(this._responsive.observe(Qt.HandsetPortrait)),this._connectionStateMachine.status$.pipe(Z(this._destroyRef)).subscribe(t=>this.displayConnectionsStatusNotification(t))}isUrlDashboard(t){if(!t)return!1;let e=t.split("?")[0].replace(/\/+$/,"");return e==="/"||e==="/dashboard"||/^\/dashboard(\/\d+)?$/.test(e)||e==="/chartplotter"||/^\/chartplotter(\/\d+)?$/.test(e)}ngOnInit(){this._uiEvent.addGestureListeners(this._swipeLeftHandler,this._swipeRightHandler)}ngAfterViewInit(){this._uiEvent.addHotkeyListener(this._hotkeyHandler,{ctrlKey:!0,keys:["arrowright","arrowleft"]})}handleKeyDown(t){switch(t){case"arrowright":this.onSwipeRight();break;case"arrowleft":this.onSwipeLeft();break;case"escape":this.backdropClicked();break}}escapeKeyPressed(t){t=t.toLowerCase(),t==="escape"&&this.backdropClicked()}displayConnectionsStatusNotification(t){let e=t.message;switch(t.operation){case 0:this.toast.show(e,5e3,!0);break;case 1:case 2:break;case 3:this.toast.show(e,3e3,!1,"warn");break;case 4:this.toast.show(e,3e3,!0,"info");break;case 5:this.toast.show(e,0,!1);break;default:console.error("[AppComponent] Unknown operation code:",t.operation),this.toast.show(`Unknown connection status: ${t.state}`,0,!1,"error")}}onSwipeRight(){this._dashboard.isDashboardStatic()&&!this._uiEvent.isDragging()&&(this.isPhonePortrait().matches?(this.actionsSidenavOpened.set(!1),this.notificationsSidenavOpened.set(!0)):(this.actionsSidenavOpened.set(!1),this.notificationsSidenavOpened.update(t=>!t)))}backdropClicked(){this.notificationsSidenavOpened.update(t=>t?!t:!1),this.actionsSidenavOpened.update(t=>t?!t:!1)}onSwipeLeft(){this._dashboard.isDashboardStatic()&&!this._uiEvent.isDragging()&&(this.isPhonePortrait().matches?(this.notificationsSidenavOpened.set(!1),this.actionsSidenavOpened.set(!0)):(this.notificationsSidenavOpened.set(!1),this.actionsSidenavOpened.update(t=>!t)))}ngOnDestroy(){this._uiEvent.removeGestureListeners(this._swipeLeftHandler,this._swipeRightHandler),this._uiEvent.removeHotkeyListener(this._hotkeyHandler),this.scheduledOpen&&(clearTimeout(this.scheduledOpen),this.scheduledOpen=null)}static \u0275fac=function(e){return new(e||i)};static \u0275cmp=O({type:i,selectors:[["app-root"]],viewQuery:function(e,n){e&1&&W(n.upgradeMessagesRef,pi,5),e&2&&Q()},inputs:{actionsSidenavOpened:[1,"actionsSidenavOpened"],notificationsSidenavOpened:[1,"notificationsSidenavOpened"]},outputs:{actionsSidenavOpened:"actionsSidenavOpenedChange",notificationsSidenavOpened:"notificationsSidenavOpenedChange"},decls:9,vars:6,consts:[["actionsSidenav",""],["upgradeMessages",""],["kipGestures","",1,"sidenav-container",3,"swipeleft","swiperight","backdropClick","mode","bridgeUiEvents"],["mode","over","position","start","autoFocus","true","disableClose","",1,"sidenav-notifications",3,"openedChange","keydown","opened"],["mode","over","position","end","autoFocus","true","disableClose","",1,"sidenav-actions",3,"openedChange","keydown","opened"],[3,"actionsSidenav"],[1,"router-outlet-container"],["role","alert","aria-live","polite",1,"config-upgrade-overlay"],[1,"config-upgrade-card"],["diameter","46","mode","indeterminate"],[1,"config-upgrade-text"],[1,"config-upgrade-messages"]],template:function(e,n){if(e&1){let r=v();o(0,"mat-sidenav-container",2),u("swipeleft",function(){return p(r),m(n.onSwipeLeft())})("swiperight",function(){return p(r),m(n.onSwipeRight())})("backdropClick",function(){return p(r),m(n.backdropClicked())}),o(1,"mat-sidenav",3),pt("openedChange",function(h){return p(r),dt(n.notificationsSidenavOpened,h)||(n.notificationsSidenavOpened=h),m(h)}),u("keydown",function(h){return p(r),m(n.escapeKeyPressed(h.key))}),x(2,"menu-notifications"),s(),o(3,"mat-sidenav",4,0),pt("openedChange",function(h){return p(r),dt(n.actionsSidenavOpened,h)||(n.actionsSidenavOpened=h),m(h)}),u("keydown",function(h){return p(r),m(n.escapeKeyPressed(h.key))}),x(5,"menu-actions",5),s(),o(6,"mat-sidenav-content"),x(7,"router-outlet",6),s()(),b(8,ui,9,1,"div",7)}if(e&2){let r=kt(4);w("mode","horizontal")("bridgeUiEvents",!0),f(),lt("opened",n.notificationsSidenavOpened),f(2),lt("opened",n.actionsSidenavOpened),f(2),w("actionsSidenav",r),f(3),C(n.upgrade.upgrading()?8:-1)}},dependencies:[Ae,Re,D,pe,A,Gt,$t,Me,ye,we,Se,Et,te,ke,xe],styles:[".sidenav-container[_ngcontent-%COMP%]{height:100%;width:100%}mat-sidenav-content[_ngcontent-%COMP%]{overflow-y:hidden;overflow-x:hidden}mat-sidenav.sidenav-notifications[_ngcontent-%COMP%]{width:230px;z-index:1200}mat-sidenav.sidenav-actions[_ngcontent-%COMP%]{width:135px;z-index:1200;overflow-x:hidden}.config-upgrade-overlay[_ngcontent-%COMP%]{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:#00000073;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);z-index:2000;pointer-events:all}.config-upgrade-card[_ngcontent-%COMP%]{display:flex;flex-direction:row;align-items:center;gap:18px;padding:20px 24px;width:clamp(300px,60vw,640px);background:var(--kip-background, #1f1f1f);border:1px solid rgba(255,255,255,.18);border-radius:14px;color:var(--kip-contrast-color, #fff);box-shadow:0 6px 22px #0000008c}.config-upgrade-text[_ngcontent-%COMP%] h3[_ngcontent-%COMP%]{margin:0 0 4px;font-size:1.05rem;font-weight:600;letter-spacing:.5px}.config-upgrade-text[_ngcontent-%COMP%] p[_ngcontent-%COMP%]{margin:0 0 10px;font-size:.8rem;opacity:.85}.config-upgrade-messages[_ngcontent-%COMP%]{list-style:disc;margin:0;padding-left:18px;max-height:180px;font-size:.68rem;line-height:1.25;scrollbar-width:thin;overflow-y:auto}.config-upgrade-messages[_ngcontent-%COMP%] li[_ngcontent-%COMP%]{margin-bottom:2px}"]})}return i})();var Fe=11,Le="connectionConfig",Ke=(()=>{class i{config;isLoggedIn=null;loggedInSubscription=null;connection=a(Y);auth=a(I);connectionStateMachine=a(F);router=a(P);delta=a(K);data=a(B);storage=a(X);internetReachability=a(Ce);constructor(){this.loggedInSubscription=this.auth.isLoggedIn$.subscribe(t=>{this.isLoggedIn=t})}initNetworkServices(){return R(this,null,function*(){this.loadLocalStorageConfig(),this.preloadFonts(),this.internetReachability.start();try{this.config?.signalKUrl!==void 0&&this.config.signalKUrl!==null&&(yield this.connection.initializeConnection({url:this.config.signalKUrl,new:!1},this.config.proxyEnabled,this.config.signalKSubscribeAll)),!this.isLoggedIn&&this.config?.signalKUrl&&this.config?.useSharedConfig&&this.config?.loginName&&this.config?.loginPassword&&(yield this.login()),this.isLoggedIn&&this.config?.useSharedConfig&&(this.storage.activeConfigFileVersion=Fe,this.storage.sharedConfigName=this.config.sharedConfigName,yield this.storage.getConfig("user",this.config.sharedConfigName,Fe,!0)),!this.isLoggedIn&&this.config?.signalKUrl&&this.config?.useSharedConfig&&this.router.navigate(["/login"])}catch(t){return t.status===0?console.warn("[AppInit Network Service] Initialization failed. Network error. Redirecting to settings page."):t.status===401?console.warn("[AppInit Network Service] Initialization failed. Unauthorized access. Redirecting to login page."):console.warn("[AppInit Network Service] Initialization failed. Error: ",JSON.stringify(t)),Promise.reject("[AppInit Network Service] Startup completed with connection issue.")}finally{console.log("[AppInit Network Service] Initialization completed"),this.connectionStateMachine.enableWebSocketMode(),this.connectionStateMachine.isHTTPConnected()&&(console.log("[AppInit Network Service] Starting WebSocket connection after initialization"),this.connectionStateMachine.startWebSocketConnection())}})}login(){return R(this,null,function*(){if(!this.isLoggedIn&&this.config.useSharedConfig&&this.config.loginName&&this.config.loginPassword)try{yield this.auth.login({usr:this.config.loginName,pwd:this.config.loginPassword})}catch(t){throw t.status===0?this.router.navigate(["/settings"]):t.status===401&&this.router.navigate(["/login"]),t}})}setLocalStorageConfig(){localStorage.setItem(Le,JSON.stringify(this.config))}loadLocalStorageConfig(){this.config=JSON.parse(localStorage.getItem(Le)),this.config?this.config.signalKUrl||(this.config.signalKUrl=window.location.origin,this.setLocalStorageConfig(),console.log(`[AppInit Network Service] Config found with no server URL. Setting Auto-Discovery URL: ${this.config.signalKUrl}`)):(this.config=Ut,this.config.signalKUrl=window.location.origin,console.log(`[AppInit Network Service] Connection Configuration not found. Creating configuration using Auto-Discovery URL: ${this.config.signalKUrl}`),this.setLocalStorageConfig()),this.config.configVersion==9&&(this.config.configVersion=10,this.setLocalStorageConfig(),console.log("[AppInit Network Service] Upgrading Connection version from 9 to 10")),this.config.configVersion==10&&(this.config.configVersion=11,this.setLocalStorageConfig(),console.log("[AppInit Network Service] Upgrading Connection version from 10 to 11")),this.config.configVersion==11&&(this.config.configVersion=12,this.setLocalStorageConfig(),console.log("[AppInit Network Service] Upgrading Connection version from 11 to 12"))}preloadFonts(){let t=[{family:"Roboto",src:"url(./assets/google-fonts/KFOlCnqEu92Fr1MmSU5fChc4AMP6lbBP.woff2)",options:{weight:"300",style:"normal"}},{family:"Roboto",src:"url(./assets/google-fonts/KFOlCnqEu92Fr1MmSU5fBBc4AMP6lQ.woff2)",options:{weight:"300",style:"normal"}},{family:"Roboto",src:"url(./assets/google-fonts/KFOmCnqEu92Fr1Mu7GxKKTU1Kvnz.woff2)",options:{weight:"400",style:"normal"}},{family:"Roboto",src:"url(./assets/google-fonts/KFOmCnqEu92Fr1Mu4mxKKTU1Kg.woff2)",options:{weight:"400",style:"normal"}},{family:"Roboto",src:"url(./assets/google-fonts/KFOlCnqEu92Fr1MmEU9fChc4AMP6lbBP.woff2)",options:{weight:"500",style:"normal"}},{family:"Roboto",src:"url(./assets/google-fonts/KFOlCnqEu92Fr1MmEU9fBBc4AMP6lQ.woff2)",options:{weight:"500",style:"normal"}}];for(let{family:e,src:n,options:r}of t){let d=new FontFace(e,n,r);d.load().then(()=>document.fonts.add(d)).catch(h=>console.log(`[AppInit Network Service] Error loading fonts: ${h}`))}}ngOnDestroy(){this.loggedInSubscription?.unsubscribe()}static \u0275fac=function(e){return new(e||i)};static \u0275prov=S({token:i,factory:i.\u0275fac})}return i})();var Be=(()=>{class i{auth=a(I);authToken=null;authTokenSubscription=null;constructor(){this.authTokenSubscription=this.auth.authToken$.subscribe(t=>{this.authToken=t})}intercept(t,e){let n=t.clone();return this.authToken&&(n=t.clone({headers:t.headers.set("authorization","JWT "+this.authToken.token)})),e.handle(n)}ngOnDestroy(){this.authTokenSubscription?.unsubscribe()}static \u0275fac=function(e){return new(e||i)};static \u0275prov=S({token:i,factory:i.\u0275fac})}return i})();var ze=(()=>{class i extends it{_createContainer(){let t=document.createElement("div");t.classList.add("cdk-overlay-container"),(document.getElementById("app-filter-wrapper")||document.querySelector("app-root")||document.body).appendChild(t),this._containerElement=t}static \u0275fac=(()=>{let t;return function(n){return(t||(t=wt(i)))(n||i)}})();static \u0275prov=S({token:i,factory:i.\u0275fac})}return i})();Pe.production&&void 0;zt(Ve,{providers:[Tt(),bt(Ht),{provide:Ft,useClass:Be,multi:!0},{provide:ie,useValue:{hasBackdrop:!0,disableClose:!1,autoFocus:"first-tabbable",delayFocusTrap:!0,backdropClass:"dialogBackdrop"}},{provide:ne,useValue:{appearance:"outline",floatLabel:"always",subscriptSizing:"dynamic"}},{provide:se,useValue:{showDelay:1500,hideDelay:0}},I,B,Y,K,F,at,N,re,M,T,de,X,Oe(),Kt(Bt()),jt(Ie,qt()),{provide:it,useClass:ze},Mt(()=>new Ke().initNetworkServices().then(()=>{}).catch(()=>{}))]});
|
package/rm-npmjs-beta.sh
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
if ! command -v jq >/dev/null 2>&1; then
|
|
5
|
-
echo "Error: jq is required (brew install jq)" >&2
|
|
6
|
-
exit 1
|
|
7
|
-
fi
|
|
8
|
-
|
|
9
|
-
if [[ $# -lt 1 ]]; then
|
|
10
|
-
echo "Usage: $0 <package-name> [deprecation-message] [channel]" >&2
|
|
11
|
-
echo "Example: $0 mypkg \"Deprecated: use >=1.2.0\" beta" >&2
|
|
12
|
-
exit 1
|
|
13
|
-
fi
|
|
14
|
-
|
|
15
|
-
PKG="$1"; shift || true
|
|
16
|
-
MSG="${1-Deprecated prerelease: please install a stable version}"; [[ $# -gt 0 ]] && shift || true
|
|
17
|
-
CHANNEL="${1-beta}"
|
|
18
|
-
|
|
19
|
-
# Get all published versions
|
|
20
|
-
versions_json=$(npm view "$PKG" versions --json 2>/dev/null || true)
|
|
21
|
-
if [[ -z "${versions_json:-}" || "${versions_json}" == "null" ]]; then
|
|
22
|
-
echo "No versions found or package not accessible: $PKG" >&2
|
|
23
|
-
exit 1
|
|
24
|
-
fi
|
|
25
|
-
|
|
26
|
-
# Select prereleases that contain -<channel>, e.g. -beta
|
|
27
|
-
VERSIONS=$(printf '%s' "$versions_json" \
|
|
28
|
-
| jq -r --arg ch "$CHANNEL" 'if type=="array" then . else [.] end | .[] | select(test("-(\($ch))","i"))')
|
|
29
|
-
|
|
30
|
-
if [[ -z "${VERSIONS:-}" ]]; then
|
|
31
|
-
echo "No $CHANNEL versions found for $PKG."
|
|
32
|
-
exit 0
|
|
33
|
-
fi
|
|
34
|
-
|
|
35
|
-
echo "Found $CHANNEL versions for $PKG:"
|
|
36
|
-
printf ' %s\n' $VERSIONS
|
|
37
|
-
|
|
38
|
-
read -r -p "Deprecate these versions with message: \"$MSG\"? [y/N] " ans
|
|
39
|
-
case "$ans" in
|
|
40
|
-
y|Y|yes|YES) ;;
|
|
41
|
-
*) echo "Aborted."; exit 0 ;;
|
|
42
|
-
esac
|
|
43
|
-
|
|
44
|
-
set -x
|
|
45
|
-
for v in $VERSIONS; do
|
|
46
|
-
npm deprecate "$PKG@$v" "$MSG"
|
|
47
|
-
done
|
|
48
|
-
set +x
|
|
49
|
-
|
|
50
|
-
echo "Done."
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
# <%= title %> Widget
|
|
2
|
-
|
|
3
|
-
Generated by KIP create-host2-widget schematic.
|
|
4
|
-
|
|
5
|
-
Selector: `<%= selector %>`
|
|
6
|
-
|
|
7
|
-
## Default Config
|
|
8
|
-
```ts
|
|
9
|
-
<%= className %>.DEFAULT_CONFIG
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
## Path(s)
|
|
13
|
-
Primary path key: `signalKPath` (<%= pathType %>) unit: `<%= convertUnitTo %>` sampleTime: <%= sampleTime %>ms
|
|
14
|
-
|
|
15
|
-
<% if (zonesSupport) { %>
|
|
16
|
-
## Zones Support
|
|
17
|
-
This widget was generated with Zones support enabled. Zones scaffolding includes two integration points:
|
|
18
|
-
|
|
19
|
-
### Zones definitions
|
|
20
|
-
- `WidgetMetadataDirective` is imported and injected.
|
|
21
|
-
- A `this.metadata.zones()` signal placeholder is included to surface zones metadata information.
|
|
22
|
-
- A `highlights` computed signal that generates KIP widget compatible zones array is included. Modify to your needs.
|
|
23
|
-
- metadata.observe('signalKPath') call is added automating zone monitoring for the configured path.
|
|
24
|
-
- Remove or adapt these parts if your widget will not visualize Signal K Zones.
|
|
25
|
-
|
|
26
|
-
### Path Data State
|
|
27
|
-
The path data may also include a state (zone classification) emitted by the server. That state corresponds to the zone whose range the value falls into. This state is sent whether or not the widget enables zones metadata support. See `path.data.state` in the observed path payload.
|
|
28
|
-
<% } %>
|
|
29
|
-
|
|
30
|
-
## Core Concepts
|
|
31
|
-
This widget follows the Host2 architecture. The runtime merges persisted config
|
|
32
|
-
(Signal K server or local storage) with `DEFAULT_CONFIG` and feeds it through the
|
|
33
|
-
injected directives. All you need to do is set the DEFAULT_CONFIG and the directive
|
|
34
|
-
will handle merging and persisting user settings automatically.
|
|
35
|
-
|
|
36
|
-
### Adding More Paths
|
|
37
|
-
Add additional keys under `DEFAULT_CONFIG.paths` (each must include `pathType` and
|
|
38
|
-
typical options). Then register observers within the second `effect()` in the
|
|
39
|
-
component (group them in a single `untracked` block for performance).
|
|
40
|
-
|
|
41
|
-
### Optional vs Required Paths
|
|
42
|
-
Mark non-critical (optional) paths with `pathRequired: false` to allow empty configuration
|
|
43
|
-
without errors. Always guard before observing.
|
|
44
|
-
|
|
45
|
-
### Units & Conversion
|
|
46
|
-
Specify `convertUnitTo` for number paths in `DEFAULT_CONFIG` to let KIP auto-convert
|
|
47
|
-
via `UnitsService`. Do not manually convert unless you have a special case.
|
|
48
|
-
UnitsService supports most formatting and conversions. If one is missing, add the
|
|
49
|
-
unit to the service instead of hardcoding conversion math in a widget.
|
|
50
|
-
|
|
51
|
-
## Available Angular Services (Commonly Injected)
|
|
52
|
-
| Service | Purpose | Typical Use |
|
|
53
|
-
|---------|---------|-------------|
|
|
54
|
-
| `UnitsService` | Central unit conversion & formatting | Convert or format values when you must post-process derived numbers. |
|
|
55
|
-
| `DataSetService` | Historical / trend datasets (chart widgets) | Create/update/remove datasets for charts or statistics. |
|
|
56
|
-
| `NotificationsService` | User-visible toast/Snackbar/info/warn/error | Present message to user |
|
|
57
|
-
| `SignalKRequestsService` | PUT/Request operations to server | Implement interactive controls and set Signal K path value (e.g., set heading). |
|
|
58
|
-
| `DataService` | Low-level Signal K value + metadata access | Rarely needed directly; `WidgetStreamsDirective` wraps it. |
|
|
59
|
-
| `CanvasService` | High-DPI sizing, font readiness, offscreen/static layer caching, draw helpers | Register & auto-resize canvases, cache static bitmaps (titles/backgrounds), release GPU memory on destroy. |
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
Inject with Angular `inject(ServiceClass)` or constructor injection (signals style recommended in Host2 files).
|
|
63
|
-
|
|
64
|
-
## Directives Supplied to Widgets
|
|
65
|
-
| Directive | Role |
|
|
66
|
-
|-----------|------|
|
|
67
|
-
| `WidgetRuntimeDirective` | Provides merged config (`options()`), id, sizing. |
|
|
68
|
-
| `WidgetStreamsDirective` | Observe path data streams with sampling, unit conversion, timeout logic. |
|
|
69
|
-
| `WidgetMetadataDirective` | Access zones / metadata (call `observe` for updates, `zones()` for current list). |
|
|
70
|
-
|
|
71
|
-
## Useful Utility Functions
|
|
72
|
-
| Utility | Source | Purpose |
|
|
73
|
-
|---------|--------|---------|
|
|
74
|
-
| `getColors(themeKey, theme)` | `core/utils/themeColors.utils` | Returns palette variants (normal/dim/bright) for configured color key. |
|
|
75
|
-
| `svg-animate.util` helpers | `widgets/utils/svg-animate.util` | Smooth rAF animations (rotation, angle transitions, sectors). |
|
|
76
|
-
| `zones-highlight.utils` | `core/utils/zones-highlight.utils` | Build highlight arrays for gauge / chart overlays from zones metadata. |
|
|
77
|
-
|
|
78
|
-
## Integrating Zones (If Enabled)
|
|
79
|
-
When zones support is active, metadata-driven highlights can be computed and surfaced to the template. Use colors derived from the active theme for contrast and accessibility. Keep highlight computation outside change detection hot paths (use signals/computed as shown).
|
|
80
|
-
|
|
81
|
-
## Performance Tips
|
|
82
|
-
1. Batch all `streams.observe` calls in one `untracked` block inside a single `effect`.
|
|
83
|
-
2. Store transient UI state in signals, never mutate the merged config object.
|
|
84
|
-
3. Use SVG animation helpers for high-frequency visual updates; they run outside Angular via `NgZone`.
|
|
85
|
-
4. Keep sample times reasonable (>= 250ms) to avoid widget churn.
|
|
86
|
-
|
|
87
|
-
## Error / Edge Handling
|
|
88
|
-
| Scenario | Recommendation |
|
|
89
|
-
|----------|---------------|
|
|
90
|
-
| Missing path | Guard before observing; show placeholder or dim widget. |
|
|
91
|
-
| Stale data (timeout) | Enable `enableTimeout` and set `dataTimeout` seconds in config. UI can style on timeout state. |
|
|
92
|
-
| Unit mismatch | Rely on `convertUnitTo`; avoid manual conversions. |
|
|
93
|
-
| Zones absent | Treat highlights array as empty; avoid errors by null-guarding metadata. |
|
|
94
|
-
|
|
95
|
-
## Icon & Description
|
|
96
|
-
Update the icon and description in the `widget.service.ts` entry inserted by the schematic. Replace `placeholder-icon` and the long TODO description. Ensure the SVG symbol id exists in `src/assets/svg/icons.svg` or add a new symbol there.
|
|
97
|
-
|
|
98
|
-
## Testing
|
|
99
|
-
If `--add-spec` was used, a basic spec file is generated. Extend tests to:
|
|
100
|
-
1. Mock `WidgetStreamsDirective` emissions to verify signal updates.
|
|
101
|
-
2. Validate conditional rendering with and without path values.
|
|
102
|
-
3. (Optional) Provide fake zones metadata to exercise highlight logic.
|
|
103
|
-
|
|
104
|
-
## Deployment Checklist
|
|
105
|
-
- [ ] Replace placeholder icon & description
|
|
106
|
-
- [ ] Adjust DEFAULT_CONFIG displayName
|
|
107
|
-
- [ ] Remove unused TODO comments
|
|
108
|
-
- [ ] Remove all console.log() calls
|
|
109
|
-
- [ ] Add tests for edge cases (timeouts, no path, zones absent)
|
package/tools/schematics/create-host2-widget/files/spec/widget-__name@dasherize__.component.spec.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { Component } from '@angular/core';
|
|
2
|
-
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
|
3
|
-
import { signal } from '@angular/core';
|
|
4
|
-
import { <%= className %> } from './<%= selector %>.component';
|
|
5
|
-
import { WidgetRuntimeDirective } from '../../core/directives/widget-runtime.directive';
|
|
6
|
-
import { WidgetStreamsDirective } from '../../core/directives/widget-streams.directive';
|
|
7
|
-
import { WidgetMetadataDirective } from '../../core/directives/widget-metadata.directive';
|
|
8
|
-
|
|
9
|
-
@Component({
|
|
10
|
-
template: `<%= '<' + selector %> [id]="id()" [type]="type()" [theme]="theme()" />`,
|
|
11
|
-
imports: [<%= className %>, WidgetRuntimeDirective, WidgetStreamsDirective, WidgetMetadataDirective]
|
|
12
|
-
})
|
|
13
|
-
class HostTestComponent {
|
|
14
|
-
id = signal('test-id');
|
|
15
|
-
type = signal('<%= selector %>');
|
|
16
|
-
theme = signal<null>(null);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
describe('<%= className %> (Host2)', () => {
|
|
20
|
-
let fixture: ComponentFixture<HostTestComponent>;
|
|
21
|
-
let host: HostTestComponent;
|
|
22
|
-
|
|
23
|
-
beforeEach(waitForAsync(() => {
|
|
24
|
-
TestBed.configureTestingModule({
|
|
25
|
-
imports: [HostTestComponent]
|
|
26
|
-
}).compileComponents();
|
|
27
|
-
}));
|
|
28
|
-
|
|
29
|
-
beforeEach(() => {
|
|
30
|
-
fixture = TestBed.createComponent(HostTestComponent);
|
|
31
|
-
host = fixture.componentInstance;
|
|
32
|
-
fixture.detectChanges();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('should create', () => {
|
|
36
|
-
expect(host).toBeTruthy();
|
|
37
|
-
});
|
|
38
|
-
});
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
<%# keep ejs tags intact %>
|
|
2
|
-
import { AfterViewInit, Component, effect, inject, input, signal, untracked<% if (zonesSupport) { %>, computed<% } %> } from '@angular/core';
|
|
3
|
-
import { IWidgetSvcConfig<% if (zonesSupport) { %>, IDataHighlight<% } %> } from '../../core/interfaces/widgets-interface';
|
|
4
|
-
import { WidgetRuntimeDirective } from '../../core/directives/widget-runtime.directive';
|
|
5
|
-
import { WidgetStreamsDirective } from '../../core/directives/widget-streams.directive';<% if (zonesSupport) { %>
|
|
6
|
-
import { WidgetMetadataDirective } from '../../core/directives/widget-metadata.directive';
|
|
7
|
-
import { getHighlights } from '../../core/utils/zones-highlight.utils';
|
|
8
|
-
import { UnitsService } from '../../core/services/units.service';<% } %>
|
|
9
|
-
import { ITheme } from '../../core/services/app-service';
|
|
10
|
-
import { getColors } from '../../core/utils/themeColors.utils';
|
|
11
|
-
import { WidgetTitleComponent } from '../../core/components/widget-title/widget-title.component';
|
|
12
|
-
|
|
13
|
-
@Component({
|
|
14
|
-
selector: '<%= selector %>',
|
|
15
|
-
templateUrl: './<%= selector %>.component.html',
|
|
16
|
-
styleUrls: ['./<%= selector %>.component.scss'],
|
|
17
|
-
imports: [WidgetTitleComponent]
|
|
18
|
-
})
|
|
19
|
-
export class <%= className %> implements AfterViewInit {
|
|
20
|
-
public id = input.required<string>();
|
|
21
|
-
public type = input.required<string>();
|
|
22
|
-
public theme = input.required<ITheme | null>();
|
|
23
|
-
public static readonly DEFAULT_CONFIG: IWidgetSvcConfig = { <%= todoBlock ? "// TODO: Update default configuration values as required. See IWidgetSvcConfig interface for all options." : "" %>
|
|
24
|
-
displayName: "My New Widget Label",
|
|
25
|
-
color: "contrast",
|
|
26
|
-
paths: {
|
|
27
|
-
"signalKPath": {
|
|
28
|
-
description: "My new widget Options path description",
|
|
29
|
-
path: <%= pathDefault === null ? 'null' : `'${pathDefault}'` %>,
|
|
30
|
-
source: null,
|
|
31
|
-
pathType: '<%= pathType %>',
|
|
32
|
-
isPathConfigurable: true,
|
|
33
|
-
pathRequired: true,
|
|
34
|
-
convertUnitTo: null,
|
|
35
|
-
showPathSkUnitsFilter: false,
|
|
36
|
-
pathSkUnitsFilter: null,
|
|
37
|
-
sampleTime: 1000
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
filterSelfPaths: true,
|
|
41
|
-
enableTimeout: false,
|
|
42
|
-
dataTimeout: 5<% if (zonesSupport) { %>,
|
|
43
|
-
ignoreZones: false<%= todoBlock ? " // TODO: Signal K Zones support: delete if disabling zones should not be a widget Option users can configure." : "" %><% } %>
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
protected readonly runtime = inject(WidgetRuntimeDirective);
|
|
47
|
-
private readonly streams = inject(WidgetStreamsDirective);<% if (zonesSupport) { %>
|
|
48
|
-
private readonly metadata = inject(WidgetMetadataDirective);
|
|
49
|
-
private readonly unitsService = inject(UnitsService);<% } %>
|
|
50
|
-
|
|
51
|
-
protected titleColor = signal<string | undefined>(undefined);
|
|
52
|
-
protected pathValue = signal<number | null>(null);<% if (zonesSupport) { %>
|
|
53
|
-
|
|
54
|
-
protected highlights = computed<IDataHighlight[]>(() => {<%= todoBlock ? " // TODO: Signal K Zones support: Sample computed signal for zones highlights. Adjust depending on your widget needs." : "" %>
|
|
55
|
-
const zones = this.metadata.zones();
|
|
56
|
-
const cfg = this.runtime.options();
|
|
57
|
-
const theme = this.theme();
|
|
58
|
-
|
|
59
|
-
if (cfg?.ignoreZones || !zones?.length || !theme) return [];
|
|
60
|
-
const unit = cfg.paths['gaugePath'].convertUnitTo;
|
|
61
|
-
const min = cfg.displayScale.lower;
|
|
62
|
-
const max = cfg.displayScale.upper;
|
|
63
|
-
return getHighlights(zones, theme, unit, this.unitsService, min, max);
|
|
64
|
-
});<% } %>
|
|
65
|
-
|
|
66
|
-
constructor() {
|
|
67
|
-
effect(() => {
|
|
68
|
-
const theme = this.theme();
|
|
69
|
-
const cfg = this.runtime.options();
|
|
70
|
-
if (!theme || !cfg) return;
|
|
71
|
-
|
|
72
|
-
untracked(() => {
|
|
73
|
-
this.titleColor.set(getColors(this.runtime.options().color, theme).dim);<%= todoBlock ? " // TODO: Themes are either, Dark, Light or Red Night mode. Add signals to include more colors and variants in your template." : "" %>
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
effect(() => {<%= todoBlock ? " // React to widget Options configuration changes." : "" %>
|
|
78
|
-
const cfg = this.runtime.options();
|
|
79
|
-
untracked(() => {
|
|
80
|
-
const pathCfg = cfg.paths['signalKPath'];
|
|
81
|
-
if (pathCfg.path) {
|
|
82
|
-
this.streams.observe('signalKPath', path => {<%= todoBlock ? " // TODO: Update the signal with the latest path value and/or do more data processing as required." : "" %>
|
|
83
|
-
this.pathValue.set(path.data.value);
|
|
84
|
-
});<% if (zonesSupport) { %>
|
|
85
|
-
this.metadata.observe('signalKPath');<% } %>
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// eslint-disable-next-line @angular-eslint/no-empty-lifecycle-method
|
|
92
|
-
ngAfterViewInit() {<%= todoBlock ? " // TODO: Add additional widget logic here (canvas drawing, animations, etc.) here or remove if not needed." : "" %>
|
|
93
|
-
}
|
|
94
|
-
}
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
const { apply, url, template, move, mergeWith, chain, noop } = require('@angular-devkit/schematics');
|
|
2
|
-
const { strings } = require('@angular-devkit/core');
|
|
3
|
-
const { appendImport, updateComponentMap, insertDefinitionObject } = require('./utils/formatting');
|
|
4
|
-
|
|
5
|
-
const WIDGETS_DIR = 'src/app/widgets';
|
|
6
|
-
const WIDGET_SERVICE = 'src/app/core/services/widget.service.ts';
|
|
7
|
-
|
|
8
|
-
function createHost2Widget(options) {
|
|
9
|
-
return (tree, ctx) => {
|
|
10
|
-
const debug = !!options.debugLogging;
|
|
11
|
-
if (debug) ctx.logger.info('[widget-schematic] Raw options input: ' + JSON.stringify(options));
|
|
12
|
-
if (debug) ctx.logger.info('[widget-schematic] Using schema x-prompt only (no inquirer).');
|
|
13
|
-
// Normalize registerWidget (case-insensitive). 'no' => skip service update.
|
|
14
|
-
const rawReg = (options.registerWidget || '').trim();
|
|
15
|
-
const lower = rawReg.toLowerCase();
|
|
16
|
-
const skip = lower === 'no';
|
|
17
|
-
const normalizedCategory = skip ? 'no' : capitalizeCategory(lower);
|
|
18
|
-
if (debug) ctx.logger.info(`[widget-schematic] registerWidget raw='${rawReg}' lower='${lower}' => normalized='${normalizedCategory}' skip=${skip}`);
|
|
19
|
-
|
|
20
|
-
const nameDashed = strings.dasherize(options.name);
|
|
21
|
-
const selector = `widget-${nameDashed}`;
|
|
22
|
-
const className = `Widget${strings.classify(options.name)}Component`;
|
|
23
|
-
const widgetFolder = `${WIDGETS_DIR}/${selector}`;
|
|
24
|
-
if (tree.exists(`${widgetFolder}/${selector}.component.ts`)) {
|
|
25
|
-
throw new Error(`Widget already exists: ${selector}`);
|
|
26
|
-
}
|
|
27
|
-
const tplSource = apply(url('./files/widget'), [
|
|
28
|
-
template({
|
|
29
|
-
...strings,
|
|
30
|
-
...options,
|
|
31
|
-
nameDashed,
|
|
32
|
-
selector,
|
|
33
|
-
className,
|
|
34
|
-
zonesSupport: !!options.zonesSupport,
|
|
35
|
-
// Hidden defaults
|
|
36
|
-
convertUnitTo: null,
|
|
37
|
-
sampleTime: 1000,
|
|
38
|
-
icon: 'placeholder-icon',
|
|
39
|
-
description: 'TODO: Add description',
|
|
40
|
-
}),
|
|
41
|
-
move(widgetFolder)
|
|
42
|
-
]);
|
|
43
|
-
const commonTemplateCtx = { ...strings, ...options, nameDashed, selector, className, zonesSupport: !!options.zonesSupport };
|
|
44
|
-
const specSource = options.addSpec ? apply(url('./files/spec'), [
|
|
45
|
-
template(commonTemplateCtx), move(widgetFolder)
|
|
46
|
-
]) : null;
|
|
47
|
-
const readmeSource = options.readme ? apply(url('./files/readme'), [
|
|
48
|
-
template({
|
|
49
|
-
...commonTemplateCtx,
|
|
50
|
-
convertUnitTo: null,
|
|
51
|
-
sampleTime: 1000,
|
|
52
|
-
icon: 'placeholder-icon',
|
|
53
|
-
description: 'TODO: Add description'
|
|
54
|
-
}), move(widgetFolder)
|
|
55
|
-
]) : null;
|
|
56
|
-
|
|
57
|
-
return chain([
|
|
58
|
-
mergeWith(tplSource),
|
|
59
|
-
specSource ? mergeWith(specSource) : noop(),
|
|
60
|
-
readmeSource ? mergeWith(readmeSource) : noop(),
|
|
61
|
-
stripTemplateSuffixRule(widgetFolder),
|
|
62
|
-
!skip ? updateWidgetService(selector, className, { ...options, category: normalizedCategory, debugLogging: debug }) : noop(),
|
|
63
|
-
logSummary(selector, className, normalizedCategory)
|
|
64
|
-
])(tree, ctx);
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function logSummary(selector, className, registerMode) {
|
|
69
|
-
return (_t, ctx) => {
|
|
70
|
-
ctx.logger.info(`✔ Created ${selector} (${className})`);
|
|
71
|
-
if (registerMode !== 'no') ctx.logger.info('✔ WidgetService updated');
|
|
72
|
-
else ctx.logger.info('ℹ Skipped WidgetService update (registerWidget=no)');
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function capitalizeCategory(lower) {
|
|
77
|
-
switch (lower) {
|
|
78
|
-
case 'core': return 'Core';
|
|
79
|
-
case 'gauge': return 'Gauge';
|
|
80
|
-
case 'component': return 'Component';
|
|
81
|
-
case 'racing': return 'Racing';
|
|
82
|
-
default: return lower; // includes 'no' or any unexpected value
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function updateWidgetService(selector, className, opts) {
|
|
87
|
-
return (tree, ctx) => {
|
|
88
|
-
if (!tree.exists(WIDGET_SERVICE)) {
|
|
89
|
-
ctx.logger.warn('WidgetService not found; skipping auto-registration');
|
|
90
|
-
return tree;
|
|
91
|
-
}
|
|
92
|
-
if (opts.debugLogging) ctx.logger.info('[widget-schematic] Updating WidgetService for ' + className);
|
|
93
|
-
let updated = tree.read(WIDGET_SERVICE).toString('utf-8');
|
|
94
|
-
const importStatement = `import { ${className} } from '../../widgets/${selector}/${selector}.component';`;
|
|
95
|
-
updated = appendImport(updated, importStatement);
|
|
96
|
-
if (opts.debugLogging) ctx.logger.info('[widget-schematic] After import insertion.');
|
|
97
|
-
updated = updateComponentMap(updated, className);
|
|
98
|
-
if (opts.debugLogging) ctx.logger.info('[widget-schematic] After component map update.');
|
|
99
|
-
updated = insertDefinitionObject(updated, selector, className, {
|
|
100
|
-
title: opts.title || opts.name || className,
|
|
101
|
-
description: "TODO: Add description. Description is found in the item related to your widget as an item in array _widgetDefinition, found in file src/app/core/services/widget.service.ts. In this file you can also set the widget icon and other properties.",
|
|
102
|
-
icon: 'placeholder-icon', // TODO: Replace with proper icon key when you add an icon. See file src/assets/svg/icons.svg
|
|
103
|
-
category: opts.category || 'Core',
|
|
104
|
-
minWidth: 1,
|
|
105
|
-
minHeight: 1,
|
|
106
|
-
defaultWidth: 4,
|
|
107
|
-
defaultHeight: 6,
|
|
108
|
-
});
|
|
109
|
-
if (opts.debugLogging) ctx.logger.info('[widget-schematic] After definition insertion.');
|
|
110
|
-
tree.overwrite(WIDGET_SERVICE, updated);
|
|
111
|
-
return tree;
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function stripTemplateSuffixRule(widgetFolder) {
|
|
116
|
-
return (tree) => {
|
|
117
|
-
const visit = (dirPath) => {
|
|
118
|
-
const dir = tree.getDir(dirPath);
|
|
119
|
-
dir.subfiles.forEach(fileName => {
|
|
120
|
-
if (fileName.endsWith('.template')) {
|
|
121
|
-
const fullPath = `${dirPath}/${fileName}`;
|
|
122
|
-
const newPath = fullPath.replace(/\.template$/, '');
|
|
123
|
-
const content = tree.read(fullPath);
|
|
124
|
-
if (content) {
|
|
125
|
-
if (!tree.exists(newPath)) tree.create(newPath, content);
|
|
126
|
-
tree.delete(fullPath);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
dir.subdirs.forEach(sub => visit(`${dirPath}/${sub}`));
|
|
131
|
-
};
|
|
132
|
-
if (tree.getDir(widgetFolder)) visit(widgetFolder);
|
|
133
|
-
return tree;
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
exports.createHost2Widget = createHost2Widget;
|
|
138
|
-
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "http://json-schema.org/draft-07/schema",
|
|
3
|
-
"$id": "CreateHost2WidgetSchema",
|
|
4
|
-
"title": "Create Host2 Widget",
|
|
5
|
-
"type": "object",
|
|
6
|
-
"properties": {
|
|
7
|
-
"name": {
|
|
8
|
-
"type": "string",
|
|
9
|
-
"description": "Widget machine name without widget- prefix (kebab-case). e.g. tides-chart",
|
|
10
|
-
"minLength": 3,
|
|
11
|
-
"x-prompt": "The widget base name is the Angular component machine name (kebab-case, without widget- prefix which will be added automatically). Using base name tides-chart will create a widget-tides-chart component.\nWidget base name:"
|
|
12
|
-
},
|
|
13
|
-
"title": {
|
|
14
|
-
"type": "string",
|
|
15
|
-
"description": "The widget title is the descriptive title used in the Add Widget dialog",
|
|
16
|
-
"minLength": 3,
|
|
17
|
-
"default": "Widget Label",
|
|
18
|
-
"x-prompt": "The widget title is the descriptive title used in the Add Widget dialog.\nWidget title:"
|
|
19
|
-
},
|
|
20
|
-
"registerWidget": {
|
|
21
|
-
"type": "string",
|
|
22
|
-
"description": "Register widget and make it available in the widget Add Options dialog",
|
|
23
|
-
"enum": [
|
|
24
|
-
"No",
|
|
25
|
-
"Core",
|
|
26
|
-
"Gauge",
|
|
27
|
-
"Component",
|
|
28
|
-
"Racing"
|
|
29
|
-
],
|
|
30
|
-
"x-prompt": "Register widget and make it available in the widget Add Options dialog (choose category or 'no' to skip):"
|
|
31
|
-
},
|
|
32
|
-
"pathType": {
|
|
33
|
-
"type": "string",
|
|
34
|
-
"description": "The type of value the Signal K path provides (number|string|boolean|Date)",
|
|
35
|
-
"enum": [
|
|
36
|
-
"number",
|
|
37
|
-
"string",
|
|
38
|
-
"boolean",
|
|
39
|
-
"Date"
|
|
40
|
-
],
|
|
41
|
-
"default": "number",
|
|
42
|
-
"x-prompt": "Path value type (number|string|boolean|Date):"
|
|
43
|
-
},
|
|
44
|
-
"pathDefault": {
|
|
45
|
-
"type": [
|
|
46
|
-
"string",
|
|
47
|
-
"null"
|
|
48
|
-
],
|
|
49
|
-
"default": null,
|
|
50
|
-
"description": "The default Signal K path the widget should use (if any)",
|
|
51
|
-
"x-prompt": "Type the default Signal K path value for the widget (press enter for no default):"
|
|
52
|
-
},
|
|
53
|
-
"zonesSupport": {
|
|
54
|
-
"type": "boolean",
|
|
55
|
-
"default": false,
|
|
56
|
-
"description": "Enable Signal K Zones metadata support.",
|
|
57
|
-
"x-prompt": "Enable Signal K Zones metadata support?"
|
|
58
|
-
},
|
|
59
|
-
"addSpec": {
|
|
60
|
-
"type": "boolean",
|
|
61
|
-
"default": true,
|
|
62
|
-
"description": "Generate widget spec (test) file.",
|
|
63
|
-
"x-prompt": "Generate widget tests file?"
|
|
64
|
-
},
|
|
65
|
-
"todoBlock": {
|
|
66
|
-
"type": "boolean",
|
|
67
|
-
"default": true,
|
|
68
|
-
"description": "Include TODO guidance block in component.",
|
|
69
|
-
"x-prompt": "Include TODO guidance block in component?"
|
|
70
|
-
},
|
|
71
|
-
"readme": {
|
|
72
|
-
"type": "boolean",
|
|
73
|
-
"default": true,
|
|
74
|
-
"description": "Generate developer instruction README file in the widget's folder.",
|
|
75
|
-
"x-prompt": "Generate developer instruction README file in the widget's folder?"
|
|
76
|
-
},
|
|
77
|
-
"debugLogging": {
|
|
78
|
-
"type": "boolean",
|
|
79
|
-
"default": false,
|
|
80
|
-
"description": "Enable verbose debug logging for schematic execution (normalization, prompts, service update)."
|
|
81
|
-
}
|
|
82
|
-
},
|
|
83
|
-
"required": [
|
|
84
|
-
"name",
|
|
85
|
-
"title",
|
|
86
|
-
"registerWidget"
|
|
87
|
-
],
|
|
88
|
-
"additionalProperties": false
|
|
89
|
-
}
|