@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.
Files changed (102) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +11 -7
  3. package/package.json +21 -8
  4. package/plugin/duckdb-parquet-storage.service.js +1182 -0
  5. package/plugin/history-series.service.js +439 -0
  6. package/plugin/index.js +705 -30
  7. package/plugin/openApi.json +253 -3
  8. package/plugin/plugin-auth.service.js +75 -0
  9. package/public/assets/help-docs/chartplotter.md +5 -18
  10. package/public/assets/help-docs/community.md +0 -3
  11. package/public/assets/help-docs/configuration.md +1 -1
  12. package/public/assets/help-docs/contact-us.md +0 -4
  13. package/public/assets/help-docs/dashboards.md +20 -18
  14. package/public/assets/help-docs/datainspector.md +7 -5
  15. package/public/assets/help-docs/history-api.md +116 -0
  16. package/public/assets/help-docs/menu.json +18 -6
  17. package/public/assets/help-docs/nodered-control-flows.md +125 -0
  18. package/public/assets/help-docs/putcontrols.md +101 -60
  19. package/public/assets/help-docs/welcome.md +6 -7
  20. package/public/assets/help-docs/widget-historical-series.md +66 -0
  21. package/public/assets/help-docs/zones.md +5 -10
  22. package/public/chunk-A6DQJFP4.js +16 -0
  23. package/public/chunk-B75MT7ND.js +1 -0
  24. package/public/{chunk-T6TFVZVM.js → chunk-CEB42O2C.js} +1 -1
  25. package/public/chunk-CHGXAEKT.js +2 -0
  26. package/public/chunk-D7VDX7ZF.js +5 -0
  27. package/public/{chunk-ZQER6AIQ.js → chunk-DEGYRCMI.js} +1 -1
  28. package/public/{chunk-M2B5OYGO.js → chunk-DEM56G4S.js} +1 -1
  29. package/public/chunk-DYTBBUMI.js +4 -0
  30. package/public/chunk-EQ2N7KDA.js +3 -0
  31. package/public/chunk-FNF7M3AE.js +1 -0
  32. package/public/chunk-IHURI4IH.js +5 -0
  33. package/public/{chunk-YIYYVDFO.js → chunk-IYRLINL7.js} +2 -2
  34. package/public/{chunk-5FEX27I4.js → chunk-JB4YVVNW.js} +1 -1
  35. package/public/chunk-JGGMFMY5.js +1 -0
  36. package/public/chunk-KPHICV76.js +5 -0
  37. package/public/{chunk-QZKCRH3H.js → chunk-KZ5DUKAX.js} +1 -1
  38. package/public/{chunk-HMOOTAEA.js → chunk-LQDSU4WS.js} +3 -3
  39. package/public/{chunk-IXQ7KIFY.js → chunk-MGPPVLZ7.js} +1 -1
  40. package/public/{chunk-QVCLOCEC.js → chunk-R7RQHWKJ.js} +1 -1
  41. package/public/chunk-RONXIZ2U.js +9 -0
  42. package/public/chunk-S72JTJPN.js +6 -0
  43. package/public/{chunk-KFFAA7DL.js → chunk-VCY32MWT.js} +8 -8
  44. package/public/chunk-YCEXTKGG.js +1 -0
  45. package/public/chunk-YKJKIWXO.js +6 -0
  46. package/public/chunk-ZV7IYYEQ.js +50 -0
  47. package/public/index.html +1 -1
  48. package/public/main-FQESQQV6.js +1 -0
  49. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -84
  50. package/.github/ISSUE_TEMPLATE/config.yml +0 -5
  51. package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -35
  52. package/.github/copilot-instructions.md +0 -205
  53. package/.github/instructions/angular.instructions.md +0 -123
  54. package/.github/instructions/best-practices.instructions.md +0 -59
  55. package/.github/instructions/project.instructions.md +0 -432
  56. package/.github/workflows/ci.yml +0 -37
  57. package/docs/widget-schematic.md +0 -102
  58. package/images/ActionSidenav.png +0 -0
  59. package/images/ChartplotterMode.png +0 -0
  60. package/images/KIPDemo.png +0 -0
  61. package/images/KipBrightness-1024.png +0 -0
  62. package/images/KipConfig-Units-1024.png +0 -0
  63. package/images/KipConfig-display-1024x488.png +0 -0
  64. package/images/KipFreeboard-SK-1024.png +0 -0
  65. package/images/KipGaugeSample1-1024x545.png +0 -0
  66. package/images/KipGaugeSample2-1024x488.png +0 -0
  67. package/images/KipGaugeSample3-1024x508.png +0 -0
  68. package/images/KipNightMode-1024.png +0 -0
  69. package/images/KipWidgetConfig-layout-1024.png +0 -0
  70. package/images/KipWidgetConfig-paths-1024x488.png +0 -0
  71. package/images/Options.png +0 -0
  72. package/images/exterior_user_installs.png +0 -0
  73. package/images/formfactor.png +0 -0
  74. package/public/assets/help-docs/datasets.md +0 -95
  75. package/public/chunk-2OB7ZJBR.js +0 -3
  76. package/public/chunk-6GGJZDRE.js +0 -1
  77. package/public/chunk-6V4GGGXE.js +0 -2
  78. package/public/chunk-A5BW6BUM.js +0 -1
  79. package/public/chunk-DGE5YFPU.js +0 -5
  80. package/public/chunk-G6M3Z3BY.js +0 -53
  81. package/public/chunk-GMGZLXY7.js +0 -4
  82. package/public/chunk-GUZ3BDVZ.js +0 -2
  83. package/public/chunk-ICDGHQFP.js +0 -6
  84. package/public/chunk-JCNE4QHQ.js +0 -15
  85. package/public/chunk-K6XYUNG4.js +0 -8
  86. package/public/chunk-LGCQEN7V.js +0 -4
  87. package/public/chunk-O3JH7UTR.js +0 -1
  88. package/public/chunk-Q3USFT4F.js +0 -2
  89. package/public/chunk-VIKU7BH7.js +0 -1
  90. package/public/chunk-XMQPXXLW.js +0 -8
  91. package/public/main-4URMGBQS.js +0 -1
  92. package/rm-npmjs-beta.sh +0 -50
  93. package/tools/schematics/collection.json +0 -9
  94. package/tools/schematics/create-host2-widget/files/readme/README.md.template +0 -109
  95. package/tools/schematics/create-host2-widget/files/spec/widget-__name@dasherize__.component.spec.ts +0 -38
  96. package/tools/schematics/create-host2-widget/files/widget/widget-__name@dasherize__.component.html +0 -6
  97. package/tools/schematics/create-host2-widget/files/widget/widget-__name@dasherize__.component.scss +0 -5
  98. package/tools/schematics/create-host2-widget/files/widget/widget-__name@dasherize__.component.ts.template +0 -94
  99. package/tools/schematics/create-host2-widget/index.js +0 -138
  100. package/tools/schematics/create-host2-widget/schema.json +0 -89
  101. package/tools/schematics/create-host2-widget/test/create-host2-widget.spec.ts +0 -70
  102. package/tools/schematics/create-host2-widget/utils/formatting.js +0 -119
@@ -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,9 +0,0 @@
1
- {
2
- "schematics": {
3
- "create-host2-widget": {
4
- "description": "Generate a Host2 KIP widget (component + optional config + service wiring)",
5
- "factory": "./create-host2-widget/index#createHost2Widget",
6
- "schema": "./create-host2-widget/schema.json"
7
- }
8
- }
9
- }
@@ -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)
@@ -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,6 +0,0 @@
1
- <widget-title [text]="runtime.options().displayName" [color]="titleColor()"/>
2
- <p style="display: block; position: absolute; z-index: 20;
3
- left: 0; right: 0;
4
- top: 15%;
5
- height: 85%;
6
- width: 100%;">Path Value: {{ pathValue() }}</p>
@@ -1,5 +0,0 @@
1
- :host {
2
- display: block;
3
- width: 100%;
4
- height: 100%;
5
- }
@@ -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
- }