chore: update Taiga to 5 (#3136)

* chore: update Taiga to 5

* chore: fix
This commit is contained in:
Alex Inkin
2026-03-15 19:51:50 +04:00
committed by GitHub
parent be921b7865
commit a90b96cddd
184 changed files with 1508 additions and 1958 deletions

View File

@@ -98,7 +98,8 @@ this.dialog.openComponent(new PolymorpheusComponent(MyComponent, injector), { la
### Errors & Tooltips
```html
<tui-error [error]="[] | tuiFieldError | async" />
<tui-error formControlName="controlName" />
<tui-error [error]="'Error text'" />
<tui-icon [tuiTooltip]="'Hint text'" />
```

View File

@@ -47,7 +47,7 @@
"projects/ui/src/manifest.webmanifest"
],
"styles": [
"node_modules/@taiga-ui/core/styles/taiga-ui-theme.less",
"node_modules/@taiga-ui/styles/taiga-ui-theme.less",
"projects/shared/styles/taiga.scss",
"projects/shared/styles/shared.scss",
"projects/ui/src/styles.scss"
@@ -158,7 +158,7 @@
}
],
"styles": [
"node_modules/@taiga-ui/core/styles/taiga-ui-theme.less",
"node_modules/@taiga-ui/styles/taiga-ui-theme.less",
"projects/shared/styles/taiga.scss",
"projects/shared/styles/shared.scss",
"projects/setup-wizard/src/styles.scss"
@@ -258,8 +258,8 @@
}
],
"styles": [
"node_modules/@taiga-ui/core/styles/taiga-ui-theme.less",
"node_modules/@taiga-ui/core/styles/taiga-ui-fonts.less",
"node_modules/@taiga-ui/styles/taiga-ui-theme.less",
"node_modules/@taiga-ui/styles/taiga-ui-fonts.less",
"projects/shared/styles/shared.scss",
"projects/start-tunnel/src/styles.scss"
],

489
web/package-lock.json generated
View File

@@ -9,7 +9,6 @@
"version": "0.4.0-alpha.20",
"license": "MIT",
"dependencies": {
"@angular/animations": "^21.2.1",
"@angular/cdk": "^21.2.1",
"@angular/common": "^21.2.1",
"@angular/compiler": "^21.2.1",
@@ -24,19 +23,19 @@
"@noble/hashes": "^1.4.0",
"@start9labs/argon2": "^0.3.0",
"@start9labs/start-sdk": "file:../sdk/baseDist",
"@taiga-ui/addon-charts": "4.73.0",
"@taiga-ui/addon-commerce": "4.73.0",
"@taiga-ui/addon-mobile": "4.73.0",
"@taiga-ui/addon-table": "4.73.0",
"@taiga-ui/cdk": "4.73.0",
"@taiga-ui/core": "4.73.0",
"@taiga-ui/dompurify": "4.1.11",
"@taiga-ui/event-plugins": "4.7.0",
"@taiga-ui/experimental": "4.73.0",
"@taiga-ui/icons": "4.73.0",
"@taiga-ui/kit": "4.73.0",
"@taiga-ui/layout": "4.73.0",
"@taiga-ui/polymorpheus": "4.9.0",
"@taiga-ui/addon-charts": "5.0.0-rc.4",
"@taiga-ui/addon-commerce": "5.0.0-rc.4",
"@taiga-ui/addon-mobile": "5.0.0-rc.4",
"@taiga-ui/addon-table": "5.0.0-rc.4",
"@taiga-ui/cdk": "5.0.0-rc.4",
"@taiga-ui/core": "5.0.0-rc.4",
"@taiga-ui/dompurify": "5.0.1",
"@taiga-ui/event-plugins": "5.0.0",
"@taiga-ui/experimental": "5.0.0-rc.4",
"@taiga-ui/icons": "5.0.0-rc.4",
"@taiga-ui/kit": "5.0.0-rc.4",
"@taiga-ui/layout": "5.0.0-rc.4",
"@taiga-ui/polymorpheus": "5.0.0",
"ansi-to-html": "^0.7.2",
"base64-js": "^1.5.1",
"buffer": "^6.0.3",
@@ -46,7 +45,7 @@
"cron": "^2.2.0",
"cronstrue": "^2.21.0",
"deep-equality-data-structures": "1.5.1",
"dompurify": "^3.1.7",
"dompurify": "^3.3.2",
"fast-json-patch": "^3.1.1",
"fuse.js": "^6.4.6",
"jose": "^4.9.0",
@@ -71,7 +70,6 @@
"@angular/cli": "^21.2.1",
"@angular/compiler-cli": "^21.2.1",
"@angular/language-service": "^21.2.1",
"@types/dompurify": "3.0.5",
"@types/estree": "^0.0.51",
"@types/js-yaml": "^4.0.5",
"@types/marked": "^4.0.3",
@@ -448,22 +446,6 @@
"hawkeye": "index.mjs"
}
},
"node_modules/@angular/animations": {
"version": "21.2.1",
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-21.2.1.tgz",
"integrity": "sha512-zT/S29pUTbziCLvZ2itBdNWd5i8tsXexofH7KA4n2yvYmK1EhNpE7TlHRjghmsHgtDt4VnGiMW4zXEyrl05Dwg==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
},
"peerDependencies": {
"@angular/core": "21.2.1"
}
},
"node_modules/@angular/build": {
"version": "21.2.1",
"resolved": "https://registry.npmjs.org/@angular/build/-/build-21.2.1.tgz",
@@ -2200,46 +2182,46 @@
]
},
"node_modules/@maskito/angular": {
"version": "3.11.1",
"resolved": "https://registry.npmjs.org/@maskito/angular/-/angular-3.11.1.tgz",
"integrity": "sha512-+OZzbRJj/9fOGhgPr0xYctSHe/Ngahip3VdNWBslRTpt7g+UTBYcB8vU9J4cHfpdXYeLM3tM0tnKksc3Eis0+Q==",
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@maskito/angular/-/angular-5.1.1.tgz",
"integrity": "sha512-NCKvUpIF3KCJDRpDWcI0DAeVQaYgsJmGAycq7h/LVu6h7P0nSd5xanYpoM2/F1MdXvGh/aEAF37KooTsfwUqog==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"tslib": "2.8.1"
},
"peerDependencies": {
"@angular/core": ">=16.0.0",
"@angular/forms": ">=16.0.0",
"@maskito/core": "^3.11.1"
"@angular/core": ">=19.0.0",
"@angular/forms": ">=19.0.0",
"@maskito/core": "^5.1.1"
}
},
"node_modules/@maskito/core": {
"version": "3.11.1",
"resolved": "https://registry.npmjs.org/@maskito/core/-/core-3.11.1.tgz",
"integrity": "sha512-zN5k/BiZXblo8mEFhsGnnXBCqKMkjEGArorOOcpB1/ymZyqF12Dk6IipEsSE6abMnWw4YF2tukzfq73BFZKz8A==",
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@maskito/core/-/core-5.1.1.tgz",
"integrity": "sha512-grxipbOGKBs++kgAM/5K/lCghY/AfkSKNcRDSp8Nspf9dngxWiraMiPQVFsS8E0sab1wHEWlMXBdqEa3WyQXPA==",
"license": "Apache-2.0",
"peer": true
},
"node_modules/@maskito/kit": {
"version": "3.11.1",
"resolved": "https://registry.npmjs.org/@maskito/kit/-/kit-3.11.1.tgz",
"integrity": "sha512-KOBUqxRz383xJWCoe+Emwxv2oAzUrZobIN+Gntmi5Py2S10XbqYnGX/6W7QHN8CUK2Nx11d3HsxbEQaq5Hinjg==",
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@maskito/kit/-/kit-5.1.1.tgz",
"integrity": "sha512-YlMPGzyX/zuYaAxWRFTwBYoP3bV1WzMhrW8D+7Tn6nUfCBUTogvnJrVhVyOi6dZzXGX8qP1yRVT6W/IVt79F5g==",
"license": "Apache-2.0",
"peer": true,
"peerDependencies": {
"@maskito/core": "^3.11.1"
"@maskito/core": "^5.1.1"
}
},
"node_modules/@maskito/phone": {
"version": "3.11.1",
"resolved": "https://registry.npmjs.org/@maskito/phone/-/phone-3.11.1.tgz",
"integrity": "sha512-ptNDPIZQs/v598qydBa9cnvoCE8+k2Sv07kKKVx3vG0V40DQnIlEL+LYKrJJbMIiPOB6CH90hB9eaA9KKReZ6w==",
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@maskito/phone/-/phone-5.1.1.tgz",
"integrity": "sha512-yBb42/7FZpEjlAvvT5SabWX8jZdMjhGv4g7G6McarVM1RcG6vcV+kZd0xjAdDvOv8s5zGww+pN39Scn0tt+QMg==",
"license": "Apache-2.0",
"peer": true,
"peerDependencies": {
"@maskito/core": "^3.11.1",
"@maskito/kit": "^3.11.1",
"@maskito/core": "^5.1.1",
"@maskito/kit": "^5.1.1",
"libphonenumber-js": ">=1.0.0"
}
},
@@ -2732,52 +2714,53 @@
}
},
"node_modules/@ng-web-apis/common": {
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@ng-web-apis/common/-/common-4.14.0.tgz",
"integrity": "sha512-eLrpy9e8R+eJKRUWv06jauaDCJ/6/9llmHPTvmWH3yTS9HIn+twk5ZmyfNwFqA/r8g9Kt4Ap48YuaydQy00nuw==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@ng-web-apis/common/-/common-5.1.0.tgz",
"integrity": "sha512-omhjrojnwgJXIPT4H4sLruqCSGlWeLLuIAUkDB+fIARd1shT0vYmA9qugaNd54XmLOnL7Hu97FYkXnULFyT85w==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/common": ">=16.0.0",
"@angular/core": ">=16.0.0",
"@angular/common": ">=19.0.0",
"@angular/core": ">=19.0.0",
"@types/dom-speech-recognition": "^0.0.7",
"rxjs": ">=7.0.0"
}
},
"node_modules/@ng-web-apis/intersection-observer": {
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@ng-web-apis/intersection-observer/-/intersection-observer-4.14.0.tgz",
"integrity": "sha512-VDuak0+jDPwPttqQbgIYCD1tM2F7wKSXZWWUjFdNfkV47GspEPzzf12lY1eiltIiCB4cv6asZNs5ijnsxpnb9A==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@ng-web-apis/intersection-observer/-/intersection-observer-5.1.0.tgz",
"integrity": "sha512-Fonur9MS3bxDyCbGX3gcuEsvIRPA/0PrkpOe2EohVJMdrVjs8eB6SbQR4DSKVv5We1maMSzt3jhd1u5KEMp2xA==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/core": ">=16.0.0",
"@ng-web-apis/common": ">=4.12.0"
"@angular/core": ">=19.0.0",
"@ng-web-apis/common": "^5.1.0"
}
},
"node_modules/@ng-web-apis/mutation-observer": {
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@ng-web-apis/mutation-observer/-/mutation-observer-4.14.0.tgz",
"integrity": "sha512-AUZDtVWN1rUSZoaB5AWhRYZ8DNSPoPQ2Ld8G8q9ocxfpslDHvjQQjUj5r0zHjAwKe1f9cNuow2nU+n6k3hF6ew==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@ng-web-apis/mutation-observer/-/mutation-observer-5.1.0.tgz",
"integrity": "sha512-X8DepT55KG/xY/WnQ05pnSxpyzv76CWiDrKanzOeUy9SUckrfpjCWkGvyz9fSzUv27mOC4t/ry6Fnu+em28Tzg==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/core": ">=16.0.0",
"@ng-web-apis/common": ">=4.12.0"
"@angular/core": ">=19.0.0",
"@ng-web-apis/common": "^5.1.0"
}
},
"node_modules/@ng-web-apis/platform": {
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@ng-web-apis/platform/-/platform-4.14.0.tgz",
"integrity": "sha512-zmsbg0cEumppG6VlxIpM+ghJXBJ6G2mlTm+XmC9HYCnRJxYJjqIVRDZbYUQ4pGQHz4iJxRbp2C3VOndre72+3g==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@ng-web-apis/platform/-/platform-5.1.0.tgz",
"integrity": "sha512-Jp6BqEDQ0C7/NpQ2TqTvy2ddjcOt1nON9KlCOaIR7L+YY88KToeQLD3dsFRFbxwWB1G1okgpxysMoFunatltYQ==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
@@ -2785,31 +2768,31 @@
}
},
"node_modules/@ng-web-apis/resize-observer": {
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@ng-web-apis/resize-observer/-/resize-observer-4.14.0.tgz",
"integrity": "sha512-W5VXT/y0Vv68Gx/2urgu/JmIQfv0jPtzfvGtyulgGALfBrnei4oVXvJe2aZmLb0/VkxH65qlYOKVEOGqBrwSwQ==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@ng-web-apis/resize-observer/-/resize-observer-5.1.0.tgz",
"integrity": "sha512-cuEPAaZJ2UDOA283S/Ehz7tdDLuCY6tLmdcYHlXTdnMcXiyrqUsu0dKQ04ZoZ0KXfoSjT95jPUTabot5psVPkw==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/core": ">=16.0.0",
"@ng-web-apis/common": ">=4.12.0"
"@angular/core": ">=19.0.0",
"@ng-web-apis/common": "^5.1.0"
}
},
"node_modules/@ng-web-apis/screen-orientation": {
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@ng-web-apis/screen-orientation/-/screen-orientation-4.14.0.tgz",
"integrity": "sha512-XlXjxhYT7QKW0WKQE4BIJ2OZ7c1EFRXUQ4eXQ41Jwl8ukG0zkixOTrxGlnfy7ijX5df3sX/+0kKCXwsyj/n5BA==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@ng-web-apis/screen-orientation/-/screen-orientation-5.1.0.tgz",
"integrity": "sha512-x6W2C45EAcaHk/0QR5hcWg6bByyPD+E/xPlQjR8sQwLN+AE8ag1hrhmIbkQOyEP2K2kvU81M0ndbre0o4dRo9Q==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/core": ">=16.0.0",
"@ng-web-apis/common": ">=4.12.0",
"@angular/core": ">=19.0.0",
"@ng-web-apis/common": "^5.1.0",
"rxjs": ">=7.0.0"
}
},
@@ -4182,118 +4165,118 @@
"link": true
},
"node_modules/@taiga-ui/addon-charts": {
"version": "4.73.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-charts/-/addon-charts-4.73.0.tgz",
"integrity": "sha512-BZhmupFnngsGICumSvaA7b8jf5lprQ5PD5Nqduq6iQ/pa02qv4VdR7HE2OM07kg4pBm4OX3OtJ0H9V0N/buchQ==",
"version": "5.0.0-rc.4",
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-charts/-/addon-charts-5.0.0-rc.4.tgz",
"integrity": "sha512-goStkREzlt2vgK0gRI2o2BEud8YNqMxXAG3NW1YxyAO/P13OwzuxWxGBW013ykzxfpEVynUlswpUPWHyowNa8g==",
"license": "Apache-2.0",
"dependencies": {
"tslib": ">=2.8.1"
},
"peerDependencies": {
"@angular/common": ">=16.0.0",
"@angular/core": ">=16.0.0",
"@ng-web-apis/common": "^4.14.0",
"@taiga-ui/cdk": "^4.73.0",
"@taiga-ui/core": "^4.73.0",
"@taiga-ui/polymorpheus": "^4.9.0"
"@angular/common": ">=19.0.0",
"@angular/core": ">=19.0.0",
"@ng-web-apis/common": "^5.1.0",
"@taiga-ui/cdk": "^5.0.0-rc.4",
"@taiga-ui/core": "^5.0.0-rc.4",
"@taiga-ui/polymorpheus": "^5.0.0"
}
},
"node_modules/@taiga-ui/addon-commerce": {
"version": "4.73.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-commerce/-/addon-commerce-4.73.0.tgz",
"integrity": "sha512-jLNGPZgAnqhrLp5jJ2LNaDVuQs7SX1pUitDm4xpt26GnO7GuQmjY2E0SmZtEbBf/9ykROAnPzgWJbaIj6e5V0A==",
"version": "5.0.0-rc.4",
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-commerce/-/addon-commerce-5.0.0-rc.4.tgz",
"integrity": "sha512-5Z/QbxC4NQKJ0F7Y7oJG86o1Ek+4XiBeV61F42/syHmD1/3Y2KEll+1GBvlx++xFHJvjfa0+xFUx3omLJjIGzA==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"tslib": ">=2.8.1"
},
"peerDependencies": {
"@angular/common": ">=16.0.0",
"@angular/core": ">=16.0.0",
"@angular/forms": ">=16.0.0",
"@maskito/angular": "^3.11.1",
"@maskito/core": "^3.11.1",
"@maskito/kit": "^3.11.1",
"@ng-web-apis/common": "^4.14.0",
"@taiga-ui/cdk": "^4.73.0",
"@taiga-ui/core": "^4.73.0",
"@taiga-ui/i18n": "^4.73.0",
"@taiga-ui/kit": "^4.73.0",
"@taiga-ui/polymorpheus": "^4.9.0",
"@angular/common": ">=19.0.0",
"@angular/core": ">=19.0.0",
"@angular/forms": ">=19.0.0",
"@maskito/angular": "^5.1.1",
"@maskito/core": "^5.1.1",
"@maskito/kit": "^5.1.1",
"@ng-web-apis/common": "^5.1.0",
"@taiga-ui/cdk": "^5.0.0-rc.4",
"@taiga-ui/core": "^5.0.0-rc.4",
"@taiga-ui/i18n": "^5.0.0-rc.4",
"@taiga-ui/kit": "^5.0.0-rc.4",
"@taiga-ui/polymorpheus": "^5.0.0",
"rxjs": ">=7.0.0"
}
},
"node_modules/@taiga-ui/addon-mobile": {
"version": "4.73.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-mobile/-/addon-mobile-4.73.0.tgz",
"integrity": "sha512-5ohDbwkE2ur57eMYmM6Tl7IOvVNxzPiHnZRB5ZcntmDkFp7UBgSOFptelqraL9DP8w5LEmvrshT6ObygRQNe+Q==",
"version": "5.0.0-rc.4",
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-mobile/-/addon-mobile-5.0.0-rc.4.tgz",
"integrity": "sha512-EkPCgOSnj7+SmgcQu4HVsmTNsBYiJ5SpuoktgxtZPmfeK4GXEmO1z6Pizotb3EHmyuyNBN69jeS4IPkdplvzsw==",
"license": "Apache-2.0",
"dependencies": {
"tslib": ">=2.8.1"
},
"peerDependencies": {
"@angular/cdk": ">=16.0.0",
"@angular/common": ">=16.0.0",
"@angular/core": ">=16.0.0",
"@ng-web-apis/common": "^4.14.0",
"@taiga-ui/cdk": "^4.73.0",
"@taiga-ui/core": "^4.73.0",
"@taiga-ui/kit": "^4.73.0",
"@taiga-ui/layout": "^4.73.0",
"@taiga-ui/polymorpheus": "^4.9.0",
"@angular/cdk": ">=19.0.0",
"@angular/common": ">=19.0.0",
"@angular/core": ">=19.0.0",
"@ng-web-apis/common": "^5.1.0",
"@ng-web-apis/platform": "^5.1.0",
"@taiga-ui/cdk": "^5.0.0-rc.4",
"@taiga-ui/core": "^5.0.0-rc.4",
"@taiga-ui/kit": "^5.0.0-rc.4",
"@taiga-ui/layout": "^5.0.0-rc.4",
"@taiga-ui/polymorpheus": "^5.0.0",
"rxjs": ">=7.0.0"
}
},
"node_modules/@taiga-ui/addon-table": {
"version": "4.73.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-table/-/addon-table-4.73.0.tgz",
"integrity": "sha512-fRxnCopvanInTdrVtNNXclY6T5u3fU87De564sngAcV9Y8Q1O44bZCyyDE/+jsoz3IIDZUD+2OEZjOGHMWLFOA==",
"version": "5.0.0-rc.4",
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-table/-/addon-table-5.0.0-rc.4.tgz",
"integrity": "sha512-j3ICBUoj4XKKvzzIbTQyLdif32+lHSlBozFDQcXhtL1x9C1mWOvidgU9kcsjygs0lEnjoIUTVza59sYMv3UKMQ==",
"license": "Apache-2.0",
"dependencies": {
"tslib": ">=2.8.1"
},
"peerDependencies": {
"@angular/common": ">=16.0.0",
"@angular/core": ">=16.0.0",
"@ng-web-apis/intersection-observer": "^4.14.0",
"@taiga-ui/cdk": "^4.73.0",
"@taiga-ui/core": "^4.73.0",
"@taiga-ui/i18n": "^4.73.0",
"@taiga-ui/kit": "^4.73.0",
"@taiga-ui/polymorpheus": "^4.9.0",
"@angular/common": ">=19.0.0",
"@angular/core": ">=19.0.0",
"@ng-web-apis/intersection-observer": "^5.1.0",
"@taiga-ui/cdk": "^5.0.0-rc.4",
"@taiga-ui/core": "^5.0.0-rc.4",
"@taiga-ui/i18n": "^5.0.0-rc.4",
"@taiga-ui/kit": "^5.0.0-rc.4",
"@taiga-ui/polymorpheus": "^5.0.0",
"rxjs": ">=7.0.0"
}
},
"node_modules/@taiga-ui/cdk": {
"version": "4.73.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/cdk/-/cdk-4.73.0.tgz",
"integrity": "sha512-bNRr5ORof60KevnMINNhbLbj8U9xgEMNvagzTtrtjVfEvCOJE1/+ufCnWjjkEdT/V0fDnbuhKsu16ngctbOhXQ==",
"version": "5.0.0-rc.4",
"resolved": "https://registry.npmjs.org/@taiga-ui/cdk/-/cdk-5.0.0-rc.4.tgz",
"integrity": "sha512-eZhqZvK8OE+alI5zJ3z0bQdufj77HCtREOC+Wsv5Fjz1mRZz4X19V3hpISfb6arp1m7kBfIscaPcZi0MuTcbUg==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"tslib": "2.8.1"
},
"optionalDependencies": {
"@angular-devkit/core": ">=16.0.0",
"@angular-devkit/schematics": ">=16.0.0",
"@schematics/angular": ">=16.0.0",
"@angular-devkit/core": ">=19.0.0",
"@angular-devkit/schematics": ">=19.0.0",
"@schematics/angular": ">=19.0.0",
"ng-morph": "^4.8.4",
"parse5": "^7.3.0"
},
"peerDependencies": {
"@angular/animations": ">=16.0.0",
"@angular/cdk": ">=16.0.0",
"@angular/common": ">=16.0.0",
"@angular/core": ">=16.0.0",
"@angular/forms": ">=16.0.0",
"@ng-web-apis/common": "^4.14.0",
"@ng-web-apis/mutation-observer": "^4.14.0",
"@ng-web-apis/platform": "^4.14.0",
"@ng-web-apis/resize-observer": "^4.14.0",
"@ng-web-apis/screen-orientation": "^4.14.0",
"@taiga-ui/event-plugins": "^4.7.0",
"@taiga-ui/font-watcher": "~0.3.0",
"@taiga-ui/polymorpheus": "^4.9.0",
"@angular/cdk": ">=19.0.0",
"@angular/common": ">=19.0.0",
"@angular/core": ">=19.0.0",
"@angular/forms": ">=19.0.0",
"@ng-web-apis/common": "^5.1.0",
"@ng-web-apis/mutation-observer": "^5.1.0",
"@ng-web-apis/platform": "^5.1.0",
"@ng-web-apis/resize-observer": "^5.1.0",
"@ng-web-apis/screen-orientation": "^5.1.0",
"@taiga-ui/event-plugins": "^5.0.0",
"@taiga-ui/font-watcher": "~0.5.0",
"@taiga-ui/polymorpheus": "^5.0.0",
"rxjs": ">=7.0.0"
}
},
@@ -4324,49 +4307,59 @@
}
},
"node_modules/@taiga-ui/core": {
"version": "4.73.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/core/-/core-4.73.0.tgz",
"integrity": "sha512-8EyHWgenkWxdfRP1LtqkE/bV/9a3mK8MtVmFxauEjcbeyafnE6MJ7K2KgD+iU9TZsOdH7nuWiA44Sv14t1wcUA==",
"version": "5.0.0-rc.4",
"resolved": "https://registry.npmjs.org/@taiga-ui/core/-/core-5.0.0-rc.4.tgz",
"integrity": "sha512-mO1W/4Y743pk3dDg539J3rbAJ5CzNDU+EBCAxYV4wOftpFeSJuCFYvATt6p8XGHtTRCDk6dNXQ1+fkR7PqQ9Yg==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"tslib": ">=2.8.1"
},
"peerDependencies": {
"@angular/animations": ">=16.0.0",
"@angular/common": ">=16.0.0",
"@angular/core": ">=16.0.0",
"@angular/forms": ">=16.0.0",
"@angular/platform-browser": ">=16.0.0",
"@angular/router": ">=16.0.0",
"@ng-web-apis/common": "^4.14.0",
"@ng-web-apis/mutation-observer": "^4.14.0",
"@taiga-ui/cdk": "^4.73.0",
"@taiga-ui/event-plugins": "^4.7.0",
"@taiga-ui/i18n": "^4.73.0",
"@taiga-ui/polymorpheus": "^4.9.0",
"@angular/common": ">=19.0.0",
"@angular/core": ">=19.0.0",
"@angular/forms": ">=19.0.0",
"@angular/platform-browser": ">=19.0.0",
"@angular/router": ">=19.0.0",
"@ng-web-apis/common": "^5.1.0",
"@ng-web-apis/mutation-observer": "^5.1.0",
"@ng-web-apis/platform": "^5.1.0",
"@taiga-ui/cdk": "^5.0.0-rc.4",
"@taiga-ui/event-plugins": "^5.0.0",
"@taiga-ui/i18n": "^5.0.0-rc.4",
"@taiga-ui/polymorpheus": "^5.0.0",
"@taiga-ui/styles": "^5.0.0-rc.4",
"rxjs": ">=7.0.0"
}
},
"node_modules/@taiga-ui/design-tokens": {
"version": "0.283.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/design-tokens/-/design-tokens-0.283.0.tgz",
"integrity": "sha512-fuLJYwY5a499hh4Cw+ABcO5bEdhcVP8l/1t6GQZApV6L1NfFQgmnf16vbtvGe/0TFCALpoydUGnELtZuwnrRiA==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
}
},
"node_modules/@taiga-ui/dompurify": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/@taiga-ui/dompurify/-/dompurify-4.1.11.tgz",
"integrity": "sha512-nwskeI/wFe+spuLQdkhVCn/GJBHJamxZ5deZuyto7C4I3O+pF8GaCNJdKM1YG5TZyGz932Qm8uNtBVZ/fZX8sQ==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@taiga-ui/dompurify/-/dompurify-5.0.1.tgz",
"integrity": "sha512-5OdVzGRMEiy1g/WZUtbPbBt3f7A1lGDWFOm9a4MZGZrGhONv2++uIKLDBkOQchY8ur09vo+QC/pBSRGCIgeQ+g==",
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.8.1"
},
"peerDependencies": {
"@angular/core": ">=16.0.0",
"@angular/platform-browser": ">=16.0.0",
"@types/dompurify": "3.0.5",
"dompurify": "3.1.7"
"@angular/core": ">=19.0.0",
"@angular/platform-browser": ">=19.0.0",
"dompurify": "^3.2.7"
}
},
"node_modules/@taiga-ui/event-plugins": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/event-plugins/-/event-plugins-4.7.0.tgz",
"integrity": "sha512-j3HPRPR7XxKxgMeytb+r/CNUoLBMVrfdfL8KJr1XiFO9jyEvoC4chFXDXWlkGyUHJIC6wy5VIXlIlI/kpqOiGg==",
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/event-plugins/-/event-plugins-5.0.0.tgz",
"integrity": "sha512-8AxIUn/lX4kwuDlIFmhElgBNtGDgQVC9/F+3O2A29IcMSt9693KRi8qKwToe9p3UuUsT9nnj5YeE11BtJMG0wA==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
@@ -4379,116 +4372,128 @@
}
},
"node_modules/@taiga-ui/experimental": {
"version": "4.73.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/experimental/-/experimental-4.73.0.tgz",
"integrity": "sha512-OFRO4rhWY780gf/iMip7sJJ1BBT2rWG5663Qqu/+6SAF4pyzcgKq5uIAkULQYFBQwqBNyB0zAx3fZAqdQ8m34A==",
"version": "5.0.0-rc.4",
"resolved": "https://registry.npmjs.org/@taiga-ui/experimental/-/experimental-5.0.0-rc.4.tgz",
"integrity": "sha512-/kw2uLivfeaNse8DkBABPNpTu3GiZ2R7JB0exBK0Flv0xZ3zZLRuIOMK2uHQqoc3v1zgQaFukXTz6CWuzUJYDA==",
"license": "Apache-2.0",
"dependencies": {
"tslib": ">=2.8.1"
},
"peerDependencies": {
"@angular/common": ">=16.0.0",
"@angular/core": ">=16.0.0",
"@taiga-ui/addon-commerce": "^4.73.0",
"@taiga-ui/cdk": "^4.73.0",
"@taiga-ui/core": "^4.73.0",
"@taiga-ui/kit": "^4.73.0",
"@taiga-ui/layout": "^4.73.0",
"@taiga-ui/polymorpheus": "^4.9.0",
"@angular/common": ">=19.0.0",
"@angular/core": ">=19.0.0",
"@taiga-ui/addon-commerce": "^5.0.0-rc.4",
"@taiga-ui/cdk": "^5.0.0-rc.4",
"@taiga-ui/core": "^5.0.0-rc.4",
"@taiga-ui/kit": "^5.0.0-rc.4",
"@taiga-ui/layout": "^5.0.0-rc.4",
"@taiga-ui/polymorpheus": "^5.0.0",
"rxjs": ">=7.0.0"
}
},
"node_modules/@taiga-ui/font-watcher": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/font-watcher/-/font-watcher-0.3.0.tgz",
"integrity": "sha512-ldI8XMvpVQEfxtcCzbLKs02QbqyB+qJKHIV/x19Q5mxs+kqrS3Pzm3j4mt8tPnuWhgi4+PHvAk3QLN9zmwcoJg==",
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/font-watcher/-/font-watcher-0.5.0.tgz",
"integrity": "sha512-9QBUh3XT6KOS+pKVy4ggr0CxgiZtbDYlterUNcfXw8cswkIkiUl5VGDQdvmta60iPHqOrOd/Z/7aAUom35vX/g==",
"license": "Apache-2.0",
"peer": true
},
"node_modules/@taiga-ui/i18n": {
"version": "4.73.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-4.73.0.tgz",
"integrity": "sha512-nU7pXeH3+mwQdWZCnXILDAZhRcQ4L/Gl+Y/G/yFpvkEfoCljhVsY0HZZgMSAEGfOSeS7R0ZhZX+9be6uHUvn4w==",
"version": "5.0.0-rc.4",
"resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-5.0.0-rc.4.tgz",
"integrity": "sha512-3hxhfewlY+5vsLPPl47rtlt47iVqnhDI2uD6waUm0ZqL/0YLhTEowkY2/DHhXoFp4+ZVcyTVRCWGf0G7BnJG+Q==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"tslib": ">=2.8.1"
},
"peerDependencies": {
"@angular/core": ">=16.0.0",
"@ng-web-apis/common": "^4.14.0",
"@angular/core": ">=19.0.0",
"@ng-web-apis/common": "^5.1.0",
"rxjs": ">=7.0.0"
}
},
"node_modules/@taiga-ui/icons": {
"version": "4.73.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/icons/-/icons-4.73.0.tgz",
"integrity": "sha512-Ae8qTTf18OlAyFgv04aDkQ6T81J0c8NtFtGWMbNnNc2/CtVt2s0e7z6dcAvs0LODNidzXU6jn/OQT4w4PeAe+w==",
"version": "5.0.0-rc.4",
"resolved": "https://registry.npmjs.org/@taiga-ui/icons/-/icons-5.0.0-rc.4.tgz",
"integrity": "sha512-2MZLcSf8x2+5yizWVdW9Ual6++zdzU64MXWeBx8zlNVyeedxTww5AFbVFs16ItaeT4YdZM0uZmRY59TmnCP9Rg==",
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.3.0"
}
},
"node_modules/@taiga-ui/kit": {
"version": "4.73.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/kit/-/kit-4.73.0.tgz",
"integrity": "sha512-8dypi4hZLi0GEFuraQERLNyKbR56BqQjU7c6p9x7X7iiMt4LpHUVH9+kd+e9irGWMr09SWLx4PFNV+q+T1c8TQ==",
"version": "5.0.0-rc.4",
"resolved": "https://registry.npmjs.org/@taiga-ui/kit/-/kit-5.0.0-rc.4.tgz",
"integrity": "sha512-MzK8tSzEl0dkhWcuKxmem/DSF+NnPvsNWJyGEx7hJO3IH0Ukcu806YfsSUld392U/7PZePAFRWKgMADdkQY29g==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"tslib": ">=2.8.1"
},
"peerDependencies": {
"@angular/common": ">=16.0.0",
"@angular/core": ">=16.0.0",
"@angular/forms": ">=16.0.0",
"@angular/router": ">=16.0.0",
"@maskito/angular": "^3.11.1",
"@maskito/core": "^3.11.1",
"@maskito/kit": "^3.11.1",
"@maskito/phone": "^3.11.1",
"@ng-web-apis/common": "^4.14.0",
"@ng-web-apis/intersection-observer": "^4.14.0",
"@ng-web-apis/mutation-observer": "^4.14.0",
"@ng-web-apis/resize-observer": "^4.14.0",
"@taiga-ui/cdk": "^4.73.0",
"@taiga-ui/core": "^4.73.0",
"@taiga-ui/i18n": "^4.73.0",
"@taiga-ui/polymorpheus": "^4.9.0",
"@angular/common": ">=19.0.0",
"@angular/core": ">=19.0.0",
"@angular/forms": ">=19.0.0",
"@angular/router": ">=19.0.0",
"@maskito/angular": "^5.1.1",
"@maskito/core": "^5.1.1",
"@maskito/kit": "^5.1.1",
"@maskito/phone": "^5.1.1",
"@ng-web-apis/common": "^5.1.0",
"@ng-web-apis/intersection-observer": "^5.1.0",
"@ng-web-apis/mutation-observer": "^5.1.0",
"@ng-web-apis/platform": "^5.1.0",
"@ng-web-apis/resize-observer": "^5.1.0",
"@taiga-ui/cdk": "^5.0.0-rc.4",
"@taiga-ui/core": "^5.0.0-rc.4",
"@taiga-ui/i18n": "^5.0.0-rc.4",
"@taiga-ui/polymorpheus": "^5.0.0",
"@taiga-ui/styles": "^5.0.0-rc.4",
"rxjs": ">=7.0.0"
}
},
"node_modules/@taiga-ui/layout": {
"version": "4.73.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/layout/-/layout-4.73.0.tgz",
"integrity": "sha512-JX2DRCdGw3ayvW07RtI7MjrceO7m7/0wj2RfkjT2Pa93elWooS85lDVy88tlVfcfm/bV2ZGwbemyecAwNjk/yQ==",
"version": "5.0.0-rc.4",
"resolved": "https://registry.npmjs.org/@taiga-ui/layout/-/layout-5.0.0-rc.4.tgz",
"integrity": "sha512-sKuksa89xxeIHgx3QchpTorySsjz+XMhcYi5ix/Mh/u+vgDxrcLCYGWAx9fcG/3mGTaCIIEMfaEQDs28jb9Ptw==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"tslib": ">=2.8.1"
},
"peerDependencies": {
"@angular/common": ">=16.0.0",
"@angular/core": ">=16.0.0",
"@taiga-ui/cdk": "^4.73.0",
"@taiga-ui/core": "^4.73.0",
"@taiga-ui/kit": "^4.73.0",
"@taiga-ui/polymorpheus": "^4.9.0",
"@angular/common": ">=19.0.0",
"@angular/core": ">=19.0.0",
"@taiga-ui/cdk": "^5.0.0-rc.4",
"@taiga-ui/core": "^5.0.0-rc.4",
"@taiga-ui/kit": "^5.0.0-rc.4",
"@taiga-ui/polymorpheus": "^5.0.0",
"rxjs": ">=7.0.0"
}
},
"node_modules/@taiga-ui/polymorpheus": {
"version": "4.9.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/polymorpheus/-/polymorpheus-4.9.0.tgz",
"integrity": "sha512-TbIIwslbEnxunKuL9OyPZdmefrvJEK6HYiADEKQHUMUs4Pk2UbhMckUieURo83yPDamk/Mww+Nu/g60J/4uh2w==",
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/polymorpheus/-/polymorpheus-5.0.0.tgz",
"integrity": "sha512-FRus7OgxYyRuETB17g1/YXYs8PAeF4x8+K4ZDfD3Ede8Vxv/XslAYZvf/Ro0wag6Uiy0kAP4bvSaIGPS1Y8Fyg==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"tslib": "^2.8.1"
},
"peerDependencies": {
"@angular/core": ">=16.0.0",
"@angular/platform-browser": ">=16.0.0"
"@angular/core": ">=19.0.0",
"@angular/platform-browser": ">=19.0.0"
}
},
"node_modules/@taiga-ui/styles": {
"version": "5.0.0-rc.4",
"resolved": "https://registry.npmjs.org/@taiga-ui/styles/-/styles-5.0.0-rc.4.tgz",
"integrity": "sha512-xAiHSlVFIv6Ti5BEE3AIkjt0zUW5jibeWnB+P7I9D7+E3vV0n/XlpX0SBHKK6DMdrVaPgURzNQnjSrwwtmkWxQ==",
"license": "Apache-2.0",
"peer": true,
"peerDependencies": {
"@taiga-ui/design-tokens": "~0.283.0"
}
},
"node_modules/@ts-morph/common": {
@@ -4622,15 +4627,12 @@
"tslib": "^2.4.0"
}
},
"node_modules/@types/dompurify": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz",
"integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==",
"node_modules/@types/dom-speech-recognition": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/@types/dom-speech-recognition/-/dom-speech-recognition-0.0.7.tgz",
"integrity": "sha512-NjiUoJbBlKhyufNsMZLSp+pbPNtPAFnR738RCJvtZy/HVQ2TZjmqpMyaeOSMXgxdfZM60nt8QGbtfmQrJAH2sw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/trusted-types": "*"
}
"peer": true
},
"node_modules/@types/estree": {
"version": "0.0.51",
@@ -4715,7 +4717,8 @@
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
"license": "MIT"
"license": "MIT",
"optional": true
},
"node_modules/@types/uuid": {
"version": "8.3.4",
@@ -6089,11 +6092,17 @@
}
},
"node_modules/dompurify": {
"version": "3.1.7",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.7.tgz",
"integrity": "sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==",
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.2.tgz",
"integrity": "sha512-6obghkliLdmKa56xdbLOpUZ43pAR6xFy1uOrxBaIDjT+yaRuuybLjGS9eVBoSR/UPU5fq3OXClEHLJNGvbxKpQ==",
"license": "(MPL-2.0 OR Apache-2.0)",
"peer": true
"peer": true,
"engines": {
"node": ">=20"
},
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
}
},
"node_modules/domutils": {
"version": "3.2.2",
@@ -8025,9 +8034,9 @@
}
},
"node_modules/libphonenumber-js": {
"version": "1.12.36",
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.36.tgz",
"integrity": "sha512-woWhKMAVx1fzzUnMCyOzglgSgf6/AFHLASdOBcchYCyvWSGWt12imw3iu2hdI5d4dGZRsNWAmWiz37sDKUPaRQ==",
"version": "1.12.38",
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.38.tgz",
"integrity": "sha512-vwzxmasAy9hZigxtqTbFEwp8ZdZ975TiqVDwj5bKx5sR+zi5ucUQy9mbVTkKM9GzqdLdxux/hTw2nmN5J7POMA==",
"license": "MIT",
"peer": true
},

View File

@@ -33,7 +33,6 @@
"format:check": "prettier --check projects/"
},
"dependencies": {
"@angular/animations": "^21.2.1",
"@angular/cdk": "^21.2.1",
"@angular/common": "^21.2.1",
"@angular/compiler": "^21.2.1",
@@ -48,19 +47,19 @@
"@noble/hashes": "^1.4.0",
"@start9labs/argon2": "^0.3.0",
"@start9labs/start-sdk": "file:../sdk/baseDist",
"@taiga-ui/addon-charts": "4.73.0",
"@taiga-ui/addon-commerce": "4.73.0",
"@taiga-ui/addon-mobile": "4.73.0",
"@taiga-ui/addon-table": "4.73.0",
"@taiga-ui/cdk": "4.73.0",
"@taiga-ui/core": "4.73.0",
"@taiga-ui/dompurify": "4.1.11",
"@taiga-ui/event-plugins": "4.7.0",
"@taiga-ui/experimental": "4.73.0",
"@taiga-ui/icons": "4.73.0",
"@taiga-ui/kit": "4.73.0",
"@taiga-ui/layout": "4.73.0",
"@taiga-ui/polymorpheus": "4.9.0",
"@taiga-ui/addon-charts": "5.0.0-rc.4",
"@taiga-ui/addon-commerce": "5.0.0-rc.4",
"@taiga-ui/addon-mobile": "5.0.0-rc.4",
"@taiga-ui/addon-table": "5.0.0-rc.4",
"@taiga-ui/cdk": "5.0.0-rc.4",
"@taiga-ui/core": "5.0.0-rc.4",
"@taiga-ui/dompurify": "5.0.1",
"@taiga-ui/event-plugins": "5.0.0",
"@taiga-ui/experimental": "5.0.0-rc.4",
"@taiga-ui/icons": "5.0.0-rc.4",
"@taiga-ui/kit": "5.0.0-rc.4",
"@taiga-ui/layout": "5.0.0-rc.4",
"@taiga-ui/polymorpheus": "5.0.0",
"ansi-to-html": "^0.7.2",
"base64-js": "^1.5.1",
"buffer": "^6.0.3",
@@ -70,7 +69,7 @@
"cron": "^2.2.0",
"cronstrue": "^2.21.0",
"deep-equality-data-structures": "1.5.1",
"dompurify": "^3.1.7",
"dompurify": "^3.3.2",
"fast-json-patch": "^3.1.1",
"fuse.js": "^6.4.6",
"jose": "^4.9.0",
@@ -95,7 +94,6 @@
"@angular/cli": "^21.2.1",
"@angular/compiler-cli": "^21.2.1",
"@angular/language-service": "^21.2.1",
"@types/dompurify": "3.0.5",
"@types/estree": "^0.0.51",
"@types/js-yaml": "^4.0.5",
"@types/marked": "^4.0.3",

View File

@@ -1,4 +1,4 @@
@use '@taiga-ui/core/styles/taiga-ui-local' as taiga;
@use '@taiga-ui/styles/utils' as taiga;
:host {
margin: 0;

View File

@@ -15,12 +15,9 @@ import { MarketplacePkgBase } from '../../../types'
selector: 'marketplace-dep-item',
template: `
<div class="outer-container">
<tui-avatar
appearance="action-grayscale"
class="dep-img"
size="l"
[src]="getImage(dep.key)"
/>
<span tuiAvatar appearance="action-grayscale" class="dep-img" size="l">
<img alt="" [src]="getImage(dep.key)" />
</span>
<div>
<tui-line-clamp [linesLimit]="2" [content]="titleContent" />
<ng-template #titleContent>
@@ -107,8 +104,10 @@ export class MarketplaceDepItemComponent {
dep!: KeyValue<string, T.DependencyMetadata>
getImage(key: string) {
const icon = this.pkg.dependencyMetadata[key]?.icon
return icon ? icon : 'assets/img/service-icons/fallback.png'
return (
this.pkg.dependencyMetadata[key]?.icon ||
'assets/img/service-icons/fallback.png'
)
}
getTitle(key: string): string {

View File

@@ -1,9 +1,8 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
import { RouterLink } from '@angular/router'
import { i18nPipe, TrustUrlPipe } from '@start9labs/shared'
import { TuiTitle } from '@taiga-ui/core'
import { TuiTitle, TuiCell } from '@taiga-ui/core'
import { TuiAvatar } from '@taiga-ui/kit'
import { TuiCell } from '@taiga-ui/layout'
import { MarketplacePkg } from '../../types'
@Component({
@@ -21,10 +20,9 @@ import { MarketplacePkg } from '../../types'
[queryParams]="{ id: pkg.id, flavor: pkg.flavor }"
queryParamsHandling="merge"
>
<tui-avatar
appearance="action-grayscale"
[src]="pkg.icon | trustUrl"
/>
<span tuiAvatar appearance="action-grayscale">
<img alt="" [src]="pkg.icon | trustUrl" />
</span>
<span tuiTitle>
{{ pkg.title }}
<span tuiSubtitle>{{ pkg.version }}</span>

View File

@@ -4,7 +4,7 @@ import {
inject,
Input,
} from '@angular/core'
import { TUI_IS_MOBILE } from '@taiga-ui/cdk'
import { WA_IS_MOBILE } from '@ng-web-apis/platform'
import { TuiButton, TuiDialogContext, TuiDialogService } from '@taiga-ui/core'
import { TuiCarousel } from '@taiga-ui/kit'
import { PolymorpheusContent } from '@taiga-ui/polymorpheus'
@@ -130,7 +130,7 @@ export class MarketplacePackageScreenshotComponent {
index = 0
isMobile = inject(TUI_IS_MOBILE)
isMobile = inject(WA_IS_MOBILE)
presentModalImg(content: PolymorpheusContent<TuiDialogContext>) {
this.dialogs

View File

@@ -8,7 +8,7 @@ import { StateService } from './services/state.service'
@Component({
selector: 'app-root',
template: '<tui-root tuiTheme="dark"><router-outlet /></tui-root>',
template: '<tui-root><router-outlet /></tui-root>',
imports: [TuiRoot, RouterOutlet],
})
export class AppComponent implements OnInit {

View File

@@ -10,7 +10,6 @@ import {
provideZoneChangeDetection,
signal,
} from '@angular/core'
import { provideAnimations } from '@angular/platform-browser/animations'
import {
PreloadAllModules,
provideRouter,
@@ -29,8 +28,10 @@ import {
import {
tuiButtonOptionsProvider,
tuiTextfieldOptionsProvider,
provideTaiga,
tuiHintOptionsProvider,
tuiDialogOptionsProvider,
} from '@taiga-ui/core'
import { provideEventPlugins } from '@taiga-ui/event-plugins'
import { ROUTES } from './app.routes'
import { ApiService } from './services/api.service'
@@ -47,8 +48,9 @@ const version = require('../../../../package.json').version
export const APP_CONFIG: ApplicationConfig = {
providers: [
provideZoneChangeDetection(),
provideAnimations(),
provideEventPlugins(),
provideTaiga({ mode: 'dark' }),
tuiHintOptionsProvider({ appearance: 'primary-grayscale' }),
tuiDialogOptionsProvider({ size: 's' }),
provideRouter(
ROUTES,
withDisabledInitialNavigation(),

View File

@@ -13,14 +13,10 @@ import {
TuiDialogContext,
TuiError,
TuiIcon,
TuiTextfield,
TuiInput,
tuiValidationErrorsProvider,
} from '@taiga-ui/core'
import {
TUI_VALIDATION_ERRORS,
TuiButtonLoading,
TuiFieldErrorPipe,
TuiPassword,
} from '@taiga-ui/kit'
import { TuiButtonLoading, TuiPassword } from '@taiga-ui/kit'
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { ApiService } from '../services/api.service'
import { StartOSDiskInfoWithId } from '../types'
@@ -36,42 +32,36 @@ export interface CifsResult {
<tui-textfield>
<label tuiLabel>{{ 'Hostname' | i18n }}*</label>
<input
tuiTextfield
tuiInput
formControlName="hostname"
placeholder="e.g. 'My Computer' OR 'my-computer.local'"
/>
</tui-textfield>
<tui-error
formControlName="hostname"
[error]="['required'] | tuiFieldError | async"
/>
<tui-error formControlName="hostname" [order]="['required']" />
<tui-textfield class="input">
<label tuiLabel>{{ 'Path' | i18n }}*</label>
<input
tuiTextfield
tuiInput
formControlName="path"
placeholder="/Desktop/my-folder"
/>
</tui-textfield>
<tui-error formControlName="path" [error]="[] | tuiFieldError | async" />
<tui-error formControlName="path" />
<tui-textfield class="input">
<label tuiLabel>{{ 'Username' | i18n }}*</label>
<input
tuiTextfield
tuiInput
formControlName="username"
placeholder="Enter username"
/>
</tui-textfield>
<tui-error
formControlName="username"
[error]="[] | tuiFieldError | async"
/>
<tui-error formControlName="username" />
<tui-textfield class="input">
<label tuiLabel>{{ 'Password' | i18n }}</label>
<input tuiTextfield type="password" formControlName="password" />
<input tuiInput type="password" formControlName="password" />
<tui-icon tuiPassword />
</tui-textfield>
@@ -107,20 +97,16 @@ export interface CifsResult {
ReactiveFormsModule,
TuiButton,
TuiButtonLoading,
TuiTextfield,
TuiInput,
TuiPassword,
TuiError,
TuiFieldErrorPipe,
TuiIcon,
i18nPipe,
],
providers: [
{
provide: TUI_VALIDATION_ERRORS,
useValue: {
required: 'This field is required',
},
},
tuiValidationErrorsProvider({
required: 'This field is required',
}),
],
})
export class CifsComponent {
@@ -183,10 +169,7 @@ export class CifsComponent {
this.dialogs
.openAlert(
'Unable to connect to network folder. Ensure (1) target computer is connected to LAN, (2) target folder is being shared, and (3) hostname, path, and credentials are accurate.',
{
label: 'Connection Failed',
size: 's',
},
{ label: 'Connection Failed' },
)
.subscribe()
}

View File

@@ -1,13 +1,19 @@
import { Component, inject } from '@angular/core'
import { Component } from '@angular/core'
import { i18nPipe } from '@start9labs/shared'
import { TuiButton } from '@taiga-ui/core'
import { TuiButton, TuiTitle } from '@taiga-ui/core'
import { TuiDialogContext } from '@taiga-ui/core'
import { injectContext } from '@taiga-ui/polymorpheus'
import { TuiHeader } from '@taiga-ui/layout'
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
@Component({
imports: [TuiButton, i18nPipe],
imports: [TuiButton, TuiHeader, TuiTitle, i18nPipe],
template: `
<p>{{ 'This drive contains existing StartOS data.' | i18n }}</p>
<header tuiHeader>
<hgroup tuiTitle>
<h2 [id]="context.id">{{ 'StartOS Data Detected' | i18n }}</h2>
<p>{{ 'This drive contains existing StartOS data.' | i18n }}</p>
</hgroup>
</header>
<ul>
<li>
<strong class="g-positive">{{ 'Preserve' | i18n }}</strong>
@@ -28,30 +34,19 @@ import { injectContext } from '@taiga-ui/polymorpheus'
</button>
<button
tuiButton
class="preserve-btn"
appearance=""
[style.background]="'var(--tui-status-positive)'"
(click)="context.completeWith(true)"
>
{{ 'Preserve' | i18n }}
</button>
</footer>
`,
styles: `
p {
margin: 0 0 0.75rem;
}
footer {
display: flex;
margin-top: 2rem;
gap: 0.5rem;
flex-direction: column-reverse;
}
.preserve-btn {
background: var(--tui-status-positive) !important;
}
`,
})
export class PreserveOverwriteDialog {
protected readonly context = injectContext<TuiDialogContext<boolean>>()
}
export const PRESERVE_OVERWRITE = new PolymorpheusComponent(
PreserveOverwriteDialog,
)

View File

@@ -1,8 +1,9 @@
import { Component, inject } from '@angular/core'
import { Component } from '@angular/core'
import { FormsModule } from '@angular/forms'
import { i18nPipe } from '@start9labs/shared'
import { TuiDialogContext, TuiTextfield } from '@taiga-ui/core'
import { TuiDialogContext, TuiTitle } from '@taiga-ui/core'
import { TuiDataListWrapper, TuiSelect } from '@taiga-ui/kit'
import { TuiHeader } from '@taiga-ui/layout'
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { StartOSDiskInfoWithId } from '../types'
@@ -11,36 +12,48 @@ interface Data {
}
@Component({
imports: [FormsModule, TuiTextfield, TuiSelect, TuiDataListWrapper, i18nPipe],
imports: [
FormsModule,
TuiSelect,
TuiDataListWrapper,
i18nPipe,
TuiHeader,
TuiTitle,
],
template: `
<p>{{ 'Multiple backups found. Select which one to restore.' | i18n }}</p>
<header tuiHeader>
<hgroup tuiTitle>
<h2 [id]="context.id">{{ 'Select Network Backup' | i18n }}</h2>
<p>
{{ 'Multiple backups found. Select which one to restore.' | i18n }}
</p>
</hgroup>
</header>
<tui-textfield [stringify]="stringify">
<label tuiLabel>{{ 'Backups' | i18n }}</label>
<input tuiSelect [(ngModel)]="selectedServer" />
<tui-data-list-wrapper
new
*tuiTextfieldDropdown
*tuiDropdown
[items]="context.data.servers"
[itemContent]="serverContent"
/>
</tui-textfield>
<ng-template #serverContent let-server>
<div class="server-item">
<span>{{ server.id }}</span>
<span tuiTitle>
{{ server.id }}
<!-- @TODO eos-version? -->
<small>{{ server['eos-version'] }}</small>
</div>
@if (server['eos-version']) {
<span tuiSubtitle>
{{ server['eos-version'] }}
</span>
}
</span>
</ng-template>
`,
styles: `
.server-item {
display: flex;
flex-direction: column;
small {
opacity: 0.7;
}
div {
margin-block-end: 1rem;
}
`,
})

View File

@@ -1,32 +1,27 @@
import { Component } from '@angular/core'
import { FormsModule } from '@angular/forms'
import { i18nPipe } from '@start9labs/shared'
import {
TuiButton,
TuiDialogContext,
TuiIcon,
TuiTextfield,
} from '@taiga-ui/core'
import { TuiButton, TuiDialogContext, TuiIcon, TuiInput } from '@taiga-ui/core'
import { TuiPassword } from '@taiga-ui/kit'
import { injectContext } from '@taiga-ui/polymorpheus'
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
@Component({
imports: [
FormsModule,
TuiButton,
TuiTextfield,
TuiPassword,
TuiIcon,
i18nPipe,
],
imports: [FormsModule, TuiButton, TuiInput, TuiPassword, TuiIcon, i18nPipe],
template: `
<p>
{{ 'Enter the password that was used to encrypt this backup.' | i18n }}
</p>
<header tuiHeader>
<hgroup tuiTitle>
<h2 [id]="context.id">{{ 'Unlock Backup' | i18n }}</h2>
<p>
{{
'Enter the password that was used to encrypt this backup.' | i18n
}}
</p>
</hgroup>
</header>
<tui-textfield>
<label tuiLabel>{{ 'Password' | i18n }}</label>
<input
tuiTextfield
tuiInput
type="password"
[(ngModel)]="password"
(keyup.enter)="unlock()"
@@ -62,3 +57,5 @@ export class UnlockPasswordDialog {
}
}
}
export const UNLOCK_PASSWORD = new PolymorpheusComponent(UnlockPasswordDialog)

View File

@@ -12,24 +12,29 @@ import {
ErrorService,
i18nKey,
i18nPipe,
LoadingService,
toGuid,
} from '@start9labs/shared'
import { TUI_IS_MOBILE } from '@taiga-ui/cdk'
import { WA_IS_MOBILE } from '@ng-web-apis/platform'
import {
TuiButton,
TuiIcon,
TuiLoader,
TuiTextfield,
TuiInput,
TuiNotification,
TuiTitle,
} from '@taiga-ui/core'
import { TuiDataListWrapper, TuiSelect, TuiTooltip } from '@taiga-ui/kit'
import {
TuiDataListWrapper,
TuiNotificationMiddleService,
TuiSelect,
TuiTooltip,
} from '@taiga-ui/kit'
import { TuiCardLarge, TuiHeader } from '@taiga-ui/layout'
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { filter, Subscription } from 'rxjs'
import { ApiService } from '../services/api.service'
import { StateService } from '../services/state.service'
import { PreserveOverwriteDialog } from '../components/preserve-overwrite.dialog'
import { PRESERVE_OVERWRITE } from '../components/preserve-overwrite.dialog'
@Component({
template: `
@@ -42,7 +47,7 @@ import { PreserveOverwriteDialog } from '../components/preserve-overwrite.dialog
@if (loading) {
<tui-loader />
} @else if (drives.length === 0) {
<p class="no-drives">
<p tuiNotification size="m" appearance="warning">
{{
'No drives found. Please connect a drive and click Refresh.'
| i18n
@@ -70,8 +75,7 @@ import { PreserveOverwriteDialog } from '../components/preserve-overwrite.dialog
}
@if (!mobile) {
<tui-data-list-wrapper
new
*tuiTextfieldDropdown
*tuiDropdown
[items]="drives"
[itemContent]="driveContent"
/>
@@ -100,36 +104,27 @@ import { PreserveOverwriteDialog } from '../components/preserve-overwrite.dialog
}
@if (!mobile) {
<tui-data-list-wrapper
new
*tuiTextfieldDropdown
*tuiDropdown
[items]="drives"
[itemContent]="driveContent"
/>
}
@if (preserveData === true) {
<tui-icon
icon="@tui.database"
style="color: var(--tui-status-positive); pointer-events: none"
/>
<tui-icon icon="@tui.database" class="g-positive" />
}
@if (preserveData === false) {
<tui-icon
icon="@tui.database-zap"
style="color: var(--tui-status-negative); pointer-events: none"
/>
<tui-icon icon="@tui.database-zap" class="g-negative" />
}
<tui-icon [tuiTooltip]="dataDriveTooltip" />
</tui-textfield>
<ng-template #driveContent let-drive>
<div class="drive-item">
<span class="drive-name">
{{ driveName(drive) }}
</span>
<small>
<span tuiTitle>
{{ driveName(drive) }}
<span tuiSubtitle>
{{ formatCapacity(drive.capacity) }} · {{ drive.logicalname }}
</small>
</div>
</span>
</span>
</ng-template>
}
@@ -152,19 +147,8 @@ import { PreserveOverwriteDialog } from '../components/preserve-overwrite.dialog
}
`,
styles: `
.no-drives {
text-align: center;
color: var(--tui-text-secondary);
padding: 2rem;
}
.drive-item {
display: flex;
flex-direction: column;
small {
opacity: 0.7;
}
tui-icon:not([tuiTooltip]) {
pointer-events: none;
}
`,
imports: [
@@ -173,7 +157,8 @@ import { PreserveOverwriteDialog } from '../components/preserve-overwrite.dialog
TuiButton,
TuiIcon,
TuiLoader,
TuiTextfield,
TuiInput,
TuiNotification,
TuiSelect,
TuiDataListWrapper,
TuiTooltip,
@@ -186,13 +171,13 @@ export default class DrivesPage {
private readonly api = inject(ApiService)
private readonly router = inject(Router)
private readonly dialogs = inject(DialogService)
private readonly loader = inject(LoadingService)
private readonly loader = inject(TuiNotificationMiddleService)
private readonly errorService = inject(ErrorService)
private readonly stateService = inject(StateService)
private readonly cdr = inject(ChangeDetectorRef)
private readonly i18n = inject(i18nPipe)
protected readonly mobile = inject(TUI_IS_MOBILE)
protected readonly mobile = inject(WA_IS_MOBILE)
@HostListener('document:keydown', ['$event'])
onKeydown(event: KeyboardEvent) {
@@ -308,38 +293,27 @@ export default class DrivesPage {
private showPreserveOverwriteDialog() {
let selectionMade = false
this.dialogs
.openComponent<boolean>(
new PolymorpheusComponent(PreserveOverwriteDialog),
{
label: 'StartOS Data Detected',
size: 's',
dismissible: true,
closeable: true,
},
)
.subscribe({
next: preserve => {
selectionMade = true
this.preserveData = preserve
this.dialogs.openComponent<boolean>(PRESERVE_OVERWRITE).subscribe({
next: preserve => {
selectionMade = true
this.preserveData = preserve
this.cdr.markForCheck()
},
complete: () => {
if (!selectionMade) {
// Dialog was dismissed without selection - clear the data drive
this.selectedDataDrive = null
this.preserveData = null
this.cdr.markForCheck()
},
complete: () => {
if (!selectionMade) {
// Dialog was dismissed without selection - clear the data drive
this.selectedDataDrive = null
this.preserveData = null
this.cdr.markForCheck()
}
},
})
}
},
})
}
private showOsDriveWarning() {
this.dialogs
.openConfirm({
label: 'Warning',
size: 's',
data: {
content: `<ul>
<li class="g-negative">${this.i18n.transform('Data on the OS drive may be overwritten.')}</li>
@@ -363,7 +337,6 @@ export default class DrivesPage {
this.dialogs
.openConfirm({
label: 'Warning',
size: 's',
data: {
content: message as i18nKey,
yes: 'Continue',
@@ -397,10 +370,9 @@ export default class DrivesPage {
this.dialogSub = this.dialogs
.openAlert('StartOS has been installed successfully.', {
label: 'Installation Complete!',
size: 's',
dismissible: false,
closeable: true,
data: { button: this.i18n.transform('Continue to Setup') },
closable: true,
data: this.i18n.transform('Continue to Setup'),
})
.subscribe({
complete: () => {

View File

@@ -1,9 +1,9 @@
import { Component, inject } from '@angular/core'
import { Router } from '@angular/router'
import { i18nPipe } from '@start9labs/shared'
import { TuiAppearance, TuiTitle } from '@taiga-ui/core'
import { TuiTitle, TuiCell } from '@taiga-ui/core'
import { TuiAvatar } from '@taiga-ui/kit'
import { TuiCardLarge, TuiCell, TuiHeader } from '@taiga-ui/layout'
import { TuiCardLarge, TuiHeader } from '@taiga-ui/layout'
import { StateService } from '../services/state.service'
@Component({
@@ -14,7 +14,7 @@ import { StateService } from '../services/state.service'
</header>
<button tuiCell="l" (click)="startFresh()">
<tui-avatar appearance="positive" src="@tui.plus" />
<span tuiAvatar="@tui.plus" appearance="positive"></span>
<div tuiTitle>
{{ 'Start Fresh' | i18n }}
<div tuiSubtitle>{{ 'Set up a brand new server' | i18n }}</div>
@@ -22,7 +22,7 @@ import { StateService } from '../services/state.service'
</button>
<button tuiCell="l" (click)="restore()">
<tui-avatar appearance="warning" src="@tui.archive-restore" />
<span tuiAvatar="@tui.archive-restore" appearance="warning"></span>
<div tuiTitle>
{{ 'Restore from Backup' | i18n }}
<div tuiSubtitle>
@@ -32,7 +32,7 @@ import { StateService } from '../services/state.service'
</button>
<button tuiCell="l" (click)="transfer()">
<tui-avatar appearance="info" src="@tui.hard-drive-download" />
<span tuiAvatar="@tui.hard-drive-download" appearance="info"></span>
<div tuiTitle>
{{ 'Transfer' | i18n }}
<div tuiSubtitle>
@@ -42,15 +42,7 @@ import { StateService } from '../services/state.service'
</button>
</div>
`,
imports: [
TuiAppearance,
TuiCardLarge,
TuiHeader,
TuiCell,
TuiTitle,
TuiAvatar,
i18nPipe,
],
imports: [TuiCardLarge, TuiHeader, TuiCell, TuiTitle, TuiAvatar, i18nPipe],
})
export default class HomePage {
private readonly router = inject(Router)

View File

@@ -6,8 +6,8 @@ import {
Keyboard,
LanguageCode,
} from '@start9labs/shared'
import { TUI_IS_MOBILE } from '@taiga-ui/cdk'
import { TuiButton, TuiTextfield, TuiTitle } from '@taiga-ui/core'
import { WA_IS_MOBILE } from '@ng-web-apis/platform'
import { TuiButton, TuiTitle } from '@taiga-ui/core'
import {
TuiButtonLoading,
TuiChevron,
@@ -36,11 +36,7 @@ import { StateService } from '../services/state.service'
<input tuiSelect [(ngModel)]="selected" />
}
@if (!mobile) {
<tui-data-list-wrapper
new
*tuiTextfieldDropdown
[items]="keyboards"
/>
<tui-data-list-wrapper *tuiDropdown [items]="keyboards" />
}
</tui-textfield>
@@ -61,7 +57,6 @@ import { StateService } from '../services/state.service'
TuiCardLarge,
TuiButton,
TuiButtonLoading,
TuiTextfield,
TuiChevron,
TuiSelect,
TuiDataListWrapper,
@@ -74,7 +69,7 @@ export default class KeyboardPage {
private readonly api = inject(ApiService)
private readonly stateService = inject(StateService)
protected readonly mobile = inject(TUI_IS_MOBILE)
protected readonly mobile = inject(WA_IS_MOBILE)
// All keyboards, with language-specific keyboards at the top
readonly keyboards = getAllKeyboardsSorted(
this.stateService.language as LanguageCode,

View File

@@ -2,9 +2,10 @@ import { Component, computed, inject, signal } from '@angular/core'
import { Router } from '@angular/router'
import { FormsModule } from '@angular/forms'
import { i18nPipe, i18nService, Language, LANGUAGES } from '@start9labs/shared'
import { TUI_IS_MOBILE } from '@taiga-ui/cdk'
import { TuiButton, TuiTextfield, TuiTitle } from '@taiga-ui/core'
import { WA_IS_MOBILE } from '@ng-web-apis/platform'
import { TuiButton, TuiCell, TuiTitle } from '@taiga-ui/core'
import {
TuiAvatar,
TuiButtonLoading,
TuiChevron,
TuiDataListWrapper,
@@ -18,13 +19,15 @@ import { StateService } from '../services/state.service'
template: `
<section tuiCardLarge="compact">
<header tuiHeader>
<h2 tuiTitle>
<span class="inline-title">
<img src="assets/img/icon.png" alt="Start9" />
<hgroup tuiTitle>
<h2 tuiCell="m">
<span tuiAvatar>
<img src="assets/img/icon.png" alt="Start9" />
</span>
{{ 'Welcome to' | i18n }} StartOS
</span>
<span tuiSubtitle>{{ 'Select your language' | i18n }}</span>
</h2>
</h2>
<p tuiSubtitle>{{ 'Select your language' | i18n }}</p>
</hgroup>
</header>
<tui-textfield
tuiChevron
@@ -48,8 +51,7 @@ import { StateService } from '../services/state.service'
}
@if (!mobile) {
<tui-data-list-wrapper
*tuiTextfieldDropdown
new
*tuiDropdown
[items]="languages"
[itemContent]="itemContent"
/>
@@ -57,11 +59,10 @@ import { StateService } from '../services/state.service'
</tui-textfield>
<ng-template #itemContent let-item>
@let lang = asLanguage(item);
<div class="language-item">
<span>{{ lang.nativeName }}</span>
<small>{{ lang.name | i18n }}</small>
</div>
<span tuiTitle>
{{ asLanguage(item).nativeName }}
<span tuiSubtitle>{{ asLanguage(item).name | i18n }}</span>
</span>
</ng-template>
<footer>
@@ -76,22 +77,13 @@ import { StateService } from '../services/state.service'
</footer>
</section>
`,
styles: `
.language-item {
display: flex;
flex-direction: column;
small {
opacity: 0.7;
}
}
`,
imports: [
FormsModule,
TuiCardLarge,
TuiButton,
TuiButtonLoading,
TuiTextfield,
TuiAvatar,
TuiCell,
TuiChevron,
TuiSelect,
TuiDataListWrapper,
@@ -106,7 +98,7 @@ export default class LanguagePage {
private readonly stateService = inject(StateService)
private readonly i18nService = inject(i18nService)
protected readonly mobile = inject(TUI_IS_MOBILE)
protected readonly mobile = inject(WA_IS_MOBILE)
readonly languages = LANGUAGES
selected =

View File

@@ -12,10 +12,10 @@ import {
getErrorMessage,
i18nPipe,
InitializingComponent,
LoadingService,
} from '@start9labs/shared'
import { T } from '@start9labs/start-sdk'
import { TuiButton } from '@taiga-ui/core'
import { TuiNotificationMiddleService } from '@taiga-ui/kit'
import {
catchError,
filter,
@@ -64,7 +64,7 @@ import { StateService } from '../services/state.service'
})
export default class LoadingPage {
private readonly api = inject(ApiService)
private readonly loader = inject(LoadingService)
private readonly loader = inject(TuiNotificationMiddleService)
private readonly dialog = inject(DialogService)
private readonly router = inject(Router)

View File

@@ -1,4 +1,3 @@
import { AsyncPipe } from '@angular/common'
import { Component, inject } from '@angular/core'
import { Router } from '@angular/router'
import {
@@ -8,31 +7,28 @@ import {
ReactiveFormsModule,
Validators,
} from '@angular/forms'
import {
ErrorService,
i18nPipe,
LoadingService,
normalizeHostname,
} from '@start9labs/shared'
import { ErrorService, i18nPipe, normalizeHostname } from '@start9labs/shared'
import { TuiAutoFocus, TuiMapperPipe, TuiValidator } from '@taiga-ui/cdk'
import {
TuiButton,
TuiError,
TuiIcon,
TuiTextfield,
TuiTitle,
} from '@taiga-ui/core'
import {
TuiFieldErrorPipe,
TuiPassword,
TuiInput,
tuiValidationErrorsProvider,
} from '@taiga-ui/kit'
import { TuiCardLarge, TuiHeader } from '@taiga-ui/layout'
} from '@taiga-ui/core'
import { TuiNotificationMiddleService, TuiPassword } from '@taiga-ui/kit'
import { TuiCardLarge, TuiForm, TuiHeader } from '@taiga-ui/layout'
import { StateService } from '../services/state.service'
@Component({
template: `
<section tuiCardLarge="compact">
<form
tuiCardLarge="compact"
tuiForm
[formGroup]="form"
(ngSubmit)="submit()"
>
<header tuiHeader>
<h2 tuiTitle>
{{
@@ -43,104 +39,80 @@ import { StateService } from '../services/state.service'
</h2>
</header>
<form [formGroup]="form" (ngSubmit)="submit()">
@if (isFresh) {
<tui-textfield>
<label tuiLabel>{{ 'Server Name' | i18n }}</label>
<input tuiTextfield tuiAutoFocus formControlName="name" />
</tui-textfield>
<tui-error
formControlName="name"
[error]="[] | tuiFieldError | async"
/>
@if (form.controls.name.value?.trim()) {
<p class="hostname-preview">{{ derivedHostname }}.local</p>
}
@if (isFresh) {
<tui-textfield>
<label tuiLabel>{{ 'Server Name' | i18n }}</label>
<input tuiInput tuiAutoFocus formControlName="name" />
</tui-textfield>
<tui-error formControlName="name" />
@if (form.controls.name.value?.trim()) {
<tui-error class="g-secondary" error="{{ derivedHostname }}.local" />
}
}
<tui-textfield [style.margin-top.rem]="isFresh ? 1 : 0">
<label tuiLabel>
{{ isFresh ? ('Password' | i18n) : ('New Password' | i18n) }}
</label>
<input
tuiTextfield
type="password"
[tuiAutoFocus]="!isFresh"
maxlength="64"
formControlName="password"
/>
<tui-icon tuiPassword />
</tui-textfield>
<tui-error
<tui-textfield>
<label tuiLabel>
{{ isFresh ? ('Password' | i18n) : ('New Password' | i18n) }}
</label>
<input
tuiInput
type="password"
[tuiAutoFocus]="!isFresh"
maxlength="64"
formControlName="password"
[error]="[] | tuiFieldError | async"
/>
<tui-icon tuiPassword />
</tui-textfield>
<tui-error formControlName="password" />
<tui-textfield [style.margin-top.rem]="1">
<label tuiLabel>{{ 'Confirm Password' | i18n }}</label>
<input
tuiTextfield
type="password"
formControlName="confirm"
[tuiValidator]="
form.controls.password.value || '' | tuiMapper: validator
"
/>
<tui-icon tuiPassword />
</tui-textfield>
<tui-error
<tui-textfield>
<label tuiLabel>{{ 'Confirm Password' | i18n }}</label>
<input
tuiInput
type="password"
formControlName="confirm"
[error]="[] | tuiFieldError | async"
[tuiValidator]="
form.controls.password.value || '' | tuiMapper: validator
"
/>
<tui-icon tuiPassword />
</tui-textfield>
<tui-error formControlName="confirm" />
<footer>
<footer>
<button
tuiButton
size="m"
[disabled]="
isFresh
? form.invalid
: form.controls.password.value && form.invalid
"
>
{{ 'Finish' | i18n }}
</button>
@if (!isFresh) {
<button
tuiButton
[disabled]="
isFresh
? form.invalid
: form.controls.password.value && form.invalid
"
size="m"
appearance="secondary"
type="button"
(click)="skip()"
>
{{ 'Finish' | i18n }}
{{ 'Skip' | i18n }}
</button>
@if (!isFresh) {
<button
tuiButton
appearance="secondary"
type="button"
(click)="skip()"
>
{{ 'Skip' | i18n }}
</button>
}
</footer>
</form>
</section>
`,
styles: `
.hostname-preview {
color: var(--tui-text-secondary);
font: var(--tui-font-text-s);
margin-top: 0.25rem;
}
footer {
display: flex;
flex-direction: column;
gap: 1rem;
margin-top: 1.5rem;
}
}
</footer>
</form>
`,
imports: [
AsyncPipe,
ReactiveFormsModule,
TuiCardLarge,
TuiButton,
TuiError,
TuiAutoFocus,
TuiFieldErrorPipe,
TuiTextfield,
TuiInput,
TuiForm,
TuiPassword,
TuiValidator,
TuiIcon,
@@ -160,7 +132,7 @@ import { StateService } from '../services/state.service'
})
export default class PasswordPage {
private readonly router = inject(Router)
private readonly loader = inject(LoadingService)
private readonly loader = inject(TuiNotificationMiddleService)
private readonly errorService = inject(ErrorService)
private readonly stateService = inject(StateService)
private readonly i18n = inject(i18nPipe)

View File

@@ -6,11 +6,11 @@ import {
TuiButton,
TuiDataList,
TuiDropdown,
TuiIcon,
TuiLink,
TuiLoader,
TuiOptGroup,
TuiTitle,
} from '@taiga-ui/core'
import { TuiChevron } from '@taiga-ui/kit'
import { TuiCardLarge, TuiHeader } from '@taiga-ui/layout'
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { ApiService } from '../services/api.service'
@@ -18,22 +18,25 @@ import { StateService } from '../services/state.service'
import { StartOSDiskInfoFull, StartOSDiskInfoWithId } from '../types'
import { CIFS, CifsResult } from '../components/cifs.component'
import { SELECT_NETWORK_BACKUP } from '../components/select-network-backup.dialog'
import { UnlockPasswordDialog } from '../components/unlock-password.dialog'
import { UNLOCK_PASSWORD } from '../components/unlock-password.dialog'
@Component({
template: `
<section tuiCardLarge="compact">
<header tuiHeader>
<h2 tuiTitle>
{{ 'Select Backup' | i18n }}
<span tuiSubtitle>
<hgroup tuiTitle>
<h2>{{ 'Select Backup' | i18n }}</h2>
<p tuiSubtitle>
{{ 'Select the StartOS backup you want to restore' | i18n }}
<a class="refresh" (click)="refresh()">
<tui-icon icon="@tui.rotate-cw" />
{{ 'Refresh' | i18n }}
</a>
</span>
</h2>
<button
tuiLink
appearance="action"
iconEnd="@tui.rotate-cw"
[textContent]="'Refresh' | i18n"
(click)="refresh()"
></button>
</p>
</hgroup>
</header>
@if (loading) {
@@ -41,82 +44,50 @@ import { UnlockPasswordDialog } from '../components/unlock-password.dialog'
} @else {
<button
tuiButton
iconEnd="@tui.chevron-down"
[tuiDropdown]="dropdown"
[tuiDropdownLimitWidth]="'fixed'"
tuiChevron
tuiDropdown
tuiDropdownLimitWidth="fixed"
[(tuiDropdownOpen)]="open"
style="width: 100%"
>
{{ 'Select Backup' | i18n }}
</button>
<ng-template #dropdown>
<tui-data-list>
<tui-opt-group>
<button tuiOption new (click)="openCifs()">
<tui-icon icon="@tui.folder-plus" />
{{ 'Open Network Backup' | i18n }}
</button>
</tui-opt-group>
<tui-data-list *tuiDropdown>
<button tuiOption iconStart="@tui.folder-plus" (click)="openCifs()">
{{ 'Open Network Backup' | i18n }}
</button>
<hr />
<tui-opt-group [label]="'Physical Backups' | i18n">
@for (server of physicalServers; track server.id) {
<button tuiOption new (click)="selectPhysicalBackup(server)">
<div class="server-item">
<span>{{ server.id }}</span>
<small>
<button tuiOption (click)="selectPhysicalBackup(server)">
<span tuiTitle>
{{ server.id }}
<span tuiSubtitle>
{{ server.drive.vendor }} {{ server.drive.model }} ·
{{ server.partition.logicalname }}
</small>
</div>
</span>
</span>
</button>
} @empty {
<div class="no-items">{{ 'No physical backups' | i18n }}</div>
<button tuiOption [disabled]="true">
{{ 'No physical backups' | i18n }}
</button>
}
</tui-opt-group>
</tui-data-list>
</ng-template>
</button>
}
</section>
`,
styles: `
.refresh {
display: inline-flex;
align-items: center;
gap: 0.25rem;
cursor: pointer;
color: var(--tui-text-action);
tui-icon {
font-size: 0.875rem;
}
}
.server-item {
display: flex;
flex-direction: column;
small {
opacity: 0.7;
}
}
.no-items {
padding: 0.5rem 0.75rem;
color: var(--tui-text-secondary);
font-style: italic;
}
`,
imports: [
TuiButton,
TuiCardLarge,
TuiDataList,
TuiDropdown,
TuiLoader,
TuiIcon,
TuiOptGroup,
TuiTitle,
TuiHeader,
i18nPipe,
TuiLink,
TuiChevron,
],
})
export default class RestorePage {
@@ -142,10 +113,7 @@ export default class RestorePage {
openCifs() {
this.open = false
this.dialogs
.openComponent<CifsResult>(CIFS, {
label: 'Connect Network Folder',
size: 's',
})
.openComponent<CifsResult>(CIFS, { label: 'Connect Network Folder' })
.subscribe(result => {
if (result) {
this.handleCifsResult(result)
@@ -167,7 +135,7 @@ export default class RestorePage {
type: 'cifs',
...result.cifs,
})
} else if (result.servers.length > 1) {
} else {
this.showSelectNetworkBackupDialog(result.cifs, result.servers)
}
}
@@ -178,8 +146,6 @@ export default class RestorePage {
) {
this.dialogs
.openComponent<StartOSDiskInfoWithId | null>(SELECT_NETWORK_BACKUP, {
label: 'Select Network Backup',
size: 's',
data: { servers },
})
.subscribe(server => {
@@ -194,13 +160,7 @@ export default class RestorePage {
target: { type: 'disk'; logicalname: string } | ({ type: 'cifs' } & T.Cifs),
) {
this.dialogs
.openComponent<string | null>(
new PolymorpheusComponent(UnlockPasswordDialog),
{
label: 'Unlock Backup',
size: 's',
},
)
.openComponent<string | null>(UNLOCK_PASSWORD)
.subscribe(password => {
if (password) {
this.stateService.recoverySource = {

View File

@@ -12,9 +12,9 @@ import {
ErrorService,
i18nPipe,
} from '@start9labs/shared'
import { TuiIcon, TuiLoader, TuiTitle } from '@taiga-ui/core'
import { TuiIcon, TuiLoader, TuiTitle, TuiCell } from '@taiga-ui/core'
import { TuiAvatar } from '@taiga-ui/kit'
import { TuiCardLarge, TuiCell, TuiHeader } from '@taiga-ui/layout'
import { TuiCardLarge, TuiHeader } from '@taiga-ui/layout'
import { ApiService } from '../services/api.service'
import { StateService } from '../services/state.service'
import { DocumentationComponent } from '../components/documentation.component'
@@ -50,7 +50,7 @@ import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
<!-- Step: Download Address Info (non-kiosk only) -->
@if (!stateService.kiosk) {
<button tuiCell="l" [disabled]="downloaded" (click)="download()">
<tui-avatar appearance="secondary" src="@tui.download" />
<span tuiAvatar="@tui.download" appearance="secondary"></span>
<div tuiTitle>
{{ 'Download Address Info' | i18n }}
<div tuiSubtitle>
@@ -74,7 +74,7 @@ import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
[disabled]="(!stateService.kiosk && !downloaded) || usbRemoved"
(click)="removeMedia()"
>
<tui-avatar appearance="secondary" src="@tui.usb" />
<span tuiAvatar="@tui.usb" appearance="secondary"></span>
<div tuiTitle>
{{ 'Remove Installation Media' | i18n }}
<div tuiSubtitle>
@@ -96,7 +96,7 @@ import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
[disabled]="!usbRemoved || rebooted || rebooting"
(click)="reboot()"
>
<tui-avatar appearance="secondary" src="@tui.rotate-cw" />
<span tuiAvatar="@tui.rotate-cw" appearance="secondary"></span>
<div tuiTitle>
{{ 'Restart Server' | i18n }}
<div tuiSubtitle>
@@ -125,7 +125,7 @@ import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
[disabled]="!canOpenAddress"
(click)="openLocalAddress()"
>
<tui-avatar appearance="secondary" src="@tui.external-link" />
<span tuiAvatar="@tui.external-link" appearance="secondary"></span>
<div tuiTitle>
{{ 'Open Local Address' | i18n }}
<div tuiSubtitle>{{ lanAddress }}</div>
@@ -143,7 +143,7 @@ import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
[disabled]="result.needsRestart && !rebooted"
(click)="exitKiosk()"
>
<tui-avatar appearance="secondary" src="@tui.log-in" />
<span tuiAvatar="@tui.log-in" appearance="secondary"></span>
<div tuiTitle>
{{ 'Continue to Login' | i18n }}
<div tuiSubtitle>
@@ -233,9 +233,8 @@ export default class SuccessPage implements AfterViewInit {
removeMedia() {
this.dialogs
.openComponent<boolean>(new PolymorpheusComponent(RemoveMediaDialog), {
size: 's',
dismissible: false,
closeable: false,
closable: false,
})
.subscribe(() => {
this.usbRemoved = true

View File

@@ -1,4 +1,4 @@
import { Component, inject } from '@angular/core'
import { Component, inject, OnInit } from '@angular/core'
import { Router } from '@angular/router'
import {
DialogService,
@@ -11,10 +11,11 @@ import {
TuiButton,
TuiDataList,
TuiDropdown,
TuiIcon,
TuiLink,
TuiLoader,
TuiTitle,
} from '@taiga-ui/core'
import { TuiChevron } from '@taiga-ui/kit'
import { TuiCardLarge, TuiHeader } from '@taiga-ui/layout'
import { filter } from 'rxjs'
import { ApiService } from '../services/api.service'
@@ -24,18 +25,21 @@ import { StateService } from '../services/state.service'
template: `
<section tuiCardLarge="compact">
<header tuiHeader>
<h2 tuiTitle>
{{ 'Transfer Data' | i18n }}
<span tuiSubtitle>
<hgroup tuiTitle>
<h2>{{ 'Transfer Data' | i18n }}</h2>
<p tuiSubtitle>
{{
'Select the drive containing your existing StartOS data' | i18n
}}
<a class="refresh" (click)="refresh()">
<tui-icon icon="@tui.rotate-cw" />
{{ 'Refresh' | i18n }}
</a>
</span>
</h2>
<button
tuiLink
appearance="action"
iconEnd="@tui.rotate-cw"
[textContent]="'Refresh' | i18n"
(click)="refresh()"
></button>
</p>
</hgroup>
</header>
@if (loading) {
@@ -43,75 +47,43 @@ import { StateService } from '../services/state.service'
} @else {
<button
tuiButton
iconEnd="@tui.chevron-down"
[tuiDropdown]="dropdown"
[tuiDropdownLimitWidth]="'fixed'"
tuiChevron
tuiDropdown
tuiDropdownLimitWidth="fixed"
[(tuiDropdownOpen)]="open"
style="width: 100%"
>
{{ 'Select Drive' | i18n }}
</button>
<ng-template #dropdown>
<tui-data-list>
<tui-data-list
*tuiDropdown
[emptyContent]="'No StartOS data drives found' | i18n"
>
@for (drive of drives; track drive.logicalname) {
<button tuiOption new (click)="select(drive)">
<div class="drive-item">
<span>{{ drive.vendor }} {{ drive.model }}</span>
<small>{{ drive.logicalname }}</small>
</div>
<button tuiOption (click)="select(drive)">
<span tuiTitle>
{{ drive.vendor }} {{ drive.model }}
<span tuiSubtitle>{{ drive.logicalname }}</span>
</span>
</button>
} @empty {
<div class="no-items">
{{ 'No StartOS data drives found' | i18n }}
</div>
}
</tui-data-list>
</ng-template>
</button>
}
</section>
`,
styles: `
.refresh {
display: inline-flex;
align-items: center;
gap: 0.25rem;
cursor: pointer;
color: var(--tui-text-action);
tui-icon {
font-size: 0.875rem;
}
}
.drive-item {
display: flex;
flex-direction: column;
small {
opacity: 0.7;
}
}
.no-items {
padding: 0.5rem 0.75rem;
color: var(--tui-text-secondary);
font-style: italic;
}
`,
imports: [
TuiButton,
TuiCardLarge,
TuiDataList,
TuiDropdown,
TuiIcon,
TuiLink,
TuiChevron,
TuiLoader,
TuiTitle,
TuiHeader,
i18nPipe,
],
})
export default class TransferPage {
export default class TransferPage implements OnInit {
private readonly api = inject(ApiService)
private readonly router = inject(Router)
private readonly dialogs = inject(DialogService)
@@ -137,7 +109,6 @@ export default class TransferPage {
this.dialogs
.openConfirm({
label: 'Warning',
size: 's',
data: {
content:
'After transferring data from this drive, do not attempt to boot into it again as a Start9 Server. This may result in services malfunctioning, data corruption, or loss of funds.',

View File

@@ -12,6 +12,10 @@ tui-root {
height: 100%;
}
ul {
padding-inline-start: 1rem;
}
router-outlet + * {
height: 100%;
max-width: min(35rem, 100vw);
@@ -30,41 +34,11 @@ router-outlet + * {
}
}
.inline-title {
display: inline-flex;
align-items: center;
gap: 0.5rem;
:first-child {
width: 2rem;
height: 2rem;
}
}
button:disabled {
opacity: var(--tui-disabled-opacity);
pointer-events: none;
}
header {
position: relative;
display: flex;
flex-direction: column;
text-align: center;
font: var(--tui-font-heading-4);
p {
font: var(--tui-font-text-m);
color: var(--tui-text-secondary);
}
}
h2 {
margin: 0;
font: var(--tui-font-heading-6);
}
.g-positive {
color: var(--tui-status-positive);
}
@@ -77,14 +51,27 @@ h2 {
color: var(--tui-status-negative);
}
.g-secondary {
color: var(--tui-text-secondary) !important;
}
.g-info {
color: var(--tui-status-info);
}
[tuiCardLarge] footer button {
width: 100%;
[tuiCardLarge] footer {
display: flex;
button {
flex: 1;
}
}
[tuiCell]:not(:last-of-type) {
[tuiCell]:not([tuiOption]):not(:last-of-type) {
box-shadow: 0 calc(0.5rem + 1px) 0 -0.5rem var(--tui-border-normal);
}
}
// TODO: Remove in Taiga v5.0
[tuiButton] {
min-block-size: var(--t-size);
}

View File

@@ -11,7 +11,12 @@ import { getErrorMessage } from '../services/error.service'
@Component({
template: `
@if (error()) {
<tui-notification appearance="negative" safeLinks [innerHTML]="error()" />
<div
tuiNotification
appearance="negative"
safeLinks
[innerHTML]="error()"
></div>
}
@if (content(); as result) {

View File

@@ -1,7 +1,7 @@
import { ChangeDetectionStrategy, Component } from '@angular/core'
import { FormsModule } from '@angular/forms'
import { TuiAutoFocus } from '@taiga-ui/cdk'
import { TuiButton, TuiDialogContext, TuiTextfield } from '@taiga-ui/core'
import { TuiButton, TuiDialogContext, TuiInput } from '@taiga-ui/core'
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { i18nPipe } from '../i18n/i18n.pipe'
import { i18nKey } from '../i18n/i18n.providers'
@@ -23,7 +23,7 @@ import { i18nKey } from '../i18n/i18n.providers'
</label>
}
<input
tuiTextfield
tuiInput
tuiAutoFocus
[ngModelOptions]="{ standalone: true }"
[(ngModel)]="value"
@@ -81,7 +81,7 @@ import { i18nKey } from '../i18n/i18n.providers'
-webkit-text-security: disc;
}
`,
imports: [FormsModule, TuiButton, TuiTextfield, TuiAutoFocus, i18nPipe],
imports: [FormsModule, TuiButton, TuiInput, TuiAutoFocus, i18nPipe],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PromptModal {

View File

@@ -1,7 +1,6 @@
import { DatePipe } from '@angular/common'
import { Component, input } from '@angular/core'
import { TuiIcon, TuiTitle } from '@taiga-ui/core'
import { TuiCell } from '@taiga-ui/layout'
import { TuiIcon, TuiTitle, TuiCell } from '@taiga-ui/core'
import { StartOSDiskInfo } from '../types/api'
@Component({

View File

@@ -5,6 +5,7 @@ import {
InjectionToken,
input,
} from '@angular/core'
import { tuiSetSignal } from '@taiga-ui/cdk'
import { TuiHintDirective } from '@taiga-ui/core'
import { i18nPipe } from '../i18n/i18n.pipe'
@@ -33,7 +34,8 @@ export class DocsLinkDirective {
})
constructor() {
inject(TuiHintDirective).content.set(
tuiSetSignal(
inject(TuiHintDirective).content,
inject(i18nPipe).transform('Documentation'),
)
}

View File

@@ -32,7 +32,6 @@ export * from './services/download-html.service'
export * from './services/exver.service'
export * from './services/error.service'
export * from './services/http.service'
export * from './services/loading.service'
export * from './services/setup-logs.service'
export * from './types/api'

View File

@@ -1,6 +1,6 @@
import { inject, Injectable } from '@angular/core'
import { Clipboard } from '@angular/cdk/clipboard'
import { TuiAlertService } from '@taiga-ui/core'
import { TuiNotificationService } from '@taiga-ui/core'
import { i18nPipe } from '../i18n/i18n.pipe'
@@ -8,7 +8,7 @@ import { i18nPipe } from '../i18n/i18n.pipe'
export class CopyService {
private readonly clipboard = inject(Clipboard)
private readonly i18n = inject(i18nPipe)
private readonly alerts = inject(TuiAlertService)
private readonly alerts = inject(TuiNotificationService)
async copy(text: string) {
const success = this.clipboard.copy(text)

View File

@@ -1,12 +1,12 @@
import { ErrorHandler, inject, Injectable } from '@angular/core'
import { TuiAlertService } from '@taiga-ui/core'
import { TuiNotificationService } from '@taiga-ui/core'
import { HttpError } from '../classes/http-error'
@Injectable({
providedIn: 'root',
})
export class ErrorService extends ErrorHandler {
private readonly alerts = inject(TuiAlertService)
private readonly alerts = inject(TuiNotificationService)
override handleError(error: HttpError | string, link?: string) {
console.error(error)

View File

@@ -1,44 +0,0 @@
import { ChangeDetectionStrategy, Component, Injectable } from '@angular/core'
import { TuiPopoverService } from '@taiga-ui/cdk'
import { TUI_DIALOGS, TuiLoader } from '@taiga-ui/core'
import { injectContext } from '@taiga-ui/polymorpheus'
import { i18nPipe } from '../i18n/i18n.pipe'
import { i18nKey } from '../i18n/i18n.providers'
@Component({
template: '<tui-loader [textContent]="content | i18n" />',
styles: `
:host {
display: flex;
align-items: center;
max-width: 80%;
margin: auto;
padding: 1.5rem;
background: var(--tui-background-elevation-1);
border-radius: var(--tui-radius-m);
box-shadow: var(--tui-shadow-popup);
--tui-background-accent-1: var(--tui-status-warning);
}
tui-loader {
flex-shrink: 0;
min-width: 2rem;
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [TuiLoader, i18nPipe],
})
class LoadingComponent {
readonly content = injectContext<{ content: i18nKey }>().content
}
@Injectable({
providedIn: `root`,
useFactory: () => new LoadingService(TUI_DIALOGS, LoadingComponent),
})
export class LoadingService extends TuiPopoverService<unknown> {
override open<G = void>(textContent: i18nKey | '' = '') {
return super.open<G>(textContent)
}
}

View File

@@ -95,9 +95,6 @@ $wide-modal: 900px;
--tw-color-zinc-800: 39 39 42;
--tw-color-zinc-900: 24 24 27;
--tw-color-zinc-950: 9 9 11;
--tui-font-text: 'Proxima Nova', system-ui;
--tui-font-heading: 'Proxima Nova', system-ui;
}
body {

View File

@@ -1,4 +1,4 @@
@use '@taiga-ui/core/styles/taiga-ui-local' as taiga;
@use '@taiga-ui/styles/utils' as taiga;
[tuiTheme='dark'] {
--tui-background-base: rgba(23, 23, 23, 1);
@@ -135,13 +135,13 @@ tui-dropdown[data-appearance='start-os'][data-appearance='start-os'] {
inset 0 0 1rem rgba(0, 0, 0, 0.25),
var(--tui-shadow-medium);
[tuiOption] {
justify-content: flex-start;
gap: 0.5rem;
input {
// Checkmark
--tui-text-action: var(--tui-text-primary);
}
}
// @TODO Alex: Move to Taiga UI
// TODO: Remove in Taiga v5.0
a[tuiIconButton]:not([href]) {
pointer-events: none;
opacity: var(--tui-disabled-opacity);
@@ -160,7 +160,7 @@ tui-textfield [tuiTooltip] {
padding-block: 0.75rem;
[tuiTitle] {
font: var(--tui-font-text-l);
font: var(--tui-typography-body-l);
}
}
@@ -169,7 +169,16 @@ tui-textfield [tuiTooltip] {
}
}
// TODO: Remove after migrating to v5
[tuiOption] {
word-break: break-word;
:root {
--tui-typography-family-text: 'Proxima Nova', system-ui;
--tui-typography-family-display: 'Proxima Nova', system-ui;
}
tui-notification-middle {
--tui-background-accent-1: var(--tui-status-warning);
}
// TODO: Remove in Taiga v5.0
[tuiButton] {
min-block-size: var(--t-size);
}

View File

@@ -1,13 +1,14 @@
import { tuiDropdownOptionsProvider } from '@taiga-ui/core'
import { provideEventPlugins } from '@taiga-ui/event-plugins'
import { provideAnimations } from '@angular/platform-browser/animations'
import {
tuiDropdownOptionsProvider,
tuiDialogOptionsProvider,
provideTaiga,
} from '@taiga-ui/core'
import {
ApplicationConfig,
provideBrowserGlobalErrorListeners,
provideZonelessChangeDetection,
} from '@angular/core'
import { provideRouter, withRouterConfig } from '@angular/router'
import { tuiDialogOptionsProvider } from '@taiga-ui/experimental'
import { PatchDB } from 'patch-db-client'
import {
PATCH_CACHE,
@@ -31,11 +32,10 @@ const {
export const appConfig: ApplicationConfig = {
providers: [
provideAnimations(),
provideBrowserGlobalErrorListeners(),
provideZonelessChangeDetection(),
provideRouter(routes, withRouterConfig({ onSameUrlNavigation: 'reload' })),
provideEventPlugins(),
provideTaiga({ mode: 'dark' }),
tuiDropdownOptionsProvider({ appearance: 'start-9' }),
tuiDialogOptionsProvider({ appearance: 'start-9 taiga' }),
{

View File

@@ -29,7 +29,7 @@ import { SidebarService } from 'src/app/services/sidebar.service'
}
h1 {
font: var(--tui-font-heading-6);
font: var(--tui-typography-heading-h6);
margin-inline-end: auto;
}

View File

@@ -1,8 +1,11 @@
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import { Router, RouterLink, RouterLinkActive } from '@angular/router'
import { ErrorService, LoadingService } from '@start9labs/shared'
import { ErrorService } from '@start9labs/shared'
import { TuiButton } from '@taiga-ui/core'
import { TuiBadgeNotification } from '@taiga-ui/kit'
import {
TuiBadgeNotification,
TuiNotificationMiddleService,
} from '@taiga-ui/kit'
import { ApiService } from 'src/app/services/api/api.service'
import { AuthService } from 'src/app/services/auth.service'
import { SidebarService } from 'src/app/services/sidebar.service'
@@ -110,7 +113,7 @@ export class Nav {
private readonly router = inject(Router)
protected readonly sidebars = inject(SidebarService)
protected readonly api = inject(ApiService)
private readonly loader = inject(LoadingService)
private readonly loader = inject(TuiNotificationMiddleService)
private readonly errorService = inject(ErrorService)
protected readonly update = inject(UpdateService)
@@ -133,7 +136,7 @@ export class Nav {
] as const
protected async logout() {
const loader = this.loader.open().subscribe()
const loader = this.loader.open('').subscribe()
try {
await this.api.logout()
this.service.authenticated.set(false)

View File

@@ -1,32 +1,26 @@
import { AsyncPipe } from '@angular/common'
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import {
NonNullableFormBuilder,
ReactiveFormsModule,
Validators,
} from '@angular/forms'
import { ErrorService, LoadingService } from '@start9labs/shared'
import { ErrorService } from '@start9labs/shared'
import { utils } from '@start9labs/start-sdk'
import { WA_IS_MOBILE } from '@ng-web-apis/platform'
import { TuiResponsiveDialogService } from '@taiga-ui/addon-mobile'
import {
TUI_IS_MOBILE,
TuiAnimated,
TuiAutoFocus,
tuiMarkControlAsTouchedAndValidate,
} from '@taiga-ui/cdk'
import {
TuiButton,
TuiDialogContext,
TuiError,
TuiTextfield,
} from '@taiga-ui/core'
import { TuiDialogService } from '@taiga-ui/experimental'
import { TuiButton, TuiDialogContext, TuiError, TuiInput } from '@taiga-ui/core'
import {
TuiChevron,
TuiDataListWrapper,
TuiElasticContainer,
TuiFieldErrorPipe,
TuiNotificationMiddleService,
TuiSelect,
} from '@taiga-ui/kit'
import { TuiForm } from '@taiga-ui/layout'
import { TuiForm, TuiElasticContainer } from '@taiga-ui/layout'
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { ApiService } from 'src/app/services/api/api.service'
@@ -44,9 +38,9 @@ import {
<form tuiForm [formGroup]="form">
<tui-textfield>
<label tuiLabel>Name</label>
<input tuiTextfield tuiAutoFocus formControlName="name" />
<input tuiInput tuiAutoFocus formControlName="name" />
</tui-textfield>
<tui-error formControlName="name" [error]="[] | tuiFieldError | async" />
<tui-error formControlName="name" />
@if (!context.data.device) {
<tui-textfield tuiChevron [stringify]="stringify">
@@ -63,31 +57,24 @@ import {
}
@if (!mobile) {
<tui-data-list-wrapper
*tuiTextfieldDropdown
new
*tuiDropdown
[items]="context.data.subnets()"
(itemClick)="onSubnet($event)"
/>
}
</tui-textfield>
<tui-error
formControlName="subnet"
[error]="[] | tuiFieldError | async"
/>
<tui-error formControlName="subnet" />
<tui-elastic-container>
@if (form.controls.subnet.value?.range) {
<tui-textfield>
<tui-textfield tuiAnimated>
<label tuiLabel>LAN IP</label>
<input tuiTextfield tuiAutoFocus formControlName="ip" />
<input tuiInput tuiAutoFocus formControlName="ip" />
</tui-textfield>
}
</tui-elastic-container>
@if (form.controls.subnet.value?.range) {
<tui-error
formControlName="ip"
[error]="[] | tuiFieldError | async"
/>
<tui-error formControlName="ip" />
}
}
<footer>
@@ -97,27 +84,26 @@ import {
`,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
AsyncPipe,
ReactiveFormsModule,
TuiAutoFocus,
TuiButton,
TuiDataListWrapper,
TuiError,
TuiFieldErrorPipe,
TuiForm,
TuiSelect,
TuiTextfield,
TuiInput,
TuiAnimated,
TuiChevron,
TuiElasticContainer,
],
})
export class DevicesAdd {
private readonly loading = inject(LoadingService)
private readonly loading = inject(TuiNotificationMiddleService)
private readonly api = inject(ApiService)
private readonly errorService = inject(ErrorService)
private readonly dialogs = inject(TuiDialogService)
private readonly dialogs = inject(TuiResponsiveDialogService)
protected readonly mobile = inject(TUI_IS_MOBILE)
protected readonly mobile = inject(WA_IS_MOBILE)
protected readonly context =
injectContext<TuiDialogContext<void, DeviceData>>()
@@ -160,7 +146,7 @@ export class DevicesAdd {
return
}
const loader = this.loading.open().subscribe()
const loader = this.loading.open('').subscribe()
const { ip, name, subnet } = this.form.getRawValue()
const data = { ip, name, subnet: subnet?.range || '' }

View File

@@ -1,11 +1,5 @@
import { ChangeDetectionStrategy, Component } from '@angular/core'
import {
TuiButton,
TuiDialogContext,
TuiIcon,
TuiTextfield,
TuiTitle,
} from '@taiga-ui/core'
import { TuiButton, TuiDialogContext, TuiIcon, TuiTitle } from '@taiga-ui/core'
import { TuiCopy, TuiSegmented, TuiTextarea } from '@taiga-ui/kit'
import { TuiHeader } from '@taiga-ui/layout'
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
@@ -60,7 +54,6 @@ import { QrCodeComponent } from 'ng-qrcode'
TuiIcon,
TuiTitle,
TuiSegmented,
TuiTextfield,
TuiTextarea,
TuiCopy,
],

View File

@@ -6,15 +6,10 @@ import {
Signal,
} from '@angular/core'
import { toSignal } from '@angular/core/rxjs-interop'
import { ErrorService, LoadingService } from '@start9labs/shared'
import {
TuiButton,
TuiDataList,
TuiDropdown,
TuiTextfield,
} from '@taiga-ui/core'
import { TuiDialogService } from '@taiga-ui/experimental'
import { TUI_CONFIRM } from '@taiga-ui/kit'
import { ErrorService } from '@start9labs/shared'
import { TuiResponsiveDialogService } from '@taiga-ui/addon-mobile'
import { TuiButton, TuiDataList, TuiDropdown } from '@taiga-ui/core'
import { TUI_CONFIRM, TuiNotificationMiddleService } from '@taiga-ui/kit'
import { PatchDB } from 'patch-db-client'
import { filter, map } from 'rxjs'
import { ApiService } from 'src/app/services/api/api.service'
@@ -49,16 +44,15 @@ import { MappedDevice, MappedSubnet } from './utils'
tuiIconButton
size="xs"
tuiDropdown
tuiDropdownOpen
tuiDropdownAuto
appearance="flat-grayscale"
iconStart="@tui.ellipsis-vertical"
>
Actions
<tui-data-list *tuiTextfieldDropdown size="s">
<tui-data-list *tuiDropdown size="s">
<button
tuiOption
iconStart="@tui.pencil"
new
(click)="onEdit(device)"
>
Rename
@@ -66,7 +60,6 @@ import { MappedDevice, MappedSubnet } from './utils'
<button
tuiOption
iconStart="@tui.settings"
new
(click)="onConfig(device)"
>
View Config
@@ -74,7 +67,6 @@ import { MappedDevice, MappedSubnet } from './utils'
<button
tuiOption
iconStart="@tui.trash"
new
(click)="onDelete(device)"
>
Delete
@@ -90,12 +82,12 @@ import { MappedDevice, MappedSubnet } from './utils'
</table>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [TuiButton, TuiDropdown, TuiDataList, TuiTextfield],
imports: [TuiButton, TuiDropdown, TuiDataList],
})
export default class Devices {
private readonly dialogs = inject(TuiDialogService)
private readonly dialogs = inject(TuiResponsiveDialogService)
private readonly api = inject(ApiService)
private readonly loading = inject(LoadingService)
private readonly loading = inject(TuiNotificationMiddleService)
private readonly errorService = inject(ErrorService)
protected readonly subnets: Signal<readonly MappedSubnet[]> = toSignal(
@@ -145,7 +137,7 @@ export default class Devices {
}
async onConfig({ subnet, ip }: MappedDevice) {
const loader = this.loading.open().subscribe()
const loader = this.loading.open('').subscribe()
try {
const data = await this.api.showDeviceConfig({ subnet: subnet.range, ip })
@@ -163,7 +155,7 @@ export default class Devices {
.open(TUI_CONFIRM, { label: 'Are you sure?' })
.pipe(filter(Boolean))
.subscribe(async () => {
const loader = this.loading.open().subscribe()
const loader = this.loading.open('').subscribe()
try {
await this.api.deleteDevice({ subnet: subnet.range, ip })
} catch (e: any) {

View File

@@ -1,13 +1,12 @@
import { AsyncPipe } from '@angular/common'
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import {
NonNullableFormBuilder,
ReactiveFormsModule,
Validators,
} from '@angular/forms'
import { ErrorService, LoadingService } from '@start9labs/shared'
import { ErrorService } from '@start9labs/shared'
import { WA_IS_MOBILE } from '@ng-web-apis/platform'
import {
TUI_IS_MOBILE,
tuiMarkControlAsTouchedAndValidate,
TuiValueChanges,
} from '@taiga-ui/cdk'
@@ -16,18 +15,16 @@ import {
TuiDialogContext,
TuiError,
TuiNumberFormat,
TuiTextfield,
TuiCheckbox,
} from '@taiga-ui/core'
import {
TuiCheckbox,
TuiChevron,
TuiDataListWrapper,
TuiFieldErrorPipe,
TuiInputNumber,
TuiNotificationMiddleService,
TuiSelect,
TuiElasticContainer,
} from '@taiga-ui/kit'
import { TuiForm } from '@taiga-ui/layout'
import { TuiForm, TuiElasticContainer } from '@taiga-ui/layout'
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { ApiService } from 'src/app/services/api/api.service'
@@ -49,17 +46,10 @@ import { MappedDevice, PortForwardsData } from './utils'
<input tuiSelect formControlName="externalip" />
}
@if (!mobile) {
<tui-data-list-wrapper
*tuiTextfieldDropdown
new
[items]="context.data.ips()"
/>
<tui-data-list-wrapper *tuiDropdown [items]="context.data.ips()" />
}
</tui-textfield>
<tui-error
formControlName="externalip"
[error]="[] | tuiFieldError | async"
/>
<tui-error formControlName="externalip" />
<tui-textfield>
<label tuiLabel>External Port</label>
<input
@@ -71,10 +61,7 @@ import { MappedDevice, PortForwardsData } from './utils'
(tuiValueChanges)="checkShow80()"
/>
</tui-textfield>
<tui-error
formControlName="externalport"
[error]="[] | tuiFieldError | async"
/>
<tui-error formControlName="externalport" />
<tui-textfield tuiChevron [stringify]="stringify">
<label tuiLabel>Device</label>
@if (mobile) {
@@ -89,16 +76,12 @@ import { MappedDevice, PortForwardsData } from './utils'
}
@if (!mobile) {
<tui-data-list-wrapper
*tuiTextfieldDropdown
new
*tuiDropdown
[items]="context.data.devices()"
/>
}
</tui-textfield>
<tui-error
formControlName="device"
[error]="[] | tuiFieldError | async"
/>
<tui-error formControlName="device" />
<tui-textfield>
<label tuiLabel>Internal Port</label>
<input
@@ -110,10 +93,7 @@ import { MappedDevice, PortForwardsData } from './utils'
(tuiValueChanges)="checkShow80()"
/>
</tui-textfield>
<tui-error
formControlName="internalport"
[error]="[] | tuiFieldError | async"
/>
<tui-error formControlName="internalport" />
<tui-elastic-container>
@if (show80) {
<label tuiLabel>
@@ -132,7 +112,6 @@ import { MappedDevice, PortForwardsData } from './utils'
`,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
AsyncPipe,
ReactiveFormsModule,
TuiButton,
TuiChevron,
@@ -140,8 +119,6 @@ import { MappedDevice, PortForwardsData } from './utils'
TuiError,
TuiInputNumber,
TuiNumberFormat,
TuiFieldErrorPipe,
TuiTextfield,
TuiSelect,
TuiForm,
TuiCheckbox,
@@ -151,12 +128,12 @@ import { MappedDevice, PortForwardsData } from './utils'
})
export class PortForwardsAdd {
private readonly api = inject(ApiService)
private readonly loading = inject(LoadingService)
private readonly loading = inject(TuiNotificationMiddleService)
private readonly errorService = inject(ErrorService)
show80 = false
protected readonly mobile = inject(TUI_IS_MOBILE)
protected readonly mobile = inject(WA_IS_MOBILE)
protected readonly context =
injectContext<TuiDialogContext<void, PortForwardsData>>()
@@ -183,7 +160,7 @@ export class PortForwardsAdd {
return
}
const loader = this.loading.open().subscribe()
const loader = this.loading.open('').subscribe()
const { externalip, externalport, device, internalport, also80 } =
this.form.getRawValue()

View File

@@ -7,11 +7,11 @@ import {
} from '@angular/core'
import { toSignal } from '@angular/core/rxjs-interop'
import { ReactiveFormsModule } from '@angular/forms'
import { ErrorService, LoadingService } from '@start9labs/shared'
import { ErrorService } from '@start9labs/shared'
import { utils } from '@start9labs/start-sdk'
import { TuiResponsiveDialogService } from '@taiga-ui/addon-mobile'
import { TuiButton } from '@taiga-ui/core'
import { TuiDialogService } from '@taiga-ui/experimental'
import { TUI_CONFIRM } from '@taiga-ui/kit'
import { TUI_CONFIRM, TuiNotificationMiddleService } from '@taiga-ui/kit'
import { PatchDB } from 'patch-db-client'
import { filter, map } from 'rxjs'
import { PORT_FORWARDS_ADD } from 'src/app/routes/home/routes/port-forwards/add'
@@ -65,9 +65,9 @@ import { MappedDevice, MappedForward } from './utils'
imports: [ReactiveFormsModule, TuiButton],
})
export default class PortForwards {
private readonly dialogs = inject(TuiDialogService)
private readonly dialogs = inject(TuiResponsiveDialogService)
private readonly api = inject(ApiService)
private readonly loading = inject(LoadingService)
private readonly loading = inject(TuiNotificationMiddleService)
private readonly patch = inject<PatchDB<TunnelData>>(PatchDB)
private readonly errorService = inject(ErrorService)
@@ -127,7 +127,7 @@ export default class PortForwards {
.open(TUI_CONFIRM, { label: 'Are you sure?' })
.pipe(filter(Boolean))
.subscribe(async () => {
const loader = this.loading.open().subscribe()
const loader = this.loading.open('').subscribe()
const source = `${externalip}:${externalport}`
try {

View File

@@ -1,4 +1,3 @@
import { AsyncPipe } from '@angular/common'
import {
ChangeDetectionStrategy,
Component,
@@ -15,17 +14,14 @@ import {
import { ErrorService } from '@start9labs/shared'
import { TuiAutoFocus, TuiValidator } from '@taiga-ui/cdk'
import {
TuiAlertService,
TuiButton,
TuiDialogContext,
TuiError,
TuiTextfield,
} from '@taiga-ui/core'
import {
TuiButtonLoading,
TuiFieldErrorPipe,
TuiNotificationService,
TuiInput,
tuiValidationErrorsProvider,
} from '@taiga-ui/kit'
} from '@taiga-ui/core'
import { TuiButtonLoading } from '@taiga-ui/kit'
import { TuiForm } from '@taiga-ui/layout'
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { map } from 'rxjs'
@@ -36,24 +32,18 @@ import { ApiService } from 'src/app/services/api/api.service'
<form tuiForm [formGroup]="form">
<tui-textfield>
<label tuiLabel>New password</label>
<input tuiTextfield tuiAutoFocus formControlName="password" />
<input tuiInput tuiAutoFocus formControlName="password" />
</tui-textfield>
<tui-error
formControlName="password"
[error]="[] | tuiFieldError | async"
/>
<tui-error formControlName="password" />
<tui-textfield>
<label tuiLabel>Confirm new password</label>
<input
tuiTextfield
tuiInput
formControlName="confirm"
[tuiValidator]="matchValidator()"
/>
</tui-textfield>
<tui-error
formControlName="confirm"
[error]="[] | tuiFieldError | async"
/>
<tui-error formControlName="confirm" />
<footer>
<button
tuiButton
@@ -76,22 +66,20 @@ import { ApiService } from 'src/app/services/api/api.service'
],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
AsyncPipe,
ReactiveFormsModule,
TuiAutoFocus,
TuiButton,
TuiButtonLoading,
TuiError,
TuiFieldErrorPipe,
TuiForm,
TuiTextfield,
TuiInput,
TuiValidator,
],
})
export class ChangePasswordDialog {
private readonly context = injectContext<TuiDialogContext<void>>()
private readonly api = inject(ApiService)
private readonly alerts = inject(TuiAlertService)
private readonly alerts = inject(TuiNotificationService)
private readonly errorService = inject(ErrorService)
protected readonly loading = signal(false)

View File

@@ -5,25 +5,25 @@ import {
signal,
} from '@angular/core'
import { ErrorService } from '@start9labs/shared'
import { TuiAppearance, TuiButton, TuiTitle } from '@taiga-ui/core'
import { TuiDialogService } from '@taiga-ui/experimental'
import { TuiResponsiveDialogService } from '@taiga-ui/addon-mobile'
import { TuiButton, TuiCell, TuiTitle } from '@taiga-ui/core'
import { TuiBadge, TuiButtonLoading } from '@taiga-ui/kit'
import { TuiCard, TuiCell } from '@taiga-ui/layout'
import { TuiCard } from '@taiga-ui/layout'
import { UpdateService } from 'src/app/services/update.service'
import { CHANGE_PASSWORD } from './change-password'
@Component({
template: `
<div tuiCardLarge tuiAppearance="neutral">
<div tuiCardLarge>
<div tuiCell>
<span tuiTitle>
<strong>
Version
@if (update.hasUpdate()) {
<tui-badge appearance="positive" size="s">
<span tuiBadge appearance="positive" size="s">
Update Available
</tui-badge>
</span>
}
</strong>
<span tuiSubtitle>Current: {{ update.installed() ?? '—' }}</span>
@@ -52,19 +52,20 @@ import { CHANGE_PASSWORD } from './change-password'
</div>
</div>
`,
styles: `
[tuiCardLarge] {
background: var(--tui-background-neutral-1);
&:not([data-appearance]) {
display: none;
}
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
TuiCard,
TuiCell,
TuiTitle,
TuiButton,
TuiButtonLoading,
TuiBadge,
TuiAppearance,
],
imports: [TuiCard, TuiCell, TuiTitle, TuiButton, TuiButtonLoading, TuiBadge],
})
export default class Settings {
private readonly dialogs = inject(TuiDialogService)
private readonly dialogs = inject(TuiResponsiveDialogService)
private readonly errorService = inject(ErrorService)
protected readonly update = inject(UpdateService)

View File

@@ -1,19 +1,12 @@
import { AsyncPipe } from '@angular/common'
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import {
NonNullableFormBuilder,
ReactiveFormsModule,
Validators,
} from '@angular/forms'
import { LoadingService } from '@start9labs/shared'
import { TuiAutoFocus, tuiMarkControlAsTouchedAndValidate } from '@taiga-ui/cdk'
import {
TuiButton,
TuiDialogContext,
TuiError,
TuiTextfield,
} from '@taiga-ui/core'
import { TuiFieldErrorPipe } from '@taiga-ui/kit'
import { TuiButton, TuiDialogContext, TuiError, TuiInput } from '@taiga-ui/core'
import { TuiNotificationMiddleService } from '@taiga-ui/kit'
import { TuiForm } from '@taiga-ui/layout'
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { ApiService } from 'src/app/services/api/api.service'
@@ -23,18 +16,15 @@ import { ApiService } from 'src/app/services/api/api.service'
<form tuiForm [formGroup]="form">
<tui-textfield>
<label tuiLabel>Name</label>
<input tuiTextfield tuiAutoFocus formControlName="name" />
<input tuiInput tuiAutoFocus formControlName="name" />
</tui-textfield>
<tui-error formControlName="name" [error]="[] | tuiFieldError | async" />
<tui-error formControlName="name" />
@if (!context.data.name) {
<tui-textfield>
<label tuiLabel>IP Range</label>
<input tuiTextfield formControlName="subnet" />
<input tuiInput formControlName="subnet" />
</tui-textfield>
<tui-error
formControlName="subnet"
[error]="[] | tuiFieldError | async"
/>
<tui-error formControlName="subnet" />
}
<footer>
<button tuiButton (click)="onSave()">Save</button>
@@ -43,19 +33,17 @@ import { ApiService } from 'src/app/services/api/api.service'
`,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
AsyncPipe,
ReactiveFormsModule,
TuiAutoFocus,
TuiButton,
TuiError,
TuiFieldErrorPipe,
TuiForm,
TuiTextfield,
TuiInput,
],
})
export class SubnetsAdd {
private readonly api = inject(ApiService)
private readonly loading = inject(LoadingService)
private readonly loading = inject(TuiNotificationMiddleService)
protected readonly context = injectContext<TuiDialogContext<void, Data>>()
protected readonly form = inject(NonNullableFormBuilder).group({
@@ -78,7 +66,7 @@ export class SubnetsAdd {
return
}
const loader = this.loading.open().subscribe()
const loader = this.loading.open('').subscribe()
const value = this.form.getRawValue()
try {

View File

@@ -1,15 +1,9 @@
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import { toSignal } from '@angular/core/rxjs-interop'
import { LoadingService } from '@start9labs/shared'
import { utils } from '@start9labs/start-sdk'
import {
TuiButton,
TuiDataList,
TuiDropdown,
TuiTextfield,
} from '@taiga-ui/core'
import { TuiDialogService } from '@taiga-ui/experimental'
import { TUI_CONFIRM } from '@taiga-ui/kit'
import { TuiResponsiveDialogService } from '@taiga-ui/addon-mobile'
import { TuiButton, TuiDataList, TuiDropdown } from '@taiga-ui/core'
import { TUI_CONFIRM, TuiNotificationMiddleService } from '@taiga-ui/kit'
import { PatchDB } from 'patch-db-client'
import { filter, map } from 'rxjs'
import { ApiService } from 'src/app/services/api/api.service'
@@ -41,16 +35,15 @@ import { SUBNETS_ADD } from './add'
tuiIconButton
size="xs"
tuiDropdown
tuiDropdownOpen
tuiDropdownAuto
appearance="flat-grayscale"
iconStart="@tui.ellipsis-vertical"
>
Actions
<tui-data-list *tuiTextfieldDropdown size="s">
<tui-data-list *tuiDropdown size="s">
<button
tuiOption
iconStart="@tui.pencil"
new
(click)="onEdit(subnet)"
>
Rename
@@ -58,7 +51,6 @@ import { SUBNETS_ADD } from './add'
<button
tuiOption
iconStart="@tui.trash"
new
(click)="onDelete($index)"
>
Delete
@@ -74,12 +66,12 @@ import { SUBNETS_ADD } from './add'
</table>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [TuiButton, TuiDropdown, TuiDataList, TuiTextfield],
imports: [TuiButton, TuiDropdown, TuiDataList],
})
export default class Subnets {
private readonly dialogs = inject(TuiDialogService)
private readonly dialogs = inject(TuiResponsiveDialogService)
private readonly api = inject(ApiService)
private readonly loading = inject(LoadingService)
private readonly loading = inject(TuiNotificationMiddleService)
protected readonly subnets = toSignal<MappedSubnet[], []>(
inject<PatchDB<TunnelData>>(PatchDB)
@@ -120,7 +112,7 @@ export default class Subnets {
.pipe(filter(Boolean))
.subscribe(async () => {
const subnet = this.subnets()[index]?.range || ''
const loader = this.loading.open().subscribe()
const loader = this.loading.open('').subscribe()
try {
await this.api.deleteSubnet({ subnet })

View File

@@ -6,7 +6,7 @@ import {
} from '@angular/core'
import { FormsModule } from '@angular/forms'
import { Router } from '@angular/router'
import { TuiButton, TuiError, TuiTextfield } from '@taiga-ui/core'
import { TuiButton, TuiError, TuiInput } from '@taiga-ui/core'
import { TuiButtonLoading } from '@taiga-ui/kit'
import { ApiService } from 'src/app/services/api/api.service'
import { AuthService } from 'src/app/services/auth.service'
@@ -17,7 +17,7 @@ import { AuthService } from 'src/app/services/auth.service'
<form (ngSubmit)="login()">
<tui-textfield [tuiTextfieldCleaner]="false">
<input
tuiTextfield
tuiInput
type="password"
placeholder="Enter password"
[ngModelOptions]="{ standalone: true }"
@@ -59,7 +59,7 @@ import { AuthService } from 'src/app/services/auth.service'
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [TuiButton, TuiTextfield, FormsModule, TuiError, TuiButtonLoading],
imports: [TuiButton, TuiInput, FormsModule, TuiError, TuiButtonLoading],
})
export default class Login {
private readonly auth = inject(AuthService)

View File

@@ -1,8 +1,8 @@
import { Component, computed, inject, Injectable, signal } from '@angular/core'
import { toObservable } from '@angular/core/rxjs-interop'
import { ErrorService } from '@start9labs/shared'
import { TuiResponsiveDialogService } from '@taiga-ui/addon-mobile'
import { TuiLoader } from '@taiga-ui/core'
import { TuiDialogService } from '@taiga-ui/experimental'
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import {
catchError,
@@ -31,7 +31,7 @@ class UpdatingDialog {
export class UpdateService {
private readonly api = inject(ApiService)
private readonly auth = inject(AuthService)
private readonly dialogs = inject(TuiDialogService)
private readonly dialogs = inject(TuiResponsiveDialogService)
private readonly errorService = inject(ErrorService)
readonly result = signal<TunnelUpdateResult | null>(null)

View File

@@ -14,7 +14,7 @@
<link rel="icon" type="image/svg+xml" href="/assets/icons/favicon.svg" />
<link rel="shortcut icon" href="/assets/icons/favicon.ico" />
</head>
<body tuiTheme="dark">
<body>
<app-root></app-root>
</body>
</html>

View File

@@ -6,6 +6,7 @@
--tui-background-accent-1: #428bf9;
--tui-background-accent-1-hover: #126df7;
--tui-background-accent-1-pressed: #156ed4;
--tui-stroke-width: 1.5px;
}
body {
@@ -41,9 +42,21 @@ tui-dropdown[data-appearance='start-9'] {
backdrop-filter: blur(1rem);
}
tui-dialog[new][data-appearance~='start-9'] {
background: var(--tui-background-neutral-1);
backdrop-filter: blur(5rem);
[tuiTheme='dark'] tui-dialog[data-appearance~='start-9'],
[tuiTheme='dark'] tui-sheet-dialog[data-appearance~='start-9'] .t-sheet {
background:
linear-gradient(45deg, #5240a89c, transparent),
linear-gradient(to bottom, #5240a854, transparent),
color-mix(in hsl, var(--tui-background-elevation-1) 90%, transparent 10%);
background-blend-mode: multiply;
header {
background: transparent;
}
}
tui-notification-middle {
--tui-background-accent-1: var(--tui-status-warning);
}
.g-table {

View File

@@ -14,7 +14,7 @@ import { PatchMonitorService } from './services/patch-monitor.service'
selector: 'app-root',
imports: [TuiRoot, RouterOutlet, ToastContainerComponent],
template: `
<tui-root tuiTheme="dark">
<tui-root>
<router-outlet />
<toast-container />
</tui-root>

View File

@@ -10,7 +10,6 @@ import {
provideZoneChangeDetection,
} from '@angular/core'
import { UntypedFormBuilder } from '@angular/forms'
import { provideAnimations } from '@angular/platform-browser/animations'
import {
ActivationStart,
PreloadAllModules,
@@ -40,21 +39,22 @@ import {
} from '@start9labs/shared'
import { tuiObfuscateOptionsProvider } from '@taiga-ui/cdk'
import {
TUI_DATE_FORMAT,
provideTaiga,
TUI_DIALOGS_CLOSE,
TUI_MEDIA,
tuiAlertOptionsProvider,
tuiButtonOptionsProvider,
tuiDateFormatProvider,
tuiDropdownOptionsProvider,
tuiHintOptionsProvider,
tuiNotificationOptionsProvider,
tuiNumberFormatProvider,
} from '@taiga-ui/core'
import { provideEventPlugins } from '@taiga-ui/event-plugins'
import {
TUI_DATE_TIME_VALUE_TRANSFORMER,
TUI_DATE_VALUE_TRANSFORMER,
tuiInputDateOptionsProvider,
tuiInputDateTimeOptionsProvider,
} from '@taiga-ui/kit'
import { PatchDB } from 'patch-db-client'
import { filter, identity, merge, of, pairwise } from 'rxjs'
import { filter, identity, merge, pairwise } from 'rxjs'
import { FilterUpdatesPipe } from 'src/app/routes/portal/routes/updates/filter-updates.pipe'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { LiveApiService } from 'src/app/services/api/embassy-live-api.service'
@@ -63,14 +63,16 @@ import { AuthService } from 'src/app/services/auth.service'
import { CategoryService } from 'src/app/services/category.service'
import { ClientStorageService } from 'src/app/services/client-storage.service'
import { ConfigService } from 'src/app/services/config.service'
import { DateTransformerService } from 'src/app/services/date-transformer.service'
import { DatetimeTransformerService } from 'src/app/services/datetime-transformer.service'
import {
PATCH_CACHE,
PatchDbSource,
} from 'src/app/services/patch-db/patch-db-source'
import { StateService } from 'src/app/services/state.service'
import { StorageService } from 'src/app/services/storage.service'
import {
DateTransformer,
DatetimeTransformer,
} from 'src/app/utils/value-transformers'
import { environment } from 'src/environments/environment'
import { ROUTES } from './app.routes'
@@ -83,8 +85,7 @@ const {
export const APP_CONFIG: ApplicationConfig = {
providers: [
provideZoneChangeDetection(),
provideAnimations(),
provideEventPlugins(),
provideTaiga({ mode: 'dark' }),
provideHttpClient(withInterceptorsFromDi(), withFetch()),
provideRouter(
ROUTES,
@@ -107,24 +108,13 @@ export const APP_CONFIG: ApplicationConfig = {
tuiNumberFormatProvider({ decimalSeparator: '.', thousandSeparator: '' }),
tuiButtonOptionsProvider({ size: 'm' }),
tuiDropdownOptionsProvider({ appearance: 'start-os' }),
tuiAlertOptionsProvider({
tuiNotificationOptionsProvider({
autoClose: appearance => (appearance === 'negative' ? 0 : 3000),
}),
{
provide: TUI_DATE_FORMAT,
useValue: of({
mode: 'MDY',
separator: '/',
}),
},
{
provide: TUI_DATE_VALUE_TRANSFORMER,
useClass: DateTransformerService,
},
{
provide: TUI_DATE_TIME_VALUE_TRANSFORMER,
useClass: DatetimeTransformerService,
},
tuiDateFormatProvider({ mode: 'mm/dd/yyyy', separator: '/' }),
tuiInputDateOptionsProvider({ valueTransformer: DateTransformer }),
tuiInputDateTimeOptionsProvider({ valueTransformer: DatetimeTransformer }),
tuiHintOptionsProvider({ appearance: 'primary-grayscale' }),
{
provide: ApiService,
useClass: useMocks ? MockApiService : LiveApiService,

View File

@@ -7,8 +7,7 @@ import {
} from '@angular/core'
import { toSignal } from '@angular/core/rxjs-interop'
import { i18nKey, i18nPipe } from '@start9labs/shared'
import { TuiDialogContext, TuiIcon, TuiTitle } from '@taiga-ui/core'
import { TuiCell } from '@taiga-ui/layout'
import { TuiDialogContext, TuiIcon, TuiTitle, TuiCell } from '@taiga-ui/core'
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { PatchDB } from 'patch-db-client'
import { map } from 'rxjs'

View File

@@ -2,7 +2,7 @@ import { AsyncPipe } from '@angular/common'
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import { RouterLink } from '@angular/router'
import { i18nPipe } from '@start9labs/shared'
import { TuiAlert, TuiLink } from '@taiga-ui/core'
import { TuiLink, TuiNotification } from '@taiga-ui/core'
import { PatchDB } from 'patch-db-client'
import { endWith, map, merge, Observable, pairwise, Subject } from 'rxjs'
import { DataModel } from 'src/app/services/patch-db/data-model'
@@ -11,9 +11,9 @@ import { DataModel } from 'src/app/services/patch-db/data-model'
selector: 'notifications-toast',
template: `
<ng-template
[tuiAlert]="!!(visible$ | async)"
[tuiAlertOptions]="{ label: 'StartOS' }"
(tuiAlertChange)="onDismiss()"
[tuiNotification]="!!(visible$ | async)"
[tuiNotificationOptions]="{ label: 'StartOS' }"
(tuiNotificationChange)="onDismiss()"
>
{{ 'New notifications' | i18n }}
<a tuiLink routerLink="/notifications" [queryParams]="{ toast: true }">
@@ -22,7 +22,7 @@ import { DataModel } from 'src/app/services/patch-db/data-model'
</ng-template>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [TuiAlert, RouterLink, AsyncPipe, i18nPipe, TuiLink],
imports: [TuiNotification, RouterLink, AsyncPipe, i18nPipe, TuiLink],
})
export class NotificationsToastComponent {
private readonly dismiss$ = new Subject<boolean>()

View File

@@ -2,11 +2,12 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import { toSignal } from '@angular/core/rxjs-interop'
import { SwUpdate } from '@angular/service-worker'
import { WA_WINDOW } from '@ng-web-apis/common'
import { i18nPipe, LoadingService } from '@start9labs/shared'
import { i18nPipe } from '@start9labs/shared'
import { Version } from '@start9labs/start-sdk'
import { TuiResponsiveDialog } from '@taiga-ui/addon-mobile'
import { TuiAutoFocus } from '@taiga-ui/cdk'
import { TuiButton } from '@taiga-ui/core'
import { TuiNotificationMiddleService } from '@taiga-ui/kit'
import { PatchDB } from 'patch-db-client'
import { distinctUntilChanged, map, merge, Subject } from 'rxjs'
import { ConfigService } from 'src/app/services/config.service'
@@ -74,7 +75,7 @@ import { DataModel } from 'src/app/services/patch-db/data-model'
export class RefreshAlertComponent {
private readonly win = inject(WA_WINDOW)
private readonly updates = inject(SwUpdate)
private readonly loader = inject(LoadingService)
private readonly loader = inject(TuiNotificationMiddleService)
private readonly version = Version.parse(inject(ConfigService).version)
readonly i18n = inject(i18nPipe)

View File

@@ -2,13 +2,9 @@ import { CommonModule } from '@angular/common'
import { Component, Inject } from '@angular/core'
import { RouterLink } from '@angular/router'
import { WA_WINDOW } from '@ng-web-apis/common'
import {
DialogService,
i18nKey,
i18nPipe,
LoadingService,
} from '@start9labs/shared'
import { DialogService, i18nKey, i18nPipe } from '@start9labs/shared'
import { TuiButton } from '@taiga-ui/core'
import { TuiNotificationMiddleService } from '@taiga-ui/kit'
import { filter } from 'rxjs'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { ConfigService } from 'src/app/services/config.service'
@@ -29,7 +25,7 @@ export default class HomePage {
}
constructor(
private readonly loader: LoadingService,
private readonly loader: TuiNotificationMiddleService,
private readonly api: ApiService,
private readonly dialog: DialogService,
@Inject(WA_WINDOW) private readonly window: Window,

View File

@@ -1,7 +1,7 @@
import { Component, inject, DOCUMENT } from '@angular/core'
import { DocsLinkDirective, i18nPipe, RELATIVE_URL } from '@start9labs/shared'
import { TuiButton, TuiIcon, TuiSurface } from '@taiga-ui/core'
import { TuiCardLarge } from '@taiga-ui/layout'
import { TuiButton, TuiIcon } from '@taiga-ui/core'
import { TuiCardLarge, TuiSurface } from '@taiga-ui/layout'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { ConfigService } from 'src/app/services/config.service'

View File

@@ -11,7 +11,7 @@
<label tuiLabel>{{ 'Password' | i18n }}</label>
<input
tuiAutoFocus
tuiTextfield
tuiInput
type="password"
[ngModelOptions]="{ standalone: true }"
[(ngModel)]="password"

View File

@@ -1,4 +1,4 @@
@use '@taiga-ui/core/styles/taiga-ui-local' as taiga;
@use '@taiga-ui/styles/utils' as taiga;
.card {
@include taiga.center-all();

View File

@@ -3,10 +3,10 @@ import { Component, DestroyRef, DOCUMENT, inject, Inject } from '@angular/core'
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'
import { FormsModule } from '@angular/forms'
import { Router } from '@angular/router'
import { i18nKey, i18nPipe, LoadingService } from '@start9labs/shared'
import { i18nKey, i18nPipe } from '@start9labs/shared'
import { TuiAutoFocus } from '@taiga-ui/cdk'
import { TuiButton, TuiError, TuiIcon, TuiTextfield } from '@taiga-ui/core'
import { TuiPassword } from '@taiga-ui/kit'
import { TuiButton, TuiError, TuiIcon, TuiInput } from '@taiga-ui/core'
import { TuiNotificationMiddleService, TuiPassword } from '@taiga-ui/kit'
import { TuiCardLarge } from '@taiga-ui/layout'
import { CAWizardComponent } from 'src/app/routes/login/ca-wizard/ca-wizard.component'
import { ApiService } from 'src/app/services/api/embassy-api.service'
@@ -23,7 +23,7 @@ import { ConfigService } from 'src/app/services/config.service'
CAWizardComponent,
TuiButton,
TuiCardLarge,
TuiTextfield,
TuiInput,
TuiIcon,
TuiPassword,
TuiAutoFocus,
@@ -39,7 +39,7 @@ export default class LoginPage {
constructor(
private readonly router: Router,
private readonly authService: AuthService,
private readonly loader: LoadingService,
private readonly loader: TuiNotificationMiddleService,
private readonly api: ApiService,
public readonly config: ConfigService,
@Inject(DOCUMENT) public readonly document: Document,

View File

@@ -14,7 +14,7 @@ import {
} from '@taiga-ui/cdk'
import { TuiButton, TuiDialogContext } from '@taiga-ui/core'
import { TuiConfirmService } from '@taiga-ui/kit'
import { POLYMORPHEUS_CONTEXT } from '@taiga-ui/polymorpheus'
import { injectContext } from '@taiga-ui/polymorpheus'
import { Operation } from 'fast-json-patch'
import { FormGroupComponent } from 'src/app/routes/portal/components/form/containers/group.component'
import { InvalidService } from 'src/app/routes/portal/components/form/containers/control.directive'
@@ -76,7 +76,7 @@ export interface FormContext<T> {
styles: `
.note {
color: var(--tui-text-secondary);
font: var(--tui-font-text-s);
font: var(--tui-typography-body-s);
margin-top: 1rem;
}
@@ -107,10 +107,9 @@ export class FormComponent<T extends Record<string, any>> implements OnInit {
private readonly confirm = inject(TuiConfirmService, { optional: true })
private readonly formService = inject(FormService)
private readonly invalidService = inject(InvalidService)
private readonly context = inject<TuiDialogContext<void, FormContext<T>>>(
POLYMORPHEUS_CONTEXT,
{ optional: true },
)
private readonly context = injectContext<
TuiDialogContext<void, FormContext<T>>
>({ optional: true })
@Input() spec = this.context?.data.spec || {}
@Input() buttons = this.context?.data.buttons || []

View File

@@ -1,4 +1,3 @@
import { AsyncPipe } from '@angular/common'
import {
ChangeDetectorRef,
Component,
@@ -11,19 +10,14 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'
import {
AbstractControl,
FormArrayName,
FormsModule,
ReactiveFormsModule,
} from '@angular/forms'
import { DialogService, i18nKey, i18nPipe } from '@start9labs/shared'
import { IST } from '@start9labs/start-sdk'
import { TuiAnimated } from '@taiga-ui/cdk'
import {
TuiButton,
TuiError,
TuiIcon,
TuiLink,
TuiTextfield,
} from '@taiga-ui/core'
import { TuiFieldErrorPipe, TuiTooltip } from '@taiga-ui/kit'
import { TuiButton, TuiError, TuiIcon, TuiInput, TuiLink } from '@taiga-ui/core'
import { TuiTooltip } from '@taiga-ui/kit'
import { filter } from 'rxjs'
import { FormService } from 'src/app/services/form.service'
@@ -51,7 +45,13 @@ import { FormObjectComponent } from './object.component'
+ {{ 'Add' | i18n }}
</button>
</div>
<tui-error [error]="order | tuiFieldError | async" />
<!-- TODO: Remove ngModel in Taiga v5.0 -->
<tui-error
[ngModel]=""
[ngModelOptions]="{ standalone: true }"
[formArray]="array.control"
[order]="order"
/>
@for (item of array.control.controls; track item) {
<div tuiAnimated class="control">
<div>
@@ -92,7 +92,7 @@ import { FormObjectComponent } from './object.component'
}
`,
styles: `
@use '@taiga-ui/core/styles/taiga-ui-local' as taiga;
@use '@taiga-ui/styles/utils' as taiga;
:host {
display: block;
@@ -102,7 +102,7 @@ import { FormObjectComponent } from './object.component'
.label {
display: flex;
align-items: center;
font: var(--tui-font-heading-6);
font: var(--tui-typography-heading-h6);
}
.add {
@@ -173,21 +173,20 @@ import { FormObjectComponent } from './object.component'
`,
hostDirectives: [ControlDirective],
imports: [
AsyncPipe,
ReactiveFormsModule,
TuiIcon,
TuiTooltip,
TuiLink,
TuiError,
TuiFieldErrorPipe,
TuiButton,
TuiTextfield,
TuiInput,
i18nPipe,
HintPipe,
MustachePipe,
FormControlComponent,
forwardRef(() => FormObjectComponent),
TuiAnimated,
FormsModule,
],
})
export class FormArrayComponent {

View File

@@ -1,19 +1,15 @@
import { AsyncPipe } from '@angular/common'
import {
ChangeDetectionStrategy,
Component,
inject,
Input,
} from '@angular/core'
import { ReactiveFormsModule } from '@angular/forms'
import { DialogService, i18nPipe } from '@start9labs/shared'
import { IST } from '@start9labs/start-sdk'
import { tuiAsControl, TuiControl } from '@taiga-ui/cdk'
import { TuiError } from '@taiga-ui/core'
import {
TUI_FORMAT_ERROR,
TUI_VALIDATION_ERRORS,
TuiFieldErrorPipe,
} from '@taiga-ui/kit'
import { TUI_VALIDATION_ERRORS, TuiError } from '@taiga-ui/core'
import { TUI_FORMAT_ERROR } from '@taiga-ui/kit'
import { PolymorpheusOutlet } from '@taiga-ui/polymorpheus'
import { ControlSpec } from '../controls/control'
@@ -35,7 +31,7 @@ export const ERRORS = [
selector: 'form-control',
template: `
<ng-container *polymorpheusOutlet="controls[spec.type]" />
<tui-error [error]="order | tuiFieldError | async" />
<tui-error [formControl]="$any(control.control)" [order]="order" />
`,
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
@@ -53,7 +49,7 @@ export const ERRORS = [
},
],
hostDirectives: [ControlDirective],
imports: [AsyncPipe, PolymorpheusOutlet, TuiError, TuiFieldErrorPipe],
imports: [PolymorpheusOutlet, TuiError, ReactiveFormsModule],
})
export class FormControlComponent<
T extends ControlSpec,
@@ -91,7 +87,7 @@ export class FormControlComponent<
.openConfirm({
label: 'Warning',
data: { content: warning as any, yes: 'Confirm', no: 'Cancel' },
closeable: false,
closable: false,
dismissible: false,
})
.subscribe(confirm => {

View File

@@ -3,13 +3,14 @@ import {
ChangeDetectionStrategy,
Component,
Input,
signal,
SkipSelf,
ViewEncapsulation,
} from '@angular/core'
import { ControlContainer, ReactiveFormsModule } from '@angular/forms'
import { IST } from '@start9labs/start-sdk'
import { TUI_DEFAULT_ERROR_MESSAGE } from '@taiga-ui/core'
import { identity, of } from 'rxjs'
import { identity } from 'rxjs'
import { FilterHiddenPipe } from '../pipes/filter-hidden.pipe'
import { FormArrayComponent } from './array.component'
@@ -92,7 +93,7 @@ import { FormUnionComponent } from './union.component'
viewProviders: [
{
provide: TUI_DEFAULT_ERROR_MESSAGE,
useValue: of('Unknown error'),
useValue: signal('Unknown error'),
},
{
provide: ControlContainer,

View File

@@ -9,8 +9,7 @@ import {
} from '@angular/core'
import { ControlContainer } from '@angular/forms'
import { IST } from '@start9labs/start-sdk'
import { TuiButton, TuiIcon } from '@taiga-ui/core'
import { TuiExpand } from '@taiga-ui/experimental'
import { TuiButton, TuiIcon, TuiExpand } from '@taiga-ui/core'
import { TuiTooltip } from '@taiga-ui/kit'
import { ControlDirective } from './control.directive'
@@ -43,7 +42,7 @@ import { FormGroupComponent } from './group.component'
</tui-expand>
`,
styles: `
@use '@taiga-ui/core/styles/taiga-ui-local' as taiga;
@use '@taiga-ui/styles/utils' as taiga;
:host {
display: flex;
@@ -57,7 +56,7 @@ import { FormGroupComponent } from './group.component'
display: flex;
align-items: center;
cursor: pointer;
font: var(--tui-font-text-l);
font: var(--tui-typography-body-l);
font-weight: bold;
margin: 0 0 -0.75rem;
}

View File

@@ -1,3 +1,4 @@
import { TuiElasticContainer } from '@taiga-ui/layout'
import {
ChangeDetectionStrategy,
Component,
@@ -13,7 +14,6 @@ import {
} from '@angular/forms'
import { IST } from '@start9labs/start-sdk'
import { TuiValueChanges } from '@taiga-ui/cdk'
import { TuiElasticContainer } from '@taiga-ui/kit'
import { FormService } from 'src/app/services/form.service'
import { FormControlComponent } from './control.component'
import { FormGroupComponent } from './group.component'

View File

@@ -1,7 +1,7 @@
import { ChangeDetectionStrategy, Component } from '@angular/core'
import { FormsModule } from '@angular/forms'
import { IST } from '@start9labs/start-sdk'
import { TuiIcon, TuiTextfield } from '@taiga-ui/core'
import { TuiIcon, TuiInput } from '@taiga-ui/core'
import { TuiInputColor, TuiTooltip } from '@taiga-ui/kit'
import { Control } from './control'
@@ -35,7 +35,7 @@ import { HintPipe } from '../pipes/hint.pipe'
`,
imports: [
FormsModule,
TuiTextfield,
TuiInput,
TuiInputColor,
TuiIcon,
TuiTooltip,

View File

@@ -8,7 +8,7 @@ import {
TuiMapperPipe,
TuiTime,
} from '@taiga-ui/cdk'
import { TuiIcon, TuiTextfield } from '@taiga-ui/core'
import { TuiIcon, TuiInput } from '@taiga-ui/core'
import {
TuiInputDate,
TuiInputDateTime,
@@ -22,21 +22,23 @@ import { HintPipe } from '../pipes/hint.pipe'
@Component({
selector: 'form-datetime',
template: `
<!--
TODO: Move @switch down to only affect <input ... /> after fix:
https://github.com/taiga-family/taiga-ui/issues/11780
-->
@switch (spec.inputmode) {
@case ('time') {
<tui-textfield (tuiActiveZoneChange)="!$event && control.onTouched()">
@if (spec.name) {
<label tuiLabel>
{{ spec.name }}
@if (spec.required) {
<span>*</span>
}
</label>
<tui-textfield (tuiActiveZoneChange)="!$event && control.onTouched()">
@if (spec.name) {
<label tuiLabel>
{{ spec.name }}
@if (spec.required) {
<span>*</span>
}
</label>
}
@if (spec | hint; as hint) {
<tui-icon [tuiTooltip]="hint" />
}
@if (spec.inputmode !== 'time') {
<tui-calendar *tuiDropdown />
}
@switch (spec.inputmode) {
@case ('time') {
<input
tuiInputTime
type="time"
@@ -47,21 +49,8 @@ import { HintPipe } from '../pipes/hint.pipe'
(ngModelChange)="value = $event?.toString() || null"
(blur)="control.onTouched()"
/>
@if (spec | hint; as hint) {
<tui-icon [tuiTooltip]="hint" />
}
</tui-textfield>
}
@case ('date') {
<tui-textfield (tuiActiveZoneChange)="!$event && control.onTouched()">
@if (spec.name) {
<label tuiLabel>
{{ spec.name }}
@if (spec.required) {
<span>*</span>
}
</label>
}
}
@case ('date') {
<input
tuiInputDate
type="date"
@@ -73,22 +62,8 @@ import { HintPipe } from '../pipes/hint.pipe'
[(ngModel)]="value"
(blur)="control.onTouched()"
/>
@if (spec | hint; as hint) {
<tui-icon [tuiTooltip]="hint" />
}
<tui-calendar *tuiTextfieldDropdown />
</tui-textfield>
}
@case ('datetime-local') {
<tui-textfield (tuiActiveZoneChange)="!$event && control.onTouched()">
@if (spec.name) {
<label tuiLabel>
{{ spec.name }}
@if (spec.required) {
<span>*</span>
}
</label>
}
}
@case ('datetime-local') {
<input
tuiInputDateTime
type="datetime-local"
@@ -100,17 +75,13 @@ import { HintPipe } from '../pipes/hint.pipe'
[(ngModel)]="value"
(blur)="control.onTouched()"
/>
@if (spec | hint; as hint) {
<tui-icon [tuiTooltip]="hint" />
}
<tui-calendar *tuiTextfieldDropdown />
</tui-textfield>
}
}
}
</tui-textfield>
`,
imports: [
FormsModule,
TuiTextfield,
TuiInput,
TuiIcon,
TuiTooltip,
TuiInputTime,

View File

@@ -30,7 +30,7 @@ import { Control } from './control'
}
</div>
@if (value) {
<tui-chip>
<span tuiChip>
{{ value.name }}
<button
tuiIconButton
@@ -42,7 +42,7 @@ import { Control } from './control'
>
{{ 'Delete' | i18n }}
</button>
</tui-chip>
</span>
} @else {
<small>{{ 'Click or drop file here' | i18n }}</small>
}
@@ -54,7 +54,7 @@ import { Control } from './control'
</label>
`,
styles: `
@use '@taiga-ui/core/styles/taiga-ui-local' as taiga;
@use '@taiga-ui/styles/utils' as taiga;
.template {
@include taiga.transition(opacity);
@@ -63,7 +63,7 @@ import { Control } from './control'
display: flex;
align-items: center;
padding: 0 0.5rem;
font: var(--tui-font-text-m);
font: var(--tui-typography-body-m);
font-weight: bold;
&_hidden {

View File

@@ -29,12 +29,6 @@ import { Control } from './control'
}
</tui-textfield>
`,
styles: `
// TODO: Remove after Taiga UI update
:host ::ng-deep .t-input {
pointer-events: none;
}
`,
imports: [
FormsModule,
TuiTextfield,

View File

@@ -1,7 +1,7 @@
import { Component } from '@angular/core'
import { FormsModule } from '@angular/forms'
import { IST } from '@start9labs/start-sdk'
import { TuiIcon, TuiNumberFormat, TuiTextfield } from '@taiga-ui/core'
import { TuiIcon, TuiNumberFormat, TuiInput } from '@taiga-ui/core'
import { TuiInputNumber, TuiTooltip } from '@taiga-ui/kit'
import { Control } from './control'
@@ -39,7 +39,7 @@ import { HintPipe } from '../pipes/hint.pipe'
`,
imports: [
FormsModule,
TuiTextfield,
TuiInput,
TuiInputNumber,
TuiNumberFormat,
TuiIcon,

View File

@@ -1,10 +1,10 @@
import { WA_IS_MOBILE } from '@ng-web-apis/platform'
import { Component, inject } from '@angular/core'
import { FormsModule } from '@angular/forms'
import { Router, RouterLink } from '@angular/router'
import { invert } from '@start9labs/shared'
import { IST } from '@start9labs/start-sdk'
import { TUI_IS_MOBILE } from '@taiga-ui/cdk'
import { TuiDataList, TuiIcon, TuiTextfield } from '@taiga-ui/core'
import { TuiDataList, TuiIcon, TuiInput } from '@taiga-ui/core'
import {
TuiChevron,
TuiFluidTypography,
@@ -50,12 +50,11 @@ import { HintPipe } from '../pipes/hint.pipe'
/>
}
@if (!mobile) {
<tui-data-list *tuiTextfieldDropdown>
<tui-data-list *tuiDropdown>
@for (item of items; track item) {
@if (inverted[item]?.startsWith('~')) {
<a
tuiOption
new
iconEnd="@tui.arrow-right"
tuiFluidTypography
[routerLink]="inverted[item]?.slice(1)"
@@ -65,7 +64,6 @@ import { HintPipe } from '../pipes/hint.pipe'
} @else {
<button
tuiOption
new
tuiFluidTypography
[style.white-space]="'nowrap'"
[value]="item"
@@ -85,7 +83,7 @@ import { HintPipe } from '../pipes/hint.pipe'
imports: [
FormsModule,
RouterLink,
TuiTextfield,
TuiInput,
TuiSelect,
TuiDataList,
TuiFluidTypography,
@@ -98,7 +96,7 @@ import { HintPipe } from '../pipes/hint.pipe'
export class FormSelectComponent extends Control<IST.ValueSpecSelect, string> {
protected readonly router = inject(Router)
protected readonly inverted = invert(this.spec.values)
protected readonly mobile = inject(TUI_IS_MOBILE)
protected readonly mobile = inject(WA_IS_MOBILE)
protected readonly items = Object.values(this.spec.values)
protected readonly disabledItemHandler = (item: string) =>
Array.isArray(this.spec.disabled) &&

View File

@@ -3,7 +3,7 @@ import { FormsModule } from '@angular/forms'
import { i18nPipe } from '@start9labs/shared'
import { IST, utils } from '@start9labs/start-sdk'
import { tuiInjectElement } from '@taiga-ui/cdk'
import { TuiButton, TuiIcon, TuiTextfield } from '@taiga-ui/core'
import { TuiButton, TuiIcon, TuiInput } from '@taiga-ui/core'
import { TuiTooltip } from '@taiga-ui/kit'
import { Control } from './control'
@@ -22,7 +22,7 @@ import { HintPipe } from '../pipes/hint.pipe'
</label>
}
<input
tuiTextfield
tuiInput
[attr.inputmode]="spec.inputmode"
[attr.minLength]="spec.minLength"
[attr.maxLength]="spec.maxLength"
@@ -86,7 +86,7 @@ import { HintPipe } from '../pipes/hint.pipe'
`,
imports: [
FormsModule,
TuiTextfield,
TuiInput,
TuiButton,
TuiIcon,
TuiTooltip,

View File

@@ -1,7 +1,7 @@
import { Component } from '@angular/core'
import { FormsModule } from '@angular/forms'
import { IST } from '@start9labs/start-sdk'
import { TuiIcon, TuiTextfield } from '@taiga-ui/core'
import { TuiIcon, TuiInput } from '@taiga-ui/core'
import { TuiTextarea, TuiTooltip } from '@taiga-ui/kit'
import { Control } from './control'
@@ -37,14 +37,7 @@ import { HintPipe } from '../pipes/hint.pipe'
}
</tui-textfield>
`,
imports: [
FormsModule,
TuiTextfield,
TuiTextarea,
TuiIcon,
TuiTooltip,
HintPipe,
],
imports: [FormsModule, TuiInput, TuiTextarea, TuiIcon, TuiTooltip, HintPipe],
})
export class FormTextareaComponent extends Control<
IST.ValueSpecTextarea,

View File

@@ -2,9 +2,8 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import { toSignal } from '@angular/core/rxjs-interop'
import { CopyService, i18nPipe } from '@start9labs/shared'
import { T } from '@start9labs/start-sdk'
import { TuiButton, TuiHint, TuiTitle } from '@taiga-ui/core'
import { TuiButton, TuiHint, TuiTitle, TuiCell } from '@taiga-ui/core'
import { TuiFade } from '@taiga-ui/kit'
import { TuiCell } from '@taiga-ui/layout'
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { PatchDB } from 'patch-db-client'
import { ConfigService } from 'src/app/services/config.service'

View File

@@ -22,7 +22,7 @@ import { HeaderStatusComponent } from './status.component'
<header-menu class="item item_corner" />
`,
styles: `
@use '@taiga-ui/core/styles/taiga-ui-local' as taiga;
@use '@taiga-ui/styles/utils' as taiga;
@keyframes connecting {
25%,
@@ -122,7 +122,7 @@ import { HeaderStatusComponent } from './status.component'
display: flex;
height: 100%;
align-items: center;
font: var(--tui-font-text-l);
font: var(--tui-typography-body-l);
padding: 1rem;
white-space: nowrap;
overflow: hidden;

View File

@@ -5,7 +5,6 @@ import {
DocsLinkDirective,
ErrorService,
i18nPipe,
LoadingService,
SafeLinksDirective,
} from '@start9labs/shared'
import {
@@ -15,6 +14,7 @@ import {
TuiHint,
TuiIcon,
} from '@taiga-ui/core'
import { TuiNotificationMiddleService } from '@taiga-ui/kit'
import { filter } from 'rxjs'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { AuthService } from 'src/app/services/auth.service'
@@ -43,71 +43,60 @@ import { ABOUT } from './about.component'
{{ status().message | i18n }}
</div>
}
<tui-data-list [style.width.rem]="13">
<tui-opt-group>
<button tuiOption iconStart="@tui.info" new (click)="about()">
{{ 'About this server' | i18n }}
</button>
</tui-opt-group>
<tui-opt-group label="" safeLinks>
<a
tuiOption
docsLink
new
iconStart="@tui.book-open-text"
path="/start-os/user-manual"
>
{{ 'User manual' | i18n }}
</a>
<a
tuiOption
new
iconStart="@tui.headphones"
href="https://start9.com/contact"
>
{{ 'Contact support' | i18n }}
</a>
<a
tuiOption
new
iconStart="@tui.dollar-sign"
href="https://donate.start9.com"
>
{{ 'Donate to Start9' | i18n }}
</a>
</tui-opt-group>
<tui-opt-group label="">
<a
tuiOption
new
iconStart="@tui.settings"
routerLink="/system"
(click)="open = false"
>
{{ 'System Settings' | i18n }}
</a>
</tui-opt-group>
<tui-opt-group label="">
<button
tuiOption
new
iconStart="@tui.refresh-cw"
(click)="promptPower('restart')"
>
{{ 'Restart' | i18n }}
</button>
<button
tuiOption
new
iconStart="@tui.power"
(click)="promptPower('shutdown')"
>
{{ 'Shutdown' | i18n }}
</button>
<button tuiOption new iconStart="@tui.log-out" (click)="logout()">
{{ 'Logout' | i18n }}
</button>
</tui-opt-group>
<tui-data-list safeLinks [style.width.rem]="13">
<button tuiOption iconStart="@tui.info" (click)="about()">
{{ 'About this server' | i18n }}
</button>
<hr />
<a
tuiOption
docsLink
iconStart="@tui.book-open-text"
path="/start-os/user-manual"
>
{{ 'User manual' | i18n }}
</a>
<a
tuiOption
iconStart="@tui.headphones"
href="https://start9.com/contact"
>
{{ 'Contact support' | i18n }}
</a>
<a
tuiOption
iconStart="@tui.dollar-sign"
href="https://donate.start9.com"
>
{{ 'Donate to Start9' | i18n }}
</a>
<hr />
<a
tuiOption
iconStart="@tui.settings"
routerLink="/system"
(click)="open = false"
>
{{ 'System Settings' | i18n }}
</a>
<hr />
<button
tuiOption
iconStart="@tui.refresh-cw"
(click)="promptPower('restart')"
>
{{ 'Restart' | i18n }}
</button>
<button
tuiOption
iconStart="@tui.power"
(click)="promptPower('shutdown')"
>
{{ 'Shutdown' | i18n }}
</button>
<button tuiOption iconStart="@tui.log-out" (click)="logout()">
{{ 'Logout' | i18n }}
</button>
</tui-data-list>
</ng-template>
`,
@@ -151,7 +140,7 @@ import { ABOUT } from './about.component'
export class HeaderMenuComponent {
private readonly api = inject(ApiService)
private readonly auth = inject(AuthService)
private readonly loader = inject(LoadingService)
private readonly loader = inject(TuiNotificationMiddleService)
private readonly errorService = inject(ErrorService)
private readonly dialog = inject(DialogService)

View File

@@ -32,7 +32,7 @@ import { getMenu } from 'src/app/utils/system-utilities'
}
`,
styles: `
@use '@taiga-ui/core/styles/taiga-ui-local' as taiga;
@use '@taiga-ui/styles/utils' as taiga;
:host {
position: relative;

View File

@@ -18,7 +18,7 @@ import { STATUS } from 'src/app/services/status.service'
<span>{{ status().message | i18n }}</span>
`,
styles: `
@use '@taiga-ui/core/styles/taiga-ui-local' as taiga;
@use '@taiga-ui/styles/utils' as taiga;
:host {
@include taiga.transition(all);

View File

@@ -5,21 +5,21 @@ import {
input,
signal,
} from '@angular/core'
import { WA_IS_MOBILE } from '@ng-web-apis/platform'
import {
CopyService,
DialogService,
ErrorService,
i18nPipe,
LoadingService,
} from '@start9labs/shared'
import { TUI_IS_MOBILE } from '@taiga-ui/cdk'
import {
TuiButton,
tuiButtonOptionsProvider,
TuiDataList,
TuiDropdown,
TuiTextfield,
TuiInput,
} from '@taiga-ui/core'
import { TuiNotificationMiddleService } from '@taiga-ui/kit'
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { QRModal } from 'src/app/routes/portal/modals/qr.component'
import { ApiService } from 'src/app/services/api/embassy-api.service'
@@ -114,11 +114,10 @@ import { DomainHealthService } from './domain-health.service'
[(tuiDropdownOpen)]="open"
>
{{ 'Actions' | i18n }}
<tui-data-list *tuiTextfieldDropdown (click)="open.set(false)">
<tui-data-list *tuiDropdown (click)="open.set(false)">
@if (address().ui) {
<a
tuiOption
new
iconStart="@tui.external-link"
target="_blank"
rel="noreferrer"
@@ -130,7 +129,6 @@ import { DomainHealthService } from './domain-health.service'
}
<button
tuiOption
new
[iconStart]="
address().enabled ? '@tui.toggle-right' : '@tui.toggle-left'
"
@@ -138,12 +136,11 @@ import { DomainHealthService } from './domain-health.service'
>
{{ (address().enabled ? 'Disable' : 'Enable') | i18n }}
</button>
<button tuiOption new iconStart="@tui.qr-code" (click)="showQR()">
<button tuiOption iconStart="@tui.qr-code" (click)="showQR()">
{{ 'Show QR' | i18n }}
</button>
<button
tuiOption
new
iconStart="@tui.copy"
(click)="copyService.copy(address().url)"
>
@@ -152,7 +149,6 @@ import { DomainHealthService } from './domain-health.service'
@if (address().hostnameInfo.metadata.kind === 'public-domain') {
<button
tuiOption
new
iconStart="@tui.settings"
(click)="showDnsValidation()"
>
@@ -162,7 +158,6 @@ import { DomainHealthService } from './domain-health.service'
@if (address().hostnameInfo.metadata.kind === 'private-domain') {
<button
tuiOption
new
iconStart="@tui.settings"
(click)="showPrivateDnsValidation()"
>
@@ -175,7 +170,6 @@ import { DomainHealthService } from './domain-health.service'
) {
<button
tuiOption
new
iconStart="@tui.settings"
(click)="showPortForwardValidation()"
>
@@ -183,12 +177,7 @@ import { DomainHealthService } from './domain-health.service'
</button>
}
@if (address().deletable) {
<button
tuiOption
new
iconStart="@tui.trash"
(click)="deleteDomain()"
>
<button tuiOption iconStart="@tui.trash" (click)="deleteDomain()">
{{ 'Delete' | i18n }}
</button>
}
@@ -224,15 +213,15 @@ import { DomainHealthService } from './domain-health.service'
}
}
`,
imports: [TuiButton, TuiDropdown, TuiDataList, i18nPipe, TuiTextfield],
imports: [TuiButton, TuiDropdown, TuiDataList, i18nPipe, TuiInput],
providers: [tuiButtonOptionsProvider({ appearance: 'icon' })],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AddressActionsComponent {
private readonly isMobile = inject(TUI_IS_MOBILE)
private readonly isMobile = inject(WA_IS_MOBILE)
private readonly dialog = inject(DialogService)
private readonly api = inject(ApiService)
private readonly loader = inject(LoadingService)
private readonly loader = inject(TuiNotificationMiddleService)
private readonly errorService = inject(ErrorService)
private readonly domainHealth = inject(DomainHealthService)
readonly copyService = inject(CopyService)
@@ -247,8 +236,8 @@ export class AddressActionsComponent {
showQR() {
this.dialog
.openComponent(new PolymorpheusComponent(QRModal), {
size: 'auto',
closeable: this.isMobile,
size: 's',
closable: this.isMobile,
data: this.address().url,
})
.subscribe()

View File

@@ -5,14 +5,10 @@ import {
input,
signal,
} from '@angular/core'
import { ErrorService, i18nPipe, LoadingService } from '@start9labs/shared'
import { ErrorService, i18nPipe } from '@start9labs/shared'
import { ISB, utils } from '@start9labs/start-sdk'
import {
TuiButton,
TuiDataList,
TuiDropdown,
TuiTextfield,
} from '@taiga-ui/core'
import { TuiButton, TuiDataList, TuiDropdown, TuiInput } from '@taiga-ui/core'
import { TuiNotificationMiddleService } from '@taiga-ui/kit'
import { PatchDB } from 'patch-db-client'
import { firstValueFrom } from 'rxjs'
import {
@@ -46,11 +42,11 @@ import { InterfaceAddressItemComponent } from './item.component'
[(tuiDropdownOpen)]="addOpen"
>
{{ 'Add Domain' | i18n }}
<tui-data-list *tuiTextfieldDropdown (click)="addOpen.set(false)">
<button tuiOption new (click)="addPublicDomain()">
<tui-data-list *tuiDropdown (click)="addOpen.set(false)">
<button tuiOption (click)="addPublicDomain()">
{{ 'Public Domain' | i18n }}
</button>
<button tuiOption new (click)="addPrivateDomain()">
<button tuiOption (click)="addPrivateDomain()">
{{ 'Private Domain' | i18n }}
</button>
</tui-data-list>
@@ -97,7 +93,7 @@ import { InterfaceAddressItemComponent } from './item.component'
TuiButton,
TuiDropdown,
TuiDataList,
TuiTextfield,
TuiInput,
TableComponent,
PlaceholderComponent,
i18nPipe,
@@ -108,7 +104,7 @@ import { InterfaceAddressItemComponent } from './item.component'
export class InterfaceAddressesComponent {
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
private readonly formDialog = inject(FormDialogService)
private readonly loader = inject(LoadingService)
private readonly loader = inject(TuiNotificationMiddleService)
private readonly errorService = inject(ErrorService)
private readonly api = inject(ApiService)
private readonly i18n = inject(i18nPipe)

View File

@@ -189,7 +189,7 @@ export type DomainValidationData = {
td,
th {
padding: 0.5rem 0.5rem !important;
font: var(--tui-font-text-s) !important;
font: var(--tui-typography-body-s) !important;
color: var(--tui-text-primary) !important;
font-weight: normal !important;
}

View File

@@ -6,11 +6,15 @@ import {
input,
signal,
} from '@angular/core'
import { ErrorService, i18nPipe, LoadingService } from '@start9labs/shared'
import { FormsModule } from '@angular/forms'
import { ErrorService, i18nPipe } from '@start9labs/shared'
import { TuiObfuscatePipe } from '@taiga-ui/cdk'
import { TuiButton, TuiIcon } from '@taiga-ui/core'
import { FormsModule } from '@angular/forms'
import { TuiBadge, TuiSwitch } from '@taiga-ui/kit'
import {
TuiBadge,
TuiNotificationMiddleService,
TuiSwitch,
} from '@taiga-ui/kit'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { GatewayAddress, MappedServiceInterface } from '../interface.service'
import { AddressActionsComponent } from './actions.component'
@@ -45,12 +49,13 @@ import { DomainHealthService } from './domain-health.service'
</span>
</td>
<td class="type">
<tui-badge
<span
tuiBadge
size="s"
[appearance]="typeAppearance(address.hostnameInfo.metadata.kind)"
>
{{ address.type }}
</tui-badge>
</span>
</td>
<td>
<div class="cert">
@@ -177,7 +182,7 @@ import { DomainHealthService } from './domain-health.service'
.access {
padding-right: 0;
font: var(--tui-font-text-m);
font: var(--tui-typography-body-m);
font-weight: bold;
tui-icon {
@@ -186,7 +191,7 @@ import { DomainHealthService } from './domain-health.service'
}
.type {
font: var(--tui-font-text-m);
font: var(--tui-typography-body-m);
font-weight: bold;
color: var(--tui-text-primary);
padding-inline-end: 0.5rem;
@@ -226,7 +231,7 @@ import { DomainHealthService } from './domain-health.service'
export class InterfaceAddressItemComponent {
private readonly api = inject(ApiService)
private readonly errorService = inject(ErrorService)
private readonly loader = inject(LoadingService)
private readonly loader = inject(TuiNotificationMiddleService)
private readonly domainHealth = inject(DomainHealthService)
readonly address = input.required<GatewayAddress>()

View File

@@ -1,3 +1,4 @@
import { WA_IS_MOBILE } from '@ng-web-apis/platform'
import {
ChangeDetectionStrategy,
Component,
@@ -7,13 +8,7 @@ import {
} from '@angular/core'
import { CopyService, DialogService, i18nPipe } from '@start9labs/shared'
import { T } from '@start9labs/start-sdk'
import { TUI_IS_MOBILE } from '@taiga-ui/cdk'
import {
TuiButton,
TuiDataList,
TuiDropdown,
TuiTextfield,
} from '@taiga-ui/core'
import { TuiButton, TuiDataList, TuiDropdown, TuiInput } from '@taiga-ui/core'
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { PlaceholderComponent } from 'src/app/routes/portal/components/placeholder.component'
import { TableComponent } from 'src/app/routes/portal/components/table.component'
@@ -109,7 +104,7 @@ import {
>
{{ 'More' | i18n }}
<tui-data-list
*tuiTextfieldDropdown
*tuiDropdown
(click)="overflowOpen.set(null)"
>
@for (
@@ -120,7 +115,6 @@ import {
@if (pluginGroup().pluginActions[actionId]; as meta) {
<button
tuiOption
new
(click)="runRowAction(actionId, meta, address)"
>
{{ meta.name }}
@@ -142,7 +136,7 @@ import {
[(tuiDropdownOpen)]="open"
>
{{ 'Actions' | i18n }}
<tui-data-list *tuiTextfieldDropdown (click)="open.set(false)">
<tui-data-list *tuiDropdown (click)="open.set(false)">
@if (address.hostnameInfo.metadata.kind === 'plugin') {
@if (address.hostnameInfo.metadata.removeAction) {
@if (
@@ -153,7 +147,6 @@ import {
) {
<button
tuiOption
new
iconStart="@tui.trash"
(click)="
runRowAction(
@@ -170,7 +163,6 @@ import {
}
<button
tuiOption
new
iconStart="@tui.qr-code"
(click)="showQR(address.url)"
>
@@ -178,7 +170,6 @@ import {
</button>
<button
tuiOption
new
iconStart="@tui.copy"
(click)="copyService.copy(address.url)"
>
@@ -192,7 +183,6 @@ import {
@if (pluginGroup().pluginActions[actionId]; as meta) {
<button
tuiOption
new
(click)="runRowAction(actionId, meta, address)"
>
{{ meta.name }}
@@ -271,7 +261,7 @@ import {
TuiButton,
TuiDropdown,
TuiDataList,
TuiTextfield,
TuiInput,
TableComponent,
PlaceholderComponent,
i18nPipe,
@@ -279,7 +269,7 @@ import {
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PluginAddressesComponent {
private readonly isMobile = inject(TUI_IS_MOBILE)
private readonly isMobile = inject(WA_IS_MOBILE)
private readonly dialog = inject(DialogService)
private readonly actionService = inject(ActionService)
readonly copyService = inject(CopyService)
@@ -290,12 +280,12 @@ export class PluginAddressesComponent {
readonly packageId = input('')
readonly value = input<MappedServiceInterface | undefined>()
showQR(url: string) {
showQR(data: string) {
this.dialog
.openComponent(new PolymorpheusComponent(QRModal), {
size: 'auto',
closeable: this.isMobile,
data: url,
size: 's',
closable: this.isMobile,
data,
})
.subscribe()
}

View File

@@ -116,7 +116,7 @@ export type PortForwardValidationData = {
td,
th {
padding: 0.5rem 0.5rem !important;
font: var(--tui-font-text-s) !important;
font: var(--tui-typography-body-s) !important;
color: var(--tui-text-primary) !important;
font-weight: normal !important;
}

View File

@@ -119,7 +119,7 @@ export type PrivateDnsValidationData = {
td,
th {
padding: 0.5rem 0.5rem !important;
font: var(--tui-font-text-s) !important;
font: var(--tui-typography-body-s) !important;
color: var(--tui-text-primary) !important;
font-weight: normal !important;
}

View File

@@ -29,7 +29,7 @@ import { PluginAddressesComponent } from './addresses/plugin.component'
flex-direction: column;
gap: 1rem;
color: var(--tui-text-secondary);
font: var(--tui-font-text-l);
font: var(--tui-typography-body-l);
::ng-deep [tuiSkeleton] {
width: 100%;

View File

@@ -3,9 +3,9 @@ import {
convertAnsi,
DownloadHTMLService,
ErrorService,
LoadingService,
} from '@start9labs/shared'
import { T } from '@start9labs/start-sdk'
import { TuiNotificationMiddleService } from '@taiga-ui/kit'
import { LogsComponent } from './logs.component'
@Directive({
@@ -13,7 +13,7 @@ import { LogsComponent } from './logs.component'
})
export class LogsDownloadDirective {
private readonly component = inject(LogsComponent)
private readonly loader = inject(LoadingService)
private readonly loader = inject(TuiNotificationMiddleService)
private readonly errorService = inject(ErrorService)
private readonly downloadHtml = inject(DownloadHTMLService)

View File

@@ -1,5 +1,5 @@
import { Directive, inject, Output } from '@angular/core'
import { IntersectionObserveeService } from '@ng-web-apis/intersection-observer'
import { WaIntersectionObserveeService } from '@ng-web-apis/intersection-observer'
import { convertAnsi, ErrorService } from '@start9labs/shared'
import { catchError, defer, filter, from, map, of, switchMap, tap } from 'rxjs'
import { LogsComponent } from './logs.component'
@@ -8,7 +8,7 @@ import { LogsComponent } from './logs.component'
selector: '[logsFetch]',
})
export class LogsFetchDirective {
private readonly observer = inject(IntersectionObserveeService)
private readonly observer = inject(WaIntersectionObserveeService)
private readonly component = inject(LogsComponent)
private readonly errors = inject(ErrorService)

View File

@@ -19,7 +19,7 @@ import { TuiIcon } from '@taiga-ui/core'
justify-content: center;
text-align: center;
padding: 1rem;
font: var(--tui-font-text-l);
font: var(--tui-typography-body-l);
color: var(--tui-text-tertiary);
tui-icon {

View File

@@ -75,7 +75,7 @@ const FILTER = ['services', 'system', 'marketplace']
</nav>
`,
styles: `
@use '@taiga-ui/core/styles/taiga-ui-local' as taiga;
@use '@taiga-ui/styles/utils' as taiga;
:host {
display: none;

View File

@@ -14,14 +14,14 @@ import { tuiIsNumber } from '@taiga-ui/cdk'
selector: 'task-info',
template: `
@if (diff.length) {
<tui-notification>
<div tuiNotification>
{{ 'The following modifications were made' | i18n }}:
<ul>
@for (d of diff; track d) {
<li [innerHTML]="d"></li>
}
</ul>
</tui-notification>
</div>
}
`,
imports: [TuiNotification, i18nPipe],

View File

@@ -6,9 +6,19 @@ import {
} from '@angular/core'
import { toSignal } from '@angular/core/rxjs-interop'
import { RouterOutlet } from '@angular/router'
import { ErrorService, LoadingService } from '@start9labs/shared'
import { TuiButton, TuiIcon, TuiLoader, TuiScrollbar } from '@taiga-ui/core'
import { TuiActionBar, TuiProgress } from '@taiga-ui/kit'
import { ErrorService } from '@start9labs/shared'
import {
TuiButton,
TuiIcon,
TuiLoader,
TuiPopup,
TuiScrollbar,
} from '@taiga-ui/core'
import {
TuiActionBar,
TuiNotificationMiddleService,
TuiProgress,
} from '@taiga-ui/kit'
import { PatchDB } from 'patch-db-client'
import { TabsComponent } from 'src/app/routes/portal/components/tabs.component'
import { ApiService } from 'src/app/services/api/embassy-api.service'
@@ -26,7 +36,7 @@ import { HeaderComponent } from './components/header/header.component'
</main>
<app-tabs />
@if (update(); as update) {
<tui-action-bar *tuiActionBar="bar()">
<tui-action-bar *tuiPopup="bar()">
@if (update === true) {
<tui-icon icon="@tui.check" class="g-positive" />
Download complete, restart to apply changes
@@ -52,7 +62,7 @@ import { HeaderComponent } from './components/header/header.component'
}
`,
styles: `
@use '@taiga-ui/core/styles/taiga-ui-local' as taiga;
@use '@taiga-ui/styles/utils' as taiga;
:host {
height: 100%;
@@ -93,10 +103,11 @@ import { HeaderComponent } from './components/header/header.component'
TuiLoader,
TuiIcon,
TuiButton,
TuiPopup,
],
})
export class PortalComponent {
private readonly loader = inject(LoadingService)
private readonly loader = inject(TuiNotificationMiddleService)
private readonly errorService = inject(ErrorService)
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
private readonly api = inject(ApiService)

View File

@@ -1,6 +1,5 @@
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import { TuiDialogService, TuiIcon, TuiTitle } from '@taiga-ui/core'
import { TuiCell } from '@taiga-ui/layout'
import { TuiDialogService, TuiIcon, TuiTitle, TuiCell } from '@taiga-ui/core'
import { BackupsUpcomingComponent } from './components/upcoming.component'
import { HISTORY } from './modals/history.component'
import { JOBS } from './modals/jobs.component'

View File

@@ -2,16 +2,14 @@ import { Component, inject } from '@angular/core'
import { FormsModule } from '@angular/forms'
import {
TuiButton,
TuiCheckbox,
TuiDialogContext,
TuiDialogOptions,
TuiGroup,
TuiLoader,
} from '@taiga-ui/core'
import { TuiBlock, TuiCheckbox } from '@taiga-ui/kit'
import {
POLYMORPHEUS_CONTEXT,
PolymorpheusComponent,
} from '@taiga-ui/polymorpheus'
import { TuiBlock } from '@taiga-ui/kit'
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { PatchDB } from 'patch-db-client'
import { firstValueFrom, map } from 'rxjs'
import { DataModel } from 'src/app/services/patch-db/data-model'
@@ -74,9 +72,7 @@ interface Package {
export class BackupsBackupModal {
private readonly patch = inject<PatchDB<DataModel>>(PatchDB)
readonly context =
inject<TuiDialogContext<string[], { btnText: string } | undefined>>(
POLYMORPHEUS_CONTEXT,
)
injectContext<TuiDialogContext<string[], { btnText: string } | undefined>>()
hasSelection = false

View File

@@ -1,17 +1,21 @@
import { Component, inject } from '@angular/core'
import { toSignal } from '@angular/core/rxjs-interop'
import { FormsModule } from '@angular/forms'
import { ErrorService, LoadingService } from '@start9labs/shared'
import { ErrorService } from '@start9labs/shared'
import { T } from '@start9labs/start-sdk'
import {
TuiButton,
TuiDialogContext,
TuiDialogService,
TuiTextfield,
TuiInput,
} from '@taiga-ui/core'
import { TuiBadge, TuiSwitch } from '@taiga-ui/kit'
import {
TuiBadge,
TuiNotificationMiddleService,
TuiSwitch,
} from '@taiga-ui/kit'
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { from, map } from 'rxjs'
import { T } from '@start9labs/start-sdk'
import { BackupJob } from 'src/app/services/api/api.types'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { ToHumanCronPipe } from '../pipes/to-human-cron.pipe'
@@ -25,7 +29,7 @@ import { TARGET, TARGET_CREATE } from './target.component'
<tui-textfield>
<label tuiLabel>Job Name</label>
<input
tuiTextfield
tuiInput
name="name"
[(ngModel)]="job.name"
placeholder="My Backup Job"
@@ -40,11 +44,12 @@ import { TARGET, TARGET_CREATE } from './target.component'
(click)="selectTarget()"
>
Target
<tui-badge
<span
tuiBadge
[appearance]="target()?.[job.targetId]?.type ? 'success' : 'warning'"
>
{{ target()?.[job.targetId]?.type || 'Select target' }}
</tui-badge>
</span>
</button>
<button
tuiButton
@@ -55,14 +60,17 @@ import { TARGET, TARGET_CREATE } from './target.component'
(click)="selectPackages()"
>
Packages
<tui-badge [appearance]="job.packageIds.length ? 'success' : 'warning'">
<span
tuiBadge
[appearance]="job.packageIds.length ? 'success' : 'warning'"
>
{{ job.packageIds.length + ' selected' }}
</tui-badge>
</span>
</button>
<tui-textfield>
<label tuiLabel>Schedule</label>
<input
tuiTextfield
tuiInput
name="cron"
[(ngModel)]="job.cron"
placeholder="* * * * *"
@@ -109,7 +117,7 @@ import { TARGET, TARGET_CREATE } from './target.component'
`,
imports: [
FormsModule,
TuiTextfield,
TuiInput,
TuiSwitch,
TuiButton,
TuiBadge,
@@ -119,7 +127,7 @@ import { TARGET, TARGET_CREATE } from './target.component'
export class BackupsEditModal {
private readonly api = inject(ApiService)
private readonly errorService = inject(ErrorService)
private readonly loader = inject(LoadingService)
private readonly loader = inject(TuiNotificationMiddleService)
private readonly dialogs = inject(TuiDialogService)
private readonly context =
injectContext<TuiDialogContext<BackupJob, BackupJobBuilder>>()

View File

@@ -1,5 +1,3 @@
import { toSignal } from '@angular/core/rxjs-interop'
import { TuiCheckbox, TuiSkeleton } from '@taiga-ui/kit'
import { CommonModule } from '@angular/common'
import {
ChangeDetectionStrategy,
@@ -7,10 +5,18 @@ import {
inject,
signal,
} from '@angular/core'
import { toSignal } from '@angular/core/rxjs-interop'
import { FormsModule } from '@angular/forms'
import { ErrorService, LoadingService } from '@start9labs/shared'
import { TUI_TRUE_HANDLER, TUI_FALSE_HANDLER } from '@taiga-ui/cdk'
import { TuiDialogService, TuiIcon, TuiLink, TuiButton } from '@taiga-ui/core'
import { ErrorService } from '@start9labs/shared'
import { TUI_FALSE_HANDLER, TUI_TRUE_HANDLER } from '@taiga-ui/cdk'
import {
TuiButton,
TuiCheckbox,
TuiDialogService,
TuiIcon,
TuiLink,
} from '@taiga-ui/core'
import { TuiNotificationMiddleService, TuiSkeleton } from '@taiga-ui/kit'
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { from } from 'rxjs'
import { REPORT } from 'src/app/components/backup-report.component'
@@ -101,7 +107,7 @@ import { HasErrorPipe } from '../pipes/has-error.pipe'
</table>
`,
styles: `
@use '@taiga-ui/core/styles/taiga-ui-local' as taiga;
@use '@taiga-ui/styles/utils' as taiga;
tui-icon {
font-size: 1rem;
@@ -170,7 +176,7 @@ export class BackupsHistoryModal {
private readonly api = inject(ApiService)
private readonly dialogs = inject(TuiDialogService)
private readonly errorService = inject(ErrorService)
private readonly loader = inject(LoadingService)
private readonly loader = inject(TuiNotificationMiddleService)
readonly targets = toSignal(from(this.api.getBackupTargets({})))
readonly runs = signal<BackupRun[] | null>(null)

View File

@@ -1,16 +1,22 @@
import { Component, inject, OnInit } from '@angular/core'
import { toSignal } from '@angular/core/rxjs-interop'
import { ErrorService, LoadingService } from '@start9labs/shared'
import { ErrorService } from '@start9labs/shared'
import {
TuiButton,
TuiDialogOptions,
TuiDialogService,
TuiIcon,
TuiButton,
TuiNotification,
TuiLink,
TuiNotification,
} from '@taiga-ui/core'
import { TuiConfirmData, TUI_CONFIRM, TuiSkeleton } from '@taiga-ui/kit'
import {
TUI_CONFIRM,
TuiConfirmData,
TuiNotificationMiddleService,
TuiSkeleton,
} from '@taiga-ui/kit'
import { PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { DocsLinkDirective } from 'projects/shared/src/public-api'
import { BehaviorSubject, filter, from } from 'rxjs'
import { BackupJob } from 'src/app/services/api/api.types'
import { ApiService } from 'src/app/services/api/embassy-api.service'
@@ -18,18 +24,17 @@ import { GetBackupIconPipe } from '../pipes/get-backup-icon.pipe'
import { ToHumanCronPipe } from '../pipes/to-human-cron.pipe'
import { BackupJobBuilder } from '../utils/job-builder'
import { EDIT } from './edit.component'
import { DocsLinkDirective } from 'projects/shared/src/public-api'
@Component({
template: `
<tui-notification>
<div tuiNotification>
Scheduling automatic backups is an excellent way to ensure your StartOS
data is safely backed up. StartOS will issue a notification whenever one
of your scheduled backups succeeds or fails.
<a tuiLink docsLink path="/start-os/user-manual/backup-create.html">
View instructions
</a>
</tui-notification>
</div>
<h3 class="g-title">
Saved Jobs
<button tuiButton size="s" iconStart="@tui.plus" (click)="create()">
@@ -147,7 +152,7 @@ import { DocsLinkDirective } from 'projects/shared/src/public-api'
})
export class BackupsJobsModal implements OnInit {
private readonly dialogs = inject(TuiDialogService)
private readonly loader = inject(LoadingService)
private readonly loader = inject(TuiNotificationMiddleService)
private readonly errorService = inject(ErrorService)
private readonly api = inject(ApiService)

View File

@@ -1,14 +1,19 @@
import { CommonModule } from '@angular/common'
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
import { FormsModule } from '@angular/forms'
import { ErrorService, LoadingService } from '@start9labs/shared'
import { ErrorService } from '@start9labs/shared'
import { T } from '@start9labs/start-sdk'
import { TuiMapperPipe } from '@taiga-ui/cdk'
import { TuiButton, TuiDialogContext, TuiGroup } from '@taiga-ui/core'
import { TuiBlock, TuiCheckbox } from '@taiga-ui/kit'
import {
TuiButton,
TuiCheckbox,
TuiDialogContext,
TuiGroup,
} from '@taiga-ui/core'
import { TuiBlock, TuiNotificationMiddleService } from '@taiga-ui/kit'
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { PatchDB } from 'patch-db-client'
import { take } from 'rxjs'
import { T } from '@start9labs/start-sdk'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { DataModel } from 'src/app/services/patch-db/data-model'
import { ToOptionsPipe } from '../pipes/to-options.pipe'
@@ -68,7 +73,7 @@ import { RecoverOption } from '../types/recover-option'
})
export class BackupsRecoverModal {
private readonly api = inject(ApiService)
private readonly loader = inject(LoadingService)
private readonly loader = inject(TuiNotificationMiddleService)
private readonly errorService = inject(ErrorService)
private readonly context =
injectContext<TuiDialogContext<void, RecoverData>>()

View File

@@ -15,8 +15,8 @@ import {
TuiIcon,
TuiLoader,
TuiTitle,
TuiCell,
} from '@taiga-ui/core'
import { TuiCell } from '@taiga-ui/layout'
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
import { PatchDB } from 'patch-db-client'
import { ApiService } from 'src/app/services/api/embassy-api.service'

Some files were not shown because too many files have changed in this diff Show More