17 Commits

Author SHA1 Message Date
Joel Wetzell b9950c64e7 0.14.2 2026-06-01 19:28:54 -05:00
Joel Wetzell 31f42e696d rework config updates to fix drag and drop 2026-06-01 19:28:43 -05:00
Joel Wetzell 947fd14364 0.14.1 2026-06-01 18:56:44 -05:00
Joel Wetzell 0a738b5c01 Merge pull request #61 from jwetzell/dependabot/npm_and_yarn/angular-529e36d02c
Bump the angular group with 11 updates
2026-06-01 18:49:15 -05:00
dependabot[bot] 876f41f3c6 Bump the angular group with 11 updates
Bumps the angular group with 11 updates:

| Package | From | To |
| --- | --- | --- |
| [@angular/cdk](https://github.com/angular/components) | `21.2.12` | `21.2.13` |
| [@angular/common](https://github.com/angular/angular/tree/HEAD/packages/common) | `21.2.14` | `21.2.15` |
| [@angular/compiler](https://github.com/angular/angular/tree/HEAD/packages/compiler) | `21.2.14` | `21.2.15` |
| [@angular/core](https://github.com/angular/angular/tree/HEAD/packages/core) | `21.2.14` | `21.2.15` |
| [@angular/forms](https://github.com/angular/angular/tree/HEAD/packages/forms) | `21.2.14` | `21.2.15` |
| [@angular/material](https://github.com/angular/components) | `21.2.12` | `21.2.13` |
| [@angular/platform-browser](https://github.com/angular/angular/tree/HEAD/packages/platform-browser) | `21.2.14` | `21.2.15` |
| [@angular/router](https://github.com/angular/angular/tree/HEAD/packages/router) | `21.2.14` | `21.2.15` |
| [@angular/build](https://github.com/angular/angular-cli) | `21.2.12` | `21.2.13` |
| [@angular/cli](https://github.com/angular/angular-cli) | `21.2.12` | `21.2.13` |
| [@angular/compiler-cli](https://github.com/angular/angular/tree/HEAD/packages/compiler-cli) | `21.2.14` | `21.2.15` |


Updates `@angular/cdk` from 21.2.12 to 21.2.13
- [Release notes](https://github.com/angular/components/releases)
- [Changelog](https://github.com/angular/components/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/components/compare/v21.2.12...v21.2.13)

Updates `@angular/common` from 21.2.14 to 21.2.15
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/v21.2.15/packages/common)

Updates `@angular/compiler` from 21.2.14 to 21.2.15
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/v21.2.15/packages/compiler)

Updates `@angular/core` from 21.2.14 to 21.2.15
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/v21.2.15/packages/core)

Updates `@angular/forms` from 21.2.14 to 21.2.15
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/v21.2.15/packages/forms)

Updates `@angular/material` from 21.2.12 to 21.2.13
- [Release notes](https://github.com/angular/components/releases)
- [Changelog](https://github.com/angular/components/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/components/compare/v21.2.12...v21.2.13)

Updates `@angular/platform-browser` from 21.2.14 to 21.2.15
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/v21.2.15/packages/platform-browser)

Updates `@angular/router` from 21.2.14 to 21.2.15
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/v21.2.15/packages/router)

Updates `@angular/build` from 21.2.12 to 21.2.13
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular-cli/compare/v21.2.12...v21.2.13)

Updates `@angular/cli` from 21.2.12 to 21.2.13
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular-cli/compare/v21.2.12...v21.2.13)

Updates `@angular/compiler-cli` from 21.2.14 to 21.2.15
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/v21.2.15/packages/compiler-cli)

---
updated-dependencies:
- dependency-name: "@angular/cdk"
  dependency-version: 21.2.13
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: angular
- dependency-name: "@angular/common"
  dependency-version: 21.2.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: angular
- dependency-name: "@angular/compiler"
  dependency-version: 21.2.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: angular
- dependency-name: "@angular/core"
  dependency-version: 21.2.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: angular
- dependency-name: "@angular/forms"
  dependency-version: 21.2.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: angular
- dependency-name: "@angular/material"
  dependency-version: 21.2.13
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: angular
- dependency-name: "@angular/platform-browser"
  dependency-version: 21.2.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: angular
- dependency-name: "@angular/router"
  dependency-version: 21.2.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: angular
- dependency-name: "@angular/build"
  dependency-version: 21.2.13
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: angular
- dependency-name: "@angular/cli"
  dependency-version: 21.2.13
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: angular
- dependency-name: "@angular/compiler-cli"
  dependency-version: 21.2.15
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: angular
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-30 06:43:50 +00:00
Joel Wetzell 903653e4b3 Merge pull request #58 from jwetzell/dependabot/npm_and_yarn/angular-ba26ebf674
Bump the angular group with 11 updates
2026-05-26 16:39:53 -05:00
Joel Wetzell 785c1b882c Merge pull request #59 from jwetzell/dependabot/npm_and_yarn/postcss-8.5.15
Bump postcss from 8.5.14 to 8.5.15
2026-05-26 16:38:39 -05:00
Joel Wetzell 4b1bd29487 Merge pull request #60 from jwetzell/dependabot/npm_and_yarn/vitest-4.1.7
Bump vitest from 4.1.6 to 4.1.7
2026-05-26 16:38:29 -05:00
dependabot[bot] 70d5578713 Bump vitest from 4.1.6 to 4.1.7
Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 4.1.6 to 4.1.7.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.7/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  dependency-version: 4.1.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-23 06:44:30 +00:00
dependabot[bot] fca57b0302 Bump postcss from 8.5.14 to 8.5.15
Bumps [postcss](https://github.com/postcss/postcss) from 8.5.14 to 8.5.15.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.5.14...8.5.15)

---
updated-dependencies:
- dependency-name: postcss
  dependency-version: 8.5.15
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-23 06:44:15 +00:00
dependabot[bot] 69599e688c Bump the angular group with 11 updates
Bumps the angular group with 11 updates:

| Package | From | To |
| --- | --- | --- |
| [@angular/cdk](https://github.com/angular/components) | `21.2.11` | `21.2.12` |
| [@angular/common](https://github.com/angular/angular/tree/HEAD/packages/common) | `21.2.13` | `21.2.14` |
| [@angular/compiler](https://github.com/angular/angular/tree/HEAD/packages/compiler) | `21.2.13` | `21.2.14` |
| [@angular/core](https://github.com/angular/angular/tree/HEAD/packages/core) | `21.2.13` | `21.2.14` |
| [@angular/forms](https://github.com/angular/angular/tree/HEAD/packages/forms) | `21.2.13` | `21.2.14` |
| [@angular/material](https://github.com/angular/components) | `21.2.11` | `21.2.12` |
| [@angular/platform-browser](https://github.com/angular/angular/tree/HEAD/packages/platform-browser) | `21.2.13` | `21.2.14` |
| [@angular/router](https://github.com/angular/angular/tree/HEAD/packages/router) | `21.2.13` | `21.2.14` |
| [@angular/build](https://github.com/angular/angular-cli) | `21.2.11` | `21.2.12` |
| [@angular/cli](https://github.com/angular/angular-cli) | `21.2.11` | `21.2.12` |
| [@angular/compiler-cli](https://github.com/angular/angular/tree/HEAD/packages/compiler-cli) | `21.2.13` | `21.2.14` |


Updates `@angular/cdk` from 21.2.11 to 21.2.12
- [Release notes](https://github.com/angular/components/releases)
- [Changelog](https://github.com/angular/components/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/components/compare/v21.2.11...v21.2.12)

Updates `@angular/common` from 21.2.13 to 21.2.14
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/v21.2.14/packages/common)

Updates `@angular/compiler` from 21.2.13 to 21.2.14
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/v21.2.14/packages/compiler)

Updates `@angular/core` from 21.2.13 to 21.2.14
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/v21.2.14/packages/core)

Updates `@angular/forms` from 21.2.13 to 21.2.14
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/v21.2.14/packages/forms)

Updates `@angular/material` from 21.2.11 to 21.2.12
- [Release notes](https://github.com/angular/components/releases)
- [Changelog](https://github.com/angular/components/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/components/compare/v21.2.11...v21.2.12)

Updates `@angular/platform-browser` from 21.2.13 to 21.2.14
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/v21.2.14/packages/platform-browser)

Updates `@angular/router` from 21.2.13 to 21.2.14
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/v21.2.14/packages/router)

Updates `@angular/build` from 21.2.11 to 21.2.12
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular-cli/compare/v21.2.11...v21.2.12)

Updates `@angular/cli` from 21.2.11 to 21.2.12
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular-cli/compare/v21.2.11...v21.2.12)

Updates `@angular/compiler-cli` from 21.2.13 to 21.2.14
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/v21.2.14/packages/compiler-cli)

---
updated-dependencies:
- dependency-name: "@angular/cdk"
  dependency-version: 21.2.12
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: angular
- dependency-name: "@angular/common"
  dependency-version: 21.2.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: angular
- dependency-name: "@angular/compiler"
  dependency-version: 21.2.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: angular
- dependency-name: "@angular/core"
  dependency-version: 21.2.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: angular
- dependency-name: "@angular/forms"
  dependency-version: 21.2.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: angular
- dependency-name: "@angular/material"
  dependency-version: 21.2.12
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: angular
- dependency-name: "@angular/platform-browser"
  dependency-version: 21.2.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: angular
- dependency-name: "@angular/router"
  dependency-version: 21.2.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: angular
- dependency-name: "@angular/build"
  dependency-version: 21.2.12
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: angular
- dependency-name: "@angular/cli"
  dependency-version: 21.2.12
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: angular
- dependency-name: "@angular/compiler-cli"
  dependency-version: 21.2.14
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: angular
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-23 06:44:03 +00:00
Joel Wetzell 5e22cbfbdd work on e2e testability of things 2026-05-18 18:24:16 -05:00
Joel Wetzell 26631d3bc2 fix playwright test config 2026-05-18 18:23:47 -05:00
Joel Wetzell 0fc02a46d1 use cloneDeep when updating routes and modules 2026-05-17 22:34:33 -05:00
Joel Wetzell 55267ab4db 0.14.0 2026-05-17 22:12:41 -05:00
Joel Wetzell 0d09bd534a add drag and drop to rest of lists 2026-05-17 22:12:21 -05:00
Joel Wetzell a433b0e036 change job name 2026-05-17 21:01:23 -05:00
18 changed files with 666 additions and 245 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ on:
tags: tags:
- 'v*' - 'v*'
jobs: jobs:
build-publish: build-webui:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check out repository - name: Check out repository
+15 -3
View File
@@ -1,8 +1,20 @@
import { test } from '@playwright/test'; import { expect, test } from '@playwright/test';
test('add module', async ({ page }) => { test('add module', async ({ page }) => {
await page.goto('/'); await page.goto('/');
await page.locator('app-module-list').getByText('add').click(); await page.getByRole('button', { name: 'Add Module' }).click();
await page.getByRole('menuitem', { name: 'HTTP Server' }).click(); await page.getByRole('menuitem', { name: 'HTTP Server' }).click();
// TODO(jwetzell): test that the module gets added const module = page.locator('#modules\\.0');
await expect(module).toBeVisible();
await expect(module).toContainText('HTTP Server');
});
test('remove module', async ({ page }) => {
await page.goto('/');
await page.getByRole('button', { name: 'Add Module' }).click();
await page.getByRole('menuitem', { name: 'HTTP Server' }).click();
const module = page.locator('#modules\\.0');
await expect(module).toBeVisible();
await module.getByRole('button', { name: 'Delete Module' }).click();
await expect(module).not.toBeVisible();
}); });
+19
View File
@@ -0,0 +1,19 @@
import { expect, test } from '@playwright/test';
test('add route', async ({ page }) => {
await page.goto('/');
await page.getByRole('button', { name: 'Add Route' }).click();
await page.getByRole('menuitem', { name: 'HTTP Server' }).click();
const route = page.locator('#routes\\.0');
await expect(route).toBeVisible();
await expect(route).toContainText('Route 0');
});
test('remove route', async ({ page }) => {
await page.goto('/');
await page.getByRole('button', { name: 'Add Route' }).click();
const route = page.locator('#routes\\.0');
await expect(route).toBeVisible();
await route.getByRole('button', { name: 'Delete Route' }).click();
await expect(route).not.toBeVisible();
});
+135 -135
View File
@@ -1,22 +1,22 @@
{ {
"name": "@showbridge/webui", "name": "@showbridge/webui",
"version": "0.13.1", "version": "0.14.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@showbridge/webui", "name": "@showbridge/webui",
"version": "0.13.1", "version": "0.14.2",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@angular/cdk": "21.2.11", "@angular/cdk": "21.2.13",
"@angular/common": "21.2.13", "@angular/common": "21.2.15",
"@angular/compiler": "21.2.13", "@angular/compiler": "21.2.15",
"@angular/core": "21.2.13", "@angular/core": "21.2.15",
"@angular/forms": "21.2.13", "@angular/forms": "21.2.15",
"@angular/material": "21.2.11", "@angular/material": "21.2.13",
"@angular/platform-browser": "21.2.13", "@angular/platform-browser": "21.2.15",
"@angular/router": "21.2.13", "@angular/router": "21.2.15",
"ajv": "8.20.0", "ajv": "8.20.0",
"js-yaml": "4.1.1", "js-yaml": "4.1.1",
"lodash-es": "4.18.1", "lodash-es": "4.18.1",
@@ -24,21 +24,21 @@
"tslib": "2.8.1" "tslib": "2.8.1"
}, },
"devDependencies": { "devDependencies": {
"@angular/build": "21.2.11", "@angular/build": "21.2.13",
"@angular/cli": "21.2.11", "@angular/cli": "21.2.13",
"@angular/compiler-cli": "21.2.13", "@angular/compiler-cli": "21.2.15",
"@angular/material": "21.2.11", "@angular/material": "21.2.13",
"@playwright/test": "1.60.0", "@playwright/test": "1.60.0",
"@tailwindcss/postcss": "4.3.0", "@tailwindcss/postcss": "4.3.0",
"@types/js-yaml": "4.0.9", "@types/js-yaml": "4.0.9",
"@types/lodash-es": "4.17.12", "@types/lodash-es": "4.17.12",
"jsdom": "29.1.1", "jsdom": "29.1.1",
"playwright-ng-schematics": "^21.1.0", "playwright-ng-schematics": "^21.1.0",
"postcss": "8.5.14", "postcss": "8.5.15",
"prettier": "3.8.3", "prettier": "3.8.3",
"tailwindcss": "4.3.0", "tailwindcss": "4.3.0",
"typescript": "5.9.3", "typescript": "5.9.3",
"vitest": "4.1.6" "vitest": "4.1.7"
} }
}, },
"node_modules/@algolia/abtesting": { "node_modules/@algolia/abtesting": {
@@ -278,13 +278,13 @@
} }
}, },
"node_modules/@angular-devkit/architect": { "node_modules/@angular-devkit/architect": {
"version": "0.2102.11", "version": "0.2102.13",
"resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2102.11.tgz", "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2102.13.tgz",
"integrity": "sha512-t7J8aaUho1mXjiIecPNX5/rjXeV8j8ZCGY5tD3ic5kzKxPkbuYYcQpJLdzlmBcN+wDgCmNdo8ySvItvU0m58lg==", "integrity": "sha512-fheyi0gPx6b7tT+WQ+ePlzdGqKjPLUK72wg5Z9pkVtQ5+VN/8yB9mlRlmoivngd2FeNG9wMeNynWZGYycnOWVw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@angular-devkit/core": "21.2.11", "@angular-devkit/core": "21.2.13",
"rxjs": "7.8.2" "rxjs": "7.8.2"
}, },
"bin": { "bin": {
@@ -297,9 +297,9 @@
} }
}, },
"node_modules/@angular-devkit/core": { "node_modules/@angular-devkit/core": {
"version": "21.2.11", "version": "21.2.13",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.2.11.tgz", "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.2.13.tgz",
"integrity": "sha512-kfMNh5X2hOdyr0uNFaaHUJR3OVr4oH2+UhI+FsTu7gqogdgYlHAVHhHAFulfDgtAEOiqpeSQF9RhQnCJl+/LXA==", "integrity": "sha512-9jLaHcUr6BumIY9nCsBib1q62p259nf++gd2igYJ7mLm1w/0wEacsZ1cC8wCGEe6vx8a+DrD+EVCQ6zivePG2A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -342,13 +342,13 @@
} }
}, },
"node_modules/@angular-devkit/schematics": { "node_modules/@angular-devkit/schematics": {
"version": "21.2.11", "version": "21.2.13",
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-21.2.11.tgz", "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-21.2.13.tgz",
"integrity": "sha512-69CWZ5/ftLdpUPAwwdAxTNosiGXUyvwdnOfmHsd9NvCT0OSTeq0eQ0UfnGcHASrXIVmnyWiNfBWM1DLqsgBXmw==", "integrity": "sha512-gifpOcMNiAy49lQmQKhzpxoSfS3qJQSEdJSF5m7RVFkAcmllfcCD76GPN4dhho3wdAnbZ3qr54LtDqrGY4xNjw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@angular-devkit/core": "21.2.11", "@angular-devkit/core": "21.2.13",
"jsonc-parser": "3.3.1", "jsonc-parser": "3.3.1",
"magic-string": "0.30.21", "magic-string": "0.30.21",
"ora": "9.3.0", "ora": "9.3.0",
@@ -361,14 +361,14 @@
} }
}, },
"node_modules/@angular/build": { "node_modules/@angular/build": {
"version": "21.2.11", "version": "21.2.13",
"resolved": "https://registry.npmjs.org/@angular/build/-/build-21.2.11.tgz", "resolved": "https://registry.npmjs.org/@angular/build/-/build-21.2.13.tgz",
"integrity": "sha512-2afR6VKkP0HH2u6OuijSMgSHsL5tU4CBCixgQtY677mlvS8TOZg/kOksJIUlz0EvDVCJZBK8WLH9cPJ6mC/Qdg==", "integrity": "sha512-Y9TDAaTQ+E5LScCKA/hPZmns/7Mpu6J2BiPj2cETA1xNjvgRpeb5Mh32KuhZb20NSFLvjpdnLuBTTtbym7hevw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@ampproject/remapping": "2.3.0", "@ampproject/remapping": "2.3.0",
"@angular-devkit/architect": "0.2102.11", "@angular-devkit/architect": "0.2102.13",
"@babel/core": "7.29.0", "@babel/core": "7.29.0",
"@babel/helper-annotate-as-pure": "7.27.3", "@babel/helper-annotate-as-pure": "7.27.3",
"@babel/helper-split-export-declaration": "7.24.7", "@babel/helper-split-export-declaration": "7.24.7",
@@ -411,7 +411,7 @@
"@angular/platform-browser": "^21.0.0", "@angular/platform-browser": "^21.0.0",
"@angular/platform-server": "^21.0.0", "@angular/platform-server": "^21.0.0",
"@angular/service-worker": "^21.0.0", "@angular/service-worker": "^21.0.0",
"@angular/ssr": "^21.2.11", "@angular/ssr": "^21.2.13",
"karma": "^6.4.0", "karma": "^6.4.0",
"less": "^4.2.0", "less": "^4.2.0",
"ng-packagr": "^21.0.0", "ng-packagr": "^21.0.0",
@@ -461,9 +461,9 @@
} }
}, },
"node_modules/@angular/cdk": { "node_modules/@angular/cdk": {
"version": "21.2.11", "version": "21.2.13",
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-21.2.11.tgz", "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-21.2.13.tgz",
"integrity": "sha512-QS5f0si1LgDQFgxXtpuDr7gBFVSQLl1fdnx3UA6GrzSmuV0sfzBhBq7NlZGxZTDaKy4ZxY34RFx4Bvm1zo+gBA==", "integrity": "sha512-nQGGJ6Efqi8n0qhT/PllsaIIY+vz+TL7/tpR7F2QKiqzS/9l4m7ea0vvS6fSMGrjEbqbkzTHbjLDsIg6X2hK+w==",
"license": "MIT", "license": "MIT",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
@@ -478,19 +478,19 @@
} }
}, },
"node_modules/@angular/cli": { "node_modules/@angular/cli": {
"version": "21.2.11", "version": "21.2.13",
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-21.2.11.tgz", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-21.2.13.tgz",
"integrity": "sha512-vpF/oa+HzLl4lF78ePCgkhBdQj29IlFvZtBsbAXXpb16FLZSua2m7+yHd/PICTlchh1+LfIxFY9snMY1BllBsQ==", "integrity": "sha512-j1kOV/f0og/3xCwG7Y8RyPd6V7uYfX2NuvXbvN1mzgxLLN2mu6CTsvPg5l/9Pu9SJI3KOPRgDxWyuP3k8KuzMg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@angular-devkit/architect": "0.2102.11", "@angular-devkit/architect": "0.2102.13",
"@angular-devkit/core": "21.2.11", "@angular-devkit/core": "21.2.13",
"@angular-devkit/schematics": "21.2.11", "@angular-devkit/schematics": "21.2.13",
"@inquirer/prompts": "7.10.1", "@inquirer/prompts": "7.10.1",
"@listr2/prompt-adapter-inquirer": "3.0.5", "@listr2/prompt-adapter-inquirer": "3.0.5",
"@modelcontextprotocol/sdk": "1.26.0", "@modelcontextprotocol/sdk": "1.26.0",
"@schematics/angular": "21.2.11", "@schematics/angular": "21.2.13",
"@yarnpkg/lockfile": "1.1.0", "@yarnpkg/lockfile": "1.1.0",
"algoliasearch": "5.48.1", "algoliasearch": "5.48.1",
"ini": "6.0.0", "ini": "6.0.0",
@@ -513,9 +513,9 @@
} }
}, },
"node_modules/@angular/common": { "node_modules/@angular/common": {
"version": "21.2.13", "version": "21.2.15",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-21.2.13.tgz", "resolved": "https://registry.npmjs.org/@angular/common/-/common-21.2.15.tgz",
"integrity": "sha512-fNvRmGAX0zbsLX/kJjgb6l8HAuGTpfYRNc06taTCIvED2RsRpfwrh79IxYlPBspr+hpFbHa0/kxU6Q5I8V0jKQ==", "integrity": "sha512-PHbICQe4YCXnax2FcmKUpiffs8XPW9A0KlZF35qgJoQyBMBZx5F8c8geCh25jxtq77n3eBTmOa/WIAdSqiitkQ==",
"license": "MIT", "license": "MIT",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
@@ -525,14 +525,14 @@
"node": "^20.19.0 || ^22.12.0 || >=24.0.0" "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/core": "21.2.13", "@angular/core": "21.2.15",
"rxjs": "^6.5.3 || ^7.4.0" "rxjs": "^6.5.3 || ^7.4.0"
} }
}, },
"node_modules/@angular/compiler": { "node_modules/@angular/compiler": {
"version": "21.2.13", "version": "21.2.15",
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.2.13.tgz", "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.2.15.tgz",
"integrity": "sha512-0OZk5ujHgowRme3iXJ1Ce1OI3eTDcGovBARBiyJT0E8kt9Y0TdQdGaYMRrNN1UzDv4hk8f1d/xVeF0BpMTvqPQ==", "integrity": "sha512-nwpNb+NbVUNzR3cck0QXbU/oFK7BpmXOXVnN/w7+P4+TsFUYeTtO1Ojbc15jkqe6mSM0lBvGlcoztVblHQkqcw==",
"license": "MIT", "license": "MIT",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
@@ -543,9 +543,9 @@
} }
}, },
"node_modules/@angular/compiler-cli": { "node_modules/@angular/compiler-cli": {
"version": "21.2.13", "version": "21.2.15",
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-21.2.13.tgz", "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-21.2.15.tgz",
"integrity": "sha512-ueETJy2ZcXZ4a0aLEr+oPMw26f8Hn903WC4QN0MCH+sLB9Zustpzydqtmzo5mdSzwuoLoxcesYJTZFmpwD1xIQ==", "integrity": "sha512-/MU7OA9d/e9P5SthR+N6JJObBmzcGsgNQaeQ2YfSUnU0lCRVQweTWwxLFDbfU6UX8MZFWB6pdI57zod8r5kXUw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true, "peer": true,
@@ -567,7 +567,7 @@
"node": "^20.19.0 || ^22.12.0 || >=24.0.0" "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/compiler": "21.2.13", "@angular/compiler": "21.2.15",
"typescript": ">=5.9 <6.1" "typescript": ">=5.9 <6.1"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
@@ -577,9 +577,9 @@
} }
}, },
"node_modules/@angular/core": { "node_modules/@angular/core": {
"version": "21.2.13", "version": "21.2.15",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-21.2.13.tgz", "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.2.15.tgz",
"integrity": "sha512-23tS4oNL8nvkHcI4l9rbruQs2WS4yqQmBVQxWakqS9cmRpArLGgveR+hKNU5tPXm5EAi8oLO34/Zy7z70jUpCg==", "integrity": "sha512-J5JsUnNtQURdeA7EA3DoCsMBizW3l01gfqM326Al72Ou3woFWmRb5P3LOXpIOzAeMQhO6Z5tW+B1t+4qmoq7uw==",
"license": "MIT", "license": "MIT",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
@@ -589,7 +589,7 @@
"node": "^20.19.0 || ^22.12.0 || >=24.0.0" "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/compiler": "21.2.13", "@angular/compiler": "21.2.15",
"rxjs": "^6.5.3 || ^7.4.0", "rxjs": "^6.5.3 || ^7.4.0",
"zone.js": "~0.15.0 || ~0.16.0" "zone.js": "~0.15.0 || ~0.16.0"
}, },
@@ -603,9 +603,9 @@
} }
}, },
"node_modules/@angular/forms": { "node_modules/@angular/forms": {
"version": "21.2.13", "version": "21.2.15",
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-21.2.13.tgz", "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-21.2.15.tgz",
"integrity": "sha512-efAKdL8eVRlGvcJWrUFcYyRE/togWfopUTw2D5TIkDAndnmmRaWA70wD4n/E1FFV5UdxSBxoyEYE0qVlPiewtQ==", "integrity": "sha512-swGUHgbBrPNvODPR9qBP6+vT2EHiyW361iEgS3HpTmvDhF/kD4l8NE0vh3P5N0DnEtGh4umOCKfQ1w6hPJ7lqA==",
"license": "MIT", "license": "MIT",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
@@ -616,23 +616,23 @@
"node": "^20.19.0 || ^22.12.0 || >=24.0.0" "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/common": "21.2.13", "@angular/common": "21.2.15",
"@angular/core": "21.2.13", "@angular/core": "21.2.15",
"@angular/platform-browser": "21.2.13", "@angular/platform-browser": "21.2.15",
"rxjs": "^6.5.3 || ^7.4.0" "rxjs": "^6.5.3 || ^7.4.0"
} }
}, },
"node_modules/@angular/material": { "node_modules/@angular/material": {
"version": "21.2.11", "version": "21.2.13",
"resolved": "https://registry.npmjs.org/@angular/material/-/material-21.2.11.tgz", "resolved": "https://registry.npmjs.org/@angular/material/-/material-21.2.13.tgz",
"integrity": "sha512-XcLJ4nyrKpa1wHqJm53ohxmecVxwK0ppbesGyHIvcxZzgbzwZ8FyU00k6sy+6NWlGuikqmrEu3Gm5A8LRYOFXQ==", "integrity": "sha512-6gWFb9LNh4cRIvkdocktej6MUVuGa9HQvap+j9gbZOtiveD7ER+FByUPlLlypreRebF29G2MRZeshKSdmv4NbA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/cdk": "21.2.11", "@angular/cdk": "21.2.13",
"@angular/common": "^21.0.0 || ^22.0.0", "@angular/common": "^21.0.0 || ^22.0.0",
"@angular/core": "^21.0.0 || ^22.0.0", "@angular/core": "^21.0.0 || ^22.0.0",
"@angular/forms": "^21.0.0 || ^22.0.0", "@angular/forms": "^21.0.0 || ^22.0.0",
@@ -641,9 +641,9 @@
} }
}, },
"node_modules/@angular/platform-browser": { "node_modules/@angular/platform-browser": {
"version": "21.2.13", "version": "21.2.15",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.2.13.tgz", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.2.15.tgz",
"integrity": "sha512-96rcwLHsklqAYRuS2SEBOUdQS5PLkuUIEEIjpYu4rxU2PVvOMapJEImM/QBxrbwjnCgRbj/CivkgfjiR0R0wSA==", "integrity": "sha512-O4ZHVV/rxkK1AuiD9M3UssL/HkoQvBcZy2+U421IMNibclGhwH9aRwc/0ZlQ7zpseS9+KPZ23FebvN4/92IbPg==",
"license": "MIT", "license": "MIT",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
@@ -653,9 +653,9 @@
"node": "^20.19.0 || ^22.12.0 || >=24.0.0" "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/animations": "21.2.13", "@angular/animations": "21.2.15",
"@angular/common": "21.2.13", "@angular/common": "21.2.15",
"@angular/core": "21.2.13" "@angular/core": "21.2.15"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"@angular/animations": { "@angular/animations": {
@@ -664,9 +664,9 @@
} }
}, },
"node_modules/@angular/router": { "node_modules/@angular/router": {
"version": "21.2.13", "version": "21.2.15",
"resolved": "https://registry.npmjs.org/@angular/router/-/router-21.2.13.tgz", "resolved": "https://registry.npmjs.org/@angular/router/-/router-21.2.15.tgz",
"integrity": "sha512-/JXtdhUH/rDGiJmUNrrbs52Aji4sygVCz5HIBujrnj3cjreKam7n98Ufkh0aZvAKybdGd5A8srNUFePzAvfExQ==", "integrity": "sha512-Cej4hYkmaTB6wXn1xQPlr4O1wHgUD0WLv//Oue1IssKqL8vkzic5f5x/H/bxtxxGlSnc+i6uIUF/lvjdGoWk/A==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"tslib": "^2.3.0" "tslib": "^2.3.0"
@@ -675,9 +675,9 @@
"node": "^20.19.0 || ^22.12.0 || >=24.0.0" "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/common": "21.2.13", "@angular/common": "21.2.15",
"@angular/core": "21.2.13", "@angular/core": "21.2.15",
"@angular/platform-browser": "21.2.13", "@angular/platform-browser": "21.2.15",
"rxjs": "^6.5.3 || ^7.4.0" "rxjs": "^6.5.3 || ^7.4.0"
} }
}, },
@@ -3844,14 +3844,14 @@
] ]
}, },
"node_modules/@schematics/angular": { "node_modules/@schematics/angular": {
"version": "21.2.11", "version": "21.2.13",
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-21.2.11.tgz", "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-21.2.13.tgz",
"integrity": "sha512-EqH12Fr3vaWFpsilFDFXkxwMIidEDZr5cGl0w2hDRG7DjXE2oRB/VXix8xmpuHkzJ40Jgew6hIc+bfbwQhFK1A==", "integrity": "sha512-e5guslSLKbb3PJ6gUuVqM+V9xgn68cJkG1IyBohho34shbpOeoWW2eYdWQQjxvn0KUdgEhYSRBluBamCHngaUA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@angular-devkit/core": "21.2.11", "@angular-devkit/core": "21.2.13",
"@angular-devkit/schematics": "21.2.11", "@angular-devkit/schematics": "21.2.13",
"jsonc-parser": "3.3.1" "jsonc-parser": "3.3.1"
}, },
"engines": { "engines": {
@@ -4360,16 +4360,16 @@
} }
}, },
"node_modules/@vitest/expect": { "node_modules/@vitest/expect": {
"version": "4.1.6", "version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.6.tgz", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.7.tgz",
"integrity": "sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg==", "integrity": "sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@standard-schema/spec": "^1.1.0", "@standard-schema/spec": "^1.1.0",
"@types/chai": "^5.2.2", "@types/chai": "^5.2.2",
"@vitest/spy": "4.1.6", "@vitest/spy": "4.1.7",
"@vitest/utils": "4.1.6", "@vitest/utils": "4.1.7",
"chai": "^6.2.2", "chai": "^6.2.2",
"tinyrainbow": "^3.1.0" "tinyrainbow": "^3.1.0"
}, },
@@ -4378,13 +4378,13 @@
} }
}, },
"node_modules/@vitest/mocker": { "node_modules/@vitest/mocker": {
"version": "4.1.6", "version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.6.tgz", "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.7.tgz",
"integrity": "sha512-MCFc63czMjEInOlcY2cpQCvCN+KgbAn+60xu9cMgP4sKaLC5JNAKw7JH8QdAnoAC88hW1IiSNZ+GgVXlN1UcMQ==", "integrity": "sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vitest/spy": "4.1.6", "@vitest/spy": "4.1.7",
"estree-walker": "^3.0.3", "estree-walker": "^3.0.3",
"magic-string": "^0.30.21" "magic-string": "^0.30.21"
}, },
@@ -4405,9 +4405,9 @@
} }
}, },
"node_modules/@vitest/pretty-format": { "node_modules/@vitest/pretty-format": {
"version": "4.1.6", "version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.6.tgz", "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.7.tgz",
"integrity": "sha512-h5SxD/IzNhZYnrSZRsUZQIC+vD0GY8cUvq0iwsmkFKixRCKLLWqCXa/FIQ4S1R+sI+PGoojkHsdNrbZiM9Qpgw==", "integrity": "sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -4418,13 +4418,13 @@
} }
}, },
"node_modules/@vitest/runner": { "node_modules/@vitest/runner": {
"version": "4.1.6", "version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.6.tgz", "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.7.tgz",
"integrity": "sha512-nOPCmn2+yD0ZNmKdsXGv/UxMMWbMuKeD6GyYncNwdkYDxpQvrPSKYj2rWuDjC2Y4b6w6hjip5dBKFzEUuZe3vA==", "integrity": "sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vitest/utils": "4.1.6", "@vitest/utils": "4.1.7",
"pathe": "^2.0.3" "pathe": "^2.0.3"
}, },
"funding": { "funding": {
@@ -4432,14 +4432,14 @@
} }
}, },
"node_modules/@vitest/snapshot": { "node_modules/@vitest/snapshot": {
"version": "4.1.6", "version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.6.tgz", "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.7.tgz",
"integrity": "sha512-YhsdE6xAVfTDmzjxL2ZDUvjj+ZsgyOKe+TdQzqkD72wIOmHka8NuGQ6NpTNZv9D2Z63fbwWKJPeVpEw4EQgYxw==", "integrity": "sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vitest/pretty-format": "4.1.6", "@vitest/pretty-format": "4.1.7",
"@vitest/utils": "4.1.6", "@vitest/utils": "4.1.7",
"magic-string": "^0.30.21", "magic-string": "^0.30.21",
"pathe": "^2.0.3" "pathe": "^2.0.3"
}, },
@@ -4448,9 +4448,9 @@
} }
}, },
"node_modules/@vitest/spy": { "node_modules/@vitest/spy": {
"version": "4.1.6", "version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.6.tgz", "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.7.tgz",
"integrity": "sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg==", "integrity": "sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"funding": { "funding": {
@@ -4458,13 +4458,13 @@
} }
}, },
"node_modules/@vitest/utils": { "node_modules/@vitest/utils": {
"version": "4.1.6", "version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.6.tgz", "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.7.tgz",
"integrity": "sha512-FxIY+U81R3LGKCxaHHFRQ5+g6/iRgGLmeHWdp2Amj4ljQRrEIWHmZyDfDYBRZlpyqA7qKxtS9DD1dhk8RnRIVQ==", "integrity": "sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vitest/pretty-format": "4.1.6", "@vitest/pretty-format": "4.1.7",
"convert-source-map": "^2.0.0", "convert-source-map": "^2.0.0",
"tinyrainbow": "^3.1.0" "tinyrainbow": "^3.1.0"
}, },
@@ -7172,9 +7172,9 @@
} }
}, },
"node_modules/nanoid": { "node_modules/nanoid": {
"version": "3.3.11", "version": "3.3.12",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -7800,9 +7800,9 @@
} }
}, },
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.5.14", "version": "8.5.15",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
"integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -7821,7 +7821,7 @@
"license": "MIT", "license": "MIT",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
"nanoid": "^3.3.11", "nanoid": "^3.3.12",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",
"source-map-js": "^1.2.1" "source-map-js": "^1.2.1"
}, },
@@ -9011,20 +9011,20 @@
} }
}, },
"node_modules/vitest": { "node_modules/vitest": {
"version": "4.1.6", "version": "4.1.7",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.6.tgz", "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.7.tgz",
"integrity": "sha512-6lvjbS3p9b4CrdCmguzbh2/4uoXhGE2q71R4OX5sqF9R1bo9Xd6fGrMAfvp5wnCzlBnFVdCOp6onuTQVbo8iUQ==", "integrity": "sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
"@vitest/expect": "4.1.6", "@vitest/expect": "4.1.7",
"@vitest/mocker": "4.1.6", "@vitest/mocker": "4.1.7",
"@vitest/pretty-format": "4.1.6", "@vitest/pretty-format": "4.1.7",
"@vitest/runner": "4.1.6", "@vitest/runner": "4.1.7",
"@vitest/snapshot": "4.1.6", "@vitest/snapshot": "4.1.7",
"@vitest/spy": "4.1.6", "@vitest/spy": "4.1.7",
"@vitest/utils": "4.1.6", "@vitest/utils": "4.1.7",
"es-module-lexer": "^2.0.0", "es-module-lexer": "^2.0.0",
"expect-type": "^1.3.0", "expect-type": "^1.3.0",
"magic-string": "^0.30.21", "magic-string": "^0.30.21",
@@ -9052,12 +9052,12 @@
"@edge-runtime/vm": "*", "@edge-runtime/vm": "*",
"@opentelemetry/api": "^1.9.0", "@opentelemetry/api": "^1.9.0",
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
"@vitest/browser-playwright": "4.1.6", "@vitest/browser-playwright": "4.1.7",
"@vitest/browser-preview": "4.1.6", "@vitest/browser-preview": "4.1.7",
"@vitest/browser-webdriverio": "4.1.6", "@vitest/browser-webdriverio": "4.1.7",
"@vitest/coverage-istanbul": "4.1.6", "@vitest/coverage-istanbul": "4.1.7",
"@vitest/coverage-v8": "4.1.6", "@vitest/coverage-v8": "4.1.7",
"@vitest/ui": "4.1.6", "@vitest/ui": "4.1.7",
"happy-dom": "*", "happy-dom": "*",
"jsdom": "*", "jsdom": "*",
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0" "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
+15 -15
View File
@@ -1,6 +1,6 @@
{ {
"name": "@showbridge/webui", "name": "@showbridge/webui",
"version": "0.13.1", "version": "0.14.2",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve",
@@ -28,14 +28,14 @@
"license": "MIT", "license": "MIT",
"packageManager": "npm@11.6.2", "packageManager": "npm@11.6.2",
"dependencies": { "dependencies": {
"@angular/cdk": "21.2.11", "@angular/cdk": "21.2.13",
"@angular/common": "21.2.13", "@angular/common": "21.2.15",
"@angular/compiler": "21.2.13", "@angular/compiler": "21.2.15",
"@angular/core": "21.2.13", "@angular/core": "21.2.15",
"@angular/forms": "21.2.13", "@angular/forms": "21.2.15",
"@angular/material": "21.2.11", "@angular/material": "21.2.13",
"@angular/platform-browser": "21.2.13", "@angular/platform-browser": "21.2.15",
"@angular/router": "21.2.13", "@angular/router": "21.2.15",
"ajv": "8.20.0", "ajv": "8.20.0",
"js-yaml": "4.1.1", "js-yaml": "4.1.1",
"lodash-es": "4.18.1", "lodash-es": "4.18.1",
@@ -43,20 +43,20 @@
"tslib": "2.8.1" "tslib": "2.8.1"
}, },
"devDependencies": { "devDependencies": {
"@angular/build": "21.2.11", "@angular/build": "21.2.13",
"@angular/cli": "21.2.11", "@angular/cli": "21.2.13",
"@angular/compiler-cli": "21.2.13", "@angular/compiler-cli": "21.2.15",
"@angular/material": "21.2.11", "@angular/material": "21.2.13",
"@playwright/test": "1.60.0", "@playwright/test": "1.60.0",
"@tailwindcss/postcss": "4.3.0", "@tailwindcss/postcss": "4.3.0",
"@types/js-yaml": "4.0.9", "@types/js-yaml": "4.0.9",
"@types/lodash-es": "4.17.12", "@types/lodash-es": "4.17.12",
"jsdom": "29.1.1", "jsdom": "29.1.1",
"playwright-ng-schematics": "^21.1.0", "playwright-ng-schematics": "^21.1.0",
"postcss": "8.5.14", "postcss": "8.5.15",
"prettier": "3.8.3", "prettier": "3.8.3",
"tailwindcss": "4.3.0", "tailwindcss": "4.3.0",
"typescript": "5.9.3", "typescript": "5.9.3",
"vitest": "4.1.6" "vitest": "4.1.7"
} }
} }
+6 -1
View File
@@ -24,9 +24,13 @@ export default defineConfig({
webServer: [ webServer: [
{ {
name: 'showbridge', name: 'showbridge',
command: 'showbridge --config config.yaml', command: 'showbridge --config config.test.yaml',
url: 'http://localhost:8080/health', url: 'http://localhost:8080/health',
timeout: 10000, timeout: 10000,
gracefulShutdown: {
signal: 'SIGTERM',
timeout: 5000,
},
}, },
], ],
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
@@ -36,6 +40,7 @@ export default defineConfig({
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry', trace: 'on-first-retry',
video: 'retain-on-failure',
}, },
/* Configure projects for major browsers */ /* Configure projects for major browsers */
projects: [ projects: [
+1 -1
View File
@@ -52,7 +52,7 @@
<div class="grow overflow-y-auto"> <div class="grow overflow-y-auto">
<app-module-list <app-module-list
[modules]="config()?.modules" [modules]="config()?.modules"
(modulesChange)="modulesUpdated($event)" (updated)="modulesUpdated($event)"
></app-module-list> ></app-module-list>
<app-route-list <app-route-list
[routes]="config()?.routes" [routes]="config()?.routes"
@@ -5,6 +5,8 @@
matTooltip="Add Module" matTooltip="Add Module"
class="flex items-center justify-center w-10 h-10 bg-transparent hover:bg-gray-700 hover:cursor-pointer text-blue-400" class="flex items-center justify-center w-10 h-10 bg-transparent hover:bg-gray-700 hover:cursor-pointer text-blue-400"
[matMenuTriggerFor]="addModuleMenu" [matMenuTriggerFor]="addModuleMenu"
role="button"
aria-label="Add Module"
> >
<mat-icon>add</mat-icon> <mat-icon>add</mat-icon>
</div> </div>
@@ -18,14 +20,21 @@
} }
<div class="text-white">Modules</div> <div class="text-white">Modules</div>
</div> </div>
<div class="grow overflow-x-hidden overflow-y-auto gap-1.5"> <div
class="grow overflow-x-hidden overflow-y-auto gap-1.5"
cdkDropList
(cdkDropListDropped)="drop($event)"
[cdkDropListData]="modules()"
>
@for (module of modules(); track module.id; let i = $index) { @for (module of modules(); track module.id; let i = $index) {
<div class="m-2"> <div class="m-2" cdkDrag>
<app-module <app-module
[id]="listService.pathToId(`modules/${i}`)"
[path]="`modules/${i}`" [path]="`modules/${i}`"
(moduleChange)="moduleUpdated(i, $event)"
[module]="module" [module]="module"
(updated)="moduleUpdated(i, $event)"
(delete)="deleteModule(i)" (delete)="deleteModule(i)"
[inDragList]="true"
></app-module> ></app-module>
</div> </div>
} }
+49 -31
View File
@@ -1,22 +1,35 @@
import { Component, inject, model } from '@angular/core'; import { CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop';
import { Component, inject, input, output } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu'; import { MatMenuModule } from '@angular/material/menu';
import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSnackBar } from '@angular/material/snack-bar';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { cloneDeep } from 'lodash-es';
import { ModuleConfig } from '../../models/config'; import { ModuleConfig } from '../../models/config';
import { ListsService } from '../../services/lists';
import { SchemaService } from '../../services/schema'; import { SchemaService } from '../../services/schema';
import { ModuleComponent } from '../module/module'; import { ModuleComponent } from '../module/module';
@Component({ @Component({
selector: 'app-module-list', selector: 'app-module-list',
imports: [MatMenuModule, MatIconModule, MatButtonModule, MatTooltipModule, ModuleComponent], imports: [
MatMenuModule,
MatIconModule,
MatButtonModule,
MatTooltipModule,
ModuleComponent,
CdkDrag,
CdkDropList,
],
templateUrl: './module-list.html', templateUrl: './module-list.html',
styleUrl: './module-list.css', styleUrl: './module-list.css',
}) })
export class ModuleListComponent { export class ModuleListComponent {
modules = model<ModuleConfig[]>(); modules = input<ModuleConfig[]>();
updated = output<ModuleConfig[]>();
public schemaService = inject(SchemaService); public schemaService = inject(SchemaService);
public listService = inject(ListsService);
private snackBar = inject(MatSnackBar); private snackBar = inject(MatSnackBar);
@@ -25,46 +38,51 @@ export class ModuleListComponent {
console.error('module is undefined, not updating'); console.error('module is undefined, not updating');
return; return;
} }
this.modules.update((modules) => { const currentModules = this.modules();
if (modules) { if (currentModules === undefined) {
modules[index].id = module.id; console.error('modules is undefined, cannot update');
modules[index].type = module.type; return;
if (module.params !== undefined) { }
modules[index].params = module.params; currentModules[index] = cloneDeep(module);
} this.updated.emit(cloneDeep(currentModules));
return [...modules];
}
return modules;
});
} }
addModule(moduleType: string) { addModule(moduleType: string) {
const moduleTemplate = this.schemaService.getSkeletonForModule(moduleType); const moduleTemplate = this.schemaService.getSkeletonForModule(moduleType);
this.modules.update((modules) => { const currentModules = this.modules() || [];
if (modules) { if (currentModules === undefined) {
if (!modules) { console.error('modules is undefined, cannot update');
modules = []; return;
} }
modules?.push(moduleTemplate); currentModules.push(moduleTemplate);
return [...modules]; this.updated.emit(cloneDeep(currentModules));
}
return modules;
});
this.snackBar.open('Module Added', 'Dismiss', { this.snackBar.open('Module Added', 'Dismiss', {
duration: 3000, duration: 3000,
}); });
} }
deleteModule(index: number) { deleteModule(index: number) {
this.modules.update((modules) => { const currentModules = this.modules();
if (modules) { if (currentModules === undefined) {
modules?.splice(index, 1); console.error('modules is undefined, cannot delete');
return [...modules]; return;
} }
return []; currentModules.splice(index, 1);
}); this.updated.emit(cloneDeep(currentModules));
this.snackBar.open('Module Removed', 'Dismiss', { this.snackBar.open('Module Removed', 'Dismiss', {
duration: 3000, duration: 3000,
}); });
} }
drop(event: CdkDragDrop<ModuleConfig[] | undefined>) {
if (event.previousContainer === event.container) {
const currentModules = this.modules();
if (currentModules === undefined) {
console.error('modules is undefined, not updating');
return;
}
moveItemInArray(currentModules, event.previousIndex, event.currentIndex);
this.updated.emit(cloneDeep(currentModules));
}
}
} }
+125
View File
@@ -5,6 +5,14 @@
}} border-solid w-full h-full" }} border-solid w-full h-full"
> >
<div class="flex items-center justify-center"> <div class="flex items-center justify-center">
@if (inDragList()) {
<div
cdkDragHandle
class="flex items-center justify-center bg-transparent hover:bg-gray-700 hover:cursor-move text-blue-400"
>
<mat-icon>drag_indicator</mat-icon>
</div>
}
<div class="flex items-center"> <div class="flex items-center">
<mat-icon [style.color]="inputIndicatorColor()" matTooltip="Input Indicator" <mat-icon [style.color]="inputIndicatorColor()" matTooltip="Input Indicator"
>keyboard_arrow_down</mat-icon >keyboard_arrow_down</mat-icon
@@ -20,6 +28,8 @@
<div <div
class="flex items-center justify-center hover:bg-gray-700 hover:cursor-pointer" class="flex items-center justify-center hover:bg-gray-700 hover:cursor-pointer"
(click)="delete.emit()" (click)="delete.emit()"
role="button"
aria-label="Delete Module"
> >
<mat-icon class="text-red-500!">close</mat-icon> <mat-icon class="text-red-500!">close</mat-icon>
</div> </div>
@@ -66,4 +76,119 @@
} }
</div> </div>
</div> </div>
<div *cdkDragPreview class="flex items-start m-2 w-fit">
<div
class="flex flex-col bg-gray-800 border-2 {{
isInError() ? 'border-red-500' : 'border-gray-600'
}} border-solid w-full h-full"
>
<div class="flex items-center justify-center">
@if (inDragList()) {
<div
cdkDragHandle
class="flex items-center justify-center bg-transparent hover:bg-gray-700 hover:cursor-move text-blue-400"
>
<mat-icon>drag_indicator</mat-icon>
</div>
}
<div class="flex items-center">
<mat-icon [style.color]="inputIndicatorColor()" matTooltip="Input Indicator"
>keyboard_arrow_down</mat-icon
>
<mat-icon [style.color]="outputIndicatorColor()" matTooltip="Output Indicator"
>keyboard_arrow_up</mat-icon
>
</div>
<div class="text-white">
{{ schema().title || module()?.type }}
</div>
<div class="grow"></div>
<div
class="flex items-center justify-center hover:bg-gray-700 hover:cursor-pointer"
(click)="delete.emit()"
>
<mat-icon class="text-red-500!">close</mat-icon>
</div>
</div>
</div>
</div>
<div *cdkDragPlaceholder class="flex items-start m-2 w-fit">
<div
class="flex flex-col bg-gray-800 border-2 {{
isInError() ? 'border-red-500' : 'border-gray-600'
}} border-solid w-full h-full opacity-30"
>
<div class="flex items-center justify-center">
@if (inDragList()) {
<div
cdkDragHandle
class="flex items-center justify-center bg-transparent hover:bg-gray-700 hover:cursor-move text-blue-400"
>
<mat-icon>drag_indicator</mat-icon>
</div>
}
<div class="flex items-center">
<mat-icon [style.color]="inputIndicatorColor()" matTooltip="Input Indicator"
>keyboard_arrow_down</mat-icon
>
<mat-icon [style.color]="outputIndicatorColor()" matTooltip="Output Indicator"
>keyboard_arrow_up</mat-icon
>
</div>
<div class="text-white">
{{ schema().title || module()?.type }}
</div>
<div class="grow"></div>
<div
class="flex items-center justify-center hover:bg-gray-700 hover:cursor-pointer"
(click)="delete.emit()"
>
<mat-icon class="text-red-500!">close</mat-icon>
</div>
</div>
<hr class="bg-gray-600 h-0.5 border-0" />
<div>
<form [formGroup]="formGroup" class="h-full">
<div class="m-2">
<div class="flex items-center m-1">
<label class="mr-2 text-white"> ID: </label>
<input
type="text"
class="pl-1 border-2 border-gray-200 text-white border-solid rounded-sm"
[placeholder]="'module-1'"
[formControlName]="'id'"
/>
<mat-icon class="text-white!" matTooltip="String">abc</mat-icon>
@if (!formGroup.controls['id'].valid) {
<!-- TODO(jwetzell): better error displaying -->
<div class="ml-2 text-red-500">{{ formGroup.controls['id'].errors | json }}</div>
}
</div>
<app-params-form
[paramsSchema]="schema()?.properties?.params"
[data]="params()"
(updated)="paramsUpdated($event)"
></app-params-form>
</div>
</form>
</div>
<div>
@if (moduleConfigErrors().length > 0) {
<div class="m-2 p-2 border-2 border-red-500">
<div class="flex items-center mb-2 text-red-500">
<mat-icon>error</mat-icon>
<span class="ml-1">Module Errors</span>
</div>
@for (error of moduleConfigErrors(); track $index) {
<div class="ml-4 mb-1 text-white">
{{ error.error }}
</div>
}
</div>
}
</div>
</div>
</div>
} }
+20 -24
View File
@@ -1,10 +1,12 @@
import { CdkDragHandle, CdkDragPlaceholder, CdkDragPreview } from '@angular/cdk/drag-drop';
import { JsonPipe } from '@angular/common'; import { JsonPipe } from '@angular/common';
import { Component, computed, inject, input, model, output, signal } from '@angular/core'; import { Component, computed, inject, input, output, signal } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu'; import { MatMenuModule } from '@angular/material/menu';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { cloneDeep } from 'lodash-es';
import { debounceTime, tap } from 'rxjs'; import { debounceTime, tap } from 'rxjs';
import { ModuleConfig } from '../../models/config'; import { ModuleConfig } from '../../models/config';
import { ConfigService } from '../../services/config'; import { ConfigService } from '../../services/config';
@@ -22,6 +24,9 @@ import { ParamsFormComponent } from '../params-form/params-form';
MatMenuModule, MatMenuModule,
MatTooltipModule, MatTooltipModule,
JsonPipe, JsonPipe,
CdkDragHandle,
CdkDragPreview,
CdkDragPlaceholder,
], ],
templateUrl: './module.html', templateUrl: './module.html',
styleUrl: './module.css', styleUrl: './module.css',
@@ -38,8 +43,10 @@ export class ModuleComponent {
return undefined; return undefined;
}); });
module = model<ModuleConfig>(); inDragList = input<boolean>(false);
module = input<ModuleConfig>();
delete = output<void>(); delete = output<void>();
updated = output<ModuleConfig>();
params = computed(() => this.module()!.params); params = computed(() => this.module()!.params);
id = computed(() => this.module()!.id); id = computed(() => this.module()!.id);
@@ -77,18 +84,12 @@ export class ModuleComponent {
}); });
this.formGroup.valueChanges.subscribe((value) => { this.formGroup.valueChanges.subscribe((value) => {
this.module.update((module) => { const currentModule = this.module();
if (module) { if (currentModule) {
module.id = value.id; currentModule.id = value.id;
module.type = value.type; currentModule.type = value.type;
return { this.updated.emit(cloneDeep(currentModule));
...module, }
id: module.id,
type: module.type,
};
}
return undefined;
});
}); });
if (this.id() !== undefined) { if (this.id() !== undefined) {
this.eventsService this.eventsService
@@ -117,16 +118,11 @@ export class ModuleComponent {
} }
paramsUpdated(params: any) { paramsUpdated(params: any) {
this.module.update((module) => { const currentModule = this.module();
if (module !== undefined && module.params !== undefined) { if (currentModule && currentModule.params !== undefined) {
module.params = params; currentModule.params = params;
return { this.updated.emit(cloneDeep(currentModule));
...module, }
params: module.params,
};
}
return undefined;
});
} }
deleteMe() { deleteMe() {
+26 -2
View File
@@ -63,13 +63,37 @@
<div <div
class="flex flex-col bg-gray-800 border-2 {{ class="flex flex-col bg-gray-800 border-2 {{
isInError() ? 'border-red-500' : 'border-gray-600' isInError() ? 'border-red-500' : 'border-gray-600'
}} border-dotted w-fit h-full" }} border-solid w-fit h-full opacity-30"
> >
<div class="flex items-center justify-center"> <div class="flex items-center justify-center">
<div class="grow ml-1 mr-3 text-transparent"> @if (inDragList()) {
<div
cdkDragHandle
class="flex items-center justify-center bg-transparent hover:bg-gray-700 hover:cursor-move text-blue-400"
>
<mat-icon>drag_indicator</mat-icon>
</div>
}
<div class="grow ml-1 mr-3 text-white">
{{ schema()?.title || processor()?.type }} {{ schema()?.title || processor()?.type }}
</div> </div>
<div
class="flex items-center justify-center hover:bg-gray-700 hover:cursor-pointer"
(click)="delete.emit()"
>
<mat-icon class="text-red-500!">close</mat-icon>
</div>
</div> </div>
@if (hasParams()) {
<hr class="bg-gray-600 h-0.5 border-0" />
<div>
<app-params-form
[paramsSchema]="schema()?.properties?.params"
[data]="params()"
(updated)="paramsUpdated($event)"
></app-params-form>
</div>
}
</div> </div>
</div> </div>
} }
+12 -3
View File
@@ -4,21 +4,30 @@
matTooltip="Add Route" matTooltip="Add Route"
class="flex items-center justify-center w-10 h-10 bg-transparent hover:bg-gray-700 hover:cursor-pointer text-blue-400" class="flex items-center justify-center w-10 h-10 bg-transparent hover:bg-gray-700 hover:cursor-pointer text-blue-400"
(click)="addRoute()" (click)="addRoute()"
role="button"
aria-label="Add Route"
> >
<mat-icon>add</mat-icon> <mat-icon>add</mat-icon>
</div> </div>
<div class="text-white">Routes</div> <div class="text-white">Routes</div>
</div> </div>
<div class="grow overflow-x-hidden overflow-y-auto gap-1.5"> <div
@for (route of routes(); track $index; let i = $index) { class="grow overflow-x-hidden overflow-y-auto gap-1.5"
<div class="m-2"> cdkDropList
(cdkDropListDropped)="drop($event)"
[cdkDropListData]="routes()"
>
@for (route of routes(); track route; let i = $index) {
<div class="m-2" cdkDrag>
<app-route <app-route
[id]="listService.pathToId(`routes/${i}`)"
[path]="`routes/${i}`" [path]="`routes/${i}`"
(updated)="routeUpdated(i, $event)" (updated)="routeUpdated(i, $event)"
[route]="route" [route]="route"
[moduleIds]="moduleIds() || []" [moduleIds]="moduleIds() || []"
(delete)="deleteRoute(i)" (delete)="deleteRoute(i)"
(moveProcessor)="moveProcessorBetweenRoutes($event)" (moveProcessor)="moveProcessorBetweenRoutes($event)"
[inDragList]="true"
></app-route> ></app-route>
</div> </div>
} }
+32 -2
View File
@@ -1,4 +1,11 @@
import { transferArrayItem } from '@angular/cdk/drag-drop'; import { A11yModule } from '@angular/cdk/a11y';
import {
CdkDrag,
CdkDragDrop,
CdkDropList,
moveItemInArray,
transferArrayItem,
} from '@angular/cdk/drag-drop';
import { Component, inject, input, output } from '@angular/core'; import { Component, inject, input, output } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
@@ -7,12 +14,22 @@ import { MatSnackBar } from '@angular/material/snack-bar';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import { RouteConfig } from '../../models/config'; import { RouteConfig } from '../../models/config';
import { ListsService } from '../../services/lists';
import { SchemaService } from '../../services/schema'; import { SchemaService } from '../../services/schema';
import { RouteComponent } from '../route/route'; import { RouteComponent } from '../route/route';
@Component({ @Component({
selector: 'app-route-list', selector: 'app-route-list',
imports: [RouteComponent, MatMenuModule, MatIconModule, MatButtonModule, MatTooltipModule], imports: [
RouteComponent,
MatMenuModule,
MatIconModule,
MatButtonModule,
MatTooltipModule,
CdkDrag,
CdkDropList,
A11yModule,
],
templateUrl: './route-list.html', templateUrl: './route-list.html',
styleUrl: './route-list.css', styleUrl: './route-list.css',
}) })
@@ -21,6 +38,7 @@ export class RouteListComponent {
moduleIds = input<string[]>(); moduleIds = input<string[]>();
updated = output<RouteConfig[]>(); updated = output<RouteConfig[]>();
public schemaService = inject(SchemaService); public schemaService = inject(SchemaService);
public listService = inject(ListsService);
private snackBar = inject(MatSnackBar); private snackBar = inject(MatSnackBar);
@@ -103,4 +121,16 @@ export class RouteListComponent {
this.updated.emit(cloneDeep(routes)); this.updated.emit(cloneDeep(routes));
} }
drop(event: CdkDragDrop<RouteConfig[] | undefined>) {
if (event.previousContainer === event.container) {
const currentRoutes = this.routes();
if (currentRoutes === undefined) {
console.error('routes is undefined, not updating');
return;
}
moveItemInArray(currentRoutes, event.previousIndex, event.currentIndex);
this.updated.emit(cloneDeep(currentRoutes));
}
}
} }
+156 -2
View File
@@ -6,6 +6,14 @@
}} border-solid w-full" }} border-solid w-full"
> >
<div class="flex items-center justify-end"> <div class="flex items-center justify-end">
@if (inDragList()) {
<div
cdkDragHandle
class="flex items-center justify-center bg-transparent hover:bg-gray-700 hover:cursor-move text-blue-400"
>
<mat-icon>drag_indicator</mat-icon>
</div>
}
<div class="flex items-center"> <div class="flex items-center">
<mat-icon [style.color]="indicatorColor()" matTooltip="Route Indicator" <mat-icon [style.color]="indicatorColor()" matTooltip="Route Indicator"
>chevron_right</mat-icon >chevron_right</mat-icon
@@ -15,6 +23,8 @@
<div <div
class="flex items-center justify-center hover:bg-gray-700 hover:cursor-pointer" class="flex items-center justify-center hover:bg-gray-700 hover:cursor-pointer"
(click)="delete.emit()" (click)="delete.emit()"
role="button"
aria-label="Delete Route"
> >
<mat-icon class="text-red-500!">close</mat-icon> <mat-icon class="text-red-500!">close</mat-icon>
</div> </div>
@@ -66,12 +76,12 @@
<div <div
class="border-2 border-gray-500 m-2" class="border-2 border-gray-500 m-2"
cdkDropList cdkDropList
[id]="listsService.registerProcessorList(path() + '/processors')" [id]="processorListId()"
[cdkDropListConnectedTo]="listsService.processorListIds" [cdkDropListConnectedTo]="listsService.processorListIds"
(cdkDropListDropped)="drop($event)" (cdkDropListDropped)="drop($event)"
[cdkDropListData]="route()?.processors" [cdkDropListData]="route()?.processors"
> >
@for (processor of processors(); track $index; let i = $index) { @for (processor of processors(); track processor; let i = $index) {
<div cdkDrag> <div cdkDrag>
<app-processor <app-processor
[path]="path() + '/processors/' + i" [path]="path() + '/processors/' + i"
@@ -115,4 +125,148 @@
</div> </div>
} }
</ng-template> </ng-template>
<div *cdkDragPreview class="flex items-start m-2 w-fit">
<div
class="flex flex-col bg-gray-800 border-2 {{
isInError() ? 'border-red-500' : 'border-gray-600'
}} border-solid w-full"
>
<div class="flex items-center justify-end">
@if (inDragList()) {
<div
cdkDragHandle
class="flex items-center justify-center bg-transparent hover:bg-gray-700 hover:cursor-move text-blue-400"
>
<mat-icon>drag_indicator</mat-icon>
</div>
}
<div class="flex items-center">
<mat-icon [style.color]="indicatorColor()" matTooltip="Route Indicator"
>chevron_right</mat-icon
>
</div>
<div class="grow text-left ml-2 text-white">Route</div>
<div
class="flex items-center justify-center hover:bg-gray-700 hover:cursor-pointer"
(click)="delete.emit()"
>
<mat-icon class="text-red-500!">close</mat-icon>
</div>
</div>
</div>
</div>
<div *cdkDragPlaceholder class="flex items-start m-2 w-fit">
<div
class="flex flex-col bg-gray-800 border-2 {{
isInError() ? 'border-red-500' : 'border-gray-600'
}} border-solid w-full opacity-30"
>
<div class="flex items-center justify-end">
@if (inDragList()) {
<div
cdkDragHandle
class="flex items-center justify-center bg-transparent hover:bg-gray-700 hover:cursor-move text-blue-400"
>
<mat-icon>drag_indicator</mat-icon>
</div>
}
<div class="flex items-center">
<mat-icon [style.color]="indicatorColor()" matTooltip="Route Indicator"
>chevron_right</mat-icon
>
</div>
<div class="grow text-left ml-2 text-white">Route</div>
<div
class="flex items-center justify-center hover:bg-gray-700 hover:cursor-pointer"
(click)="delete.emit()"
>
<mat-icon class="text-red-500!">close</mat-icon>
</div>
</div>
<hr class="bg-gray-600 h-0.5 border-0" />
<div>
<form [formGroup]="formGroup">
<div class="m-2">
<div class="flex items-center m-1">
<label class="mr-2 text-white"> Input: </label>
<select
class="pl-1 border-2 border-gray-200 text-white border-solid rounded-sm w-full"
formControlName="input"
>
@for (option of moduleIds(); track option) {
<option [value]="option">
{{ option }}
</option>
}
</select>
@if (!formGroup.controls['input'].valid) {
<!-- TODO(jwetzell): better error displaying -->
<div class="ml-2 text-red-500">{{ formGroup.controls['input'].errors | json }}</div>
}
</div>
</div>
</form>
<div>
<div class="flex items-center">
@if (schemaService.processorTypes.length > 0) {
<div
matTooltip="Add Processor"
class="flex items-center justify-center w-10 h-10 bg-transparent hover:bg-gray-700 hover:cursor-pointer text-blue-400"
[matMenuTriggerFor]="addProcessorMenu"
>
<mat-icon>add</mat-icon>
</div>
<mat-menu #addProcessorMenu="matMenu">
@for (processorType of schemaService.processorTypes; track processorType) {
<button mat-menu-item (click)="addProcessor(processorType.type)">
<span>{{ processorType.name }}</span>
</button>
}
</mat-menu>
}
<div class="text-md text-white text-center">Processors</div>
</div>
@if (processors() !== undefined && processors()!.length > 0) {
<div
class="border-2 border-gray-500 m-2"
cdkDropList
[id]="processorListId()"
[cdkDropListConnectedTo]="listsService.processorListIds"
(cdkDropListDropped)="drop($event)"
[cdkDropListData]="route()?.processors"
>
@for (processor of processors(); track processor; let i = $index) {
<div cdkDrag>
<app-processor
[path]="path() + '/processors/' + i"
(updated)="processorUpdated(i, $event)"
[processor]="processor"
(delete)="deleteProcessor(i)"
[inDragList]="true"
></app-processor>
</div>
}
</div>
}
</div>
</div>
<div>
@if (routeConfigErrors().length > 0) {
<div class="m-2 p-2 border-2 border-red-500">
<div class="flex items-center mb-2 text-red-500">
<mat-icon>error</mat-icon>
<span class="ml-1">Route Errors</span>
</div>
@for (error of routeConfigErrors(); track error.index) {
<div class="ml-4 mb-1 text-white">
{{ error.error }}
</div>
}
</div>
}
</div>
</div>
</div>
} }
+18 -1
View File
@@ -1,4 +1,11 @@
import { CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop'; import {
CdkDrag,
CdkDragDrop,
CdkDragPlaceholder,
CdkDragPreview,
CdkDropList,
moveItemInArray,
} from '@angular/cdk/drag-drop';
import { JsonPipe } from '@angular/common'; import { JsonPipe } from '@angular/common';
import { Component, computed, inject, input, output, signal } from '@angular/core'; import { Component, computed, inject, input, output, signal } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
@@ -28,6 +35,8 @@ import { ProcessorComponent } from '../processor/processor';
MatTooltipModule, MatTooltipModule,
CdkDrag, CdkDrag,
CdkDropList, CdkDropList,
CdkDragPreview,
CdkDragPlaceholder,
], ],
templateUrl: './route.html', templateUrl: './route.html',
styleUrl: './route.css', styleUrl: './route.css',
@@ -45,6 +54,7 @@ export class RouteComponent {
return undefined; return undefined;
}); });
inDragList = input<boolean>(false);
route = input<RouteConfig>(); route = input<RouteConfig>();
moduleIds = input<string[]>([]); moduleIds = input<string[]>([]);
delete = output<void>(); delete = output<void>();
@@ -83,8 +93,14 @@ export class RouteComponent {
private configService = inject(ConfigService); private configService = inject(ConfigService);
public listsService = inject(ListsService); public listsService = inject(ListsService);
public processorListId = signal<string>('');
indicatorColor = signal<string>('gray'); indicatorColor = signal<string>('gray');
ngOnDestroy(): void {
this.listsService.removeProcessorList(this.path() + '/processors');
}
ngOnInit(): void { ngOnInit(): void {
this.formGroup.patchValue({ this.formGroup.patchValue({
input: this.route()?.input, input: this.route()?.input,
@@ -115,6 +131,7 @@ export class RouteComponent {
this.indicatorColor.set('gray'); this.indicatorColor.set('gray');
}); });
} }
this.processorListId.set(this.listsService.registerProcessorList(this.path() + '/processors'));
} }
isInError(): boolean { isInError(): boolean {
+15 -21
View File
@@ -17,17 +17,18 @@ import { SettingsService } from './settings';
}) })
export class ConfigService { export class ConfigService {
pendingConfigIsValid = computed(() => { pendingConfigIsValid = computed(() => {
const config = this.currentlyShownConfig(); const config = this._currentlyShownConfig();
if (config === undefined) { if (config === undefined) {
return false; return false;
} }
return this.schemaService.validate(this.schemaService.configSchemaId, config); return this.schemaService.validate(this.schemaService.configSchemaId, config);
}); });
currentlyShownConfig = signal<Config | undefined>(undefined); private runningConfig = signal<Config | undefined>(undefined);
runningConfig = signal<Config | undefined>(undefined); private _currentlyShownConfig = signal<Config | undefined>(undefined);
readonly currentlyShownConfig = this._currentlyShownConfig.asReadonly();
configIsDirty = computed(() => { configIsDirty = computed(() => {
const currentlyShown = this.currentlyShownConfig(); const currentlyShown = this._currentlyShownConfig();
const running = this.runningConfig(); const running = this.runningConfig();
if (currentlyShown === undefined || running === undefined) { if (currentlyShown === undefined || running === undefined) {
return false; return false;
@@ -44,7 +45,7 @@ export class ConfigService {
constructor(private schemaService: SchemaService) { constructor(private schemaService: SchemaService) {
effect(() => { effect(() => {
console.log('config state changed', this.currentlyShownConfig()); console.log('config state changed', this._currentlyShownConfig());
}); });
effect(() => { effect(() => {
@@ -124,36 +125,29 @@ export class ConfigService {
} }
updateCurrentlyShownConfig(config: Config) { updateCurrentlyShownConfig(config: Config) {
if (isEqual(config, this.currentlyShownConfig())) { this._currentlyShownConfig.set(cloneDeep(config));
// NOTE(jwetzell): no update
return;
}
this.currentlyShownConfig.set(cloneDeep(config));
} }
updateModules(modules: ModuleConfig[]) { updateModules(modules: ModuleConfig[]) {
const currentConfig = this.currentlyShownConfig(); var newConfig = cloneDeep(this._currentlyShownConfig());
if (!currentConfig) { if (!newConfig) {
console.error('No currently shown config to update modules on'); console.error('No currently shown config to update modules on');
return; return;
} }
newConfig.modules = cloneDeep(modules);
this.currentlyShownConfig.set({ this.updateCurrentlyShownConfig(newConfig);
...currentConfig,
modules: modules,
});
} }
updateRoutes(routes: RouteConfig[]) { updateRoutes(routes: RouteConfig[]) {
const currentConfig = this.currentlyShownConfig(); const currentConfig = this._currentlyShownConfig();
if (!currentConfig) { if (!currentConfig) {
console.error('No currently shown config to update routes on'); console.error('No currently shown config to update routes on');
return; return;
} }
this.currentlyShownConfig.set({ currentConfig.routes = cloneDeep(routes);
...currentConfig,
routes: routes, this.updateCurrentlyShownConfig(currentConfig);
});
} }
} }
+9
View File
@@ -18,6 +18,15 @@ export class ListsService {
return id; return id;
} }
removeProcessorList(path: string | undefined) {
if (path === undefined) {
return;
}
const id = this.pathToId(path);
this.processorListIds = this.processorListIds.filter((listId) => listId !== id);
}
pathToId(path: string) { pathToId(path: string) {
return path.replaceAll('/', '.'); return path.replaceAll('/', '.');
} }