From 32128ccb91c89cfae9ef5b1578c2150f0596718a Mon Sep 17 00:00:00 2001 From: Giuliano Silvestro Date: Fri, 13 Feb 2026 21:48:43 +1000 Subject: [PATCH] Initial commit: notification-elements-ui library Angular 19 component library for notifications & communication: - ntf-bell: notification bell with badge count and shake animation - ntf-feed / ntf-feed-item: real-time notification feed with grouping - ntf-center: full notification center with category filter tabs - ntf-inbox / ntf-inbox-item: two-column inbox with search and detail - ntf-comment / ntf-thread: comment threads with replies and reactions - ntf-mention-input: text input with @mention autocomplete - ntf-empty-state: empty state placeholder - ntf-item-def: custom template directive for notification items Includes signal-based services, SCSS design tokens with dark mode, utility functions, and full TypeScript type definitions. Co-Authored-By: Claude Opus 4.6 --- .gitignore | 4 + build-for-dev.sh | 8 + ng-package.json | 12 + package-lock.json | 3767 +++++++++++++++++ package.json | 39 + src/components/index.ts | 11 + src/components/ntf-bell/index.ts | 1 + .../ntf-bell/ntf-bell.component.html | 17 + .../ntf-bell/ntf-bell.component.scss | 103 + src/components/ntf-bell/ntf-bell.component.ts | 28 + src/components/ntf-center/index.ts | 1 + .../ntf-center/ntf-center.component.html | 30 + .../ntf-center/ntf-center.component.scss | 62 + .../ntf-center/ntf-center.component.ts | 78 + src/components/ntf-comment/index.ts | 1 + .../ntf-comment/ntf-comment.component.html | 91 + .../ntf-comment/ntf-comment.component.scss | 249 ++ .../ntf-comment/ntf-comment.component.ts | 93 + src/components/ntf-empty-state/index.ts | 1 + .../ntf-empty-state.component.ts | 82 + src/components/ntf-feed-item/index.ts | 1 + .../ntf-feed-item.component.html | 103 + .../ntf-feed-item.component.scss | 189 + .../ntf-feed-item/ntf-feed-item.component.ts | 80 + src/components/ntf-feed/index.ts | 1 + .../ntf-feed/ntf-feed.component.html | 70 + .../ntf-feed/ntf-feed.component.scss | 141 + src/components/ntf-feed/ntf-feed.component.ts | 91 + src/components/ntf-inbox-item/index.ts | 1 + .../ntf-inbox-item.component.html | 42 + .../ntf-inbox-item.component.scss | 156 + .../ntf-inbox-item.component.ts | 47 + src/components/ntf-inbox/index.ts | 1 + .../ntf-inbox/ntf-inbox.component.html | 66 + .../ntf-inbox/ntf-inbox.component.scss | 141 + .../ntf-inbox/ntf-inbox.component.ts | 65 + src/components/ntf-item-def/index.ts | 1 + .../ntf-item-def/ntf-item-def.directive.ts | 25 + src/components/ntf-mention-input/index.ts | 1 + .../ntf-mention-input.component.html | 59 + .../ntf-mention-input.component.scss | 133 + .../ntf-mention-input.component.ts | 140 + src/components/ntf-thread/index.ts | 1 + .../ntf-thread/ntf-thread.component.html | 70 + .../ntf-thread/ntf-thread.component.scss | 66 + .../ntf-thread/ntf-thread.component.ts | 77 + src/index.ts | 53 + src/providers/index.ts | 1 + src/providers/notification-config.provider.ts | 27 + src/services/index.ts | 3 + src/services/mention.service.ts | 26 + src/services/notification-feed.service.ts | 46 + src/services/notification.service.ts | 56 + src/styles/_index.scss | 2 + src/styles/_mixins.scss | 118 + src/styles/_tokens.scss | 143 + src/types/comment.types.ts | 28 + src/types/config.types.ts | 13 + src/types/event.types.ts | 83 + src/types/inbox.types.ts | 18 + src/types/index.ts | 5 + src/types/notification.types.ts | 56 + src/utils/filter.utils.ts | 68 + src/utils/index.ts | 3 + src/utils/mention.utils.ts | 48 + src/utils/time.utils.ts | 63 + tsconfig.json | 28 + 67 files changed, 7334 insertions(+) create mode 100644 .gitignore create mode 100755 build-for-dev.sh create mode 100644 ng-package.json create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/components/index.ts create mode 100644 src/components/ntf-bell/index.ts create mode 100644 src/components/ntf-bell/ntf-bell.component.html create mode 100644 src/components/ntf-bell/ntf-bell.component.scss create mode 100644 src/components/ntf-bell/ntf-bell.component.ts create mode 100644 src/components/ntf-center/index.ts create mode 100644 src/components/ntf-center/ntf-center.component.html create mode 100644 src/components/ntf-center/ntf-center.component.scss create mode 100644 src/components/ntf-center/ntf-center.component.ts create mode 100644 src/components/ntf-comment/index.ts create mode 100644 src/components/ntf-comment/ntf-comment.component.html create mode 100644 src/components/ntf-comment/ntf-comment.component.scss create mode 100644 src/components/ntf-comment/ntf-comment.component.ts create mode 100644 src/components/ntf-empty-state/index.ts create mode 100644 src/components/ntf-empty-state/ntf-empty-state.component.ts create mode 100644 src/components/ntf-feed-item/index.ts create mode 100644 src/components/ntf-feed-item/ntf-feed-item.component.html create mode 100644 src/components/ntf-feed-item/ntf-feed-item.component.scss create mode 100644 src/components/ntf-feed-item/ntf-feed-item.component.ts create mode 100644 src/components/ntf-feed/index.ts create mode 100644 src/components/ntf-feed/ntf-feed.component.html create mode 100644 src/components/ntf-feed/ntf-feed.component.scss create mode 100644 src/components/ntf-feed/ntf-feed.component.ts create mode 100644 src/components/ntf-inbox-item/index.ts create mode 100644 src/components/ntf-inbox-item/ntf-inbox-item.component.html create mode 100644 src/components/ntf-inbox-item/ntf-inbox-item.component.scss create mode 100644 src/components/ntf-inbox-item/ntf-inbox-item.component.ts create mode 100644 src/components/ntf-inbox/index.ts create mode 100644 src/components/ntf-inbox/ntf-inbox.component.html create mode 100644 src/components/ntf-inbox/ntf-inbox.component.scss create mode 100644 src/components/ntf-inbox/ntf-inbox.component.ts create mode 100644 src/components/ntf-item-def/index.ts create mode 100644 src/components/ntf-item-def/ntf-item-def.directive.ts create mode 100644 src/components/ntf-mention-input/index.ts create mode 100644 src/components/ntf-mention-input/ntf-mention-input.component.html create mode 100644 src/components/ntf-mention-input/ntf-mention-input.component.scss create mode 100644 src/components/ntf-mention-input/ntf-mention-input.component.ts create mode 100644 src/components/ntf-thread/index.ts create mode 100644 src/components/ntf-thread/ntf-thread.component.html create mode 100644 src/components/ntf-thread/ntf-thread.component.scss create mode 100644 src/components/ntf-thread/ntf-thread.component.ts create mode 100644 src/index.ts create mode 100644 src/providers/index.ts create mode 100644 src/providers/notification-config.provider.ts create mode 100644 src/services/index.ts create mode 100644 src/services/mention.service.ts create mode 100644 src/services/notification-feed.service.ts create mode 100644 src/services/notification.service.ts create mode 100644 src/styles/_index.scss create mode 100644 src/styles/_mixins.scss create mode 100644 src/styles/_tokens.scss create mode 100644 src/types/comment.types.ts create mode 100644 src/types/config.types.ts create mode 100644 src/types/event.types.ts create mode 100644 src/types/inbox.types.ts create mode 100644 src/types/index.ts create mode 100644 src/types/notification.types.ts create mode 100644 src/utils/filter.utils.ts create mode 100644 src/utils/index.ts create mode 100644 src/utils/mention.utils.ts create mode 100644 src/utils/time.utils.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8d1996f --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +dist/ +node_modules/ +.angular/ +*.tgz diff --git a/build-for-dev.sh b/build-for-dev.sh new file mode 100755 index 0000000..1d54d96 --- /dev/null +++ b/build-for-dev.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -e + +echo "Building @sda/notification-elements-ui for local development..." +npm run build +cd dist +npm pack +echo "Done! Package is ready in dist/" diff --git a/ng-package.json b/ng-package.json new file mode 100644 index 0000000..1775ca2 --- /dev/null +++ b/ng-package.json @@ -0,0 +1,12 @@ +{ + "$schema": "node_modules/ng-packagr/ng-package.schema.json", + "lib": { + "entryFile": "src/index.ts", + "styleIncludePaths": ["src/styles"] + }, + "dest": "dist", + "deleteDestPath": true, + "assets": [ + "src/styles/**/*.scss" + ] +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..3554c02 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3767 @@ +{ + "name": "@sda/notification-elements-ui", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@sda/notification-elements-ui", + "version": "0.1.0", + "license": "MIT", + "devDependencies": { + "@angular/common": "^19.1.0", + "@angular/compiler": "^19.1.0", + "@angular/compiler-cli": "^19.1.0", + "@angular/core": "^19.1.0", + "@angular/forms": "^19.2.18", + "ng-packagr": "^19.1.0", + "typescript": "~5.7.2" + }, + "peerDependencies": { + "@angular/common": "^19.0.0", + "@angular/core": "^19.0.0", + "@sda/base-ui": "*" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular/common": { + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-19.2.18.tgz", + "integrity": "sha512-CrV02Omzw/QtfjlEVXVPJVXipdx83NuA+qSASZYrxrhKFusUZyK3P/Zznqg+wiAeNDbedQwMUVqoAARHf0xQrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/core": "19.2.18", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/compiler": { + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-19.2.18.tgz", + "integrity": "sha512-3MscvODxRVxc3Cs0ZlHI5Pk5rEvE80otfvxZTMksOZuPlv1B+S8MjWfc3X3jk9SbyUEzODBEH55iCaBHD48V3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + } + }, + "node_modules/@angular/compiler-cli": { + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-19.2.18.tgz", + "integrity": "sha512-N4TMtLfImJIoMaRL6mx7885UBeQidywptHH6ACZj71Ar6++DBc1mMlcwuvbeJCd3r3y8MQ5nLv5PZSN/tHr13w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "7.26.9", + "@jridgewell/sourcemap-codec": "^1.4.14", + "chokidar": "^4.0.0", + "convert-source-map": "^1.5.1", + "reflect-metadata": "^0.2.0", + "semver": "^7.0.0", + "tslib": "^2.3.0", + "yargs": "^17.2.1" + }, + "bin": { + "ng-xi18n": "bundles/src/bin/ng_xi18n.js", + "ngc": "bundles/src/bin/ngc.js", + "ngcc": "bundles/ngcc/index.js" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/compiler": "19.2.18", + "typescript": ">=5.5 <5.9" + } + }, + "node_modules/@angular/core": { + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-19.2.18.tgz", + "integrity": "sha512-+QRrf0Igt8ccUWXHA+7doK5W6ODyhHdqVyblSlcQ8OciwkzIIGGEYNZom5OZyWMh+oI54lcSeyV2O3xaDepSrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.15.0" + } + }, + "node_modules/@angular/forms": { + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-19.2.18.tgz", + "integrity": "sha512-pe40934jWhoS7DyGl7jyZdoj1gvBgur2t1zrJD+csEkTitYnW14+La2Pv6SW1pNX5nIzFsgsS9Nex1KcH5S6Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/common": "19.2.18", + "@angular/core": "19.2.18", + "@angular/platform-browser": "19.2.18", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz", + "integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.9", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.9", + "@babel/parser": "^7.26.9", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.9", + "@babel/types": "^7.26.9", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/nice": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.1.1.tgz", + "integrity": "sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/nice-android-arm-eabi": "1.1.1", + "@napi-rs/nice-android-arm64": "1.1.1", + "@napi-rs/nice-darwin-arm64": "1.1.1", + "@napi-rs/nice-darwin-x64": "1.1.1", + "@napi-rs/nice-freebsd-x64": "1.1.1", + "@napi-rs/nice-linux-arm-gnueabihf": "1.1.1", + "@napi-rs/nice-linux-arm64-gnu": "1.1.1", + "@napi-rs/nice-linux-arm64-musl": "1.1.1", + "@napi-rs/nice-linux-ppc64-gnu": "1.1.1", + "@napi-rs/nice-linux-riscv64-gnu": "1.1.1", + "@napi-rs/nice-linux-s390x-gnu": "1.1.1", + "@napi-rs/nice-linux-x64-gnu": "1.1.1", + "@napi-rs/nice-linux-x64-musl": "1.1.1", + "@napi-rs/nice-openharmony-arm64": "1.1.1", + "@napi-rs/nice-win32-arm64-msvc": "1.1.1", + "@napi-rs/nice-win32-ia32-msvc": "1.1.1", + "@napi-rs/nice-win32-x64-msvc": "1.1.1" + } + }, + "node_modules/@napi-rs/nice-android-arm-eabi": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.1.1.tgz", + "integrity": "sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-android-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm64/-/nice-android-arm64-1.1.1.tgz", + "integrity": "sha512-blG0i7dXgbInN5urONoUCNf+DUEAavRffrO7fZSeoRMJc5qD+BJeNcpr54msPF6qfDD6kzs9AQJogZvT2KD5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-arm64/-/nice-darwin-arm64-1.1.1.tgz", + "integrity": "sha512-s/E7w45NaLqTGuOjC2p96pct4jRfo61xb9bU1unM/MJ/RFkKlJyJDx7OJI/O0ll/hrfpqKopuAFDV8yo0hfT7A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-x64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-x64/-/nice-darwin-x64-1.1.1.tgz", + "integrity": "sha512-dGoEBnVpsdcC+oHHmW1LRK5eiyzLwdgNQq3BmZIav+9/5WTZwBYX7r5ZkQC07Nxd3KHOCkgbHSh4wPkH1N1LiQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-freebsd-x64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-freebsd-x64/-/nice-freebsd-x64-1.1.1.tgz", + "integrity": "sha512-kHv4kEHAylMYmlNwcQcDtXjklYp4FCf0b05E+0h6nDHsZ+F0bDe04U/tXNOqrx5CmIAth4vwfkjjUmp4c4JktQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm-gnueabihf": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm-gnueabihf/-/nice-linux-arm-gnueabihf-1.1.1.tgz", + "integrity": "sha512-E1t7K0efyKXZDoZg1LzCOLxgolxV58HCkaEkEvIYQx12ht2pa8hoBo+4OB3qh7e+QiBlp1SRf+voWUZFxyhyqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-gnu/-/nice-linux-arm64-gnu-1.1.1.tgz", + "integrity": "sha512-CIKLA12DTIZlmTaaKhQP88R3Xao+gyJxNWEn04wZwC2wmRapNnxCUZkVwggInMJvtVElA+D4ZzOU5sX4jV+SmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-musl": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-musl/-/nice-linux-arm64-musl-1.1.1.tgz", + "integrity": "sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-ppc64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-ppc64-gnu/-/nice-linux-ppc64-gnu-1.1.1.tgz", + "integrity": "sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-riscv64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-riscv64-gnu/-/nice-linux-riscv64-gnu-1.1.1.tgz", + "integrity": "sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-s390x-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-s390x-gnu/-/nice-linux-s390x-gnu-1.1.1.tgz", + "integrity": "sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-gnu/-/nice-linux-x64-gnu-1.1.1.tgz", + "integrity": "sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-musl": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-musl/-/nice-linux-x64-musl-1.1.1.tgz", + "integrity": "sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-openharmony-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-openharmony-arm64/-/nice-openharmony-arm64-1.1.1.tgz", + "integrity": "sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-arm64-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-arm64-msvc/-/nice-win32-arm64-msvc-1.1.1.tgz", + "integrity": "sha512-uoTb4eAvM5B2aj/z8j+Nv8OttPf2m+HVx3UjA5jcFxASvNhQriyCQF1OB1lHL43ZhW+VwZlgvjmP5qF3+59atA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-ia32-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-ia32-msvc/-/nice-win32-ia32-msvc-1.1.1.tgz", + "integrity": "sha512-CNQqlQT9MwuCsg1Vd/oKXiuH+TcsSPJmlAFc5frFyX/KkOh0UpBLEj7aoY656d5UKZQMQFP7vJNa1DNUNORvug==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-x64-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-x64-msvc/-/nice-win32-x64-msvc-1.1.1.tgz", + "integrity": "sha512-vB+4G/jBQCAh0jelMTY3+kgFy00Hlx2f2/1zjMoH821IbplbWZOkLiTYXQkygNTzQJTq5cvwBDgn2ppHD+bglQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", + "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.6", + "@parcel/watcher-darwin-arm64": "2.5.6", + "@parcel/watcher-darwin-x64": "2.5.6", + "@parcel/watcher-freebsd-x64": "2.5.6", + "@parcel/watcher-linux-arm-glibc": "2.5.6", + "@parcel/watcher-linux-arm-musl": "2.5.6", + "@parcel/watcher-linux-arm64-glibc": "2.5.6", + "@parcel/watcher-linux-arm64-musl": "2.5.6", + "@parcel/watcher-linux-x64-glibc": "2.5.6", + "@parcel/watcher-linux-x64-musl": "2.5.6", + "@parcel/watcher-win32-arm64": "2.5.6", + "@parcel/watcher-win32-ia32": "2.5.6", + "@parcel/watcher-win32-x64": "2.5.6" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", + "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", + "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", + "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", + "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", + "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", + "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", + "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", + "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", + "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", + "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", + "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", + "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", + "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@rollup/plugin-json": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", + "integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", + "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", + "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", + "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", + "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", + "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", + "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", + "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", + "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", + "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", + "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", + "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", + "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", + "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", + "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", + "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", + "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", + "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", + "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", + "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", + "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", + "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", + "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", + "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", + "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", + "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/wasm-node": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/wasm-node/-/wasm-node-4.57.1.tgz", + "integrity": "sha512-b0rcJH8ykEanfgTeDtlPubhphIUOx0oaAek+3hizTaFkoC1FBSTsY0GixwB4D5HZ5r3Gt2yI9c8M13OcW/kW5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001769", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", + "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dependency-graph": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-1.0.0.tgz", + "integrity": "sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.286", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", + "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immutable": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", + "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", + "dev": true, + "license": "MIT" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/injection-js": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/injection-js/-/injection-js-2.6.1.tgz", + "integrity": "sha512-dbR5bdhi7TWDoCye9cByZqeg/gAfamm8Vu3G1KZOTYkOif8WkuM8CD0oeDPtZYMzT5YH76JAFB7bkmyY9OJi2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/less": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/less/-/less-4.5.1.tgz", + "integrity": "sha512-UKgI3/KON4u6ngSsnDADsUERqhZknsVZbnuzlRZXLQCmfC/MDld42fTydUE9B+Mla1AL6SJ/Pp6SlEFi/AVGfw==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=14" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" + } + }, + "node_modules/less/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/needle": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", + "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/ng-packagr": { + "version": "19.2.2", + "resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-19.2.2.tgz", + "integrity": "sha512-dFuwFsDJMBSd1YtmLLcX5bNNUCQUlRqgf34aXA+79PmkOP+0eF8GP2949wq3+jMjmFTNm80Oo8IUYiSLwklKCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/plugin-json": "^6.1.0", + "@rollup/wasm-node": "^4.24.0", + "ajv": "^8.17.1", + "ansi-colors": "^4.1.3", + "browserslist": "^4.22.1", + "chokidar": "^4.0.1", + "commander": "^13.0.0", + "convert-source-map": "^2.0.0", + "dependency-graph": "^1.0.0", + "esbuild": "^0.25.0", + "fast-glob": "^3.3.2", + "find-cache-dir": "^3.3.2", + "injection-js": "^2.4.0", + "jsonc-parser": "^3.3.1", + "less": "^4.2.0", + "ora": "^5.1.0", + "piscina": "^4.7.0", + "postcss": "^8.4.47", + "rxjs": "^7.8.1", + "sass": "^1.81.0" + }, + "bin": { + "ng-packagr": "cli/main.js" + }, + "engines": { + "node": "^18.19.1 || >=20.11.1" + }, + "optionalDependencies": { + "rollup": "^4.24.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^19.0.0 || ^19.1.0-next.0 || ^19.2.0-next.0", + "tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0", + "tslib": "^2.3.0", + "typescript": ">=5.5 <5.9" + }, + "peerDependenciesMeta": { + "tailwindcss": { + "optional": true + } + } + }, + "node_modules/ng-packagr/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/piscina": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.9.2.tgz", + "integrity": "sha512-Fq0FERJWFEUpB4eSY59wSNwXD4RYqR+nR/WiEVcZW8IWfVBxJJafcgTEZDQo8k3w0sUarJ8RyVbbUF4GQ2LGbQ==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "@napi-rs/nice": "^1.0.1" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", + "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.57.1", + "@rollup/rollup-android-arm64": "4.57.1", + "@rollup/rollup-darwin-arm64": "4.57.1", + "@rollup/rollup-darwin-x64": "4.57.1", + "@rollup/rollup-freebsd-arm64": "4.57.1", + "@rollup/rollup-freebsd-x64": "4.57.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", + "@rollup/rollup-linux-arm-musleabihf": "4.57.1", + "@rollup/rollup-linux-arm64-gnu": "4.57.1", + "@rollup/rollup-linux-arm64-musl": "4.57.1", + "@rollup/rollup-linux-loong64-gnu": "4.57.1", + "@rollup/rollup-linux-loong64-musl": "4.57.1", + "@rollup/rollup-linux-ppc64-gnu": "4.57.1", + "@rollup/rollup-linux-ppc64-musl": "4.57.1", + "@rollup/rollup-linux-riscv64-gnu": "4.57.1", + "@rollup/rollup-linux-riscv64-musl": "4.57.1", + "@rollup/rollup-linux-s390x-gnu": "4.57.1", + "@rollup/rollup-linux-x64-gnu": "4.57.1", + "@rollup/rollup-linux-x64-musl": "4.57.1", + "@rollup/rollup-openbsd-x64": "4.57.1", + "@rollup/rollup-openharmony-arm64": "4.57.1", + "@rollup/rollup-win32-arm64-msvc": "4.57.1", + "@rollup/rollup-win32-ia32-msvc": "4.57.1", + "@rollup/rollup-win32-x64-gnu": "4.57.1", + "@rollup/rollup-win32-x64-msvc": "4.57.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/sass": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.3.tgz", + "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/sax": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.4.tgz", + "integrity": "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==", + "dev": true, + "license": "BlueOak-1.0.0", + "optional": true, + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..1806b12 --- /dev/null +++ b/package.json @@ -0,0 +1,39 @@ +{ + "name": "@sda/notification-elements-ui", + "version": "0.1.0", + "description": "Angular components for notification center, real-time feeds, inbox, comment threads, and @mentions powered by @sda/base-ui", + "keywords": [ + "angular", + "notifications", + "inbox", + "comments", + "mentions", + "feed", + "components", + "ui" + ], + "repository": { + "type": "git", + "url": "https://git.sky-ai.com/ui-core-design/notification-elements-ui.git" + }, + "license": "MIT", + "sideEffects": false, + "scripts": { + "build": "ng-packagr -p ng-package.json", + "build:dev": "./build-for-dev.sh" + }, + "peerDependencies": { + "@angular/common": "^19.0.0", + "@angular/core": "^19.0.0", + "@sda/base-ui": "*" + }, + "devDependencies": { + "@angular/common": "^19.1.0", + "@angular/compiler": "^19.1.0", + "@angular/compiler-cli": "^19.1.0", + "@angular/core": "^19.1.0", + "@angular/forms": "^19.2.18", + "ng-packagr": "^19.1.0", + "typescript": "~5.7.2" + } +} diff --git a/src/components/index.ts b/src/components/index.ts new file mode 100644 index 0000000..cfa3c6e --- /dev/null +++ b/src/components/index.ts @@ -0,0 +1,11 @@ +export * from './ntf-bell'; +export * from './ntf-feed'; +export * from './ntf-feed-item'; +export * from './ntf-center'; +export * from './ntf-inbox'; +export * from './ntf-inbox-item'; +export * from './ntf-comment'; +export * from './ntf-thread'; +export * from './ntf-mention-input'; +export * from './ntf-empty-state'; +export * from './ntf-item-def'; diff --git a/src/components/ntf-bell/index.ts b/src/components/ntf-bell/index.ts new file mode 100644 index 0000000..26c77e7 --- /dev/null +++ b/src/components/ntf-bell/index.ts @@ -0,0 +1 @@ +export { NtfBellComponent } from './ntf-bell.component'; diff --git a/src/components/ntf-bell/ntf-bell.component.html b/src/components/ntf-bell/ntf-bell.component.html new file mode 100644 index 0000000..052cadd --- /dev/null +++ b/src/components/ntf-bell/ntf-bell.component.html @@ -0,0 +1,17 @@ + diff --git a/src/components/ntf-bell/ntf-bell.component.scss b/src/components/ntf-bell/ntf-bell.component.scss new file mode 100644 index 0000000..930ed94 --- /dev/null +++ b/src/components/ntf-bell/ntf-bell.component.scss @@ -0,0 +1,103 @@ +:host { + display: inline-block; +} + +.ntf-bell { + position: relative; + display: inline-flex; + align-items: center; + justify-content: center; + width: var(--ntf-bell-size); + height: var(--ntf-bell-size); + padding: 0; + border: none; + border-radius: 50%; + background: transparent; + color: var(--ntf-bell-color); + cursor: pointer; + transition: + background-color var(--ntf-transition, 150ms ease-in-out), + color var(--ntf-transition, 150ms ease-in-out); + + &:hover { + background: var(--ntf-bell-hover-bg); + } + + &:focus-visible { + outline: 2px solid var(--color-primary-500, #3b82f6); + outline-offset: 2px; + } + + // Sizes + &--sm { + --ntf-bell-size: 32px; + + .ntf-bell__icon { + width: 16px; + height: 16px; + } + + .ntf-bell__badge { + min-width: 16px; + height: 16px; + font-size: 0.5625rem; + top: 2px; + right: 2px; + } + } + + &--lg { + --ntf-bell-size: 48px; + + .ntf-bell__icon { + width: 24px; + height: 24px; + } + } + + // Animation + &--animate { + .ntf-bell__icon { + animation: ntf-bell-shake 0.5s ease-in-out; + } + } +} + +.ntf-bell__icon { + width: 20px; + height: 20px; + transition: transform var(--ntf-transition, 150ms ease-in-out); +} + +.ntf-bell__badge { + position: absolute; + top: 4px; + right: 4px; + min-width: var(--ntf-badge-size); + height: var(--ntf-badge-size); + padding: 0 5px; + border-radius: 999px; + background: var(--ntf-badge-bg); + color: var(--ntf-badge-color); + font-size: 0.625rem; + font-weight: 600; + line-height: var(--ntf-badge-size); + text-align: center; + pointer-events: none; + animation: ntf-badge-pop 300ms var(--ntf-ease-spring, cubic-bezier(0.34, 1.56, 0.64, 1)); +} + +@keyframes ntf-bell-shake { + 0%, 100% { transform: rotate(0deg); } + 15% { transform: rotate(14deg); } + 30% { transform: rotate(-14deg); } + 45% { transform: rotate(10deg); } + 60% { transform: rotate(-8deg); } + 75% { transform: rotate(4deg); } +} + +@keyframes ntf-badge-pop { + 0% { transform: scale(0); } + 60% { transform: scale(1.2); } + 100% { transform: scale(1); } +} diff --git a/src/components/ntf-bell/ntf-bell.component.ts b/src/components/ntf-bell/ntf-bell.component.ts new file mode 100644 index 0000000..06b57e2 --- /dev/null +++ b/src/components/ntf-bell/ntf-bell.component.ts @@ -0,0 +1,28 @@ +import { Component, ChangeDetectionStrategy, input, output, computed } from '@angular/core'; + +@Component({ + selector: 'ntf-bell', + standalone: true, + templateUrl: './ntf-bell.component.html', + styleUrl: './ntf-bell.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class NtfBellComponent { + readonly count = input(0); + readonly animate = input(false); + readonly size = input<'sm' | 'md' | 'lg'>('md'); + + readonly bellClick = output(); + + readonly displayCount = computed(() => { + const c = this.count(); + if (c > 99) return '99+'; + return c > 0 ? String(c) : ''; + }); + + readonly hasUnread = computed(() => this.count() > 0); + + onClick(): void { + this.bellClick.emit(); + } +} diff --git a/src/components/ntf-center/index.ts b/src/components/ntf-center/index.ts new file mode 100644 index 0000000..70f24b5 --- /dev/null +++ b/src/components/ntf-center/index.ts @@ -0,0 +1 @@ +export { NtfCenterComponent } from './ntf-center.component'; diff --git a/src/components/ntf-center/ntf-center.component.html b/src/components/ntf-center/ntf-center.component.html new file mode 100644 index 0000000..000672f --- /dev/null +++ b/src/components/ntf-center/ntf-center.component.html @@ -0,0 +1,30 @@ +
+ +
+ @for (filter of filters; track filter.value) { + + } +
+ + + +
diff --git a/src/components/ntf-center/ntf-center.component.scss b/src/components/ntf-center/ntf-center.component.scss new file mode 100644 index 0000000..1e03b08 --- /dev/null +++ b/src/components/ntf-center/ntf-center.component.scss @@ -0,0 +1,62 @@ +:host { + display: block; +} + +.ntf-center { + background: var(--ntf-feed-bg); + border: 1px solid var(--ntf-feed-border); + border-radius: var(--ntf-feed-radius); + box-shadow: var(--ntf-feed-shadow); + overflow: hidden; +} + +.ntf-center__tabs { + display: flex; + gap: 0.25rem; + padding: 0.75rem 1rem; + border-bottom: 1px solid var(--ntf-item-border); + overflow-x: auto; + + &::-webkit-scrollbar { + display: none; + } +} + +.ntf-center__tab { + padding: 0.375rem 0.75rem; + border: none; + border-radius: 999px; + background: transparent; + font-size: var(--font-size-xs, 0.75rem); + font-weight: 500; + color: var(--ntf-item-body-color); + cursor: pointer; + white-space: nowrap; + transition: + background-color var(--ntf-transition, 150ms ease-in-out), + color var(--ntf-transition, 150ms ease-in-out); + + &:hover { + background: var(--ntf-item-hover-bg); + color: var(--ntf-item-title-color); + } + + &--active { + background: var(--color-primary-500, #3b82f6); + color: #ffffff; + + &:hover { + background: var(--color-primary-600, #2563eb); + color: #ffffff; + } + } +} + +ntf-feed { + ::ng-deep .ntf-feed { + border: none; + border-radius: 0; + box-shadow: none; + width: 100%; + } +} diff --git a/src/components/ntf-center/ntf-center.component.ts b/src/components/ntf-center/ntf-center.component.ts new file mode 100644 index 0000000..9fb3b79 --- /dev/null +++ b/src/components/ntf-center/ntf-center.component.ts @@ -0,0 +1,78 @@ +import { Component, ChangeDetectionStrategy, input, output, computed } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import type { NtfNotification, NtfCategory } from '../../types/notification.types'; +import type { + NtfNotificationClickEvent, + NtfActionClickEvent, + NtfMarkReadEvent, + NtfMarkAllReadEvent, + NtfArchiveEvent, + NtfFilterChangeEvent, +} from '../../types/event.types'; +import { NtfFeedComponent } from '../ntf-feed/ntf-feed.component'; + +@Component({ + selector: 'ntf-center', + standalone: true, + imports: [CommonModule, NtfFeedComponent], + templateUrl: './ntf-center.component.html', + styleUrl: './ntf-center.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class NtfCenterComponent { + readonly notifications = input([]); + readonly activeFilter = input('all'); + readonly loading = input(false); + + readonly notificationClick = output(); + readonly markRead = output(); + readonly markAllRead = output(); + readonly archive = output(); + readonly actionClick = output(); + readonly loadMore = output(); + readonly filterChange = output(); + + readonly filters: { label: string; value: NtfCategory | 'all' }[] = [ + { label: 'All', value: 'all' }, + { label: 'Mentions', value: 'mention' }, + { label: 'Comments', value: 'comment' }, + { label: 'Assignments', value: 'assignment' }, + { label: 'System', value: 'system' }, + { label: 'Alerts', value: 'alert' }, + ]; + + readonly filteredNotifications = computed(() => { + const filter = this.activeFilter(); + const items = this.notifications(); + if (filter === 'all') return items; + return items.filter((n) => n.category === filter); + }); + + onFilterClick(filter: NtfCategory | 'all'): void { + this.filterChange.emit({ filter }); + } + + onNotificationClick(event: NtfNotificationClickEvent): void { + this.notificationClick.emit(event); + } + + onMarkRead(event: NtfMarkReadEvent): void { + this.markRead.emit(event); + } + + onMarkAllRead(event: NtfMarkAllReadEvent): void { + this.markAllRead.emit(event); + } + + onArchive(event: NtfArchiveEvent): void { + this.archive.emit(event); + } + + onActionClick(event: NtfActionClickEvent): void { + this.actionClick.emit(event); + } + + onLoadMore(): void { + this.loadMore.emit(); + } +} diff --git a/src/components/ntf-comment/index.ts b/src/components/ntf-comment/index.ts new file mode 100644 index 0000000..124052e --- /dev/null +++ b/src/components/ntf-comment/index.ts @@ -0,0 +1 @@ +export { NtfCommentComponent } from './ntf-comment.component'; diff --git a/src/components/ntf-comment/ntf-comment.component.html b/src/components/ntf-comment/ntf-comment.component.html new file mode 100644 index 0000000..4999c49 --- /dev/null +++ b/src/components/ntf-comment/ntf-comment.component.html @@ -0,0 +1,91 @@ +
+ +
+ @if (comment().authorAvatar) { + + } @else { +
+ {{ comment().authorName.charAt(0).toUpperCase() }} +
+ } +
+ + +
+ +
+ {{ comment().authorName }} + {{ timeDisplay() }} + @if (editedDisplay()) { + ({{ editedDisplay() }}) + } +
+ + + @if (isEditing()) { +
+ +
+ + +
+
+ } @else { +
+ } + + + @if (comment().reactions?.length && allowReactions()) { +
+ @for (reaction of comment().reactions; track reaction.emoji) { + + } +
+ } + + + @if (!isEditing()) { + + } +
+
diff --git a/src/components/ntf-comment/ntf-comment.component.scss b/src/components/ntf-comment/ntf-comment.component.scss new file mode 100644 index 0000000..75a706d --- /dev/null +++ b/src/components/ntf-comment/ntf-comment.component.scss @@ -0,0 +1,249 @@ +:host { + display: block; +} + +.ntf-comment { + display: flex; + gap: 0.75rem; +} + +.ntf-comment__avatar-img { + width: 32px; + height: 32px; + border-radius: 50%; + object-fit: cover; + flex-shrink: 0; +} + +.ntf-comment__avatar-placeholder { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border-radius: 50%; + background: var(--color-primary-50, #eff6ff); + color: var(--color-primary-500, #3b82f6); + font-size: 0.75rem; + font-weight: 600; + flex-shrink: 0; +} + +.ntf-comment__body { + flex: 1; + min-width: 0; + background: var(--ntf-comment-bg); + border: 1px solid var(--ntf-comment-border); + border-radius: var(--ntf-comment-radius); + padding: var(--ntf-comment-padding); +} + +.ntf-comment__header { + display: flex; + align-items: baseline; + gap: 0.5rem; + margin-bottom: 0.25rem; +} + +.ntf-comment__author { + font-size: var(--ntf-comment-author-font-size); + font-weight: var(--ntf-comment-author-font-weight); + color: var(--ntf-comment-author-color); +} + +.ntf-comment__time { + font-size: var(--font-size-xs, 0.75rem); + color: var(--ntf-item-time-color); +} + +.ntf-comment__edited { + font-size: var(--font-size-xs, 0.75rem); + color: var(--ntf-item-time-color); + font-style: italic; +} + +.ntf-comment__content { + font-size: var(--ntf-comment-content-font-size); + color: var(--ntf-comment-content-color); + line-height: 1.5; + word-break: break-word; + + :host ::ng-deep .ntf-mention { + background: var(--ntf-mention-bg); + color: var(--ntf-mention-color); + padding: 0.125rem 0.25rem; + border-radius: 0.25rem; + font-weight: 500; + } +} + +// Edit mode +.ntf-comment__edit-input { + width: 100%; + padding: 0.5rem 0.75rem; + border: 1px solid var(--ntf-feed-border); + border-radius: 0.375rem; + background: var(--ntf-item-bg); + color: var(--ntf-item-title-color); + font-size: var(--font-size-sm, 0.875rem); + font-family: inherit; + line-height: 1.5; + resize: vertical; + + &:focus { + outline: none; + border-color: var(--color-primary-500, #3b82f6); + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15); + } +} + +.ntf-comment__edit-actions { + display: flex; + justify-content: flex-end; + gap: 0.5rem; + margin-top: 0.5rem; +} + +.ntf-comment__edit-btn { + padding: 0.25rem 0.75rem; + border: 1px solid var(--ntf-feed-border); + border-radius: 0.375rem; + font-size: var(--font-size-xs, 0.75rem); + cursor: pointer; + transition: + background-color var(--ntf-transition, 150ms ease-in-out), + border-color var(--ntf-transition, 150ms ease-in-out); + + &--cancel { + background: transparent; + color: var(--ntf-item-body-color); + + &:hover { + background: var(--ntf-item-hover-bg); + } + } + + &--save { + background: var(--color-primary-500, #3b82f6); + color: #ffffff; + border-color: var(--color-primary-500, #3b82f6); + + &:hover { + background: var(--color-primary-600, #2563eb); + } + } +} + +// Reactions +.ntf-comment__reactions { + display: flex; + flex-wrap: wrap; + gap: 0.25rem; + margin-top: 0.5rem; +} + +.ntf-comment__reaction { + display: inline-flex; + align-items: center; + gap: 0.25rem; + padding: 0.125rem 0.5rem; + border: 1px solid var(--ntf-feed-border); + border-radius: 999px; + background: transparent; + font-size: var(--font-size-xs, 0.75rem); + cursor: pointer; + transition: + background-color var(--ntf-transition, 150ms ease-in-out), + border-color var(--ntf-transition, 150ms ease-in-out); + + &:hover { + background: var(--ntf-item-hover-bg); + } + + &--active { + background: var(--ntf-mention-bg); + border-color: var(--color-primary-500, #3b82f6); + } +} + +.ntf-comment__reaction-count { + color: var(--ntf-item-body-color); +} + +// Footer +.ntf-comment__footer { + display: flex; + align-items: center; + gap: 0.5rem; + margin-top: 0.5rem; +} + +.ntf-comment__footer-btn { + padding: 0; + border: none; + background: transparent; + font-size: var(--font-size-xs, 0.75rem); + color: var(--ntf-item-time-color); + cursor: pointer; + transition: color var(--ntf-transition, 150ms ease-in-out); + + &:hover { + color: var(--ntf-item-title-color); + } + + svg { + display: block; + } +} + +.ntf-comment__more { + position: relative; + margin-left: auto; +} + +.ntf-comment__more-menu { + position: absolute; + top: 100%; + right: 0; + margin-top: 4px; + min-width: 120px; + background: var(--ntf-mention-popup-bg); + border: 1px solid var(--ntf-feed-border); + border-radius: 0.375rem; + box-shadow: var(--ntf-mention-popup-shadow); + z-index: 10; + overflow: hidden; + animation: ntf-menu-in 150ms var(--ntf-ease-smooth, ease-out); +} + +.ntf-comment__more-item { + display: block; + width: 100%; + padding: 0.5rem 0.75rem; + border: none; + background: transparent; + text-align: left; + font-size: var(--font-size-sm, 0.875rem); + color: var(--ntf-item-title-color); + cursor: pointer; + transition: background-color var(--ntf-transition, 150ms ease-in-out); + + &:hover { + background: var(--ntf-item-hover-bg); + } + + &--danger { + color: var(--color-error-500, #ef4444); + } +} + +@keyframes ntf-menu-in { + from { + opacity: 0; + transform: translateY(-4px); + } + to { + opacity: 1; + transform: translateY(0); + } +} diff --git a/src/components/ntf-comment/ntf-comment.component.ts b/src/components/ntf-comment/ntf-comment.component.ts new file mode 100644 index 0000000..7a3e340 --- /dev/null +++ b/src/components/ntf-comment/ntf-comment.component.ts @@ -0,0 +1,93 @@ +import { Component, ChangeDetectionStrategy, input, output, signal, computed, inject } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { NOTIFICATION_CONFIG } from '../../providers/notification-config.provider'; +import type { NtfComment } from '../../types/comment.types'; +import type { + NtfCommentEditEvent, + NtfCommentDeleteEvent, + NtfReactionEvent, + NtfReplyEvent, +} from '../../types/event.types'; +import { formatRelativeTime, formatTimestamp } from '../../utils/time.utils'; + +@Component({ + selector: 'ntf-comment', + standalone: true, + imports: [CommonModule, FormsModule], + templateUrl: './ntf-comment.component.html', + styleUrl: './ntf-comment.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class NtfCommentComponent { + private config = inject(NOTIFICATION_CONFIG); + + readonly comment = input.required(); + readonly currentUserId = input(''); + readonly allowReactions = input(true); + readonly allowEdit = input(true); + readonly allowDelete = input(true); + + readonly reply = output(); + readonly edit = output(); + readonly delete = output(); + readonly react = output(); + + readonly isEditing = signal(false); + readonly editContent = signal(''); + readonly showActions = signal(false); + + readonly isOwner = computed(() => this.comment().authorId === this.currentUserId()); + + readonly timeDisplay = computed(() => { + const ts = this.comment().timestamp; + return this.config.relativeTime ? formatRelativeTime(ts) : formatTimestamp(ts, this.config.locale); + }); + + readonly editedDisplay = computed(() => { + const editedAt = this.comment().editedAt; + if (!editedAt) return ''; + return `edited ${this.config.relativeTime ? formatRelativeTime(editedAt) : formatTimestamp(editedAt, this.config.locale)}`; + }); + + onReply(): void { + this.reply.emit({ parentId: this.comment().id }); + } + + startEdit(): void { + this.editContent.set(this.comment().content); + this.isEditing.set(true); + this.showActions.set(false); + } + + cancelEdit(): void { + this.isEditing.set(false); + } + + saveEdit(): void { + const newContent = this.editContent().trim(); + if (newContent && newContent !== this.comment().content) { + this.edit.emit({ + comment: this.comment(), + newContent, + }); + } + this.isEditing.set(false); + } + + onDelete(): void { + this.delete.emit({ commentId: this.comment().id }); + this.showActions.set(false); + } + + onReact(emoji: string): void { + this.react.emit({ + commentId: this.comment().id, + emoji, + }); + } + + toggleActions(): void { + this.showActions.update((v) => !v); + } +} diff --git a/src/components/ntf-empty-state/index.ts b/src/components/ntf-empty-state/index.ts new file mode 100644 index 0000000..edbb260 --- /dev/null +++ b/src/components/ntf-empty-state/index.ts @@ -0,0 +1 @@ +export { NtfEmptyStateComponent } from './ntf-empty-state.component'; diff --git a/src/components/ntf-empty-state/ntf-empty-state.component.ts b/src/components/ntf-empty-state/ntf-empty-state.component.ts new file mode 100644 index 0000000..fcc4036 --- /dev/null +++ b/src/components/ntf-empty-state/ntf-empty-state.component.ts @@ -0,0 +1,82 @@ +import { Component, ChangeDetectionStrategy, input } from '@angular/core'; + +@Component({ + selector: 'ntf-empty-state', + standalone: true, + template: ` +
+
+ + @switch (icon()) { + @case ('bell') { + + + } + @case ('inbox') { + + + } + @case ('comment') { + + } + @default { + + + } + } + +
+

{{ title() }}

+ @if (description()) { +

{{ description() }}

+ } +
+ `, + styles: [` + :host { + display: block; + } + + .ntf-empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 3rem 1.5rem; + text-align: center; + } + + .ntf-empty-state__icon { + width: var(--ntf-empty-icon-size, 48px); + height: var(--ntf-empty-icon-size, 48px); + color: var(--ntf-empty-color, #9ca3af); + margin-bottom: 1rem; + opacity: 0.6; + + svg { + width: 100%; + height: 100%; + } + } + + .ntf-empty-state__title { + font-size: var(--font-size-sm, 0.875rem); + font-weight: 500; + color: var(--ntf-item-title-color, #111827); + margin-bottom: 0.25rem; + } + + .ntf-empty-state__description { + font-size: var(--font-size-xs, 0.75rem); + color: var(--ntf-empty-color, #9ca3af); + margin: 0; + max-width: 240px; + } + `], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class NtfEmptyStateComponent { + readonly icon = input('bell'); + readonly title = input('No notifications'); + readonly description = input(''); +} diff --git a/src/components/ntf-feed-item/index.ts b/src/components/ntf-feed-item/index.ts new file mode 100644 index 0000000..02d292d --- /dev/null +++ b/src/components/ntf-feed-item/index.ts @@ -0,0 +1 @@ +export { NtfFeedItemComponent } from './ntf-feed-item.component'; diff --git a/src/components/ntf-feed-item/ntf-feed-item.component.html b/src/components/ntf-feed-item/ntf-feed-item.component.html new file mode 100644 index 0000000..8d29af1 --- /dev/null +++ b/src/components/ntf-feed-item/ntf-feed-item.component.html @@ -0,0 +1,103 @@ +@if (customTemplate()) { +
+ +
+} @else { +
+ + @if (isUnread()) { +
+ } + + +
+ @if (notification().avatar || notification().sender?.avatar) { + + } @else { +
+ + @switch (getCategoryIcon()) { + @case ('at-sign') { + + } + @case ('message') { + + } + @case ('user-plus') { + + } + @case ('refresh') { + + } + @case ('alert-triangle') { + + } + @case ('mail') { + + } + @case ('settings') { + + } + @default { + + } + } + +
+ } +
+ + +
+
+ {{ notification().title }} + {{ timeDisplay() }} +
+ + @if (notification().body) { +

{{ notification().body }}

+ } + + @if (notification().actions?.length) { +
+ @for (action of notification().actions; track action.id) { + + } +
+ } +
+ + +
+ @if (isUnread()) { + + } + +
+
+} diff --git a/src/components/ntf-feed-item/ntf-feed-item.component.scss b/src/components/ntf-feed-item/ntf-feed-item.component.scss new file mode 100644 index 0000000..0cc604d --- /dev/null +++ b/src/components/ntf-feed-item/ntf-feed-item.component.scss @@ -0,0 +1,189 @@ +:host { + display: block; +} + +.ntf-feed-item { + position: relative; + display: flex; + gap: var(--ntf-item-gap); + padding: var(--ntf-item-padding); + background: var(--ntf-item-bg); + border-bottom: 1px solid var(--ntf-item-border); + cursor: pointer; + transition: + background-color var(--ntf-transition, 150ms ease-in-out), + border-color var(--ntf-transition, 150ms ease-in-out); + + &:hover { + background: var(--ntf-item-hover-bg); + + .ntf-feed-item__quick-actions { + opacity: 1; + pointer-events: auto; + } + } + + &--unread { + background: var(--ntf-item-unread-bg); + border-left: 3px solid var(--ntf-item-unread-border); + + .ntf-feed-item__title { + font-weight: 600; + } + } +} + +.ntf-feed-item__unread-dot { + position: absolute; + top: 50%; + left: 6px; + transform: translateY(-50%); + width: 6px; + height: 6px; + border-radius: 50%; + background: var(--ntf-item-unread-border); +} + +.ntf-feed-item__avatar { + flex-shrink: 0; +} + +.ntf-feed-item__avatar-img { + width: 36px; + height: 36px; + border-radius: 50%; + object-fit: cover; +} + +.ntf-feed-item__avatar-icon { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + border-radius: 50%; + background: var(--color-primary-50, #eff6ff); + color: var(--color-primary-500, #3b82f6); + + svg { + width: 18px; + height: 18px; + } +} + +.ntf-feed-item__content { + flex: 1; + min-width: 0; +} + +.ntf-feed-item__header { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 0.5rem; +} + +.ntf-feed-item__title { + font-size: var(--ntf-item-title-font-size); + font-weight: var(--ntf-item-title-font-weight); + color: var(--ntf-item-title-color); + line-height: 1.4; +} + +.ntf-feed-item__time { + font-size: var(--ntf-item-time-font-size); + color: var(--ntf-item-time-color); + white-space: nowrap; + flex-shrink: 0; +} + +.ntf-feed-item__body { + margin: 0.25rem 0 0; + font-size: var(--ntf-item-body-font-size); + color: var(--ntf-item-body-color); + line-height: 1.5; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.ntf-feed-item__actions { + display: flex; + gap: 0.5rem; + margin-top: 0.5rem; +} + +.ntf-feed-item__action { + padding: 0.25rem 0.75rem; + border: 1px solid var(--ntf-item-border); + border-radius: 0.375rem; + background: transparent; + font-size: var(--font-size-xs, 0.75rem); + color: var(--ntf-item-title-color); + cursor: pointer; + transition: + background-color var(--ntf-transition, 150ms ease-in-out), + border-color var(--ntf-transition, 150ms ease-in-out); + + &:hover { + background: var(--ntf-item-hover-bg); + } + + &--primary { + background: var(--color-primary-500, #3b82f6); + color: #ffffff; + border-color: var(--color-primary-500, #3b82f6); + + &:hover { + background: var(--color-primary-600, #2563eb); + } + } + + &--danger { + color: var(--color-error-500, #ef4444); + border-color: var(--color-error-500, #ef4444); + + &:hover { + background: rgba(239, 68, 68, 0.1); + } + } +} + +.ntf-feed-item__quick-actions { + position: absolute; + top: 0.5rem; + right: 0.5rem; + display: flex; + gap: 0.25rem; + opacity: 0; + pointer-events: none; + transition: opacity var(--ntf-transition, 150ms ease-in-out); +} + +.ntf-feed-item__quick-btn { + display: flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + padding: 0; + border: 1px solid var(--ntf-item-border); + border-radius: 0.375rem; + background: var(--ntf-item-bg); + color: var(--ntf-item-body-color); + cursor: pointer; + transition: + background-color var(--ntf-transition, 150ms ease-in-out), + color var(--ntf-transition, 150ms ease-in-out); + + &:hover { + background: var(--ntf-item-hover-bg); + color: var(--ntf-item-title-color); + } + + svg { + width: 14px; + height: 14px; + } +} diff --git a/src/components/ntf-feed-item/ntf-feed-item.component.ts b/src/components/ntf-feed-item/ntf-feed-item.component.ts new file mode 100644 index 0000000..1c80eed --- /dev/null +++ b/src/components/ntf-feed-item/ntf-feed-item.component.ts @@ -0,0 +1,80 @@ +import { Component, ChangeDetectionStrategy, TemplateRef, input, output, computed, inject } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { NOTIFICATION_CONFIG } from '../../providers/notification-config.provider'; +import type { NtfNotification, NtfAction } from '../../types/notification.types'; +import type { NtfNotificationClickEvent, NtfActionClickEvent, NtfMarkReadEvent, NtfArchiveEvent } from '../../types/event.types'; +import { formatRelativeTime, formatTimestamp } from '../../utils/time.utils'; +import type { NtfItemDefContext } from '../ntf-item-def/ntf-item-def.directive'; + +@Component({ + selector: 'ntf-feed-item', + standalone: true, + imports: [CommonModule], + templateUrl: './ntf-feed-item.component.html', + styleUrl: './ntf-feed-item.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class NtfFeedItemComponent { + private config = inject(NOTIFICATION_CONFIG); + + readonly notification = input.required(); + readonly customTemplate = input | null>(null); + readonly index = input(0); + + readonly notificationClick = output(); + readonly markRead = output(); + readonly archive = output(); + readonly actionClick = output(); + + readonly isUnread = computed(() => this.notification().status === 'unread'); + + readonly timeDisplay = computed(() => { + const ts = this.notification().timestamp; + return this.config.relativeTime ? formatRelativeTime(ts) : formatTimestamp(ts, this.config.locale); + }); + + readonly templateContext = computed(() => ({ + $implicit: this.notification(), + notification: this.notification(), + index: this.index(), + isRead: this.notification().status === 'read', + })); + + onClick(): void { + this.notificationClick.emit({ + notification: this.notification(), + index: this.index(), + }); + } + + onMarkRead(event: Event): void { + event.stopPropagation(); + this.markRead.emit({ notificationId: this.notification().id }); + } + + onArchive(event: Event): void { + event.stopPropagation(); + this.archive.emit({ notificationId: this.notification().id }); + } + + onActionClick(event: Event, action: NtfAction): void { + event.stopPropagation(); + this.actionClick.emit({ + notification: this.notification(), + action, + }); + } + + getCategoryIcon(): string { + switch (this.notification().category) { + case 'mention': return 'at-sign'; + case 'comment': return 'message'; + case 'assignment': return 'user-plus'; + case 'update': return 'refresh'; + case 'alert': return 'alert-triangle'; + case 'message': return 'mail'; + case 'system': return 'settings'; + default: return 'bell'; + } + } +} diff --git a/src/components/ntf-feed/index.ts b/src/components/ntf-feed/index.ts new file mode 100644 index 0000000..f2370c3 --- /dev/null +++ b/src/components/ntf-feed/index.ts @@ -0,0 +1 @@ +export { NtfFeedComponent } from './ntf-feed.component'; diff --git a/src/components/ntf-feed/ntf-feed.component.html b/src/components/ntf-feed/ntf-feed.component.html new file mode 100644 index 0000000..93d3ebc --- /dev/null +++ b/src/components/ntf-feed/ntf-feed.component.html @@ -0,0 +1,70 @@ +
+ + @if (showHeader()) { +
+
+

{{ title() }}

+ @if (unreadCount() > 0) { + {{ unreadCount() }} + } +
+ @if (unreadCount() > 0) { + + } +
+ } + + +
+ @if (loading()) { +
+
+
+ } @else if (!hasNotifications()) { + + } @else { + @if (grouped()) { + @for (group of groupedNotifications(); track group.label) { +
+
{{ group.label }}
+ @for (item of group.items; track item.id; let i = $index) { + + } +
+ } + } @else { + @for (item of visibleNotifications(); track item.id; let i = $index) { + + } + } + + @if (hasMore()) { + + } + } +
+
diff --git a/src/components/ntf-feed/ntf-feed.component.scss b/src/components/ntf-feed/ntf-feed.component.scss new file mode 100644 index 0000000..3d448dd --- /dev/null +++ b/src/components/ntf-feed/ntf-feed.component.scss @@ -0,0 +1,141 @@ +:host { + display: block; +} + +.ntf-feed { + background: var(--ntf-feed-bg); + border: 1px solid var(--ntf-feed-border); + border-radius: var(--ntf-feed-radius); + box-shadow: var(--ntf-feed-shadow); + width: var(--ntf-feed-width); + overflow: hidden; +} + +.ntf-feed__header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 1rem 1rem 0.75rem; + border-bottom: 1px solid var(--ntf-item-border); +} + +.ntf-feed__header-left { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.ntf-feed__title { + font-size: var(--font-size-sm, 0.875rem); + font-weight: 600; + color: var(--ntf-item-title-color); + margin: 0; +} + +.ntf-feed__unread-badge { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 20px; + height: 20px; + padding: 0 6px; + border-radius: 999px; + background: var(--ntf-badge-bg); + color: var(--ntf-badge-color); + font-size: 0.6875rem; + font-weight: 600; + line-height: 1; +} + +.ntf-feed__mark-all { + padding: 0.25rem 0.5rem; + border: none; + border-radius: 0.375rem; + background: transparent; + font-size: var(--font-size-xs, 0.75rem); + color: var(--color-primary-500, #3b82f6); + cursor: pointer; + transition: background-color var(--ntf-transition, 150ms ease-in-out); + + &:hover { + background: var(--ntf-item-hover-bg); + } +} + +.ntf-feed__content { + max-height: var(--ntf-feed-max-height); + overflow-y: auto; + + &::-webkit-scrollbar { + width: 4px; + } + + &::-webkit-scrollbar-track { + background: transparent; + } + + &::-webkit-scrollbar-thumb { + background: var(--ntf-item-time-color); + border-radius: 4px; + + &:hover { + background: var(--ntf-item-body-color); + } + } + + scrollbar-width: thin; + scrollbar-color: var(--ntf-item-time-color) transparent; +} + +.ntf-feed__group-label { + padding: 0.5rem 1rem; + font-size: var(--font-size-xs, 0.75rem); + font-weight: 600; + color: var(--ntf-item-time-color); + text-transform: uppercase; + letter-spacing: 0.05em; + background: var(--ntf-item-hover-bg); + border-bottom: 1px solid var(--ntf-item-border); + position: sticky; + top: 0; + z-index: 1; +} + +.ntf-feed__loading { + display: flex; + align-items: center; + justify-content: center; + padding: 3rem; +} + +.ntf-feed__spinner { + width: 24px; + height: 24px; + border: 2px solid var(--ntf-item-border); + border-top-color: var(--color-primary-500, #3b82f6); + border-radius: 50%; + animation: ntf-spin 0.6s linear infinite; +} + +.ntf-feed__load-more { + display: block; + width: 100%; + padding: 0.75rem; + border: none; + border-top: 1px solid var(--ntf-item-border); + background: transparent; + font-size: var(--font-size-sm, 0.875rem); + color: var(--color-primary-500, #3b82f6); + cursor: pointer; + transition: background-color var(--ntf-transition, 150ms ease-in-out); + + &:hover { + background: var(--ntf-item-hover-bg); + } +} + +@keyframes ntf-spin { + to { + transform: rotate(360deg); + } +} diff --git a/src/components/ntf-feed/ntf-feed.component.ts b/src/components/ntf-feed/ntf-feed.component.ts new file mode 100644 index 0000000..1d9c8fd --- /dev/null +++ b/src/components/ntf-feed/ntf-feed.component.ts @@ -0,0 +1,91 @@ +import { Component, ChangeDetectionStrategy, input, output, computed, contentChildren } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import type { NtfNotification } from '../../types/notification.types'; +import type { + NtfNotificationClickEvent, + NtfActionClickEvent, + NtfMarkReadEvent, + NtfMarkAllReadEvent, + NtfArchiveEvent, +} from '../../types/event.types'; +import { groupByDate } from '../../utils/time.utils'; +import { NtfFeedItemComponent } from '../ntf-feed-item/ntf-feed-item.component'; +import { NtfEmptyStateComponent } from '../ntf-empty-state/ntf-empty-state.component'; +import { NtfItemDefDirective } from '../ntf-item-def/ntf-item-def.directive'; + +@Component({ + selector: 'ntf-feed', + standalone: true, + imports: [CommonModule, NtfFeedItemComponent, NtfEmptyStateComponent], + templateUrl: './ntf-feed.component.html', + styleUrl: './ntf-feed.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class NtfFeedComponent { + readonly notifications = input([]); + readonly grouped = input(false); + readonly maxVisible = input(50); + readonly loading = input(false); + readonly emptyMessage = input('No notifications yet'); + readonly emptyDescription = input('When you receive notifications, they will appear here.'); + readonly showHeader = input(true); + readonly title = input('Notifications'); + + readonly notificationClick = output(); + readonly markRead = output(); + readonly markAllRead = output(); + readonly archive = output(); + readonly actionClick = output(); + readonly loadMore = output(); + + private itemDefs = contentChildren(NtfItemDefDirective); + + protected itemTemplate = computed(() => { + const defs = this.itemDefs(); + return defs.length > 0 ? defs[0].templateRef : null; + }); + + readonly visibleNotifications = computed(() => { + const items = this.notifications().filter((n) => n.status !== 'archived'); + return items.slice(0, this.maxVisible()); + }); + + readonly unreadCount = computed(() => + this.notifications().filter((n) => n.status === 'unread').length, + ); + + readonly groupedNotifications = computed(() => { + if (!this.grouped()) return []; + return groupByDate(this.visibleNotifications()); + }); + + readonly hasNotifications = computed(() => this.visibleNotifications().length > 0); + + readonly hasMore = computed(() => + this.notifications().filter((n) => n.status !== 'archived').length > this.maxVisible(), + ); + + onNotificationClick(event: NtfNotificationClickEvent): void { + this.notificationClick.emit(event); + } + + onMarkRead(event: NtfMarkReadEvent): void { + this.markRead.emit(event); + } + + onMarkAllRead(): void { + this.markAllRead.emit({ count: this.unreadCount() }); + } + + onArchive(event: NtfArchiveEvent): void { + this.archive.emit(event); + } + + onActionClick(event: NtfActionClickEvent): void { + this.actionClick.emit(event); + } + + onLoadMore(): void { + this.loadMore.emit(); + } +} diff --git a/src/components/ntf-inbox-item/index.ts b/src/components/ntf-inbox-item/index.ts new file mode 100644 index 0000000..68bc77e --- /dev/null +++ b/src/components/ntf-inbox-item/index.ts @@ -0,0 +1 @@ +export { NtfInboxItemComponent } from './ntf-inbox-item.component'; diff --git a/src/components/ntf-inbox-item/ntf-inbox-item.component.html b/src/components/ntf-inbox-item/ntf-inbox-item.component.html new file mode 100644 index 0000000..99d4ce3 --- /dev/null +++ b/src/components/ntf-inbox-item/ntf-inbox-item.component.html @@ -0,0 +1,42 @@ +
+ + + + +
+
+ {{ message().from }} + {{ timeDisplay() }} +
+
{{ message().subject }}
+
{{ message().preview }}
+ + @if (message().labels?.length) { +
+ @for (label of message().labels; track label.id) { + + {{ label.name }} + + } +
+ } +
+ + + +
diff --git a/src/components/ntf-inbox-item/ntf-inbox-item.component.scss b/src/components/ntf-inbox-item/ntf-inbox-item.component.scss new file mode 100644 index 0000000..faa6f8a --- /dev/null +++ b/src/components/ntf-inbox-item/ntf-inbox-item.component.scss @@ -0,0 +1,156 @@ +:host { + display: block; +} + +.ntf-inbox-item { + display: flex; + align-items: flex-start; + gap: 0.5rem; + padding: 0.75rem 1rem; + background: var(--ntf-inbox-item-bg); + border-bottom: 1px solid var(--ntf-item-border); + cursor: pointer; + transition: + background-color var(--ntf-transition, 150ms ease-in-out), + border-color var(--ntf-transition, 150ms ease-in-out); + + &:hover { + background: var(--ntf-inbox-item-hover-bg); + + .ntf-inbox-item__archive { + opacity: 1; + } + } + + &--unread { + .ntf-inbox-item__from, + .ntf-inbox-item__subject { + font-weight: var(--ntf-inbox-unread-weight); + } + } + + &--selected { + background: var(--ntf-inbox-selected-bg); + } +} + +.ntf-inbox-item__star { + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + padding: 0; + border: none; + background: transparent; + color: var(--ntf-item-time-color); + cursor: pointer; + flex-shrink: 0; + margin-top: 2px; + transition: color var(--ntf-transition, 150ms ease-in-out); + + svg { + width: 14px; + height: 14px; + } + + &:hover { + color: var(--ntf-inbox-star-color); + } + + .ntf-inbox-item--unread &, + [fill="currentColor"] & { + color: var(--ntf-inbox-star-color); + } +} + +// Star filled state +.ntf-inbox-item__star svg[fill="currentColor"] { + color: var(--ntf-inbox-star-color); +} + +.ntf-inbox-item__content { + flex: 1; + min-width: 0; +} + +.ntf-inbox-item__header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.5rem; +} + +.ntf-inbox-item__from { + font-size: var(--font-size-sm, 0.875rem); + color: var(--ntf-item-title-color); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.ntf-inbox-item__time { + font-size: var(--font-size-xs, 0.75rem); + color: var(--ntf-item-time-color); + white-space: nowrap; + flex-shrink: 0; +} + +.ntf-inbox-item__subject { + font-size: var(--font-size-sm, 0.875rem); + color: var(--ntf-item-title-color); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-top: 0.125rem; +} + +.ntf-inbox-item__preview { + font-size: var(--font-size-xs, 0.75rem); + color: var(--ntf-item-body-color); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-top: 0.125rem; +} + +.ntf-inbox-item__labels { + display: flex; + gap: 0.25rem; + margin-top: 0.375rem; +} + +.ntf-inbox-item__label { + display: inline-block; + padding: 0.0625rem 0.375rem; + border-radius: 999px; + font-size: 0.625rem; + font-weight: 500; +} + +.ntf-inbox-item__archive { + display: flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + padding: 0; + border: none; + background: transparent; + color: var(--ntf-item-time-color); + cursor: pointer; + flex-shrink: 0; + opacity: 0; + transition: + opacity var(--ntf-transition, 150ms ease-in-out), + color var(--ntf-transition, 150ms ease-in-out); + + svg { + width: 14px; + height: 14px; + } + + &:hover { + color: var(--ntf-item-title-color); + } +} diff --git a/src/components/ntf-inbox-item/ntf-inbox-item.component.ts b/src/components/ntf-inbox-item/ntf-inbox-item.component.ts new file mode 100644 index 0000000..d61f374 --- /dev/null +++ b/src/components/ntf-inbox-item/ntf-inbox-item.component.ts @@ -0,0 +1,47 @@ +import { Component, ChangeDetectionStrategy, input, output, computed, inject } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { NOTIFICATION_CONFIG } from '../../providers/notification-config.provider'; +import type { NtfInboxMessage } from '../../types/inbox.types'; +import type { NtfInboxSelectEvent, NtfInboxStarEvent, NtfInboxArchiveEvent } from '../../types/event.types'; +import { formatRelativeTime, formatTimestamp } from '../../utils/time.utils'; + +@Component({ + selector: 'ntf-inbox-item', + standalone: true, + imports: [CommonModule], + templateUrl: './ntf-inbox-item.component.html', + styleUrl: './ntf-inbox-item.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class NtfInboxItemComponent { + private config = inject(NOTIFICATION_CONFIG); + + readonly message = input.required(); + readonly selected = input(false); + + readonly select = output(); + readonly star = output(); + readonly archive = output(); + + readonly timeDisplay = computed(() => { + const ts = this.message().timestamp; + return this.config.relativeTime ? formatRelativeTime(ts) : formatTimestamp(ts, this.config.locale); + }); + + onClick(): void { + this.select.emit({ message: this.message() }); + } + + onStar(event: Event): void { + event.stopPropagation(); + this.star.emit({ + messageId: this.message().id, + starred: !this.message().starred, + }); + } + + onArchive(event: Event): void { + event.stopPropagation(); + this.archive.emit({ messageId: this.message().id }); + } +} diff --git a/src/components/ntf-inbox/index.ts b/src/components/ntf-inbox/index.ts new file mode 100644 index 0000000..440eb87 --- /dev/null +++ b/src/components/ntf-inbox/index.ts @@ -0,0 +1 @@ +export { NtfInboxComponent } from './ntf-inbox.component'; diff --git a/src/components/ntf-inbox/ntf-inbox.component.html b/src/components/ntf-inbox/ntf-inbox.component.html new file mode 100644 index 0000000..e2a4849 --- /dev/null +++ b/src/components/ntf-inbox/ntf-inbox.component.html @@ -0,0 +1,66 @@ +
+ +
+ + + + +
+ @if (loading()) { +
+
+
+ } @else if (filteredMessages().length === 0) { + + } @else { + @for (msg of filteredMessages(); track msg.id) { + + } + } +
+
+ + +
+ @if (selectedMessage()) { +
+

{{ selectedMessage()!.subject }}

+
+ {{ selectedMessage()!.from }} +
+
+
+ {{ selectedMessage()!.body || selectedMessage()!.preview }} +
+ } @else { +
+ +
+ } +
+
diff --git a/src/components/ntf-inbox/ntf-inbox.component.scss b/src/components/ntf-inbox/ntf-inbox.component.scss new file mode 100644 index 0000000..4821f21 --- /dev/null +++ b/src/components/ntf-inbox/ntf-inbox.component.scss @@ -0,0 +1,141 @@ +:host { + display: block; +} + +.ntf-inbox { + display: flex; + height: 100%; + min-height: 400px; + background: var(--ntf-feed-bg); + border: 1px solid var(--ntf-feed-border); + border-radius: var(--ntf-feed-radius); + overflow: hidden; +} + +// List panel +.ntf-inbox__list { + width: 360px; + min-width: 280px; + border-right: 1px solid var(--ntf-item-border); + display: flex; + flex-direction: column; + flex-shrink: 0; +} + +.ntf-inbox__search { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem 1rem; + border-bottom: 1px solid var(--ntf-item-border); +} + +.ntf-inbox__search-icon { + width: 16px; + height: 16px; + color: var(--ntf-item-time-color); + flex-shrink: 0; +} + +.ntf-inbox__search-input { + flex: 1; + border: none; + background: transparent; + font-size: var(--font-size-sm, 0.875rem); + color: var(--ntf-item-title-color); + outline: none; + + &::placeholder { + color: var(--ntf-item-time-color); + } +} + +.ntf-inbox__messages { + flex: 1; + overflow-y: auto; + + &::-webkit-scrollbar { + width: 4px; + } + + &::-webkit-scrollbar-track { + background: transparent; + } + + &::-webkit-scrollbar-thumb { + background: var(--ntf-item-time-color); + border-radius: 4px; + } + + scrollbar-width: thin; + scrollbar-color: var(--ntf-item-time-color) transparent; +} + +.ntf-inbox__loading { + display: flex; + align-items: center; + justify-content: center; + padding: 3rem; +} + +.ntf-inbox__spinner { + width: 24px; + height: 24px; + border: 2px solid var(--ntf-item-border); + border-top-color: var(--color-primary-500, #3b82f6); + border-radius: 50%; + animation: ntf-inbox-spin 0.6s linear infinite; +} + +// Detail panel +.ntf-inbox__detail { + flex: 1; + display: flex; + flex-direction: column; + overflow-y: auto; +} + +.ntf-inbox__detail-header { + padding: 1.5rem; + border-bottom: 1px solid var(--ntf-item-border); +} + +.ntf-inbox__detail-subject { + font-size: var(--font-size-lg, 1.125rem); + font-weight: 600; + color: var(--ntf-item-title-color); + margin: 0 0 0.25rem; +} + +.ntf-inbox__detail-meta { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.ntf-inbox__detail-from { + font-size: var(--font-size-sm, 0.875rem); + color: var(--ntf-item-body-color); +} + +.ntf-inbox__detail-body { + flex: 1; + padding: 1.5rem; + font-size: var(--font-size-sm, 0.875rem); + color: var(--ntf-item-title-color); + line-height: 1.7; + white-space: pre-wrap; +} + +.ntf-inbox__detail-empty { + flex: 1; + display: flex; + align-items: center; + justify-content: center; +} + +@keyframes ntf-inbox-spin { + to { + transform: rotate(360deg); + } +} diff --git a/src/components/ntf-inbox/ntf-inbox.component.ts b/src/components/ntf-inbox/ntf-inbox.component.ts new file mode 100644 index 0000000..bd79396 --- /dev/null +++ b/src/components/ntf-inbox/ntf-inbox.component.ts @@ -0,0 +1,65 @@ +import { Component, ChangeDetectionStrategy, input, output, signal, computed } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import type { NtfInboxMessage } from '../../types/inbox.types'; +import type { NtfInboxSelectEvent, NtfInboxStarEvent, NtfInboxArchiveEvent } from '../../types/event.types'; +import { NtfInboxItemComponent } from '../ntf-inbox-item/ntf-inbox-item.component'; +import { NtfEmptyStateComponent } from '../ntf-empty-state/ntf-empty-state.component'; + +@Component({ + selector: 'ntf-inbox', + standalone: true, + imports: [CommonModule, FormsModule, NtfInboxItemComponent, NtfEmptyStateComponent], + templateUrl: './ntf-inbox.component.html', + styleUrl: './ntf-inbox.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class NtfInboxComponent { + readonly messages = input([]); + readonly selectedId = input(''); + readonly loading = input(false); + + readonly messageSelect = output(); + readonly messageStar = output(); + readonly messageArchive = output(); + readonly messageDelete = output<{ messageId: string }>(); + + readonly searchTerm = signal(''); + + readonly filteredMessages = computed(() => { + const term = this.searchTerm().toLowerCase(); + if (!term) return this.messages(); + return this.messages().filter( + (m) => + m.subject.toLowerCase().includes(term) || + m.from.toLowerCase().includes(term) || + m.preview.toLowerCase().includes(term), + ); + }); + + readonly selectedMessage = computed(() => { + const id = this.selectedId(); + if (!id) return null; + return this.messages().find((m) => m.id === id) ?? null; + }); + + readonly unreadCount = computed(() => + this.messages().filter((m) => !m.read).length, + ); + + onSearch(event: Event): void { + this.searchTerm.set((event.target as HTMLInputElement).value); + } + + onMessageSelect(event: NtfInboxSelectEvent): void { + this.messageSelect.emit(event); + } + + onMessageStar(event: NtfInboxStarEvent): void { + this.messageStar.emit(event); + } + + onMessageArchive(event: NtfInboxArchiveEvent): void { + this.messageArchive.emit(event); + } +} diff --git a/src/components/ntf-item-def/index.ts b/src/components/ntf-item-def/index.ts new file mode 100644 index 0000000..8ae4691 --- /dev/null +++ b/src/components/ntf-item-def/index.ts @@ -0,0 +1 @@ +export { NtfItemDefDirective, type NtfItemDefContext } from './ntf-item-def.directive'; diff --git a/src/components/ntf-item-def/ntf-item-def.directive.ts b/src/components/ntf-item-def/ntf-item-def.directive.ts new file mode 100644 index 0000000..4d053f1 --- /dev/null +++ b/src/components/ntf-item-def/ntf-item-def.directive.ts @@ -0,0 +1,25 @@ +import { Directive, TemplateRef, inject } from '@angular/core'; +import type { NtfNotification } from '../../types/notification.types'; + +/** Template context for custom notification item rendering */ +export interface NtfItemDefContext { + $implicit: NtfNotification; + notification: NtfNotification; + index: number; + isRead: boolean; +} + +@Directive({ + selector: 'ng-template[ntfItemDef]', + standalone: true, +}) +export class NtfItemDefDirective { + readonly templateRef = inject(TemplateRef); + + static ngTemplateContextGuard( + _dir: NtfItemDefDirective, + ctx: unknown, + ): ctx is NtfItemDefContext { + return true; + } +} diff --git a/src/components/ntf-mention-input/index.ts b/src/components/ntf-mention-input/index.ts new file mode 100644 index 0000000..1fffc10 --- /dev/null +++ b/src/components/ntf-mention-input/index.ts @@ -0,0 +1 @@ +export { NtfMentionInputComponent } from './ntf-mention-input.component'; diff --git a/src/components/ntf-mention-input/ntf-mention-input.component.html b/src/components/ntf-mention-input/ntf-mention-input.component.html new file mode 100644 index 0000000..42cd985 --- /dev/null +++ b/src/components/ntf-mention-input/ntf-mention-input.component.html @@ -0,0 +1,59 @@ +
+
+ @if (multiline()) { + + } @else { + + } + + + @if (showPopup() && filteredUsers().length > 0) { +
+ @for (user of filteredUsers(); track user.id; let i = $index) { + + } +
+ } +
+ + @if (effectiveMaxLength() > 0) { + + } +
diff --git a/src/components/ntf-mention-input/ntf-mention-input.component.scss b/src/components/ntf-mention-input/ntf-mention-input.component.scss new file mode 100644 index 0000000..6e36d98 --- /dev/null +++ b/src/components/ntf-mention-input/ntf-mention-input.component.scss @@ -0,0 +1,133 @@ +:host { + display: block; +} + +.ntf-mention-input { + position: relative; +} + +.ntf-mention-input__wrapper { + position: relative; +} + +.ntf-mention-input__field { + width: 100%; + padding: 0.625rem 0.875rem; + border: 1px solid var(--ntf-feed-border); + border-radius: 0.5rem; + background: var(--ntf-item-bg); + color: var(--ntf-item-title-color); + font-size: var(--font-size-sm, 0.875rem); + font-family: inherit; + line-height: 1.5; + resize: none; + transition: + border-color var(--ntf-transition, 150ms ease-in-out), + box-shadow var(--ntf-transition, 150ms ease-in-out); + + &::placeholder { + color: var(--ntf-item-time-color); + } + + &:focus { + outline: none; + border-color: var(--color-primary-500, #3b82f6); + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15); + } + + &--multiline { + min-height: 80px; + resize: vertical; + } +} + +.ntf-mention-input__popup { + position: absolute; + bottom: 100%; + left: 0; + right: 0; + margin-bottom: 4px; + background: var(--ntf-mention-popup-bg); + border: 1px solid var(--ntf-feed-border); + border-radius: var(--ntf-mention-popup-radius); + box-shadow: var(--ntf-mention-popup-shadow); + max-height: var(--ntf-mention-popup-max-height); + overflow-y: auto; + z-index: 10; + animation: ntf-popup-in 150ms var(--ntf-ease-smooth, ease-out); +} + +.ntf-mention-input__popup-item { + display: flex; + align-items: center; + gap: 0.5rem; + width: 100%; + padding: 0.5rem 0.75rem; + border: none; + background: transparent; + text-align: left; + cursor: pointer; + transition: background-color var(--ntf-transition, 150ms ease-in-out); + + &:hover, + &--active { + background: var(--ntf-item-hover-bg); + } + + &:first-child { + border-radius: var(--ntf-mention-popup-radius) var(--ntf-mention-popup-radius) 0 0; + } + + &:last-child { + border-radius: 0 0 var(--ntf-mention-popup-radius) var(--ntf-mention-popup-radius); + } +} + +.ntf-mention-input__popup-avatar { + width: 24px; + height: 24px; + border-radius: 50%; + object-fit: cover; + flex-shrink: 0; +} + +.ntf-mention-input__popup-avatar-placeholder { + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + border-radius: 50%; + background: var(--color-primary-50, #eff6ff); + color: var(--color-primary-500, #3b82f6); + font-size: 0.6875rem; + font-weight: 600; + flex-shrink: 0; +} + +.ntf-mention-input__popup-name { + font-size: var(--font-size-sm, 0.875rem); + color: var(--ntf-item-title-color); +} + +.ntf-mention-input__footer { + display: flex; + justify-content: flex-end; + padding-top: 0.25rem; +} + +.ntf-mention-input__counter { + font-size: var(--font-size-xs, 0.75rem); + color: var(--ntf-item-time-color); +} + +@keyframes ntf-popup-in { + from { + opacity: 0; + transform: translateY(4px); + } + to { + opacity: 1; + transform: translateY(0); + } +} diff --git a/src/components/ntf-mention-input/ntf-mention-input.component.ts b/src/components/ntf-mention-input/ntf-mention-input.component.ts new file mode 100644 index 0000000..464c7c7 --- /dev/null +++ b/src/components/ntf-mention-input/ntf-mention-input.component.ts @@ -0,0 +1,140 @@ +import { + Component, ChangeDetectionStrategy, input, output, signal, computed, + ElementRef, viewChild, inject, +} from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { NOTIFICATION_CONFIG } from '../../providers/notification-config.provider'; +import type { NtfMention } from '../../types/notification.types'; +import type { NtfMentionSelectEvent, NtfMentionSearchEvent } from '../../types/event.types'; +import { insertMention } from '../../utils/mention.utils'; + +@Component({ + selector: 'ntf-mention-input', + standalone: true, + imports: [CommonModule, FormsModule], + templateUrl: './ntf-mention-input.component.html', + styleUrl: './ntf-mention-input.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class NtfMentionInputComponent { + private config = inject(NOTIFICATION_CONFIG); + + readonly value = input(''); + readonly users = input([]); + readonly placeholder = input('Type a message...'); + readonly multiline = input(true); + readonly maxLength = input(0); + + readonly valueChange = output(); + readonly mentionSearch = output(); + readonly mentionSelect = output(); + readonly submit = output(); + + readonly textValue = signal(''); + readonly showPopup = signal(false); + readonly mentionQuery = signal(''); + readonly selectedIndex = signal(0); + + private inputRef = viewChild>('inputEl'); + + readonly filteredUsers = computed(() => { + const q = this.mentionQuery().toLowerCase(); + if (!q) return this.users(); + return this.users().filter((u) => u.name.toLowerCase().includes(q)); + }); + + readonly effectiveMaxLength = computed(() => + this.maxLength() || this.config.commentMaxLength, + ); + + onInput(event: Event): void { + const target = event.target as HTMLTextAreaElement | HTMLInputElement; + const val = target.value; + this.textValue.set(val); + this.valueChange.emit(val); + + const cursorPos = target.selectionStart ?? val.length; + const textBeforeCursor = val.substring(0, cursorPos); + const triggerIndex = textBeforeCursor.lastIndexOf(this.config.mentionTrigger); + + if (triggerIndex !== -1) { + const charBefore = triggerIndex > 0 ? textBeforeCursor[triggerIndex - 1] : ' '; + if (charBefore === ' ' || charBefore === '\n' || triggerIndex === 0) { + const query = textBeforeCursor.substring(triggerIndex + 1); + if (!query.includes(' ')) { + this.mentionQuery.set(query); + this.showPopup.set(true); + this.selectedIndex.set(0); + this.mentionSearch.emit({ query }); + return; + } + } + } + + this.showPopup.set(false); + } + + onKeydown(event: KeyboardEvent): void { + if (this.showPopup()) { + const users = this.filteredUsers(); + + switch (event.key) { + case 'ArrowDown': + event.preventDefault(); + this.selectedIndex.update((i) => (i + 1) % Math.max(users.length, 1)); + break; + case 'ArrowUp': + event.preventDefault(); + this.selectedIndex.update((i) => (i - 1 + users.length) % Math.max(users.length, 1)); + break; + case 'Enter': + event.preventDefault(); + if (users.length > 0) { + this.selectMention(users[this.selectedIndex()]); + } + break; + case 'Escape': + this.showPopup.set(false); + break; + } + return; + } + + if (event.key === 'Enter' && !event.shiftKey && !this.multiline()) { + event.preventDefault(); + this.onSubmit(); + } + } + + selectMention(user: NtfMention): void { + const el = this.inputRef()?.nativeElement; + if (!el) return; + + const cursorPos = el.selectionStart ?? this.textValue().length; + const result = insertMention(this.textValue(), cursorPos, user); + + this.textValue.set(result.text); + this.valueChange.emit(result.text); + this.showPopup.set(false); + this.mentionSelect.emit({ userId: user.id, userName: user.name }); + + setTimeout(() => { + el.focus(); + el.setSelectionRange(result.cursorPos, result.cursorPos); + }); + } + + onSubmit(): void { + const val = this.textValue().trim(); + if (val) { + this.submit.emit(val); + this.textValue.set(''); + this.valueChange.emit(''); + } + } + + onBlur(): void { + setTimeout(() => this.showPopup.set(false), 200); + } +} diff --git a/src/components/ntf-thread/index.ts b/src/components/ntf-thread/index.ts new file mode 100644 index 0000000..34a16eb --- /dev/null +++ b/src/components/ntf-thread/index.ts @@ -0,0 +1 @@ +export { NtfThreadComponent } from './ntf-thread.component'; diff --git a/src/components/ntf-thread/ntf-thread.component.html b/src/components/ntf-thread/ntf-thread.component.html new file mode 100644 index 0000000..bbaf445 --- /dev/null +++ b/src/components/ntf-thread/ntf-thread.component.html @@ -0,0 +1,70 @@ +
+ @for (comment of comments(); track comment.id) { +
+ + + + @if (replyingTo() === comment.id && allowReply()) { +
+ + +
+ } + + + @if (comment.replies?.length) { +
+
+ @for (reply of comment.replies; track reply.id) { +
+ + + + @if (replyingTo() === reply.id && allowReply()) { +
+ + +
+ } +
+ } +
+ } +
+ } + + + @if (allowReply()) { +
+ +
+ } +
diff --git a/src/components/ntf-thread/ntf-thread.component.scss b/src/components/ntf-thread/ntf-thread.component.scss new file mode 100644 index 0000000..6f1374d --- /dev/null +++ b/src/components/ntf-thread/ntf-thread.component.scss @@ -0,0 +1,66 @@ +:host { + display: block; +} + +.ntf-thread { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.ntf-thread__item { + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.ntf-thread__replies { + position: relative; + padding-left: 2.5rem; + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.ntf-thread__connector { + position: absolute; + left: 16px; + top: 0; + bottom: 0; + width: var(--ntf-thread-connector-width); + background: var(--ntf-thread-connector-color); + border-radius: 1px; +} + +.ntf-thread__reply-item { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.ntf-thread__reply-form { + display: flex; + flex-direction: column; + gap: 0.25rem; + padding-left: 2.5rem; +} + +.ntf-thread__reply-cancel { + align-self: flex-end; + padding: 0.125rem 0.5rem; + border: none; + background: transparent; + font-size: var(--font-size-xs, 0.75rem); + color: var(--ntf-item-time-color); + cursor: pointer; + transition: color var(--ntf-transition, 150ms ease-in-out); + + &:hover { + color: var(--ntf-item-title-color); + } +} + +.ntf-thread__new-comment { + padding-top: 0.5rem; + border-top: 1px solid var(--ntf-item-border); +} diff --git a/src/components/ntf-thread/ntf-thread.component.ts b/src/components/ntf-thread/ntf-thread.component.ts new file mode 100644 index 0000000..ec92092 --- /dev/null +++ b/src/components/ntf-thread/ntf-thread.component.ts @@ -0,0 +1,77 @@ +import { Component, ChangeDetectionStrategy, input, output, signal } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import type { NtfComment } from '../../types/comment.types'; +import type { NtfMention } from '../../types/notification.types'; +import type { + NtfCommentSubmitEvent, + NtfCommentEditEvent, + NtfCommentDeleteEvent, + NtfReactionEvent, + NtfReplyEvent, +} from '../../types/event.types'; +import { NtfCommentComponent } from '../ntf-comment/ntf-comment.component'; +import { NtfMentionInputComponent } from '../ntf-mention-input/ntf-mention-input.component'; +import { parseMentions } from '../../utils/mention.utils'; + +@Component({ + selector: 'ntf-thread', + standalone: true, + imports: [CommonModule, NtfCommentComponent, NtfMentionInputComponent], + templateUrl: './ntf-thread.component.html', + styleUrl: './ntf-thread.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class NtfThreadComponent { + readonly comments = input([]); + readonly currentUserId = input(''); + readonly allowReply = input(true); + readonly maxDepth = input(3); + readonly mentionUsers = input([]); + + readonly commentSubmit = output(); + readonly commentEdit = output(); + readonly commentDelete = output(); + readonly reaction = output(); + readonly replyEvent = output(); + + readonly replyingTo = signal(null); + + onReply(event: NtfReplyEvent): void { + this.replyingTo.set(event.parentId); + this.replyEvent.emit(event); + } + + onSubmitReply(content: string, parentId?: string): void { + const mentions = parseMentions(content); + this.commentSubmit.emit({ + content, + mentions, + parentId, + }); + this.replyingTo.set(null); + } + + onSubmitNew(content: string): void { + const mentions = parseMentions(content); + this.commentSubmit.emit({ + content, + mentions, + }); + } + + onEdit(event: NtfCommentEditEvent): void { + this.commentEdit.emit(event); + } + + onDelete(event: NtfCommentDeleteEvent): void { + this.commentDelete.emit(event); + } + + onReact(event: NtfReactionEvent): void { + this.reaction.emit(event); + } + + cancelReply(): void { + this.replyingTo.set(null); + } +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..2d1bd74 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,53 @@ +/** + * Notification Elements UI + * Main library entry point + * + * Angular components for notification center, real-time feeds, inbox, + * comment threads, and @mentions powered by @sda/base-ui + * + * @example + * ```typescript + * import { Component, signal } from '@angular/core'; + * import { + * NtfBellComponent, NtfFeedComponent, NtfCenterComponent, + * NtfItemDefDirective, type NtfNotification, + * } from '@sda/notification-elements-ui'; + * + * @Component({ + * standalone: true, + * imports: [NtfBellComponent, NtfFeedComponent, NtfItemDefDirective], + * template: ` + * + * + * + *
{{ notification.title }}
+ *
+ *
+ * ` + * }) + * export class AppComponent { + * notifications = signal([...]); + * unreadCount = computed(() => this.notifications().filter(n => n.status === 'unread').length); + * } + * ``` + */ + +// Types +export * from './types'; + +// Utils +export * from './utils'; + +// Providers +export * from './providers'; + +// Services +export * from './services'; + +// Components +export * from './components'; diff --git a/src/providers/index.ts b/src/providers/index.ts new file mode 100644 index 0000000..b575175 --- /dev/null +++ b/src/providers/index.ts @@ -0,0 +1 @@ +export * from './notification-config.provider'; diff --git a/src/providers/notification-config.provider.ts b/src/providers/notification-config.provider.ts new file mode 100644 index 0000000..a8ae835 --- /dev/null +++ b/src/providers/notification-config.provider.ts @@ -0,0 +1,27 @@ +import { InjectionToken, makeEnvironmentProviders, EnvironmentProviders } from '@angular/core'; +import type { NtfConfig } from '../types/config.types'; + +export const DEFAULT_NOTIFICATION_CONFIG: NtfConfig = { + maxVisible: 50, + groupBy: 'date', + autoMarkReadDelay: 3000, + showTimestamps: true, + relativeTime: true, + allowArchive: true, + allowMarkAllRead: true, + mentionTrigger: '@', + mentionDebounce: 300, + commentMaxLength: 2000, + locale: 'en-US', +}; + +export const NOTIFICATION_CONFIG = new InjectionToken('NOTIFICATION_CONFIG', { + providedIn: 'root', + factory: () => DEFAULT_NOTIFICATION_CONFIG, +}); + +export function provideNotificationConfig(config: Partial = {}): EnvironmentProviders { + return makeEnvironmentProviders([ + { provide: NOTIFICATION_CONFIG, useValue: { ...DEFAULT_NOTIFICATION_CONFIG, ...config } }, + ]); +} diff --git a/src/services/index.ts b/src/services/index.ts new file mode 100644 index 0000000..7384f63 --- /dev/null +++ b/src/services/index.ts @@ -0,0 +1,3 @@ +export * from './notification.service'; +export * from './notification-feed.service'; +export * from './mention.service'; diff --git a/src/services/mention.service.ts b/src/services/mention.service.ts new file mode 100644 index 0000000..2e82374 --- /dev/null +++ b/src/services/mention.service.ts @@ -0,0 +1,26 @@ +import { Injectable, signal, computed } from '@angular/core'; +import type { NtfMention } from '../types/notification.types'; + +@Injectable() +export class MentionService { + readonly users = signal([]); + readonly query = signal(''); + + readonly searchResults = computed(() => { + const q = this.query().toLowerCase(); + if (!q) return []; + return this.users().filter((u) => u.name.toLowerCase().includes(q)); + }); + + setUsers(users: NtfMention[]): void { + this.users.set(users); + } + + search(query: string): void { + this.query.set(query); + } + + clear(): void { + this.query.set(''); + } +} diff --git a/src/services/notification-feed.service.ts b/src/services/notification-feed.service.ts new file mode 100644 index 0000000..da90dcd --- /dev/null +++ b/src/services/notification-feed.service.ts @@ -0,0 +1,46 @@ +import { Injectable, signal } from '@angular/core'; +import type { NtfNotification } from '../types/notification.types'; + +@Injectable() +export class NotificationFeedService { + readonly feedItems = signal([]); + readonly loading = signal(false); + readonly hasMore = signal(true); + + private allItems: NtfNotification[] = []; + private pageSize = 20; + private currentPage = 0; + + setItems(items: NtfNotification[]): void { + this.allItems = items; + this.currentPage = 0; + this.feedItems.set(items.slice(0, this.pageSize)); + this.hasMore.set(items.length > this.pageSize); + } + + addToFeed(notification: NtfNotification): void { + this.allItems = [notification, ...this.allItems]; + this.feedItems.update((items) => [notification, ...items]); + } + + loadMore(): void { + if (this.loading() || !this.hasMore()) return; + + this.loading.set(true); + this.currentPage++; + + const start = this.currentPage * this.pageSize; + const end = start + this.pageSize; + const newItems = this.allItems.slice(start, end); + + this.feedItems.update((items) => [...items, ...newItems]); + this.hasMore.set(end < this.allItems.length); + this.loading.set(false); + } + + refresh(): void { + this.currentPage = 0; + this.feedItems.set(this.allItems.slice(0, this.pageSize)); + this.hasMore.set(this.allItems.length > this.pageSize); + } +} diff --git a/src/services/notification.service.ts b/src/services/notification.service.ts new file mode 100644 index 0000000..dd892c1 --- /dev/null +++ b/src/services/notification.service.ts @@ -0,0 +1,56 @@ +import { Injectable, inject, signal, computed } from '@angular/core'; +import { NOTIFICATION_CONFIG } from '../providers/notification-config.provider'; +import type { NtfNotification, NtfGroup } from '../types/notification.types'; +import { groupByDate } from '../utils/time.utils'; + +@Injectable() +export class NotificationService { + private config = inject(NOTIFICATION_CONFIG); + + readonly notifications = signal([]); + + readonly unreadCount = computed(() => + this.notifications().filter((n) => n.status === 'unread').length, + ); + + readonly grouped = computed(() => { + const items = this.notifications().filter((n) => n.status !== 'archived'); + const dateGroups = groupByDate(items); + + return dateGroups.map((g) => ({ + id: g.label.toLowerCase().replace(/\s+/g, '-'), + label: g.label, + notifications: g.items, + })); + }); + + add(notification: NtfNotification): void { + this.notifications.update((list) => [notification, ...list]); + } + + markRead(notificationId: string): void { + this.notifications.update((list) => + list.map((n) => (n.id === notificationId ? { ...n, status: 'read' as const } : n)), + ); + } + + markAllRead(): void { + this.notifications.update((list) => + list.map((n) => (n.status === 'unread' ? { ...n, status: 'read' as const } : n)), + ); + } + + archive(notificationId: string): void { + this.notifications.update((list) => + list.map((n) => (n.id === notificationId ? { ...n, status: 'archived' as const } : n)), + ); + } + + remove(notificationId: string): void { + this.notifications.update((list) => list.filter((n) => n.id !== notificationId)); + } + + clear(): void { + this.notifications.set([]); + } +} diff --git a/src/styles/_index.scss b/src/styles/_index.scss new file mode 100644 index 0000000..ec2276a --- /dev/null +++ b/src/styles/_index.scss @@ -0,0 +1,2 @@ +@forward 'tokens'; +@forward 'mixins'; diff --git a/src/styles/_mixins.scss b/src/styles/_mixins.scss new file mode 100644 index 0000000..8a48676 --- /dev/null +++ b/src/styles/_mixins.scss @@ -0,0 +1,118 @@ +@mixin ntf-container { + display: block; + position: relative; + width: 100%; + font-family: var(--ntf-font-family, inherit); +} + +@mixin ntf-flex-center { + display: flex; + align-items: center; +} + +@mixin ntf-flex-between { + display: flex; + align-items: center; + justify-content: space-between; +} + +@mixin ntf-truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +@mixin ntf-line-clamp($lines: 2) { + display: -webkit-box; + -webkit-line-clamp: $lines; + -webkit-box-orient: vertical; + overflow: hidden; +} + +@mixin ntf-item-base { + background: var(--ntf-item-bg); + border-bottom: 1px solid var(--ntf-item-border); + padding: var(--ntf-item-padding); + transition: + background-color var(--ntf-transition), + border-color var(--ntf-transition); + cursor: pointer; + + &:hover { + background: var(--ntf-item-hover-bg); + } +} + +@mixin ntf-comment-base { + background: var(--ntf-comment-bg); + border: 1px solid var(--ntf-comment-border); + border-radius: var(--ntf-comment-radius); + padding: var(--ntf-comment-padding); + transition: + background-color var(--ntf-transition), + border-color var(--ntf-transition); +} + +@mixin ntf-avatar($size: 36px) { + width: $size; + height: $size; + border-radius: 50%; + object-fit: cover; + flex-shrink: 0; +} + +@mixin ntf-badge { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: var(--ntf-badge-size); + height: var(--ntf-badge-size); + padding: 0 6px; + border-radius: 999px; + background: var(--ntf-badge-bg); + color: var(--ntf-badge-color); + font-size: 0.6875rem; + font-weight: 600; + line-height: 1; +} + +@mixin ntf-scrollbar($width: 4px) { + &::-webkit-scrollbar { + width: $width; + height: $width; + } + + &::-webkit-scrollbar-track { + background: transparent; + } + + &::-webkit-scrollbar-thumb { + background: var(--ntf-item-time-color); + border-radius: $width; + opacity: 0.4; + + &:hover { + background: var(--ntf-item-body-color); + } + } + + scrollbar-width: thin; + scrollbar-color: var(--ntf-item-time-color) transparent; +} + +@mixin ntf-hover-lift { + transition: + box-shadow 200ms var(--ntf-ease-smooth), + transform 250ms var(--ntf-ease-spring); + + &:hover { + transform: translateY(-1px); + } +} + +@mixin ntf-popup { + background: var(--ntf-mention-popup-bg); + border: 1px solid var(--ntf-feed-border); + border-radius: var(--ntf-mention-popup-radius); + box-shadow: var(--ntf-mention-popup-shadow); +} diff --git a/src/styles/_tokens.scss b/src/styles/_tokens.scss new file mode 100644 index 0000000..3dd32cc --- /dev/null +++ b/src/styles/_tokens.scss @@ -0,0 +1,143 @@ +:root { + // Bell + --ntf-bell-size: 40px; + --ntf-bell-color: var(--color-text-secondary, #6b7280); + --ntf-bell-hover-bg: var(--color-bg-hover, #f3f4f6); + --ntf-badge-bg: var(--color-error-500, #ef4444); + --ntf-badge-color: #ffffff; + --ntf-badge-size: 20px; + + // Feed / Center + --ntf-feed-bg: var(--color-bg-primary, #ffffff); + --ntf-feed-border: var(--color-border-primary, #e5e7eb); + --ntf-feed-radius: var(--radius-container-lg, 1rem); + --ntf-feed-shadow: 0 10px 15px rgba(0, 0, 0, 0.1), 0 4px 6px rgba(0, 0, 0, 0.05); + --ntf-feed-width: 420px; + --ntf-feed-max-height: 480px; + + // Item + --ntf-item-bg: var(--color-bg-primary, #ffffff); + --ntf-item-hover-bg: var(--color-bg-hover, #f9fafb); + --ntf-item-unread-bg: var(--color-primary-50, #eff6ff); + --ntf-item-unread-border: var(--color-primary-500, #3b82f6); + --ntf-item-padding: 1rem; + --ntf-item-gap: 0.75rem; + --ntf-item-border: var(--color-border-primary, #e5e7eb); + --ntf-item-title-color: var(--color-text-primary, #111827); + --ntf-item-title-font-size: var(--font-size-sm, 0.875rem); + --ntf-item-title-font-weight: 500; + --ntf-item-body-color: var(--color-text-secondary, #6b7280); + --ntf-item-body-font-size: var(--font-size-xs, 0.75rem); + --ntf-item-time-color: var(--color-text-muted, #9ca3af); + --ntf-item-time-font-size: var(--font-size-xs, 0.75rem); + + // Comment / Thread + --ntf-comment-bg: var(--color-bg-secondary, #f9fafb); + --ntf-comment-border: var(--color-border-primary, #e5e7eb); + --ntf-comment-radius: 0.75rem; + --ntf-comment-padding: 1rem; + --ntf-comment-author-color: var(--color-text-primary, #111827); + --ntf-comment-author-font-size: var(--font-size-sm, 0.875rem); + --ntf-comment-author-font-weight: 600; + --ntf-comment-content-color: var(--color-text-secondary, #374151); + --ntf-comment-content-font-size: var(--font-size-sm, 0.875rem); + --ntf-thread-connector-color: var(--color-border-primary, #e5e7eb); + --ntf-thread-connector-width: 2px; + + // Inbox + --ntf-inbox-item-bg: var(--color-bg-primary, #ffffff); + --ntf-inbox-item-hover-bg: var(--color-bg-hover, #f9fafb); + --ntf-inbox-unread-weight: 600; + --ntf-inbox-star-color: #eab308; + --ntf-inbox-selected-bg: var(--color-primary-50, #eff6ff); + + // Mention + --ntf-mention-bg: var(--color-primary-50, #eff6ff); + --ntf-mention-color: var(--color-primary-700, #1d4ed8); + --ntf-mention-popup-bg: var(--color-bg-primary, #ffffff); + --ntf-mention-popup-shadow: 0 10px 15px rgba(0, 0, 0, 0.1), 0 4px 6px rgba(0, 0, 0, 0.05); + --ntf-mention-popup-radius: 0.5rem; + --ntf-mention-popup-max-height: 200px; + + // Empty state + --ntf-empty-color: var(--color-text-muted, #9ca3af); + --ntf-empty-icon-size: 48px; + + // Transitions + --ntf-transition: 150ms ease-in-out; + --ntf-transition-slow: 300ms ease-in-out; + + // Easing + --ntf-ease-smooth: cubic-bezier(0.25, 0.46, 0.45, 0.94); + --ntf-ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); +} + +@media (prefers-color-scheme: dark) { + :root { + --ntf-feed-bg: var(--color-bg-primary, #111827); + --ntf-feed-border: var(--color-border-primary, #1e2536); + --ntf-feed-shadow: 0 10px 15px rgba(0, 0, 0, 0.4), 0 4px 6px rgba(0, 0, 0, 0.3); + + --ntf-item-bg: var(--color-bg-primary, #111827); + --ntf-item-hover-bg: var(--color-bg-hover, #1e2536); + --ntf-item-unread-bg: rgba(59, 130, 246, 0.1); + --ntf-item-border: var(--color-border-primary, #1e2536); + --ntf-item-title-color: var(--color-text-primary, #f9fafb); + --ntf-item-body-color: var(--color-text-secondary, #9ca3af); + --ntf-item-time-color: var(--color-text-muted, #6b7280); + + --ntf-comment-bg: var(--color-bg-secondary, #161b26); + --ntf-comment-border: var(--color-border-primary, #1e2536); + --ntf-comment-author-color: var(--color-text-primary, #f9fafb); + --ntf-comment-content-color: var(--color-text-secondary, #9ca3af); + --ntf-thread-connector-color: var(--color-border-primary, #1e2536); + + --ntf-inbox-item-bg: var(--color-bg-primary, #111827); + --ntf-inbox-item-hover-bg: var(--color-bg-hover, #1e2536); + --ntf-inbox-selected-bg: rgba(59, 130, 246, 0.1); + + --ntf-mention-bg: rgba(59, 130, 246, 0.15); + --ntf-mention-color: #93bbfd; + --ntf-mention-popup-bg: var(--color-bg-primary, #111827); + --ntf-mention-popup-shadow: 0 10px 15px rgba(0, 0, 0, 0.5), 0 4px 6px rgba(0, 0, 0, 0.3); + + --ntf-bell-color: var(--color-text-secondary, #9ca3af); + --ntf-bell-hover-bg: var(--color-bg-hover, #1e2536); + + --ntf-empty-color: var(--color-text-muted, #6b7280); + } +} + +[data-mode="dark"] { + --ntf-feed-bg: var(--color-bg-primary, #111827); + --ntf-feed-border: var(--color-border-primary, #1e2536); + --ntf-feed-shadow: 0 10px 15px rgba(0, 0, 0, 0.4), 0 4px 6px rgba(0, 0, 0, 0.3); + + --ntf-item-bg: var(--color-bg-primary, #111827); + --ntf-item-hover-bg: var(--color-bg-hover, #1e2536); + --ntf-item-unread-bg: rgba(59, 130, 246, 0.1); + --ntf-item-border: var(--color-border-primary, #1e2536); + --ntf-item-title-color: var(--color-text-primary, #f9fafb); + --ntf-item-body-color: var(--color-text-secondary, #9ca3af); + --ntf-item-time-color: var(--color-text-muted, #6b7280); + + --ntf-comment-bg: var(--color-bg-secondary, #161b26); + --ntf-comment-border: var(--color-border-primary, #1e2536); + --ntf-comment-author-color: var(--color-text-primary, #f9fafb); + --ntf-comment-content-color: var(--color-text-secondary, #9ca3af); + --ntf-thread-connector-color: var(--color-border-primary, #1e2536); + + --ntf-inbox-item-bg: var(--color-bg-primary, #111827); + --ntf-inbox-item-hover-bg: var(--color-bg-hover, #1e2536); + --ntf-inbox-selected-bg: rgba(59, 130, 246, 0.1); + + --ntf-mention-bg: rgba(59, 130, 246, 0.15); + --ntf-mention-color: #93bbfd; + --ntf-mention-popup-bg: var(--color-bg-primary, #111827); + --ntf-mention-popup-shadow: 0 10px 15px rgba(0, 0, 0, 0.5), 0 4px 6px rgba(0, 0, 0, 0.3); + + --ntf-bell-color: var(--color-text-secondary, #9ca3af); + --ntf-bell-hover-bg: var(--color-bg-hover, #1e2536); + + --ntf-empty-color: var(--color-text-muted, #6b7280); +} diff --git a/src/types/comment.types.ts b/src/types/comment.types.ts new file mode 100644 index 0000000..2b1a9bd --- /dev/null +++ b/src/types/comment.types.ts @@ -0,0 +1,28 @@ +export interface NtfReaction { + emoji: string; + count: number; + reacted: boolean; +} + +export interface NtfAttachment { + id: string; + name: string; + url: string; + type: string; + size?: number; +} + +export interface NtfComment { + id: string; + authorId: string; + authorName: string; + authorAvatar?: string; + content: string; + mentions?: string[]; + timestamp: Date | string; + editedAt?: Date | string; + reactions?: NtfReaction[]; + replies?: NtfComment[]; + parentId?: string; + attachments?: NtfAttachment[]; +} diff --git a/src/types/config.types.ts b/src/types/config.types.ts new file mode 100644 index 0000000..7fed17c --- /dev/null +++ b/src/types/config.types.ts @@ -0,0 +1,13 @@ +export interface NtfConfig { + maxVisible: number; + groupBy: 'date' | 'category' | 'priority' | 'none'; + autoMarkReadDelay: number; + showTimestamps: boolean; + relativeTime: boolean; + allowArchive: boolean; + allowMarkAllRead: boolean; + mentionTrigger: string; + mentionDebounce: number; + commentMaxLength: number; + locale: string; +} diff --git a/src/types/event.types.ts b/src/types/event.types.ts new file mode 100644 index 0000000..03ff077 --- /dev/null +++ b/src/types/event.types.ts @@ -0,0 +1,83 @@ +import type { NtfNotification, NtfAction, NtfCategory } from './notification.types'; +import type { NtfComment } from './comment.types'; +import type { NtfInboxMessage } from './inbox.types'; + +// Notification events +export interface NtfNotificationClickEvent { + notification: NtfNotification; + index: number; +} + +export interface NtfActionClickEvent { + notification: NtfNotification; + action: NtfAction; +} + +export interface NtfMarkReadEvent { + notificationId: string; +} + +export interface NtfMarkAllReadEvent { + count: number; +} + +export interface NtfArchiveEvent { + notificationId: string; +} + +export interface NtfDeleteEvent { + notificationId: string; +} + +export interface NtfFilterChangeEvent { + filter: NtfCategory | 'all'; +} + +// Comment events +export interface NtfCommentSubmitEvent { + content: string; + mentions: string[]; + parentId?: string; +} + +export interface NtfCommentEditEvent { + comment: NtfComment; + newContent: string; +} + +export interface NtfCommentDeleteEvent { + commentId: string; +} + +export interface NtfReactionEvent { + commentId: string; + emoji: string; +} + +export interface NtfReplyEvent { + parentId: string; +} + +// Mention events +export interface NtfMentionSelectEvent { + userId: string; + userName: string; +} + +export interface NtfMentionSearchEvent { + query: string; +} + +// Inbox events +export interface NtfInboxSelectEvent { + message: NtfInboxMessage; +} + +export interface NtfInboxStarEvent { + messageId: string; + starred: boolean; +} + +export interface NtfInboxArchiveEvent { + messageId: string; +} diff --git a/src/types/inbox.types.ts b/src/types/inbox.types.ts new file mode 100644 index 0000000..a367a8e --- /dev/null +++ b/src/types/inbox.types.ts @@ -0,0 +1,18 @@ +export interface NtfInboxLabel { + id: string; + name: string; + color: string; +} + +export interface NtfInboxMessage { + id: string; + from: string; + subject: string; + preview: string; + body?: string; + timestamp: Date | string; + read: boolean; + starred: boolean; + labels?: NtfInboxLabel[]; + threadId?: string; +} diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..b31b28d --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,5 @@ +export * from './notification.types'; +export * from './comment.types'; +export * from './inbox.types'; +export * from './config.types'; +export * from './event.types'; diff --git a/src/types/notification.types.ts b/src/types/notification.types.ts new file mode 100644 index 0000000..0c5b60c --- /dev/null +++ b/src/types/notification.types.ts @@ -0,0 +1,56 @@ +export type NtfPriority = 'urgent' | 'high' | 'normal' | 'low'; + +export type NtfCategory = + | 'system' + | 'mention' + | 'comment' + | 'assignment' + | 'update' + | 'alert' + | 'message' + | 'custom'; + +export type NtfStatus = 'unread' | 'read' | 'archived'; + +export interface NtfAction { + id: string; + label: string; + icon?: string; + variant?: 'primary' | 'secondary' | 'danger'; + href?: string; +} + +export interface NtfSender { + id: string; + name: string; + avatar?: string; + email?: string; +} + +export interface NtfMention { + id: string; + name: string; + avatar?: string; +} + +export interface NtfNotification { + id: string; + title: string; + body?: string; + category: NtfCategory; + priority?: NtfPriority; + status: NtfStatus; + timestamp: Date | string; + sender?: NtfSender; + avatar?: string; + icon?: string; + actions?: NtfAction[]; + groupId?: string; + metadata?: Record; +} + +export interface NtfGroup { + id: string; + label: string; + notifications: NtfNotification[]; +} diff --git a/src/utils/filter.utils.ts b/src/utils/filter.utils.ts new file mode 100644 index 0000000..1790bca --- /dev/null +++ b/src/utils/filter.utils.ts @@ -0,0 +1,68 @@ +import type { NtfNotification, NtfCategory, NtfPriority, NtfStatus } from '../types/notification.types'; + +export interface NtfFilter { + category?: NtfCategory; + priority?: NtfPriority; + status?: NtfStatus; + searchTerm?: string; +} + +export function filterNotifications( + items: NtfNotification[], + filter: NtfFilter, +): NtfNotification[] { + return items.filter((item) => { + if (filter.category && item.category !== filter.category) return false; + if (filter.priority && item.priority !== filter.priority) return false; + if (filter.status && item.status !== filter.status) return false; + if (filter.searchTerm) { + const term = filter.searchTerm.toLowerCase(); + const matchesTitle = item.title.toLowerCase().includes(term); + const matchesBody = item.body?.toLowerCase().includes(term) ?? false; + if (!matchesTitle && !matchesBody) return false; + } + return true; + }); +} + +export type NtfSortField = 'timestamp' | 'priority' | 'title'; +export type NtfSortDirection = 'asc' | 'desc'; + +const PRIORITY_ORDER: Record = { + urgent: 0, + high: 1, + normal: 2, + low: 3, +}; + +export function sortNotifications( + items: NtfNotification[], + field: NtfSortField, + direction: NtfSortDirection = 'desc', +): NtfNotification[] { + const sorted = [...items].sort((a, b) => { + let cmp = 0; + + switch (field) { + case 'timestamp': { + const tA = new Date(a.timestamp).getTime(); + const tB = new Date(b.timestamp).getTime(); + cmp = tA - tB; + break; + } + case 'priority': { + const pA = PRIORITY_ORDER[a.priority ?? 'normal'] ?? 2; + const pB = PRIORITY_ORDER[b.priority ?? 'normal'] ?? 2; + cmp = pA - pB; + break; + } + case 'title': + cmp = a.title.localeCompare(b.title); + break; + } + + return direction === 'desc' ? -cmp : cmp; + }); + + return sorted; +} diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..baeb6c6 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,3 @@ +export * from './time.utils'; +export * from './mention.utils'; +export * from './filter.utils'; diff --git a/src/utils/mention.utils.ts b/src/utils/mention.utils.ts new file mode 100644 index 0000000..db9ba4e --- /dev/null +++ b/src/utils/mention.utils.ts @@ -0,0 +1,48 @@ +import type { NtfMention } from '../types/notification.types'; + +const MENTION_REGEX = /@(\w+)/g; + +export function parseMentions(text: string): string[] { + const matches: string[] = []; + let match: RegExpExecArray | null; + const regex = new RegExp(MENTION_REGEX.source, MENTION_REGEX.flags); + + while ((match = regex.exec(text)) !== null) { + matches.push(match[1]); + } + + return matches; +} + +export function highlightMentions(text: string, mentions: NtfMention[]): string { + const mentionNames = new Set(mentions.map((m) => m.name.toLowerCase())); + + return text.replace(MENTION_REGEX, (full, name: string) => { + if (mentionNames.has(name.toLowerCase())) { + return `${full}`; + } + return full; + }); +} + +export function insertMention(text: string, cursorPos: number, mention: NtfMention): { + text: string; + cursorPos: number; +} { + const beforeCursor = text.substring(0, cursorPos); + const afterCursor = text.substring(cursorPos); + + const triggerIndex = beforeCursor.lastIndexOf('@'); + if (triggerIndex === -1) { + return { text, cursorPos }; + } + + const before = text.substring(0, triggerIndex); + const mentionText = `@${mention.name} `; + const newText = before + mentionText + afterCursor; + + return { + text: newText, + cursorPos: before.length + mentionText.length, + }; +} diff --git a/src/utils/time.utils.ts b/src/utils/time.utils.ts new file mode 100644 index 0000000..886b736 --- /dev/null +++ b/src/utils/time.utils.ts @@ -0,0 +1,63 @@ +export function formatRelativeTime(date: Date | string): string { + const now = new Date(); + const d = date instanceof Date ? date : new Date(date); + const diffMs = now.getTime() - d.getTime(); + const diffSec = Math.floor(diffMs / 1000); + const diffMin = Math.floor(diffSec / 60); + const diffHour = Math.floor(diffMin / 60); + const diffDay = Math.floor(diffHour / 24); + + if (diffSec < 60) return 'Just now'; + if (diffMin < 60) return `${diffMin}m ago`; + if (diffHour < 24) return `${diffHour}h ago`; + if (diffDay === 1) return 'Yesterday'; + if (diffDay < 7) return `${diffDay}d ago`; + + return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); +} + +export function formatTimestamp(date: Date | string, locale: string = 'en-US'): string { + const d = date instanceof Date ? date : new Date(date); + return d.toLocaleString(locale, { + month: 'short', + day: 'numeric', + year: 'numeric', + hour: 'numeric', + minute: '2-digit', + }); +} + +export type DateGroup = 'Today' | 'Yesterday' | 'This Week' | 'Earlier'; + +export function getDateGroup(date: Date | string): DateGroup { + const now = new Date(); + const d = date instanceof Date ? date : new Date(date); + + const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); + const yesterday = new Date(today.getTime() - 86400000); + const weekAgo = new Date(today.getTime() - 7 * 86400000); + + if (d >= today) return 'Today'; + if (d >= yesterday) return 'Yesterday'; + if (d >= weekAgo) return 'This Week'; + return 'Earlier'; +} + +export function groupByDate( + items: T[], +): { label: DateGroup; items: T[] }[] { + const groups = new Map(); + const order: DateGroup[] = ['Today', 'Yesterday', 'This Week', 'Earlier']; + + for (const item of items) { + const group = getDateGroup(item.timestamp); + if (!groups.has(group)) { + groups.set(group, []); + } + groups.get(group)!.push(item); + } + + return order + .filter((label) => groups.has(label)) + .map((label) => ({ label, items: groups.get(label)! })); +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..dddab58 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "outDir": "./dist/out-tsc", + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "esModuleInterop": true, + "sourceMap": true, + "declaration": true, + "experimentalDecorators": true, + "moduleResolution": "bundler", + "importHelpers": true, + "target": "ES2022", + "module": "ES2022", + "lib": ["ES2022", "dom"], + "useDefineForClassFields": false + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +}