refactor: add file control to form service

This commit is contained in:
waterplea
2025-08-06 19:07:21 +07:00
parent d8d1009417
commit b35a89da29
9 changed files with 126 additions and 119 deletions

146
web/package-lock.json generated
View File

@@ -25,19 +25,19 @@
"@noble/hashes": "^1.4.0",
"@start9labs/argon2": "^0.3.0",
"@start9labs/start-sdk": "file:../sdk/baseDist",
"@taiga-ui/addon-charts": "4.47.0",
"@taiga-ui/addon-commerce": "4.47.0",
"@taiga-ui/addon-mobile": "4.47.0",
"@taiga-ui/addon-table": "4.47.0",
"@taiga-ui/cdk": "4.47.0",
"@taiga-ui/core": "4.47.0",
"@taiga-ui/addon-charts": "4.48.0",
"@taiga-ui/addon-commerce": "4.48.0",
"@taiga-ui/addon-mobile": "4.48.0",
"@taiga-ui/addon-table": "4.48.0",
"@taiga-ui/cdk": "4.48.0",
"@taiga-ui/core": "4.48.0",
"@taiga-ui/dompurify": "4.1.11",
"@taiga-ui/event-plugins": "4.6.0",
"@taiga-ui/experimental": "4.47.0",
"@taiga-ui/icons": "4.47.0",
"@taiga-ui/kit": "4.47.0",
"@taiga-ui/layout": "4.47.0",
"@taiga-ui/legacy": "4.47.0",
"@taiga-ui/experimental": "4.48.0",
"@taiga-ui/icons": "4.48.0",
"@taiga-ui/kit": "4.48.0",
"@taiga-ui/layout": "4.48.0",
"@taiga-ui/legacy": "4.48.0",
"@taiga-ui/polymorpheus": "4.9.0",
"ansi-to-html": "^0.7.2",
"base64-js": "^1.5.1",
@@ -4714,9 +4714,9 @@
"link": true
},
"node_modules/@taiga-ui/addon-charts": {
"version": "4.47.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-charts/-/addon-charts-4.47.0.tgz",
"integrity": "sha512-BLMw9zNBJp2tC9PyuG0+7j5VrAL4QFrngGvVlSALjWy9Caj/4mHaoDp9PUwAQrsuoFIMc6BwTbDdbO6/DJeVUQ==",
"version": "4.48.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-charts/-/addon-charts-4.48.0.tgz",
"integrity": "sha512-0oEfjhV+B50ITyS5oXnVAzeclSrAVX9FiEvWkX7zJ92uy7PKzkoGx+wEsKw3m1ax0I+cVYrh+rX6VivpX4dBZw==",
"license": "Apache-2.0",
"dependencies": {
"tslib": ">=2.8.1"
@@ -4725,15 +4725,15 @@
"@angular/common": ">=16.0.0",
"@angular/core": ">=16.0.0",
"@ng-web-apis/common": "^4.12.0",
"@taiga-ui/cdk": "^4.47.0",
"@taiga-ui/core": "^4.47.0",
"@taiga-ui/cdk": "^4.48.0",
"@taiga-ui/core": "^4.48.0",
"@taiga-ui/polymorpheus": "^4.9.0"
}
},
"node_modules/@taiga-ui/addon-commerce": {
"version": "4.47.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-commerce/-/addon-commerce-4.47.0.tgz",
"integrity": "sha512-Vh9kbQ47mUT6et3gc2/yJ7N6vebXDPRLHWBhpXxrXNVwel/dQT84NkvNBRJEPeSi2KRQNAy/qlxDdBVWUOFInw==",
"version": "4.48.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-commerce/-/addon-commerce-4.48.0.tgz",
"integrity": "sha512-IGWBSRlsQmkNQfKFk90N0N7TkPsFBo0pBBuTXeuVGBo9us4AJafUAMnVlS5U77XSL1xK1pGRkazKfLgLz3yMzg==",
"license": "Apache-2.0",
"dependencies": {
"tslib": ">=2.8.1"
@@ -4746,18 +4746,18 @@
"@maskito/core": "^3.10.2",
"@maskito/kit": "^3.10.2",
"@ng-web-apis/common": "^4.12.0",
"@taiga-ui/cdk": "^4.47.0",
"@taiga-ui/core": "^4.47.0",
"@taiga-ui/i18n": "^4.47.0",
"@taiga-ui/kit": "^4.47.0",
"@taiga-ui/cdk": "^4.48.0",
"@taiga-ui/core": "^4.48.0",
"@taiga-ui/i18n": "^4.48.0",
"@taiga-ui/kit": "^4.48.0",
"@taiga-ui/polymorpheus": "^4.9.0",
"rxjs": ">=7.0.0"
}
},
"node_modules/@taiga-ui/addon-mobile": {
"version": "4.47.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-mobile/-/addon-mobile-4.47.0.tgz",
"integrity": "sha512-rJeJUpXgEJyNNriiqLmVB7w8H+dSiECXjj1LRvSPffuL5bmvtBJKq8nw5Lpy+M3xeZz5qIMndJPNxSsKmkB0JA==",
"version": "4.48.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-mobile/-/addon-mobile-4.48.0.tgz",
"integrity": "sha512-aGuCkE0T+EaKSr31R2TYuN1h1STi8iATGlNHX4kZ3+Ab/mebER8Xi7uo5gy9olMOGB65syl5Bo4VL02/wc5HKw==",
"license": "Apache-2.0",
"dependencies": {
"tslib": ">=2.8.1"
@@ -4767,18 +4767,18 @@
"@angular/common": ">=16.0.0",
"@angular/core": ">=16.0.0",
"@ng-web-apis/common": "^4.12.0",
"@taiga-ui/cdk": "^4.47.0",
"@taiga-ui/core": "^4.47.0",
"@taiga-ui/kit": "^4.47.0",
"@taiga-ui/layout": "^4.47.0",
"@taiga-ui/cdk": "^4.48.0",
"@taiga-ui/core": "^4.48.0",
"@taiga-ui/kit": "^4.48.0",
"@taiga-ui/layout": "^4.48.0",
"@taiga-ui/polymorpheus": "^4.9.0",
"rxjs": ">=7.0.0"
}
},
"node_modules/@taiga-ui/addon-table": {
"version": "4.47.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-table/-/addon-table-4.47.0.tgz",
"integrity": "sha512-5ZarUauEfPhr+S+nJIXZjyifcvim6Yi00cADI+0PmgitolArjHEB4ZrdosS+Iqfqj9znKF6gPkcmDlv6i0eMCg==",
"version": "4.48.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/addon-table/-/addon-table-4.48.0.tgz",
"integrity": "sha512-omwAOlwxom03jTWECDjSDVTOItHD6ZyiPMB5aY/HI/jjsQIZXDlPJLYLfS0+rBR4mwBWBMCXaLvVPPAPy2U4eA==",
"license": "Apache-2.0",
"dependencies": {
"tslib": ">=2.8.1"
@@ -4787,18 +4787,18 @@
"@angular/common": ">=16.0.0",
"@angular/core": ">=16.0.0",
"@ng-web-apis/intersection-observer": "^4.12.0",
"@taiga-ui/cdk": "^4.47.0",
"@taiga-ui/core": "^4.47.0",
"@taiga-ui/i18n": "^4.47.0",
"@taiga-ui/kit": "^4.47.0",
"@taiga-ui/cdk": "^4.48.0",
"@taiga-ui/core": "^4.48.0",
"@taiga-ui/i18n": "^4.48.0",
"@taiga-ui/kit": "^4.48.0",
"@taiga-ui/polymorpheus": "^4.9.0",
"rxjs": ">=7.0.0"
}
},
"node_modules/@taiga-ui/cdk": {
"version": "4.47.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/cdk/-/cdk-4.47.0.tgz",
"integrity": "sha512-TqUg+7p/IZqlk34IB4/ZqTfw7HXifX2SqL9psCEmtW5Pg9zWN9of0S9g6Ccj9ALF+4Q4JxuHQf/xQdtAXdYdqg==",
"version": "4.48.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/cdk/-/cdk-4.48.0.tgz",
"integrity": "sha512-CJdGnLqOmQsLTXDhliriVpvyjTCZNXtfqMpoBBNQwUdRC+2+0mhhltnmE2FnnyvsKYoFoZ87q1NpKkRqotvstA==",
"license": "Apache-2.0",
"dependencies": {
"tslib": "2.8.1"
@@ -4827,9 +4827,9 @@
}
},
"node_modules/@taiga-ui/core": {
"version": "4.47.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/core/-/core-4.47.0.tgz",
"integrity": "sha512-Z/6djJcMWn4/gFcdW6BDd7GU5tkGnidmfVFhSMCFoRYIY2YU7USjp6wYkjC9jhpRYZZVWVwY3IX4/GJGG5gLQg==",
"version": "4.48.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/core/-/core-4.48.0.tgz",
"integrity": "sha512-PkPN4gS1Wnf1nB1e0D9kB+wc6GMndjyAZvxntduG1UKGyFAl4rohbAJI5Fh5bjm/Gr4mQUUBX1LzeQFDY+ob6Q==",
"license": "Apache-2.0",
"dependencies": {
"tslib": ">=2.8.1"
@@ -4843,9 +4843,9 @@
"@angular/router": ">=16.0.0",
"@ng-web-apis/common": "^4.12.0",
"@ng-web-apis/mutation-observer": "^4.12.0",
"@taiga-ui/cdk": "^4.47.0",
"@taiga-ui/cdk": "^4.48.0",
"@taiga-ui/event-plugins": "^4.6.0",
"@taiga-ui/i18n": "^4.47.0",
"@taiga-ui/i18n": "^4.48.0",
"@taiga-ui/polymorpheus": "^4.9.0",
"rxjs": ">=7.0.0"
}
@@ -4880,9 +4880,9 @@
}
},
"node_modules/@taiga-ui/experimental": {
"version": "4.47.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/experimental/-/experimental-4.47.0.tgz",
"integrity": "sha512-EkswWcDKwtDhjA5A5oMtYHw9fnRBITpVb3cOwG45lQrlFBP0tipmx2QVlKKu0ga8VPjcv3W+b/pZsOWdp7UkJQ==",
"version": "4.48.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/experimental/-/experimental-4.48.0.tgz",
"integrity": "sha512-ZKVNos1nbKo5koh34TBX5AsLRqbDoNn4crFKqyXux1MmmrCLgqYxeou7/u3g9bIqC263n+p3urM/9oFC7jllBw==",
"license": "Apache-2.0",
"dependencies": {
"tslib": ">=2.8.1"
@@ -4890,18 +4890,18 @@
"peerDependencies": {
"@angular/common": ">=16.0.0",
"@angular/core": ">=16.0.0",
"@taiga-ui/addon-commerce": "^4.47.0",
"@taiga-ui/cdk": "^4.47.0",
"@taiga-ui/core": "^4.47.0",
"@taiga-ui/kit": "^4.47.0",
"@taiga-ui/addon-commerce": "^4.48.0",
"@taiga-ui/cdk": "^4.48.0",
"@taiga-ui/core": "^4.48.0",
"@taiga-ui/kit": "^4.48.0",
"@taiga-ui/polymorpheus": "^4.9.0",
"rxjs": ">=7.0.0"
}
},
"node_modules/@taiga-ui/i18n": {
"version": "4.47.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-4.47.0.tgz",
"integrity": "sha512-TC9BugM8W7IgIXy3IoLtEWlxIb0xAxm17bfAtVLH9M8BfuQY6Jk0yHVQAspmaEz8pmaoLx9Tl2bXV9ugYG/ypA==",
"version": "4.48.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-4.48.0.tgz",
"integrity": "sha512-E73l8P1YPFSydgDmz0ajn856ee7eDVIJosrgX3vpaAH1m2pICp4PYwZfqCuHwhogk/mKdAtnVZoBaOgr6ybXlg==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
@@ -4914,18 +4914,18 @@
}
},
"node_modules/@taiga-ui/icons": {
"version": "4.47.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/icons/-/icons-4.47.0.tgz",
"integrity": "sha512-/SV6RdsCZoX5uUIlHiAPMLNNY7j8nxmE8NhGFtj4E5szx+V84LIKh2oES+zawPa7lIcVUT5M+FtXAtqdMiQa2g==",
"version": "4.48.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/icons/-/icons-4.48.0.tgz",
"integrity": "sha512-TCWAQ2RshcBwgumk7UayYuDwpNQCwP6bDppsn3yz/JcKH1OagDPcLRy3oV15Gpwvi0AcrnrfE74IkeMdClMQUQ==",
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.3.0"
}
},
"node_modules/@taiga-ui/kit": {
"version": "4.47.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/kit/-/kit-4.47.0.tgz",
"integrity": "sha512-7LZNA4QInvB76Q38DRD1Ba2vIiKrpk4b/IoSmpahojYU1nrYByY69jGGD7ImxmCMJaIxymaODwHTcEJd3hW4Sw==",
"version": "4.48.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/kit/-/kit-4.48.0.tgz",
"integrity": "sha512-OraV1GAZqmBYwqTrsJPGar6d3Vo0keUhCGzd8rUxeL0ZKtRX+vsRRPtKAQP7B8IYPxnkZRQLZuV1XLZqmwEiaw==",
"license": "Apache-2.0",
"dependencies": {
"tslib": ">=2.8.1"
@@ -4943,17 +4943,17 @@
"@ng-web-apis/intersection-observer": "^4.12.0",
"@ng-web-apis/mutation-observer": "^4.12.0",
"@ng-web-apis/resize-observer": "^4.12.0",
"@taiga-ui/cdk": "^4.47.0",
"@taiga-ui/core": "^4.47.0",
"@taiga-ui/i18n": "^4.47.0",
"@taiga-ui/cdk": "^4.48.0",
"@taiga-ui/core": "^4.48.0",
"@taiga-ui/i18n": "^4.48.0",
"@taiga-ui/polymorpheus": "^4.9.0",
"rxjs": ">=7.0.0"
}
},
"node_modules/@taiga-ui/layout": {
"version": "4.47.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/layout/-/layout-4.47.0.tgz",
"integrity": "sha512-/P1dhno9/gvUT8lkbJGbNE8etm2D4G+2nRJAQwgj2Az2VePdR39nTOq0NKlGPyyjvSGi21/Mq2Hr5rRig3aglw==",
"version": "4.48.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/layout/-/layout-4.48.0.tgz",
"integrity": "sha512-Q4420HZRv4iIuC5kpGuHzbWR+njBusOjUlpKJ5B6coduw6oXP5zr/R7czZmD110+2jdLj2p4owlc0Rr+8LwNBQ==",
"license": "Apache-2.0",
"dependencies": {
"tslib": ">=2.8.1"
@@ -4961,17 +4961,17 @@
"peerDependencies": {
"@angular/common": ">=16.0.0",
"@angular/core": ">=16.0.0",
"@taiga-ui/cdk": "^4.47.0",
"@taiga-ui/core": "^4.47.0",
"@taiga-ui/kit": "^4.47.0",
"@taiga-ui/cdk": "^4.48.0",
"@taiga-ui/core": "^4.48.0",
"@taiga-ui/kit": "^4.48.0",
"@taiga-ui/polymorpheus": "^4.9.0",
"rxjs": ">=7.0.0"
}
},
"node_modules/@taiga-ui/legacy": {
"version": "4.47.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/legacy/-/legacy-4.47.0.tgz",
"integrity": "sha512-+82AOSKr2D/d8WSwdHZxW1BdL1fAh7HQEyhjCNT00hCaeQAeQ5QZNzDRX3qxFKwWZPWkC/sG8a/AtDtjkjB7Ew==",
"version": "4.48.0",
"resolved": "https://registry.npmjs.org/@taiga-ui/legacy/-/legacy-4.48.0.tgz",
"integrity": "sha512-1zv8oHcOYUs4W9T/ihL0b2psdVB7PFdLcZ6wkPBIaD/luVrdAGI1RUMrrtcm9SU6uo9hpqDkcaaymf9hnS6Itw==",
"license": "Apache-2.0",
"dependencies": {
"tslib": ">=2.8.1"

View File

@@ -46,19 +46,19 @@
"@noble/hashes": "^1.4.0",
"@start9labs/argon2": "^0.3.0",
"@start9labs/start-sdk": "file:../sdk/baseDist",
"@taiga-ui/addon-charts": "4.47.0",
"@taiga-ui/addon-commerce": "4.47.0",
"@taiga-ui/addon-mobile": "4.47.0",
"@taiga-ui/addon-table": "4.47.0",
"@taiga-ui/cdk": "4.47.0",
"@taiga-ui/core": "4.47.0",
"@taiga-ui/addon-charts": "4.48.0",
"@taiga-ui/addon-commerce": "4.48.0",
"@taiga-ui/addon-mobile": "4.48.0",
"@taiga-ui/addon-table": "4.48.0",
"@taiga-ui/cdk": "4.48.0",
"@taiga-ui/core": "4.48.0",
"@taiga-ui/dompurify": "4.1.11",
"@taiga-ui/event-plugins": "4.6.0",
"@taiga-ui/experimental": "4.47.0",
"@taiga-ui/icons": "4.47.0",
"@taiga-ui/kit": "4.47.0",
"@taiga-ui/layout": "4.47.0",
"@taiga-ui/legacy": "4.47.0",
"@taiga-ui/experimental": "4.48.0",
"@taiga-ui/icons": "4.48.0",
"@taiga-ui/kit": "4.48.0",
"@taiga-ui/layout": "4.48.0",
"@taiga-ui/legacy": "4.48.0",
"@taiga-ui/polymorpheus": "4.9.0",
"ansi-to-html": "^0.7.2",
"base64-js": "^1.5.1",

View File

@@ -1,6 +1,6 @@
import { forwardRef, Provider } from '@angular/core'
import { TUI_VALIDATION_ERRORS } from '@taiga-ui/kit'
import { IST } from '@start9labs/start-sdk'
import { TUI_FORMAT_ERROR, TUI_VALIDATION_ERRORS } from '@taiga-ui/kit'
import { FormControlComponent } from './form-control.component'
interface ValidatorsPatternError {
@@ -25,6 +25,7 @@ export const FORM_CONTROL_PROVIDERS: Provider[] = [
({ regex }) => String(regex) === String(requiredPattern),
)?.description) ||
'Invalid format',
[TUI_FORMAT_ERROR]: 'Invalid file format',
}),
},
]

View File

@@ -17,9 +17,9 @@ import { knownAuthorities, toAuthorityName } from 'src/app/utils/acme'
import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec'
export type Authority = {
url: string | null
name: string
contact: readonly string[] | null
url?: string | null
contact?: readonly string[] | null
}
export type RemoteAuthority = Authority & { url: string }
@@ -37,11 +37,6 @@ export class AuthorityService {
readonly authorities = toSignal<Authority[]>(
this.patch.watch$('serverInfo', 'network', 'acme').pipe(
map(acme => [
{
url: null,
name: toAuthorityName(null),
contact: null,
},
...Object.keys(acme).map(url => ({
url,
name: toAuthorityName(url),

View File

@@ -19,9 +19,7 @@ import { Authority, AuthorityService } from './authority.service'
@if (authority(); as authority) {
<td>{{ authority.name }}</td>
<td>{{ authority.url || '-' }}</td>
<td class="hidden">
{{ authority.contact ? authority.contact.join(', ') : '-' }}
</td>
<td class="hidden">{{ authority.contact?.join(', ') || '-' }}</td>
<td>
<button
tuiIconButton
@@ -61,6 +59,7 @@ import { Authority, AuthorityService } from './authority.service'
<a
tuiOption
new
download
iconStart="@tui.download"
href="/static/local-root-ca.crt"
>

View File

@@ -9,6 +9,7 @@ import { AuthorityService } from './authority.service'
selector: 'authorities-table',
template: `
<table [appTable]="['Provider', 'URL', 'Contact', null]">
<tr [authority]="{ name: 'Local Root CA' }"></tr>
@for (authority of authorityService.authorities(); track $index) {
<tr [authority]="authority"></tr>
} @empty {

View File

@@ -10,9 +10,7 @@ import { TableComponent } from 'src/app/routes/portal/components/table.component
selector: 'dns',
template: `
<section class="g-card">
<header>
{{ $any('Using IP') | i18n }}
</header>
<header>{{ $any('Using IP') | i18n }}</header>
@let subdomain = context.data.subdomain;
@let wanIp = context.data.gateway.ipInfo?.wanIp || ('Error' | i18n);
@@ -26,9 +24,7 @@ import { TableComponent } from 'src/app/routes/portal/components/table.component
</tr>
<tr>
<td>A</td>
<td>
{{ subdomain ? '*.' + subdomain : '*' }}
</td>
<td>{{ subdomain ? '*.' + subdomain : '*' }}</td>
<td>{{ wanIp }}</td>
<td></td>
</tr>
@@ -37,9 +33,7 @@ import { TableComponent } from 'src/app/routes/portal/components/table.component
@if (context.data.gateway.ipInfo?.deviceType !== 'wireguard') {
<section class="g-card">
<header>
{{ $any('Using Dynamic DNS') | i18n }}
</header>
<header>{{ $any('Using Dynamic DNS') | i18n }}</header>
<table [appTable]="['Type', $any('Host'), 'Value', 'Purpose']">
<tr>
<td>ALIAS</td>
@@ -57,9 +51,16 @@ import { TableComponent } from 'src/app/routes/portal/components/table.component
</section>
}
<button tuiButton size="l" (click)="testDns()">
{{ 'Test' | i18n }}
</button>
<footer class="g-buttons">
<button tuiButton size="l" (click)="testDns()">
{{ 'Test' | i18n }}
</button>
</footer>
`,
styles: `
section {
margin: 1.5rem 0;
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [TuiButton, i18nPipe, TableComponent],

View File

@@ -8,6 +8,7 @@ import {
Validators,
} from '@angular/forms'
import { IST, utils } from '@start9labs/start-sdk'
import { tuiCreateFileFormatValidator } from '@taiga-ui/kit'
import Mustache from 'mustache'
@Injectable({
@@ -83,6 +84,7 @@ export class FormService {
currentValue?: any,
): UntypedFormGroup | UntypedFormArray | UntypedFormControl {
let value: any
console.log(spec)
switch (spec.type) {
case 'text':
if (currentValue !== undefined) {
@@ -138,6 +140,8 @@ export class FormService {
case 'multiselect':
value = currentValue === undefined ? spec.default : currentValue
return this.formBuilder.control(value, multiselectValidators(spec))
case 'file':
return this.formBuilder.control(null, fileValidators(spec))
default:
return this.formBuilder.control(null)
}
@@ -236,6 +240,18 @@ function multiselectValidators(spec: IST.ValueSpecMultiselect): ValidatorFn[] {
return validators
}
function fileValidators(spec: IST.ValueSpecFile): ValidatorFn[] {
const validators: ValidatorFn[] = [
tuiCreateFileFormatValidator(spec.extensions.join(',')),
]
if (spec.required) {
validators.push(Validators.required)
}
return validators
}
function listValidators(spec: IST.ValueSpecList): ValidatorFn[] {
const validators: ValidatorFn[] = []
validators.push(listInRange(spec.minLength, spec.maxLength))

View File

@@ -51,11 +51,8 @@ hr {
var(--tui-background-base) 90%,
transparent
);
background-image: linear-gradient(
to bottom,
rgba(255, 255, 255, 0.1),
transparent
),
background-image:
linear-gradient(to bottom, rgba(255, 255, 255, 0.1), transparent),
linear-gradient(to bottom, rgba(255, 255, 255, 0.1), transparent);
box-shadow:
0 0.25rem 0.125rem rgba(0, 0, 0, 0.25),
@@ -97,11 +94,8 @@ hr {
overflow: hidden;
color: var(--tui-text-primary);
background-color: color-mix(in hsl, var(--start9-base-1) 50%, transparent);
background-image: linear-gradient(
to bottom,
var(--tui-background-neutral-2),
transparent
),
background-image:
linear-gradient(to bottom, var(--tui-background-neutral-2), transparent),
linear-gradient(to bottom, var(--tui-background-neutral-2), transparent);
background-size: 1px 100%;
background-repeat: no-repeat;
@@ -293,8 +287,8 @@ hr {
.g-buttons {
display: flex;
justify-content: flex-end;
gap: 16px;
margin-top: 24px;
gap: 1rem;
margin-top: 1.5rem;
}
.g-toggle {