Merge pull request #477 from danditomaso/refactor/use-deno
Switch from Bun to Deno for dev environment
This commit is contained in:
@@ -16,14 +16,16 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup bun
|
- name: Setup Deno
|
||||||
uses: oven-sh/setup-bun@v2
|
uses: denoland/setup-deno@v2
|
||||||
|
with:
|
||||||
|
deno-version: v2.x
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: bun install
|
run: deno install
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: bun run test:run
|
run: deno task test
|
||||||
|
|
||||||
- name: Build Package
|
- name: Build Package
|
||||||
run: bun run build
|
run: deno task build
|
||||||
|
|||||||
@@ -15,20 +15,22 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup bun
|
- name: Setup Deno
|
||||||
uses: oven-sh/setup-bun@v2
|
uses: denoland/setup-deno@v2
|
||||||
|
with:
|
||||||
|
deno-version: v2.x
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: bun install
|
run: deno install
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: bun run test:run
|
run: deno task test
|
||||||
|
|
||||||
- name: Build Package
|
- name: Build Package
|
||||||
run: bun run build
|
run: deno task build
|
||||||
|
|
||||||
- name: Package Output
|
- name: Package Output
|
||||||
run: bun run package
|
run: deno task package
|
||||||
|
|
||||||
- name: Archive compressed build
|
- name: Archive compressed build
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
|||||||
@@ -9,20 +9,22 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup bun
|
- name: Setup Deno
|
||||||
uses: oven-sh/setup-bun@v2
|
uses: denoland/setup-deno@v2
|
||||||
|
with:
|
||||||
|
deno-version: v2.x
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: bun install
|
run: deno install
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: bun run test:run
|
run: deno task test
|
||||||
|
|
||||||
- name: Build Package
|
- name: Build Package
|
||||||
run: bun run build
|
run: deno task build
|
||||||
|
|
||||||
- name: Compress build
|
- name: Compress build
|
||||||
run: bun run package
|
run: deno task package
|
||||||
|
|
||||||
- name: Archive compressed build
|
- name: Archive compressed build
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
|||||||
@@ -15,20 +15,22 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup bun
|
- name: Setup Deno
|
||||||
uses: oven-sh/setup-bun@v2
|
uses: denoland/setup-deno@v2
|
||||||
|
with:
|
||||||
|
deno-version: v2.x
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: bun install
|
run: deno install
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: bun run test:run
|
run: deno task test
|
||||||
|
|
||||||
- name: Build Package
|
- name: Build Package
|
||||||
run: bun run build
|
run: deno task build
|
||||||
|
|
||||||
- name: Package Output
|
- name: Package Output
|
||||||
run: bun run package
|
run: deno task package
|
||||||
|
|
||||||
- name: Archive compressed build
|
- name: Archive compressed build
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
|||||||
+2
-1
@@ -2,4 +2,5 @@ dist
|
|||||||
node_modules
|
node_modules
|
||||||
stats.html
|
stats.html
|
||||||
.vercel
|
.vercel
|
||||||
dev-dist
|
.vite/deps
|
||||||
|
dev-dist
|
||||||
|
|||||||
Vendored
+4
-5
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"editor.defaultFormatter": "biomejs.biome",
|
"deno.enable": true,
|
||||||
"editor.codeActionsOnSave": {
|
"deno.suggest.imports.autoDiscover": true,
|
||||||
"quickfix.biome": "explicit"
|
"editor.formatOnSave": true,
|
||||||
},
|
"editor.defaultFormatter": "denoland.vscode-deno"
|
||||||
"editor.formatOnSave": true
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Official [Meshtastic](https://meshtastic.org) web interface, that can be hosted or served from a node
|
Official [Meshtastic](https://meshtastic.org) web interface, that can be hosted
|
||||||
|
or served from a node
|
||||||
|
|
||||||
**[Hosted version](https://client.meshtastic.org)**
|
**[Hosted version](https://client.meshtastic.org)**
|
||||||
|
|
||||||
@@ -19,7 +20,8 @@ Official [Meshtastic](https://meshtastic.org) web interface, that can be hosted
|
|||||||
|
|
||||||
## Progress Web App Support (PWA)
|
## Progress Web App Support (PWA)
|
||||||
|
|
||||||
Meshtastic Web Client now includes Progressive Web App (PWA) functionality, allowing users to:
|
Meshtastic Web Client now includes Progressive Web App (PWA) functionality,
|
||||||
|
allowing users to:
|
||||||
|
|
||||||
- Install the app on desktop and mobile devices
|
- Install the app on desktop and mobile devices
|
||||||
- Access the interface offline
|
- Access the interface offline
|
||||||
@@ -35,8 +37,10 @@ PWA functionality works with both the hosted version and self-hosted instances.
|
|||||||
|
|
||||||
## Self-host
|
## Self-host
|
||||||
|
|
||||||
The client can be self hosted using the precompiled container images with an OCI compatible runtime such as [Docker](https://www.docker.com/) or [Podman](https://podman.io/).
|
The client can be self hosted using the precompiled container images with an OCI
|
||||||
The base image used is [Nginx 1.27](https://hub.docker.com/_/nginx)
|
compatible runtime such as [Docker](https://www.docker.com/) or
|
||||||
|
[Podman](https://podman.io/). The base image used is
|
||||||
|
[Nginx 1.27](https://hub.docker.com/_/nginx)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# With Docker
|
# With Docker
|
||||||
@@ -46,9 +50,11 @@ docker run -d -p 8080:8080 --restart always --name Meshtastic-Web ghcr.io/meshta
|
|||||||
podman run -d -p 8080:8080 --restart always --name Meshtastic-Web ghcr.io/meshtastic/web
|
podman run -d -p 8080:8080 --restart always --name Meshtastic-Web ghcr.io/meshtastic/web
|
||||||
```
|
```
|
||||||
|
|
||||||
## Nightly releases
|
## Nightly releases
|
||||||
|
|
||||||
Our nightly releases provide the latest development builds with cutting-edge features and fixes. These builds are automatically generated from the latest main branch every night and are available for testing and early adoption.
|
Our nightly releases provide the latest development builds with cutting-edge
|
||||||
|
features and fixes. These builds are automatically generated from the latest
|
||||||
|
main branch every night and are available for testing and early adoption.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# With Docker
|
# With Docker
|
||||||
@@ -57,38 +63,102 @@ docker run -d -p 8080:8080 --restart always --name Meshtastic-Web ghcr.io/meshta
|
|||||||
podman run -d -p 8080:8080 --restart always --name Meshtastic-Web ghcr.io/meshtastic/web:nightly
|
podman run -d -p 8080:8080 --restart always --name Meshtastic-Web ghcr.io/meshtastic/web:nightly
|
||||||
```
|
```
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> - Nightly builds represent the latest development state and may contain breaking changes
|
>
|
||||||
> - These builds undergo automated testing but may be less stable than tagged release versions
|
> - Nightly builds represent the latest development state and may contain
|
||||||
> - Not recommended for production environments unless you are actively testing new features
|
> breaking changes
|
||||||
|
> - These builds undergo automated testing but may be less stable than tagged
|
||||||
|
> release versions
|
||||||
|
> - Not recommended for production environments unless you are actively testing
|
||||||
|
> new features
|
||||||
> - No guarantee of backward compatibility between nightly builds
|
> - No guarantee of backward compatibility between nightly builds
|
||||||
|
|
||||||
### Version Information
|
### Version Information
|
||||||
|
|
||||||
Each nightly build is tagged with:
|
Each nightly build is tagged with:
|
||||||
|
|
||||||
- The nightly tag for the latest build
|
- The nightly tag for the latest build
|
||||||
- A specific SHA for build reproducibility
|
- A specific SHA for build reproducibility
|
||||||
|
|
||||||
### Feedback
|
### Feedback
|
||||||
If you encounter any issues with nightly builds, please report them in our [issues tracker](https://github.com/meshtastic/web/issues). Your feedback helps improve the stability of future releases
|
|
||||||
|
If you encounter any issues with nightly builds, please report them in our
|
||||||
|
[issues tracker](https://github.com/meshtastic/web/issues). Your feedback helps
|
||||||
|
improve the stability of future releases
|
||||||
|
|
||||||
## Development & Building
|
## Development & Building
|
||||||
You'll need to download the package manager used with this repo. You can install it by visiting [Bun.sh](https://bun.sh/) and following the installation instructions.
|
|
||||||
|
|
||||||
### Debugging
|
You'll need to download the package manager used with this repo. You can install
|
||||||
|
it by visiting [deno.com](https://deno.com/) and following the installation
|
||||||
|
instructions listed on the home page.
|
||||||
|
|
||||||
|
### Development
|
||||||
|
|
||||||
|
Install the dependencies.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
deno i
|
||||||
|
```
|
||||||
|
|
||||||
|
Start the development server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
deno task dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Building and Packaging
|
||||||
|
|
||||||
|
Build the project:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
deno task build
|
||||||
|
```
|
||||||
|
|
||||||
|
GZip the output:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
deno task package
|
||||||
|
```
|
||||||
|
|
||||||
|
### Why Deno?
|
||||||
|
|
||||||
|
Meshtastic Web uses Deno as its development platform for several compelling
|
||||||
|
reasons:
|
||||||
|
|
||||||
|
- **Built-in Security**: Deno's security-first approach requires explicit
|
||||||
|
permissions for file, network, and environment access, reducing vulnerability
|
||||||
|
risks.
|
||||||
|
- **TypeScript Support**: Native TypeScript support without additional
|
||||||
|
configuration, enhancing code quality and developer experience.
|
||||||
|
- **Modern JavaScript**: First-class support for ESM imports, top-level await,
|
||||||
|
and other modern JavaScript features.
|
||||||
|
- **Simplified Tooling**: Built-in formatter, linter, test runner, and bundler
|
||||||
|
eliminate the need for multiple third-party tools.
|
||||||
|
- **Reproducible Builds**: Lockfile ensures consistent builds across all
|
||||||
|
environments.
|
||||||
|
- **Web Standard APIs**: Uses browser-compatible APIs, making code more portable
|
||||||
|
between server and client environments.
|
||||||
|
|
||||||
|
### Debugging
|
||||||
|
|
||||||
#### Debugging with React Scan
|
#### Debugging with React Scan
|
||||||
Meshtastic Web Client has included the library [React Scan](https://github.com/aidenybai/react-scan) to help you identify and resolve render performance issues during development.
|
|
||||||
|
|
||||||
React's comparison-by-reference approach to props makes it easy to inadvertently cause unnecessary re-renders, especially with:
|
Meshtastic Web Client has included the library
|
||||||
|
[React Scan](https://github.com/aidenybai/react-scan) to help you identify and
|
||||||
|
resolve render performance issues during development.
|
||||||
|
|
||||||
|
React's comparison-by-reference approach to props makes it easy to inadvertently
|
||||||
|
cause unnecessary re-renders, especially with:
|
||||||
|
|
||||||
- Inline function callbacks (`onClick={() => handleClick()}`)
|
- Inline function callbacks (`onClick={() => handleClick()}`)
|
||||||
- Object literals (`style={{ color: "purple" }}`)
|
- Object literals (`style={{ color: "purple" }}`)
|
||||||
- Array literals (`items={[1, 2, 3]}`)
|
- Array literals (`items={[1, 2, 3]}`)
|
||||||
|
|
||||||
These are recreated on every render, causing child components to re-render even when nothing has actually changed.
|
These are recreated on every render, causing child components to re-render even
|
||||||
|
when nothing has actually changed.
|
||||||
|
|
||||||
Unlike React DevTools, React Scan specifically focuses on performance optimization by:
|
Unlike React DevTools, React Scan specifically focuses on performance
|
||||||
|
optimization by:
|
||||||
|
|
||||||
- Clearly distinguishing between necessary and unnecessary renders
|
- Clearly distinguishing between necessary and unnecessary renders
|
||||||
- Providing render counts for components
|
- Providing render counts for components
|
||||||
@@ -96,10 +166,11 @@ Unlike React DevTools, React Scan specifically focuses on performance optimizati
|
|||||||
- Offering a dedicated performance debugging experience
|
- Offering a dedicated performance debugging experience
|
||||||
|
|
||||||
#### Usage
|
#### Usage
|
||||||
|
|
||||||
When experiencing slow renders, run:
|
When experiencing slow renders, run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun run dev:scan
|
deno task dev:scan
|
||||||
```
|
```
|
||||||
|
|
||||||
This will allow you to discover the following about your components and pages:
|
This will allow you to discover the following about your components and pages:
|
||||||
@@ -109,32 +180,5 @@ This will allow you to discover the following about your components and pages:
|
|||||||
- Expensive hook operations
|
- Expensive hook operations
|
||||||
- Props that change reference on every render
|
- Props that change reference on every render
|
||||||
|
|
||||||
Use these insights to apply targeted optimizations like `React.memo()`, `useCallback()`, or `useMemo()` where they'll have the most impact.
|
Use these insights to apply targeted optimizations like `React.memo()`,
|
||||||
|
`useCallback()`, or `useMemo()` where they'll have the most impact.
|
||||||
### Building and Packaging
|
|
||||||
|
|
||||||
Build the project:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun run build
|
|
||||||
```
|
|
||||||
|
|
||||||
GZip the output:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun run package
|
|
||||||
```
|
|
||||||
|
|
||||||
### Development
|
|
||||||
|
|
||||||
Install the dependencies.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun i
|
|
||||||
```
|
|
||||||
|
|
||||||
Start the development server:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun run dev
|
|
||||||
```
|
|
||||||
|
|||||||
-27
@@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://biomejs.dev/schemas/1.7.3/schema.json",
|
|
||||||
"organizeImports": {
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
"files": {
|
|
||||||
"ignoreUnknown": true,
|
|
||||||
"ignore": ["vercel.json"]
|
|
||||||
},
|
|
||||||
"vcs": {
|
|
||||||
"enabled": true,
|
|
||||||
"clientKind": "git",
|
|
||||||
"useIgnoreFile": true,
|
|
||||||
"defaultBranch": "master"
|
|
||||||
},
|
|
||||||
"formatter": {
|
|
||||||
"enabled": true,
|
|
||||||
"indentStyle": "space",
|
|
||||||
"indentWidth": 2
|
|
||||||
},
|
|
||||||
"linter": {
|
|
||||||
"enabled": true,
|
|
||||||
"rules": {
|
|
||||||
"recommended": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -47,7 +47,6 @@
|
|||||||
"zustand": "5.0.3",
|
"zustand": "5.0.3",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^1.9.4",
|
|
||||||
"@tailwindcss/postcss": "^4.0.9",
|
"@tailwindcss/postcss": "^4.0.9",
|
||||||
"@testing-library/react": "^16.2.0",
|
"@testing-library/react": "^16.2.0",
|
||||||
"@types/chrome": "^0.0.307",
|
"@types/chrome": "^0.0.307",
|
||||||
@@ -262,24 +261,6 @@
|
|||||||
|
|
||||||
"@babel/types": ["@babel/types@7.26.9", "", { "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" } }, "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw=="],
|
"@babel/types": ["@babel/types@7.26.9", "", { "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" } }, "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw=="],
|
||||||
|
|
||||||
"@biomejs/biome": ["@biomejs/biome@1.9.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "1.9.4", "@biomejs/cli-darwin-x64": "1.9.4", "@biomejs/cli-linux-arm64": "1.9.4", "@biomejs/cli-linux-arm64-musl": "1.9.4", "@biomejs/cli-linux-x64": "1.9.4", "@biomejs/cli-linux-x64-musl": "1.9.4", "@biomejs/cli-win32-arm64": "1.9.4", "@biomejs/cli-win32-x64": "1.9.4" }, "bin": { "biome": "bin/biome" } }, "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog=="],
|
|
||||||
|
|
||||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@1.9.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw=="],
|
|
||||||
|
|
||||||
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@1.9.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg=="],
|
|
||||||
|
|
||||||
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g=="],
|
|
||||||
|
|
||||||
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA=="],
|
|
||||||
|
|
||||||
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg=="],
|
|
||||||
|
|
||||||
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg=="],
|
|
||||||
|
|
||||||
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@1.9.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg=="],
|
|
||||||
|
|
||||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="],
|
|
||||||
|
|
||||||
"@bufbuild/protobuf": ["@bufbuild/protobuf@2.2.3", "", {}, "sha512-tFQoXHJdkEOSwj5tRIZSPNUuXK3RaR7T1nUrPgbYX1pUbvqqaaZAsfo+NXBPsz5rZMSKVFrgK1WL8Q/MSLvprg=="],
|
"@bufbuild/protobuf": ["@bufbuild/protobuf@2.2.3", "", {}, "sha512-tFQoXHJdkEOSwj5tRIZSPNUuXK3RaR7T1nUrPgbYX1pUbvqqaaZAsfo+NXBPsz5rZMSKVFrgK1WL8Q/MSLvprg=="],
|
||||||
|
|
||||||
"@clack/core": ["@clack/core@0.3.5", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-5cfhQNH+1VQ2xLQlmzXMqUoiaH0lRBq9/CLW9lTyMbuKLC3+xEK01tHVvyut++mLOn5urSHmkm6I0Lg9MaJSTQ=="],
|
"@clack/core": ["@clack/core@0.3.5", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-5cfhQNH+1VQ2xLQlmzXMqUoiaH0lRBq9/CLW9lTyMbuKLC3+xEK01tHVvyut++mLOn5urSHmkm6I0Lg9MaJSTQ=="],
|
||||||
@@ -362,7 +343,7 @@
|
|||||||
|
|
||||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
|
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
|
||||||
|
|
||||||
"@jsr/meshtastic__core": ["@jsr/meshtastic__core@2.6.0-1", "https://npm.jsr.io/~/11/@jsr/meshtastic__core/2.6.0-1.tgz", { "dependencies": { "@bufbuild/protobuf": "^2.2.3", "@jsr/meshtastic__protobufs": "^2.6.0", "crc": "^4.3.2", "ste-simple-events": "^3.0.11", "tslog": "^4.9.3" } }, "sha512-fVlFVImuthS5oIH3J5Glue+0OizGmhtvzTWEmAJwdgmYyUn7mfA2iEvoUHFv3EiEDq6BJ9SjkLzOSqMTRPSNuw=="],
|
"@jsr/meshtastic__core": ["@jsr/meshtastic__core@2.6.0", "https://npm.jsr.io/~/11/@jsr/meshtastic__core/2.6.0.tgz", { "dependencies": { "@bufbuild/protobuf": "^2.2.3", "@jsr/meshtastic__protobufs": "^2.6.0", "crc": "^4.3.2", "ste-simple-events": "^3.0.11", "tslog": "^4.9.3" } }, "sha512-+Ik6gzZnfi5sW+WC06bRayA6KGF2NI+zi3bqKbvA8mGDNSOPgsFhA4VZ79DKY4bSflTW170MRIUeyYo0IWQQuw=="],
|
||||||
|
|
||||||
"@jsr/meshtastic__protobufs": ["@jsr/meshtastic__protobufs@2.6.0", "https://npm.jsr.io/~/11/@jsr/meshtastic__protobufs/2.6.0.tgz", { "dependencies": { "@bufbuild/protobuf": "^2.2.3" } }, "sha512-CGlgBdzAuQCZuGPrnzP8zU+EcLlmyYeeMbqFHuJ834cYfArWXDjDh1UYaPo2rI03LTjqa3MeWpfqDlzBR8kIMg=="],
|
"@jsr/meshtastic__protobufs": ["@jsr/meshtastic__protobufs@2.6.0", "https://npm.jsr.io/~/11/@jsr/meshtastic__protobufs/2.6.0.tgz", { "dependencies": { "@bufbuild/protobuf": "^2.2.3" } }, "sha512-CGlgBdzAuQCZuGPrnzP8zU+EcLlmyYeeMbqFHuJ834cYfArWXDjDh1UYaPo2rI03LTjqa3MeWpfqDlzBR8kIMg=="],
|
||||||
|
|
||||||
@@ -370,8 +351,6 @@
|
|||||||
|
|
||||||
"@mapbox/jsonlint-lines-primitives": ["@mapbox/jsonlint-lines-primitives@2.0.2", "", {}, "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ=="],
|
"@mapbox/jsonlint-lines-primitives": ["@mapbox/jsonlint-lines-primitives@2.0.2", "", {}, "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ=="],
|
||||||
|
|
||||||
"@mapbox/mapbox-gl-supported": ["@mapbox/mapbox-gl-supported@3.0.0", "", {}, "sha512-2XghOwu16ZwPJLOFVuIOaLbN0iKMn867evzXFyf0P22dqugezfJwLmdanAgU25ITvz1TvOfVP4jsDImlDJzcWg=="],
|
|
||||||
|
|
||||||
"@mapbox/point-geometry": ["@mapbox/point-geometry@0.1.0", "", {}, "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ=="],
|
"@mapbox/point-geometry": ["@mapbox/point-geometry@0.1.0", "", {}, "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ=="],
|
||||||
|
|
||||||
"@mapbox/tiny-sdf": ["@mapbox/tiny-sdf@2.0.6", "", {}, "sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA=="],
|
"@mapbox/tiny-sdf": ["@mapbox/tiny-sdf@2.0.6", "", {}, "sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA=="],
|
||||||
@@ -388,9 +367,9 @@
|
|||||||
|
|
||||||
"@meshtastic/js": ["@jsr/meshtastic__js@2.6.0-0", "https://npm.jsr.io/~/11/@jsr/meshtastic__js/2.6.0-0.tgz", { "dependencies": { "@bufbuild/protobuf": "^2.2.3", "@jsr/meshtastic__protobufs": "^2.6.0", "crc": "^4.3.2", "ste-simple-events": "^3.0.11", "tslog": "^4.9.3" } }, "sha512-+xpZpxK6oUIVOuEs7C+LyxRr2druvc7UNNNTK9Rl8ioXj63Jz1uQXlYe2Gj0xjnRAiSQLR7QVaPef21BR/YTxA=="],
|
"@meshtastic/js": ["@jsr/meshtastic__js@2.6.0-0", "https://npm.jsr.io/~/11/@jsr/meshtastic__js/2.6.0-0.tgz", { "dependencies": { "@bufbuild/protobuf": "^2.2.3", "@jsr/meshtastic__protobufs": "^2.6.0", "crc": "^4.3.2", "ste-simple-events": "^3.0.11", "tslog": "^4.9.3" } }, "sha512-+xpZpxK6oUIVOuEs7C+LyxRr2druvc7UNNNTK9Rl8ioXj63Jz1uQXlYe2Gj0xjnRAiSQLR7QVaPef21BR/YTxA=="],
|
||||||
|
|
||||||
"@meshtastic/transport-http": ["@jsr/meshtastic__transport-http@0.1.0", "https://npm.jsr.io/~/11/@jsr/meshtastic__transport-http/0.1.0.tgz", { "dependencies": { "@jsr/meshtastic__core": "^2.6.0-1" } }, "sha512-+cICcTAowbnTz9yffUod1c2XxuPgJRw6VfXYHZgJZ3oaBXtUoUFHl9WdMdZLE0juzMRpTARZY2/bAH3brCf0rw=="],
|
"@meshtastic/transport-http": ["@jsr/meshtastic__transport-http@0.2.1", "https://npm.jsr.io/~/11/@jsr/meshtastic__transport-http/0.2.1.tgz", { "dependencies": { "@jsr/meshtastic__core": "^2.6.0" } }, "sha512-lmQKr3aIINKvtGROU4HchmSVqbZSbkIHqajowRRC8IAjsnR0zNTyxz210QyY4pFUF9hpcW3GRjwq5h/VO2JuGg=="],
|
||||||
|
|
||||||
"@meshtastic/transport-web-serial": ["@jsr/meshtastic__transport-web-serial@0.2.0", "https://npm.jsr.io/~/11/@jsr/meshtastic__transport-web-serial/0.2.0.tgz", { "dependencies": { "@jsr/meshtastic__core": "^2.6.0-0" } }, "sha512-mP/nxOj0syABh3FkG5iIolWhUMiFh/qtJtvqihxLkaRoxdabUyW62mOtfhCMBEjxgVnKg4Gy7GkaXfC/eFy19Q=="],
|
"@meshtastic/transport-web-serial": ["@jsr/meshtastic__transport-web-serial@0.2.1", "https://npm.jsr.io/~/11/@jsr/meshtastic__transport-web-serial/0.2.1.tgz", { "dependencies": { "@jsr/meshtastic__core": "^2.6.0" } }, "sha512-yumjEGLkAuJYOC3aWKvZzbQqi/LnqaKfNpVCY7Ki7oLtAshNiZrBLiwiFhN7+ZR9FfMdJThyBMqREBDRRWTO1Q=="],
|
||||||
|
|
||||||
"@noble/curves": ["@noble/curves@1.8.1", "", { "dependencies": { "@noble/hashes": "1.7.1" } }, "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ=="],
|
"@noble/curves": ["@noble/curves@1.8.1", "", { "dependencies": { "@noble/hashes": "1.7.1" } }, "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ=="],
|
||||||
|
|
||||||
@@ -500,43 +479,43 @@
|
|||||||
|
|
||||||
"@rollup/pluginutils": ["@rollup/pluginutils@5.1.4", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ=="],
|
"@rollup/pluginutils": ["@rollup/pluginutils@5.1.4", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ=="],
|
||||||
|
|
||||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.34.8", "", { "os": "android", "cpu": "arm" }, "sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw=="],
|
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.34.9", "", { "os": "android", "cpu": "arm" }, "sha512-qZdlImWXur0CFakn2BJ2znJOdqYZKiedEPEVNTBrpfPjc/YuTGcaYZcdmNFTkUj3DU0ZM/AElcM8Ybww3xVLzA=="],
|
||||||
|
|
||||||
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.34.8", "", { "os": "android", "cpu": "arm64" }, "sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q=="],
|
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.34.9", "", { "os": "android", "cpu": "arm64" }, "sha512-4KW7P53h6HtJf5Y608T1ISKvNIYLWRKMvfnG0c44M6In4DQVU58HZFEVhWINDZKp7FZps98G3gxwC1sb0wXUUg=="],
|
||||||
|
|
||||||
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.34.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q=="],
|
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.34.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ=="],
|
||||||
|
|
||||||
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.34.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw=="],
|
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.34.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q=="],
|
||||||
|
|
||||||
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.34.8", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA=="],
|
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.34.9", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-2lzjQPJbN5UnHm7bHIUKFMulGTQwdvOkouJDpPysJS+QFBGDJqcfh+CxxtG23Ik/9tEvnebQiylYoazFMAgrYw=="],
|
||||||
|
|
||||||
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.34.8", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q=="],
|
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.34.9", "", { "os": "freebsd", "cpu": "x64" }, "sha512-SLl0hi2Ah2H7xQYd6Qaiu01kFPzQ+hqvdYSoOtHYg/zCIFs6t8sV95kaoqjzjFwuYQLtOI0RZre/Ke0nPaQV+g=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.34.8", "", { "os": "linux", "cpu": "arm" }, "sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g=="],
|
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.34.9", "", { "os": "linux", "cpu": "arm" }, "sha512-88I+D3TeKItrw+Y/2ud4Tw0+3CxQ2kLgu3QvrogZ0OfkmX/DEppehus7L3TS2Q4lpB+hYyxhkQiYPJ6Mf5/dPg=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.34.8", "", { "os": "linux", "cpu": "arm" }, "sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA=="],
|
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.34.9", "", { "os": "linux", "cpu": "arm" }, "sha512-3qyfWljSFHi9zH0KgtEPG4cBXHDFhwD8kwg6xLfHQ0IWuH9crp005GfoUUh/6w9/FWGBwEHg3lxK1iHRN1MFlA=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.34.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A=="],
|
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.34.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.34.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q=="],
|
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.34.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.34.8", "", { "os": "linux", "cpu": "none" }, "sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ=="],
|
"@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.34.9", "", { "os": "linux", "cpu": "none" }, "sha512-dRAgTfDsn0TE0HI6cmo13hemKpVHOEyeciGtvlBTkpx/F65kTvShtY/EVyZEIfxFkV5JJTuQ9tP5HGBS0hfxIg=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.34.8", "", { "os": "linux", "cpu": "ppc64" }, "sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw=="],
|
"@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.34.9", "", { "os": "linux", "cpu": "ppc64" }, "sha512-PHcNOAEhkoMSQtMf+rJofwisZqaU8iQ8EaSps58f5HYll9EAY5BSErCZ8qBDMVbq88h4UxaNPlbrKqfWP8RfJA=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.34.8", "", { "os": "linux", "cpu": "none" }, "sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw=="],
|
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.34.9", "", { "os": "linux", "cpu": "none" }, "sha512-Z2i0Uy5G96KBYKjeQFKbbsB54xFOL5/y1P5wNBsbXB8yE+At3oh0DVMjQVzCJRJSfReiB2tX8T6HUFZ2k8iaKg=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.34.8", "", { "os": "linux", "cpu": "s390x" }, "sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA=="],
|
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.34.9", "", { "os": "linux", "cpu": "s390x" }, "sha512-U+5SwTMoeYXoDzJX5dhDTxRltSrIax8KWwfaaYcynuJw8mT33W7oOgz0a+AaXtGuvhzTr2tVKh5UO8GVANTxyQ=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.34.8", "", { "os": "linux", "cpu": "x64" }, "sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA=="],
|
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.34.9", "", { "os": "linux", "cpu": "x64" }, "sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.34.8", "", { "os": "linux", "cpu": "x64" }, "sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ=="],
|
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.34.9", "", { "os": "linux", "cpu": "x64" }, "sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA=="],
|
||||||
|
|
||||||
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.34.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ=="],
|
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.34.9", "", { "os": "win32", "cpu": "arm64" }, "sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q=="],
|
||||||
|
|
||||||
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.34.8", "", { "os": "win32", "cpu": "ia32" }, "sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w=="],
|
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.34.9", "", { "os": "win32", "cpu": "ia32" }, "sha512-KB48mPtaoHy1AwDNkAJfHXvHp24H0ryZog28spEs0V48l3H1fr4i37tiyHsgKZJnCmvxsbATdZGBpbmxTE3a9w=="],
|
||||||
|
|
||||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.34.8", "", { "os": "win32", "cpu": "x64" }, "sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g=="],
|
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.34.9", "", { "os": "win32", "cpu": "x64" }, "sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw=="],
|
||||||
|
|
||||||
"@surma/rollup-plugin-off-main-thread": ["@surma/rollup-plugin-off-main-thread@2.2.3", "", { "dependencies": { "ejs": "^3.1.6", "json5": "^2.2.0", "magic-string": "^0.25.0", "string.prototype.matchall": "^4.0.6" } }, "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ=="],
|
"@surma/rollup-plugin-off-main-thread": ["@surma/rollup-plugin-off-main-thread@2.2.3", "", { "dependencies": { "ejs": "^3.1.6", "json5": "^2.2.0", "magic-string": "^0.25.0", "string.prototype.matchall": "^4.0.6" } }, "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ=="],
|
||||||
|
|
||||||
@@ -834,7 +813,7 @@
|
|||||||
|
|
||||||
"@types/mapbox__vector-tile": ["@types/mapbox__vector-tile@1.3.4", "", { "dependencies": { "@types/geojson": "*", "@types/mapbox__point-geometry": "*", "@types/pbf": "*" } }, "sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg=="],
|
"@types/mapbox__vector-tile": ["@types/mapbox__vector-tile@1.3.4", "", { "dependencies": { "@types/geojson": "*", "@types/mapbox__point-geometry": "*", "@types/pbf": "*" } }, "sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg=="],
|
||||||
|
|
||||||
"@types/node": ["@types/node@22.13.7", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-oU2q+BsQldB9lYxHNp/5aZO+/Bs0Usa74Abo9mAKulz4ahQyXRHK6UVKYIN8KSC8HXwhWSi7b49JnX+txuac0w=="],
|
"@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||||
|
|
||||||
"@types/pbf": ["@types/pbf@3.0.5", "", {}, "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA=="],
|
"@types/pbf": ["@types/pbf@3.0.5", "", {}, "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA=="],
|
||||||
|
|
||||||
@@ -966,16 +945,14 @@
|
|||||||
|
|
||||||
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
||||||
|
|
||||||
"call-bound": ["call-bound@1.0.3", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "get-intrinsic": "^1.2.6" } }, "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA=="],
|
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
|
||||||
|
|
||||||
"caniuse-lite": ["caniuse-lite@1.0.30001700", "", {}, "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ=="],
|
"caniuse-lite": ["caniuse-lite@1.0.30001701", "", {}, "sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw=="],
|
||||||
|
|
||||||
"chai": ["chai@5.2.0", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw=="],
|
"chai": ["chai@5.2.0", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw=="],
|
||||||
|
|
||||||
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||||
|
|
||||||
"cheap-ruler": ["cheap-ruler@4.0.0", "", {}, "sha512-0BJa8f4t141BYKQyn9NSQt1PguFQXMXwZiA5shfoaBYHAb2fFk2RAX+tiWMoQU+Agtzt3mdt0JtuyshAXqZ+Vw=="],
|
|
||||||
|
|
||||||
"check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="],
|
"check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="],
|
||||||
|
|
||||||
"chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
|
"chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
|
||||||
@@ -1008,7 +985,7 @@
|
|||||||
|
|
||||||
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
||||||
|
|
||||||
"core-js-compat": ["core-js-compat@3.40.0", "", { "dependencies": { "browserslist": "^4.24.3" } }, "sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ=="],
|
"core-js-compat": ["core-js-compat@3.41.0", "", { "dependencies": { "browserslist": "^4.24.4" } }, "sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A=="],
|
||||||
|
|
||||||
"core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
|
"core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
|
||||||
|
|
||||||
@@ -1028,8 +1005,6 @@
|
|||||||
|
|
||||||
"crypto-random-string": ["crypto-random-string@5.0.0", "", { "dependencies": { "type-fest": "^2.12.2" } }, "sha512-KWjTXWwxFd6a94m5CdRGW/t82Tr8DoBc9dNnPCAbFI1EBweN6v1tv8y4Y1m7ndkp/nkIBRxUxAzpaBnR2k3bcQ=="],
|
"crypto-random-string": ["crypto-random-string@5.0.0", "", { "dependencies": { "type-fest": "^2.12.2" } }, "sha512-KWjTXWwxFd6a94m5CdRGW/t82Tr8DoBc9dNnPCAbFI1EBweN6v1tv8y4Y1m7ndkp/nkIBRxUxAzpaBnR2k3bcQ=="],
|
||||||
|
|
||||||
"csscolorparser": ["csscolorparser@1.0.3", "", {}, "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w=="],
|
|
||||||
|
|
||||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||||
|
|
||||||
"d3-array": ["d3-array@1.2.4", "", {}, "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="],
|
"d3-array": ["d3-array@1.2.4", "", {}, "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="],
|
||||||
@@ -1080,7 +1055,7 @@
|
|||||||
|
|
||||||
"ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="],
|
"ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="],
|
||||||
|
|
||||||
"electron-to-chromium": ["electron-to-chromium@1.5.102", "", {}, "sha512-eHhqaja8tE/FNpIiBrvBjFV/SSKpyWHLvxuR9dPTdo+3V9ppdLmFB7ZZQ98qNovcngPLYIz0oOBF9P0FfZef5Q=="],
|
"electron-to-chromium": ["electron-to-chromium@1.5.111", "", {}, "sha512-vJyJlO95wQRAw6K2ZGF/8nol7AcbCOnp8S6H91mwOOBbXoS9seDBYxCTPYAFsvXLxl3lc0jLXXe9GLxC4nXVog=="],
|
||||||
|
|
||||||
"elliptic": ["elliptic@6.6.1", "", { "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", "hash.js": "^1.0.0", "hmac-drbg": "^1.0.1", "inherits": "^2.0.4", "minimalistic-assert": "^1.0.1", "minimalistic-crypto-utils": "^1.0.1" } }, "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g=="],
|
"elliptic": ["elliptic@6.6.1", "", { "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", "hash.js": "^1.0.0", "hmac-drbg": "^1.0.1", "inherits": "^2.0.4", "minimalistic-assert": "^1.0.1", "minimalistic-crypto-utils": "^1.0.1" } }, "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g=="],
|
||||||
|
|
||||||
@@ -1116,7 +1091,7 @@
|
|||||||
|
|
||||||
"evp_bytestokey": ["evp_bytestokey@1.0.3", "", { "dependencies": { "md5.js": "^1.3.4", "safe-buffer": "^5.1.1" } }, "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA=="],
|
"evp_bytestokey": ["evp_bytestokey@1.0.3", "", { "dependencies": { "md5.js": "^1.3.4", "safe-buffer": "^5.1.1" } }, "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA=="],
|
||||||
|
|
||||||
"expect-type": ["expect-type@1.1.0", "", {}, "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA=="],
|
"expect-type": ["expect-type@1.2.0", "", {}, "sha512-80F22aiJ3GLyVnS/B3HzgR6RelZVumzj9jkL0Rhz4h0xYbNW9PjlQz5h3J/SShErbXBc295vseR4/MIbVmUbeA=="],
|
||||||
|
|
||||||
"extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="],
|
"extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="],
|
||||||
|
|
||||||
@@ -1134,7 +1109,7 @@
|
|||||||
|
|
||||||
"for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
|
"for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
|
||||||
|
|
||||||
"foreground-child": ["foreground-child@3.3.0", "", { "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" } }, "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg=="],
|
"foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
|
||||||
|
|
||||||
"fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="],
|
"fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="],
|
||||||
|
|
||||||
@@ -1158,7 +1133,7 @@
|
|||||||
|
|
||||||
"geojson-vt": ["geojson-vt@4.0.2", "", {}, "sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A=="],
|
"geojson-vt": ["geojson-vt@4.0.2", "", {}, "sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A=="],
|
||||||
|
|
||||||
"get-intrinsic": ["get-intrinsic@1.2.7", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "function-bind": "^1.1.2", "get-proto": "^1.0.0", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA=="],
|
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
||||||
|
|
||||||
"get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
|
"get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
|
||||||
|
|
||||||
@@ -1188,11 +1163,9 @@
|
|||||||
|
|
||||||
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||||
|
|
||||||
"grid-index": ["grid-index@1.1.0", "", {}, "sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA=="],
|
|
||||||
|
|
||||||
"gzipper": ["gzipper@8.2.0", "", { "dependencies": { "@gfx/zopfli": "^1.0.15", "commander": "^12.1.0", "simple-zstd": "^1.4.2" }, "bin": { "gzipper": "bin/index.js" } }, "sha512-JUvhzo8dHQWJp1eyYy1ShaPfcowsPbRc2rvwkD4LRyou/80UUz96bn+EOOYLWO4PG0Y5f3+UlUX9Gmu8RZhrtw=="],
|
"gzipper": ["gzipper@8.2.0", "", { "dependencies": { "@gfx/zopfli": "^1.0.15", "commander": "^12.1.0", "simple-zstd": "^1.4.2" }, "bin": { "gzipper": "bin/index.js" } }, "sha512-JUvhzo8dHQWJp1eyYy1ShaPfcowsPbRc2rvwkD4LRyou/80UUz96bn+EOOYLWO4PG0Y5f3+UlUX9Gmu8RZhrtw=="],
|
||||||
|
|
||||||
"happy-dom": ["happy-dom@17.1.8", "", { "dependencies": { "webidl-conversions": "^7.0.0", "whatwg-mimetype": "^3.0.0" } }, "sha512-Yxbq/FG79z1rhAf/iB6YM8wO2JB/JDQBy99RiLSs+2siEAi5J05x9eW1nnASHZJbpldjJE2KuFLsLZ+AzX/IxA=="],
|
"happy-dom": ["happy-dom@17.1.9", "", { "dependencies": { "webidl-conversions": "^7.0.0", "whatwg-mimetype": "^3.0.0" } }, "sha512-HL26ajjMVe/wr3xlzjF0sCPCiAKaZJcIRFZHmG4yKHRJp4YAkHPG5X6GfWxCeDTpOmuHhNiOyNKUoZjjnm0tjw=="],
|
||||||
|
|
||||||
"has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="],
|
"has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="],
|
||||||
|
|
||||||
@@ -1334,7 +1307,7 @@
|
|||||||
|
|
||||||
"leven": ["leven@3.1.0", "", {}, "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A=="],
|
"leven": ["leven@3.1.0", "", {}, "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A=="],
|
||||||
|
|
||||||
"libphonenumber-js": ["libphonenumber-js@1.11.20", "", {}, "sha512-/ipwAMvtSZRdiQBHqW1qxqeYiBMzncOQLVA+62MWYr7N4m7Q2jqpJ0WgT7zlOEOpyLRSqrMXidbJpC0J77AaKA=="],
|
"libphonenumber-js": ["libphonenumber-js@1.12.4", "", {}, "sha512-vLmhg7Gan7idyAKfc6pvCtNzvar4/eIzrVVk3hjNFH5+fGqyjD0gQRovdTrDl20wsmZhBtmZpcsR0tOfquwb8g=="],
|
||||||
|
|
||||||
"lightningcss": ["lightningcss@1.29.1", "", { "dependencies": { "detect-libc": "^1.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.29.1", "lightningcss-darwin-x64": "1.29.1", "lightningcss-freebsd-x64": "1.29.1", "lightningcss-linux-arm-gnueabihf": "1.29.1", "lightningcss-linux-arm64-gnu": "1.29.1", "lightningcss-linux-arm64-musl": "1.29.1", "lightningcss-linux-x64-gnu": "1.29.1", "lightningcss-linux-x64-musl": "1.29.1", "lightningcss-win32-arm64-msvc": "1.29.1", "lightningcss-win32-x64-msvc": "1.29.1" } }, "sha512-FmGoeD4S05ewj+AkhTY+D+myDvXI6eL27FjHIjoyUkO/uw7WZD1fBVs0QxeYWa7E17CUHJaYX/RUGISCtcrG4Q=="],
|
"lightningcss": ["lightningcss@1.29.1", "", { "dependencies": { "detect-libc": "^1.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.29.1", "lightningcss-darwin-x64": "1.29.1", "lightningcss-freebsd-x64": "1.29.1", "lightningcss-linux-arm-gnueabihf": "1.29.1", "lightningcss-linux-arm64-gnu": "1.29.1", "lightningcss-linux-arm64-musl": "1.29.1", "lightningcss-linux-x64-gnu": "1.29.1", "lightningcss-linux-x64-musl": "1.29.1", "lightningcss-win32-arm64-msvc": "1.29.1", "lightningcss-win32-x64-msvc": "1.29.1" } }, "sha512-FmGoeD4S05ewj+AkhTY+D+myDvXI6eL27FjHIjoyUkO/uw7WZD1fBVs0QxeYWa7E17CUHJaYX/RUGISCtcrG4Q=="],
|
||||||
|
|
||||||
@@ -1378,8 +1351,6 @@
|
|||||||
|
|
||||||
"magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
|
"magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
|
||||||
|
|
||||||
"mapbox-gl": ["mapbox-gl@3.10.0", "", { "dependencies": { "@mapbox/jsonlint-lines-primitives": "^2.0.2", "@mapbox/mapbox-gl-supported": "^3.0.0", "@mapbox/point-geometry": "^0.1.0", "@mapbox/tiny-sdf": "^2.0.6", "@mapbox/unitbezier": "^0.0.1", "@mapbox/vector-tile": "^1.3.1", "@mapbox/whoots-js": "^3.1.0", "@types/geojson": "^7946.0.16", "@types/geojson-vt": "^3.2.5", "@types/mapbox__point-geometry": "^0.1.4", "@types/mapbox__vector-tile": "^1.3.4", "@types/pbf": "^3.0.5", "@types/supercluster": "^7.1.3", "cheap-ruler": "^4.0.0", "csscolorparser": "~1.0.3", "earcut": "^3.0.0", "geojson-vt": "^4.0.2", "gl-matrix": "^3.4.3", "grid-index": "^1.1.0", "kdbush": "^4.0.2", "murmurhash-js": "^1.0.0", "pbf": "^3.2.1", "potpack": "^2.0.0", "quickselect": "^3.0.0", "serialize-to-js": "^3.1.2", "supercluster": "^8.0.1", "tinyqueue": "^3.0.0", "vt-pbf": "^3.1.3" } }, "sha512-YnQxjlthuv/tidcxGYU2C8nRDVXMlAHa3qFhuOJeX4AfRP72OMRBf9ApL+M+k5VWcAXi2fcNOUVgphknjLumjA=="],
|
|
||||||
|
|
||||||
"maplibre-gl": ["maplibre-gl@5.1.1", "", { "dependencies": { "@mapbox/geojson-rewind": "^0.5.2", "@mapbox/jsonlint-lines-primitives": "^2.0.2", "@mapbox/point-geometry": "^0.1.0", "@mapbox/tiny-sdf": "^2.0.6", "@mapbox/unitbezier": "^0.0.1", "@mapbox/vector-tile": "^1.3.1", "@mapbox/whoots-js": "^3.1.0", "@maplibre/maplibre-gl-style-spec": "^23.1.0", "@types/geojson": "^7946.0.16", "@types/geojson-vt": "3.2.5", "@types/mapbox__point-geometry": "^0.1.4", "@types/mapbox__vector-tile": "^1.3.4", "@types/pbf": "^3.0.5", "@types/supercluster": "^7.1.3", "earcut": "^3.0.1", "geojson-vt": "^4.0.2", "gl-matrix": "^3.4.3", "global-prefix": "^4.0.0", "kdbush": "^4.0.2", "murmurhash-js": "^1.0.0", "pbf": "^3.3.0", "potpack": "^2.0.0", "quickselect": "^3.0.0", "supercluster": "^8.0.1", "tinyqueue": "^3.0.0", "vt-pbf": "^3.1.3" } }, "sha512-0Z6ODzyFu/grwT6K1eIBpv6MZE4xnJD1AV+Yq1hPzOh/YCY36r9BlSaU7d7n2/HJOaoKOy0b2YF8cS4dD+iEVQ=="],
|
"maplibre-gl": ["maplibre-gl@5.1.1", "", { "dependencies": { "@mapbox/geojson-rewind": "^0.5.2", "@mapbox/jsonlint-lines-primitives": "^2.0.2", "@mapbox/point-geometry": "^0.1.0", "@mapbox/tiny-sdf": "^2.0.6", "@mapbox/unitbezier": "^0.0.1", "@mapbox/vector-tile": "^1.3.1", "@mapbox/whoots-js": "^3.1.0", "@maplibre/maplibre-gl-style-spec": "^23.1.0", "@types/geojson": "^7946.0.16", "@types/geojson-vt": "3.2.5", "@types/mapbox__point-geometry": "^0.1.4", "@types/mapbox__vector-tile": "^1.3.4", "@types/pbf": "^3.0.5", "@types/supercluster": "^7.1.3", "earcut": "^3.0.1", "geojson-vt": "^4.0.2", "gl-matrix": "^3.4.3", "global-prefix": "^4.0.0", "kdbush": "^4.0.2", "murmurhash-js": "^1.0.0", "pbf": "^3.3.0", "potpack": "^2.0.0", "quickselect": "^3.0.0", "supercluster": "^8.0.1", "tinyqueue": "^3.0.0", "vt-pbf": "^3.1.3" } }, "sha512-0Z6ODzyFu/grwT6K1eIBpv6MZE4xnJD1AV+Yq1hPzOh/YCY36r9BlSaU7d7n2/HJOaoKOy0b2YF8cS4dD+iEVQ=="],
|
||||||
|
|
||||||
"marchingsquares": ["marchingsquares@1.3.3", "", {}, "sha512-gz6nNQoVK7Lkh2pZulrT4qd4347S/toG9RXH2pyzhLgkL5mLkBoqgv4EvAGXcV0ikDW72n/OQb3Xe8bGagQZCg=="],
|
"marchingsquares": ["marchingsquares@1.3.3", "", {}, "sha512-gz6nNQoVK7Lkh2pZulrT4qd4347S/toG9RXH2pyzhLgkL5mLkBoqgv4EvAGXcV0ikDW72n/OQb3Xe8bGagQZCg=="],
|
||||||
@@ -1488,7 +1459,7 @@
|
|||||||
|
|
||||||
"potpack": ["potpack@2.0.0", "", {}, "sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw=="],
|
"potpack": ["potpack@2.0.0", "", {}, "sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw=="],
|
||||||
|
|
||||||
"preact": ["preact@10.26.2", "", {}, "sha512-0gNmv4qpS9HaN3+40CLBAnKe0ZfyE4ZWo5xKlC1rVrr0ckkEvJvAQqKaHANdFKsGstoxrY4AItZ7kZSGVoVjgg=="],
|
"preact": ["preact@10.26.4", "", {}, "sha512-KJhO7LBFTjP71d83trW+Ilnjbo+ySsaAgCfXOXUlmGzJ4ygYPWmysm77yg4emwfmoz3b22yvH5IsVFHbhUaH5w=="],
|
||||||
|
|
||||||
"pretty-bytes": ["pretty-bytes@6.1.1", "", {}, "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ=="],
|
"pretty-bytes": ["pretty-bytes@6.1.1", "", {}, "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ=="],
|
||||||
|
|
||||||
@@ -1582,7 +1553,7 @@
|
|||||||
|
|
||||||
"robust-predicates": ["robust-predicates@3.0.2", "", {}, "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="],
|
"robust-predicates": ["robust-predicates@3.0.2", "", {}, "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="],
|
||||||
|
|
||||||
"rollup": ["rollup@4.34.8", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.34.8", "@rollup/rollup-android-arm64": "4.34.8", "@rollup/rollup-darwin-arm64": "4.34.8", "@rollup/rollup-darwin-x64": "4.34.8", "@rollup/rollup-freebsd-arm64": "4.34.8", "@rollup/rollup-freebsd-x64": "4.34.8", "@rollup/rollup-linux-arm-gnueabihf": "4.34.8", "@rollup/rollup-linux-arm-musleabihf": "4.34.8", "@rollup/rollup-linux-arm64-gnu": "4.34.8", "@rollup/rollup-linux-arm64-musl": "4.34.8", "@rollup/rollup-linux-loongarch64-gnu": "4.34.8", "@rollup/rollup-linux-powerpc64le-gnu": "4.34.8", "@rollup/rollup-linux-riscv64-gnu": "4.34.8", "@rollup/rollup-linux-s390x-gnu": "4.34.8", "@rollup/rollup-linux-x64-gnu": "4.34.8", "@rollup/rollup-linux-x64-musl": "4.34.8", "@rollup/rollup-win32-arm64-msvc": "4.34.8", "@rollup/rollup-win32-ia32-msvc": "4.34.8", "@rollup/rollup-win32-x64-msvc": "4.34.8", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ=="],
|
"rollup": ["rollup@4.34.9", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.34.9", "@rollup/rollup-android-arm64": "4.34.9", "@rollup/rollup-darwin-arm64": "4.34.9", "@rollup/rollup-darwin-x64": "4.34.9", "@rollup/rollup-freebsd-arm64": "4.34.9", "@rollup/rollup-freebsd-x64": "4.34.9", "@rollup/rollup-linux-arm-gnueabihf": "4.34.9", "@rollup/rollup-linux-arm-musleabihf": "4.34.9", "@rollup/rollup-linux-arm64-gnu": "4.34.9", "@rollup/rollup-linux-arm64-musl": "4.34.9", "@rollup/rollup-linux-loongarch64-gnu": "4.34.9", "@rollup/rollup-linux-powerpc64le-gnu": "4.34.9", "@rollup/rollup-linux-riscv64-gnu": "4.34.9", "@rollup/rollup-linux-s390x-gnu": "4.34.9", "@rollup/rollup-linux-x64-gnu": "4.34.9", "@rollup/rollup-linux-x64-musl": "4.34.9", "@rollup/rollup-win32-arm64-msvc": "4.34.9", "@rollup/rollup-win32-ia32-msvc": "4.34.9", "@rollup/rollup-win32-x64-msvc": "4.34.9", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-nF5XYqWWp9hx/LrpC8sZvvvmq0TeTjQgaZHYmAgwysT9nh8sWnZhBnM8ZyVbbJFIQBLwHDNoMqsBZBbUo4U8sQ=="],
|
||||||
|
|
||||||
"rw": ["rw@1.3.3", "", {}, "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="],
|
"rw": ["rw@1.3.3", "", {}, "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="],
|
||||||
|
|
||||||
@@ -1600,8 +1571,6 @@
|
|||||||
|
|
||||||
"serialize-javascript": ["serialize-javascript@6.0.2", "", { "dependencies": { "randombytes": "^2.1.0" } }, "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g=="],
|
"serialize-javascript": ["serialize-javascript@6.0.2", "", { "dependencies": { "randombytes": "^2.1.0" } }, "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g=="],
|
||||||
|
|
||||||
"serialize-to-js": ["serialize-to-js@3.1.2", "", {}, "sha512-owllqNuDDEimQat7EPG0tH7JjO090xKNzUtYz6X+Sk2BXDnOCilDdNLwjWeFywG9xkJul1ULvtUQa9O4pUaY0w=="],
|
|
||||||
|
|
||||||
"set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
|
"set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
|
||||||
|
|
||||||
"set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="],
|
"set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="],
|
||||||
@@ -1660,7 +1629,7 @@
|
|||||||
|
|
||||||
"stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="],
|
"stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="],
|
||||||
|
|
||||||
"std-env": ["std-env@3.8.0", "", {}, "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w=="],
|
"std-env": ["std-env@3.8.1", "", {}, "sha512-vj5lIj3Mwf9D79hBkltk5qmkFI+biIKWS2IBxEyEU3AX1tUf7AoL8nSazCOiiqQsGKIq01SClsKEzweu34uwvA=="],
|
||||||
|
|
||||||
"ste-core": ["ste-core@3.0.11", "", {}, "sha512-ivkRENMh0mdGoPlZ4xVcEaC8rXQfTEfvonRw5m8VDKV7kgcbZbaNd1TnKl08wXbcLdT7okSc63HNP8cVhy95zg=="],
|
"ste-core": ["ste-core@3.0.11", "", {}, "sha512-ivkRENMh0mdGoPlZ4xVcEaC8rXQfTEfvonRw5m8VDKV7kgcbZbaNd1TnKl08wXbcLdT7okSc63HNP8cVhy95zg=="],
|
||||||
|
|
||||||
@@ -1788,7 +1757,7 @@
|
|||||||
|
|
||||||
"upath": ["upath@1.2.0", "", {}, "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg=="],
|
"upath": ["upath@1.2.0", "", {}, "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg=="],
|
||||||
|
|
||||||
"update-browserslist-db": ["update-browserslist-db@1.1.2", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg=="],
|
"update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="],
|
||||||
|
|
||||||
"url": ["url@0.11.4", "", { "dependencies": { "punycode": "^1.4.1", "qs": "^6.12.3" } }, "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg=="],
|
"url": ["url@0.11.4", "", { "dependencies": { "punycode": "^1.4.1", "qs": "^6.12.3" } }, "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg=="],
|
||||||
|
|
||||||
@@ -1884,8 +1853,6 @@
|
|||||||
|
|
||||||
"zustand": ["zustand@5.0.3", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg=="],
|
"zustand": ["zustand@5.0.3", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg=="],
|
||||||
|
|
||||||
"@meshtastic/transport-web-serial/@jsr/meshtastic__core": ["@jsr/meshtastic__core@2.6.0-0", "https://npm.jsr.io/~/11/@jsr/meshtastic__core/2.6.0-0.tgz", { "dependencies": { "@bufbuild/protobuf": "^2.2.3", "@jsr/meshtastic__protobufs": "^2.6.0", "crc": "^4.3.2", "ste-simple-events": "^3.0.11", "tslog": "^4.9.3" } }, "sha512-Ks71sRagbBipotznULpsJZ1EMcQIqCEJQx6mf628dmCNVf2YECi2zi/i/5zErp1hGPgfbDvCz9oPogvsd/7fMA=="],
|
|
||||||
|
|
||||||
"@rollup/plugin-babel/@rollup/pluginutils": ["@rollup/pluginutils@3.1.0", "", { "dependencies": { "@types/estree": "0.0.39", "estree-walker": "^1.0.1", "picomatch": "^2.2.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0" } }, "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg=="],
|
"@rollup/plugin-babel/@rollup/pluginutils": ["@rollup/pluginutils@3.1.0", "", { "dependencies": { "@types/estree": "0.0.39", "estree-walker": "^1.0.1", "picomatch": "^2.2.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0" } }, "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg=="],
|
||||||
|
|
||||||
"@rollup/plugin-babel/rollup": ["rollup@2.79.2", "", { "optionalDependencies": { "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ=="],
|
"@rollup/plugin-babel/rollup": ["rollup@2.79.2", "", { "optionalDependencies": { "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ=="],
|
||||||
@@ -1944,7 +1911,7 @@
|
|||||||
|
|
||||||
"rbush/quickselect": ["quickselect@2.0.0", "", {}, "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw=="],
|
"rbush/quickselect": ["quickselect@2.0.0", "", {}, "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw=="],
|
||||||
|
|
||||||
"react-scan/@types/node": ["@types/node@20.17.19", "", { "dependencies": { "undici-types": "~6.19.2" } }, "sha512-LEwC7o1ifqg/6r2gn9Dns0f1rhK+fPFDoMiceTJ6kWmVk6bgXBI/9IOWfVan4WiAavK9pIVWdX0/e3J+eEUh5A=="],
|
"react-scan/@types/node": ["@types/node@20.17.23", "", { "dependencies": { "undici-types": "~6.19.2" } }, "sha512-8PCGZ1ZJbEZuYNTMqywO+Sj4vSKjSjT6Ua+6RFOYlEvIvKQABPtrNkoVSLSKDb4obYcMhspVKmsw8Cm10NFRUg=="],
|
||||||
|
|
||||||
"react-scan/esbuild": ["esbuild@0.24.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.24.2", "@esbuild/android-arm": "0.24.2", "@esbuild/android-arm64": "0.24.2", "@esbuild/android-x64": "0.24.2", "@esbuild/darwin-arm64": "0.24.2", "@esbuild/darwin-x64": "0.24.2", "@esbuild/freebsd-arm64": "0.24.2", "@esbuild/freebsd-x64": "0.24.2", "@esbuild/linux-arm": "0.24.2", "@esbuild/linux-arm64": "0.24.2", "@esbuild/linux-ia32": "0.24.2", "@esbuild/linux-loong64": "0.24.2", "@esbuild/linux-mips64el": "0.24.2", "@esbuild/linux-ppc64": "0.24.2", "@esbuild/linux-riscv64": "0.24.2", "@esbuild/linux-s390x": "0.24.2", "@esbuild/linux-x64": "0.24.2", "@esbuild/netbsd-arm64": "0.24.2", "@esbuild/netbsd-x64": "0.24.2", "@esbuild/openbsd-arm64": "0.24.2", "@esbuild/openbsd-x64": "0.24.2", "@esbuild/sunos-x64": "0.24.2", "@esbuild/win32-arm64": "0.24.2", "@esbuild/win32-ia32": "0.24.2", "@esbuild/win32-x64": "0.24.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA=="],
|
"react-scan/esbuild": ["esbuild@0.24.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.24.2", "@esbuild/android-arm": "0.24.2", "@esbuild/android-arm64": "0.24.2", "@esbuild/android-x64": "0.24.2", "@esbuild/darwin-arm64": "0.24.2", "@esbuild/darwin-x64": "0.24.2", "@esbuild/freebsd-arm64": "0.24.2", "@esbuild/freebsd-x64": "0.24.2", "@esbuild/linux-arm": "0.24.2", "@esbuild/linux-arm64": "0.24.2", "@esbuild/linux-ia32": "0.24.2", "@esbuild/linux-loong64": "0.24.2", "@esbuild/linux-mips64el": "0.24.2", "@esbuild/linux-ppc64": "0.24.2", "@esbuild/linux-riscv64": "0.24.2", "@esbuild/linux-s390x": "0.24.2", "@esbuild/linux-x64": "0.24.2", "@esbuild/netbsd-arm64": "0.24.2", "@esbuild/netbsd-x64": "0.24.2", "@esbuild/openbsd-arm64": "0.24.2", "@esbuild/openbsd-x64": "0.24.2", "@esbuild/sunos-x64": "0.24.2", "@esbuild/win32-arm64": "0.24.2", "@esbuild/win32-ia32": "0.24.2", "@esbuild/win32-x64": "0.24.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA=="],
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"imports": {
|
||||||
|
"@app/": "./src/",
|
||||||
|
"@pages/": "./src/pages/",
|
||||||
|
"@components/": "./src/components/",
|
||||||
|
"@core/": "./src/core/",
|
||||||
|
"@layouts/": "./src/layouts/"
|
||||||
|
},
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": [
|
||||||
|
"DOM",
|
||||||
|
"DOM.Iterable",
|
||||||
|
"ESNext",
|
||||||
|
"deno.window",
|
||||||
|
"deno.ns"
|
||||||
|
],
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"types": [
|
||||||
|
"vite/client",
|
||||||
|
"node",
|
||||||
|
"@types/web-bluetooth",
|
||||||
|
"@types/w3c-web-serial"
|
||||||
|
],
|
||||||
|
"strictPropertyInitialization": false
|
||||||
|
},
|
||||||
|
"unstable": [
|
||||||
|
"sloppy-imports"
|
||||||
|
]
|
||||||
|
}
|
||||||
+20
-22
@@ -6,27 +6,17 @@
|
|||||||
"license": "GPL-3.0-only",
|
"license": "GPL-3.0-only",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"build:analyze": "BUNDLE_ANALYZE=true vite build",
|
"build:analyze": "BUNDLE_ANALYZE=true deno task build",
|
||||||
"check": "biome check src/",
|
"lint": "deno lint src/",
|
||||||
"check:fix": "pnpm check --write src/",
|
"lint:fix": "deno lint --fix src/",
|
||||||
"format": "biome format --write src/",
|
"format": "deno fmt src/",
|
||||||
"dev": "vite dev --open",
|
"dev": "deno task dev:ui",
|
||||||
"dev:scan": "VITE_DEBUG_SCAN=true vite dev",
|
"dev:ui": "deno run -A npm:vite dev",
|
||||||
"test": "vitest",
|
"dev:scan": "VITE_DEBUG_SCAN=true deno task dev:ui",
|
||||||
"test:ui": "vitest --ui",
|
"test": "deno run -A npm:vitest",
|
||||||
"test:run": "vitest run",
|
"test:ui": "deno task test --ui",
|
||||||
"preview": "vite preview",
|
"preview": "deno run -A npm:vite preview",
|
||||||
"package": "gzipper c -i html,js,css,png,ico,svg,webmanifest,txt dist dist/output && tar -cvf dist/build.tar -C ./dist/output/ $(ls ./dist/output/)",
|
"package": "gzipper c -i html,js,css,png,ico,svg,webmanifest,txt dist dist/output && tar -cvf dist/build.tar -C ./dist/output/ ."
|
||||||
"postinstall": "npx simple-git-hooks"
|
|
||||||
},
|
|
||||||
"simple-git-hooks": {
|
|
||||||
"pre-commit": "bun run check:fix && bun run format"
|
|
||||||
},
|
|
||||||
"lint-staged": {
|
|
||||||
"*.{ts,tsx}": [
|
|
||||||
"bun run check:fix",
|
|
||||||
"bun run format"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -35,6 +25,15 @@
|
|||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/meshtastic/web/issues"
|
"url": "https://github.com/meshtastic/web/issues"
|
||||||
},
|
},
|
||||||
|
"simple-git-hooks": {
|
||||||
|
"pre-commit": "deno task lint:fix && deno task format"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{ts,tsx}": [
|
||||||
|
"deno task lint:fix",
|
||||||
|
"deno task format"
|
||||||
|
]
|
||||||
|
},
|
||||||
"homepage": "https://meshtastic.org",
|
"homepage": "https://meshtastic.org",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bufbuild/protobuf": "^2.2.3",
|
"@bufbuild/protobuf": "^2.2.3",
|
||||||
@@ -80,7 +79,6 @@
|
|||||||
"zustand": "5.0.3"
|
"zustand": "5.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^1.9.4",
|
|
||||||
"@tailwindcss/postcss": "^4.0.9",
|
"@tailwindcss/postcss": "^4.0.9",
|
||||||
"@testing-library/react": "^16.2.0",
|
"@testing-library/react": "^16.2.0",
|
||||||
"@types/chrome": "^0.0.307",
|
"@types/chrome": "^0.0.307",
|
||||||
|
|||||||
+3
-4
@@ -1,11 +1,10 @@
|
|||||||
import { DeviceWrapper } from "@app/DeviceWrapper.tsx";
|
import { DeviceWrapper } from "@app/DeviceWrapper.tsx";
|
||||||
import { PageRouter } from "@app/PageRouter.tsx";
|
import { PageRouter } from "@app/PageRouter.tsx";
|
||||||
import { ThemeProvider } from "@app/components/generic/ThemeProvider";
|
|
||||||
import { CommandPalette } from "@components/CommandPalette.tsx";
|
import { CommandPalette } from "@components/CommandPalette.tsx";
|
||||||
import { DeviceSelector } from "@components/DeviceSelector.tsx";
|
import { DeviceSelector } from "@components/DeviceSelector.tsx";
|
||||||
import { DialogManager } from "@components/Dialog/DialogManager";
|
import { DialogManager } from "@components/Dialog/DialogManager.tsx";
|
||||||
import { NewDeviceDialog } from "@components/Dialog/NewDeviceDialog.tsx";
|
import { NewDeviceDialog } from "@components/Dialog/NewDeviceDialog.tsx";
|
||||||
import { KeyBackupReminder } from "@components/KeyBackupReminder";
|
import { KeyBackupReminder } from "@components/KeyBackupReminder.tsx";
|
||||||
import { Toaster } from "@components/Toaster.tsx";
|
import { Toaster } from "@components/Toaster.tsx";
|
||||||
import Footer from "@components/UI/Footer.tsx";
|
import Footer from "@components/UI/Footer.tsx";
|
||||||
import { useAppStore } from "@core/stores/appStore.ts";
|
import { useAppStore } from "@core/stores/appStore.ts";
|
||||||
@@ -13,7 +12,7 @@ import { useDeviceStore } from "@core/stores/deviceStore.ts";
|
|||||||
import { Dashboard } from "@pages/Dashboard/index.tsx";
|
import { Dashboard } from "@pages/Dashboard/index.tsx";
|
||||||
import type { JSX } from "react";
|
import type { JSX } from "react";
|
||||||
import { ErrorBoundary } from "react-error-boundary";
|
import { ErrorBoundary } from "react-error-boundary";
|
||||||
import { ErrorPage } from "./components/UI/ErrorPage";
|
import { ErrorPage } from "@components/UI/ErrorPage.tsx";
|
||||||
import { MapProvider } from "react-map-gl/maplibre";
|
import { MapProvider } from "react-map-gl/maplibre";
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ import ConfigPage from "@pages/Config/index.tsx";
|
|||||||
import MessagesPage from "@pages/Messages.tsx";
|
import MessagesPage from "@pages/Messages.tsx";
|
||||||
import NodesPage from "@pages/Nodes.tsx";
|
import NodesPage from "@pages/Nodes.tsx";
|
||||||
import { ErrorBoundary } from "react-error-boundary";
|
import { ErrorBoundary } from "react-error-boundary";
|
||||||
import { ErrorPage } from "./components/UI/ErrorPage";
|
import { ErrorPage } from "@components/UI/ErrorPage.tsx";
|
||||||
|
|
||||||
export const ErrorBoundaryWrapper = ({
|
export const ErrorBoundaryWrapper = ({
|
||||||
children,
|
children,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Avatar } from "@components/UI/Avatar";
|
import { Avatar } from "./UI/Avatar.tsx";
|
||||||
import {
|
import {
|
||||||
CommandDialog,
|
CommandDialog,
|
||||||
CommandEmpty,
|
CommandEmpty,
|
||||||
@@ -117,13 +117,11 @@ export const CommandPalette = () => {
|
|||||||
return {
|
return {
|
||||||
label:
|
label:
|
||||||
device.nodes.get(device.hardware.myNodeNum)?.user?.longName ??
|
device.nodes.get(device.hardware.myNodeNum)?.user?.longName ??
|
||||||
device.hardware.myNodeNum.toString(),
|
device.hardware.myNodeNum.toString(),
|
||||||
icon: (
|
icon: (
|
||||||
<Avatar
|
<Avatar
|
||||||
text={
|
text={device.nodes.get(device.hardware.myNodeNum)?.user
|
||||||
device.nodes.get(device.hardware.myNodeNum)?.user
|
?.shortName ?? device.hardware.myNodeNum.toString()}
|
||||||
?.shortName ?? device.hardware.myNodeNum.toString()
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
action() {
|
action() {
|
||||||
@@ -241,8 +239,8 @@ export const CommandPalette = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener("keydown", handleKeydown);
|
globalThis.addEventListener("keydown", handleKeydown);
|
||||||
return () => window.removeEventListener("keydown", handleKeydown);
|
return () => globalThis.removeEventListener("keydown", handleKeydown);
|
||||||
}, [setCommandPaletteOpen]);
|
}, [setCommandPaletteOpen]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -5,10 +5,9 @@ import { Code } from "@components/UI/Typography/Code.tsx";
|
|||||||
import { useAppStore } from "@core/stores/appStore.ts";
|
import { useAppStore } from "@core/stores/appStore.ts";
|
||||||
import { useDeviceStore } from "@core/stores/deviceStore.ts";
|
import { useDeviceStore } from "@core/stores/deviceStore.ts";
|
||||||
import { HomeIcon, PlusIcon, SearchIcon } from "lucide-react";
|
import { HomeIcon, PlusIcon, SearchIcon } from "lucide-react";
|
||||||
import type { JSX } from "react";
|
import { Avatar } from "@components/UI/Avatar.tsx";
|
||||||
import { Avatar } from "./UI/Avatar";
|
|
||||||
|
|
||||||
export const DeviceSelector = (): JSX.Element => {
|
export const DeviceSelector = () => {
|
||||||
const { getDevices } = useDeviceStore();
|
const { getDevices } = useDeviceStore();
|
||||||
const {
|
const {
|
||||||
selectedDevice,
|
selectedDevice,
|
||||||
@@ -38,11 +37,9 @@ export const DeviceSelector = (): JSX.Element => {
|
|||||||
active={selectedDevice === device.id}
|
active={selectedDevice === device.id}
|
||||||
>
|
>
|
||||||
<Avatar
|
<Avatar
|
||||||
text={
|
text={device.nodes
|
||||||
device.nodes
|
.get(device.hardware.myNodeNum)
|
||||||
.get(device.hardware.myNodeNum)
|
?.user?.shortName.toString() ?? "UNK"}
|
||||||
?.user?.shortName.toString() ?? "UNK"
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</DeviceSelectorButton>
|
</DeviceSelectorButton>
|
||||||
))}
|
))}
|
||||||
@@ -66,11 +63,13 @@ export const DeviceSelector = (): JSX.Element => {
|
|||||||
<SearchIcon />
|
<SearchIcon />
|
||||||
</button>
|
</button>
|
||||||
{/* TODO: This is being commented out until its fixed */}
|
{/* TODO: This is being commented out until its fixed */}
|
||||||
{/* <button type="button" className="transition-all hover:text-accent">
|
{
|
||||||
|
/* <button type="button" className="transition-all hover:text-accent">
|
||||||
<LanguagesIcon />
|
<LanguagesIcon />
|
||||||
</button> */}
|
</button> */
|
||||||
|
}
|
||||||
<Separator />
|
<Separator />
|
||||||
<Code>{process.env.COMMIT_HASH}</Code>
|
<Code>{import.meta.env.VITE_COMMIT_HASH}</Code>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ export interface DeviceSelectorButtonProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const DeviceSelectorButton = ({
|
export const DeviceSelectorButton = ({
|
||||||
active,
|
|
||||||
onClick,
|
onClick,
|
||||||
children,
|
children,
|
||||||
}: DeviceSelectorButtonProps) => (
|
}: DeviceSelectorButtonProps) => (
|
||||||
@@ -14,9 +13,11 @@ export const DeviceSelectorButton = ({
|
|||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
onKeyDown={onClick}
|
onKeyDown={onClick}
|
||||||
>
|
>
|
||||||
{/* {active && (
|
{
|
||||||
|
/* {active && (
|
||||||
<div className="absolute -left-2 h-10 w-1.5 rounded-full bg-accent" />
|
<div className="absolute -left-2 h-10 w-1.5 rounded-full bg-accent" />
|
||||||
)} */}
|
)} */
|
||||||
|
}
|
||||||
<div className="flex aspect-square cursor-pointer flex-col items-center justify-center">
|
<div className="flex aspect-square cursor-pointer flex-col items-center justify-center">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useDevice } from "@app/core/stores/deviceStore.ts";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { create } from "@bufbuild/protobuf";
|
import { create } from "@bufbuild/protobuf";
|
||||||
import { Button } from "@components/UI/Button.tsx";
|
import { Button } from "@components/UI/Button.tsx";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { RemoveNodeDialog } from "@app/components/Dialog/RemoveNodeDialog.tsx";
|
import { RemoveNodeDialog } from "@components/Dialog/RemoveNodeDialog.tsx";
|
||||||
import { DeviceNameDialog } from "@components/Dialog/DeviceNameDialog.tsx";
|
import { DeviceNameDialog } from "@components/Dialog/DeviceNameDialog.tsx";
|
||||||
import { ImportDialog } from "@components/Dialog/ImportDialog.tsx";
|
import { ImportDialog } from "@components/Dialog/ImportDialog.tsx";
|
||||||
import { PkiBackupDialog } from "@components/Dialog/PKIBackupDialog";
|
import { PkiBackupDialog } from "./PKIBackupDialog.tsx";
|
||||||
import { QRDialog } from "@components/Dialog/QRDialog.tsx";
|
import { QRDialog } from "@components/Dialog/QRDialog.tsx";
|
||||||
import { RebootDialog } from "@components/Dialog/RebootDialog.tsx";
|
import { RebootDialog } from "@components/Dialog/RebootDialog.tsx";
|
||||||
import { ShutdownDialog } from "@components/Dialog/ShutdownDialog.tsx";
|
import { ShutdownDialog } from "@components/Dialog/ShutdownDialog.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import type { JSX } from "react";
|
|
||||||
import { NodeDetailsDialog } from "./NodeDetailsDialog";
|
|
||||||
|
|
||||||
export const DialogManager = (): JSX.Element => {
|
import { NodeDetailsDialog } from "./NodeDetailsDialog.tsx";
|
||||||
|
|
||||||
|
export const DialogManager = () => {
|
||||||
const { channels, config, dialog, setDialogOpen } = useDevice();
|
const { channels, config, dialog, setDialogOpen } = useDevice();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { Switch } from "@components/UI/Switch.tsx";
|
|||||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/core";
|
import { Protobuf } from "@meshtastic/core";
|
||||||
import { toByteArray } from "base64-js";
|
import { toByteArray } from "base64-js";
|
||||||
import { type JSX, useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
export interface ImportDialogProps {
|
export interface ImportDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -26,7 +26,7 @@ export interface ImportDialogProps {
|
|||||||
export const ImportDialog = ({
|
export const ImportDialog = ({
|
||||||
open,
|
open,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
}: ImportDialogProps): JSX.Element => {
|
}: ImportDialogProps) => {
|
||||||
const [importDialogInput, setImportDialogInput] = useState<string>("");
|
const [importDialogInput, setImportDialogInput] = useState<string>("");
|
||||||
const [channelSet, setChannelSet] = useState<Protobuf.AppOnly.ChannelSet>();
|
const [channelSet, setChannelSet] = useState<Protobuf.AppOnly.ChannelSet>();
|
||||||
const [validUrl, setValidUrl] = useState<boolean>(false);
|
const [validUrl, setValidUrl] = useState<boolean>(false);
|
||||||
@@ -62,7 +62,7 @@ export const ImportDialog = ({
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
setValidUrl(true);
|
setValidUrl(true);
|
||||||
} catch (error) {
|
} catch (_error) {
|
||||||
setValidUrl(false);
|
setValidUrl(false);
|
||||||
setChannelSet(undefined);
|
setChannelSet(undefined);
|
||||||
}
|
}
|
||||||
@@ -73,10 +73,9 @@ export const ImportDialog = ({
|
|||||||
connection?.setChannel(
|
connection?.setChannel(
|
||||||
create(Protobuf.Channel.ChannelSchema, {
|
create(Protobuf.Channel.ChannelSchema, {
|
||||||
index,
|
index,
|
||||||
role:
|
role: index === 0
|
||||||
index === 0
|
? Protobuf.Channel.Channel_Role.PRIMARY
|
||||||
? Protobuf.Channel.Channel_Role.PRIMARY
|
: Protobuf.Channel.Channel_Role.SECONDARY,
|
||||||
: Protobuf.Channel.Channel_Role.SECONDARY,
|
|
||||||
settings: ch,
|
settings: ch,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -119,25 +118,29 @@ export const ImportDialog = ({
|
|||||||
<div className="w-36">
|
<div className="w-36">
|
||||||
<Label>Use Preset?</Label>
|
<Label>Use Preset?</Label>
|
||||||
<Switch
|
<Switch
|
||||||
disabled={true}
|
disabled
|
||||||
checked={channelSet?.loraConfig?.usePreset ?? true}
|
checked={channelSet?.loraConfig?.usePreset ?? true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* <Select
|
{
|
||||||
|
/* <Select
|
||||||
label="Modem Preset"
|
label="Modem Preset"
|
||||||
disabled
|
disabled
|
||||||
value={channelSet?.loraConfig?.modemPreset}
|
value={channelSet?.loraConfig?.modemPreset}
|
||||||
>
|
>
|
||||||
{renderOptions(Protobuf.Config_LoRaConfig_ModemPreset)}
|
{renderOptions(Protobuf.Config_LoRaConfig_ModemPreset)}
|
||||||
</Select> */}
|
</Select> */
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
{/* <Select
|
{
|
||||||
|
/* <Select
|
||||||
label="Region"
|
label="Region"
|
||||||
disabled
|
disabled
|
||||||
value={channelSet?.loraConfig?.region}
|
value={channelSet?.loraConfig?.region}
|
||||||
>
|
>
|
||||||
{renderOptions(Protobuf.Config_LoRaConfig_RegionCode)}
|
{renderOptions(Protobuf.Config_LoRaConfig_RegionCode)}
|
||||||
</Select> */}
|
</Select> */
|
||||||
|
}
|
||||||
|
|
||||||
<span className="text-md block font-medium text-text-primary">
|
<span className="text-md block font-medium text-text-primary">
|
||||||
Channels:
|
Channels:
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
import { useDevice } from "@app/core/stores/deviceStore";
|
import { useDevice } from "../../core/stores/deviceStore.ts";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogDescription,
|
DialogDescription,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@components/UI/Dialog";
|
} from "../UI/Dialog.tsx";
|
||||||
import type { Protobuf, Types } from "@meshtastic/core";
|
import type { Protobuf, Types } from "@meshtastic/core";
|
||||||
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
|
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
|
||||||
import type { JSX } from "react";
|
|
||||||
|
|
||||||
export interface LocationResponseDialogProps {
|
export interface LocationResponseDialogProps {
|
||||||
location: Types.PacketMetadata<Protobuf.Mesh.location> | undefined;
|
location: Types.PacketMetadata<Protobuf.Mesh.location> | undefined;
|
||||||
@@ -20,15 +19,13 @@ export const LocationResponseDialog = ({
|
|||||||
location,
|
location,
|
||||||
open,
|
open,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
}: LocationResponseDialogProps): JSX.Element => {
|
}: LocationResponseDialogProps) => {
|
||||||
const { nodes } = useDevice();
|
const { nodes } = useDevice();
|
||||||
|
|
||||||
const from = nodes.get(location?.from ?? 0);
|
const from = nodes.get(location?.from ?? 0);
|
||||||
const longName =
|
const longName = from?.user?.longName ??
|
||||||
from?.user?.longName ??
|
|
||||||
(from ? `!${numberToHexUnpadded(from?.num)}` : "Unknown");
|
(from ? `!${numberToHexUnpadded(from?.num)}` : "Unknown");
|
||||||
const shortName =
|
const shortName = from?.user?.shortName ??
|
||||||
from?.user?.shortName ??
|
|
||||||
(from ? `${numberToHexUnpadded(from?.num).substring(0, 4)}` : "UNK");
|
(from ? `${numberToHexUnpadded(from?.num).substring(0, 4)}` : "UNK");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
type BrowserFeature,
|
type BrowserFeature,
|
||||||
useBrowserFeatureDetection,
|
useBrowserFeatureDetection,
|
||||||
} from "@app/core/hooks/useBrowserFeatureDetection";
|
} from "../../core/hooks/useBrowserFeatureDetection.ts";
|
||||||
import { BLE } from "@components/PageComponents/Connect/BLE.tsx";
|
import { BLE } from "@components/PageComponents/Connect/BLE.tsx";
|
||||||
import { HTTP } from "@components/PageComponents/Connect/HTTP.tsx";
|
import { HTTP } from "@components/PageComponents/Connect/HTTP.tsx";
|
||||||
import { Serial } from "@components/PageComponents/Connect/Serial.tsx";
|
import { Serial } from "@components/PageComponents/Connect/Serial.tsx";
|
||||||
@@ -18,9 +18,9 @@ import {
|
|||||||
TabsTrigger,
|
TabsTrigger,
|
||||||
} from "@components/UI/Tabs.tsx";
|
} from "@components/UI/Tabs.tsx";
|
||||||
import { Subtle } from "@components/UI/Typography/Subtle.tsx";
|
import { Subtle } from "@components/UI/Typography/Subtle.tsx";
|
||||||
import { AlertCircle, InfoIcon } from "lucide-react";
|
import { AlertCircle } from "lucide-react";
|
||||||
import { Fragment, type JSX } from "react/jsx-runtime";
|
import { Link } from "../UI/Typography/Link.tsx";
|
||||||
import { Link } from "../UI/Typography/Link";
|
import { Fragment } from "react/jsx-runtime";
|
||||||
|
|
||||||
export interface TabElementProps {
|
export interface TabElementProps {
|
||||||
closeDialog: () => void;
|
closeDialog: () => void;
|
||||||
@@ -85,14 +85,16 @@ const ErrorMessage = ({ missingFeatures }: FeatureErrorProps) => {
|
|||||||
<p className="text-sm">
|
<p className="text-sm">
|
||||||
{browserFeatures.length > 0 && (
|
{browserFeatures.length > 0 && (
|
||||||
<>
|
<>
|
||||||
This application requires {formatFeatureList(browserFeatures)}.
|
This application requires{" "}
|
||||||
Please use a Chromium-based browser like Chrome or Edge.
|
{formatFeatureList(browserFeatures)}. Please use a
|
||||||
|
Chromium-based browser like Chrome or Edge.
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{needsSecureContext && (
|
{needsSecureContext && (
|
||||||
<>
|
<>
|
||||||
{browserFeatures.length > 0 && " Additionally, it"}
|
{browserFeatures.length > 0 && " Additionally, it"}
|
||||||
{browserFeatures.length === 0 && "This application"} requires a{" "}
|
{browserFeatures.length === 0 && "This application"} requires a
|
||||||
|
{" "}
|
||||||
<Link href={links["Secure Context"]}>secure context</Link>.
|
<Link href={links["Secure Context"]}>secure context</Link>.
|
||||||
Please connect using HTTPS or localhost.
|
Please connect using HTTPS or localhost.
|
||||||
</>
|
</>
|
||||||
@@ -107,7 +109,7 @@ const ErrorMessage = ({ missingFeatures }: FeatureErrorProps) => {
|
|||||||
export const NewDeviceDialog = ({
|
export const NewDeviceDialog = ({
|
||||||
open,
|
open,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
}: NewDeviceProps): JSX.Element => {
|
}: NewDeviceProps) => {
|
||||||
const { unsupported } = useBrowserFeatureDetection();
|
const { unsupported } = useBrowserFeatureDetection();
|
||||||
|
|
||||||
const tabs: TabManifest[] = [
|
const tabs: TabManifest[] = [
|
||||||
@@ -119,15 +121,13 @@ export const NewDeviceDialog = ({
|
|||||||
{
|
{
|
||||||
label: "Bluetooth",
|
label: "Bluetooth",
|
||||||
element: BLE,
|
element: BLE,
|
||||||
isDisabled:
|
isDisabled: unsupported.includes("Web Bluetooth") ||
|
||||||
unsupported.includes("Web Bluetooth") ||
|
|
||||||
unsupported.includes("Secure Context"),
|
unsupported.includes("Secure Context"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Serial",
|
label: "Serial",
|
||||||
element: Serial,
|
element: Serial,
|
||||||
isDisabled:
|
isDisabled: unsupported.includes("Web Serial") ||
|
||||||
unsupported.includes("Web Serial") ||
|
|
||||||
unsupported.includes("Secure Context"),
|
unsupported.includes("Secure Context"),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -149,9 +149,9 @@ export const NewDeviceDialog = ({
|
|||||||
{tabs.map((tab) => (
|
{tabs.map((tab) => (
|
||||||
<TabsContent key={tab.label} value={tab.label}>
|
<TabsContent key={tab.label} value={tab.label}>
|
||||||
<fieldset disabled={tab.isDisabled}>
|
<fieldset disabled={tab.isDisabled}>
|
||||||
{tab.isDisabled ? (
|
{tab.isDisabled
|
||||||
<ErrorMessage missingFeatures={unsupported} />
|
? <ErrorMessage missingFeatures={unsupported} />
|
||||||
) : null}
|
: null}
|
||||||
<tab.element closeDialog={() => onOpenChange(false)} />
|
<tab.element closeDialog={() => onOpenChange(false)} />
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
import { useAppStore } from "@app/core/stores/appStore";
|
import { useAppStore } from "../../core/stores/appStore.ts";
|
||||||
import { useDevice } from "@app/core/stores/deviceStore";
|
import { useDevice } from "../../core/stores/deviceStore.ts";
|
||||||
import {
|
import {
|
||||||
Accordion,
|
Accordion,
|
||||||
AccordionContent,
|
AccordionContent,
|
||||||
AccordionItem,
|
AccordionItem,
|
||||||
AccordionTrigger,
|
AccordionTrigger,
|
||||||
} from "@components/UI/Accordion";
|
} from "../UI/Accordion.tsx";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogFooter,
|
DialogFooter,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@components/UI/Dialog";
|
} from "../UI/Dialog.tsx";
|
||||||
import { Protobuf } from "@meshtastic/core";
|
import { Protobuf } from "@meshtastic/core";
|
||||||
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
|
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
|
||||||
import { DeviceImage } from "../generic/DeviceImage";
|
import { DeviceImage } from "../generic/DeviceImage.tsx";
|
||||||
import { TimeAgo } from "../generic/TimeAgo";
|
import { TimeAgo } from "../generic/TimeAgo.tsx";
|
||||||
import { Uptime } from "../generic/Uptime";
|
import { Uptime } from "../generic/Uptime.tsx";
|
||||||
|
|
||||||
export interface NodeDetailsDialogProps {
|
export interface NodeDetailsDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -32,134 +32,159 @@ export const NodeDetailsDialog = ({
|
|||||||
const { nodeNumDetails } = useAppStore();
|
const { nodeNumDetails } = useAppStore();
|
||||||
const device: Protobuf.Mesh.NodeInfo = nodes.get(nodeNumDetails);
|
const device: Protobuf.Mesh.NodeInfo = nodes.get(nodeNumDetails);
|
||||||
|
|
||||||
return device ? (
|
return device
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
? (
|
||||||
<DialogContent>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogHeader>
|
<DialogContent>
|
||||||
<DialogTitle>
|
<DialogHeader>
|
||||||
Node Details for {device.user?.longName ?? "UNKNOWN"} (
|
<DialogTitle>
|
||||||
{device.user?.shortName ?? "UNK"})
|
Node Details for {device.user?.longName ?? "UNKNOWN"} (
|
||||||
</DialogTitle>
|
{device.user?.shortName ?? "UNK"})
|
||||||
</DialogHeader>
|
</DialogTitle>
|
||||||
<DialogFooter>
|
</DialogHeader>
|
||||||
<div className="w-full">
|
<DialogFooter>
|
||||||
<DeviceImage
|
<div className="w-full">
|
||||||
className="w-32 h-32 mx-auto rounded-lg border-4 border-slate-200 dark:border-slate-800"
|
<DeviceImage
|
||||||
deviceType={
|
className="w-32 h-32 mx-auto rounded-lg border-4 border-slate-200 dark:border-slate-800"
|
||||||
Protobuf.Mesh.HardwareModel[device.user?.hwModel ?? 0]
|
deviceType={Protobuf.Mesh
|
||||||
}
|
.HardwareModel[device.user?.hwModel ?? 0]}
|
||||||
/>
|
/>
|
||||||
<div className="bg-slate-100 dark:bg-slate-800 p-3 rounded-lg mt-3">
|
<div className="bg-slate-100 dark:bg-slate-800 p-3 rounded-lg mt-3">
|
||||||
<p className="text-lg font-semibold text-slate-900 dark:text-slate-50">
|
<p className="text-lg font-semibold text-slate-900 dark:text-slate-50">
|
||||||
Details:
|
Details:
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Hardware:{" "}
|
Hardware:{" "}
|
||||||
{Protobuf.Mesh.HardwareModel[device.user?.hwModel ?? 0]}
|
{Protobuf.Mesh.HardwareModel[device.user?.hwModel ?? 0]}
|
||||||
</p>
|
</p>
|
||||||
<p>Node Number: {device.num}</p>
|
<p>Node Number: {device.num}</p>
|
||||||
<p>Node HEX: !{numberToHexUnpadded(device.num)}</p>
|
<p>Node HEX: !{numberToHexUnpadded(device.num)}</p>
|
||||||
<p>
|
<p>
|
||||||
Role:{" "}
|
Role: {Protobuf.Config.Config_DeviceConfig_Role[
|
||||||
{
|
|
||||||
Protobuf.Config.Config_DeviceConfig_Role[
|
|
||||||
device.user?.role ?? 0
|
device.user?.role ?? 0
|
||||||
]
|
]}
|
||||||
}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Last Heard:{" "}
|
|
||||||
{device.lastHeard === 0 ? (
|
|
||||||
"Never"
|
|
||||||
) : (
|
|
||||||
<TimeAgo timestamp={device.lastHeard * 1000} />
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{device.position ? (
|
|
||||||
<div className="mt-5 bg-slate-100 dark:bg-slate-800 p-3 rounded-lg mt-3">
|
|
||||||
<p className="text-lg font-semibold text-slate-900 dark:text-slate-50">
|
|
||||||
Position:
|
|
||||||
</p>
|
</p>
|
||||||
{device.position.latitudeI && device.position.longitudeI ? (
|
<p>
|
||||||
<p>
|
Last Heard: {device.lastHeard === 0
|
||||||
Coordinates:{" "}
|
? (
|
||||||
<a
|
"Never"
|
||||||
className="text-blue-500 dark:text-blue-400"
|
)
|
||||||
href={`https://www.openstreetmap.org/?mlat=${
|
: <TimeAgo timestamp={device.lastHeard * 1000} />}
|
||||||
device.position.latitudeI / 1e7
|
</p>
|
||||||
}&mlon=${device.position.longitudeI / 1e7}&layers=N`}
|
</div>
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
{device.position
|
||||||
|
? (
|
||||||
|
<div className="mt-5 bg-slate-100 dark:bg-slate-800 p-3 rounded-lg mt-3">
|
||||||
|
<p className="text-lg font-semibold text-slate-900 dark:text-slate-50">
|
||||||
|
Position:
|
||||||
|
</p>
|
||||||
|
{device.position.latitudeI && device.position.longitudeI
|
||||||
|
? (
|
||||||
|
<p>
|
||||||
|
Coordinates:{" "}
|
||||||
|
<a
|
||||||
|
className="text-blue-500 dark:text-blue-400"
|
||||||
|
href={`https://www.openstreetmap.org/?mlat=${
|
||||||
|
device.position.latitudeI / 1e7
|
||||||
|
}&mlon=${
|
||||||
|
device.position.longitudeI / 1e7
|
||||||
|
}&layers=N`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
{device.position.latitudeI / 1e7},{" "}
|
||||||
|
{device.position.longitudeI / 1e7}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
: null}
|
||||||
|
{device.position.altitude
|
||||||
|
? <p>Altitude: {device.position.altitude}m</p>
|
||||||
|
: null}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
: null}
|
||||||
|
|
||||||
|
{device.deviceMetrics
|
||||||
|
? (
|
||||||
|
<div className="mt-5 bg-slate-100 dark:bg-slate-800 p-3 rounded-lg mt-3">
|
||||||
|
<p className="text-lg font-semibold text-slate-900 dark:text-slate-50">
|
||||||
|
Device Metrics:
|
||||||
|
</p>
|
||||||
|
{device.deviceMetrics.airUtilTx
|
||||||
|
? (
|
||||||
|
<p>
|
||||||
|
Air TX utilization:{" "}
|
||||||
|
{device.deviceMetrics.airUtilTx.toFixed(2)}%
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
: null}
|
||||||
|
{device.deviceMetrics.channelUtilization
|
||||||
|
? (
|
||||||
|
<p>
|
||||||
|
Channel utilization:{" "}
|
||||||
|
{device.deviceMetrics.channelUtilization.toFixed(2)}%
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
: null}
|
||||||
|
{device.deviceMetrics.batteryLevel
|
||||||
|
? (
|
||||||
|
<p>
|
||||||
|
Battery level:{" "}
|
||||||
|
{device.deviceMetrics.batteryLevel.toFixed(2)}%
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
: null}
|
||||||
|
{device.deviceMetrics.voltage
|
||||||
|
? (
|
||||||
|
<p>
|
||||||
|
Voltage: {device.deviceMetrics.voltage.toFixed(2)}V
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
: null}
|
||||||
|
{device.deviceMetrics.uptimeSeconds
|
||||||
|
? (
|
||||||
|
<p>
|
||||||
|
Uptime:{" "}
|
||||||
|
<Uptime
|
||||||
|
seconds={device.deviceMetrics.uptimeSeconds}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
: null}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
: null}
|
||||||
|
|
||||||
|
{device
|
||||||
|
? (
|
||||||
|
<div className="mt-5 w-full max-w-[464px] bg-slate-100 dark:bg-slate-800 p-3 rounded-lg mt-3">
|
||||||
|
<Accordion
|
||||||
|
className="AccordionRoot"
|
||||||
|
type="single"
|
||||||
|
collapsible
|
||||||
>
|
>
|
||||||
{device.position.latitudeI / 1e7},{" "}
|
<AccordionItem className="AccordionItem" value="item-1">
|
||||||
{device.position.longitudeI / 1e7}
|
<AccordionTrigger>
|
||||||
</a>
|
<p className="text-lg font-semibold text-slate-900 dark:text-slate-50">
|
||||||
</p>
|
All Raw Metrics:
|
||||||
) : null}
|
</p>
|
||||||
{device.position.altitude ? (
|
</AccordionTrigger>
|
||||||
<p>Altitude: {device.position.altitude}m</p>
|
<AccordionContent className="overflow-x-scroll">
|
||||||
) : null}
|
<pre className="text-xs w-full">
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{device.deviceMetrics ? (
|
|
||||||
<div className="mt-5 bg-slate-100 dark:bg-slate-800 p-3 rounded-lg mt-3">
|
|
||||||
<p className="text-lg font-semibold text-slate-900 dark:text-slate-50">
|
|
||||||
Device Metrics:
|
|
||||||
</p>
|
|
||||||
{device.deviceMetrics.airUtilTx ? (
|
|
||||||
<p>
|
|
||||||
Air TX utilization:{" "}
|
|
||||||
{device.deviceMetrics.airUtilTx.toFixed(2)}%
|
|
||||||
</p>
|
|
||||||
) : null}
|
|
||||||
{device.deviceMetrics.channelUtilization ? (
|
|
||||||
<p>
|
|
||||||
Channel utilization:{" "}
|
|
||||||
{device.deviceMetrics.channelUtilization.toFixed(2)}%
|
|
||||||
</p>
|
|
||||||
) : null}
|
|
||||||
{device.deviceMetrics.batteryLevel ? (
|
|
||||||
<p>
|
|
||||||
Battery level:{" "}
|
|
||||||
{device.deviceMetrics.batteryLevel.toFixed(2)}%
|
|
||||||
</p>
|
|
||||||
) : null}
|
|
||||||
{device.deviceMetrics.voltage ? (
|
|
||||||
<p>Voltage: {device.deviceMetrics.voltage.toFixed(2)}V</p>
|
|
||||||
) : null}
|
|
||||||
{device.deviceMetrics.uptimeSeconds ? (
|
|
||||||
<p>
|
|
||||||
Uptime:{" "}
|
|
||||||
<Uptime seconds={device.deviceMetrics.uptimeSeconds} />
|
|
||||||
</p>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{device ? (
|
|
||||||
<div className="mt-5 w-full max-w-[464px] bg-slate-100 dark:bg-slate-800 p-3 rounded-lg mt-3">
|
|
||||||
<Accordion className="AccordionRoot" type="single" collapsible>
|
|
||||||
<AccordionItem className="AccordionItem" value="item-1">
|
|
||||||
<AccordionTrigger>
|
|
||||||
<p className="text-lg font-semibold text-slate-900 dark:text-slate-50">
|
|
||||||
All Raw Metrics:
|
|
||||||
</p>
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent className="overflow-x-scroll">
|
|
||||||
<pre className="text-xs w-full">
|
|
||||||
{JSON.stringify(device, null, 2)}
|
{JSON.stringify(device, null, 2)}
|
||||||
</pre>
|
</pre>
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
)
|
||||||
</div>
|
: null}
|
||||||
</DialogFooter>
|
</div>
|
||||||
</DialogContent>
|
</DialogFooter>
|
||||||
</Dialog>
|
</DialogContent>
|
||||||
) : null;
|
</Dialog>
|
||||||
|
)
|
||||||
|
: null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import { toast } from "@app/core/hooks/useToast";
|
import { toast } from "../../core/hooks/useToast.ts";
|
||||||
import { useAppStore } from "@app/core/stores/appStore";
|
import { useAppStore } from "../../core/stores/appStore.ts";
|
||||||
import { useDevice } from "@app/core/stores/deviceStore";
|
import { useDevice } from "../../core/stores/deviceStore.ts";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@components/UI/Dialog";
|
} from "../UI/Dialog.tsx";
|
||||||
import type { Protobuf } from "@meshtastic/core";
|
import type { Protobuf } from "@meshtastic/core";
|
||||||
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
|
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
|
||||||
import { TrashIcon } from "lucide-react";
|
import { TrashIcon } from "lucide-react";
|
||||||
import type { JSX } from "react";
|
|
||||||
import { Button } from "../UI/Button";
|
import { Button } from "../UI/Button.tsx";
|
||||||
|
|
||||||
export interface NodeOptionsDialogProps {
|
export interface NodeOptionsDialogProps {
|
||||||
node: Protobuf.Mesh.NodeInfo | undefined;
|
node: Protobuf.Mesh.NodeInfo | undefined;
|
||||||
@@ -23,7 +23,7 @@ export const NodeOptionsDialog = ({
|
|||||||
node,
|
node,
|
||||||
open,
|
open,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
}: NodeOptionsDialogProps): JSX.Element => {
|
}: NodeOptionsDialogProps) => {
|
||||||
const { setDialogOpen, connection, setActivePage } = useDevice();
|
const { setDialogOpen, connection, setActivePage } = useDevice();
|
||||||
const {
|
const {
|
||||||
setNodeNumToBeRemoved,
|
setNodeNumToBeRemoved,
|
||||||
@@ -31,11 +31,9 @@ export const NodeOptionsDialog = ({
|
|||||||
setChatType,
|
setChatType,
|
||||||
setActiveChat,
|
setActiveChat,
|
||||||
} = useAppStore();
|
} = useAppStore();
|
||||||
const longName =
|
const longName = node?.user?.longName ??
|
||||||
node?.user?.longName ??
|
|
||||||
(node ? `!${numberToHexUnpadded(node?.num)}` : "Unknown");
|
(node ? `!${numberToHexUnpadded(node?.num)}` : "Unknown");
|
||||||
const shortName =
|
const shortName = node?.user?.shortName ??
|
||||||
node?.user?.shortName ??
|
|
||||||
(node ? `${numberToHexUnpadded(node?.num).substring(0, 4)}` : "UNK");
|
(node ? `${numberToHexUnpadded(node?.num).substring(0, 4)}` : "UNK");
|
||||||
|
|
||||||
function handleDirectMessage() {
|
function handleDirectMessage() {
|
||||||
@@ -53,7 +51,7 @@ export const NodeOptionsDialog = ({
|
|||||||
connection?.requestPosition(node.num).then(() =>
|
connection?.requestPosition(node.num).then(() =>
|
||||||
toast({
|
toast({
|
||||||
title: "Position request sent.",
|
title: "Position request sent.",
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
onOpenChange();
|
onOpenChange();
|
||||||
}
|
}
|
||||||
@@ -66,7 +64,7 @@ export const NodeOptionsDialog = ({
|
|||||||
connection?.traceRoute(node.num).then(() =>
|
connection?.traceRoute(node.num).then(() =>
|
||||||
toast({
|
toast({
|
||||||
title: "Traceroute sent.",
|
title: "Traceroute sent.",
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
onOpenChange();
|
onOpenChange();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useDevice } from "@app/core/stores/deviceStore";
|
import { useDevice } from "../../core/stores/deviceStore.ts";
|
||||||
import { Button } from "@components/UI/Button";
|
import { Button } from "../UI/Button.tsx";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -40,7 +40,7 @@ export const PkiBackupDialog = ({
|
|||||||
const renderPrintWindow = React.useCallback(() => {
|
const renderPrintWindow = React.useCallback(() => {
|
||||||
if (!privateKey || !publicKey) return;
|
if (!privateKey || !publicKey) return;
|
||||||
|
|
||||||
const printWindow = window.open("", "_blank");
|
const printWindow = globalThis.open("", "_blank");
|
||||||
if (printWindow) {
|
if (printWindow) {
|
||||||
printWindow.document.write(`
|
printWindow.document.write(`
|
||||||
<html>
|
<html>
|
||||||
@@ -116,14 +116,14 @@ export const PkiBackupDialog = ({
|
|||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<DialogFooter className="mt-6">
|
<DialogFooter className="mt-6">
|
||||||
<Button
|
<Button
|
||||||
variant={"default"}
|
variant="default"
|
||||||
onClick={() => createDownloadKeyFile()}
|
onClick={() => createDownloadKeyFile()}
|
||||||
className=""
|
className=""
|
||||||
>
|
>
|
||||||
<DownloadIcon size={20} className="mr-2" />
|
<DownloadIcon size={20} className="mr-2" />
|
||||||
Download
|
Download
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant={"default"} onClick={() => renderPrintWindow()}>
|
<Button variant="default" onClick={() => renderPrintWindow()}>
|
||||||
<PrinterIcon size={20} className="mr-2" />
|
<PrinterIcon size={20} className="mr-2" />
|
||||||
Print
|
Print
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export const PkiRegenerateDialog = ({
|
|||||||
open,
|
open,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
}: PkiRegenerateDialogProps): JSX.Element => {
|
}: PkiRegenerateDialogProps) => {
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export const QRDialog = ({
|
|||||||
onOpenChange,
|
onOpenChange,
|
||||||
loraConfig,
|
loraConfig,
|
||||||
channels,
|
channels,
|
||||||
}: QRDialogProps): JSX.Element => {
|
}: QRDialogProps) => {
|
||||||
const [selectedChannels, setSelectedChannels] = useState<number[]>([0]);
|
const [selectedChannels, setSelectedChannels] = useState<number[]>([0]);
|
||||||
const [qrCodeUrl, setQrCodeUrl] = useState<string>("");
|
const [qrCodeUrl, setQrCodeUrl] = useState<string>("");
|
||||||
const [qrCodeAdd, setQrCodeAdd] = useState<boolean>();
|
const [qrCodeAdd, setQrCodeAdd] = useState<boolean>();
|
||||||
@@ -77,8 +77,8 @@ export const QRDialog = ({
|
|||||||
{channel.settings?.name.length
|
{channel.settings?.name.length
|
||||||
? channel.settings.name
|
? channel.settings.name
|
||||||
: channel.role === Protobuf.Channel.Channel_Role.PRIMARY
|
: channel.role === Protobuf.Channel.Channel_Role.PRIMARY
|
||||||
? "Primary"
|
? "Primary"
|
||||||
: `Channel: ${channel.index}`}
|
: `Channel: ${channel.index}`}
|
||||||
</Label>
|
</Label>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
key={channel.index}
|
key={channel.index}
|
||||||
@@ -86,7 +86,9 @@ export const QRDialog = ({
|
|||||||
onCheckedChange={() => {
|
onCheckedChange={() => {
|
||||||
if (selectedChannels.includes(channel.index)) {
|
if (selectedChannels.includes(channel.index)) {
|
||||||
setSelectedChannels(
|
setSelectedChannels(
|
||||||
selectedChannels.filter((c) => c !== channel.index),
|
selectedChannels.filter((c) =>
|
||||||
|
c !== channel.index
|
||||||
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
setSelectedChannels([
|
setSelectedChannels([
|
||||||
@@ -130,7 +132,7 @@ export const QRDialog = ({
|
|||||||
<Label>Sharable URL</Label>
|
<Label>Sharable URL</Label>
|
||||||
<Input
|
<Input
|
||||||
value={qrCodeUrl}
|
value={qrCodeUrl}
|
||||||
disabled={true}
|
disabled
|
||||||
className="dark:text-slate-900"
|
className="dark:text-slate-900"
|
||||||
action={{
|
action={{
|
||||||
icon: ClipboardIcon,
|
icon: ClipboardIcon,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export interface RebootDialogProps {
|
|||||||
export const RebootDialog = ({
|
export const RebootDialog = ({
|
||||||
open,
|
open,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
}: RebootDialogProps): JSX.Element => {
|
}: RebootDialogProps) => {
|
||||||
const { connection } = useDevice();
|
const { connection } = useDevice();
|
||||||
|
|
||||||
const [time, setTime] = useState<number>(5);
|
const [time, setTime] = useState<number>(5);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useAppStore } from "@app/core/stores/appStore";
|
import { useAppStore } from "../../core/stores/appStore.ts";
|
||||||
import { useDevice } from "@app/core/stores/deviceStore.ts";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Button } from "@components/UI/Button.tsx";
|
import { Button } from "@components/UI/Button.tsx";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -19,7 +19,7 @@ export interface RemoveNodeDialogProps {
|
|||||||
export const RemoveNodeDialog = ({
|
export const RemoveNodeDialog = ({
|
||||||
open,
|
open,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
}: RemoveNodeDialogProps): JSX.Element => {
|
}: RemoveNodeDialogProps) => {
|
||||||
const { connection, nodes, removeNode } = useDevice();
|
const { connection, nodes, removeNode } = useDevice();
|
||||||
const { nodeNumToBeRemoved } = useAppStore();
|
const { nodeNumToBeRemoved } = useAppStore();
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export interface ShutdownDialogProps {
|
|||||||
export const ShutdownDialog = ({
|
export const ShutdownDialog = ({
|
||||||
open,
|
open,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
}: ShutdownDialogProps): JSX.Element => {
|
}: ShutdownDialogProps) => {
|
||||||
const { connection } = useDevice();
|
const { connection } = useDevice();
|
||||||
|
|
||||||
const [time, setTime] = useState<number>(5);
|
const [time, setTime] = useState<number>(5);
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { useDevice } from "@app/core/stores/deviceStore";
|
import { useDevice } from "../../core/stores/deviceStore.ts";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogDescription,
|
DialogDescription,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@components/UI/Dialog";
|
} from "../UI/Dialog.tsx";
|
||||||
import type { Protobuf, Types } from "@meshtastic/core";
|
import type { Protobuf, Types } from "@meshtastic/core";
|
||||||
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
|
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
|
||||||
import type { JSX } from "react";
|
|
||||||
import { TraceRoute } from "../PageComponents/Messages/TraceRoute";
|
import { TraceRoute } from "../PageComponents/Messages/TraceRoute.tsx";
|
||||||
|
|
||||||
export interface TracerouteResponseDialogProps {
|
export interface TracerouteResponseDialogProps {
|
||||||
traceroute: Types.PacketMetadata<Protobuf.Mesh.RouteDiscovery> | undefined;
|
traceroute: Types.PacketMetadata<Protobuf.Mesh.RouteDiscovery> | undefined;
|
||||||
@@ -21,18 +21,16 @@ export const TracerouteResponseDialog = ({
|
|||||||
traceroute,
|
traceroute,
|
||||||
open,
|
open,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
}: TracerouteResponseDialogProps): JSX.Element => {
|
}: TracerouteResponseDialogProps) => {
|
||||||
const { nodes } = useDevice();
|
const { nodes } = useDevice();
|
||||||
const route: number[] = traceroute?.data.route ?? [];
|
const route: number[] = traceroute?.data.route ?? [];
|
||||||
const routeBack: number[] = traceroute?.data.routeBack ?? [];
|
const routeBack: number[] = traceroute?.data.routeBack ?? [];
|
||||||
const snrTowards = traceroute?.data.snrTowards ?? [];
|
const snrTowards = traceroute?.data.snrTowards ?? [];
|
||||||
const snrBack = traceroute?.data.snrBack ?? [];
|
const snrBack = traceroute?.data.snrBack ?? [];
|
||||||
const from = nodes.get(traceroute?.from ?? 0);
|
const from = nodes.get(traceroute?.from ?? 0);
|
||||||
const longName =
|
const longName = from?.user?.longName ??
|
||||||
from?.user?.longName ??
|
|
||||||
(from ? `!${numberToHexUnpadded(from?.num)}` : "Unknown");
|
(from ? `!${numberToHexUnpadded(from?.num)}` : "Unknown");
|
||||||
const shortName =
|
const shortName = from?.user?.shortName ??
|
||||||
from?.user?.shortName ??
|
|
||||||
(from ? `${numberToHexUnpadded(from?.num).substring(0, 4)}` : "UNK");
|
(from ? `${numberToHexUnpadded(from?.num).substring(0, 4)}` : "UNK");
|
||||||
const to = nodes.get(traceroute?.to ?? 0);
|
const to = nodes.get(traceroute?.to ?? 0);
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ import {
|
|||||||
type SubmitHandler,
|
type SubmitHandler,
|
||||||
useForm,
|
useForm,
|
||||||
} from "react-hook-form";
|
} from "react-hook-form";
|
||||||
import { Heading } from "../UI/Typography/Heading";
|
import { Heading } from "@components/UI/Typography/Heading.tsx";
|
||||||
|
|
||||||
|
|
||||||
interface DisabledBy<T> {
|
interface DisabledBy<T> {
|
||||||
fieldName: Path<T>;
|
fieldName: Path<T>;
|
||||||
@@ -76,10 +77,11 @@ export function DynamicForm<T extends FieldValues>({
|
|||||||
const value = getValues(field.fieldName);
|
const value = getValues(field.fieldName);
|
||||||
if (value === "always") return true;
|
if (value === "always") return true;
|
||||||
if (typeof value === "boolean") return field.invert ? value : !value;
|
if (typeof value === "boolean") return field.invert ? value : !value;
|
||||||
if (typeof value === "number")
|
if (typeof value === "number") {
|
||||||
return field.invert
|
return field.invert
|
||||||
? field.selector !== value
|
? field.selector !== value
|
||||||
: field.selector === value;
|
: field.selector === value;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -87,11 +89,9 @@ export function DynamicForm<T extends FieldValues>({
|
|||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
className="space-y-8"
|
className="space-y-8"
|
||||||
{...(submitType === "onSubmit"
|
{...(submitType === "onSubmit" ? { onSubmit: handleSubmit(onSubmit) } : {
|
||||||
? { onSubmit: handleSubmit(onSubmit) }
|
onChange: handleSubmit(onSubmit),
|
||||||
: {
|
})}
|
||||||
onChange: handleSubmit(onSubmit),
|
|
||||||
})}
|
|
||||||
>
|
>
|
||||||
{fieldGroups.map((fieldGroup) => (
|
{fieldGroups.map((fieldGroup) => (
|
||||||
<div key={fieldGroup.label} className="space-y-8 sm:space-y-5">
|
<div key={fieldGroup.label} className="space-y-8 sm:space-y-5">
|
||||||
@@ -110,10 +110,8 @@ export function DynamicForm<T extends FieldValues>({
|
|||||||
label={field.label}
|
label={field.label}
|
||||||
fieldName={field.name}
|
fieldName={field.name}
|
||||||
description={field.description}
|
description={field.description}
|
||||||
valid={
|
valid={field.validationText === undefined ||
|
||||||
field.validationText === undefined ||
|
field.validationText === ""}
|
||||||
field.validationText === ""
|
|
||||||
}
|
|
||||||
validationText={field.validationText}
|
validationText={field.validationText}
|
||||||
>
|
>
|
||||||
<DynamicFormField
|
<DynamicFormField
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
type MultiSelectFieldProps,
|
type MultiSelectFieldProps,
|
||||||
MultiSelectInput,
|
MultiSelectInput,
|
||||||
} from "@app/components/Form/FormMultiSelect";
|
} from "./FormMultiSelect.tsx";
|
||||||
import {
|
import {
|
||||||
GenericInput,
|
GenericInput,
|
||||||
type InputFieldProps,
|
type InputFieldProps,
|
||||||
@@ -48,11 +48,19 @@ export function DynamicFormField<T extends FieldValues>({
|
|||||||
|
|
||||||
case "toggle":
|
case "toggle":
|
||||||
return (
|
return (
|
||||||
<ToggleInput field={field} control={control} disabled={disabled} />
|
<ToggleInput
|
||||||
|
field={field}
|
||||||
|
control={control}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
case "select":
|
case "select":
|
||||||
return (
|
return (
|
||||||
<SelectInput field={field} control={control} disabled={disabled} />
|
<SelectInput
|
||||||
|
field={field}
|
||||||
|
control={control}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
case "passwordGenerator":
|
case "passwordGenerator":
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -40,17 +40,15 @@ export function GenericInput<T extends FieldValues>({
|
|||||||
control={control}
|
control={control}
|
||||||
render={({ field: { value, onChange, ...rest } }) => (
|
render={({ field: { value, onChange, ...rest } }) => (
|
||||||
<Input
|
<Input
|
||||||
type={
|
type={field.type === "password" && passwordShown
|
||||||
field.type === "password" && passwordShown ? "text" : field.type
|
? "text"
|
||||||
}
|
: field.type}
|
||||||
action={
|
action={field.type === "password"
|
||||||
field.type === "password"
|
? {
|
||||||
? {
|
icon: passwordShown ? EyeOff : Eye,
|
||||||
icon: passwordShown ? EyeOff : Eye,
|
onClick: togglePasswordVisiblity,
|
||||||
onClick: togglePasswordVisiblity,
|
}
|
||||||
}
|
: undefined}
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
step={field.properties?.step}
|
step={field.properties?.step}
|
||||||
value={field.type === "number" ? Number.parseFloat(value) : value}
|
value={field.type === "number" ? Number.parseFloat(value) : value}
|
||||||
id={field.name}
|
id={field.name}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type {
|
|||||||
GenericFormElementProps,
|
GenericFormElementProps,
|
||||||
} from "@components/Form/DynamicForm.tsx";
|
} from "@components/Form/DynamicForm.tsx";
|
||||||
import type { FieldValues } from "react-hook-form";
|
import type { FieldValues } from "react-hook-form";
|
||||||
import { MultiSelect, MultiSelectItem } from "../UI/MultiSelect";
|
import { MultiSelect, MultiSelectItem } from "../UI/MultiSelect.tsx";
|
||||||
|
|
||||||
export interface MultiSelectFieldProps<T> extends BaseFormBuilderProps<T> {
|
export interface MultiSelectFieldProps<T> extends BaseFormBuilderProps<T> {
|
||||||
type: "multiSelect";
|
type: "multiSelect";
|
||||||
@@ -28,8 +28,8 @@ export function MultiSelectInput<T extends FieldValues>({
|
|||||||
// Make sure to filter out the UNSET value, as it shouldn't be shown in the UI
|
// Make sure to filter out the UNSET value, as it shouldn't be shown in the UI
|
||||||
const optionsEnumValues = enumValue
|
const optionsEnumValues = enumValue
|
||||||
? Object.entries(enumValue)
|
? Object.entries(enumValue)
|
||||||
.filter((value) => typeof value[1] === "number")
|
.filter((value) => typeof value[1] === "number")
|
||||||
.filter((value) => value[0] !== "UNSET")
|
.filter((value) => value[0] !== "UNSET")
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const formatName = (name: string) => {
|
const formatName = (name: string) => {
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import type {
|
|||||||
BaseFormBuilderProps,
|
BaseFormBuilderProps,
|
||||||
GenericFormElementProps,
|
GenericFormElementProps,
|
||||||
} from "@components/Form/DynamicForm.tsx";
|
} from "@components/Form/DynamicForm.tsx";
|
||||||
import type { ButtonVariant } from "@components/UI/Button";
|
import type { ButtonVariant } from "../UI/Button.tsx";
|
||||||
import { Generator } from "@components/UI/Generator.tsx";
|
import { Generator } from "@components/UI/Generator.tsx";
|
||||||
import { Eye, EyeOff } from "lucide-react";
|
import { Eye, EyeOff } from "lucide-react";
|
||||||
import type { ChangeEventHandler, MouseEventHandler } from "react";
|
import type { ChangeEventHandler } from "react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Controller, type FieldValues } from "react-hook-form";
|
import { Controller, type FieldValues } from "react-hook-form";
|
||||||
|
|
||||||
@@ -43,14 +43,12 @@ export function PasswordGenerator<T extends FieldValues>({
|
|||||||
<Generator
|
<Generator
|
||||||
type={field.hide && !passwordShown ? "password" : "text"}
|
type={field.hide && !passwordShown ? "password" : "text"}
|
||||||
id={field.id}
|
id={field.id}
|
||||||
action={
|
action={field.hide
|
||||||
field.hide
|
? {
|
||||||
? {
|
icon: passwordShown ? EyeOff : Eye,
|
||||||
icon: passwordShown ? EyeOff : Eye,
|
onClick: togglePasswordVisiblity,
|
||||||
onClick: togglePasswordVisiblity,
|
}
|
||||||
}
|
: undefined}
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
devicePSKBitCount={field.devicePSKBitCount}
|
devicePSKBitCount={field.devicePSKBitCount}
|
||||||
bits={field.bits}
|
bits={field.bits}
|
||||||
inputChange={field.inputChange}
|
inputChange={field.inputChange}
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ export function SelectInput<T extends FieldValues>({
|
|||||||
field.properties;
|
field.properties;
|
||||||
const optionsEnumValues = enumValue
|
const optionsEnumValues = enumValue
|
||||||
? Object.entries(enumValue).filter(
|
? Object.entries(enumValue).filter(
|
||||||
(value) => typeof value[1] === "number",
|
(value) => typeof value[1] === "number",
|
||||||
)
|
)
|
||||||
: [];
|
: [];
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
@@ -58,11 +58,13 @@ export function SelectInput<T extends FieldValues>({
|
|||||||
<SelectItem key={name} value={value.toString()}>
|
<SelectItem key={name} value={value.toString()}>
|
||||||
{formatEnumName
|
{formatEnumName
|
||||||
? name
|
? name
|
||||||
.replace(/_/g, " ")
|
.replace(/_/g, " ")
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.split(" ")
|
.split(" ")
|
||||||
.map((s) => s.charAt(0).toUpperCase() + s.substring(1))
|
.map((s) =>
|
||||||
.join(" ")
|
s.charAt(0).toUpperCase() + s.substring(1)
|
||||||
|
)
|
||||||
|
.join(" ")
|
||||||
: name}
|
: name}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useBackupReminder } from "@app/core/hooks/useKeyBackupReminder";
|
import { useBackupReminder } from "@core/hooks/useKeyBackupReminder.tsx";
|
||||||
import { useDevice } from "@app/core/stores/deviceStore";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
|
|
||||||
export const KeyBackupReminder = (): JSX.Element => {
|
export const KeyBackupReminder = () => {
|
||||||
const { setDialogOpen } = useDevice();
|
const { setDialogOpen } = useDevice();
|
||||||
|
|
||||||
useBackupReminder({
|
useBackupReminder({
|
||||||
@@ -15,5 +15,6 @@ export const KeyBackupReminder = (): JSX.Element => {
|
|||||||
sameSite: "strict",
|
sameSite: "strict",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
// deno-lint-ignore jsx-no-useless-fragment
|
||||||
return <></>;
|
return <></>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ import { Protobuf } from "@meshtastic/core";
|
|||||||
import { fromByteArray, toByteArray } from "base64-js";
|
import { fromByteArray, toByteArray } from "base64-js";
|
||||||
import cryptoRandomString from "crypto-random-string";
|
import cryptoRandomString from "crypto-random-string";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { PkiRegenerateDialog } from "../Dialog/PkiRegenerateDialog";
|
import { PkiRegenerateDialog } from "../Dialog/PkiRegenerateDialog.tsx";
|
||||||
|
|
||||||
export interface SettingsPanelProps {
|
export interface SettingsPanelProps {
|
||||||
channel: Protobuf.Channel.Channel;
|
channel: Protobuf.Channel.Channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
|
export const Channel = ({ channel }: SettingsPanelProps) => {
|
||||||
const { config, connection, addChannel } = useDevice();
|
const { config, connection, addChannel } = useDevice();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
@@ -24,8 +24,9 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
|
|||||||
channel?.settings?.psk.length ?? 16,
|
channel?.settings?.psk.length ?? 16,
|
||||||
);
|
);
|
||||||
const [validationText, setValidationText] = useState<string>();
|
const [validationText, setValidationText] = useState<string>();
|
||||||
const [preSharedDialogOpen, setPreSharedDialogOpen] =
|
const [preSharedDialogOpen, setPreSharedDialogOpen] = useState<boolean>(
|
||||||
useState<boolean>(false);
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
const onSubmit = (data: ChannelValidation) => {
|
const onSubmit = (data: ChannelValidation) => {
|
||||||
const channel = create(Protobuf.Channel.ChannelSchema, {
|
const channel = create(Protobuf.Channel.ChannelSchema, {
|
||||||
@@ -92,7 +93,7 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
|
|||||||
<DynamicForm<ChannelValidation>
|
<DynamicForm<ChannelValidation>
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
submitType="onSubmit"
|
submitType="onSubmit"
|
||||||
hasSubmitButton={true}
|
hasSubmitButton
|
||||||
defaultValues={{
|
defaultValues={{
|
||||||
...channel,
|
...channel,
|
||||||
...{
|
...{
|
||||||
@@ -107,7 +108,7 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
|
|||||||
channel?.settings?.moduleSettings?.positionPrecision === 32,
|
channel?.settings?.moduleSettings?.positionPrecision === 32,
|
||||||
positionPrecision:
|
positionPrecision:
|
||||||
channel?.settings?.moduleSettings?.positionPrecision ===
|
channel?.settings?.moduleSettings?.positionPrecision ===
|
||||||
undefined
|
undefined
|
||||||
? 10
|
? 10
|
||||||
: channel?.settings?.moduleSettings?.positionPrecision,
|
: channel?.settings?.moduleSettings?.positionPrecision,
|
||||||
},
|
},
|
||||||
@@ -126,10 +127,9 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
|
|||||||
description:
|
description:
|
||||||
"Device telemetry is sent over PRIMARY. Only one PRIMARY allowed",
|
"Device telemetry is sent over PRIMARY. Only one PRIMARY allowed",
|
||||||
properties: {
|
properties: {
|
||||||
enumValue:
|
enumValue: channel.index === 0
|
||||||
channel.index === 0
|
? { PRIMARY: 1 }
|
||||||
? { PRIMARY: 1 }
|
: { DISABLED: 0, SECONDARY: 2 },
|
||||||
: { DISABLED: 0, SECONDARY: 2 },
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -192,32 +192,31 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
|
|||||||
description:
|
description:
|
||||||
"If not sharing precise location, position shared on channel will be accurate within this distance",
|
"If not sharing precise location, position shared on channel will be accurate within this distance",
|
||||||
properties: {
|
properties: {
|
||||||
enumValue:
|
enumValue: config.display?.units === 0
|
||||||
config.display?.units === 0
|
? {
|
||||||
? {
|
"Within 23 km": 10,
|
||||||
"Within 23 km": 10,
|
"Within 12 km": 11,
|
||||||
"Within 12 km": 11,
|
"Within 5.8 km": 12,
|
||||||
"Within 5.8 km": 12,
|
"Within 2.9 km": 13,
|
||||||
"Within 2.9 km": 13,
|
"Within 1.5 km": 14,
|
||||||
"Within 1.5 km": 14,
|
"Within 700 m": 15,
|
||||||
"Within 700 m": 15,
|
"Within 350 m": 16,
|
||||||
"Within 350 m": 16,
|
"Within 200 m": 17,
|
||||||
"Within 200 m": 17,
|
"Within 90 m": 18,
|
||||||
"Within 90 m": 18,
|
"Within 50 m": 19,
|
||||||
"Within 50 m": 19,
|
}
|
||||||
}
|
: {
|
||||||
: {
|
"Within 15 miles": 10,
|
||||||
"Within 15 miles": 10,
|
"Within 7.3 miles": 11,
|
||||||
"Within 7.3 miles": 11,
|
"Within 3.6 miles": 12,
|
||||||
"Within 3.6 miles": 12,
|
"Within 1.8 miles": 13,
|
||||||
"Within 1.8 miles": 13,
|
"Within 0.9 miles": 14,
|
||||||
"Within 0.9 miles": 14,
|
"Within 0.5 miles": 15,
|
||||||
"Within 0.5 miles": 15,
|
"Within 0.2 miles": 16,
|
||||||
"Within 0.2 miles": 16,
|
"Within 600 feet": 17,
|
||||||
"Within 600 feet": 17,
|
"Within 300 feet": 18,
|
||||||
"Within 300 feet": 18,
|
"Within 150 feet": 19,
|
||||||
"Within 150 feet": 19,
|
},
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useAppStore } from "@app/core/stores/appStore";
|
import { useAppStore } from "../../../core/stores/appStore.ts";
|
||||||
import type { BluetoothValidation } from "@app/validation/config/bluetooth.tsx";
|
import type { BluetoothValidation } from "@app/validation/config/bluetooth.tsx";
|
||||||
import { create } from "@bufbuild/protobuf";
|
import { create } from "@bufbuild/protobuf";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
@@ -113,9 +113,8 @@ export const Bluetooth = () => {
|
|||||||
disabledBy: [
|
disabledBy: [
|
||||||
{
|
{
|
||||||
fieldName: "mode",
|
fieldName: "mode",
|
||||||
selector:
|
selector: Protobuf.Config.Config_BluetoothConfig_PairingMode
|
||||||
Protobuf.Config.Config_BluetoothConfig_PairingMode
|
.FIXED_PIN,
|
||||||
.FIXED_PIN,
|
|
||||||
invert: true,
|
invert: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
|||||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/core";
|
import { Protobuf } from "@meshtastic/core";
|
||||||
|
|
||||||
export const Device = (): JSX.Element => {
|
export const Device = () => {
|
||||||
const { config, setWorkingConfig } = useDevice();
|
const { config, setWorkingConfig } = useDevice();
|
||||||
|
|
||||||
const onSubmit = (data: DeviceValidation) => {
|
const onSubmit = (data: DeviceValidation) => {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
|||||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/core";
|
import { Protobuf } from "@meshtastic/core";
|
||||||
|
|
||||||
export const Display = (): JSX.Element => {
|
export const Display = () => {
|
||||||
const { config, setWorkingConfig } = useDevice();
|
const { config, setWorkingConfig } = useDevice();
|
||||||
|
|
||||||
const onSubmit = (data: DisplayValidation) => {
|
const onSubmit = (data: DisplayValidation) => {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
|||||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/core";
|
import { Protobuf } from "@meshtastic/core";
|
||||||
|
|
||||||
export const LoRa = (): JSX.Element => {
|
export const LoRa = () => {
|
||||||
const { config, setWorkingConfig } = useDevice();
|
const { config, setWorkingConfig } = useDevice();
|
||||||
|
|
||||||
const onSubmit = (data: LoRaValidation) => {
|
const onSubmit = (data: LoRaValidation) => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
type FlagName,
|
type FlagName,
|
||||||
usePositionFlags,
|
usePositionFlags,
|
||||||
} from "@app/core/hooks/usePositionFlags";
|
} from "../../../core/hooks/usePositionFlags.ts";
|
||||||
import type { PositionValidation } from "@app/validation/config/position.tsx";
|
import type { PositionValidation } from "@app/validation/config/position.tsx";
|
||||||
import { create } from "@bufbuild/protobuf";
|
import { create } from "@bufbuild/protobuf";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
|||||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/core";
|
import { Protobuf } from "@meshtastic/core";
|
||||||
|
|
||||||
export const Power = (): JSX.Element => {
|
export const Power = () => {
|
||||||
const { config, setWorkingConfig } = useDevice();
|
const { config, setWorkingConfig } = useDevice();
|
||||||
|
|
||||||
const onSubmit = (data: PowerValidation) => {
|
const onSubmit = (data: PowerValidation) => {
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import { PkiRegenerateDialog } from "@app/components/Dialog/PkiRegenerateDialog";
|
import { PkiRegenerateDialog } from "@components/Dialog/PkiRegenerateDialog.tsx";
|
||||||
import { DynamicForm } from "@app/components/Form/DynamicForm.tsx";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { useAppStore } from "@app/core/stores/appStore";
|
import { useAppStore } from "@core/stores/appStore.ts";
|
||||||
import {
|
import {
|
||||||
getX25519PrivateKey,
|
getX25519PrivateKey,
|
||||||
getX25519PublicKey,
|
getX25519PublicKey,
|
||||||
} from "@app/core/utils/x25519";
|
} from "@core/utils/x25519.ts";
|
||||||
import type { SecurityValidation } from "@app/validation/config/security.tsx";
|
import type { SecurityValidation } from "@app/validation/config/security.ts";
|
||||||
import { create } from "@bufbuild/protobuf";
|
import { create } from "@bufbuild/protobuf";
|
||||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/core";
|
import { Protobuf } from "@meshtastic/core";
|
||||||
import { fromByteArray, toByteArray } from "base64-js";
|
import { fromByteArray, toByteArray } from "base64-js";
|
||||||
import { Eye, EyeOff } from "lucide-react";
|
import { Eye, EyeOff } from "lucide-react";
|
||||||
import { useReducer } from "react";
|
import { useReducer } from "react";
|
||||||
import { securityReducer } from "./securityReducer";
|
import { securityReducer } from "@components/PageComponents/Config/Security/securityReducer.tsx";
|
||||||
|
|
||||||
export const Security = () => {
|
export const Security = () => {
|
||||||
const { config, setWorkingConfig, setDialogOpen } = useDevice();
|
const { config, setWorkingConfig, setDialogOpen } = useDevice();
|
||||||
@@ -58,8 +58,7 @@ export const Security = () => {
|
|||||||
if (input.length % 4 !== 0) {
|
if (input.length % 4 !== 0) {
|
||||||
addError(
|
addError(
|
||||||
fieldName,
|
fieldName,
|
||||||
`${
|
`${fieldName === "privateKey" ? "Private" : "Admin"
|
||||||
fieldName === "privateKey" ? "Private" : "Admin"
|
|
||||||
} Key is required to be a 256 bit pre-shared key (PSK)`,
|
} Key is required to be a 256 bit pre-shared key (PSK)`,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
@@ -74,8 +73,7 @@ export const Security = () => {
|
|||||||
console.error(e);
|
console.error(e);
|
||||||
addError(
|
addError(
|
||||||
fieldName,
|
fieldName,
|
||||||
`Invalid ${
|
`Invalid ${fieldName === "privateKey" ? "Private" : "Admin"
|
||||||
fieldName === "privateKey" ? "Private" : "Admin"
|
|
||||||
} Key format`,
|
} Key format`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -85,8 +83,6 @@ export const Security = () => {
|
|||||||
if (hasErrors()) {
|
if (hasErrors()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(toByteArray(state.adminKey));
|
|
||||||
|
|
||||||
setWorkingConfig(
|
setWorkingConfig(
|
||||||
create(Protobuf.Config.ConfigSchema, {
|
create(Protobuf.Config.ConfigSchema, {
|
||||||
payloadVariant: {
|
payloadVariant: {
|
||||||
@@ -248,7 +244,7 @@ export const Security = () => {
|
|||||||
? getErrorMessage("adminKey")
|
? getErrorMessage("adminKey")
|
||||||
: "",
|
: "",
|
||||||
inputChange: adminKeyInputChangeEvent,
|
inputChange: adminKeyInputChangeEvent,
|
||||||
selectChange: () => {},
|
selectChange: () => { },
|
||||||
bits: [{ text: "256 bit", value: "32", key: "bit256" }],
|
bits: [{ text: "256 bit", value: "32", key: "bit256" }],
|
||||||
devicePSKBitCount: state.privateKeyBitCount,
|
devicePSKBitCount: state.privateKeyBitCount,
|
||||||
hide: !state.adminKeyVisible,
|
hide: !state.adminKeyVisible,
|
||||||
@@ -308,8 +304,7 @@ export const Security = () => {
|
|||||||
<PkiRegenerateDialog
|
<PkiRegenerateDialog
|
||||||
open={state.privateKeyDialogOpen}
|
open={state.privateKeyDialogOpen}
|
||||||
onOpenChange={() =>
|
onOpenChange={() =>
|
||||||
dispatch({ type: "SHOW_PRIVATE_KEY_DIALOG", payload: false })
|
dispatch({ type: "SHOW_PRIVATE_KEY_DIALOG", payload: false })}
|
||||||
}
|
|
||||||
onSubmit={pkiRegenerate}
|
onSubmit={pkiRegenerate}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SecurityAction, SecurityState } from "./types";
|
import type { SecurityAction, SecurityState } from "./types.ts";
|
||||||
|
|
||||||
export function securityReducer(
|
export function securityReducer(
|
||||||
state: SecurityState,
|
state: SecurityState,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export type SecurityAction =
|
|||||||
| { type: "SET_ADMIN_KEY"; payload: string }
|
| { type: "SET_ADMIN_KEY"; payload: string }
|
||||||
| { type: "SHOW_PRIVATE_KEY_DIALOG"; payload: boolean }
|
| { type: "SHOW_PRIVATE_KEY_DIALOG"; payload: boolean }
|
||||||
| {
|
| {
|
||||||
type: "REGENERATE_PRIV_PUB_KEY";
|
type: "REGENERATE_PRIV_PUB_KEY";
|
||||||
payload: { privateKey: string; publicKey: string };
|
payload: { privateKey: string; publicKey: string };
|
||||||
}
|
}
|
||||||
| { type: "REGENERATE_ADMIN_KEY"; payload: { adminKey: string } };
|
| { type: "REGENERATE_ADMIN_KEY"; payload: { adminKey: string } };
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { TabElementProps } from "@app/components/Dialog/NewDeviceDialog";
|
import type { TabElementProps } from "../../Dialog/NewDeviceDialog.tsx";
|
||||||
import { Button } from "@components/UI/Button.tsx";
|
import { Button } from "@components/UI/Button.tsx";
|
||||||
import { Mono } from "@components/generic/Mono.tsx";
|
import { Mono } from "@components/generic/Mono.tsx";
|
||||||
import { useAppStore } from "@core/stores/appStore.ts";
|
import { useAppStore } from "@core/stores/appStore.ts";
|
||||||
@@ -8,7 +8,7 @@ import { randId } from "@core/utils/randId.ts";
|
|||||||
import { BleConnection, Constants } from "@meshtastic/js";
|
import { BleConnection, Constants } from "@meshtastic/js";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
export const BLE = ({ closeDialog }: TabElementProps): JSX.Element => {
|
export const BLE = ({ closeDialog }: TabElementProps) => {
|
||||||
const [bleDevices, setBleDevices] = useState<BluetoothDevice[]>([]);
|
const [bleDevices, setBleDevices] = useState<BluetoothDevice[]>([]);
|
||||||
const { addDevice } = useDeviceStore();
|
const { addDevice } = useDeviceStore();
|
||||||
const { setSelectedDevice } = useAppStore();
|
const { setSelectedDevice } = useAppStore();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { TabElementProps } from "@app/components/Dialog/NewDeviceDialog";
|
import type { TabElementProps } from "@components/Dialog/NewDeviceDialog.tsx";
|
||||||
import { Button } from "@components/UI/Button.tsx";
|
import { Button } from "@components/UI/Button.tsx";
|
||||||
import { Input } from "@components/UI/Input.tsx";
|
import { Input } from "@components/UI/Input.tsx";
|
||||||
import { Label } from "@components/UI/Label.tsx";
|
import { Label } from "@components/UI/Label.tsx";
|
||||||
@@ -9,23 +9,23 @@ import { subscribeAll } from "@core/subscriptions.ts";
|
|||||||
import { randId } from "@core/utils/randId.ts";
|
import { randId } from "@core/utils/randId.ts";
|
||||||
import { MeshDevice } from "@meshtastic/core";
|
import { MeshDevice } from "@meshtastic/core";
|
||||||
import { TransportHTTP } from "@meshtastic/transport-http";
|
import { TransportHTTP } from "@meshtastic/transport-http";
|
||||||
import { type JSX, useState } from "react";
|
import { useState } from "react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
|
||||||
export const HTTP = ({ closeDialog }: TabElementProps): JSX.Element => {
|
export const HTTP = ({ closeDialog }: TabElementProps) => {
|
||||||
const [https, setHTTPS] = useState(false);
|
const [https, setHTTPS] = useState(false);
|
||||||
const { addDevice } = useDeviceStore();
|
const { addDevice } = useDeviceStore();
|
||||||
const { setSelectedDevice } = useAppStore();
|
const { setSelectedDevice } = useAppStore();
|
||||||
const { register, handleSubmit, control, watch } = useForm<{
|
const { register, handleSubmit, control } = useForm<{
|
||||||
ip: string;
|
ip: string;
|
||||||
tls: boolean;
|
tls: boolean;
|
||||||
}>({
|
}>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
ip: ["client.meshtastic.org", "localhost"].includes(
|
ip: ["client.meshtastic.org", "localhost"].includes(
|
||||||
window.location.hostname,
|
globalThis.location.hostname,
|
||||||
)
|
)
|
||||||
? "meshtastic.local"
|
? "meshtastic.local"
|
||||||
: window.location.host,
|
: globalThis.location.host,
|
||||||
tls: location.protocol === "https:",
|
tls: location.protocol === "https:",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -61,16 +61,15 @@ export const HTTP = ({ closeDialog }: TabElementProps): JSX.Element => {
|
|||||||
<Controller
|
<Controller
|
||||||
name="tls"
|
name="tls"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { value, onChange, ...rest } }) => (
|
render={({ field: { ...rest } }) => (
|
||||||
<>
|
<>
|
||||||
<Label>Use HTTPS</Label>
|
<Label>Use HTTPS</Label>
|
||||||
<Switch
|
<Switch
|
||||||
onCheckedChange={(checked) => {
|
onCheckedChange={(checked: boolean) => {
|
||||||
checked ? setHTTPS(true) : setHTTPS(false);
|
checked ? setHTTPS(true) : setHTTPS(false);
|
||||||
}}
|
}}
|
||||||
disabled={
|
disabled={location.protocol === "https:" ||
|
||||||
location.protocol === "https:" || connectionInProgress
|
connectionInProgress}
|
||||||
}
|
|
||||||
checked={https}
|
checked={https}
|
||||||
{...rest}
|
{...rest}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { TabElementProps } from "@app/components/Dialog/NewDeviceDialog";
|
import type { TabElementProps } from "../../Dialog/NewDeviceDialog.tsx";
|
||||||
import { Button } from "@components/UI/Button.tsx";
|
import { Button } from "@components/UI/Button.tsx";
|
||||||
import { Mono } from "@components/generic/Mono.tsx";
|
import { Mono } from "@components/generic/Mono.tsx";
|
||||||
import { useAppStore } from "@core/stores/appStore.ts";
|
import { useAppStore } from "@core/stores/appStore.ts";
|
||||||
@@ -9,7 +9,7 @@ import { MeshDevice } from "@meshtastic/core";
|
|||||||
import { TransportWebSerial } from "@meshtastic/transport-web-serial";
|
import { TransportWebSerial } from "@meshtastic/transport-web-serial";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
export const Serial = ({ closeDialog }: TabElementProps): JSX.Element => {
|
export const Serial = ({ closeDialog }: TabElementProps) => {
|
||||||
const [serialPorts, setSerialPorts] = useState<SerialPort[]>([]);
|
const [serialPorts, setSerialPorts] = useState<SerialPort[]>([]);
|
||||||
const { addDevice } = useDeviceStore();
|
const { addDevice } = useDeviceStore();
|
||||||
const { setSelectedDevice } = useAppStore();
|
const { setSelectedDevice } = useAppStore();
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Separator } from "@app/components/UI/Seperator";
|
import { Separator } from "@components/UI/Seperator.tsx";
|
||||||
import { Heading } from "@app/components/UI/Typography/Heading";
|
import { Heading } from "@components/UI/Typography/Heading.tsx";
|
||||||
import { Subtle } from "@app/components/UI/Typography/Subtle.tsx";
|
import { Subtle } from "@components/UI/Typography/Subtle.tsx";
|
||||||
import { formatQuantity } from "@app/core/utils/string";
|
import { formatQuantity } from "@core/utils/string.ts";
|
||||||
import { Avatar } from "@components/UI/Avatar";
|
import { Avatar } from "@components/UI/Avatar.tsx";
|
||||||
import { Mono } from "@components/generic/Mono.tsx";
|
import { Mono } from "@components/generic/Mono.tsx";
|
||||||
import { TimeAgo } from "@components/generic/TimeAgo.tsx";
|
import { TimeAgo } from "@components/generic/TimeAgo.tsx";
|
||||||
import { Protobuf } from "@meshtastic/core";
|
import { Protobuf } from "@meshtastic/core";
|
||||||
@@ -34,24 +34,26 @@ export const NodeDetail = ({ node }: NodeDetailProps) => {
|
|||||||
<div className="dark:text-slate-900 p-1">
|
<div className="dark:text-slate-900 p-1">
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<div className="flex flex-col items-center gap-2 min-w-6 pt-1">
|
<div className="flex flex-col items-center gap-2 min-w-6 pt-1">
|
||||||
<Avatar text={node.user?.shortName} />
|
<Avatar text={node.user?.shortName ?? "UNK"} />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
{node.user?.publicKey && node.user?.publicKey.length > 0 ? (
|
{node.user?.publicKey && node.user?.publicKey.length > 0
|
||||||
<LockIcon
|
? (
|
||||||
className="text-green-600"
|
<LockIcon
|
||||||
size={12}
|
className="text-green-600"
|
||||||
strokeWidth={3}
|
size={12}
|
||||||
aria-label="Public Key Enabled"
|
strokeWidth={3}
|
||||||
/>
|
aria-label="Public Key Enabled"
|
||||||
) : (
|
/>
|
||||||
<LockOpenIcon
|
)
|
||||||
className="text-yellow-500"
|
: (
|
||||||
size={12}
|
<LockOpenIcon
|
||||||
strokeWidth={3}
|
className="text-yellow-500"
|
||||||
aria-label="No Public Key"
|
size={12}
|
||||||
/>
|
strokeWidth={3}
|
||||||
)}
|
aria-label="No Public Key"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Star
|
<Star
|
||||||
@@ -69,19 +71,16 @@ export const NodeDetail = ({ node }: NodeDetailProps) => {
|
|||||||
{!!node.deviceMetrics?.batteryLevel && (
|
{!!node.deviceMetrics?.batteryLevel && (
|
||||||
<div
|
<div
|
||||||
className="flex items-center gap-1"
|
className="flex items-center gap-1"
|
||||||
title={`${
|
title={`${node.deviceMetrics?.voltage?.toPrecision(3) ?? "Unknown"
|
||||||
node.deviceMetrics?.voltage?.toPrecision(3) ?? "Unknown"
|
} volts`}
|
||||||
} volts`}
|
|
||||||
>
|
>
|
||||||
{node.deviceMetrics?.batteryLevel > 100 ? (
|
{node.deviceMetrics?.batteryLevel > 100
|
||||||
<BatteryChargingIcon size={22} />
|
? <BatteryChargingIcon size={22} />
|
||||||
) : node.deviceMetrics?.batteryLevel > 80 ? (
|
: node.deviceMetrics?.batteryLevel > 80
|
||||||
<BatteryFullIcon size={22} />
|
? <BatteryFullIcon size={22} />
|
||||||
) : node.deviceMetrics?.batteryLevel > 20 ? (
|
: node.deviceMetrics?.batteryLevel > 20
|
||||||
<BatteryMediumIcon size={22} />
|
? <BatteryMediumIcon size={22} />
|
||||||
) : (
|
: <BatteryLowIcon size={22} />}
|
||||||
<BatteryLowIcon size={22} />
|
|
||||||
)}
|
|
||||||
<Subtle aria-label="Battery">
|
<Subtle aria-label="Battery">
|
||||||
{node.deviceMetrics?.batteryLevel > 100
|
{node.deviceMetrics?.batteryLevel > 100
|
||||||
? "Charging"
|
? "Charging"
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
import {
|
import { type MessageWithState, useDevice } from "@core/stores/deviceStore.ts";
|
||||||
type MessageWithState,
|
|
||||||
useDevice,
|
|
||||||
} from "@app/core/stores/deviceStore.ts";
|
|
||||||
import { Message } from "@components/PageComponents/Messages/Message.tsx";
|
import { Message } from "@components/PageComponents/Messages/Message.tsx";
|
||||||
import { MessageInput } from "@components/PageComponents/Messages/MessageInput.tsx";
|
import { MessageInput } from "@components/PageComponents/Messages/MessageInput.tsx";
|
||||||
import type { Types } from "@meshtastic/core";
|
import type { Types } from "@meshtastic/core";
|
||||||
import { InboxIcon } from "lucide-react";
|
import { InboxIcon } from "lucide-react";
|
||||||
import { useCallback, useEffect, useRef } from "react";
|
import { useCallback, useEffect, useRef } from "react";
|
||||||
import type { JSX } from "react";
|
|
||||||
|
|
||||||
export interface ChannelChatProps {
|
export interface ChannelChatProps {
|
||||||
messages?: MessageWithState[];
|
messages?: MessageWithState[];
|
||||||
@@ -26,7 +22,7 @@ export const ChannelChat = ({
|
|||||||
messages,
|
messages,
|
||||||
channel,
|
channel,
|
||||||
to,
|
to,
|
||||||
}: ChannelChatProps): JSX.Element => {
|
}: ChannelChatProps) => {
|
||||||
const { nodes } = useDevice();
|
const { nodes } = useDevice();
|
||||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||||
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
||||||
@@ -34,8 +30,7 @@ export const ChannelChat = ({
|
|||||||
const scrollToBottom = useCallback(() => {
|
const scrollToBottom = useCallback(() => {
|
||||||
const scrollContainer = scrollContainerRef.current;
|
const scrollContainer = scrollContainerRef.current;
|
||||||
if (scrollContainer) {
|
if (scrollContainer) {
|
||||||
const isNearBottom =
|
const isNearBottom = scrollContainer.scrollHeight -
|
||||||
scrollContainer.scrollHeight -
|
|
||||||
scrollContainer.scrollTop -
|
scrollContainer.scrollTop -
|
||||||
scrollContainer.clientHeight <
|
scrollContainer.clientHeight <
|
||||||
100;
|
100;
|
||||||
@@ -72,9 +67,8 @@ export const ChannelChat = ({
|
|||||||
key={message.id}
|
key={message.id}
|
||||||
message={message}
|
message={message}
|
||||||
sender={nodes.get(message.from)}
|
sender={nodes.get(message.from)}
|
||||||
lastMsgSameUser={
|
lastMsgSameUser={index > 0 &&
|
||||||
index > 0 && messages[index - 1].from === message.from
|
messages[index - 1].from === message.from}
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import {
|
|||||||
TooltipContent,
|
TooltipContent,
|
||||||
TooltipProvider,
|
TooltipProvider,
|
||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from "@app/components/UI/Tooltip";
|
} from "@components/UI/Tooltip.tsx";
|
||||||
import {
|
import {
|
||||||
type MessageWithState,
|
type MessageWithState,
|
||||||
useDeviceStore,
|
useDeviceStore,
|
||||||
} from "@app/core/stores/deviceStore.ts";
|
} from "@core/stores/deviceStore.ts";
|
||||||
import { cn } from "@app/core/utils/cn";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
import { Avatar } from "@components/UI/Avatar";
|
import { Avatar } from "@components/UI/Avatar.tsx";
|
||||||
import type { Protobuf } from "@meshtastic/core";
|
import type { Protobuf } from "@meshtastic/core";
|
||||||
import { AlertCircle, CheckCircle2, CircleEllipsis } from "lucide-react";
|
import { AlertCircle, CheckCircle2, CircleEllipsis } from "lucide-react";
|
||||||
import type { LucideIcon } from "lucide-react";
|
import type { LucideIcon } from "lucide-react";
|
||||||
@@ -44,13 +44,13 @@ const STATUS_TEXT_MAP: Record<MessageState, string> = {
|
|||||||
[MESSAGE_STATES.ACK]: "Message delivered",
|
[MESSAGE_STATES.ACK]: "Message delivered",
|
||||||
[MESSAGE_STATES.WAITING]: "Waiting for delivery",
|
[MESSAGE_STATES.WAITING]: "Waiting for delivery",
|
||||||
[MESSAGE_STATES.FAILED]: "Delivery failed",
|
[MESSAGE_STATES.FAILED]: "Delivery failed",
|
||||||
} as const;
|
};
|
||||||
|
|
||||||
const STATUS_ICON_MAP: Record<MessageState, LucideIcon> = {
|
const STATUS_ICON_MAP: Record<MessageState, LucideIcon> = {
|
||||||
[MESSAGE_STATES.ACK]: CheckCircle2,
|
[MESSAGE_STATES.ACK]: CheckCircle2,
|
||||||
[MESSAGE_STATES.WAITING]: CircleEllipsis,
|
[MESSAGE_STATES.WAITING]: CircleEllipsis,
|
||||||
[MESSAGE_STATES.FAILED]: AlertCircle,
|
[MESSAGE_STATES.FAILED]: AlertCircle,
|
||||||
} as const;
|
};
|
||||||
|
|
||||||
const getStatusText = (state: MessageState): string => STATUS_TEXT_MAP[state];
|
const getStatusText = (state: MessageState): string => STATUS_TEXT_MAP[state];
|
||||||
|
|
||||||
@@ -93,7 +93,6 @@ const StatusIcon = ({ state, className, ...otherProps }: StatusIconProps) => {
|
|||||||
const getMessageTextStyles = (state: MessageState) => {
|
const getMessageTextStyles = (state: MessageState) => {
|
||||||
const isAcknowledged = state === MESSAGE_STATES.ACK;
|
const isAcknowledged = state === MESSAGE_STATES.ACK;
|
||||||
const isFailed = state === MESSAGE_STATES.FAILED;
|
const isFailed = state === MESSAGE_STATES.FAILED;
|
||||||
const isWaiting = state === MESSAGE_STATES.WAITING;
|
|
||||||
|
|
||||||
return cn(
|
return cn(
|
||||||
"break-words overflow-hidden",
|
"break-words overflow-hidden",
|
||||||
@@ -144,16 +143,18 @@ export const Message = ({ lastMsgSameUser, message, sender }: MessageProps) => {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
{!lastMsgSameUser ? (
|
{!lastMsgSameUser
|
||||||
<div className="flex place-items-center gap-2 mb-1">
|
? (
|
||||||
<Avatar text={messageUser?.shortName} />
|
<div className="flex place-items-center gap-2 mb-1">
|
||||||
<div className="flex flex-col">
|
<Avatar text={messageUser?.shortName ?? "UNK"} />
|
||||||
<span className="font-medium text-slate-900 dark:text-white truncate">
|
<div className="flex flex-col">
|
||||||
{messageUser?.longName}
|
<span className="font-medium text-slate-900 dark:text-white truncate">
|
||||||
</span>
|
{messageUser?.longName}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)
|
||||||
) : null}
|
: null}
|
||||||
</div>
|
</div>
|
||||||
<TimeDisplay date={message.rxTime} />
|
<TimeDisplay date={message.rxTime} />
|
||||||
<div className="flex place-items-center gap-2 pb-2">
|
<div className="flex place-items-center gap-2 pb-2">
|
||||||
|
|||||||
@@ -1,16 +1,10 @@
|
|||||||
import { debounce } from "@app/core/utils/debounce";
|
import { debounce } from "../../../core/utils/debounce.ts";
|
||||||
import { Button } from "@components/UI/Button.tsx";
|
import { Button } from "@components/UI/Button.tsx";
|
||||||
import { Input } from "@components/UI/Input.tsx";
|
import { Input } from "@components/UI/Input.tsx";
|
||||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import type { Types } from "@meshtastic/core";
|
import type { Types } from "@meshtastic/core";
|
||||||
import { SendIcon } from "lucide-react";
|
import { SendIcon } from "lucide-react";
|
||||||
import {
|
import { startTransition, useCallback, useMemo, useState } from "react";
|
||||||
type JSX,
|
|
||||||
startTransition,
|
|
||||||
useCallback,
|
|
||||||
useMemo,
|
|
||||||
useState,
|
|
||||||
} from "react";
|
|
||||||
|
|
||||||
export interface MessageInputProps {
|
export interface MessageInputProps {
|
||||||
to: Types.Destination;
|
to: Types.Destination;
|
||||||
@@ -22,7 +16,7 @@ export const MessageInput = ({
|
|||||||
to,
|
to,
|
||||||
channel,
|
channel,
|
||||||
maxBytes,
|
maxBytes,
|
||||||
}: MessageInputProps): JSX.Element => {
|
}: MessageInputProps) => {
|
||||||
const {
|
const {
|
||||||
connection,
|
connection,
|
||||||
setMessageState,
|
setMessageState,
|
||||||
@@ -43,7 +37,7 @@ export const MessageInput = ({
|
|||||||
async (message: string) => {
|
async (message: string) => {
|
||||||
await connection
|
await connection
|
||||||
?.sendText(message, to, true, channel)
|
?.sendText(message, to, true, channel)
|
||||||
.then((id) =>
|
.then((id: number) =>
|
||||||
setMessageState(
|
setMessageState(
|
||||||
to === "broadcast" ? "broadcast" : "direct",
|
to === "broadcast" ? "broadcast" : "direct",
|
||||||
channel,
|
channel,
|
||||||
@@ -51,7 +45,7 @@ export const MessageInput = ({
|
|||||||
myNodeNum,
|
myNodeNum,
|
||||||
id,
|
id,
|
||||||
"ack",
|
"ack",
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
.catch((e: Types.PacketError) =>
|
.catch((e: Types.PacketError) =>
|
||||||
setMessageState(
|
setMessageState(
|
||||||
@@ -61,7 +55,7 @@ export const MessageInput = ({
|
|||||||
myNodeNum,
|
myNodeNum,
|
||||||
e.id,
|
e.id,
|
||||||
e.error,
|
e.error,
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[channel, connection, myNodeNum, setMessageState, to],
|
[channel, connection, myNodeNum, setMessageState, to],
|
||||||
@@ -82,7 +76,7 @@ export const MessageInput = ({
|
|||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<form
|
<form
|
||||||
className="w-full"
|
className="w-full"
|
||||||
action={async (formData: FormData) => {
|
action={(formData: FormData) => {
|
||||||
// prevent user from sending blank/empty message
|
// prevent user from sending blank/empty message
|
||||||
if (localDraft === "") return;
|
if (localDraft === "") return;
|
||||||
const message = formData.get("messageInput") as string;
|
const message = formData.get("messageInput") as string;
|
||||||
@@ -97,7 +91,7 @@ export const MessageInput = ({
|
|||||||
<div className="flex grow gap-2">
|
<div className="flex grow gap-2">
|
||||||
<span className="w-full">
|
<span className="w-full">
|
||||||
<Input
|
<Input
|
||||||
autoFocus={true}
|
autoFocus
|
||||||
minLength={1}
|
minLength={1}
|
||||||
name="messageInput"
|
name="messageInput"
|
||||||
placeholder="Enter Message"
|
placeholder="Enter Message"
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { useDevice } from "@app/core/stores/deviceStore.ts";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import type { Protobuf } from "@meshtastic/core";
|
import type { Protobuf } from "@meshtastic/core";
|
||||||
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
|
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
|
||||||
import type { JSX } from "react";
|
|
||||||
|
|
||||||
export interface TraceRouteProps {
|
export interface TraceRouteProps {
|
||||||
from?: Protobuf.Mesh.NodeInfo;
|
from?: Protobuf.Mesh.NodeInfo;
|
||||||
@@ -19,7 +18,7 @@ export const TraceRoute = ({
|
|||||||
routeBack,
|
routeBack,
|
||||||
snrTowards,
|
snrTowards,
|
||||||
snrBack,
|
snrBack,
|
||||||
}: TraceRouteProps): JSX.Element => {
|
}: TraceRouteProps) => {
|
||||||
const { nodes } = useDevice();
|
const { nodes } = useDevice();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -38,23 +37,25 @@ export const TraceRoute = ({
|
|||||||
))}
|
))}
|
||||||
{from?.user?.longName}
|
{from?.user?.longName}
|
||||||
</span>
|
</span>
|
||||||
{routeBack ? (
|
{routeBack
|
||||||
<span className="ml-4 border-l-2 border-l-background-primary pl-2 text-text-primary">
|
? (
|
||||||
<p className="font-semibold">Route back:</p>
|
<span className="ml-4 border-l-2 border-l-background-primary pl-2 text-text-primary">
|
||||||
<p>{from?.user?.longName}</p>
|
<p className="font-semibold">Route back:</p>
|
||||||
<p>↓ {snrBack?.[0] ? snrBack[0] : "??"}dB</p>
|
<p>{from?.user?.longName}</p>
|
||||||
{routeBack.map((hop, i) => (
|
<p>↓ {snrBack?.[0] ? snrBack[0] : "??"}dB</p>
|
||||||
<span key={nodes.get(hop)?.num}>
|
{routeBack.map((hop, i) => (
|
||||||
<p>
|
<span key={nodes.get(hop)?.num}>
|
||||||
{nodes.get(hop)?.user?.longName ??
|
<p>
|
||||||
`!${numberToHexUnpadded(hop)}`}
|
{nodes.get(hop)?.user?.longName ??
|
||||||
</p>
|
`!${numberToHexUnpadded(hop)}`}
|
||||||
<p>↓ {snrBack?.[i + 1] ? snrBack[i + 1] : "??"}dB</p>
|
</p>
|
||||||
</span>
|
<p>↓ {snrBack?.[i + 1] ? snrBack[i + 1] : "??"}dB</p>
|
||||||
))}
|
</span>
|
||||||
{to?.user?.longName}
|
))}
|
||||||
</span>
|
{to?.user?.longName}
|
||||||
) : null}
|
</span>
|
||||||
|
)
|
||||||
|
: null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { useDevice } from "@app/core/stores/deviceStore.ts";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import type { AmbientLightingValidation } from "@app/validation/moduleConfig/ambientLighting.tsx";
|
import type { AmbientLightingValidation } from "@app/validation/moduleConfig/ambientLighting.tsx";
|
||||||
import { create } from "@bufbuild/protobuf";
|
import { create } from "@bufbuild/protobuf";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { Protobuf } from "@meshtastic/core";
|
import { Protobuf } from "@meshtastic/core";
|
||||||
|
|
||||||
export const AmbientLighting = (): JSX.Element => {
|
export const AmbientLighting = () => {
|
||||||
const { moduleConfig, setWorkingModuleConfig } = useDevice();
|
const { moduleConfig, setWorkingModuleConfig } = useDevice();
|
||||||
|
|
||||||
const onSubmit = (data: AmbientLightingValidation) => {
|
const onSubmit = (data: AmbientLightingValidation) => {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
|||||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/core";
|
import { Protobuf } from "@meshtastic/core";
|
||||||
|
|
||||||
export const Audio = (): JSX.Element => {
|
export const Audio = () => {
|
||||||
const { moduleConfig, setWorkingModuleConfig } = useDevice();
|
const { moduleConfig, setWorkingModuleConfig } = useDevice();
|
||||||
|
|
||||||
const onSubmit = (data: AudioValidation) => {
|
const onSubmit = (data: AudioValidation) => {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
|||||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/core";
|
import { Protobuf } from "@meshtastic/core";
|
||||||
|
|
||||||
export const CannedMessage = (): JSX.Element => {
|
export const CannedMessage = () => {
|
||||||
const { moduleConfig, setWorkingModuleConfig } = useDevice();
|
const { moduleConfig, setWorkingModuleConfig } = useDevice();
|
||||||
|
|
||||||
const onSubmit = (data: CannedMessageValidation) => {
|
const onSubmit = (data: CannedMessageValidation) => {
|
||||||
@@ -63,9 +63,8 @@ export const CannedMessage = (): JSX.Element => {
|
|||||||
label: "Clockwise event",
|
label: "Clockwise event",
|
||||||
description: "Select input event.",
|
description: "Select input event.",
|
||||||
properties: {
|
properties: {
|
||||||
enumValue:
|
enumValue: Protobuf.ModuleConfig
|
||||||
Protobuf.ModuleConfig
|
.ModuleConfig_CannedMessageConfig_InputEventChar,
|
||||||
.ModuleConfig_CannedMessageConfig_InputEventChar,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -74,9 +73,8 @@ export const CannedMessage = (): JSX.Element => {
|
|||||||
label: "Counter Clockwise event",
|
label: "Counter Clockwise event",
|
||||||
description: "Select input event.",
|
description: "Select input event.",
|
||||||
properties: {
|
properties: {
|
||||||
enumValue:
|
enumValue: Protobuf.ModuleConfig
|
||||||
Protobuf.ModuleConfig
|
.ModuleConfig_CannedMessageConfig_InputEventChar,
|
||||||
.ModuleConfig_CannedMessageConfig_InputEventChar,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -85,9 +83,8 @@ export const CannedMessage = (): JSX.Element => {
|
|||||||
label: "Press event",
|
label: "Press event",
|
||||||
description: "Select input event",
|
description: "Select input event",
|
||||||
properties: {
|
properties: {
|
||||||
enumValue:
|
enumValue: Protobuf.ModuleConfig
|
||||||
Protobuf.ModuleConfig
|
.ModuleConfig_CannedMessageConfig_InputEventChar,
|
||||||
.ModuleConfig_CannedMessageConfig_InputEventChar,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { useDevice } from "@app/core/stores/deviceStore.ts";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import type { DetectionSensorValidation } from "@app/validation/moduleConfig/detectionSensor.tsx";
|
import type { DetectionSensorValidation } from "@app/validation/moduleConfig/detectionSensor.tsx";
|
||||||
import { create } from "@bufbuild/protobuf";
|
import { create } from "@bufbuild/protobuf";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { Protobuf } from "@meshtastic/core";
|
import { Protobuf } from "@meshtastic/core";
|
||||||
|
|
||||||
export const DetectionSensor = (): JSX.Element => {
|
export const DetectionSensor = () => {
|
||||||
const { moduleConfig, setWorkingModuleConfig } = useDevice();
|
const { moduleConfig, setWorkingModuleConfig } = useDevice();
|
||||||
|
|
||||||
const onSubmit = (data: DetectionSensorValidation) => {
|
const onSubmit = (data: DetectionSensorValidation) => {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
|||||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/core";
|
import { Protobuf } from "@meshtastic/core";
|
||||||
|
|
||||||
export const ExternalNotification = (): JSX.Element => {
|
export const ExternalNotification = () => {
|
||||||
const { moduleConfig, setWorkingModuleConfig } = useDevice();
|
const { moduleConfig, setWorkingModuleConfig } = useDevice();
|
||||||
|
|
||||||
const onSubmit = (data: ExternalNotificationValidation) => {
|
const onSubmit = (data: ExternalNotificationValidation) => {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { useDevice } from "@app/core/stores/deviceStore.ts";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import type { MqttValidation } from "@app/validation/moduleConfig/mqtt.tsx";
|
import type { MqttValidation } from "@app/validation/moduleConfig/mqtt.tsx";
|
||||||
import { create } from "@bufbuild/protobuf";
|
import { create } from "@bufbuild/protobuf";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { Protobuf } from "@meshtastic/core";
|
import { Protobuf } from "@meshtastic/core";
|
||||||
|
|
||||||
export const MQTT = (): JSX.Element => {
|
export const MQTT = () => {
|
||||||
const { config, moduleConfig, setWorkingModuleConfig } = useDevice();
|
const { config, moduleConfig, setWorkingModuleConfig } = useDevice();
|
||||||
|
|
||||||
const onSubmit = (data: MqttValidation) => {
|
const onSubmit = (data: MqttValidation) => {
|
||||||
@@ -165,32 +165,31 @@ export const MQTT = (): JSX.Element => {
|
|||||||
description:
|
description:
|
||||||
"Position shared will be accurate within this distance",
|
"Position shared will be accurate within this distance",
|
||||||
properties: {
|
properties: {
|
||||||
enumValue:
|
enumValue: config.display?.units === 0
|
||||||
config.display?.units === 0
|
? {
|
||||||
? {
|
"Within 23 km": 10,
|
||||||
"Within 23 km": 10,
|
"Within 12 km": 11,
|
||||||
"Within 12 km": 11,
|
"Within 5.8 km": 12,
|
||||||
"Within 5.8 km": 12,
|
"Within 2.9 km": 13,
|
||||||
"Within 2.9 km": 13,
|
"Within 1.5 km": 14,
|
||||||
"Within 1.5 km": 14,
|
"Within 700 m": 15,
|
||||||
"Within 700 m": 15,
|
"Within 350 m": 16,
|
||||||
"Within 350 m": 16,
|
"Within 200 m": 17,
|
||||||
"Within 200 m": 17,
|
"Within 90 m": 18,
|
||||||
"Within 90 m": 18,
|
"Within 50 m": 19,
|
||||||
"Within 50 m": 19,
|
}
|
||||||
}
|
: {
|
||||||
: {
|
"Within 15 miles": 10,
|
||||||
"Within 15 miles": 10,
|
"Within 7.3 miles": 11,
|
||||||
"Within 7.3 miles": 11,
|
"Within 3.6 miles": 12,
|
||||||
"Within 3.6 miles": 12,
|
"Within 1.8 miles": 13,
|
||||||
"Within 1.8 miles": 13,
|
"Within 0.9 miles": 14,
|
||||||
"Within 0.9 miles": 14,
|
"Within 0.5 miles": 15,
|
||||||
"Within 0.5 miles": 15,
|
"Within 0.2 miles": 16,
|
||||||
"Within 0.2 miles": 16,
|
"Within 600 feet": 17,
|
||||||
"Within 600 feet": 17,
|
"Within 300 feet": 18,
|
||||||
"Within 300 feet": 18,
|
"Within 150 feet": 19,
|
||||||
"Within 150 feet": 19,
|
},
|
||||||
},
|
|
||||||
},
|
},
|
||||||
disabledBy: [
|
disabledBy: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { useDevice } from "@app/core/stores/deviceStore.ts";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import type { NeighborInfoValidation } from "@app/validation/moduleConfig/neighborInfo.tsx";
|
import type { NeighborInfoValidation } from "@app/validation/moduleConfig/neighborInfo.tsx";
|
||||||
import { create } from "@bufbuild/protobuf";
|
import { create } from "@bufbuild/protobuf";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
||||||
import { Protobuf } from "@meshtastic/core";
|
import { Protobuf } from "@meshtastic/core";
|
||||||
|
|
||||||
export const NeighborInfo = (): JSX.Element => {
|
export const NeighborInfo = () => {
|
||||||
const { moduleConfig, setWorkingModuleConfig } = useDevice();
|
const { moduleConfig, setWorkingModuleConfig } = useDevice();
|
||||||
|
|
||||||
const onSubmit = (data: NeighborInfoValidation) => {
|
const onSubmit = (data: NeighborInfoValidation) => {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
|||||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/core";
|
import { Protobuf } from "@meshtastic/core";
|
||||||
|
|
||||||
export const Paxcounter = (): JSX.Element => {
|
export const Paxcounter = () => {
|
||||||
const { moduleConfig, setWorkingModuleConfig } = useDevice();
|
const { moduleConfig, setWorkingModuleConfig } = useDevice();
|
||||||
|
|
||||||
const onSubmit = (data: PaxcounterValidation) => {
|
const onSubmit = (data: PaxcounterValidation) => {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
|||||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/core";
|
import { Protobuf } from "@meshtastic/core";
|
||||||
|
|
||||||
export const RangeTest = (): JSX.Element => {
|
export const RangeTest = () => {
|
||||||
const { moduleConfig, setWorkingModuleConfig } = useDevice();
|
const { moduleConfig, setWorkingModuleConfig } = useDevice();
|
||||||
|
|
||||||
const onSubmit = (data: RangeTestValidation) => {
|
const onSubmit = (data: RangeTestValidation) => {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
|||||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/core";
|
import { Protobuf } from "@meshtastic/core";
|
||||||
|
|
||||||
export const Serial = (): JSX.Element => {
|
export const Serial = () => {
|
||||||
const { moduleConfig, setWorkingModuleConfig } = useDevice();
|
const { moduleConfig, setWorkingModuleConfig } = useDevice();
|
||||||
|
|
||||||
const onSubmit = (data: SerialValidation) => {
|
const onSubmit = (data: SerialValidation) => {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
|||||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/core";
|
import { Protobuf } from "@meshtastic/core";
|
||||||
|
|
||||||
export const StoreForward = (): JSX.Element => {
|
export const StoreForward = () => {
|
||||||
const { moduleConfig, setWorkingModuleConfig } = useDevice();
|
const { moduleConfig, setWorkingModuleConfig } = useDevice();
|
||||||
|
|
||||||
const onSubmit = (data: StoreForwardValidation) => {
|
const onSubmit = (data: StoreForwardValidation) => {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { DynamicForm } from "@components/Form/DynamicForm.tsx";
|
|||||||
import { useDevice } from "@core/stores/deviceStore.ts";
|
import { useDevice } from "@core/stores/deviceStore.ts";
|
||||||
import { Protobuf } from "@meshtastic/core";
|
import { Protobuf } from "@meshtastic/core";
|
||||||
|
|
||||||
export const Telemetry = (): JSX.Element => {
|
export const Telemetry = () => {
|
||||||
const { moduleConfig, setWorkingModuleConfig } = useDevice();
|
const { moduleConfig, setWorkingModuleConfig } = useDevice();
|
||||||
|
|
||||||
const onSubmit = (data: TelemetryValidation) => {
|
const onSubmit = (data: TelemetryValidation) => {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { cn } from "@app/core/utils/cn.ts";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
import { AlignLeftIcon, type LucideIcon } from "lucide-react";
|
import { AlignLeftIcon, type LucideIcon } from "lucide-react";
|
||||||
|
import Footer from "@components/UI/Footer.tsx";
|
||||||
|
import { Spinner } from "@components/UI/Spinner.tsx";
|
||||||
import { ErrorBoundary } from "react-error-boundary";
|
import { ErrorBoundary } from "react-error-boundary";
|
||||||
import { ErrorPage } from "./UI/ErrorPage";
|
import { ErrorPage } from "@components/UI/ErrorPage.tsx";
|
||||||
import Footer from "./UI/Footer";
|
|
||||||
import { Spinner } from "./UI/Spinner";
|
|
||||||
|
|
||||||
export interface PageLayoutProps {
|
export interface PageLayoutProps {
|
||||||
label: string;
|
label: string;
|
||||||
@@ -46,9 +47,7 @@ export const PageLayout = ({
|
|||||||
className="transition-all hover:text-accent"
|
className="transition-all hover:text-accent"
|
||||||
onClick={action.onClick}
|
onClick={action.onClick}
|
||||||
>
|
>
|
||||||
{action?.isLoading ? (
|
{action?.isLoading ? <Spinner /> : (
|
||||||
<Spinner />
|
|
||||||
) : (
|
|
||||||
<action.icon
|
<action.icon
|
||||||
className={action.iconClasses}
|
className={action.iconClasses}
|
||||||
aria-disabled={action.disabled}
|
aria-disabled={action.disabled}
|
||||||
|
|||||||
+65
-63
@@ -23,7 +23,7 @@ export interface SidebarProps {
|
|||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Sidebar = ({ children }: SidebarProps): JSX.Element => {
|
export const Sidebar = ({ children }: SidebarProps) => {
|
||||||
const { hardware, nodes, metadata } = useDevice();
|
const { hardware, nodes, metadata } = useDevice();
|
||||||
const myNode = nodes.get(hardware.myNodeNum);
|
const myNode = nodes.get(hardware.myNodeNum);
|
||||||
const myMetadata = metadata.get(0);
|
const myMetadata = metadata.get(0);
|
||||||
@@ -64,69 +64,71 @@ export const Sidebar = ({ children }: SidebarProps): JSX.Element => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return showSidebar ? (
|
return showSidebar
|
||||||
<div className="min-w-[280px] max-w-min flex-col overflow-y-auto border-r-[0.5px] bg-background-primary border-slate-300 dark:border-slate-700">
|
? (
|
||||||
<div className="flex justify-between px-8 pt-6">
|
<div className="min-w-[280px] max-w-min flex-col overflow-y-auto border-r-[0.5px] bg-background-primary border-slate-300 dark:border-slate-700">
|
||||||
<div>
|
<div className="flex justify-between px-8 pt-6">
|
||||||
<span className="text-lg font-medium">
|
<div>
|
||||||
{myNode?.user?.shortName ?? "UNK"}
|
<span className="text-lg font-medium">
|
||||||
</span>
|
{myNode?.user?.shortName ?? "UNK"}
|
||||||
<Subtle>{myNode?.user?.longName ?? "UNK"}</Subtle>
|
</span>
|
||||||
|
<Subtle>{myNode?.user?.longName ?? "UNK"}</Subtle>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="transition-all hover:text-accent"
|
||||||
|
onClick={() => setDialogOpen("deviceName", true)}
|
||||||
|
>
|
||||||
|
<EditIcon size={16} />
|
||||||
|
</button>
|
||||||
|
<button type="button" onClick={() => setShowSidebar(false)}>
|
||||||
|
<SidebarCloseIcon size={24} />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<div className="px-8 pb-6">
|
||||||
type="button"
|
<div className="flex items-center">
|
||||||
className="transition-all hover:text-accent"
|
<BatteryMediumIcon size={24} viewBox="0 0 28 24" />
|
||||||
onClick={() => setDialogOpen("deviceName", true)}
|
<Subtle>
|
||||||
>
|
{myNode?.deviceMetrics?.batteryLevel
|
||||||
<EditIcon size={16} />
|
? myNode?.deviceMetrics?.batteryLevel > 100
|
||||||
</button>
|
? "Charging"
|
||||||
<button type="button" onClick={() => setShowSidebar(false)}>
|
: `${myNode?.deviceMetrics?.batteryLevel}%`
|
||||||
<SidebarCloseIcon size={24} />
|
: "UNK"}
|
||||||
</button>
|
</Subtle>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-8 pb-6">
|
<div className="flex items-center">
|
||||||
<div className="flex items-center">
|
<ZapIcon size={24} viewBox="0 0 36 24" />
|
||||||
<BatteryMediumIcon size={24} viewBox={"0 0 28 24"} />
|
<Subtle>
|
||||||
<Subtle>
|
{myNode?.deviceMetrics?.voltage?.toPrecision(3) ?? "UNK"} volts
|
||||||
{myNode?.deviceMetrics?.batteryLevel
|
</Subtle>
|
||||||
? myNode?.deviceMetrics?.batteryLevel > 100
|
</div>
|
||||||
? "Charging"
|
<div className="flex items-center">
|
||||||
: `${myNode?.deviceMetrics?.batteryLevel}%`
|
<CpuIcon size={24} viewBox="0 0 36 24" />
|
||||||
: "UNK"}
|
<Subtle>v{myMetadata?.firmwareVersion ?? "UNK"}</Subtle>
|
||||||
</Subtle>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center">
|
|
||||||
<ZapIcon size={24} viewBox={"0 0 36 24"} />
|
|
||||||
<Subtle>
|
|
||||||
{myNode?.deviceMetrics?.voltage?.toPrecision(3) ?? "UNK"} volts
|
|
||||||
</Subtle>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center">
|
|
||||||
<CpuIcon size={24} viewBox={"0 0 36 24"} />
|
|
||||||
<Subtle>v{myMetadata?.firmwareVersion ?? "UNK"}</Subtle>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<SidebarSection label="Navigation">
|
<SidebarSection label="Navigation">
|
||||||
{pages.map((link) => (
|
{pages.map((link) => (
|
||||||
<SidebarButton
|
<SidebarButton
|
||||||
key={link.name}
|
key={link.name}
|
||||||
label={link.name}
|
label={link.name}
|
||||||
Icon={link.icon}
|
Icon={link.icon}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setActivePage(link.page);
|
setActivePage(link.page);
|
||||||
}}
|
}}
|
||||||
active={link.page === activePage}
|
active={link.page === activePage}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</SidebarSection>
|
</SidebarSection>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
)
|
||||||
<div className="px-1 pt-8 border-r-[0.5px] border-slate-700">
|
: (
|
||||||
<button type="button" onClick={() => setShowSidebar(true)}>
|
<div className="px-1 pt-8 border-r-[0.5px] border-slate-700">
|
||||||
<SidebarOpenIcon size={24} />
|
<button type="button" onClick={() => setShowSidebar(true)}>
|
||||||
</button>
|
<SidebarOpenIcon size={24} />
|
||||||
</div>
|
</button>
|
||||||
);
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useTheme } from "@app/core/hooks/useTheme";
|
import { useTheme } from "../core/hooks/useTheme.ts";
|
||||||
import { cn } from "@app/core/utils/cn";
|
import { cn } from "../core/utils/cn.ts";
|
||||||
import { Monitor, Moon, Sun } from "lucide-react";
|
import { Monitor, Moon, Sun } from "lucide-react";
|
||||||
|
|
||||||
type ThemePreference = "light" | "dark" | "system";
|
type ThemePreference = "light" | "dark" | "system";
|
||||||
@@ -32,11 +32,9 @@ export default function ThemeSwitcher({
|
|||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
onClick={toggleTheme}
|
onClick={toggleTheme}
|
||||||
aria-label={
|
aria-label={preference === "system"
|
||||||
preference === "system"
|
? `System theme (currently ${theme}). Click to change theme.`
|
||||||
? `System theme (currently ${theme}). Click to change theme.`
|
: `Current theme: ${theme}. Click to change theme.`}
|
||||||
: `Current theme: ${theme}. Click to change theme.`
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{themeIcons[preference]}
|
{themeIcons[preference]}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import {
|
|||||||
ToastProvider,
|
ToastProvider,
|
||||||
ToastTitle,
|
ToastTitle,
|
||||||
ToastViewport,
|
ToastViewport,
|
||||||
} from "@components/UI/Toast";
|
} from "./UI/Toast.tsx";
|
||||||
import { useToast } from "@core/hooks/useToast";
|
import { useToast } from "../core/hooks/useToast.ts";
|
||||||
|
|
||||||
export function Toaster() {
|
export function Toaster() {
|
||||||
const { toasts } = useToast();
|
const { toasts } = useToast();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { cn } from "@app/core/utils/cn";
|
import { cn } from "../../core/utils/cn.ts";
|
||||||
import type React from "react";
|
import type React from "react";
|
||||||
|
|
||||||
type RGBColor = {
|
type RGBColor = {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { type VariantProps, cva } from "class-variance-authority";
|
import { cva, type VariantProps } from "class-variance-authority";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@core/utils/cn.ts";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
@@ -20,7 +20,8 @@ const buttonVariants = cva(
|
|||||||
"bg-slate-100 text-slate-700 hover:bg-slate-200 dark:bg-slate-500 dark:text-white dark:hover:bg-slate-400",
|
"bg-slate-100 text-slate-700 hover:bg-slate-200 dark:bg-slate-500 dark:text-white dark:hover:bg-slate-400",
|
||||||
ghost:
|
ghost:
|
||||||
"bg-transparent hover:bg-slate-100 dark:hover:bg-slate-800 dark:text-slate-100 dark:hover:text-slate-100 data-[state=open]:bg-transparent dark:data-[state=open]:bg-transparent",
|
"bg-transparent hover:bg-slate-100 dark:hover:bg-slate-800 dark:text-slate-100 dark:hover:text-slate-100 data-[state=open]:bg-transparent dark:data-[state=open]:bg-transparent",
|
||||||
link: "bg-transparent underline-offset-4 hover:underline text-slate-900 dark:text-slate-100 hover:bg-transparent dark:hover:bg-transparent",
|
link:
|
||||||
|
"bg-transparent underline-offset-4 hover:underline text-slate-900 dark:text-slate-100 hover:bg-transparent dark:hover:bg-transparent",
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
default: "h-10 py-2 px-4",
|
default: "h-10 py-2 px-4",
|
||||||
@@ -38,7 +39,8 @@ const buttonVariants = cva(
|
|||||||
export type ButtonVariant = VariantProps<typeof buttonVariants>["variant"];
|
export type ButtonVariant = VariantProps<typeof buttonVariants>["variant"];
|
||||||
|
|
||||||
export interface ButtonProps
|
export interface ButtonProps
|
||||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
extends
|
||||||
|
React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||||
VariantProps<typeof buttonVariants> {}
|
VariantProps<typeof buttonVariants> {}
|
||||||
|
|
||||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||||
|
|||||||
@@ -144,11 +144,11 @@ CommandShortcut.displayName = "CommandShortcut";
|
|||||||
export {
|
export {
|
||||||
Command,
|
Command,
|
||||||
CommandDialog,
|
CommandDialog,
|
||||||
CommandInput,
|
|
||||||
CommandList,
|
|
||||||
CommandEmpty,
|
CommandEmpty,
|
||||||
CommandGroup,
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
CommandItem,
|
CommandItem,
|
||||||
CommandShortcut,
|
CommandList,
|
||||||
CommandSeparator,
|
CommandSeparator,
|
||||||
|
CommandShortcut,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ DialogPortal.displayName = DialogPrimitive.Portal.displayName;
|
|||||||
const DialogOverlay = React.forwardRef<
|
const DialogOverlay = React.forwardRef<
|
||||||
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
||||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
|
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
|
||||||
>(({ className, children, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<DialogPrimitive.Overlay
|
<DialogPrimitive.Overlay
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed inset-0 z-50 bg-black/50 backdrop-blur-xs transition-opacity animate-in fade-in",
|
"fixed inset-0 z-50 bg-black/50 backdrop-blur-xs transition-opacity animate-in fade-in",
|
||||||
@@ -113,10 +113,10 @@ DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogTrigger,
|
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogHeader,
|
|
||||||
DialogFooter,
|
|
||||||
DialogTitle,
|
|
||||||
DialogDescription,
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -184,18 +184,18 @@ DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuTrigger,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuCheckboxItem,
|
DropdownMenuCheckboxItem,
|
||||||
DropdownMenuRadioItem,
|
DropdownMenuContent,
|
||||||
|
DropdownMenuGroup,
|
||||||
|
DropdownMenuItem,
|
||||||
DropdownMenuLabel,
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuPortal,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuShortcut,
|
DropdownMenuShortcut,
|
||||||
DropdownMenuGroup,
|
|
||||||
DropdownMenuPortal,
|
|
||||||
DropdownMenuSub,
|
DropdownMenuSub,
|
||||||
DropdownMenuSubContent,
|
DropdownMenuSubContent,
|
||||||
DropdownMenuSubTrigger,
|
DropdownMenuSubTrigger,
|
||||||
DropdownMenuRadioGroup,
|
DropdownMenuTrigger,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import newGithubIssueUrl from "@app/core/utils/github";
|
import newGithubIssueUrl from "../../core/utils/github.ts";
|
||||||
import { ExternalLink } from "lucide-react";
|
import { ExternalLink } from "lucide-react";
|
||||||
import { Heading } from "./Typography/Heading";
|
import { Heading } from "./Typography/Heading.tsx";
|
||||||
import { Link } from "./Typography/Link";
|
import { Link } from "./Typography/Link.tsx";
|
||||||
import { P } from "./Typography/P";
|
import { P } from "./Typography/P.tsx";
|
||||||
|
|
||||||
|
|
||||||
export function ErrorPage({ error }: { error: Error }) {
|
export function ErrorPage({ error }: { error: Error }) {
|
||||||
if (!error) {
|
if (!error) {
|
||||||
|
|||||||
@@ -1,30 +1,32 @@
|
|||||||
import { cn } from "@app/core/utils/cn";
|
import { cn } from "@core/utils/cn.ts"
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
export interface FooterProps extends React.HTMLAttributes<HTMLElement> {}
|
type FooterProps = {
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
const Footer = React.forwardRef<HTMLElement, FooterProps>(
|
const Footer = ({ className, ...props }: FooterProps) => {
|
||||||
({ className, ...props }, ref) => {
|
return (
|
||||||
return (
|
<footer
|
||||||
<footer className={cn("flex mt-auto justify-center p-2", className)}>
|
className={cn("flex mt-auto justify-center p-2", className)}
|
||||||
<p>
|
{...props}
|
||||||
<a
|
>
|
||||||
href="https://vercel.com/?utm_source=meshtastic&utm_campaign=oss"
|
<p>
|
||||||
className="hover:underline text-link"
|
<a
|
||||||
>
|
href="https://vercel.com/?utm_source=meshtastic&utm_campaign=oss"
|
||||||
Powered by ▲ Vercel
|
className="hover:underline text-link"
|
||||||
</a>{" "}
|
>
|
||||||
| Meshtastic® is a registered trademark of Meshtastic LLC. |{" "}
|
Powered by ▲ Vercel
|
||||||
<a
|
</a>{" "}
|
||||||
href="https://meshtastic.org/docs/legal"
|
| Meshtastic® is a registered trademark of Meshtastic LLC. |{" "}
|
||||||
className="hover:underline text-link"
|
<a
|
||||||
>
|
href="https://meshtastic.org/docs/legal"
|
||||||
Legal Information
|
className="hover:underline text-link"
|
||||||
</a>
|
>
|
||||||
</p>
|
Legal Information
|
||||||
</footer>
|
</a>
|
||||||
);
|
</p>
|
||||||
},
|
</footer>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default Footer;
|
export default Footer;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export interface GeneratorProps extends React.BaseHTMLAttributes<HTMLElement> {
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Generator = React.forwardRef<HTMLInputElement, GeneratorProps>(
|
const Generator =
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
type,
|
type,
|
||||||
@@ -53,8 +53,7 @@ const Generator = React.forwardRef<HTMLInputElement, GeneratorProps>(
|
|||||||
action,
|
action,
|
||||||
disabled,
|
disabled,
|
||||||
...props
|
...props
|
||||||
},
|
}: GeneratorProps
|
||||||
ref,
|
|
||||||
) => {
|
) => {
|
||||||
const inputRef = React.useRef<HTMLInputElement>(null);
|
const inputRef = React.useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
@@ -116,8 +115,7 @@ const Generator = React.forwardRef<HTMLInputElement, GeneratorProps>(
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
);
|
|
||||||
Generator.displayName = "Button";
|
Generator.displayName = "Button";
|
||||||
|
|
||||||
export { Generator };
|
export { Generator };
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@core/utils/cn.ts";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
import { type VariantProps, cva } from "class-variance-authority";
|
import { cva, type VariantProps } from "class-variance-authority";
|
||||||
import type { LucideIcon } from "lucide-react";
|
import type { LucideIcon } from "lucide-react";
|
||||||
|
|
||||||
const inputVariants = cva(
|
const inputVariants = cva(
|
||||||
@@ -20,7 +20,8 @@ const inputVariants = cva(
|
|||||||
);
|
);
|
||||||
|
|
||||||
export interface InputProps
|
export interface InputProps
|
||||||
extends React.InputHTMLAttributes<HTMLInputElement>,
|
extends
|
||||||
|
React.InputHTMLAttributes<HTMLInputElement>,
|
||||||
VariantProps<typeof inputVariants> {
|
VariantProps<typeof inputVariants> {
|
||||||
prefix?: string;
|
prefix?: string;
|
||||||
suffix?: string;
|
suffix?: string;
|
||||||
|
|||||||
@@ -216,19 +216,19 @@ MenubarShortcut.displayname = "MenubarShortcut";
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
Menubar,
|
Menubar,
|
||||||
MenubarMenu,
|
|
||||||
MenubarTrigger,
|
|
||||||
MenubarContent,
|
|
||||||
MenubarItem,
|
|
||||||
MenubarSeparator,
|
|
||||||
MenubarLabel,
|
|
||||||
MenubarCheckboxItem,
|
MenubarCheckboxItem,
|
||||||
|
MenubarContent,
|
||||||
|
MenubarGroup,
|
||||||
|
MenubarItem,
|
||||||
|
MenubarLabel,
|
||||||
|
MenubarMenu,
|
||||||
|
MenubarPortal,
|
||||||
MenubarRadioGroup,
|
MenubarRadioGroup,
|
||||||
MenubarRadioItem,
|
MenubarRadioItem,
|
||||||
MenubarPortal,
|
MenubarSeparator,
|
||||||
|
MenubarShortcut,
|
||||||
|
MenubarSub,
|
||||||
MenubarSubContent,
|
MenubarSubContent,
|
||||||
MenubarSubTrigger,
|
MenubarSubTrigger,
|
||||||
MenubarGroup,
|
MenubarTrigger,
|
||||||
MenubarSub,
|
|
||||||
MenubarShortcut,
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
export interface ModalProps {
|
|
||||||
title: string;
|
|
||||||
actions?: JSX.Element[];
|
|
||||||
children: React.ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Modal = ({
|
|
||||||
title,
|
|
||||||
actions,
|
|
||||||
children,
|
|
||||||
}: ModalProps): JSX.Element => {
|
|
||||||
return (
|
|
||||||
<div className="rounded-md overflow-hidden w-full">
|
|
||||||
<div className="flex h-12 px-3 bg-slate-200 dark:bg-slate-700 justify-between">
|
|
||||||
<h2 className="my-auto font-semibold text-lg">{title}</h2>
|
|
||||||
{actions && (
|
|
||||||
<div className="my-auto">{actions.map((action) => action)}</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="h-full border border-slate-200 dark:border-slate-700">
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { cn } from "@app/core/utils/cn";
|
import { cn } from "../../core/utils/cn.ts";
|
||||||
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
||||||
import { Check } from "lucide-react";
|
import { Check } from "lucide-react";
|
||||||
|
|
||||||
@@ -8,9 +8,8 @@ interface MultiSelectProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MultiSelect = ({ children, className = "" }: MultiSelectProps) => {
|
const MultiSelect = ({ children, className = "" }: MultiSelectProps) => {
|
||||||
return (
|
return <div className={cn("flex flex-wrap gap-2", className)}>{children}
|
||||||
<div className={cn("flex flex-wrap gap-2", className)}>{children}</div>
|
</div>;
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
interface MultiSelectItemProps {
|
interface MultiSelectItemProps {
|
||||||
|
|||||||
@@ -26,4 +26,4 @@ const PopoverContent = React.forwardRef<
|
|||||||
));
|
));
|
||||||
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
||||||
|
|
||||||
export { Popover, PopoverTrigger, PopoverContent };
|
export { Popover, PopoverContent, PopoverTrigger };
|
||||||
|
|||||||
@@ -101,11 +101,11 @@ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
Select,
|
Select,
|
||||||
SelectGroup,
|
|
||||||
SelectValue,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectContent,
|
SelectContent,
|
||||||
SelectLabel,
|
SelectGroup,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
|
SelectLabel,
|
||||||
SelectSeparator,
|
SelectSeparator,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Heading } from "../Typography/Heading";
|
import { Heading } from "../Typography/Heading.tsx";
|
||||||
|
|
||||||
export interface SidebarSectionProps {
|
export interface SidebarSectionProps {
|
||||||
label: string;
|
label: string;
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { Button } from "@components/UI/Button.tsx";
|
import { Button } from "@components/UI/Button.tsx";
|
||||||
import type { LucideIcon } from "lucide-react";
|
import type { LucideIcon } from "lucide-react";
|
||||||
import type { JSX } from "react";
|
|
||||||
|
|
||||||
export interface SidebarButtonProps {
|
export interface SidebarButtonProps {
|
||||||
label: string;
|
label: string;
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
Icon?: LucideIcon;
|
Icon?: LucideIcon;
|
||||||
element?: JSX.Element;
|
element?;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,7 +15,7 @@ export const SidebarButton = ({
|
|||||||
Icon,
|
Icon,
|
||||||
element,
|
element,
|
||||||
onClick,
|
onClick,
|
||||||
}: SidebarButtonProps): JSX.Element => (
|
}: SidebarButtonProps) => (
|
||||||
<Button
|
<Button
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
variant={active ? "subtle" : "ghost"}
|
variant={active ? "subtle" : "ghost"}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { cn } from "@app/core/utils/cn";
|
import { cn } from "../../core/utils/cn.ts";
|
||||||
|
|
||||||
interface SpinnerProps extends React.HTMLAttributes<HTMLDivElement> {
|
interface SpinnerProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
size?: "sm" | "md" | "lg";
|
size?: "sm" | "md" | "lg";
|
||||||
|
|||||||
@@ -50,4 +50,4 @@ const TabsContent = React.forwardRef<
|
|||||||
));
|
));
|
||||||
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
||||||
|
|
||||||
export { Tabs, TabsList, TabsTrigger, TabsContent };
|
export { Tabs, TabsContent, TabsList, TabsTrigger };
|
||||||
|
|||||||
+11
-11
@@ -1,9 +1,9 @@
|
|||||||
import * as ToastPrimitives from "@radix-ui/react-toast";
|
import * as ToastPrimitives from "@radix-ui/react-toast";
|
||||||
import { type VariantProps, cva } from "class-variance-authority";
|
import { cva, type VariantProps } from "class-variance-authority";
|
||||||
import { X } from "lucide-react";
|
import { X } from "lucide-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@core/utils/cn";
|
import { cn } from "../../core/utils/cn.ts";
|
||||||
|
|
||||||
const ToastProvider = ToastPrimitives.Provider;
|
const ToastProvider = ToastPrimitives.Provider;
|
||||||
|
|
||||||
@@ -41,8 +41,8 @@ const toastVariants = cva(
|
|||||||
|
|
||||||
const Toast = React.forwardRef<
|
const Toast = React.forwardRef<
|
||||||
React.ElementRef<typeof ToastPrimitives.Root>,
|
React.ElementRef<typeof ToastPrimitives.Root>,
|
||||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
|
& React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root>
|
||||||
VariantProps<typeof toastVariants>
|
& VariantProps<typeof toastVariants>
|
||||||
>(({ className, variant, ...props }, ref) => {
|
>(({ className, variant, ...props }, ref) => {
|
||||||
return (
|
return (
|
||||||
<ToastPrimitives.Root
|
<ToastPrimitives.Root
|
||||||
@@ -116,13 +116,13 @@ type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;
|
|||||||
type ToastActionElement = React.ReactElement<typeof ToastAction>;
|
type ToastActionElement = React.ReactElement<typeof ToastAction>;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
type ToastProps,
|
|
||||||
type ToastActionElement,
|
|
||||||
ToastProvider,
|
|
||||||
ToastViewport,
|
|
||||||
Toast,
|
Toast,
|
||||||
ToastTitle,
|
|
||||||
ToastDescription,
|
|
||||||
ToastClose,
|
|
||||||
ToastAction,
|
ToastAction,
|
||||||
|
type ToastActionElement,
|
||||||
|
ToastClose,
|
||||||
|
ToastDescription,
|
||||||
|
type ToastProps,
|
||||||
|
ToastProvider,
|
||||||
|
ToastTitle,
|
||||||
|
ToastViewport,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipTrigger,
|
TooltipArrow,
|
||||||
TooltipContent,
|
TooltipContent,
|
||||||
TooltipProvider,
|
TooltipProvider,
|
||||||
TooltipArrow,
|
TooltipTrigger,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ export interface BlockquoteProps {
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BlockQuote = ({ children }: BlockquoteProps): JSX.Element => (
|
export const BlockQuote = ({ children }: BlockquoteProps) => (
|
||||||
<blockquote className="mt-6 border-l-2 border-slate-300 pl-6 italic text-slate-800 dark:border-slate-600 dark:text-slate-200">
|
<blockquote className="mt-6 border-l-2 border-slate-300 pl-6 italic text-slate-800 dark:border-slate-600 dark:text-slate-200">
|
||||||
{children}
|
{children}
|
||||||
</blockquote>
|
</blockquote>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ export interface CodeProps {
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Code = ({ children }: CodeProps): JSX.Element => (
|
export const Code = ({ children }: CodeProps) => (
|
||||||
<code className="relative rounded-sm bg-slate-100 px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold text-slate-900 dark:bg-slate-800 dark:text-slate-400">
|
<code className="relative rounded-sm bg-slate-100 px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold text-slate-900 dark:bg-slate-800 dark:text-slate-400">
|
||||||
{children}
|
{children}
|
||||||
</code>
|
</code>
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ import type React from "react";
|
|||||||
|
|
||||||
const headingStyles = {
|
const headingStyles = {
|
||||||
h1: "scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl",
|
h1: "scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl",
|
||||||
h2: "scroll-m-20 border-b border-b-slate-200 pb-2 text-3xl font-semibold tracking-tight transition-colors first:mt-0 dark:border-b-slate-700",
|
h2:
|
||||||
|
"scroll-m-20 border-b border-b-slate-200 pb-2 text-3xl font-semibold tracking-tight transition-colors first:mt-0 dark:border-b-slate-700",
|
||||||
h3: "scroll-m-20 text-2xl font-semibold tracking-tight",
|
h3: "scroll-m-20 text-2xl font-semibold tracking-tight",
|
||||||
h4: "scroll-m-20 text-xl font-semibold tracking-tight",
|
h4: "scroll-m-20 text-xl font-semibold tracking-tight",
|
||||||
h5: "scroll-m-20 text-lg font-medium tracking-tight",
|
h5: "scroll-m-20 text-lg font-medium tracking-tight",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { cn } from "@app/core/utils/cn";
|
import { cn } from "../../../core/utils/cn.ts";
|
||||||
|
|
||||||
export interface LinkProps {
|
export interface LinkProps {
|
||||||
href: string;
|
href: string;
|
||||||
@@ -6,10 +6,10 @@ export interface LinkProps {
|
|||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Link = ({ href, children, className }: LinkProps): JSX.Element => (
|
export const Link = ({ href, children, className }: LinkProps) => (
|
||||||
<a
|
<a
|
||||||
href={href}
|
href={href}
|
||||||
target={"_blank"}
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className={cn(
|
className={cn(
|
||||||
"font-medium text-slate-900 underline underline-offset-4 dark:text-slate-50",
|
"font-medium text-slate-900 underline underline-offset-4 dark:text-slate-50",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { cn } from "@app/core/utils/cn";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
|
|
||||||
export interface PProps {
|
export interface PProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { cn } from "@app/core/utils/cn.ts";
|
import { cn } from "@core/utils/cn.ts";
|
||||||
|
|
||||||
export interface SubtleProps {
|
export interface SubtleProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Subtle = ({ className, children }: SubtleProps): JSX.Element => (
|
export const Subtle = ({ className, children }: SubtleProps) => (
|
||||||
<p className={cn("text-sm text-slate-500 dark:text-slate-400", className)}>
|
<p className={cn("text-sm text-slate-500 dark:text-slate-400", className)}>
|
||||||
{children}
|
{children}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export const Blur = (): JSX.Element => {
|
export const Blur = () => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="fixed inset-0 backdrop-blur-md backdrop-brightness-press"
|
className="fixed inset-0 backdrop-blur-md backdrop-brightness-press"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ export const Mono = ({
|
|||||||
children,
|
children,
|
||||||
className,
|
className,
|
||||||
...rest
|
...rest
|
||||||
}: JSX.IntrinsicElements["span"]): JSX.Element => {
|
}: JSX.IntrinsicElements["span"]) => {
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={`font-mono text-sm text-text-secondary ${className ?? ""}`}
|
className={`font-mono text-sm text-text-secondary ${className ?? ""}`}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user