From 9de904dd4c31d4e0538891e9c02c0a31d6a3b512 Mon Sep 17 00:00:00 2001
From: Luke Gustafson <88517757+LukeGus@users.noreply.github.com>
Date: Mon, 29 Jun 2026 13:28:26 -0500
Subject: [PATCH] release-2.5.0 (#994)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* feat(sshid) - sshid.io equivalent for termix (#919)
* feat(ssh-id): database schema, migrations and field encryption
Adds ssh_identities, ssh_identity_keys and ssh_identity_ca tables (public keys
stored plaintext for the unauthenticated resolver; CA private key registered
for per-user field encryption), with UNIQUE(user_id), an index on
ssh_identity_keys(identity_id), and idempotent CREATE TABLE migrations.
* feat(ssh-id): backend API — resolver, key management, CA and certificates
Mounts /sshid (nginx route added). Public text/plain authorized_keys resolver
(+ exact /:algo filter, HTML viewer) and CA public-key endpoint; no-store +
noindex headers on every resolver response including early 404s. Authenticated
management: claim/rename/delete handle, add/import/generate/enable/delete keys,
and a per-user CA (create/rotate/delete) with pure-Node OpenSSH certificate
issuance. Audit logging on all mutations; UNIQUE races map to a precise 409.
Unit tests for key parsing and certificate signing (ssh-keygen-validated).
* feat(ssh-id): frontend panel, API client and i18n
SSH ID panel wired into the app rail and AppShell: claim handle, resolver URL +
curl one-liner, key list, generate, paste/import, CA enable/rotate/remove with
server trust command, and per-key certificate issuance. API client re-exported
through main-axios.ts; all strings i18n'd.
* style(ssh-id): align panel and resolver page with Termix theme
- Rebuild the SSH ID sidebar panel with the theme's square components
(SectionCard / SettingRow / FakeSwitch) instead of rounded ad-hoc cards;
use accent-brand and destructive tokens rather than raw red/green.
- Fix panel scrolling: move overflow to a block scroll container so the
cards keep their natural height instead of being clipped.
- Restyle the public resolver HTML page (/sshid/u/:handle) to the Termix
dark theme: square corners, #18181b/#303032 palette, #f59145 accent,
uppercase section labels.
- Tidy copy: 'Save To Credentials' label, drop the redundant generate intro,
and correct the generate tooltip (the key is stored when saving to vault).
* feat: rename to Termix ID, improve UI, backend inconsistencies, and general bug fixes
---------
Co-authored-by: LukeGus
* ci(deps): bump actions/checkout from 6 to 7 in the github-actions group (#922)
Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout).
Updates `actions/checkout` from 6 to 7
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)
---
updated-dependencies:
- dependency-name: actions/checkout
dependency-version: '7'
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* chore(deps-dev): bump the dev-patch-updates group with 11 updates (#923)
Bumps the dev-patch-updates group with 11 updates:
| Package | From | To |
| --- | --- | --- |
| [@codemirror/search](https://github.com/codemirror/search) | `6.7.0` | `6.7.1` |
| [@codemirror/view](https://github.com/codemirror/view) | `6.43.0` | `6.43.1` |
| [@tailwindcss/vite](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/@tailwindcss-vite) | `4.3.0` | `4.3.1` |
| [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8) | `4.1.8` | `4.1.9` |
| [@vitest/ui](https://github.com/vitest-dev/vitest/tree/HEAD/packages/ui) | `4.1.8` | `4.1.9` |
| [eslint-plugin-react-refresh](https://github.com/ArnaudBarre/eslint-plugin-react-refresh) | `0.5.2` | `0.5.3` |
| [lint-staged](https://github.com/lint-staged/lint-staged) | `17.0.7` | `17.0.8` |
| [prettier](https://github.com/prettier/prettier) | `3.8.3` | `3.8.4` |
| [sharp](https://github.com/lovell/sharp) | `0.35.1` | `0.35.2` |
| [tailwindcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss) | `4.3.0` | `4.3.1` |
| [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `4.1.8` | `4.1.9` |
Updates `@codemirror/search` from 6.7.0 to 6.7.1
- [Changelog](https://github.com/codemirror/search/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codemirror/search/commits)
Updates `@codemirror/view` from 6.43.0 to 6.43.1
- [Changelog](https://github.com/codemirror/view/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codemirror/view/commits)
Updates `@tailwindcss/vite` from 4.3.0 to 4.3.1
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.3.1/packages/@tailwindcss-vite)
Updates `@vitest/coverage-v8` from 4.1.8 to 4.1.9
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.9/packages/coverage-v8)
Updates `@vitest/ui` from 4.1.8 to 4.1.9
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.9/packages/ui)
Updates `eslint-plugin-react-refresh` from 0.5.2 to 0.5.3
- [Release notes](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/releases)
- [Changelog](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/compare/v0.5.2...v0.5.3)
Updates `lint-staged` from 17.0.7 to 17.0.8
- [Release notes](https://github.com/lint-staged/lint-staged/releases)
- [Changelog](https://github.com/lint-staged/lint-staged/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lint-staged/lint-staged/compare/v17.0.7...v17.0.8)
Updates `prettier` from 3.8.3 to 3.8.4
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.8.3...3.8.4)
Updates `sharp` from 0.35.1 to 0.35.2
- [Release notes](https://github.com/lovell/sharp/releases)
- [Commits](https://github.com/lovell/sharp/compare/v0.35.1...v0.35.2)
Updates `tailwindcss` from 4.3.0 to 4.3.1
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.3.1/packages/tailwindcss)
Updates `vitest` from 4.1.8 to 4.1.9
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.9/packages/vitest)
---
updated-dependencies:
- dependency-name: "@codemirror/search"
dependency-version: 6.7.1
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
- dependency-name: "@codemirror/view"
dependency-version: 6.43.1
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
- dependency-name: "@tailwindcss/vite"
dependency-version: 4.3.1
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
- dependency-name: "@vitest/coverage-v8"
dependency-version: 4.1.9
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
- dependency-name: "@vitest/ui"
dependency-version: 4.1.9
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
- dependency-name: eslint-plugin-react-refresh
dependency-version: 0.5.3
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
- dependency-name: lint-staged
dependency-version: 17.0.8
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
- dependency-name: prettier
dependency-version: 3.8.4
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
- dependency-name: sharp
dependency-version: 0.35.2
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
- dependency-name: tailwindcss
dependency-version: 4.3.1
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
- dependency-name: vitest
dependency-version: 4.1.9
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* chore(deps): bump nanoid in the prod-patch-updates group (#925)
Bumps the prod-patch-updates group with 1 update: [nanoid](https://github.com/ai/nanoid).
Updates `nanoid` from 5.1.11 to 5.1.15
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/5.1.11...5.1.15)
---
updated-dependencies:
- dependency-name: nanoid
dependency-version: 5.1.15
dependency-type: direct:production
update-type: version-update:semver-patch
dependency-group: prod-patch-updates
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* chore(deps): bump the major-updates group with 5 updates (#926)
Bumps the major-updates group with 5 updates:
| Package | From | To |
| --- | --- | --- |
| [js-yaml](https://github.com/nodeca/js-yaml) | `4.2.0` | `5.0.0` |
| [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) | `9.39.4` | `10.0.1` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `25.9.2` | `26.0.0` |
| [concurrently](https://github.com/open-cli-tools/concurrently) | `9.2.1` | `10.0.3` |
| [eslint](https://github.com/eslint/eslint) | `9.39.4` | `10.5.0` |
Updates `js-yaml` from 4.2.0 to 5.0.0
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.2.0...5.0.0)
Updates `@eslint/js` from 9.39.4 to 10.0.1
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/commits/v10.0.1/packages/js)
Updates `@types/node` from 25.9.2 to 26.0.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)
Updates `concurrently` from 9.2.1 to 10.0.3
- [Release notes](https://github.com/open-cli-tools/concurrently/releases)
- [Commits](https://github.com/open-cli-tools/concurrently/compare/v9.2.1...v10.0.3)
Updates `eslint` from 9.39.4 to 10.5.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v9.39.4...v10.5.0)
---
updated-dependencies:
- dependency-name: js-yaml
dependency-version: 5.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: major-updates
- dependency-name: "@eslint/js"
dependency-version: 10.0.1
dependency-type: direct:development
update-type: version-update:semver-major
dependency-group: major-updates
- dependency-name: "@types/node"
dependency-version: 26.0.0
dependency-type: direct:development
update-type: version-update:semver-major
dependency-group: major-updates
- dependency-name: concurrently
dependency-version: 10.0.3
dependency-type: direct:development
update-type: version-update:semver-major
dependency-group: major-updates
- dependency-name: eslint
dependency-version: 10.5.0
dependency-type: direct:development
update-type: version-update:semver-major
dependency-group: major-updates
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* feat(ssh): add HashiCorp Vault SSH signer authentication
* fix: small fixes to vault feature to align with Termix codebase
* chore: add view docs links for vault/termix id
* fix: file upload fails with 400 and missing schema migrations on upgrade (#929)
Two bugs introduced in v2.4.1:
1. uploadFileStream uses fileManagerApi.post() which triggers axios's
transformRequest to JSON-serialize the FormData because the instance
default Content-Type is application/json. Change to postForm() which
sets Content-Type: multipart/form-data so the browser XHR sends the
correct multipart body with boundary.
2. Two schema items added to schema.ts were not included in migrateSchema()
in db/index.ts, causing 500 errors on existing installations upgrading
from v2.4.0:
- user_preferences.status_color_scheme (no such column)
- dashboard_service_links table (no such table)
Fixes #928
Co-authored-by: sash
* fix: support PuTTY PPK ssh keys (#930)
* fix: chunk large file manager uploads (#932)
* fix: route dashboard hosts by protocol (#934)
* fix: resolve tunnel endpoints reliably (#935)
* Fix Electron OIDC browser auth failures (#936)
* Allow RDP connections without stored credentials (#937)
* Sync role credential shares for OIDC users (#938)
* Fix terminal link dialog layering (#940)
* Confirm large files before opening editor (#942)
* Confirm closing active host connections (#943)
* Preserve file path case in file manager UI (#941)
* fix: preserve unicode guacamole tokens (#933)
* Persist VNC authentication settings (#944)
* Fix Guacamole websocket base path (#946)
* Promote file manager terminals to tabs (#939)
* Guard Guacamole disconnect during startup (#945)
* chore: increment ver
* feat: bitwarden ssh agent integration
* feat: serial connections support
* fix: various small bug fixes
* feat: open all sessions in a folder and terminal custom theme color support
* feat: cross host file manager clipboard and several small bug fixes
* feat: tailscale/wireguard support and added a new status state for when backend is checking status
* feat: grafana like server stats history, new alert system, ntfy/webhook support
* feat: new grid and widget based homepage function
* feat: new donate button in dashboard
* fix: alert ui incorrectly using termix css and fixed issue with alert system not loading
* chore: fix ci checks (#966)
* Fix dashboard service link creation (#950)
* Fix jump host SOCKS5 proxy selection (#951)
* Fix jump host SOCKS5 proxy selection
* fix: type jump host socks proxy config
* Fix tmux detection path handling (#949)
* Support GUACD_URL environment config (#952)
* Retry autostart tunnel host fetches (#953)
* Retry autostart tunnel host fetches
* chore: format tunnel route
* Fix PUID html ownership in Docker entrypoint (#954)
* feat: allow custom tunnel endpoints (#977)
* fix: skip metrics start for non-ssh hosts (#976)
* Fix Proxmox import auth fallback (#956)
* Fix Proxmox import auth fallback
* chore: format proxmox import auth
* Fix SSH heading syntax highlighting (#955)
* Fix SSH heading syntax highlighting
* chore: format terminal highlighter
* Initialize auth before fullscreen terminal routes (#957)
* Add WebAuthn passkey authentication (#959)
* chore: add Biome tooling (#965)
* chore: add biome tooling
* chore: support tailwind syntax in biome
* Fix VNC required argument handshake (#968)
* Prioritize host results in command palette search (#969)
* Prevent sidebar host hover layout shift (#970)
* Add terminal font zoom with mouse wheel (#971)
* Add host temperature metrics card (#972)
* Make file downloads reliable in desktop app (#973)
* Add app rail hover expansion setting (#974)
* fix: use correct translation key for nav.close (#964)
* fix(tunnel): skip endpoint credential validation for direct tunnels (#963)
* fix: SSH port connection bug (#975)
* fix: chunked upload for files >=1.5GB to bypass browser ArrayBuffer limit (#948)
* feat: support SSH agent auth across SSH features (#960)
* feat: add Podman container runtime support (#958)
* chore: update readme and release notes
* fix: stabilize Windows app icon (#978)
* Add safe host sharing export (#979)
* Add external editor support for file manager (#985)
* Rework SSH credential password handling (#984)
* Support password fallback for SSH key credentials
* Complete SSH credential password fallback
* Fix runtime base path for auth callbacks (#982)
* Fix TUI terminal output highlighting (#983)
Co-authored-by: LukeGus
* Add app fullscreen mode (#993)
* Fix host metrics startup polling (#986)
* chore: update release notes
* fix: line chart text overlap
* chore: update RELEASE_NOTES.md
* chore: add donation goal to readme
* chore: update readme
* fix: hide full-screen button in electron app
* fix: failed unit tests
* chore: lint, format, and bump version to 2.5.0
* chore: remove timeout from crowdin translate
* chore: sync Crowdin translations for 2.5.0
---------
Signed-off-by: dependabot[bot]
Co-authored-by: DivByZero
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: devdanetra <46488477+devdanetra@users.noreply.github.com>
Co-authored-by: Aleksandr Fominykh
Co-authored-by: sash
Co-authored-by: ZacharyZcR
---
.github/workflows/dependabot-retarget.yml | 2 +-
.github/workflows/docker.yml | 2 +-
.github/workflows/donation-goal.yml | 126 +
.github/workflows/electron.yml | 14 +-
.github/workflows/openapi.yml | 2 +-
.github/workflows/pr-check.yml | 2 +-
.github/workflows/release.yml | 18 +-
README.md | 49 +-
RELEASE_NOTES.md | 170 +-
biome.json | 58 +
docker/Dockerfile | 8 +-
docker/entrypoint.sh | 4 +-
docker/nginx-https.conf | 75 +
docker/nginx.conf | 75 +
electron-builder.json | 7 +-
electron/main.cjs | 312 +-
electron/preload.js | 20 +
package-lock.json | 1969 ++++--
package.json | 42 +-
readme/HOST-TO-HOST-TRANSFER.md | 185 -
readme/README-AR.md | 49 +-
readme/README-CN.md | 49 +-
readme/README-DE.md | 49 +-
readme/README-ES.md | 49 +-
readme/README-FR.md | 49 +-
readme/README-HI.md | 49 +-
readme/README-IT.md | 49 +-
readme/README-JA.md | 49 +-
readme/README-KO.md | 49 +-
readme/README-PT.md | 49 +-
readme/README-RU.md | 49 +-
readme/README-TR.md | 49 +-
readme/README-VI.md | 49 +-
repo-images/Image 15.png | Bin 0 -> 539961 bytes
repo-images/Image 16.png | Bin 0 -> 76082 bytes
repo-images/donation-goal.svg | 8 +
scripts/crowdin-pretranslate.cjs | 3 +-
scripts/patch-guacamole-lite.cjs | 187 +-
scripts/patch-guacamole-lite.test.ts | 23 +
scripts/publish-youtube.cjs | 3 +-
src/backend/database/database.ts | 6 +
src/backend/database/db/index.ts | 417 +-
src/backend/database/db/schema.ts | 276 +-
.../database/routes/acme-ssl-routes.ts | 17 +-
.../database/routes/alert-rules-routes.ts | 660 ++
.../routes/credential-deploy-routes.ts | 160 +-
src/backend/database/routes/credentials.ts | 10 +-
.../routes/dashboard-service-links-routes.ts | 25 +-
.../routes/homepage-favicon-routes.ts | 103 +
.../database/routes/homepage-items-routes.ts | 225 +
.../database/routes/homepage-layout-routes.ts | 110 +
.../database/routes/homepage-ping-routes.ts | 127 +
.../database/routes/homepage-proxy-routes.ts | 100 +
.../database/routes/homepage-rss-routes.ts | 148 +
.../database/routes/host-bulk-routes.ts | 123 +-
.../database/routes/host-normalizers.test.ts | 10 +
.../database/routes/host-normalizers.ts | 10 +-
src/backend/database/routes/host.ts | 317 +-
src/backend/database/routes/proxmox.ts | 10 +
.../database/routes/service-link-url.test.ts | 28 +
.../database/routes/service-link-url.ts | 16 +
src/backend/database/routes/snippets.ts | 13 +-
.../database/routes/ssh-certificate.test.ts | 127 +
.../database/routes/ssh-certificate.ts | 145 +
.../database/routes/termix-id-keys.test.ts | 95 +
src/backend/database/routes/termix-id-keys.ts | 89 +
src/backend/database/routes/termix-id.test.ts | 147 +
src/backend/database/routes/termix-id.ts | 1550 +++++
.../routes/user-password-reset-routes.ts | 4 +
.../database/routes/user-preferences.ts | 11 +
.../database/routes/user-settings-routes.ts | 12 +-
.../database/routes/user-webauthn-routes.ts | 514 ++
src/backend/database/routes/users.ts | 73 +-
src/backend/database/routes/vault.ts | 450 ++
src/backend/guacamole/guacamole-server.ts | 20 +-
src/backend/guacamole/routes.ts | 11 +-
src/backend/guacamole/token-service.test.ts | 48 +
src/backend/guacamole/token-service.ts | 2 +
src/backend/homepage.ts | 35 +
src/backend/serial/serial.ts | 204 +
src/backend/ssh/alert-engine.ts | 380 ++
src/backend/ssh/container-runtime.ts | 43 +
src/backend/ssh/docker-console.ts | 53 +-
src/backend/ssh/docker-container-routes.ts | 43 +-
src/backend/ssh/docker.ts | 132 +-
.../ssh/file-manager-content-routes.ts | 265 +
src/backend/ssh/file-manager-list-routes.ts | 6 +
src/backend/ssh/file-manager-utils.test.ts | 45 +
src/backend/ssh/file-manager-utils.ts | 37 +
src/backend/ssh/file-manager.ts | 49 +-
src/backend/ssh/host-metrics-helpers.test.ts | 1 +
.../ssh/host-metrics-history-routes.ts | 125 +
src/backend/ssh/host-metrics-jump-hosts.ts | 29 +-
.../ssh/host-metrics-preferences.test.ts | 2 +-
.../ssh/host-metrics-settings-routes.ts | 90 +
src/backend/ssh/host-metrics.ts | 185 +-
src/backend/ssh/host-resolver.ts | 24 +-
src/backend/ssh/jump-host-chain.ts | 29 +-
src/backend/ssh/jump-host-proxy.ts | 51 +
src/backend/ssh/managers/health.ts | 31 +-
src/backend/ssh/managers/index.ts | 4 +
src/backend/ssh/managers/tailscale.ts | 177 +
src/backend/ssh/managers/validation.ts | 17 +
src/backend/ssh/managers/wireguard.ts | 223 +
src/backend/ssh/terminal-agent-auth.test.ts | 135 +
src/backend/ssh/terminal-auth-helpers.ts | 50 +
src/backend/ssh/terminal-jump-hosts.ts | 29 +-
src/backend/ssh/terminal.ts | 418 +-
src/backend/ssh/tmux-helper.test.ts | 20 +
src/backend/ssh/tmux-helper.ts | 31 +-
src/backend/ssh/tmux-monitor.ts | 69 +-
src/backend/ssh/tunnel.ts | 283 +-
src/backend/ssh/vault-oidc-auth.ts | 216 +
src/backend/ssh/vault-signer-auth.ts | 167 +
src/backend/ssh/vault-signer-core.test.ts | 268 +
src/backend/ssh/vault-signer-core.ts | 265 +
src/backend/ssh/widgets/network-collector.ts | 38 +-
.../ssh/widgets/temperature-collector.test.ts | 33 +
.../ssh/widgets/temperature-collector.ts | 117 +
src/backend/starter.ts | 2 +
src/backend/swagger.ts | 4 +
src/backend/utils/alert-trigger.ts | 29 +
src/backend/utils/auth-manager.ts | 25 +
src/backend/utils/field-crypto.ts | 2 +
src/backend/utils/guacd-config.ts | 86 +
src/backend/utils/logger.ts | 1 +
src/backend/utils/notification-sender.ts | 139 +
src/backend/utils/request-origin.test.ts | 108 +
src/backend/utils/request-origin.ts | 48 +
.../utils/shared-credential-manager.ts | 90 +
src/backend/utils/simple-db-ops.ts | 1 +
src/backend/utils/ssh-key-utils.test.ts | 27 +
src/backend/utils/ssh-key-utils.ts | 57 +-
src/backend/utils/user-crypto.ts | 113 +
src/main.tsx | 81 +-
src/types/electron.d.ts | 28 +
src/types/homepage-types.ts | 417 ++
src/types/host-metrics.ts | 8 +-
src/types/index.ts | 46 +-
src/types/stats-widgets.ts | 15 +-
src/types/ui-types.ts | 54 +-
src/ui/AppShell.tsx | 307 +-
src/ui/api/alerts-api.ts | 133 +
src/ui/api/credentials-api.ts | 2 +-
src/ui/api/dashboard-api.ts | 14 +-
src/ui/api/homepage-api.ts | 81 +
src/ui/api/host-metrics-api.ts | 37 +
src/ui/api/open-tabs-api.ts | 1 +
src/ui/api/ssh-file-operations-api.ts | 104 +-
src/ui/api/ssh-host-management-api.ts | 22 +-
src/ui/api/termix-id-api.ts | 217 +
src/ui/api/vault-profiles-api.ts | 60 +
src/ui/api/webauthn-api.ts | 119 +
src/ui/auth/Auth.tsx | 22 +
src/ui/auth/ElectronLoginForm.tsx | 61 +-
src/ui/auth/LoginPage.tsx | 124 +-
src/ui/components/charts/LineChart.tsx | 292 +
.../proxmox/ProxmoxDiscoverDialog.tsx | 12 +-
.../proxmox/proxmox-import-auth.test.ts | 30 +
.../components/proxmox/proxmox-import-auth.ts | 33 +
src/ui/dashboard/DashboardTab.tsx | 513 +-
.../dashboard/cards/HomepagePreviewCard.tsx | 36 +
src/ui/dashboard/cards/NetworkGraphCard.tsx | 4 +-
src/ui/features/file-manager/FileManager.tsx | 251 +-
.../components/DraggableWindow.tsx | 6 +-
.../file-manager/components/FileViewer.tsx | 30 +
.../file-manager/components/FileWindow.tsx | 115 +
.../components/PermissionsDialog.tsx | 2 +-
.../components/TerminalWindow.tsx | 22 +
.../file-manager/file-manager-types.ts | 1 +
src/ui/features/guacamole/GuacamoleApp.tsx | 147 +-
.../guacamole/GuacamoleDisplay.test.ts | 68 +
.../features/guacamole/GuacamoleDisplay.tsx | 98 +-
.../guacamole/guacamole-websocket-url.ts | 31 +
src/ui/features/homepage/HomepageApp.tsx | 12 +
src/ui/features/homepage/HomepageCanvas.tsx | 571 ++
.../homepage/canvas/CanvasDotBackground.tsx | 63 +
.../homepage/canvas/canvasGeometry.test.ts | 80 +
.../homepage/canvas/canvasGeometry.ts | 39 +
.../homepage/canvas/snapToGrid.test.ts | 39 +
src/ui/features/homepage/canvas/snapToGrid.ts | 9 +
src/ui/features/homepage/canvas/useBoxDrag.ts | 66 +
.../features/homepage/canvas/useBoxResize.ts | 82 +
.../homepage/canvas/useCanvasInput.ts | 85 +
.../homepage/dialogs/AddWidgetMenu.tsx | 150 +
.../homepage/dialogs/AlertFeedEditForm.tsx | 46 +
.../homepage/dialogs/BookmarkListEditForm.tsx | 65 +
.../homepage/dialogs/CalendarEditForm.tsx | 41 +
.../homepage/dialogs/ClockEditForm.tsx | 48 +
.../homepage/dialogs/CountdownEditForm.tsx | 57 +
.../homepage/dialogs/CustomApiEditForm.tsx | 137 +
.../dialogs/DashboardLinksEditForm.tsx | 64 +
.../dialogs/DockerActivityEditForm.tsx | 46 +
.../homepage/dialogs/DockerWidgetEditForm.tsx | 18 +
.../dialogs/FileManagerWidgetEditForm.tsx | 18 +
.../homepage/dialogs/FolderEditForm.tsx | 44 +
.../homepage/dialogs/HostGridEditForm.tsx | 90 +
.../homepage/dialogs/HostStatusEditForm.tsx | 122 +
.../homepage/dialogs/IframeEditForm.tsx | 35 +
.../homepage/dialogs/ImageWidgetEditForm.tsx | 71 +
.../homepage/dialogs/LinkTreeEditForm.tsx | 134 +
.../dialogs/MarkdownNotesEditForm.tsx | 76 +
.../homepage/dialogs/MetricsChartEditForm.tsx | 110 +
.../homepage/dialogs/NotesEditForm.tsx | 25 +
.../homepage/dialogs/PingStatusEditForm.tsx | 96 +
.../homepage/dialogs/QuickConnectEditForm.tsx | 143 +
.../dialogs/RecentActivityEditForm.tsx | 92 +
.../homepage/dialogs/RssFeedEditForm.tsx | 56 +
.../homepage/dialogs/SearchBarEditForm.tsx | 82 +
.../homepage/dialogs/SearchLinksEditForm.tsx | 83 +
.../homepage/dialogs/ServiceGridEditForm.tsx | 114 +
.../homepage/dialogs/ServiceLinkEditForm.tsx | 101 +
.../homepage/dialogs/SingleHostEditForm.tsx | 45 +
.../dialogs/SshQuickConnectEditForm.tsx | 105 +
.../homepage/dialogs/SshTerminalEditForm.tsx | 33 +
.../dialogs/SystemOverviewEditForm.tsx | 49 +
.../homepage/dialogs/TermixUptimeEditForm.tsx | 27 +
.../homepage/dialogs/TextBannerEditForm.tsx | 142 +
.../homepage/dialogs/TunnelWidgetEditForm.tsx | 18 +
.../homepage/dialogs/WeatherEditForm.tsx | 59 +
.../homepage/dialogs/WidgetEditDialog.tsx | 378 ++
.../homepage/toolbar/HomepageToolbar.tsx | 127 +
.../homepage/widgets/AlertFeedWidget.tsx | 150 +
.../homepage/widgets/BookmarkListWidget.tsx | 75 +
.../homepage/widgets/CalendarWidget.tsx | 164 +
.../features/homepage/widgets/ClockWidget.tsx | 65 +
.../homepage/widgets/CountdownWidget.tsx | 148 +
.../homepage/widgets/CustomApiWidget.tsx | 207 +
.../homepage/widgets/DashboardLinksWidget.tsx | 147 +
.../homepage/widgets/DockerActivityWidget.tsx | 108 +
.../homepage/widgets/DockerWidget.tsx | 94 +
.../homepage/widgets/FileManagerWidget.tsx | 86 +
.../homepage/widgets/FolderWidget.tsx | 49 +
.../homepage/widgets/HostGridWidget.tsx | 144 +
.../homepage/widgets/HostStatusWidget.tsx | 336 +
.../homepage/widgets/IframeWidget.tsx | 56 +
.../features/homepage/widgets/ImageWidget.tsx | 91 +
.../homepage/widgets/LinkTreeWidget.tsx | 125 +
.../homepage/widgets/MarkdownNotesWidget.tsx | 127 +
.../homepage/widgets/MetricsChartWidget.tsx | 250 +
.../features/homepage/widgets/NotesWidget.tsx | 39 +
.../homepage/widgets/PingStatusWidget.tsx | 138 +
.../homepage/widgets/QuickConnectWidget.tsx | 171 +
.../homepage/widgets/RecentActivityWidget.tsx | 143 +
.../homepage/widgets/RssFeedWidget.tsx | 155 +
.../homepage/widgets/SearchBarWidget.tsx | 103 +
.../homepage/widgets/SearchLinksWidget.tsx | 129 +
.../homepage/widgets/ServiceGridWidget.tsx | 160 +
.../homepage/widgets/ServiceLinkWidget.tsx | 131 +
.../widgets/SshQuickConnectWidget.tsx | 162 +
.../homepage/widgets/SshTerminalWidget.tsx | 169 +
.../homepage/widgets/SystemOverviewWidget.tsx | 135 +
.../homepage/widgets/TermixUptimeWidget.tsx | 145 +
.../homepage/widgets/TextBannerWidget.tsx | 60 +
.../homepage/widgets/TunnelWidget.tsx | 122 +
.../homepage/widgets/WeatherWidget.tsx | 195 +
.../homepage/widgets/WidgetRegistry.ts | 20 +
.../features/homepage/widgets/WidgetShell.tsx | 168 +
.../features/homepage/widgets/WidgetTitle.tsx | 20 +
.../features/host-metrics/HostMetricsTab.tsx | 25 +-
.../host-metrics/MetricsHistorySection.tsx | 222 +
.../host-metrics/cards/CardTimeTabs.tsx | 34 +
.../features/host-metrics/cards/CpuCard.tsx | 126 +-
.../features/host-metrics/cards/DiskCard.tsx | 115 +-
.../host-metrics/cards/MemoryCard.tsx | 117 +-
.../host-metrics/cards/NetworkCard.tsx | 203 +-
.../host-metrics/cards/TemperatureCard.tsx | 56 +
src/ui/features/host-metrics/cards/index.tsx | 30 +-
.../cards/managers/TailscaleManagerCard.tsx | 171 +
.../cards/managers/WireGuardManagerCard.tsx | 209 +
src/ui/features/serial/Serial.tsx | 381 ++
src/ui/features/serial/serial-types.ts | 6 +
src/ui/features/terminal/Terminal.tsx | 504 +-
src/ui/features/terminal/TerminalPreview.tsx | 8 +-
src/ui/features/terminal/terminal-theme.ts | 8 +
src/ui/features/terminal/terminal-types.ts | 1 +
src/ui/features/tunnel/TunnelTab.tsx | 11 +-
src/ui/features/tunnel/tunnel-endpoints.ts | 25 +
src/ui/hooks/use-status-color-scheme.ts | 11 +
src/ui/lib/ServerStatusContext.tsx | 4 +
src/ui/lib/host-connection-tabs.ts | 40 +
src/ui/lib/service-link-url.ts | 16 +
.../lib/terminal-syntax-highlighter.test.ts | 21 +
src/ui/lib/terminal-syntax-highlighter.ts | 44 +-
src/ui/lib/terminal-themes.ts | 28 +
src/ui/lib/theme.ts | 6 +
src/ui/locales/en.json | 561 +-
src/ui/locales/translated/af_ZA.json | 4276 +++++++------
src/ui/locales/translated/ar_SA.json | 5400 ++++++++--------
src/ui/locales/translated/bg_BG.json | 4370 +++++++------
src/ui/locales/translated/bn_BD.json | 4390 +++++++------
src/ui/locales/translated/ca_ES.json | 4230 +++++++------
src/ui/locales/translated/cs_CZ.json | 5384 ++++++++--------
src/ui/locales/translated/da_DK.json | 5208 +++++++++-------
src/ui/locales/translated/de_DE.json | 5152 +++++++++-------
src/ui/locales/translated/el_GR.json | 5358 ++++++++--------
src/ui/locales/translated/es_ES.json | 5194 +++++++++-------
src/ui/locales/translated/fi_FI.json | 5408 +++++++++--------
src/ui/locales/translated/fr_FR.json | 5266 +++++++++-------
src/ui/locales/translated/he_IL.json | 4356 +++++++------
src/ui/locales/translated/hi_IN.json | 4372 +++++++------
src/ui/locales/translated/hu_HU.json | 4288 +++++++------
src/ui/locales/translated/id_ID.json | 4234 +++++++------
src/ui/locales/translated/it_IT.json | 5282 ++++++++--------
src/ui/locales/translated/ja_JP.json | 5358 ++++++++--------
src/ui/locales/translated/ko_KR.json | 4352 +++++++------
src/ui/locales/translated/nl_NL.json | 5180 +++++++++-------
src/ui/locales/translated/no_NO.json | 5274 +++++++++-------
src/ui/locales/translated/pl_PL.json | 5306 ++++++++--------
src/ui/locales/translated/pt_BR.json | 5124 +++++++++-------
src/ui/locales/translated/pt_PT.json | 5302 ++++++++--------
src/ui/locales/translated/ro_RO.json | 5252 +++++++++-------
src/ui/locales/translated/ru_RU.json | 5392 ++++++++--------
src/ui/locales/translated/sr_SP.json | 4382 +++++++------
src/ui/locales/translated/sv_SE.json | 5294 ++++++++--------
src/ui/locales/translated/th_TH.json | 4382 +++++++------
src/ui/locales/translated/tr_TR.json | 4276 +++++++------
src/ui/locales/translated/uk_UA.json | 5392 ++++++++--------
src/ui/locales/translated/vi_VN.json | 4290 +++++++------
src/ui/locales/translated/zh_CN.json | 5154 +++++++++-------
src/ui/locales/translated/zh_TW.json | 4326 +++++++------
src/ui/main-axios.ts | 50 +
src/ui/shell/CommandPalette.tsx | 291 +-
src/ui/shell/TabBar.tsx | 34 +-
src/ui/shell/tabUtils.tsx | 33 +
src/ui/sidebar/AdminSettingsPanel.tsx | 29 +-
src/ui/sidebar/AdminSettingsSections.tsx | 33 +
src/ui/sidebar/AlertRuleDialog.tsx | 274 +
src/ui/sidebar/AlertsPanel.tsx | 460 ++
src/ui/sidebar/AppRail.tsx | 73 +-
src/ui/sidebar/CredentialEditorView.tsx | 528 +-
src/ui/sidebar/HostCredentialList.tsx | 16 +
src/ui/sidebar/HostEditor.tsx | 326 +-
src/ui/sidebar/HostEditorData.test.ts | 46 +-
src/ui/sidebar/HostEditorData.ts | 17 +-
src/ui/sidebar/HostEditorFeatureTabs.tsx | 29 +
src/ui/sidebar/HostManager.tsx | 13 +
src/ui/sidebar/HostManagerData.ts | 12 +-
src/ui/sidebar/HostsPanel.tsx | 35 +-
src/ui/sidebar/NotificationChannelDialog.tsx | 243 +
src/ui/sidebar/SerialPanel.tsx | 352 ++
src/ui/sidebar/SidebarTree.tsx | 53 +-
src/ui/sidebar/SnippetsPanel.tsx | 134 +
src/ui/sidebar/SshToolsPanel.tsx | 54 +-
src/ui/sidebar/TermixIdPanel.tsx | 933 +++
src/ui/sidebar/UserProfilePanel.tsx | 191 +
src/ui/sidebar/VaultProfileManager.tsx | 276 +
src/ui/ssh/dialogs/SSHAuthDialog.tsx | 14 +-
348 files changed, 125184 insertions(+), 76537 deletions(-)
create mode 100644 .github/workflows/donation-goal.yml
create mode 100644 biome.json
delete mode 100644 readme/HOST-TO-HOST-TRANSFER.md
create mode 100644 repo-images/Image 15.png
create mode 100644 repo-images/Image 16.png
create mode 100644 repo-images/donation-goal.svg
create mode 100644 scripts/patch-guacamole-lite.test.ts
create mode 100644 src/backend/database/routes/alert-rules-routes.ts
create mode 100644 src/backend/database/routes/homepage-favicon-routes.ts
create mode 100644 src/backend/database/routes/homepage-items-routes.ts
create mode 100644 src/backend/database/routes/homepage-layout-routes.ts
create mode 100644 src/backend/database/routes/homepage-ping-routes.ts
create mode 100644 src/backend/database/routes/homepage-proxy-routes.ts
create mode 100644 src/backend/database/routes/homepage-rss-routes.ts
create mode 100644 src/backend/database/routes/service-link-url.test.ts
create mode 100644 src/backend/database/routes/service-link-url.ts
create mode 100644 src/backend/database/routes/ssh-certificate.test.ts
create mode 100644 src/backend/database/routes/ssh-certificate.ts
create mode 100644 src/backend/database/routes/termix-id-keys.test.ts
create mode 100644 src/backend/database/routes/termix-id-keys.ts
create mode 100644 src/backend/database/routes/termix-id.test.ts
create mode 100644 src/backend/database/routes/termix-id.ts
create mode 100644 src/backend/database/routes/user-webauthn-routes.ts
create mode 100644 src/backend/database/routes/vault.ts
create mode 100644 src/backend/guacamole/token-service.test.ts
create mode 100644 src/backend/homepage.ts
create mode 100644 src/backend/serial/serial.ts
create mode 100644 src/backend/ssh/alert-engine.ts
create mode 100644 src/backend/ssh/container-runtime.ts
create mode 100644 src/backend/ssh/host-metrics-history-routes.ts
create mode 100644 src/backend/ssh/jump-host-proxy.ts
create mode 100644 src/backend/ssh/managers/tailscale.ts
create mode 100644 src/backend/ssh/managers/wireguard.ts
create mode 100644 src/backend/ssh/terminal-agent-auth.test.ts
create mode 100644 src/backend/ssh/tmux-helper.test.ts
create mode 100644 src/backend/ssh/vault-oidc-auth.ts
create mode 100644 src/backend/ssh/vault-signer-auth.ts
create mode 100644 src/backend/ssh/vault-signer-core.test.ts
create mode 100644 src/backend/ssh/vault-signer-core.ts
create mode 100644 src/backend/ssh/widgets/temperature-collector.test.ts
create mode 100644 src/backend/ssh/widgets/temperature-collector.ts
create mode 100644 src/backend/utils/alert-trigger.ts
create mode 100644 src/backend/utils/guacd-config.ts
create mode 100644 src/backend/utils/notification-sender.ts
create mode 100644 src/backend/utils/request-origin.test.ts
create mode 100644 src/types/homepage-types.ts
create mode 100644 src/ui/api/alerts-api.ts
create mode 100644 src/ui/api/homepage-api.ts
create mode 100644 src/ui/api/termix-id-api.ts
create mode 100644 src/ui/api/vault-profiles-api.ts
create mode 100644 src/ui/api/webauthn-api.ts
create mode 100644 src/ui/components/charts/LineChart.tsx
create mode 100644 src/ui/components/proxmox/proxmox-import-auth.test.ts
create mode 100644 src/ui/components/proxmox/proxmox-import-auth.ts
create mode 100644 src/ui/dashboard/cards/HomepagePreviewCard.tsx
create mode 100644 src/ui/features/guacamole/GuacamoleDisplay.test.ts
create mode 100644 src/ui/features/guacamole/guacamole-websocket-url.ts
create mode 100644 src/ui/features/homepage/HomepageApp.tsx
create mode 100644 src/ui/features/homepage/HomepageCanvas.tsx
create mode 100644 src/ui/features/homepage/canvas/CanvasDotBackground.tsx
create mode 100644 src/ui/features/homepage/canvas/canvasGeometry.test.ts
create mode 100644 src/ui/features/homepage/canvas/canvasGeometry.ts
create mode 100644 src/ui/features/homepage/canvas/snapToGrid.test.ts
create mode 100644 src/ui/features/homepage/canvas/snapToGrid.ts
create mode 100644 src/ui/features/homepage/canvas/useBoxDrag.ts
create mode 100644 src/ui/features/homepage/canvas/useBoxResize.ts
create mode 100644 src/ui/features/homepage/canvas/useCanvasInput.ts
create mode 100644 src/ui/features/homepage/dialogs/AddWidgetMenu.tsx
create mode 100644 src/ui/features/homepage/dialogs/AlertFeedEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/BookmarkListEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/CalendarEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/ClockEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/CountdownEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/CustomApiEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/DashboardLinksEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/DockerActivityEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/DockerWidgetEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/FileManagerWidgetEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/FolderEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/HostGridEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/HostStatusEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/IframeEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/ImageWidgetEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/LinkTreeEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/MarkdownNotesEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/MetricsChartEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/NotesEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/PingStatusEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/QuickConnectEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/RecentActivityEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/RssFeedEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/SearchBarEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/SearchLinksEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/ServiceGridEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/ServiceLinkEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/SingleHostEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/SshQuickConnectEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/SshTerminalEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/SystemOverviewEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/TermixUptimeEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/TextBannerEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/TunnelWidgetEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/WeatherEditForm.tsx
create mode 100644 src/ui/features/homepage/dialogs/WidgetEditDialog.tsx
create mode 100644 src/ui/features/homepage/toolbar/HomepageToolbar.tsx
create mode 100644 src/ui/features/homepage/widgets/AlertFeedWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/BookmarkListWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/CalendarWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/ClockWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/CountdownWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/CustomApiWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/DashboardLinksWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/DockerActivityWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/DockerWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/FileManagerWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/FolderWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/HostGridWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/HostStatusWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/IframeWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/ImageWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/LinkTreeWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/MarkdownNotesWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/MetricsChartWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/NotesWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/PingStatusWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/QuickConnectWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/RecentActivityWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/RssFeedWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/SearchBarWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/SearchLinksWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/ServiceGridWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/ServiceLinkWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/SshQuickConnectWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/SshTerminalWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/SystemOverviewWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/TermixUptimeWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/TextBannerWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/TunnelWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/WeatherWidget.tsx
create mode 100644 src/ui/features/homepage/widgets/WidgetRegistry.ts
create mode 100644 src/ui/features/homepage/widgets/WidgetShell.tsx
create mode 100644 src/ui/features/homepage/widgets/WidgetTitle.tsx
create mode 100644 src/ui/features/host-metrics/MetricsHistorySection.tsx
create mode 100644 src/ui/features/host-metrics/cards/CardTimeTabs.tsx
create mode 100644 src/ui/features/host-metrics/cards/TemperatureCard.tsx
create mode 100644 src/ui/features/host-metrics/cards/managers/TailscaleManagerCard.tsx
create mode 100644 src/ui/features/host-metrics/cards/managers/WireGuardManagerCard.tsx
create mode 100644 src/ui/features/serial/Serial.tsx
create mode 100644 src/ui/features/serial/serial-types.ts
create mode 100644 src/ui/features/tunnel/tunnel-endpoints.ts
create mode 100644 src/ui/lib/host-connection-tabs.ts
create mode 100644 src/ui/lib/service-link-url.ts
create mode 100644 src/ui/sidebar/AlertRuleDialog.tsx
create mode 100644 src/ui/sidebar/AlertsPanel.tsx
create mode 100644 src/ui/sidebar/NotificationChannelDialog.tsx
create mode 100644 src/ui/sidebar/SerialPanel.tsx
create mode 100644 src/ui/sidebar/TermixIdPanel.tsx
create mode 100644 src/ui/sidebar/VaultProfileManager.tsx
diff --git a/.github/workflows/dependabot-retarget.yml b/.github/workflows/dependabot-retarget.yml
index 36f904ca..a3edad47 100644
--- a/.github/workflows/dependabot-retarget.yml
+++ b/.github/workflows/dependabot-retarget.yml
@@ -14,7 +14,7 @@ jobs:
runs-on: blacksmith-2vcpu-ubuntu-2404
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
fetch-depth: 1
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index b8fb0e7f..38098726 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -35,7 +35,7 @@ jobs:
runs-on: blacksmith-8vcpu-ubuntu-2404
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
fetch-depth: 1
diff --git a/.github/workflows/donation-goal.yml b/.github/workflows/donation-goal.yml
new file mode 100644
index 00000000..e495594f
--- /dev/null
+++ b/.github/workflows/donation-goal.yml
@@ -0,0 +1,126 @@
+name: Donation Goal Badge
+
+on:
+ schedule:
+ - cron: "0 */6 * * *"
+ workflow_dispatch:
+
+jobs:
+ update:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Generate donation goal SVG
+ run: |
+ set -e
+
+ EVM_ADDRESS="0x83eA1Db55cc6E34fCD11Da2b7849621af67b6E34"
+ BTC_ADDRESS="bc1qrxc3vpnl6qhh9p8akjmjukcgmgmq852ua64h05"
+ SOL_ADDRESS="T4BF5ioySVUjwaPNw4Sdu7oK8SXLxgQRcMaTQ6YJ2UJ"
+ DOCS_SNAPSHOT_URL="https://raw.githubusercontent.com/Termix-SSH/Termix-Docs/main/static/donation-snapshot.json"
+ MONTHLY_GOAL=750
+
+ # Fetch crypto prices
+ PRICES=$(curl -sf "https://api.coingecko.com/api/v3/simple/price?ids=ethereum,bitcoin,solana&vs_currencies=usd" || echo '{}')
+ ETH_PRICE=$(echo "$PRICES" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('ethereum',{}).get('usd',0))" 2>/dev/null || echo 0)
+ BTC_PRICE=$(echo "$PRICES" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('bitcoin',{}).get('usd',0))" 2>/dev/null || echo 0)
+ SOL_PRICE=$(echo "$PRICES" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('solana',{}).get('usd',0))" 2>/dev/null || echo 0)
+
+ # Fetch live balances
+ ETH_RAW=$(curl -sf "https://eth.blockscout.com/api/v2/addresses/${EVM_ADDRESS}" || echo '{"coin_balance":"0"}')
+ ETH_BAL=$(echo "$ETH_RAW" | python3 -c "import sys,json; d=json.load(sys.stdin); print(int(d.get('coin_balance','0')) / 1e18)" 2>/dev/null || echo 0)
+
+ BASE_RAW=$(curl -sf "https://base.blockscout.com/api/v2/addresses/${EVM_ADDRESS}" || echo '{"coin_balance":"0"}')
+ BASE_BAL=$(echo "$BASE_RAW" | python3 -c "import sys,json; d=json.load(sys.stdin); print(int(d.get('coin_balance','0')) / 1e18)" 2>/dev/null || echo 0)
+
+ BTC_RAW=$(curl -sf "https://blockstream.info/api/address/${BTC_ADDRESS}" || echo '{"chain_stats":{"funded_txo_sum":0,"spent_txo_sum":0}}')
+ BTC_BAL=$(echo "$BTC_RAW" | python3 -c "import sys,json; d=json.load(sys.stdin); s=d.get('chain_stats',{}); print((s.get('funded_txo_sum',0)-s.get('spent_txo_sum',0))/1e8)" 2>/dev/null || echo 0)
+
+ SOL_RAW=$(curl -sf -X POST "https://api.mainnet-beta.solana.com" \
+ -H "Content-Type: application/json" \
+ -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"getBalance\",\"params\":[\"${SOL_ADDRESS}\"]}" || echo '{"result":{"value":0}}')
+ SOL_BAL=$(echo "$SOL_RAW" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('result',{}).get('value',0)/1e9)" 2>/dev/null || echo 0)
+
+ # Fetch snapshot baseline from docs repo
+ SNAPSHOT=$(curl -sf "$DOCS_SNAPSHOT_URL" || echo '{"ethBal":0,"baseBal":0,"btcBal":0,"solBal":0}')
+ SNAP_ETH=$(echo "$SNAPSHOT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('ethBal',0))" 2>/dev/null || echo 0)
+ SNAP_BASE=$(echo "$SNAPSHOT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('baseBal',0))" 2>/dev/null || echo 0)
+ SNAP_BTC=$(echo "$SNAPSHOT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('btcBal',0))" 2>/dev/null || echo 0)
+ SNAP_SOL=$(echo "$SNAPSHOT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('solBal',0))" 2>/dev/null || echo 0)
+
+ export ETH_BAL BASE_BAL BTC_BAL SOL_BAL SNAP_ETH SNAP_BASE SNAP_BTC SNAP_SOL ETH_PRICE BTC_PRICE SOL_PRICE MONTHLY_GOAL
+
+ python3 - <<'PYEOF'
+ import os, math
+
+ eth_bal = float(os.environ.get('ETH_BAL', 0))
+ base_bal = float(os.environ.get('BASE_BAL', 0))
+ btc_bal = float(os.environ.get('BTC_BAL', 0))
+ sol_bal = float(os.environ.get('SOL_BAL', 0))
+ snap_eth = float(os.environ.get('SNAP_ETH', 0))
+ snap_base = float(os.environ.get('SNAP_BASE', 0))
+ snap_btc = float(os.environ.get('SNAP_BTC', 0))
+ snap_sol = float(os.environ.get('SNAP_SOL', 0))
+ eth_price = float(os.environ.get('ETH_PRICE', 0))
+ btc_price = float(os.environ.get('BTC_PRICE', 0))
+ sol_price = float(os.environ.get('SOL_PRICE', 0))
+ goal = float(os.environ.get('MONTHLY_GOAL', 750))
+
+ delta_eth = max(0, eth_bal - snap_eth)
+ delta_base = max(0, base_bal - snap_base)
+ delta_btc = max(0, btc_bal - snap_btc)
+ delta_sol = max(0, sol_bal - snap_sol)
+
+ total = delta_eth * eth_price + delta_base * eth_price + delta_btc * btc_price + delta_sol * sol_price
+ pct = min(total / goal * 100, 100) if goal > 0 else 0
+
+ if total < 1:
+ display = '< $1'
+ else:
+ display = f'${math.floor(total):,}'
+
+ goal_display = f'${int(goal):,}'
+
+ # SVG dimensions
+ W = 400
+ BAR_W = 360
+ BAR_X = 20
+ BAR_H = 8
+ FILL_W = round(BAR_W * pct / 100)
+
+ label = f'Monthly Donation Goal {display} / {goal_display}'
+ pct_label = f'{round(pct)}% of monthly goal' if pct < 100 else 'Goal reached this month. Thank you!'
+
+ svg = f'''
+ Monthly donation goal
+
+ {label}
+
+
+ {pct_label}
+ '''
+
+ with open('repo-images/donation-goal.svg', 'w') as f:
+ f.write(svg)
+ print(f'SVG written: {display} / {goal_display} ({round(pct)}%)')
+ PYEOF
+ - name: Commit SVG
+ run: |
+ git config user.name "github-actions[bot]"
+ git config user.email "github-actions[bot]@users.noreply.github.com"
+ git add repo-images/donation-goal.svg
+ if git diff --cached --quiet; then
+ exit 0
+ fi
+ # Amend the previous bot commit if it exists, otherwise create a new one
+ LAST_AUTHOR=$(git log -1 --format='%ae')
+ if [ "$LAST_AUTHOR" = "github-actions[bot]@users.noreply.github.com" ]; then
+ git commit --amend --no-edit
+ git push --force-with-lease
+ else
+ git commit -m "chore: update donation goal badge"
+ git push
+ fi
diff --git a/.github/workflows/electron.yml b/.github/workflows/electron.yml
index e6e6e4ad..c6ac48ed 100644
--- a/.github/workflows/electron.yml
+++ b/.github/workflows/electron.yml
@@ -52,7 +52,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
fetch-depth: 1
@@ -142,7 +142,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
fetch-depth: 1
@@ -352,7 +352,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
fetch-depth: 1
@@ -578,7 +578,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
fetch-depth: 1
@@ -684,7 +684,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
fetch-depth: 1
@@ -821,7 +821,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
fetch-depth: 1
@@ -931,7 +931,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
fetch-depth: 1
diff --git a/.github/workflows/openapi.yml b/.github/workflows/openapi.yml
index 2ea9b852..c76952da 100644
--- a/.github/workflows/openapi.yml
+++ b/.github/workflows/openapi.yml
@@ -10,7 +10,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
- name: Setup Node.js
uses: actions/setup-node@v6
diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml
index 6c40ebae..2e48e167 100644
--- a/.github/workflows/pr-check.yml
+++ b/.github/workflows/pr-check.yml
@@ -13,7 +13,7 @@ jobs:
steps:
- name: Checkout code
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
- name: Setup Node.js
uses: actions/setup-node@v6
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 1b17cb42..a3940b86 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -39,7 +39,7 @@ jobs:
exit 1
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
fetch-depth: 1
@@ -85,7 +85,7 @@ jobs:
runs-on: blacksmith-2vcpu-ubuntu-2404
steps:
- name: Checkout dev branch
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
ref: ${{ needs.prep.outputs.dev_branch }}
fetch-depth: 0
@@ -137,7 +137,7 @@ jobs:
runs-on: blacksmith-2vcpu-ubuntu-2404
steps:
- name: Checkout dev branch
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
ref: ${{ needs.prep.outputs.dev_branch }}
fetch-depth: 0
@@ -220,7 +220,7 @@ jobs:
build_ref: ${{ steps.merge.outputs.build_ref }}
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
fetch-depth: 1
@@ -297,7 +297,7 @@ jobs:
contents: write
steps:
- name: Checkout main
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
ref: main
fetch-depth: 1
@@ -370,7 +370,7 @@ jobs:
contents: write
steps:
- name: Checkout main
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
ref: main
fetch-depth: 1
@@ -409,7 +409,7 @@ jobs:
runs-on: blacksmith-2vcpu-ubuntu-2404
steps:
- name: Checkout Termix
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
ref: ${{ needs.merge-to-main.outputs.build_ref }}
fetch-depth: 1
@@ -429,7 +429,7 @@ jobs:
npm run generate:openapi
- name: Checkout Docs repository
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
repository: Termix-SSH/Docs
ref: main
@@ -498,7 +498,7 @@ jobs:
runs-on: blacksmith-2vcpu-ubuntu-2404
steps:
- name: Checkout main
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
ref: main
fetch-depth: 1
diff --git a/README.md b/README.md
index 92c5426f..e1ac7b30 100644
--- a/README.md
+++ b/README.md
@@ -33,6 +33,12 @@
+Termix is free and open source. If you find it useful, consider [donating](https://donate.termix.site/) to help cover server costs and development time.
+
+
+
+
+
@@ -88,8 +94,8 @@ Manage files directly on remote servers with support for viewing and editing cod
-**Docker Management:**
-Start, stop, pause, remove containers. View container stats. Control container using docker exec terminal. It was not made to replace Portainer or Dockge but rather to simply manage your containers compared to creating them.
+**Docker and Podman Management:**
+Start, stop, pause, remove containers. View container stats. Control containers using a docker exec terminal. Supports both Docker and Podman as the container runtime. It was not made to replace Portainer or Dockge but rather to simply manage your containers compared to creating them.
@@ -103,13 +109,13 @@ Save, organize, and manage your SSH connections with tags and folders (folder cu
**Host Metrics:**
-View CPU, memory, disk usage, network, uptime, system information, firewall, port monitor, log viewer, users/permissions, certificates, and many more which work on most Linux based servers.
+View CPU, memory, disk usage, network, uptime, system information, firewall, port monitor, log viewer, users/permissions, certificates, and many more which work on most Linux based servers. Includes time-series history graphs and threshold-based alerts with ntfy and webhook support.
**User Authentication:**
-Secure user management with admin controls and OIDC/LDAP/SSO (with access control) and 2FA (TOTP) support. View active user sessions across all platforms and revoke permissions. Link your OIDC/Local accounts together. View audit log of all users actions.
+Secure user management with admin controls and OIDC/LDAP/SSO (with access control), 2FA (TOTP), and passkey (WebAuthn) support. View active user sessions across all platforms and revoke permissions. Link your OIDC/Local accounts together. View audit log of all users actions.
@@ -130,32 +136,52 @@ Create roles and share hosts across users/roles.
+**Serial Connections:**
+Connect to serial devices (routers, switches, microcontrollers, etc.) directly from the browser or desktop app. Configure baud rate, data bits, stop bits, and parity. Uses the Web Serial API in supported browsers or a native backend in the Electron app.
+
+
+
+
+**Alerts:**
+Set threshold-based alert rules on host metrics (CPU, memory, disk, etc.) and get notified via ntfy or webhooks when they fire. View firing and resolved alerts in a history log.
+
+
+
+
+
+
+**Homepage:**
+A fully customizable homepage with a drag-and-drop widget grid. Add widgets for host status, service links, clocks, notes, RSS feeds, weather, Docker containers, host metrics charts, embedded terminals, iframes, and more.
+
+
+
+
**Database Encryption:**
Backend stored as encrypted SQLite database files. View [docs](https://docs.termix.site/security) for more.
+
+
**Network Graph:**
Customize your Dashboard to visualize your homelab based off your SSH connections with status support.
-
-
**SSH Tools:**
Create reusable command snippets that execute with a single click. Run one command simultaneously across multiple open terminals.
+
+
**Persistent Tabs:**
SSH sessions and tabs stay open across devices/refreshes if enabled in user profile.
-
-
**Languages:**
@@ -180,7 +206,8 @@ Built-in support ~30 languages (managed by [Crowdin](https://docs.termix.site/tr
- **Quick Connect** - Connect to a server without having to save the connection data
- **Command Palette** - Double tap left shift to quickly access SSH connections with your keyboard
- **Proxmox Integration** - Auto-add hosts into Termix from your Proxmox instance
-- **SSH Feature Rich** - Supports jump hosts, Warpgate, TOTP based connections, SOCKS5, host key verification, password autofill, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, terminal logging, etc.
+- **SSH Feature Rich** - Supports jump hosts, Warpgate, TOTP based connections, SOCKS5, host key verification, password autofill, [OPKSSH](https://github.com/openpubkey/opkssh), tmux, port knocking, terminal logging, SSH agent forwarding, Bitwarden SSH agent, HashiCorp Vault SSH signing, and more.
+- **Termix ID** - A sshid.io equivalent built into Termix. Claim a handle, publish your public SSH keys at a resolver URL, and use a built-in CA to issue SSH certificates.
@@ -314,6 +341,10 @@ Termix is free and open source with no subscriptions or paid plans. If you find
+
+
+
+
Some videos and images may be out of date or may not perfectly showcase features.
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 160a559e..2718dc5c 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,123 +1,81 @@
-Dozens of bug fixes and small new features, including VNC/RDP sharing, OIDC improvements, file manager fixes, SSH jump host fixes, terminal enhancements, RDP keyboard layout support, and much more.
+Major new features including serial connections, Tailscale/WireGuard support, HashiCorp Vault SSH auth, Bitwarden SSH agent, WebAuthn passkeys, Podman support, a new grid-based dashboard, host metrics history with alerting, and much more.
-https://youtu.be/ImwAbm4hW-k
+https://youtu.be/c3UD4q2jW_8
-- VNC, RDP, and Telnet credential sharing support
-- Default host settings (SOCKS5, credentials, terminal settings, and more)
-- Custom terminal background image per host
-- Silent OIDC login configurable as default (no URL parameter required)
-- Bulk open SSH sessions for multiple selected hosts at once
-- Improved Nord theme contrast and accessibility
-- Admin can manually create users while registration is disabled
-- OIDC auto-provisioned users supported when registration and password login are disabled
-- OIDC username usable as SSH username credential
-- Custom labels and drag-to-reorder for sessions in the Connections panel
-- Appearance and profile settings persisted to the database across devices
-- Saved server URL dropdown in the app connection screen
-- File manager text editor font size adjustment
-- Tab key shortcut for entering your username in the terminal
-- Shift+Tab hotkey support for mobile
-- Terminal keyboard shortcuts support
-- UTF-8 encoding support in the file manager
-- Optional broadcast address for Wake-on-LAN packets
-- SFTP legacy mode support
-- Snippets JSON import and export with folder metadata
-- Clickable links and service shortcuts on the dashboard
-- Host status color indicators restored to sidebar
-- Per-host configuration to set tab title to shell's window title instead of host name
-- Split screen hotkeys
-- Support for AZERTY and other non-QWERTY keyboard layouts in RDP
-- RDP load balancing info and Connection Broker Cookie support
-- Per-connection guacd proxy host and port configuration
-- Deep link support for bookmarking direct SSH or file manager sessions
-- ACME/Certbot SSL certificate support
-- Import hosts from SSH config file
-- OIDC with custom CA certificate support
-- Open files directly in the file manager text editor
-- File manager text editor Ctrl+W capture to prevent accidental tab close
-- Option to collapse hosts to a single line in the sidebar
-- Select a different backend Termix server from within the app
-- Windows portable app now truly portable (no registry writes)
-- Bundle fonts for offline environments
-- Option to disable clickable links in the terminal
-- Proxmox login options including OPKSSH
-- UI suggestions and general interface improvements
-- Per-protocol host metrics (online/offline detection) configuration
-- Increase file manager max upload size by splicing files and reassembling them (~5GB)
+- Termix ID with a public handle, hosted public key resolver, and built-in CA for issuing SSH certificates
+- Serial connections support
+- Tailscale and WireGuard VPN host integration with status detection
+- HashiCorp Vault SSH signer authentication
+- Bitwarden SSH agent integration
+- WebAuthn passkey authentication
+- Podman container runtime support alongside Docker
+- SSH agent forwarding support across all SSH features
+- New grid and widget-based dashboard homepage
+- Grafana-style server stats history graphs
+- Alert system with ntfy and webhook notification support
+- Host temperature metrics card
+- App fullscreen mode
+- External editor support for file manager (desktop app)
+- Safe host sharing export
+- SSH credential password fallback for key-based auth
+- Open all sessions in a folder at once
+- Custom terminal theme color support
+- Custom tunnel endpoints configuration
+- GUACD_URL environment variable support
+- App rail hover expansion setting
+- Terminal font zoom with mouse wheel
+- File manager terminals promoted to full tabs
+- Donate button on dashboard
+- PuTTY PPK SSH key support
+- Confirmation dialog when closing active host connections
+- Confirmation prompt before opening large files in the editor
+- Cross-host file manager clipboard
+- Prioritize host results in command palette search
+- Retry autostart tunnel host fetches on failure
-- File transfers over 100MB failing
-- File transfers over 30 seconds aborted by axios timeout
-- SSH terminal through jump host chain timing out with malformed SSH messages
-- Cloning a host with SSH key auth not allowing auth method change on the clone
-- File deletion in the file manager affecting selected files in inactive tabs
-- File manager not connecting when cert passphrase is not saved
-- File text editor cursor position lost on save
-- macOS Option + Left/Right Arrow outputting raw ANSI sequences instead of moving cursor by word
-- File manager tree view not sorted alphabetically
-- SFTP failing with timeout when using a jump host chain
-- SSH key auth with Duo not showing prompt and failing immediately
-- Enabling 2FA with password login disabled causing a login deadlock
-- Failed to disable 2FA even when providing correct TOTP or password
-- Arrow buttons not working in Midnight Commander on the Android app
-- Credential deploy command copy failing silently (clipboard API unavailable in some browsers)
-- Disabling password login still showing the password login form
-- Folder picker in the new host form not showing existing folders
-- Command palette always opening hosts as SSH terminal regardless of protocol settings
-- Saving SSH credentials failing on fresh installs
-- Import hosts failing when hosts use SSH key credentials
-- Warpgate authentication prompting for a password unnecessarily
-- File manager folder icon not appearing under host name on hover
-- File manager delete silently failing on Windows hosts (rm -f not supported by PowerShell)
-- SSH terminal failing with keepalive timeout when server MOTD is slow to load
-- SSH connection via SOCKS5 proxy failing after update
-- Linking an OIDC account to a password account not working
-- User password copy missing and RBAC issues in admin panel
-- Debian and Android packages not opening the server on launch
-- Username field in host edit and new host form having no effect
-- Mobile terminal crashing on iOS with React error 130
-- Network interface symbols incorrect in host metrics
-- Sudo password auto-fill not working on Ubuntu Server 26.04
-- get_cwd command injecting into live PTY and corrupting interactive programs
-- Initial directory command failing on Windows PowerShell SSH targets
-- 3-way split not working with layout issues
-- Docker management not working for Windows hosts
-- Docker management failing on Debian with exit code 1
-- Docker logs not respecting the current theme
-- API not responding correctly after update
-- Caddy reverse proxy configuration not working
-- Wrong keyboard layout in VNC sessions
-- KDE scaling issues in the desktop app
-- Linux app failing to start due to better-sqlite3 Node version mismatch
-- Tab autocomplete not working in the macOS x86 app
-- Cloudflare SSL tunnels with third-level subdomains blocking Termix web portal access
-- Fzf completion not working in the Android app
-- SSH terminal frame delivery jitter in Docker (ssh2 native crypto not compiled)
-- OIDC login failing in Linux and Android apps when Authentik has a Captcha stage
-- Android app crashing after entering password when auth is set to None
-- Editing or saving a host clearing the password for RDP and VNC connections
-- Hardcoded 30-minute open-tabs TTL defeating session persistence
-- Keyboard mapping issues on Windows Server 2019 via RDP
-- Shared server not appearing for other users
-- RBAC role assignment failing for OIDC users
-- SSO configuration broken when supplied via environment variables
-- RDP requiring credentials even when none are needed
-- Terminal outputting success right after folder path
-- GitHub/Google SSO provider giving ERR_INVALID_URL
-- Keyboard focus not on main screen when selecting tmux session
-- Remove all references to SALT variable
-- Syntax highlighter duplicating path, visual corruptions, cursor jumps, etc.
-- RDP session screen clips/spills over on viewport resize
+- SSH port connection bug
+- VNC required argument handshake failure
+- Jump host SOCKS5 proxy selection using wrong proxy
+- Tunnel endpoint resolution failing in some configurations
+- Direct tunnel skipping endpoint credential validation incorrectly
+- Dashboard host routing ignoring protocol settings
+- Dashboard service link creation broken
+- File manager uploads failing with 400 error and missing schema migrations on upgrade
+- Large file manager uploads not chunked (chunked for files >=1.5GB)
+- File uploads over 100MB failing due to ArrayBuffer browser limit
+- File path case not preserved in file manager UI
+- File downloads unreliable in the desktop app
+- Tmux detection path handling incorrect
+- Host metrics startup polling incorrect
+- TUI terminal output highlighting incorrect
+- Runtime base path for auth callbacks incorrect
+- Windows app icon unstable
+- SSH heading syntax highlighting broken
+- Terminal link dialog layering issue
+- Electron OIDC browser authentication failures
+- Proxmox import auth fallback not working
+- OIDC role credential shares not synced for OIDC users
+- RDP connections requiring credentials when none are needed
+- VNC authentication settings not persisted
+- Guacamole unicode token corruption
+- Guacamole websocket base path incorrect
+- Guacamole disconnect during startup crash
+- Host metrics starting for non-SSH hosts
+- Sidebar host hover causing layout shift
+- Alert UI incorrectly applying Termix CSS and alert system failing to load
+- Translation key incorrect for nav close action
+- PUID HTML ownership in Docker entrypoint
diff --git a/biome.json b/biome.json
new file mode 100644
index 00000000..68bf4687
--- /dev/null
+++ b/biome.json
@@ -0,0 +1,58 @@
+{
+ "$schema": "https://biomejs.dev/schemas/2.5.1/schema.json",
+ "vcs": {
+ "enabled": true,
+ "clientKind": "git",
+ "useIgnoreFile": true,
+ "defaultBranch": "dev-2.5.0"
+ },
+ "files": {
+ "ignoreUnknown": true,
+ "includes": [
+ "**",
+ "!!build",
+ "!!coverage",
+ "!!dist",
+ "!!dist-ssr",
+ "!!release",
+ "!!node_modules",
+ "!!src/mcp-server/node_modules",
+ "!!db",
+ "!!.env",
+ "!!**/*.min.js",
+ "!!**/*.min.css",
+ "!!openapi.json"
+ ]
+ },
+ "formatter": {
+ "enabled": true,
+ "indentStyle": "space",
+ "indentWidth": 2,
+ "lineWidth": 80,
+ "lineEnding": "lf"
+ },
+ "linter": {
+ "enabled": false
+ },
+ "javascript": {
+ "formatter": {
+ "quoteStyle": "double",
+ "semicolons": "always",
+ "trailingCommas": "all",
+ "arrowParentheses": "always"
+ }
+ },
+ "json": {
+ "formatter": {
+ "trailingCommas": "none"
+ }
+ },
+ "css": {
+ "parser": {
+ "tailwindDirectives": true
+ }
+ },
+ "assist": {
+ "enabled": false
+ }
+}
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 5b2c28e1..a016afbd 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -1,5 +1,5 @@
# Stage 1: Install dependencies
-FROM node:26-slim AS deps
+FROM node:24-slim AS deps
WORKDIR /app
RUN apt-get update && apt-get install -y python3 make g++ && rm -rf /var/lib/apt/lists/*
@@ -36,7 +36,7 @@ RUN npm rebuild better-sqlite3
RUN npm run build:backend
# Stage 4: Production dependencies only
-FROM node:26-slim AS production-deps
+FROM node:24-slim AS production-deps
WORKDIR /app
RUN apt-get update && apt-get install -y python3 make g++ && rm -rf /var/lib/apt/lists/*
@@ -53,7 +53,7 @@ RUN npm ci --omit=dev --ignore-scripts && \
npm cache clean --force
# Stage 5: Final optimized image
-FROM node:26-slim
+FROM node:24-slim
WORKDIR /app
ENV DATA_DIR=/app/data \
@@ -78,7 +78,7 @@ COPY --chown=node:node package.json ./
VOLUME ["/app/data"]
-EXPOSE ${PORT} 30001 30002 30003 30004 30005 30006
+EXPOSE ${PORT} 30001 30002 30003 30004 30005 30006 30007 30008 30009 30010 30011 30012
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD wget -q -O /dev/null http://localhost:30001/health || exit 1
diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh
index c46e28c2..917ea608 100644
--- a/docker/entrypoint.sh
+++ b/docker/entrypoint.sh
@@ -14,7 +14,7 @@ if [ "$(id -u)" = "0" ]; then
groupmod -o -g "$PGID" node 2>/dev/null || true
usermod -o -u "$PUID" node 2>/dev/null || true
- chown -R node:node /app/data /app/uploads /tmp/nginx 2>/dev/null || true
+ chown -R node:node /app/data /app/uploads /app/html /tmp/nginx 2>/dev/null || true
echo "User node is now UID: $PUID, GID: $PGID"
@@ -167,4 +167,4 @@ node dist/backend/backend/starter.js
echo "All services started"
-tail -f /dev/null
\ No newline at end of file
+tail -f /dev/null
diff --git a/docker/nginx-https.conf b/docker/nginx-https.conf
index ee58e8fb..d2e38f5b 100644
--- a/docker/nginx-https.conf
+++ b/docker/nginx-https.conf
@@ -159,6 +159,33 @@ http {
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
+ location ~ ^/alert-rules(/.*)?$ {
+ proxy_pass http://127.0.0.1:30001;
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
+ }
+
+ location ~ ^/notification-channels(/.*)?$ {
+ proxy_pass http://127.0.0.1:30001;
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
+ }
+
+ location ~ ^/alert-firings(/.*)?$ {
+ proxy_pass http://127.0.0.1:30001;
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
+ }
+
location ~ ^/rbac(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
@@ -190,6 +217,24 @@ http {
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
+ location ~ ^/vault(/.*)?$ {
+ proxy_pass http://127.0.0.1:30001;
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
+ }
+
+ location ~ ^/termix-id(/.*)?$ {
+ proxy_pass http://127.0.0.1:30001;
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ }
+
location ~ ^/c2s-tunnel-presets(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
@@ -610,6 +655,15 @@ http {
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
+ location ~ ^/homepage(/.*)?$ {
+ proxy_pass http://127.0.0.1:30012;
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
+ }
+
location ^~ /docker/console/ {
proxy_pass http://127.0.0.1:30009/;
proxy_http_version 1.1;
@@ -663,6 +717,27 @@ http {
proxy_read_timeout 300s;
}
+ location ^~ /serial/websocket/ {
+ proxy_pass http://127.0.0.1:30011/;
+ proxy_http_version 1.1;
+
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_set_header Host $http_host;
+ proxy_cache_bypass $http_upgrade;
+
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
+
+ proxy_read_timeout 86400s;
+ proxy_send_timeout 86400s;
+ proxy_connect_timeout 10s;
+
+ proxy_buffering off;
+ proxy_request_buffering off;
+ }
+
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /app/html;
diff --git a/docker/nginx.conf b/docker/nginx.conf
index 5da83bcf..a8a44e2a 100644
--- a/docker/nginx.conf
+++ b/docker/nginx.conf
@@ -148,6 +148,33 @@ http {
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
+ location ~ ^/alert-rules(/.*)?$ {
+ proxy_pass http://127.0.0.1:30001;
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
+ }
+
+ location ~ ^/notification-channels(/.*)?$ {
+ proxy_pass http://127.0.0.1:30001;
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
+ }
+
+ location ~ ^/alert-firings(/.*)?$ {
+ proxy_pass http://127.0.0.1:30001;
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
+ }
+
location ~ ^/rbac(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
@@ -179,6 +206,24 @@ http {
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
+ location ~ ^/vault(/.*)?$ {
+ proxy_pass http://127.0.0.1:30001;
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
+ }
+
+ location ~ ^/termix-id(/.*)?$ {
+ proxy_pass http://127.0.0.1:30001;
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ }
+
location ~ ^/proxmox(/.*)?$ {
proxy_pass http://127.0.0.1:30001;
proxy_http_version 1.1;
@@ -611,6 +656,15 @@ http {
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
+ location ~ ^/homepage(/.*)?$ {
+ proxy_pass http://127.0.0.1:30012;
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
+ }
+
location ^~ /docker/console/ {
proxy_pass http://127.0.0.1:30009/;
proxy_http_version 1.1;
@@ -664,6 +718,27 @@ http {
proxy_read_timeout 300s;
}
+ location ^~ /serial/websocket/ {
+ proxy_pass http://127.0.0.1:30011/;
+ proxy_http_version 1.1;
+
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_set_header Host $http_host;
+ proxy_cache_bypass $http_upgrade;
+
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
+
+ proxy_read_timeout 86400s;
+ proxy_send_timeout 86400s;
+ proxy_connect_timeout 10s;
+
+ proxy_buffering off;
+ proxy_request_buffering off;
+ }
+
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /app/html;
diff --git a/electron-builder.json b/electron-builder.json
index d2eb0685..81106b0b 100644
--- a/electron-builder.json
+++ b/electron-builder.json
@@ -28,11 +28,8 @@
"!dist/icon-mac.png",
"!public/icon-mac.png",
"!dist/icon.ico",
- "!public/icon.ico",
"!dist/icon.icns",
- "!public/icon.icns",
- "!dist/icons/**/*",
- "!public/icons/**/*"
+ "!public/icon.icns"
],
"extraMetadata": {
"main": "electron/main.cjs",
@@ -61,6 +58,8 @@
"artifactName": "termix_windows_${arch}_nsis.${ext}",
"createDesktopShortcut": true,
"createStartMenuShortcut": true,
+ "installerIcon": "public/icon.ico",
+ "uninstallerIcon": "public/icon.ico",
"shortcutName": "Termix",
"uninstallDisplayName": "Termix"
},
diff --git a/electron/main.cjs b/electron/main.cjs
index 75600256..2efe350f 100644
--- a/electron/main.cjs
+++ b/electron/main.cjs
@@ -9,6 +9,7 @@ const {
safeStorage,
Tray,
clipboard,
+ nativeImage,
} = require("electron");
const path = require("path");
const fs = require("fs");
@@ -17,7 +18,7 @@ const https = require("https");
const http = require("http");
const net = require("net");
const { URL } = require("url");
-const { fork } = require("child_process");
+const { fork, spawn } = require("child_process");
const WebSocket = require("ws");
// Portable mode: if a `.portable` marker exists next to the executable,
@@ -528,15 +529,117 @@ let backendProcess = null;
let backendStartFailed = false;
let tray = null;
let isQuitting = false;
+const tempFiles = new Map();
+const externalEditorSessions = new Map();
const isDev = process.env.NODE_ENV === "development" || !app.isPackaged;
const appRoot = isDev ? process.cwd() : path.join(__dirname, "..");
+const windowsAppUserModelId = "com.karmaa.termix";
const electronCacheBuildPath = path.join(
app.getPath("userData"),
"client-cache-build.json",
);
const termixSessionPartition = "persist:termix";
+function getTempRoot() {
+ const tempRoot = path.join(app.getPath("temp"), "termix");
+ fs.mkdirSync(tempRoot, { recursive: true });
+ return tempRoot;
+}
+
+function sanitizeFileName(fileName) {
+ const baseName = path.basename(String(fileName || "file"));
+ return baseName.replace(/[<>:"/\\|?*\x00-\x1f]/g, "_") || "file";
+}
+
+function decodeFileContent(content, encoding) {
+ if (encoding === "base64") {
+ return Buffer.from(String(content || ""), "base64");
+ }
+ return Buffer.from(String(content || ""), "utf8");
+}
+
+function createManagedTempFile(fileName, content, encoding = "utf8") {
+ const tempId = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
+ const tempDir = fs.mkdtempSync(path.join(getTempRoot(), `${tempId}-`));
+ const filePath = path.join(tempDir, sanitizeFileName(fileName));
+ fs.writeFileSync(filePath, decodeFileContent(content, encoding));
+ tempFiles.set(tempId, { path: filePath, dir: tempDir });
+ return { tempId, path: filePath };
+}
+
+function cleanupManagedTempFile(tempId) {
+ const temp = tempFiles.get(tempId);
+ if (!temp) return;
+ try {
+ fs.rmSync(temp.dir, { recursive: true, force: true });
+ } catch (error) {
+ logToFile("Failed to clean up temporary file:", error.message);
+ }
+ tempFiles.delete(tempId);
+}
+
+function closeExternalEditorSession(editId) {
+ const session = externalEditorSessions.get(editId);
+ if (!session) return;
+ if (session.timer) clearTimeout(session.timer);
+ try {
+ session.watcher.close();
+ } catch {
+ // watcher may already be closed
+ }
+ externalEditorSessions.delete(editId);
+ cleanupManagedTempFile(editId);
+}
+
+function notifyExternalEditorSaved(editId) {
+ const session = externalEditorSessions.get(editId);
+ if (!session || !mainWindow || mainWindow.isDestroyed()) return;
+
+ try {
+ const stat = fs.statSync(session.path);
+ if (stat.mtimeMs === session.lastMtimeMs) return;
+ session.lastMtimeMs = stat.mtimeMs;
+
+ const content = fs.readFileSync(session.path, "utf8");
+ mainWindow.webContents.send("external-editor-saved", {
+ editId,
+ content,
+ encoding: "utf8",
+ path: session.path,
+ });
+ } catch (error) {
+ logToFile("Failed to read external editor file:", error.message);
+ }
+}
+
+function openPathWithEditor(filePath, editorPath) {
+ if (!editorPath) {
+ return shell.openPath(filePath);
+ }
+
+ return new Promise((resolve) => {
+ let settled = false;
+ const child = spawn(editorPath, [filePath], {
+ detached: true,
+ stdio: "ignore",
+ });
+
+ child.once("error", (error) => {
+ if (settled) return;
+ settled = true;
+ resolve(error.message);
+ });
+
+ setTimeout(() => {
+ if (settled) return;
+ settled = true;
+ child.unref();
+ resolve("");
+ }, 500);
+ });
+}
+
app.on(
"certificate-error",
(event, _webContents, url, error, certificate, callback) => {
@@ -690,9 +793,11 @@ function getBackendPaths() {
backendCwd: backendDir,
};
}
- // fork() does not go through Electron's asar redirector — use the unpacked path
+ // fork() does not go through Electron's asar redirector — use the unpacked path.
+ // On macOS multi-arch builds (mergeASARs: false), electron-builder names the ASAR
+ // app-arm64.asar / app-x64.asar instead of app.asar, so match all variants.
const unpackedRoot = appRoot.replace(
- /app\.asar(?!\.unpacked)/,
+ /app(-[a-z0-9]+)?\.asar(?!\.unpacked)/,
"app.asar.unpacked",
);
const backendDir = path.join(unpackedRoot, "dist", "backend", "backend");
@@ -850,7 +955,13 @@ function createTray() {
// use the unpacked path so the OS sees a real file.
const publicRoot = isDev
? path.join(appRoot, "public")
- : path.join(appRoot.replace("app.asar", "app.asar.unpacked"), "public");
+ : path.join(
+ appRoot.replace(
+ /app(-[a-z0-9]+)?\.asar(?!\.unpacked)/,
+ "app.asar.unpacked",
+ ),
+ "public",
+ );
let trayIcon;
if (process.platform === "darwin") {
@@ -920,7 +1031,11 @@ function createWindow() {
minWidth: 800,
minHeight: 600,
title: "Termix",
- icon: path.join(appRoot, "public", "icon.png"),
+ icon: path.join(
+ appRoot,
+ "public",
+ process.platform === "win32" ? "icon.ico" : "icon.png",
+ ),
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
@@ -1231,9 +1346,11 @@ ipcMain.handle(
async (_event, authUrl, callbackPort) => {
const http = require("http");
- return new Promise((resolve, reject) => {
+ return new Promise((resolve) => {
+ let timeout;
+ let settled = false;
const server = http.createServer((req, res) => {
- const url = new URL(req.url, `http://localhost:${callbackPort}`);
+ const url = new URL(req.url || "/", `http://localhost:${callbackPort}`);
if (url.pathname === "/oidc-callback") {
const success = url.searchParams.get("success");
const error = url.searchParams.get("error");
@@ -1244,27 +1361,54 @@ ipcMain.handle(
`${success === "true" ? "Authentication successful!" : "Authentication failed."} You can close this tab and return to Termix.
`,
);
- server.close();
if (success === "true") {
- resolve({ success: true, token });
+ finish({ success: true, token });
} else {
- resolve({
+ finish({
success: false,
error: error || "Authentication failed",
});
}
+ return;
+ }
+
+ res.writeHead(404, { "Content-Type": "text/plain" });
+ res.end("Not found");
+ });
+
+ const finish = (result) => {
+ if (settled) return;
+ settled = true;
+ if (timeout) clearTimeout(timeout);
+ try {
+ server.close();
+ } catch {
+ // Server may not have started yet.
+ }
+ resolve(result);
+ };
+
+ const fail = (error) => {
+ finish({
+ success: false,
+ error: error instanceof Error ? error.message : String(error),
+ });
+ };
+
+ server.once("error", fail);
+
+ server.listen(callbackPort, "127.0.0.1", async () => {
+ try {
+ await shell.openExternal(authUrl);
+ } catch (error) {
+ fail(error);
}
});
- server.listen(callbackPort, "127.0.0.1", () => {
- shell.openExternal(authUrl);
- });
-
// Timeout after 5 minutes
- setTimeout(
+ timeout = setTimeout(
() => {
- server.close();
- reject(new Error("OIDC authentication timed out"));
+ fail(new Error("OIDC authentication timed out"));
},
5 * 60 * 1000,
);
@@ -2500,6 +2644,131 @@ ipcMain.handle("clipboard-write-text", (_event, text) => {
ipcMain.handle("clipboard-read-text", () => clipboard.readText());
+ipcMain.handle("show-save-dialog", async (_event, options) => {
+ return dialog.showSaveDialog(mainWindow, options || {});
+});
+
+ipcMain.handle("show-open-dialog", async (_event, options) => {
+ return dialog.showOpenDialog(mainWindow, options || {});
+});
+
+ipcMain.handle("create-temp-file", async (_event, fileData) => {
+ try {
+ const result = createManagedTempFile(
+ fileData?.fileName,
+ fileData?.content,
+ fileData?.encoding,
+ );
+ return { success: true, ...result };
+ } catch (error) {
+ return { success: false, error: error.message };
+ }
+});
+
+ipcMain.handle("create-temp-folder", async (_event, folderData) => {
+ try {
+ const tempId = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
+ const tempDir = fs.mkdtempSync(path.join(getTempRoot(), `${tempId}-`));
+ const folderPath = path.join(
+ tempDir,
+ sanitizeFileName(folderData?.folderName || "files"),
+ );
+ fs.mkdirSync(folderPath, { recursive: true });
+
+ for (const file of folderData?.files || []) {
+ const relativePath = String(file.relativePath || "")
+ .split(/[\\/]+/)
+ .map(sanitizeFileName)
+ .filter(Boolean)
+ .join(path.sep);
+ if (!relativePath) continue;
+ const targetPath = path.join(folderPath, relativePath);
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
+ fs.writeFileSync(
+ targetPath,
+ decodeFileContent(file.content, file.encoding),
+ );
+ }
+
+ tempFiles.set(tempId, { path: folderPath, dir: tempDir });
+ return { success: true, tempId, path: folderPath };
+ } catch (error) {
+ return { success: false, error: error.message };
+ }
+});
+
+ipcMain.handle("start-drag-to-desktop", (event, dragData) => {
+ try {
+ const temp = tempFiles.get(dragData?.tempId);
+ if (!temp) return { success: false, error: "Temporary file not found" };
+
+ event.sender.startDrag({
+ file: temp.path,
+ icon: nativeImage.createEmpty(),
+ });
+ return { success: true };
+ } catch (error) {
+ return { success: false, error: error.message };
+ }
+});
+
+ipcMain.handle("cleanup-temp-file", (_event, tempId) => {
+ try {
+ cleanupManagedTempFile(tempId);
+ return { success: true };
+ } catch (error) {
+ return { success: false, error: error.message };
+ }
+});
+
+ipcMain.handle("open-external-editor", async (_event, fileData) => {
+ try {
+ const result = createManagedTempFile(
+ fileData?.fileName,
+ fileData?.content,
+ fileData?.encoding,
+ );
+ const editId = result.tempId;
+ const stat = fs.statSync(result.path);
+ const watcher = fs.watch(result.path, { persistent: false }, () => {
+ const session = externalEditorSessions.get(editId);
+ if (!session) return;
+ if (session.timer) clearTimeout(session.timer);
+ session.timer = setTimeout(() => notifyExternalEditorSaved(editId), 500);
+ });
+
+ externalEditorSessions.set(editId, {
+ path: result.path,
+ watcher,
+ timer: null,
+ lastMtimeMs: stat.mtimeMs,
+ });
+
+ const editorPath =
+ typeof fileData?.editorPath === "string" && fileData.editorPath.trim()
+ ? fileData.editorPath.trim()
+ : null;
+ const openError = await openPathWithEditor(result.path, editorPath);
+ if (openError) {
+ closeExternalEditorSession(editId);
+ return { success: false, error: openError };
+ }
+
+ return { success: true, editId, path: result.path };
+ } catch (error) {
+ return { success: false, error: error.message };
+ }
+});
+
+ipcMain.handle("close-external-editor", (_event, editId) => {
+ try {
+ closeExternalEditorSession(editId);
+ return { success: true };
+ } catch (error) {
+ return { success: false, error: error.message };
+ }
+});
+
ipcMain.handle("test-server-connection", async (event, serverUrl) => {
try {
const normalizedServerUrl = serverUrl.replace(/\/$/, "");
@@ -2680,6 +2949,9 @@ app.whenReady().then(async () => {
"arch:",
process.arch,
);
+ if (process.platform === "win32") {
+ app.setAppUserModelId(windowsAppUserModelId);
+ }
createMenu();
await clearElectronClientCacheIfBuildChanged();
await clearElectronJwtCookiesAtStartup();
@@ -2719,6 +2991,12 @@ app.on("before-quit", () => {
app.on("will-quit", () => {
console.log("App will quit...");
+ for (const editId of externalEditorSessions.keys()) {
+ closeExternalEditorSession(editId);
+ }
+ for (const tempId of tempFiles.keys()) {
+ cleanupManagedTempFile(tempId);
+ }
stopAllC2STunnels();
stopBackendServer();
});
diff --git a/electron/preload.js b/electron/preload.js
index ea3becad..6b9cf1c0 100644
--- a/electron/preload.js
+++ b/electron/preload.js
@@ -46,6 +46,26 @@ contextBridge.exposeInMainWorld("electronAPI", {
oidcSystemBrowserAuth: (authUrl, callbackPort) =>
ipcRenderer.invoke("oidc-system-browser-auth", authUrl, callbackPort),
+ openExternalEditor: (fileData) =>
+ ipcRenderer.invoke("open-external-editor", fileData),
+ closeExternalEditor: (editId) =>
+ ipcRenderer.invoke("close-external-editor", editId),
+ onExternalEditorSaved: (callback) => {
+ const listener = (_event, payload) => callback(payload);
+ ipcRenderer.on("external-editor-saved", listener);
+ return () => ipcRenderer.removeListener("external-editor-saved", listener);
+ },
+
+ showSaveDialog: (options) => ipcRenderer.invoke("show-save-dialog", options),
+ showOpenDialog: (options) => ipcRenderer.invoke("show-open-dialog", options),
+ createTempFile: (fileData) =>
+ ipcRenderer.invoke("create-temp-file", fileData),
+ createTempFolder: (folderData) =>
+ ipcRenderer.invoke("create-temp-folder", folderData),
+ startDragToDesktop: (dragData) =>
+ ipcRenderer.invoke("start-drag-to-desktop", dragData),
+ cleanupTempFile: (tempId) => ipcRenderer.invoke("cleanup-temp-file", tempId),
+
invoke: (channel, ...args) => ipcRenderer.invoke(channel, ...args),
});
diff --git a/package-lock.json b/package-lock.json
index 4010ad1b..06505929 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,14 +1,16 @@
{
"name": "termix",
- "version": "2.4.1",
+ "version": "2.5.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "termix",
- "version": "2.4.1",
+ "version": "2.5.0",
"hasInstallScript": true,
"dependencies": {
+ "@simplewebauthn/browser": "^13.3.0",
+ "@simplewebauthn/server": "^13.3.2",
"@types/ldapjs": "^3.0.6",
"axios": "^1.18.0",
"bcryptjs": "^3.0.3",
@@ -22,14 +24,15 @@
"express": "^5.2.1",
"guacamole-lite": "^1.2.0",
"jose": "^6.2.2",
- "js-yaml": "^4.2.0",
+ "js-yaml": "^5.0.0",
"jsonwebtoken": "^9.0.3",
"jszip": "^3.10.1",
"ldapjs": "^3.0.7",
"motion": "^12.38.0",
"multer": "^2.2.0",
- "nanoid": "^5.1.9",
+ "nanoid": "^5.1.15",
"qrcode": "^1.5.4",
+ "serialport": "^13.0.0",
"socks": "^2.8.7",
"speakeasy": "^2.0.0",
"ssh2": "^1.17.0",
@@ -37,17 +40,18 @@
"ws": "^8.20.0"
},
"devDependencies": {
+ "@biomejs/biome": "2.5.1",
"@codemirror/autocomplete": "^6.20.3",
"@codemirror/commands": "^6.10.3",
- "@codemirror/search": "^6.7.0",
+ "@codemirror/search": "^6.7.1",
"@codemirror/theme-one-dark": "^6.1.3",
- "@codemirror/view": "^6.41.1",
+ "@codemirror/view": "^6.43.1",
"@commitlint/cli": "^21.0.2",
"@commitlint/config-conventional": "^21.0.2",
"@deadendjs/swagger-jsdoc": "^8.1.2",
"@electron/notarize": "^3.1.1",
"@electron/rebuild": "^4.0.4",
- "@eslint/js": "^9.0.0",
+ "@eslint/js": "^10.0.1",
"@fontsource-variable/jetbrains-mono": "^5.2.8",
"@fontsource/fira-code": "^5.2.7",
"@fontsource/jetbrains-mono": "^5.2.8",
@@ -69,7 +73,7 @@
"@radix-ui/react-switch": "^1.3.1",
"@radix-ui/react-tabs": "^1.1.14",
"@radix-ui/react-tooltip": "^1.2.9",
- "@tailwindcss/vite": "^4.2.4",
+ "@tailwindcss/vite": "^4.3.1",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
@@ -82,7 +86,7 @@
"@types/js-yaml": "^4.0.9",
"@types/jsonwebtoken": "^9.0.10",
"@types/multer": "^2.1.0",
- "@types/node": "^25.9.2",
+ "@types/node": "^26.0.0",
"@types/qrcode": "^1.5.6",
"@types/react": "^19.2.17",
"@types/react-dom": "^19.2.3",
@@ -93,8 +97,8 @@
"@uiw/codemirror-theme-github": "^4.25.9",
"@uiw/react-codemirror": "^4.25.9",
"@vitejs/plugin-react": "^6.0.1",
- "@vitest/coverage-v8": "^4.1.8",
- "@vitest/ui": "^4.1.8",
+ "@vitest/coverage-v8": "^4.1.9",
+ "@vitest/ui": "^4.1.9",
"@xterm/addon-clipboard": "^0.2.0",
"@xterm/addon-fit": "^0.11.0",
"@xterm/addon-unicode11": "^0.9.0",
@@ -103,13 +107,13 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
- "concurrently": "^9.2.1",
+ "concurrently": "^10.0.3",
"cytoscape": "^3.34.0",
"electron": "^42.4.1",
"electron-builder": "^26.15.3",
- "eslint": "^9.0.0",
+ "eslint": "^10.5.0",
"eslint-plugin-react-hooks": "^7.1.1",
- "eslint-plugin-react-refresh": "^0.5.2",
+ "eslint-plugin-react-refresh": "^0.5.3",
"eslint-plugin-unused-imports": "^4.4.1",
"globals": "^17.5.0",
"guacamole-common-js": "^1.5.0",
@@ -117,9 +121,9 @@
"i18next": "^26.3.1",
"i18next-browser-languagedetector": "^8.2.1",
"jsdom": "^29.1.1",
- "lint-staged": "^17.0.7",
+ "lint-staged": "^17.0.8",
"lucide-react": "^1.20.0",
- "prettier": "3.8.3",
+ "prettier": "3.8.4",
"radix-ui": "^1.6.0",
"react": "^19.2.7",
"react-cytoscapejs": "^2.0.0",
@@ -134,7 +138,7 @@
"react-syntax-highlighter": "^16.1.1",
"react-xtermjs": "^1.0.10",
"remark-gfm": "^4.0.1",
- "sharp": "^0.35.1",
+ "sharp": "^0.35.2",
"sonner": "^2.0.7",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.2.4",
@@ -143,7 +147,7 @@
"typescript-eslint": "^8.61.1",
"vite": "^8.0.16",
"vite-plugin-svgr": "^5.2.0",
- "vitest": "^4.1.8"
+ "vitest": "^4.1.9"
},
"engines": {
"node": ">=22.12.0",
@@ -174,6 +178,29 @@
"url": "https://github.com/sponsors/philsturgeon"
}
},
+ "node_modules/@apidevtools/json-schema-ref-parser/node_modules/js-yaml": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
+ "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
"node_modules/@apidevtools/openapi-schemas": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz",
@@ -540,6 +567,181 @@
"node": ">=18"
}
},
+ "node_modules/@biomejs/biome": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.1.tgz",
+ "integrity": "sha512-IXWLCxKmae+rI7LOHS1B3EbVisQ6GRAWbhN9msa6KjNCyFWrvKZWR4oUdinaNssrV852OrSHuSPa95h1GPJc7Q==",
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "bin": {
+ "biome": "bin/biome"
+ },
+ "engines": {
+ "node": ">=14.21.3"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/biome"
+ },
+ "optionalDependencies": {
+ "@biomejs/cli-darwin-arm64": "2.5.1",
+ "@biomejs/cli-darwin-x64": "2.5.1",
+ "@biomejs/cli-linux-arm64": "2.5.1",
+ "@biomejs/cli-linux-arm64-musl": "2.5.1",
+ "@biomejs/cli-linux-x64": "2.5.1",
+ "@biomejs/cli-linux-x64-musl": "2.5.1",
+ "@biomejs/cli-win32-arm64": "2.5.1",
+ "@biomejs/cli-win32-x64": "2.5.1"
+ }
+ },
+ "node_modules/@biomejs/cli-darwin-arm64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.1.tgz",
+ "integrity": "sha512-npqDzvqv7vFaWRiNN1Te71siRgPaqS9MpqgYCdP/CrUbkJ7ApezaeaKjueKHRN/JH/6lRjJQAHi8acQDCAz22w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
+ "node_modules/@biomejs/cli-darwin-x64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.1.tgz",
+ "integrity": "sha512-RgwTqPAM8g2tn1j+b5oRjF/DbSBX8a4gwojtuG9XuhfK7GgomvZ9+T+tqjXiVbjLEeGJOoL6VEk8mvRTVeSybw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
+ "node_modules/@biomejs/cli-linux-arm64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.1.tgz",
+ "integrity": "sha512-yhV35CzZh38VyMvTEXi3JTjxZBs++oCKK9KG8vB6VI5+uvQvZNR3BFWEKKzuOmx9DJJj7sQpZ4LQJcmbGTs3+Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
+ "node_modules/@biomejs/cli-linux-arm64-musl": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.1.tgz",
+ "integrity": "sha512-WMcvMLgByyTqVxGlq918NBBYliq9FRR9GAQVETHb+VjGVqXCZFfHlZHC1FX4ibuYY/Hg6TJE3rHU0xVrdJXNRw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
+ "node_modules/@biomejs/cli-linux-x64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.1.tgz",
+ "integrity": "sha512-J/7uHSX7NfoYDI7HijAkd8lnQIOrRb2W7j3X+tw4R+N5ExvXGsyXFiGdQcfcxfOmNQmZVSQOCDk757fwpzqQcg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
+ "node_modules/@biomejs/cli-linux-x64-musl": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.1.tgz",
+ "integrity": "sha512-ANTowtlLmPYm5yeMckWY8Xzb9Ix+JJP3tgHR/n6xRj1VWyIzzWtfRfih9hv9VmClwadpBvZduISZIbBsIlYG3A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
+ "node_modules/@biomejs/cli-win32-arm64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.1.tgz",
+ "integrity": "sha512-zgXnKNgWPC4iPF7Y1lR3STUeCUuZRpD6IiOrC7TZTlh0Lx6FiVUT05myuMQHQ9D+1cc7uyMldi4forE6lp0ivQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
+ "node_modules/@biomejs/cli-win32-x64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.1.tgz",
+ "integrity": "sha512-6uxpR9hvaglANkZemeSiN/FhYgkGasrEGn267eXIWvjrjJ2LhDlk251IhjVJq6MXzkV2/bcXwLwSroLyPtqRZg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
"node_modules/@bramus/specificity": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz",
@@ -903,9 +1105,9 @@
}
},
"node_modules/@codemirror/search": {
- "version": "6.7.0",
- "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.7.0.tgz",
- "integrity": "sha512-ZvGm99wc/s2cITtMT15LFdn8aH/aS+V+DqyGq/N5ZlV5vWtH+nILvC2nw0zX7ByNoHHDZ2IxxdW38O0tc5nVHg==",
+ "version": "6.7.1",
+ "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.7.1.tgz",
+ "integrity": "sha512-uMe5UO6PamJtSHrXhhHOzSX3ReWtiJrva6GnPMwSOrZtiExb5X5eExhr2OUZQVvdxPsKpY3Ro2mFbQadpPWmHA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -938,9 +1140,9 @@
}
},
"node_modules/@codemirror/view": {
- "version": "6.43.0",
- "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.0.tgz",
- "integrity": "sha512-V7ZCLQO3Jus9hzh2jVCCPW3mO4IBMr43O37PqSUYautJSnnJF41YlgLw21x0fLJTYvJ+Vkm6Gp+qKGH9pltgXA==",
+ "version": "6.43.1",
+ "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.1.tgz",
+ "integrity": "sha512-+BIjw/AG3tDQ4pJgTLPYdAW25eDE66YsvM4LKyVPgGzVgZ4a9Wj1SRX8kPVKgBDdPt8oHtZ15F0qx7p0oOHdHw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1848,204 +2050,89 @@
}
},
"node_modules/@eslint/config-array": {
- "version": "0.21.2",
- "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
- "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
+ "version": "0.23.5",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz",
+ "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@eslint/object-schema": "^2.1.7",
+ "@eslint/object-schema": "^3.0.5",
"debug": "^4.3.1",
- "minimatch": "^3.1.5"
+ "minimatch": "^10.2.4"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@eslint/config-array/node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@eslint/config-array/node_modules/brace-expansion": {
- "version": "1.1.15",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
- "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/@eslint/config-array/node_modules/minimatch": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
- "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
"node_modules/@eslint/config-helpers": {
- "version": "0.4.2",
- "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
- "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz",
+ "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@eslint/core": "^0.17.0"
+ "@eslint/core": "^1.2.1"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
"node_modules/@eslint/core": {
- "version": "0.17.0",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
- "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz",
+ "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@types/json-schema": "^7.0.15"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@eslint/eslintrc": {
- "version": "3.3.5",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz",
- "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ajv": "^6.14.0",
- "debug": "^4.3.2",
- "espree": "^10.0.1",
- "globals": "^14.0.0",
- "ignore": "^5.2.0",
- "import-fresh": "^3.2.1",
- "js-yaml": "^4.1.1",
- "minimatch": "^3.1.5",
- "strip-json-comments": "^3.1.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/ajv": {
- "version": "6.15.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
- "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "fast-deep-equal": "^3.1.1",
- "fast-json-stable-stringify": "^2.0.0",
- "json-schema-traverse": "^0.4.1",
- "uri-js": "^4.2.2"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/epoberezkin"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
- "version": "1.1.15",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
- "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/globals": {
- "version": "14.0.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
- "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
- "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@eslint/eslintrc/node_modules/minimatch": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
- "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
"node_modules/@eslint/js": {
- "version": "9.39.4",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
- "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==",
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz",
+ "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==",
"dev": true,
"license": "MIT",
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
"url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "eslint": "^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
}
},
"node_modules/@eslint/object-schema": {
- "version": "2.1.7",
- "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
- "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz",
+ "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==",
"dev": true,
"license": "Apache-2.0",
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
"node_modules/@eslint/plugin-kit": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
- "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz",
+ "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@eslint/core": "^0.17.0",
+ "@eslint/core": "^1.2.1",
"levn": "^0.4.1"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
"node_modules/@exodus/bytes": {
@@ -2148,6 +2235,12 @@
"url": "https://github.com/sponsors/ayuhito"
}
},
+ "node_modules/@hexagon/base64": {
+ "version": "1.1.28",
+ "resolved": "https://registry.npmjs.org/@hexagon/base64/-/base64-1.1.28.tgz",
+ "integrity": "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==",
+ "license": "MIT"
+ },
"node_modules/@humanfs/core": {
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
@@ -2248,9 +2341,9 @@
}
},
"node_modules/@img/sharp-darwin-arm64": {
- "version": "0.35.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.1.tgz",
- "integrity": "sha512-T15JRWOubQ3f5+GxnWeIvo47u5qV0M9HBgJhT+f2gE1e9e6OhR6K73Re52Hm80qWcu1DNb3GweKmpr/MnuP2Ow==",
+ "version": "0.35.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.2.tgz",
+ "integrity": "sha512-eEieHsMksAW4IiO5NzauESRl2D2qz3J/kwUxUrSfV06A93eEaRfMpHXyUb1mAqrR7i8U9A0GRqE9pjn6u1Jjpg==",
"cpu": [
"arm64"
],
@@ -2267,13 +2360,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-darwin-arm64": "1.3.0"
+ "@img/sharp-libvips-darwin-arm64": "1.3.1"
}
},
"node_modules/@img/sharp-darwin-x64": {
- "version": "0.35.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.1.tgz",
- "integrity": "sha512-t1CPD0cr7XCHjwUj6tQ5MC0pCi866I+gUW6zbUX4aFPnKd1DFBtk0M+gWcjX8VeEzgfCNiSiNTVFZ6b7kvdbnQ==",
+ "version": "0.35.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.2.tgz",
+ "integrity": "sha512-BaktuGPCeHJMARpodR8jK4uKiZrPAy9WrfQW0sdI37clracq8Bp01AYS3SZgi5FS/y5twa9t4+LIuuxQjqRrWw==",
"cpu": [
"x64"
],
@@ -2290,13 +2383,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-darwin-x64": "1.3.0"
+ "@img/sharp-libvips-darwin-x64": "1.3.1"
}
},
"node_modules/@img/sharp-freebsd-wasm32": {
- "version": "0.35.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.1.tgz",
- "integrity": "sha512-MBSQXqNPThW9EcZ905H6N4sEdX5EwZEYzGx5EBq9ncDCGJALMiY1xPFJxNdzuB1iBjLOpIfxajM6YxdvwmQSLA==",
+ "version": "0.35.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.2.tgz",
+ "integrity": "sha512-YoAxdnd8hPUkvLHd3bWY+YA8nw3xM/RyRopYucNsWHVSan8NLVM3X2volsfoRDcXdUJPg6tXahSd7HXPK7lRnw==",
"dev": true,
"license": "Apache-2.0",
"optional": true,
@@ -2304,7 +2397,7 @@
"freebsd"
],
"dependencies": {
- "@img/sharp-wasm32": "0.35.1"
+ "@img/sharp-wasm32": "0.35.2"
},
"engines": {
"node": ">=20.9.0"
@@ -2314,9 +2407,9 @@
}
},
"node_modules/@img/sharp-libvips-darwin-arm64": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.0.tgz",
- "integrity": "sha512-EKbmBKtyTH+GPFDRw2TgK2oV6hyxxlJVIar4hoTYSNmIwipgMFdxPQqR392GmfdsPGWga0mCFN1cCKjRb9cljw==",
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.1.tgz",
+ "integrity": "sha512-4V/M3roRMTYjiwZY9IOVQOE8OyeCxFAkYmyZDrZl51uOKjibm3oeEJ4WAmLxutAfzFbC9jqUiPs2gbnGflH+7g==",
"cpu": [
"arm64"
],
@@ -2331,9 +2424,9 @@
}
},
"node_modules/@img/sharp-libvips-darwin-x64": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.0.tgz",
- "integrity": "sha512-Pl2OmOvrJ42adUllESxBsG54PfXLo1OYg9i3c5/5Ln/qJ0gZuTM9YMhQJPIbXqwidLRc/c2zuHt4RsrymmNv7A==",
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.1.tgz",
+ "integrity": "sha512-c0/DxItpJv2+dGhgycJBBgotdqruGYDvA79drdh0MD1dFpy7JzJ/PlXwi1H4rFf0eTy8tgbI91aHDnZIceY3jQ==",
"cpu": [
"x64"
],
@@ -2348,9 +2441,9 @@
}
},
"node_modules/@img/sharp-libvips-linux-arm": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.0.tgz",
- "integrity": "sha512-A8UpHoUDW4DwnXoV6+q3C1s7QLRAHtPDEjWuNZjwHMyoCNZnm0GeNN8ls9f/bsEYTRQRW96C/n34XJQHJ2fT7A==",
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.1.tgz",
+ "integrity": "sha512-aGGy9aWzXgHBG7HNyQPWorZthlp7+x6fDRoPAQbGO3ThcttuTyKIx3NuSHb6zb4gBNq6/yNn9f1cy9nFKS/Vmg==",
"cpu": [
"arm"
],
@@ -2368,9 +2461,9 @@
}
},
"node_modules/@img/sharp-libvips-linux-arm64": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.0.tgz",
- "integrity": "sha512-C0SqjoFKnszqa44EQ7xoaT48nnO0lOyXEULfXMWi8krrjOPGYkeK30Okzla6ATbBYsyZ0ySinK0FVkpv3DwzfQ==",
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.1.tgz",
+ "integrity": "sha512-JznefmcK9j1JKPz8AkQDh89kjojubyfOasWBPKfzMIhPwsgDy9evpE/naJTXXXmghS1iFwR8u/kTwh/I2/+GCw==",
"cpu": [
"arm64"
],
@@ -2388,9 +2481,9 @@
}
},
"node_modules/@img/sharp-libvips-linux-ppc64": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.0.tgz",
- "integrity": "sha512-WOpkVxAjFd369iaIzEgNRreFD+gWdUMIGD5zplhNKNeqS6mm5dac3q2AFyCBmzYoAdouzZvRBgxy4z8QHZb4/A==",
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.1.tgz",
+ "integrity": "sha512-1EkwGNCZk6iWNCMWqrvdJ+r1j0PT1zIz60CNPhYnJlK/zyeWqlsPZIe+ocBVqPF8k/Ssee/NCk+tE9Ryrko6ng==",
"cpu": [
"ppc64"
],
@@ -2408,9 +2501,9 @@
}
},
"node_modules/@img/sharp-libvips-linux-riscv64": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.0.tgz",
- "integrity": "sha512-DRWw0mOHusrCCuw2rqP87oLg6PGlkomVDFqw2hIwsSfwWpu4k3XLcBPaKKl6ct/GtL/cwNkgwjV/tc0Mqht3VA==",
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.1.tgz",
+ "integrity": "sha512-Ilays+w2bXdnxzxtQdmXR62u8o8GYa3eL4+Gr+1KiE4xperMZUslRaVPJwwPkzlHEjGfXAfRVAa/7CYCtSqsBw==",
"cpu": [
"riscv64"
],
@@ -2428,9 +2521,9 @@
}
},
"node_modules/@img/sharp-libvips-linux-s390x": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.0.tgz",
- "integrity": "sha512-9APy+nFWhHS+kzLgWZfLcyrUd7YqnAQVa4BPOo4xkoHpdoktOAPG4cEr9+Jpl0TtqfVmcMJimNL5qNTyyOHZNA==",
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.1.tgz",
+ "integrity": "sha512-VfBwVHQTbRoj4XlpA/KLZ7ltgMpz+4WSejFzQ+GnoImjo1PtEJ59QB2qR1xQEeRPYIkNrPIm2L4cICMvz4C2ew==",
"cpu": [
"s390x"
],
@@ -2448,9 +2541,9 @@
}
},
"node_modules/@img/sharp-libvips-linux-x64": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.0.tgz",
- "integrity": "sha512-y9RNUYDe2A1UAdhLyfeOodGRszQdaEoe4nfOpp/sNVPl2CWIcUyFaDoCh4vPLPxu19803j2naLqZup2WxDXCLA==",
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.1.tgz",
+ "integrity": "sha512-+c8ukgwU62DS54nCAjw7keOfHUkmr0B5QHEdcOqRnodF/MNXJbVI8Eopoj4B/0H8Asr65I+A4Amrn7a85/md6A==",
"cpu": [
"x64"
],
@@ -2468,9 +2561,9 @@
}
},
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.0.tgz",
- "integrity": "sha512-cC1wkC0Mlucd0KSiGrLkJnB/ZqPvZCntc/Lk7ZnYO5ZSbF2euNek4Xvxafojq+wN1q/W0eprdpUIjUr/EV2PBg==",
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.1.tgz",
+ "integrity": "sha512-qlKb/pwbkAi1WMsJrYHk7CuDrd12s27U2QnRhFYUoJNrRCmkosMTttuRFat/DDB3IlDm5qE1TJgZ4JDnHX8Ldw==",
"cpu": [
"arm64"
],
@@ -2488,9 +2581,9 @@
}
},
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.0.tgz",
- "integrity": "sha512-LiYMhUZicB1QG//+RvmYZpXJO8fYRENfp+MZUCnG9aw+AKvGAy9gPaCnuwsPcBFs8EV66M0NNxj9VHcNklE8zw==",
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.1.tgz",
+ "integrity": "sha512-yO21HwoUVLN8Qa+/SBjQLMYwBWAVJjeGPNe+hc0OUeMeifEtJqu5a1c4HayE1nNpDih9y3/KkoltfkDodmKAlg==",
"cpu": [
"x64"
],
@@ -2508,9 +2601,9 @@
}
},
"node_modules/@img/sharp-linux-arm": {
- "version": "0.35.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.1.tgz",
- "integrity": "sha512-jygmR02PpCYypt7xB7nst1vqjZp/BpRA/Kf9nK7qRponJ/KrLPaZWEG4G15z1d2FZ6XqI+T0350ha3RSnKx24A==",
+ "version": "0.35.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.2.tgz",
+ "integrity": "sha512-SE4kzF2mepn6z+6E7L6lsV8FzuLL6IPQdyX8ZiwROAG/G8td+hP/m7FsFPwidtrF19gvajuC9l6TxAVcsA4S7A==",
"cpu": [
"arm"
],
@@ -2530,13 +2623,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linux-arm": "1.3.0"
+ "@img/sharp-libvips-linux-arm": "1.3.1"
}
},
"node_modules/@img/sharp-linux-arm64": {
- "version": "0.35.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.1.tgz",
- "integrity": "sha512-ErCRyGU7LeoaFBZ0xW8hhLlXzhAg80sc4vxePB86qvtEvW1jEhhmbiNBP4oEzZfPMnu6HwHXfzD2W2kBU+RnCw==",
+ "version": "0.35.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.2.tgz",
+ "integrity": "sha512-af12Pnd0ZGu2HfP8NayB0kk6eC/lrfbQE6HlR4jD+34wdJ1Vw9TF6TMn6ZvffT+WgqVsl0hRbmNvz2u/23VmwA==",
"cpu": [
"arm64"
],
@@ -2556,13 +2649,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linux-arm64": "1.3.0"
+ "@img/sharp-libvips-linux-arm64": "1.3.1"
}
},
"node_modules/@img/sharp-linux-ppc64": {
- "version": "0.35.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.1.tgz",
- "integrity": "sha512-LUWZ2+r2UoLCd8j0RLCwQ4gL6w47+Y7igxtVnPIDXOOEjV86LpBkAHq5VpJeg+GHbw0KN/JWlPJOdZjyZnFqFQ==",
+ "version": "0.35.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.2.tgz",
+ "integrity": "sha512-hYSBm7zcNtDCozCxQHYZJiu63b/bXsgRZuOxCIBZsStMM9Vap47iFHdbX4kCvQsblPB/k+clhELpdQJHQLSHvg==",
"cpu": [
"ppc64"
],
@@ -2582,13 +2675,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linux-ppc64": "1.3.0"
+ "@img/sharp-libvips-linux-ppc64": "1.3.1"
}
},
"node_modules/@img/sharp-linux-riscv64": {
- "version": "0.35.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.1.tgz",
- "integrity": "sha512-i7x6J3mwF4JgT0sM4V4WlAWdJ0bucPtA9rzO1bTji1n5qgBq/W5nn87RvOQPleuuxahNoLdTngByD8/vDDLArw==",
+ "version": "0.35.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.2.tgz",
+ "integrity": "sha512-qQt0Kc13+Hoan/Awq/qMSQw3L+RI1NCRPgD5cUJ/1WSSmIoysLOc72jlRM3E0OHN9Yr313jgeQ2T+zW+F03QFA==",
"cpu": [
"riscv64"
],
@@ -2608,13 +2701,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linux-riscv64": "1.3.0"
+ "@img/sharp-libvips-linux-riscv64": "1.3.1"
}
},
"node_modules/@img/sharp-linux-s390x": {
- "version": "0.35.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.1.tgz",
- "integrity": "sha512-0zSaTUjTF0kIWTSYxD4EG/nvCU4jez53+3RdURtoY3HvbXtIQ98W90JnrGz/oLRFuEnfIy9+7xeq883euc0ZWw==",
+ "version": "0.35.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.2.tgz",
+ "integrity": "sha512-E4fLLfRPzDLlEeDaTzI98OFLcv++WL5ChLLMwPoVd0CIoZQqupBSNbOisPL5am9XsbQ9T84+iiMpUvbFtkunbA==",
"cpu": [
"s390x"
],
@@ -2634,13 +2727,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linux-s390x": "1.3.0"
+ "@img/sharp-libvips-linux-s390x": "1.3.1"
}
},
"node_modules/@img/sharp-linux-x64": {
- "version": "0.35.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.1.tgz",
- "integrity": "sha512-NbJD4mWdeyrNQKluO/tR/wBDOelcowSVGNBWxI0e3ZtlXc6F/UOVKDj1MLD4zl3oHTuvKW3s+MA9N54YTldAYw==",
+ "version": "0.35.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.2.tgz",
+ "integrity": "sha512-gi0zFJJRLswfCZmHtJdikXPOc5u7qamSOS3NHedLqLd4W8Q0NqjdBr6TTRIgsfFjqfTsHFgdfvJ9LwqSgcHiAA==",
"cpu": [
"x64"
],
@@ -2660,13 +2753,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linux-x64": "1.3.0"
+ "@img/sharp-libvips-linux-x64": "1.3.1"
}
},
"node_modules/@img/sharp-linuxmusl-arm64": {
- "version": "0.35.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.1.tgz",
- "integrity": "sha512-VoW2sQCWI+0YIKQEmWJ8vzaQjTg9wIyfkFpvEfAS2h43X6iHu7GTk1hhOgB4IpSzCHe8UwQZIcx7b81VTaOrJA==",
+ "version": "0.35.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.2.tgz",
+ "integrity": "sha512-siWbOW1u6HFnFLrp0waKyW7VEf7jYvcDWdrXEFa8AkdAQgEvuu5Fz8/Y70w9EeqAdwDtfU012BhEHHaDqvQNzg==",
"cpu": [
"arm64"
],
@@ -2686,13 +2779,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linuxmusl-arm64": "1.3.0"
+ "@img/sharp-libvips-linuxmusl-arm64": "1.3.1"
}
},
"node_modules/@img/sharp-linuxmusl-x64": {
- "version": "0.35.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.1.tgz",
- "integrity": "sha512-LjBoSd/c5JU0/K5MwzDMlgsSRP2bPn98JQGFFQAOLQ0bU/1z4ekxUdSKY9BmlwSh/cA+OrvpgsWqfZyYfVHBRw==",
+ "version": "0.35.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.2.tgz",
+ "integrity": "sha512-YBqMMcjDi4QGYiSn4vNOYBhmlC4z5AXqkOUUqI2e0AFA4urNv4ESgOgwNl3K+4etQhha0twXlzeF20bbULm9Yg==",
"cpu": [
"x64"
],
@@ -2712,18 +2805,18 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linuxmusl-x64": "1.3.0"
+ "@img/sharp-libvips-linuxmusl-x64": "1.3.1"
}
},
"node_modules/@img/sharp-wasm32": {
- "version": "0.35.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.1.tgz",
- "integrity": "sha512-PCQUoQdZyE8tp3HpbevuihfUmgSP4qWI0FGEPWoeXqaS+cUrFfemabHQiebUmUmlUhCuNnQMxGrQ+CPqK4hnxg==",
+ "version": "0.35.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.2.tgz",
+ "integrity": "sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw==",
"dev": true,
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
"optional": true,
"dependencies": {
- "@emnapi/runtime": "^1.11.0"
+ "@emnapi/runtime": "^1.11.1"
},
"engines": {
"node": ">=20.9.0"
@@ -2744,9 +2837,9 @@
}
},
"node_modules/@img/sharp-webcontainers-wasm32": {
- "version": "0.35.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.1.tgz",
- "integrity": "sha512-xU2ml2bU2OPxYVvW2A6ae4M1g5QKyhKG06P4FAt+YEaFQQO0919Qx+XxIZEUuWTMoDViLpMws2/dQwoe/VcA6A==",
+ "version": "0.35.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.2.tgz",
+ "integrity": "sha512-QNV27pxs9wpApEiCfvHM1RDoP1w1+2KrUWWDPEhEwg+latvOrfuhWrHWZKwdSFwU6jh3myjw/yOCRsUIuOft3g==",
"cpu": [
"wasm32"
],
@@ -2754,7 +2847,7 @@
"license": "Apache-2.0",
"optional": true,
"dependencies": {
- "@img/sharp-wasm32": "0.35.1"
+ "@img/sharp-wasm32": "0.35.2"
},
"engines": {
"node": ">=20.9.0"
@@ -2764,9 +2857,9 @@
}
},
"node_modules/@img/sharp-win32-arm64": {
- "version": "0.35.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.1.tgz",
- "integrity": "sha512-IkmHwuFhYpd3bTsN5SAahjwhiAcyXPooBt8vEUgxY3T0IP70sSJ0nU1xiPzZY8AH/OB1XpV3j8aZSVSOSfTbdA==",
+ "version": "0.35.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.2.tgz",
+ "integrity": "sha512-BiVRYc/t6/Vl3e1hBx0hugG4oN9Pydf4fgMSpxTQJmwGUg/YoXTWHiFeRymHfCZzifxu4F4rpk/I67D0LQ20wQ==",
"cpu": [
"arm64"
],
@@ -2784,9 +2877,9 @@
}
},
"node_modules/@img/sharp-win32-ia32": {
- "version": "0.35.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.1.tgz",
- "integrity": "sha512-wQahqCi9MD8Yxzg4gVM4fNrZxh+r6vD55PyIg+WJPaM5ZRUyF35iQpwJCuma3r6viU9/8Pxlc+XHV+woVa6nCQ==",
+ "version": "0.35.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.2.tgz",
+ "integrity": "sha512-YYEhx9PImCC7T0tI8JDMi4DB9LwLCXCU5OWNYEXAxh5Q1ShKkyC6byxzoBJ3gEFDnH2lQckWuDe70G7mB2XJog==",
"cpu": [
"ia32"
],
@@ -2804,9 +2897,9 @@
}
},
"node_modules/@img/sharp-win32-x64": {
- "version": "0.35.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.1.tgz",
- "integrity": "sha512-WzBtkYtZHATLPe8XRharxZXxQ9cdLrQWHiwxt+BJ5rBsisQrKeeV86ErxPSVhcG6xCEuNhs0SqLpWr7XDa2k6w==",
+ "version": "0.35.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.2.tgz",
+ "integrity": "sha512-imoOyBcoM/iiUr4J6VPpCNjPnjvP/Gks95898yB8YqoGGYmHYbOyCuNv9FMhFgtaiHFGbHW8bxKqRV6VjtXThQ==",
"cpu": [
"x64"
],
@@ -2991,6 +3084,12 @@
"deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md",
"license": "MIT"
},
+ "node_modules/@levischuck/tiny-cbor": {
+ "version": "0.2.11",
+ "resolved": "https://registry.npmjs.org/@levischuck/tiny-cbor/-/tiny-cbor-0.2.11.tgz",
+ "integrity": "sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow==",
+ "license": "MIT"
+ },
"node_modules/@lezer/common": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.2.tgz",
@@ -3576,11 +3675,112 @@
"url": "https://github.com/sponsors/Boshen"
}
},
+ "node_modules/@peculiar/asn1-android": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/@peculiar/asn1-android/-/asn1-android-2.8.0.tgz",
+ "integrity": "sha512-skLbS+IOGv1lUgDqtChr8xvtvEr3HMse/JGBaL2r1J1o/n7a8wqOrovMtlRq/UXLhxvmLaONP67hwtshgzwfzA==",
+ "license": "MIT",
+ "dependencies": {
+ "@peculiar/asn1-schema": "^2.8.0",
+ "asn1js": "^3.0.10",
+ "tslib": "^2.8.1"
+ }
+ },
+ "node_modules/@peculiar/asn1-cms": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.8.0.tgz",
+ "integrity": "sha512-NgekZOrSJFSBFLFoLfwePguAWAx7z1+f2TEsWFUMyiqqfntZ4+S/S5hzqME3q4pCA0iOsFKdwiQ35dwY24eVqA==",
+ "license": "MIT",
+ "dependencies": {
+ "@peculiar/asn1-schema": "^2.8.0",
+ "@peculiar/asn1-x509": "^2.8.0",
+ "@peculiar/asn1-x509-attr": "^2.8.0",
+ "asn1js": "^3.0.10",
+ "tslib": "^2.8.1"
+ }
+ },
+ "node_modules/@peculiar/asn1-csr": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.8.0.tgz",
+ "integrity": "sha512-akbF8+uvleHs8sejNPQxwmVFuInAg6FMNHOwMILXfP518YfFJwdR3jr6oNUPOaEJfuEhn/vkNOCIT6ASUd4mbg==",
+ "license": "MIT",
+ "dependencies": {
+ "@peculiar/asn1-schema": "^2.8.0",
+ "@peculiar/asn1-x509": "^2.8.0",
+ "asn1js": "^3.0.10",
+ "tslib": "^2.8.1"
+ }
+ },
+ "node_modules/@peculiar/asn1-ecc": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.8.0.tgz",
+ "integrity": "sha512-ohwlk+u9Rv2NOAY1c6MfHj45ATVF8R1DUN/WCgABiRtLi2ZftlZWZX7KvpAbU8v9xPcmoILfELeEABj/rn18AQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@peculiar/asn1-schema": "^2.8.0",
+ "@peculiar/asn1-x509": "^2.8.0",
+ "asn1js": "^3.0.10",
+ "tslib": "^2.8.1"
+ }
+ },
+ "node_modules/@peculiar/asn1-pfx": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.8.0.tgz",
+ "integrity": "sha512-5yof1ytoB++RQtaFbqSUJ8pxDJtZT6vbVqZ8XoJ61ph7UjNVvfFwAilnCodqkNsAodpy13gDhoxZXw00pghnyg==",
+ "license": "MIT",
+ "dependencies": {
+ "@peculiar/asn1-cms": "^2.8.0",
+ "@peculiar/asn1-pkcs8": "^2.8.0",
+ "@peculiar/asn1-rsa": "^2.8.0",
+ "@peculiar/asn1-schema": "^2.8.0",
+ "asn1js": "^3.0.10",
+ "tslib": "^2.8.1"
+ }
+ },
+ "node_modules/@peculiar/asn1-pkcs8": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.8.0.tgz",
+ "integrity": "sha512-qAKXtLpBEw9LqhKpjw3ajZSXlBur+ipW+y2ivVBQAG6F6qRx94yO+1ZR4mvw+YaCfKSaOzLeYEzsPaBp4SJELA==",
+ "license": "MIT",
+ "dependencies": {
+ "@peculiar/asn1-schema": "^2.8.0",
+ "@peculiar/asn1-x509": "^2.8.0",
+ "asn1js": "^3.0.10",
+ "tslib": "^2.8.1"
+ }
+ },
+ "node_modules/@peculiar/asn1-pkcs9": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.8.0.tgz",
+ "integrity": "sha512-b5nDWCnkV60+cQ141D6sVVwK9nz64R5n3zSVnklGd+ECdkW2Ol3U1a6yYFlalpSOaD557yuJB64A+q42jG7lUQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@peculiar/asn1-cms": "^2.8.0",
+ "@peculiar/asn1-pfx": "^2.8.0",
+ "@peculiar/asn1-pkcs8": "^2.8.0",
+ "@peculiar/asn1-schema": "^2.8.0",
+ "@peculiar/asn1-x509": "^2.8.0",
+ "@peculiar/asn1-x509-attr": "^2.8.0",
+ "asn1js": "^3.0.10",
+ "tslib": "^2.8.1"
+ }
+ },
+ "node_modules/@peculiar/asn1-rsa": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.8.0.tgz",
+ "integrity": "sha512-zHEUlCqB2mk7x2lxDwHHJy7hWZOPdGHVlsmITWKB5/PbQo61atbu9PJ/0r9dQNMwFzbKPXZ8uK8/91eUhRznSg==",
+ "license": "MIT",
+ "dependencies": {
+ "@peculiar/asn1-schema": "^2.8.0",
+ "@peculiar/asn1-x509": "^2.8.0",
+ "asn1js": "^3.0.10",
+ "tslib": "^2.8.1"
+ }
+ },
"node_modules/@peculiar/asn1-schema": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.8.0.tgz",
"integrity": "sha512-7YT0U/ze0tF2QOBbE15gKZwy5tvgGyLRiRHLzhlbOpf7BT032oBSd0haZqXn5W6l26WLlu3dyxzjM+2638/z2Q==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@peculiar/utils": "^2.0.2",
@@ -3588,6 +3788,30 @@
"tslib": "^2.8.1"
}
},
+ "node_modules/@peculiar/asn1-x509": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.8.0.tgz",
+ "integrity": "sha512-N0CMuhWUzsWEVq6F1q9X6+VKUnWzSW+cSVg+aPaGGwDdbFoFWTYgin5MHwXgpWd6y9COMBxnfy/Qc+Xc7F0Zwg==",
+ "license": "MIT",
+ "dependencies": {
+ "@peculiar/asn1-schema": "^2.8.0",
+ "@peculiar/utils": "^2.0.2",
+ "asn1js": "^3.0.10",
+ "tslib": "^2.8.1"
+ }
+ },
+ "node_modules/@peculiar/asn1-x509-attr": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.8.0.tgz",
+ "integrity": "sha512-tHjkfS/qhMnmrlB2J9NhflQlQ7In3khO3CfmVrriOlpTeErY9ZIKOso1hQ5JQiyrJ7ShvqVPk7E5fQmbclkSKA==",
+ "license": "MIT",
+ "dependencies": {
+ "@peculiar/asn1-schema": "^2.8.0",
+ "@peculiar/asn1-x509": "^2.8.0",
+ "asn1js": "^3.0.10",
+ "tslib": "^2.8.1"
+ }
+ },
"node_modules/@peculiar/json-schema": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.12.tgz",
@@ -3605,7 +3829,6 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@peculiar/utils/-/utils-2.0.3.tgz",
"integrity": "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"tslib": "^2.8.1"
@@ -3628,6 +3851,28 @@
"node": ">=14.18.0"
}
},
+ "node_modules/@peculiar/x509": {
+ "version": "1.14.3",
+ "resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.3.tgz",
+ "integrity": "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==",
+ "license": "MIT",
+ "dependencies": {
+ "@peculiar/asn1-cms": "^2.6.0",
+ "@peculiar/asn1-csr": "^2.6.0",
+ "@peculiar/asn1-ecc": "^2.6.0",
+ "@peculiar/asn1-pkcs9": "^2.6.0",
+ "@peculiar/asn1-rsa": "^2.6.0",
+ "@peculiar/asn1-schema": "^2.6.0",
+ "@peculiar/asn1-x509": "^2.6.0",
+ "pvtsutils": "^1.3.6",
+ "reflect-metadata": "^0.2.2",
+ "tslib": "^2.8.1",
+ "tsyringe": "^4.10.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
"node_modules/@polka/url": {
"version": "1.0.0-next.29",
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz",
@@ -5524,6 +5769,245 @@
}
}
},
+ "node_modules/@serialport/binding-mock": {
+ "version": "10.2.2",
+ "resolved": "https://registry.npmjs.org/@serialport/binding-mock/-/binding-mock-10.2.2.tgz",
+ "integrity": "sha512-HAFzGhk9OuFMpuor7aT5G1ChPgn5qSsklTFOTUX72Rl6p0xwcSVsRtG/xaGp6bxpN7fI9D/S8THLBWbBgS6ldw==",
+ "license": "MIT",
+ "dependencies": {
+ "@serialport/bindings-interface": "^1.2.1",
+ "debug": "^4.3.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/@serialport/bindings-cpp": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-13.0.0.tgz",
+ "integrity": "sha512-r25o4Bk/vaO1LyUfY/ulR6hCg/aWiN6Wo2ljVlb4Pj5bqWGcSRC4Vse4a9AcapuAu/FeBzHCbKMvRQeCuKjzIQ==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "@serialport/bindings-interface": "1.2.2",
+ "@serialport/parser-readline": "12.0.0",
+ "debug": "4.4.0",
+ "node-addon-api": "8.3.0",
+ "node-gyp-build": "4.8.4"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/serialport/donate"
+ }
+ },
+ "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-delimiter": {
+ "version": "12.0.0",
+ "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-12.0.0.tgz",
+ "integrity": "sha512-gu26tVt5lQoybhorLTPsH2j2LnX3AOP2x/34+DUSTNaUTzu2fBXw+isVjQJpUBFWu6aeQRZw5bJol5X9Gxjblw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/serialport/donate"
+ }
+ },
+ "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-readline": {
+ "version": "12.0.0",
+ "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-12.0.0.tgz",
+ "integrity": "sha512-O7cywCWC8PiOMvo/gglEBfAkLjp/SENEML46BXDykfKP5mTPM46XMaX1L0waWU6DXJpBgjaL7+yX6VriVPbN4w==",
+ "license": "MIT",
+ "dependencies": {
+ "@serialport/parser-delimiter": "12.0.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/serialport/donate"
+ }
+ },
+ "node_modules/@serialport/bindings-cpp/node_modules/debug": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
+ "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@serialport/bindings-interface": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@serialport/bindings-interface/-/bindings-interface-1.2.2.tgz",
+ "integrity": "sha512-CJaUd5bLvtM9c5dmO9rPBHPXTa9R2UwpkJ0wdh9JCYcbrPWsKz+ErvR0hBLeo7NPeiFdjFO4sonRljiw4d2XiA==",
+ "license": "MIT",
+ "engines": {
+ "node": "^12.22 || ^14.13 || >=16"
+ }
+ },
+ "node_modules/@serialport/parser-byte-length": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-13.0.0.tgz",
+ "integrity": "sha512-32yvqeTAqJzAEtX5zCrN1Mej56GJ5h/cVFsCDPbF9S1ZSC9FWjOqNAgtByseHfFTSTs/4ZBQZZcZBpolt8sUng==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/serialport/donate"
+ }
+ },
+ "node_modules/@serialport/parser-cctalk": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-13.0.0.tgz",
+ "integrity": "sha512-RErAe57g9gvnlieVYGIn1xymb1bzNXb2QtUQd14FpmbQQYlcrmuRnJwKa1BgTCujoCkhtaTtgHlbBWOxm8U2uA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/serialport/donate"
+ }
+ },
+ "node_modules/@serialport/parser-delimiter": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-13.0.0.tgz",
+ "integrity": "sha512-Qqyb0FX1avs3XabQqNaZSivyVbl/yl0jywImp7ePvfZKLwx7jBZjvL+Hawt9wIG6tfq6zbFM24vzCCK7REMUig==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/serialport/donate"
+ }
+ },
+ "node_modules/@serialport/parser-inter-byte-timeout": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-13.0.0.tgz",
+ "integrity": "sha512-a0w0WecTW7bD2YHWrpTz1uyiWA2fDNym0kjmPeNSwZ2XCP+JbirZt31l43m2ey6qXItTYVuQBthm75sPVeHnGA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/serialport/donate"
+ }
+ },
+ "node_modules/@serialport/parser-packet-length": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/@serialport/parser-packet-length/-/parser-packet-length-13.0.0.tgz",
+ "integrity": "sha512-60ZDDIqYRi0Xs2SPZUo4Jr5LLIjtb+rvzPKMJCohrO6tAqSDponcNpcB1O4W21mKTxYjqInSz+eMrtk0LLfZIg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/@serialport/parser-readline": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-13.0.0.tgz",
+ "integrity": "sha512-dov3zYoyf0dt1Sudd1q42VVYQ4WlliF0MYvAMA3MOyiU1IeG4hl0J6buBA2w4gl3DOCC05tGgLDN/3yIL81gsA==",
+ "license": "MIT",
+ "dependencies": {
+ "@serialport/parser-delimiter": "13.0.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/serialport/donate"
+ }
+ },
+ "node_modules/@serialport/parser-ready": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-13.0.0.tgz",
+ "integrity": "sha512-JNUQA+y2Rfs4bU+cGYNqOPnNMAcayhhW+XJZihSLQXOHcZsFnOa2F9YtMg9VXRWIcnHldHYtisp62Etjlw24bw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/serialport/donate"
+ }
+ },
+ "node_modules/@serialport/parser-regex": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-13.0.0.tgz",
+ "integrity": "sha512-m7HpIf56G5XcuDdA3DB34Z0pJiwxNRakThEHjSa4mG05OnWYv0IG8l2oUyYfuGMowQWaVnQ+8r+brlPxGVH+eA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/serialport/donate"
+ }
+ },
+ "node_modules/@serialport/parser-slip-encoder": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/@serialport/parser-slip-encoder/-/parser-slip-encoder-13.0.0.tgz",
+ "integrity": "sha512-fUHZEExm6izJ7rg0A1yjXwu4sOzeBkPAjDZPfb+XQoqgtKAk+s+HfICiYn7N2QU9gyaeCO8VKgWwi+b/DowYOg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/serialport/donate"
+ }
+ },
+ "node_modules/@serialport/parser-spacepacket": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/@serialport/parser-spacepacket/-/parser-spacepacket-13.0.0.tgz",
+ "integrity": "sha512-DoXJ3mFYmyD8X/8931agJvrBPxqTaYDsPoly9/cwQSeh/q4EjQND9ySXBxpWz5WcpyCU4jOuusqCSAPsbB30Eg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/serialport/donate"
+ }
+ },
+ "node_modules/@serialport/stream": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-13.0.0.tgz",
+ "integrity": "sha512-F7xLJKsjGo2WuEWMSEO1SimRcOA+WtWICsY13r0ahx8s2SecPQH06338g28OT7cW7uRXI7oEQAk62qh5gHJW3g==",
+ "license": "MIT",
+ "dependencies": {
+ "@serialport/bindings-interface": "1.2.2",
+ "debug": "4.4.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/serialport/donate"
+ }
+ },
+ "node_modules/@serialport/stream/node_modules/debug": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
+ "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@simple-libs/child-process-utils": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@simple-libs/child-process-utils/-/child-process-utils-1.0.2.tgz",
@@ -5553,6 +6037,31 @@
"url": "https://ko-fi.com/dangreen"
}
},
+ "node_modules/@simplewebauthn/browser": {
+ "version": "13.3.0",
+ "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-13.3.0.tgz",
+ "integrity": "sha512-BE/UWv6FOToAdVk0EokzkqQQDOWtNydYlY6+OrmiZ5SCNmb41VehttboTetUM3T/fr6EAFYVXjz4My2wg230rQ==",
+ "license": "MIT"
+ },
+ "node_modules/@simplewebauthn/server": {
+ "version": "13.3.2",
+ "resolved": "https://registry.npmjs.org/@simplewebauthn/server/-/server-13.3.2.tgz",
+ "integrity": "sha512-KEDhfcGP1PAKRVSDjA3npTQFqS2b/srm+ipoNBNHdkzrHAlaRQUTE+a5f4ywsx6thxAw1NU2rYcLEY1949RGbQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@hexagon/base64": "^1.1.27",
+ "@levischuck/tiny-cbor": "^0.2.2",
+ "@peculiar/asn1-android": "^2.6.0",
+ "@peculiar/asn1-ecc": "^2.6.1",
+ "@peculiar/asn1-rsa": "^2.6.1",
+ "@peculiar/asn1-schema": "^2.6.0",
+ "@peculiar/asn1-x509": "^2.6.1",
+ "@peculiar/x509": "^1.14.3"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
"node_modules/@standard-schema/spec": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
@@ -5771,6 +6280,29 @@
}
}
},
+ "node_modules/@svgr/core/node_modules/js-yaml": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
+ "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
"node_modules/@svgr/hast-util-to-babel-ast": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz",
@@ -5813,49 +6345,49 @@
}
},
"node_modules/@tailwindcss/node": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz",
- "integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.1.tgz",
+ "integrity": "sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/remapping": "^2.3.5",
- "enhanced-resolve": "^5.21.0",
- "jiti": "^2.6.1",
+ "enhanced-resolve": "5.21.6",
+ "jiti": "^2.7.0",
"lightningcss": "1.32.0",
"magic-string": "^0.30.21",
"source-map-js": "^1.2.1",
- "tailwindcss": "4.3.0"
+ "tailwindcss": "4.3.1"
}
},
"node_modules/@tailwindcss/oxide": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.0.tgz",
- "integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.1.tgz",
+ "integrity": "sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 20"
},
"optionalDependencies": {
- "@tailwindcss/oxide-android-arm64": "4.3.0",
- "@tailwindcss/oxide-darwin-arm64": "4.3.0",
- "@tailwindcss/oxide-darwin-x64": "4.3.0",
- "@tailwindcss/oxide-freebsd-x64": "4.3.0",
- "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0",
- "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0",
- "@tailwindcss/oxide-linux-arm64-musl": "4.3.0",
- "@tailwindcss/oxide-linux-x64-gnu": "4.3.0",
- "@tailwindcss/oxide-linux-x64-musl": "4.3.0",
- "@tailwindcss/oxide-wasm32-wasi": "4.3.0",
- "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0",
- "@tailwindcss/oxide-win32-x64-msvc": "4.3.0"
+ "@tailwindcss/oxide-android-arm64": "4.3.1",
+ "@tailwindcss/oxide-darwin-arm64": "4.3.1",
+ "@tailwindcss/oxide-darwin-x64": "4.3.1",
+ "@tailwindcss/oxide-freebsd-x64": "4.3.1",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.1",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.3.1",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.3.1",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.3.1",
+ "@tailwindcss/oxide-linux-x64-musl": "4.3.1",
+ "@tailwindcss/oxide-wasm32-wasi": "4.3.1",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.3.1",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.3.1"
}
},
"node_modules/@tailwindcss/oxide-android-arm64": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.0.tgz",
- "integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.1.tgz",
+ "integrity": "sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ==",
"cpu": [
"arm64"
],
@@ -5870,9 +6402,9 @@
}
},
"node_modules/@tailwindcss/oxide-darwin-arm64": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz",
- "integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.1.tgz",
+ "integrity": "sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA==",
"cpu": [
"arm64"
],
@@ -5887,9 +6419,9 @@
}
},
"node_modules/@tailwindcss/oxide-darwin-x64": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.0.tgz",
- "integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.1.tgz",
+ "integrity": "sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg==",
"cpu": [
"x64"
],
@@ -5904,9 +6436,9 @@
}
},
"node_modules/@tailwindcss/oxide-freebsd-x64": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.0.tgz",
- "integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.1.tgz",
+ "integrity": "sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g==",
"cpu": [
"x64"
],
@@ -5921,9 +6453,9 @@
}
},
"node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.0.tgz",
- "integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.1.tgz",
+ "integrity": "sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg==",
"cpu": [
"arm"
],
@@ -5938,13 +6470,16 @@
}
},
"node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz",
- "integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.1.tgz",
+ "integrity": "sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ==",
"cpu": [
"arm64"
],
"dev": true,
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -5955,13 +6490,16 @@
}
},
"node_modules/@tailwindcss/oxide-linux-arm64-musl": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.0.tgz",
- "integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.1.tgz",
+ "integrity": "sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA==",
"cpu": [
"arm64"
],
"dev": true,
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -5972,13 +6510,16 @@
}
},
"node_modules/@tailwindcss/oxide-linux-x64-gnu": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.0.tgz",
- "integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.1.tgz",
+ "integrity": "sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg==",
"cpu": [
"x64"
],
"dev": true,
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -5989,13 +6530,16 @@
}
},
"node_modules/@tailwindcss/oxide-linux-x64-musl": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.0.tgz",
- "integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.1.tgz",
+ "integrity": "sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ==",
"cpu": [
"x64"
],
"dev": true,
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -6006,9 +6550,9 @@
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.0.tgz",
- "integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.1.tgz",
+ "integrity": "sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA==",
"bundleDependencies": [
"@napi-rs/wasm-runtime",
"@emnapi/core",
@@ -6028,17 +6572,83 @@
"@emnapi/runtime": "^1.10.0",
"@emnapi/wasi-threads": "^1.2.1",
"@napi-rs/wasm-runtime": "^1.1.4",
- "@tybys/wasm-util": "^0.10.1",
+ "@tybys/wasm-util": "^0.10.2",
"tslib": "^2.8.1"
},
"engines": {
"node": ">=14.0.0"
}
},
+ "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": {
+ "version": "1.10.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.1",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": {
+ "version": "1.10.0",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.1.4",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@tybys/wasm-util": "^0.10.1"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "peerDependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": {
+ "version": "0.10.2",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": {
+ "version": "2.8.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "0BSD",
+ "optional": true
+ },
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz",
- "integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.1.tgz",
+ "integrity": "sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg==",
"cpu": [
"arm64"
],
@@ -6053,9 +6663,9 @@
}
},
"node_modules/@tailwindcss/oxide-win32-x64-msvc": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.0.tgz",
- "integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.1.tgz",
+ "integrity": "sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA==",
"cpu": [
"x64"
],
@@ -6070,15 +6680,15 @@
}
},
"node_modules/@tailwindcss/vite": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.0.tgz",
- "integrity": "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.1.tgz",
+ "integrity": "sha512-hItDHuIIlEV61R+faXu66s1K36aTurO/Qw0e45Vskz57gXl9pWOT6eg3zmcEui6CZXddbN7zd41bwmvag4JGwQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@tailwindcss/node": "4.3.0",
- "@tailwindcss/oxide": "4.3.0",
- "tailwindcss": "4.3.0"
+ "@tailwindcss/node": "4.3.1",
+ "@tailwindcss/oxide": "4.3.1",
+ "tailwindcss": "4.3.1"
},
"peerDependencies": {
"vite": "^5.2.0 || ^6 || ^7 || ^8"
@@ -6280,6 +6890,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/esrecurse": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
+ "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/estree": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
@@ -6418,12 +7035,12 @@
}
},
"node_modules/@types/node": {
- "version": "25.9.2",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.2.tgz",
- "integrity": "sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==",
+ "version": "26.0.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.0.tgz",
+ "integrity": "sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA==",
"license": "MIT",
"dependencies": {
- "undici-types": ">=7.24.0 <7.24.7"
+ "undici-types": "~8.3.0"
}
},
"node_modules/@types/prismjs": {
@@ -6945,14 +7562,14 @@
}
},
"node_modules/@vitest/coverage-v8": {
- "version": "4.1.8",
- "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.8.tgz",
- "integrity": "sha512-lt3kovsyHwYe00wq4D1ti0Z974fWj4NLp6siqiyEufUpyFwK9Yhi7rBhac9JL5aA0zoMrJqc4vYPZRUnI7l7nw==",
+ "version": "4.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.9.tgz",
+ "integrity": "sha512-G9/lgqibheLVBDRuya45EbsEXTYcWoSG+TLg7i2axuzx0Eq62eXn+aWXyaVdV5vKvFSWd6ywcX8hA7la9Pvu8g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@bcoe/v8-coverage": "^1.0.2",
- "@vitest/utils": "4.1.8",
+ "@vitest/utils": "4.1.9",
"ast-v8-to-istanbul": "^1.0.0",
"istanbul-lib-coverage": "^3.2.2",
"istanbul-lib-report": "^3.0.1",
@@ -6966,8 +7583,8 @@
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
- "@vitest/browser": "4.1.8",
- "vitest": "4.1.8"
+ "@vitest/browser": "4.1.9",
+ "vitest": "4.1.9"
},
"peerDependenciesMeta": {
"@vitest/browser": {
@@ -6976,16 +7593,16 @@
}
},
"node_modules/@vitest/expect": {
- "version": "4.1.8",
- "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.8.tgz",
- "integrity": "sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==",
+ "version": "4.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz",
+ "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.1.0",
"@types/chai": "^5.2.2",
- "@vitest/spy": "4.1.8",
- "@vitest/utils": "4.1.8",
+ "@vitest/spy": "4.1.9",
+ "@vitest/utils": "4.1.9",
"chai": "^6.2.2",
"tinyrainbow": "^3.1.0"
},
@@ -6994,13 +7611,13 @@
}
},
"node_modules/@vitest/mocker": {
- "version": "4.1.8",
- "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.8.tgz",
- "integrity": "sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==",
+ "version": "4.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz",
+ "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/spy": "4.1.8",
+ "@vitest/spy": "4.1.9",
"estree-walker": "^3.0.3",
"magic-string": "^0.30.21"
},
@@ -7031,9 +7648,9 @@
}
},
"node_modules/@vitest/pretty-format": {
- "version": "4.1.8",
- "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.8.tgz",
- "integrity": "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==",
+ "version": "4.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz",
+ "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7044,13 +7661,13 @@
}
},
"node_modules/@vitest/runner": {
- "version": "4.1.8",
- "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.8.tgz",
- "integrity": "sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==",
+ "version": "4.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz",
+ "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/utils": "4.1.8",
+ "@vitest/utils": "4.1.9",
"pathe": "^2.0.3"
},
"funding": {
@@ -7058,14 +7675,14 @@
}
},
"node_modules/@vitest/snapshot": {
- "version": "4.1.8",
- "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.8.tgz",
- "integrity": "sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==",
+ "version": "4.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz",
+ "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/pretty-format": "4.1.8",
- "@vitest/utils": "4.1.8",
+ "@vitest/pretty-format": "4.1.9",
+ "@vitest/utils": "4.1.9",
"magic-string": "^0.30.21",
"pathe": "^2.0.3"
},
@@ -7074,9 +7691,9 @@
}
},
"node_modules/@vitest/spy": {
- "version": "4.1.8",
- "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.8.tgz",
- "integrity": "sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==",
+ "version": "4.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz",
+ "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==",
"dev": true,
"license": "MIT",
"funding": {
@@ -7084,13 +7701,13 @@
}
},
"node_modules/@vitest/ui": {
- "version": "4.1.8",
- "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.1.8.tgz",
- "integrity": "sha512-RUS2ZU2TsduVrI+9c12uTNaKrNUTsm6yFt3fueEUB9iKvyC2UP83F+sqIz00HQIah4UOL1TMoDAki8K0NjGvsA==",
+ "version": "4.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.1.9.tgz",
+ "integrity": "sha512-U/cRvtqfEPj27FI1n9cyUvi4vXXdcLhjJiI+InYKdk8hP4VrS6RXOjGL7rfFaeBc37iRKANsR6eEzIoC7lmgBQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/utils": "4.1.8",
+ "@vitest/utils": "4.1.9",
"fflate": "^0.8.2",
"flatted": "^3.4.2",
"pathe": "^2.0.3",
@@ -7102,17 +7719,17 @@
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
- "vitest": "4.1.8"
+ "vitest": "4.1.9"
}
},
"node_modules/@vitest/utils": {
- "version": "4.1.8",
- "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.8.tgz",
- "integrity": "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==",
+ "version": "4.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz",
+ "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/pretty-format": "4.1.8",
+ "@vitest/pretty-format": "4.1.9",
"convert-source-map": "^2.0.0",
"tinyrainbow": "^3.1.0"
},
@@ -7201,9 +7818,9 @@
}
},
"node_modules/acorn": {
- "version": "8.16.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
- "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
+ "version": "8.17.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz",
+ "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==",
"dev": true,
"license": "MIT",
"bin": {
@@ -7436,6 +8053,29 @@
"node": ">=18"
}
},
+ "node_modules/app-builder-lib/node_modules/js-yaml": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
+ "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
"node_modules/app-builder-lib/node_modules/semver": {
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
@@ -7520,7 +8160,6 @@
"version": "3.0.10",
"resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.10.tgz",
"integrity": "sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==",
- "dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"pvtsutils": "^1.3.6",
@@ -8054,6 +8693,29 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
+ "node_modules/builder-util/node_modules/js-yaml": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
+ "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
"node_modules/builder-util/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -8536,58 +9198,127 @@
}
},
"node_modules/concurrently": {
- "version": "9.2.1",
- "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz",
- "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==",
+ "version": "10.0.3",
+ "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-10.0.3.tgz",
+ "integrity": "sha512-hc3LH4UaKWd/bbyDK/IGVa4RB6PtQ3CUYwtrkzqHn+wIG3Hr5fhpRlk0L/gCa8ZE1L/Ufj50Zho69cI5w8SQBA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "chalk": "4.1.2",
+ "chalk": "5.6.2",
"rxjs": "7.8.2",
- "shell-quote": "1.8.3",
- "supports-color": "8.1.1",
+ "shell-quote": "1.8.4",
+ "supports-color": "10.2.2",
"tree-kill": "1.2.2",
- "yargs": "17.7.2"
+ "yargs": "18.0.0"
},
"bin": {
- "conc": "dist/bin/concurrently.js",
- "concurrently": "dist/bin/concurrently.js"
+ "conc": "dist/bin/index.js",
+ "concurrently": "dist/bin/index.js"
},
"engines": {
- "node": ">=18"
+ "node": ">=22"
},
"funding": {
"url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
}
},
- "node_modules/concurrently/node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "node_modules/concurrently/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
"engines": {
- "node": ">=10"
+ "node": ">=12"
},
"funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "node_modules/concurrently/node_modules/chalk/node_modules/supports-color": {
+ "node_modules/concurrently/node_modules/cliui": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz",
+ "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^7.2.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/concurrently/node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/concurrently/node_modules/string-width": {
"version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "has-flag": "^4.0.0"
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
},
"engines": {
- "node": ">=8"
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/concurrently/node_modules/wrap-ansi": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/concurrently/node_modules/yargs": {
+ "version": "18.0.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz",
+ "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^9.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "string-width": "^7.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^22.0.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
+ }
+ },
+ "node_modules/concurrently/node_modules/yargs-parser": {
+ "version": "22.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
+ "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
}
},
"node_modules/content-disposition": {
@@ -8768,6 +9499,29 @@
"jiti": "lib/jiti-cli.mjs"
}
},
+ "node_modules/cosmiconfig/node_modules/js-yaml": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
+ "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
"node_modules/cpu-features": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz",
@@ -9053,6 +9807,29 @@
"js-yaml": "^4.1.0"
}
},
+ "node_modules/dmg-builder/node_modules/js-yaml": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
+ "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
"node_modules/dom-accessibility-api": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz",
@@ -9477,9 +10254,9 @@
}
},
"node_modules/enhanced-resolve": {
- "version": "5.22.0",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.22.0.tgz",
- "integrity": "sha512-xYcDWrpELkFzz9SpZ3PlI6Eu6eD93Yf0WLDRxikGhWJ3MAir2SNZTIVCVZqZ/NUyx8AdMc2gT9C0gPiw18kG+A==",
+ "version": "5.21.6",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.6.tgz",
+ "integrity": "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9636,33 +10413,33 @@
}
},
"node_modules/eslint": {
- "version": "9.39.4",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz",
- "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.5.0.tgz",
+ "integrity": "sha512-1y+7C+vi12bUK1IpZeaV3gsH9fHLBmPvYmPx42pvT/E9yG0IC8g3PUZZgp0+JLJl7ZDK0flc2gc+Aw9dpCvIsQ==",
"dev": true,
"license": "MIT",
+ "workspaces": [
+ "packages/*"
+ ],
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
- "@eslint-community/regexpp": "^4.12.1",
- "@eslint/config-array": "^0.21.2",
- "@eslint/config-helpers": "^0.4.2",
- "@eslint/core": "^0.17.0",
- "@eslint/eslintrc": "^3.3.5",
- "@eslint/js": "9.39.4",
- "@eslint/plugin-kit": "^0.4.1",
+ "@eslint-community/regexpp": "^4.12.2",
+ "@eslint/config-array": "^0.23.5",
+ "@eslint/config-helpers": "^0.6.0",
+ "@eslint/core": "^1.2.1",
+ "@eslint/plugin-kit": "^0.7.2",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2",
"@types/estree": "^1.0.6",
"ajv": "^6.14.0",
- "chalk": "^4.0.0",
"cross-spawn": "^7.0.6",
"debug": "^4.3.2",
"escape-string-regexp": "^4.0.0",
- "eslint-scope": "^8.4.0",
- "eslint-visitor-keys": "^4.2.1",
- "espree": "^10.4.0",
- "esquery": "^1.5.0",
+ "eslint-scope": "^9.1.2",
+ "eslint-visitor-keys": "^5.0.1",
+ "espree": "^11.2.0",
+ "esquery": "^1.7.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
"file-entry-cache": "^8.0.0",
@@ -9672,8 +10449,7 @@
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
- "lodash.merge": "^4.6.2",
- "minimatch": "^3.1.5",
+ "minimatch": "^10.2.4",
"natural-compare": "^1.4.0",
"optionator": "^0.9.3"
},
@@ -9681,7 +10457,7 @@
"eslint": "bin/eslint.js"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
"url": "https://eslint.org/donate"
@@ -9716,9 +10492,9 @@
}
},
"node_modules/eslint-plugin-react-refresh": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.2.tgz",
- "integrity": "sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==",
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.3.tgz",
+ "integrity": "sha512-5EMmLCV98Pi4o/f/3DP/v/tNqLHMIc9I8LKClNDWhZ9JTho89/kQcitCXQBMG7sAfVRK0Ie3T2EDOzp1YXYiVA==",
"dev": true,
"license": "MIT",
"peerDependencies": {
@@ -9742,17 +10518,19 @@
}
},
"node_modules/eslint-scope": {
- "version": "8.4.0",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
- "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "version": "9.1.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz",
+ "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
+ "@types/esrecurse": "^4.3.1",
+ "@types/estree": "^1.0.8",
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
"url": "https://opencollective.com/eslint"
@@ -9788,41 +10566,6 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
- "node_modules/eslint/node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/eslint/node_modules/brace-expansion": {
- "version": "1.1.15",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
- "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/eslint/node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
"node_modules/eslint/node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
@@ -9830,45 +10573,19 @@
"dev": true,
"license": "MIT"
},
- "node_modules/eslint/node_modules/minimatch": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
- "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/eslint/node_modules/supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/espree": {
- "version": "10.4.0",
- "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
- "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "version": "11.2.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz",
+ "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
- "acorn": "^8.15.0",
+ "acorn": "^8.16.0",
"acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^4.2.1"
+ "eslint-visitor-keys": "^5.0.1"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
"url": "https://opencollective.com/eslint"
@@ -11355,9 +12072,9 @@
"license": "MIT"
},
"node_modules/js-yaml": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
- "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.0.0.tgz",
+ "integrity": "sha512-GSvaPUbk1U+FMZ7rJzF+F8e5YVtu7KnD40et/5rBXXRBv2jCO9L3qCewvIDDdudC0QycTFlf6EAA+h3kxBsuUw==",
"funding": [
{
"type": "github",
@@ -11373,7 +12090,7 @@
"argparse": "^2.0.1"
},
"bin": {
- "js-yaml": "bin/js-yaml.js"
+ "js-yaml": "bin/js-yaml.mjs"
}
},
"node_modules/jsdom": {
@@ -11891,9 +12608,9 @@
"license": "MIT"
},
"node_modules/lint-staged": {
- "version": "17.0.7",
- "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-17.0.7.tgz",
- "integrity": "sha512-JrSobt+tW3rH8IOMi8tDZd3foorM5yPEkLD/V2NxobgHrFfHWGee4MOLVuZeScgxftEwbHrPHIFA/ZL+nUJeuA==",
+ "version": "17.0.8",
+ "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-17.0.8.tgz",
+ "integrity": "sha512-B2P/d+jVW0UXOQ0MVMLrB/9ydA1P+zz6jYfdrbbEd9ur3S2rcbduFWKiUCC02Sm5hbC8nrm7y24WuYMG54HfxA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -12088,13 +12805,6 @@
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
"license": "MIT"
},
- "node_modules/lodash.merge": {
- "version": "4.6.2",
- "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
- "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/lodash.once": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
@@ -13569,9 +14279,9 @@
"optional": true
},
"node_modules/nanoid": {
- "version": "5.1.11",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.11.tgz",
- "integrity": "sha512-v+KEsUv2ps74PaSKv0gHTxTCgMXOIfBEbaqa6w6ISIGC7ZsvHN4N9oJ8d4cmf0n5oTzQz2SLmThbQWhjd/8eKg==",
+ "version": "5.1.15",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.15.tgz",
+ "integrity": "sha512-kBg3RpGtIe+RpTbyXwoI6pk5yD7KUiI3sygUqgeBMRst42KmhB4RZC7eiO9Wa1HIpaCCtpE2DJ6OI4Wi5ebwFw==",
"funding": [
{
"type": "github",
@@ -13632,6 +14342,15 @@
"node": ">=22.12.0"
}
},
+ "node_modules/node-addon-api": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.3.0.tgz",
+ "integrity": "sha512-8VOpLHFrOQlAH+qA0ZzuGRlALRA6/LVh8QJldbrC4DY0hXoMP0l4Acq8TzFC018HztWiRqyCEj2aTWY2UvnJUg==",
+ "license": "MIT",
+ "engines": {
+ "node": "^18 || ^20 || >= 21"
+ }
+ },
"node_modules/node-api-version": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.1.tgz",
@@ -13667,6 +14386,17 @@
"node": "^20.17.0 || >=22.9.0"
}
},
+ "node_modules/node-gyp-build": {
+ "version": "4.8.4",
+ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
+ "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==",
+ "license": "MIT",
+ "bin": {
+ "node-gyp-build": "bin.js",
+ "node-gyp-build-optional": "optional.js",
+ "node-gyp-build-test": "build-test.js"
+ }
+ },
"node_modules/node-gyp/node_modules/isexe": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz",
@@ -14253,9 +14983,9 @@
}
},
"node_modules/prettier": {
- "version": "3.8.3",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz",
- "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==",
+ "version": "3.8.4",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.4.tgz",
+ "integrity": "sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==",
"dev": true,
"license": "MIT",
"bin": {
@@ -14457,7 +15187,6 @@
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz",
"integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"tslib": "^2.8.1"
@@ -14467,7 +15196,6 @@
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz",
"integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=16.0.0"
@@ -15122,6 +15850,12 @@
"node": ">=8"
}
},
+ "node_modules/reflect-metadata": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
+ "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
+ "license": "Apache-2.0"
+ },
"node_modules/refractor": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/refractor/-/refractor-5.0.0.tgz",
@@ -15460,6 +16194,51 @@
"url": "https://opencollective.com/express"
}
},
+ "node_modules/serialport": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/serialport/-/serialport-13.0.0.tgz",
+ "integrity": "sha512-PHpnTd8isMGPfFTZNCzOZp9m4mAJSNWle9Jxu6BPTcWq7YXl5qN7tp8Sgn0h+WIGcD6JFz5QDgixC2s4VW7vzg==",
+ "license": "MIT",
+ "dependencies": {
+ "@serialport/binding-mock": "10.2.2",
+ "@serialport/bindings-cpp": "13.0.0",
+ "@serialport/parser-byte-length": "13.0.0",
+ "@serialport/parser-cctalk": "13.0.0",
+ "@serialport/parser-delimiter": "13.0.0",
+ "@serialport/parser-inter-byte-timeout": "13.0.0",
+ "@serialport/parser-packet-length": "13.0.0",
+ "@serialport/parser-readline": "13.0.0",
+ "@serialport/parser-ready": "13.0.0",
+ "@serialport/parser-regex": "13.0.0",
+ "@serialport/parser-slip-encoder": "13.0.0",
+ "@serialport/parser-spacepacket": "13.0.0",
+ "@serialport/stream": "13.0.0",
+ "debug": "4.4.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/serialport/donate"
+ }
+ },
+ "node_modules/serialport/node_modules/debug": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
+ "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
"node_modules/serve-static": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz",
@@ -15498,9 +16277,9 @@
"license": "ISC"
},
"node_modules/sharp": {
- "version": "0.35.1",
- "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.1.tgz",
- "integrity": "sha512-lW979AMi+ESidzMv/Lnv+F9bknzLyxLqFI05Sm433vOeRcltgxQmXpnfOOFIAlKtwXU/ksupm2srQoFCkR214g==",
+ "version": "0.35.2",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.2.tgz",
+ "integrity": "sha512-FVtFjtBCMiJS6yb5CX7Sop45WFMpeGw6oRKuJnXYgf/f1ms/D7LE/ZUSNxnW7rZ/dbslQWYkoqFHGPaDBtaK4w==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -15515,31 +16294,31 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-darwin-arm64": "0.35.1",
- "@img/sharp-darwin-x64": "0.35.1",
- "@img/sharp-freebsd-wasm32": "0.35.1",
- "@img/sharp-libvips-darwin-arm64": "1.3.0",
- "@img/sharp-libvips-darwin-x64": "1.3.0",
- "@img/sharp-libvips-linux-arm": "1.3.0",
- "@img/sharp-libvips-linux-arm64": "1.3.0",
- "@img/sharp-libvips-linux-ppc64": "1.3.0",
- "@img/sharp-libvips-linux-riscv64": "1.3.0",
- "@img/sharp-libvips-linux-s390x": "1.3.0",
- "@img/sharp-libvips-linux-x64": "1.3.0",
- "@img/sharp-libvips-linuxmusl-arm64": "1.3.0",
- "@img/sharp-libvips-linuxmusl-x64": "1.3.0",
- "@img/sharp-linux-arm": "0.35.1",
- "@img/sharp-linux-arm64": "0.35.1",
- "@img/sharp-linux-ppc64": "0.35.1",
- "@img/sharp-linux-riscv64": "0.35.1",
- "@img/sharp-linux-s390x": "0.35.1",
- "@img/sharp-linux-x64": "0.35.1",
- "@img/sharp-linuxmusl-arm64": "0.35.1",
- "@img/sharp-linuxmusl-x64": "0.35.1",
- "@img/sharp-webcontainers-wasm32": "0.35.1",
- "@img/sharp-win32-arm64": "0.35.1",
- "@img/sharp-win32-ia32": "0.35.1",
- "@img/sharp-win32-x64": "0.35.1"
+ "@img/sharp-darwin-arm64": "0.35.2",
+ "@img/sharp-darwin-x64": "0.35.2",
+ "@img/sharp-freebsd-wasm32": "0.35.2",
+ "@img/sharp-libvips-darwin-arm64": "1.3.1",
+ "@img/sharp-libvips-darwin-x64": "1.3.1",
+ "@img/sharp-libvips-linux-arm": "1.3.1",
+ "@img/sharp-libvips-linux-arm64": "1.3.1",
+ "@img/sharp-libvips-linux-ppc64": "1.3.1",
+ "@img/sharp-libvips-linux-riscv64": "1.3.1",
+ "@img/sharp-libvips-linux-s390x": "1.3.1",
+ "@img/sharp-libvips-linux-x64": "1.3.1",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.3.1",
+ "@img/sharp-libvips-linuxmusl-x64": "1.3.1",
+ "@img/sharp-linux-arm": "0.35.2",
+ "@img/sharp-linux-arm64": "0.35.2",
+ "@img/sharp-linux-ppc64": "0.35.2",
+ "@img/sharp-linux-riscv64": "0.35.2",
+ "@img/sharp-linux-s390x": "0.35.2",
+ "@img/sharp-linux-x64": "0.35.2",
+ "@img/sharp-linuxmusl-arm64": "0.35.2",
+ "@img/sharp-linuxmusl-x64": "0.35.2",
+ "@img/sharp-webcontainers-wasm32": "0.35.2",
+ "@img/sharp-win32-arm64": "0.35.2",
+ "@img/sharp-win32-ia32": "0.35.2",
+ "@img/sharp-win32-x64": "0.35.2"
}
},
"node_modules/shebang-command": {
@@ -15566,9 +16345,9 @@
}
},
"node_modules/shell-quote": {
- "version": "1.8.3",
- "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
- "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==",
+ "version": "1.8.4",
+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.4.tgz",
+ "integrity": "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -16023,19 +16802,6 @@
"node": ">=8"
}
},
- "node_modules/strip-json-comments": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
- "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/style-mod": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz",
@@ -16077,16 +16843,13 @@
}
},
"node_modules/supports-color": {
- "version": "8.1.1",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
- "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "version": "10.2.2",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz",
+ "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "has-flag": "^4.0.0"
- },
"engines": {
- "node": ">=10"
+ "node": ">=18"
},
"funding": {
"url": "https://github.com/chalk/supports-color?sponsor=1"
@@ -16118,9 +16881,9 @@
}
},
"node_modules/tailwindcss": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz",
- "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.1.tgz",
+ "integrity": "sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q==",
"dev": true,
"license": "MIT"
},
@@ -16437,6 +17200,24 @@
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
+ "node_modules/tsyringe": {
+ "version": "4.10.0",
+ "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz",
+ "integrity": "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^1.9.3"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/tsyringe/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "license": "0BSD"
+ },
"node_modules/tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
@@ -16563,9 +17344,9 @@
}
},
"node_modules/undici-types": {
- "version": "7.24.6",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
- "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz",
+ "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==",
"license": "MIT"
},
"node_modules/unified": {
@@ -17003,19 +17784,19 @@
}
},
"node_modules/vitest": {
- "version": "4.1.8",
- "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.8.tgz",
- "integrity": "sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==",
+ "version": "4.1.9",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz",
+ "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/expect": "4.1.8",
- "@vitest/mocker": "4.1.8",
- "@vitest/pretty-format": "4.1.8",
- "@vitest/runner": "4.1.8",
- "@vitest/snapshot": "4.1.8",
- "@vitest/spy": "4.1.8",
- "@vitest/utils": "4.1.8",
+ "@vitest/expect": "4.1.9",
+ "@vitest/mocker": "4.1.9",
+ "@vitest/pretty-format": "4.1.9",
+ "@vitest/runner": "4.1.9",
+ "@vitest/snapshot": "4.1.9",
+ "@vitest/spy": "4.1.9",
+ "@vitest/utils": "4.1.9",
"es-module-lexer": "^2.0.0",
"expect-type": "^1.3.0",
"magic-string": "^0.30.21",
@@ -17043,12 +17824,12 @@
"@edge-runtime/vm": "*",
"@opentelemetry/api": "^1.9.0",
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
- "@vitest/browser-playwright": "4.1.8",
- "@vitest/browser-preview": "4.1.8",
- "@vitest/browser-webdriverio": "4.1.8",
- "@vitest/coverage-istanbul": "4.1.8",
- "@vitest/coverage-v8": "4.1.8",
- "@vitest/ui": "4.1.8",
+ "@vitest/browser-playwright": "4.1.9",
+ "@vitest/browser-preview": "4.1.9",
+ "@vitest/browser-webdriverio": "4.1.9",
+ "@vitest/coverage-istanbul": "4.1.9",
+ "@vitest/coverage-v8": "4.1.9",
+ "@vitest/ui": "4.1.9",
"happy-dom": "*",
"jsdom": "*",
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
diff --git a/package.json b/package.json
index f8efb547..afc647c0 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "termix",
"private": true,
- "version": "2.4.1",
+ "version": "2.5.0",
"description": "Self-hosted SSH and remote desktop management.",
"author": "Karmaa",
"main": "electron/main.cjs",
@@ -12,6 +12,8 @@
"scripts": {
"format": "prettier --write .",
"format:check": "prettier --check .",
+ "biome:check": "biome check biome.json package.json",
+ "biome:fix": "biome check --write biome.json package.json",
"postinstall": "node scripts/patch-app-builder-lib.cjs && node scripts/patch-guacamole-lite.cjs && node scripts/patch-better-sqlite3.cjs && node scripts/patch-nan.cjs",
"prebuild": "node scripts/write-electron-build-info.cjs",
"lint": "eslint .",
@@ -31,7 +33,7 @@
"preview": "vite preview",
"electron:dev": "concurrently \"npm run dev\" \"powershell -c \\\"Start-Sleep -Seconds 5\\\" && electron .\"",
"electron:patch-builder": "node scripts/patch-app-builder-lib.cjs",
- "electron:rebuild": "electron-rebuild -f -w better-sqlite3",
+ "electron:rebuild": "electron-rebuild -f -w better-sqlite3 -w serialport",
"build:win-portable": "npm run build && npm run electron:rebuild && npm run electron:patch-builder && electron-builder --win --dir",
"build:win-installer": "npm run build && npm run electron:rebuild && npm run electron:patch-builder && electron-builder --win --publish=never",
"build:linux-portable": "npm run build && npm run electron:rebuild && npm run electron:patch-builder && electron-builder --linux --dir",
@@ -41,6 +43,8 @@
"build:mac-dev": "npm run build && npm run electron:rebuild && npm run electron:patch-builder && electron-builder --mac dir --publish=never"
},
"dependencies": {
+ "@simplewebauthn/browser": "^13.3.0",
+ "@simplewebauthn/server": "^13.3.2",
"@types/ldapjs": "^3.0.6",
"axios": "^1.18.0",
"bcryptjs": "^3.0.3",
@@ -54,14 +58,15 @@
"express": "^5.2.1",
"guacamole-lite": "^1.2.0",
"jose": "^6.2.2",
- "js-yaml": "^4.2.0",
+ "js-yaml": "^5.0.0",
"jsonwebtoken": "^9.0.3",
"jszip": "^3.10.1",
"ldapjs": "^3.0.7",
"motion": "^12.38.0",
"multer": "^2.2.0",
- "nanoid": "^5.1.9",
+ "nanoid": "^5.1.15",
"qrcode": "^1.5.4",
+ "serialport": "^13.0.0",
"socks": "^2.8.7",
"speakeasy": "^2.0.0",
"ssh2": "^1.17.0",
@@ -69,17 +74,18 @@
"ws": "^8.20.0"
},
"devDependencies": {
+ "@biomejs/biome": "2.5.1",
"@codemirror/autocomplete": "^6.20.3",
"@codemirror/commands": "^6.10.3",
- "@codemirror/search": "^6.7.0",
+ "@codemirror/search": "^6.7.1",
"@codemirror/theme-one-dark": "^6.1.3",
- "@codemirror/view": "^6.41.1",
+ "@codemirror/view": "^6.43.1",
"@commitlint/cli": "^21.0.2",
"@commitlint/config-conventional": "^21.0.2",
"@deadendjs/swagger-jsdoc": "^8.1.2",
"@electron/notarize": "^3.1.1",
"@electron/rebuild": "^4.0.4",
- "@eslint/js": "^9.0.0",
+ "@eslint/js": "^10.0.1",
"@fontsource-variable/jetbrains-mono": "^5.2.8",
"@fontsource/fira-code": "^5.2.7",
"@fontsource/jetbrains-mono": "^5.2.8",
@@ -101,7 +107,7 @@
"@radix-ui/react-switch": "^1.3.1",
"@radix-ui/react-tabs": "^1.1.14",
"@radix-ui/react-tooltip": "^1.2.9",
- "@tailwindcss/vite": "^4.2.4",
+ "@tailwindcss/vite": "^4.3.1",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
@@ -114,7 +120,7 @@
"@types/js-yaml": "^4.0.9",
"@types/jsonwebtoken": "^9.0.10",
"@types/multer": "^2.1.0",
- "@types/node": "^25.9.2",
+ "@types/node": "^26.0.0",
"@types/qrcode": "^1.5.6",
"@types/react": "^19.2.17",
"@types/react-dom": "^19.2.3",
@@ -125,8 +131,8 @@
"@uiw/codemirror-theme-github": "^4.25.9",
"@uiw/react-codemirror": "^4.25.9",
"@vitejs/plugin-react": "^6.0.1",
- "@vitest/coverage-v8": "^4.1.8",
- "@vitest/ui": "^4.1.8",
+ "@vitest/coverage-v8": "^4.1.9",
+ "@vitest/ui": "^4.1.9",
"@xterm/addon-clipboard": "^0.2.0",
"@xterm/addon-fit": "^0.11.0",
"@xterm/addon-unicode11": "^0.9.0",
@@ -135,13 +141,13 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
- "concurrently": "^9.2.1",
+ "concurrently": "^10.0.3",
"cytoscape": "^3.34.0",
"electron": "^42.4.1",
"electron-builder": "^26.15.3",
- "eslint": "^9.0.0",
+ "eslint": "^10.5.0",
"eslint-plugin-react-hooks": "^7.1.1",
- "eslint-plugin-react-refresh": "^0.5.2",
+ "eslint-plugin-react-refresh": "^0.5.3",
"eslint-plugin-unused-imports": "^4.4.1",
"globals": "^17.5.0",
"guacamole-common-js": "^1.5.0",
@@ -149,9 +155,9 @@
"i18next": "^26.3.1",
"i18next-browser-languagedetector": "^8.2.1",
"jsdom": "^29.1.1",
- "lint-staged": "^17.0.7",
+ "lint-staged": "^17.0.8",
"lucide-react": "^1.20.0",
- "prettier": "3.8.3",
+ "prettier": "3.8.4",
"radix-ui": "^1.6.0",
"react": "^19.2.7",
"react-cytoscapejs": "^2.0.0",
@@ -166,7 +172,7 @@
"react-syntax-highlighter": "^16.1.1",
"react-xtermjs": "^1.0.10",
"remark-gfm": "^4.0.1",
- "sharp": "^0.35.1",
+ "sharp": "^0.35.2",
"sonner": "^2.0.7",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.2.4",
@@ -175,7 +181,7 @@
"typescript-eslint": "^8.61.1",
"vite": "^8.0.16",
"vite-plugin-svgr": "^5.2.0",
- "vitest": "^4.1.8"
+ "vitest": "^4.1.9"
},
"lint-staged": {
"*.{ts,tsx}": [
diff --git a/readme/HOST-TO-HOST-TRANSFER.md b/readme/HOST-TO-HOST-TRANSFER.md
deleted file mode 100644
index d156e4e0..00000000
--- a/readme/HOST-TO-HOST-TRANSFER.md
+++ /dev/null
@@ -1,185 +0,0 @@
-# Host-to-host file transfer
-
-This document describes the host-to-host copy/move feature. It is intended for operators and contributors. Direct host-to-host routing via SSH tunnels is **not implemented**; that is documented under [Future work](#future-work-direct-routing-via-tunnels).
-
-## Overview
-
-Host-to-host transfer copies or moves files from one SSH host to another through the **Termix server** as a relay. The UI lives in **File Manager**: right-click files or folders and choose **Copy to host…** or **Move to host…**.
-
-Compared to copying via your laptop (`scp -3` or `ssh one 'cat …' | ssh two 'cat …'`), Termix keeps the job on the server so transfers continue if you close the browser, and you get integrated progress, cancel, retry, and cleanup.
-
-```mermaid
-flowchart LR
- Browser[Browser_UI]
- Termix[Termix_server]
- Src[Source_host]
- Dst[Destination_host]
-
- Browser -->|start_transfer_API| Termix
- Termix -->|dedicated_SSH_xfer_src| Src
- Termix -->|dedicated_SSH_xfer_dst| Dst
- Termix -->|SFTP_read_then_write| Termix
-```
-
-Data for remote-to-remote paths **always passes through the Termix process** (pipelined SFTP buffers, or a tar archive stream). There is no source→destination SSH tunnel for file bytes today.
-
-## Prerequisites
-
-1. **Two hosts** with File Manager enabled, saved in Host Manager.
-2. **Both hosts reachable from the Termix server** on SSH (each host’s jump hosts and SOCKS5 proxy chain apply to the **Termix→host** connection, not host→host).
-3. **File Manager open** on the source host (browse session connected). The destination host should show **Ready** in the transfer dialog; if it shows authentication required, open File Manager on that host once first.
-4. For **multi-file or folder** transfers, pick a **destination directory** (not a file path).
-
-The dialog shows a reminder: _“Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.”_
-
-## Using the UI
-
-### Start a transfer
-
-1. Open File Manager on the **source** host.
-2. Select one or more files or folders.
-3. Right-click → **Copy to host…** or **Move to host…** (sidebar tree supports the same context menu).
-4. In the dialog:
- - Choose **destination host** (other connected file-manager hosts).
- - Set **destination path** (type a path or use **Browse destination folders**).
- - Optionally pick a **recent destination** (collapsed by default).
- - For multi-item transfers, choose **transfer method** (Auto / Tar archive / Per-file SFTP); a preview explains what Auto will pick.
- - Pin folders with **Add to shortcuts** (per-destination host; uses normal file-manager shortcuts, not a separate favourites list).
-5. Confirm **Copy** or **Move**.
-
-### During transfer
-
-- A **progress toast** shows phase (compressing, transferring, extracting), bytes, speed, and **Cancel**.
-- **Transfer monitor** (desktop, when authenticated) picks up active transfers started in another tab or window.
-- Large **single files** use segmented SFTP (256 MiB segments) with **2 parallel lanes** by default (separate dedicated SSH session pairs per lane). The toast can show total speed and lane count when multiple lanes are active.
-
-### After completion or failure
-
-| Outcome | What happens |
-| ------------- | ---------------------------------------------------------------------------------------------------------------- |
-| **Success** | Toast success; destination path saved to **recent destinations** for that source host; file manager can refresh. |
-| **Partial** | Some paths failed; toast lists failed paths; source may be kept on move. |
-| **Error** | Toast with **Retry** when partial data on destination allows resume. |
-| **Cancelled** | Toast with optional **Clean up destination** to remove partial files. |
-
-**Move** deletes source files only after a successful full transfer (same as copy, then source delete). Cancelled or partial moves may leave files on both sides.
-
-### Metrics
-
-On success, expanded timing details can include: prepare destination, compress (tar), per-hop throughput (source→server, server→dest), extract, source delete, total duration.
-
-## Transfer methods
-
-| Method | When used | Behavior |
-| ------------------------ | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
-| **Stream (single file)** | Exactly one file, copy or move | Pipelined SFTP read on source → write on destination; segmented above 32 MiB; parallel lanes for throughput. |
-| **Tar archive** | Multi-file/folder when Auto or user selects Tar, and both sides are Unix with `tar` | `tar -czf` on source → one archive streamed through Termix → `tar -xzf` on destination. |
-| **Per-file SFTP** | Windows involved, tar unavailable, or Auto chooses it | Each file copied sequentially over SFTP through Termix. |
-
-**Auto** heuristics (see `src/backend/ssh/transfer-routing.ts`) consider file count, total size, largest file, and compressibility (e.g. many small files → tar; large incompressible sets → per-file SFTP).
-
-**Method preview** is locked for the current source path set until you change Auto/Tar/Per-file preference, so changing only the destination host does not re-scan the source.
-
-## Architecture (implementation)
-
-| Component | Role |
-| ------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
-| [`src/backend/ssh/host-transfer.ts`](../src/backend/ssh/host-transfer.ts) | Transfer engine: sessions, SFTP pipeline, tar path, retry, cancel, progress. |
-| [`src/backend/ssh/file-manager.ts`](../src/backend/ssh/file-manager.ts) | HTTP routes, `openDedicatedTransferSession`, jump/SOCKS connect. |
-| [`src/backend/ssh/transfer-routing.ts`](../src/backend/ssh/transfer-routing.ts) | Tar vs per-file SFTP selection. |
-| [`src/backend/ssh/transfer-paths.ts`](../src/backend/ssh/transfer-paths.ts) | Path normalization (Unix/Windows). |
-| [`src/ui/.../TransferToHostDialog.tsx`](../src/ui/desktop/apps/features/file-manager/components/TransferToHostDialog.tsx) | Transfer dialog. |
-| [`src/ui/.../transferProgressMonitor.tsx`](../src/ui/desktop/apps/features/file-manager/transferProgressMonitor.tsx) | Toasts, cancel, retry, cleanup. |
-| [`src/ui/.../TransferMonitor.tsx`](../src/ui/desktop/apps/features/file-manager/TransferMonitor.tsx) | Global active-transfer polling. |
-
-**Sessions:** Browse sessions identify hosts. Each transfer opens **dedicated** SSH sessions (`xfer:{transferId}:src` / `:dst`) so browsing and transfers do not share channels. Parallel lanes add `xfer:{transferId}:src:pN` / `:dst:pN`.
-
-**Special case:** If the destination host is the **same machine as Termix** (local SSH endpoint), writes use the local filesystem via `fastGet` instead of dest SFTP; data still originates from the remote source through Termix.
-
-**Persistence:** Recent destinations are stored in `transfer_recent` (per user, per source host). Folder shortcuts use `file_manager_shortcuts` on the destination host.
-
-## HTTP API (file manager service)
-
-| Endpoint | Purpose |
-| -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `POST /ssh/file_manager/ssh/transferMethodPreview` | Scan source; return resolved tar vs item_sftp and reason. |
-| `POST /ssh/file_manager/ssh/transferToHost` | Start transfer; body includes `sourceSessionId`, `destSessionId`, `sourcePaths`, `destPath`, `move`, `methodPreference`, optional `parallelSegmentCount` (1–8, default 2). |
-| `GET /ssh/file_manager/ssh/transferStatus/:transferId` | Poll progress. |
-| `GET /ssh/file_manager/ssh/activeTransfers` | List running transfers for user. |
-| `POST /ssh/file_manager/ssh/transferCancel/:transferId` | Request cancel. |
-| `POST /ssh/file_manager/ssh/transferCleanup/:transferId` | Remove partial destination artifacts after cancel/failure. |
-| `POST /ssh/file_manager/ssh/transferRetry/:transferId` | Retry with same snapshot (resume when possible). |
-
-Database (main API): `GET/POST /host/transfer/recent` for recent destinations.
-
-## Reliability features
-
-- **Resume:** Destination file size is probed; SFTP write opens with resume when a partial file exists (per segment on large files).
-- **Retry:** Reconnects dedicated sessions; segment-level and full-copy retries with backoff; fresh SSH pairs after repeated failures (lane reset).
-- **Stall detection:** ~45 s without progress on a segment; hung transfer/reconnect probing on status polls.
-- **Cancel:** Aborts in-flight SFTP; user can clean up destination paths that were created or partially written.
-- **Overlap guard:** Refuses transfer when source and destination paths overlap in a destructive way.
-
-## Limitations
-
-1. **No direct host-to-host data path** — Termix must reach **both** hosts independently (with each host’s jump/proxy settings).
-2. **Not the same as S2S SSH tunnels** — Tunnels in Host Manager forward TCP ports; they do not carry file-manager transfers today.
-3. **Throughput** — Remote-to-remote speed is bounded by Termix CPU/RAM and min(Termix↔source, Termix↔dest) links; very large files on a small Termix box may be slower than `scp -3` from a powerful desktop.
-4. **Parallel lanes** — Writes are out of order on disk; fine for copy, not for playing media from a partially written file. Default is 2 lanes; UI may not expose lane count (API default applies).
-5. **Tar** — Requires `tar` on both Unix hosts; temporary archive under `/tmp` on source during transfer.
-6. **Windows** — Tar path disabled; per-file SFTP only for Windows endpoints.
-7. **Jump hosts on S2S tunnels** — Server-to-server **tunnel** connect does not use jump hosts; only transfer/browse SSH does. A host reachable only via jump may work for transfer but not as an S2S tunnel source until tunnel code is aligned.
-
-## Troubleshooting
-
-| Symptom | Things to check |
-| ------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
-| No destination hosts listed | Open File Manager on another host; ensure host has File Manager enabled. |
-| Destination “Authentication required” | Connect File Manager on that host once in this session. |
-| Transfer fails immediately | SSH from Termix to both hosts (firewall, jump host, SOCKS5, credentials). |
-| Slow speed | Termix link to slower side; try off-peak; for single huge files, parallel lanes help if CPU/network allow. |
-| Stuck progress | Wait for stall/reconnect; cancel and retry; check server logs for `host_transfer` / `transfer_ssh_*`. |
-| Partial files after cancel | Use **Clean up destination** in the toast. |
-| 28 GB / 25 GB style progress | Usually parallel progress accounting; status polls use destination size probes. |
-
-## Comparison to manual `scp` between remotes
-
-See [Unix & Linux: scp from one remote server to another](https://unix.stackexchange.com/questions/85292/scp-from-one-remote-server-to-another-remote-server). Naive `scp one:file two:file` runs **from the first host** and fails unless that host can SSH to the second. `scp -3` relays through your workstation. **Termix relay** is analogous to `scp -3` through the **Termix server**, with richer lifecycle management, not analogous to direct `ssh source 'scp … dest'`.
-
----
-
-## Future work: direct routing via tunnels
-
-The following are **planned / discussed** enhancements, not shipped in the current build. They build on existing **S2S SSH tunnels** (`src/backend/ssh/tunnel.ts`), which already connect **source → endpoint** using `forwardOut` from the source host to the endpoint’s SSH port.
-
-### Why tunnels matter
-
-Many homelabs have a destination that is **only reachable from another host** (e.g. NAS on LAN behind a Pi), while Termix runs elsewhere. Today that destination cannot receive a dedicated `xfer:dst` session from Termix even if an S2S tunnel from Pi → NAS is configured and working.
-
-### Possible routes (future)
-
-| Route | Termix SSH legs | Data path | Benefit vs today |
-| --------------------------------------- | --------------- | --------------------------- | ---------------------------------------------------------------------------------------- |
-| **Relay (current)** | 2 | Termix buffers SFTP | Works when both hosts reachable from Termix. |
-| **Tunnel-bridged SFTP** | 1 (+ bridge) | Still through Termix memory | Dest reached via source `forwardOut`; fixes reachability; reuses most of current engine. |
-| **Direct remote (rsync/scp on source)** | 1 (control) | **Source → dest** bytes | Best throughput; Termix orchestrates `rsync`/`scp` on source when forward + tools allow. |
-
-### Integration ideas (not implemented)
-
-1. **`transfer-bridge` module** — Shared `forwardOut` probe and `connectDestThroughSource` (extracted from tunnel code); lookup matching `tunnel_connections` on the source host record.
-2. **Route resolver** — Auto-select relay vs bridged vs direct; expose route in method preview (“via Termix” vs “direct host-to-host”).
-3. **Reuse active S2S tunnel** — If Host Manager tunnel source→dest is already connected, reuse `endpointClient` instead of opening a second bridge.
-4. **Jump hosts on S2S tunnel source** — Align tunnel connect with file-manager jump chains so tunnel and transfer eligibility match.
-5. **Fallback** — Always fall back to current relay when probe or remote `rsync` fails (Windows, missing tools, forwarding denied).
-
-### What would be preserved
-
-Cancel, partial cleanup, retry/resume (rsync `--partial` on direct path; existing SFTP segment resume on relay/bridged), dedicated sessions, transfer monitor, recent destinations, folder shortcuts, and tar/per-file method selection (with direct-path variants for multi-file).
-
-### References
-
-- Internal plan: `.cursor/plans/host-to-host_direct_transfer_*.plan.md` (if present in your checkout).
-- Tunnel implementation: [`src/backend/ssh/tunnel.ts`](../src/backend/ssh/tunnel.ts) — `connectEndpointThroughSource`, `establishManagedS2STunnel`.
-- Transfer engine: [`src/backend/ssh/host-transfer.ts`](../src/backend/ssh/host-transfer.ts).
-
----
diff --git a/readme/README-AR.md b/readme/README-AR.md
index 1e8e2085..1a3066a4 100644
--- a/readme/README-AR.md
+++ b/readme/README-AR.md
@@ -28,10 +28,17 @@
+
+Termix مجاني ومفتوح المصدر. إذا وجدته مفيدًا، فكّر في [التبرع](https://donate.termix.site/) للمساعدة في تغطية تكاليف الخادم ووقت التطوير.
+
+
+
+
+
@@ -87,8 +94,8 @@ Termix هي منصة مفتوحة المصدر ومجانية للأبد وذا
-**إدارة Docker:**
-تشغيل وإيقاف وتعليق وحذف الحاويات. عرض إحصائيات الحاويات. التحكم في الحاوية باستخدام طرفية docker exec. لم يُصمم ليحل محل Portainer أو Dockge بل لإدارة حاوياتك ببساطة مقارنة بإنشائها.
+**إدارة Docker و Podman:**
+تشغيل وإيقاف وتعليق وحذف الحاويات. عرض إحصائيات الحاويات. التحكم في الحاوية باستخدام طرفية docker exec. يدعم كلاً من Docker و Podman كبيئة تشغيل للحاويات. لم يُصمم ليحل محل Portainer أو Dockge بل لإدارة حاوياتك ببساطة مقارنة بإنشائها.
@@ -115,9 +122,37 @@ Termix هي منصة مفتوحة المصدر ومجانية للأبد وذا
+**تكامل Tailscale:**
+عرض أجهزة شبكتك من Tailscale لإضافتها بسرعة كمضيفات، والاتصال عبر Tailscale SSH كطريقة مصادقة، مما يتيح لقوائم تحكم الوصول في Tailscale التعامل مع التفويض دون الحاجة لتخزين بيانات اعتماد.
+
+
+
+
**RBAC:**
إنشاء الأدوار ومشاركة المضيفات عبر المستخدمين/الأدوار.
+
+
+
+
+
+**الاتصالات التسلسلية:**
+الاتصال بالأجهزة التسلسلية (أجهزة التوجيه والمفاتيح والمتحكمات الدقيقة وغيرها) مباشرة من المتصفح أو تطبيق سطح المكتب. ضبط معدل نقل البيانات وبتات البيانات وبتات التوقف والتكافؤ. يستخدم Web Serial API في المتصفحات المدعومة أو خلفية أصلية في تطبيق Electron.
+
+
+
+
+**التنبيهات:**
+ضبط قواعد تنبيه قائمة على الحدود لمقاييس المضيف (المعالج والذاكرة والقرص وغيرها) والحصول على إشعارات عبر ntfy أو webhooks عند إطلاقها. عرض التنبيهات النشطة والمحلولة في سجل التاريخ.
+
+
+
+
+
+
+**الصفحة الرئيسية:**
+صفحة رئيسية قابلة للتخصيص بالكامل مع شبكة أدوات قابلة للسحب والإفلات. أضف أدوات لحالة المضيف وروابط الخدمات والساعات والملاحظات وخلاصات RSS والطقس وحاويات Docker ومخططات مقاييس المضيف والطرفيات المضمنة والإطارات المضمنة وأكثر.
+
@@ -255,9 +290,9 @@ networks:
## التبرع
-Termix مجاني ومفتوح المصدر بدون اشتراكات أو خطط مدفوعة. إذا وجدته مفيدًا، فكّر في التبرع للمساعدة في تغطية تكاليف الخادم والنطاقات ووقت التطوير.
+Termix مجاني ومفتوح المصدر. إذا وجدته مفيدًا، فكّر في [التبرع](https://donate.termix.site/) للمساعدة في تغطية تكاليف الخادم ووقت التطوير.
-[تبرع](https://donate.termix.site/)
+
@@ -303,6 +338,10 @@ Termix مجاني ومفتوح المصدر بدون اشتراكات أو خط
+
+
+
+
قد تكون بعض مقاطع الفيديو والصور قديمة أو قد لا تعرض الميزات بشكل مثالي.
@@ -313,7 +352,7 @@ Termix مجاني ومفتوح المصدر بدون اشتراكات أو خط
## الميزات المخططة
-راجع [المشاريع](https://github.com/orgs/Termix-SSH/projects/2) لعرض جميع الميزات المخططة. إذا كنت تتطلع للمساهمة، راجع [المساهمة](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
+راجع [المشاريع](https://github.com/orgs/Termix-SSH/projects/5) لعرض جميع الميزات المخططة. إذا كنت تتطلع للمساهمة، راجع [المساهمة](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
diff --git a/readme/README-CN.md b/readme/README-CN.md
index 8f0ae44c..c4fa5331 100644
--- a/readme/README-CN.md
+++ b/readme/README-CN.md
@@ -28,10 +28,17 @@
+
+Termix 免费且开源。如果您觉得它有用,请考虑[捐赠](https://donate.termix.site/)以帮助支付服务器费用和开发时间。
+
+
+
+
+
@@ -87,8 +94,8 @@ Termix 是一个开源、永久免费、自托管的一体化服务器管理平
-**Docker 管理:**
-启动、停止、暂停、移除容器。查看容器统计信息。通过 docker exec 终端控制容器。它的初衷不是取代 Portainer 或 Dockge,而是为了比直接创建容器更简单地管理它们。
+**Docker 和 Podman 管理:**
+启动、停止、暂停、移除容器。查看容器统计信息。通过 docker exec 终端控制容器。同时支持 Docker 和 Podman 作为容器运行时。它的初衷不是取代 Portainer 或 Dockge,而是为了比直接创建容器更简单地管理它们。
@@ -115,9 +122,37 @@ Termix 是一个开源、永久免费、自托管的一体化服务器管理平
+**Tailscale 集成:**
+列出您 Tailscale 网络中的设备以快速添加为主机,并使用 Tailscale SSH 作为身份验证方式,让您的 Tailscale ACL 处理授权而无需存储凭据。
+
+
+
+
**RBAC:**
创建角色并在用户/角色之间共享主机。
+
+
+
+
+
+**串口连接:**
+直接从浏览器或桌面应用连接到串口设备(路由器、交换机、微控制器等)。配置波特率、数据位、停止位和奇偶校验。在支持的浏览器中使用 Web Serial API,或在 Electron 应用中使用原生后端。
+
+
+
+
+**告警:**
+为主机指标(CPU、内存、磁盘等)设置基于阈值的告警规则,并通过 ntfy 或 webhook 接收触发通知。在历史日志中查看触发和已解决的告警。
+
+
+
+
+
+
+**主页:**
+具有拖放小组件网格的完全可定制主页。添加主机状态、服务链接、时钟、笔记、RSS 订阅、天气、Docker 容器、主机指标图表、嵌入式终端、iframe 等小组件。
+
@@ -255,9 +290,9 @@ networks:
## 捐赠
-Termix 是免费且开源的,没有订阅或付费计划。如果您觉得它有用,请考虑捐赠以帮助支付服务器费用、域名费用和开发时间。
+Termix 免费且开源。如果您觉得它有用,请考虑[捐赠](https://donate.termix.site/)以帮助支付服务器费用和开发时间。
-[捐赠](https://donate.termix.site/)
+
@@ -303,6 +338,10 @@ Termix 是免费且开源的,没有订阅或付费计划。如果您觉得它
+
+
+
+
某些视频和图像可能已过时,或者可能无法完美展示功能。
@@ -313,7 +352,7 @@ Termix 是免费且开源的,没有订阅或付费计划。如果您觉得它
## 计划功能
-查看 [Projects](https://github.com/orgs/Termix-SSH/projects/2) 了解所有计划功能。如果您想贡献代码,请参阅 [Contributing](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md)。
+查看 [Projects](https://github.com/orgs/Termix-SSH/projects/5) 了解所有计划功能。如果您想贡献代码,请参阅 [Contributing](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md)。
diff --git a/readme/README-DE.md b/readme/README-DE.md
index 5c2d5514..deb0836f 100644
--- a/readme/README-DE.md
+++ b/readme/README-DE.md
@@ -28,10 +28,17 @@
+
+Termix ist kostenlos und Open Source. Wenn Sie es nützlich finden, erwägen Sie eine [Spende](https://donate.termix.site/), um Serverkosten und Entwicklungszeit zu decken.
+
+
+
+
+
@@ -87,8 +94,8 @@ Verwalten Sie Dateien direkt auf Remote-Servern mit Unterstutzung fur das Anzeig
-**Docker-Verwaltung:**
-Container starten, stoppen, pausieren, entfernen. Container-Statistiken anzeigen. Container uber Docker-Exec-Terminal steuern. Es wurde nicht entwickelt, um Portainer oder Dockge zu ersetzen, sondern um Ihre Container einfach zu verwalten, anstatt sie zu erstellen.
+**Docker- und Podman-Verwaltung:**
+Container starten, stoppen, pausieren, entfernen. Container-Statistiken anzeigen. Container uber Docker-Exec-Terminal steuern. Unterstutzt sowohl Docker als auch Podman als Container-Laufzeitumgebung. Es wurde nicht entwickelt, um Portainer oder Dockge zu ersetzen, sondern um Ihre Container einfach zu verwalten, anstatt sie zu erstellen.
@@ -115,9 +122,37 @@ Sichere Benutzerverwaltung mit Admin-Kontrollen und OIDC-/LDAP-/SSO-Unterstutzun
+**Tailscale-Integration:**
+Gerate aus Ihrem Tailnet auflisten, um sie schnell als Hosts hinzuzufugen, und mit Tailscale SSH als Authentifizierungsmethode verbinden, sodass Ihre Tailnet-ACLs die Autorisierung ubernehmen, ohne Anmeldedaten speichern zu mussen.
+
+
+
+
**RBAC:**
Rollen erstellen und Hosts uber Benutzer/Rollen teilen.
+
+
+
+
+
+**Serielle Verbindungen:**
+Verbinden Sie sich direkt vom Browser oder der Desktop-App aus mit seriellen Geraten (Router, Switches, Mikrocontroller usw.). Konfigurieren Sie Baudrate, Datenbits, Stoppbits und Paritat. Verwendet die Web Serial API in unterstutzten Browsern oder ein natives Backend in der Electron-App.
+
+
+
+
+**Warnmeldungen:**
+Legen Sie schwellenwertbasierte Warnregeln fur Host-Metriken (CPU, Arbeitsspeicher, Festplatte usw.) fest und erhalten Sie Benachrichtigungen uber ntfy oder Webhooks, wenn diese ausgelost werden. Zeigen Sie ausgeloste und aufgeloste Warnmeldungen in einem Verlaufsprotokoll an.
+
+
+
+
+
+
+**Startseite:**
+Eine vollstandig anpassbare Startseite mit einem Drag-and-Drop-Widget-Raster. Fugen Sie Widgets fur Hoststatus, Service-Links, Uhren, Notizen, RSS-Feeds, Wetter, Docker-Container, Host-Metrik-Diagramme, eingebettete Terminals, iFrames und mehr hinzu.
+
@@ -255,9 +290,9 @@ networks:
## Spenden
-Termix ist kostenlos und Open Source ohne Abonnements oder kostenpflichtige Pläne. Wenn Sie es nützlich finden, erwägen Sie eine Spende, um Serverkosten, Domains und Entwicklungszeit zu decken.
+Termix ist kostenlos und Open Source. Wenn Sie es nützlich finden, erwägen Sie eine [Spende](https://donate.termix.site/), um Serverkosten und Entwicklungszeit zu decken.
-[Spenden](https://donate.termix.site/)
+
@@ -303,6 +338,10 @@ Termix ist kostenlos und Open Source ohne Abonnements oder kostenpflichtige Plä
+
+
+
+
Einige Videos und Bilder konnen veraltet sein oder Funktionen moglicherweise nicht perfekt darstellen.
@@ -313,7 +352,7 @@ Termix ist kostenlos und Open Source ohne Abonnements oder kostenpflichtige Plä
## Geplante Funktionen
-Siehe [Projekte](https://github.com/orgs/Termix-SSH/projects/2) fur alle geplanten Funktionen. Wenn Sie beitragen mochten, siehe [Mitwirken](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
+Siehe [Projekte](https://github.com/orgs/Termix-SSH/projects/5) fur alle geplanten Funktionen. Wenn Sie beitragen mochten, siehe [Mitwirken](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
diff --git a/readme/README-ES.md b/readme/README-ES.md
index 6b164c21..041d3f15 100644
--- a/readme/README-ES.md
+++ b/readme/README-ES.md
@@ -28,10 +28,17 @@
+
+Termix es gratuito y de código abierto. Si lo encuentras útil, considera [donar](https://donate.termix.site/) para ayudar a cubrir los costos del servidor y el tiempo de desarrollo.
+
+
+
+
+
@@ -87,8 +94,8 @@ Gestione archivos directamente en servidores remotos con soporte para visualizar
-**Gestion de Docker:**
-Inicie, detenga, pause, elimine contenedores. Vea estadisticas de contenedores. Controle contenedores usando el terminal docker exec. No fue creado para reemplazar Portainer o Dockge, sino para simplemente gestionar sus contenedores en lugar de crearlos.
+**Gestion de Docker y Podman:**
+Inicie, detenga, pause, elimine contenedores. Vea estadisticas de contenedores. Controle contenedores usando el terminal docker exec. Compatible con Docker y Podman como entorno de ejecucion de contenedores. No fue creado para reemplazar Portainer o Dockge, sino para simplemente gestionar sus contenedores en lugar de crearlos.
@@ -115,9 +122,37 @@ Gestion segura de usuarios con controles de administrador y soporte para OIDC/LD
+**Integracion con Tailscale:**
+Liste dispositivos de su red Tailscale para agregarlos rapidamente como hosts y conectese usando Tailscale SSH como metodo de autenticacion, permitiendo que las ACL de Tailscale gestionen la autorizacion sin almacenar credenciales.
+
+
+
+
**RBAC:**
Cree roles y comparta hosts entre usuarios/roles.
+
+
+
+
+
+**Conexiones Serie:**
+Conectese a dispositivos serie (routers, switches, microcontroladores, etc.) directamente desde el navegador o la aplicacion de escritorio. Configure la tasa de baudios, bits de datos, bits de parada y paridad. Utiliza la Web Serial API en navegadores compatibles o un backend nativo en la aplicacion Electron.
+
+
+
+
+**Alertas:**
+Configure reglas de alerta basadas en umbrales para metricas del host (CPU, memoria, disco, etc.) y reciba notificaciones a traves de ntfy o webhooks cuando se activen. Vea las alertas activas y resueltas en un historial de registros.
+
+
+
+
+
+
+**Pagina de Inicio:**
+Una pagina de inicio completamente personalizable con una cuadricula de widgets de arrastrar y soltar. Agregue widgets para estado del host, enlaces de servicios, relojes, notas, feeds RSS, clima, contenedores Docker, graficos de metricas del host, terminales integrados, iframes y mas.
+
@@ -255,9 +290,9 @@ networks:
## Donar
-Termix es gratuito y de código abierto sin suscripciones ni planes de pago. Si lo encuentras útil, considera hacer una donación para ayudar a cubrir los costos del servidor, dominios y tiempo de desarrollo.
+Termix es gratuito y de código abierto. Si lo encuentras útil, considera [donar](https://donate.termix.site/) para ayudar a cubrir los costos del servidor y el tiempo de desarrollo.
-[Donar](https://donate.termix.site/)
+
@@ -303,6 +338,10 @@ Termix es gratuito y de código abierto sin suscripciones ni planes de pago. Si
+
+
+
+
Algunos videos e imagenes pueden estar desactualizados o no mostrar perfectamente las caracteristicas.
@@ -313,7 +352,7 @@ Termix es gratuito y de código abierto sin suscripciones ni planes de pago. Si
## Caracteristicas Planeadas
-Consulte [Proyectos](https://github.com/orgs/Termix-SSH/projects/2) para todas las caracteristicas planeadas. Si desea contribuir, consulte [Contribuir](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
+Consulte [Proyectos](https://github.com/orgs/Termix-SSH/projects/5) para todas las caracteristicas planeadas. Si desea contribuir, consulte [Contribuir](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
diff --git a/readme/README-FR.md b/readme/README-FR.md
index 0915f825..c32ae6ba 100644
--- a/readme/README-FR.md
+++ b/readme/README-FR.md
@@ -28,10 +28,17 @@
+
+Termix est gratuit et open source. Si vous le trouvez utile, pensez à [faire un don](https://donate.termix.site/) pour aider à couvrir les coûts de serveur et le temps de développement.
+
+
+
+
+
@@ -87,8 +94,8 @@ Gerez les fichiers directement sur les serveurs distants avec support de la visu
-**Gestion Docker:**
-Demarrez, arretez, mettez en pause, supprimez des conteneurs. Consultez les statistiques des conteneurs. Controlez les conteneurs via le terminal docker exec. Non concu pour remplacer Portainer ou Dockge, mais plutot pour gerer simplement vos conteneurs plutot que de les creer.
+**Gestion Docker et Podman:**
+Demarrez, arretez, mettez en pause, supprimez des conteneurs. Consultez les statistiques des conteneurs. Controlez les conteneurs via le terminal docker exec. Compatible avec Docker et Podman comme environnement d'execution de conteneurs. Non concu pour remplacer Portainer ou Dockge, mais plutot pour gerer simplement vos conteneurs plutot que de les creer.
@@ -115,9 +122,37 @@ Gestion securisee des utilisateurs avec controles administrateur et support OIDC
+**Integration Tailscale:**
+Listez les appareils de votre reseau Tailscale pour les ajouter rapidement comme hotes, et connectez-vous en utilisant Tailscale SSH comme methode d'authentification, laissant les ACL de votre reseau gerer l'autorisation sans stocker de credentials.
+
+
+
+
**RBAC:**
Creez des roles et partagez des hotes entre utilisateurs/roles.
+
+
+
+
+
+**Connexions Serie:**
+Connectez-vous a des appareils serie (routeurs, commutateurs, microcontroleurs, etc.) directement depuis le navigateur ou l'application bureau. Configurez le debit en bauds, les bits de donnees, les bits d'arret et la parite. Utilise l'API Web Serial dans les navigateurs compatibles ou un backend natif dans l'application Electron.
+
+
+
+
+**Alertes:**
+Definissez des regles d'alerte basees sur des seuils pour les metriques d'hote (CPU, memoire, disque, etc.) et recevez des notifications via ntfy ou webhooks lorsqu'elles se declenchent. Consultez les alertes actives et resolues dans un journal d'historique.
+
+
+
+
+
+
+**Page d'accueil:**
+Une page d'accueil entierement personnalisable avec une grille de widgets glisser-deposer. Ajoutez des widgets pour l'etat des hotes, les liens de services, les horloges, les notes, les flux RSS, la meteo, les conteneurs Docker, les graphiques de metriques d'hote, les terminaux integres, les iframes et plus encore.
+
@@ -255,9 +290,9 @@ networks:
## Faire un don
-Termix est gratuit et open source sans abonnements ni plans payants. Si vous le trouvez utile, pensez à faire un don pour aider à couvrir les coûts de serveur, les noms de domaine et le temps de développement.
+Termix est gratuit et open source. Si vous le trouvez utile, pensez à [faire un don](https://donate.termix.site/) pour aider à couvrir les coûts de serveur et le temps de développement.
-[Faire un don](https://donate.termix.site/)
+
@@ -303,6 +338,10 @@ Termix est gratuit et open source sans abonnements ni plans payants. Si vous le
+
+
+
+
Certaines videos et images peuvent etre obsoletes ou ne pas presenter parfaitement les fonctionnalites.
@@ -313,7 +352,7 @@ Termix est gratuit et open source sans abonnements ni plans payants. Si vous le
## Fonctionnalites prevues
-Consultez les [Projects](https://github.com/orgs/Termix-SSH/projects/2) pour toutes les fonctionnalites prevues. Si vous souhaitez contribuer, consultez [Contributing](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
+Consultez les [Projects](https://github.com/orgs/Termix-SSH/projects/5) pour toutes les fonctionnalites prevues. Si vous souhaitez contribuer, consultez [Contributing](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
diff --git a/readme/README-HI.md b/readme/README-HI.md
index 04e83adc..d88917de 100644
--- a/readme/README-HI.md
+++ b/readme/README-HI.md
@@ -28,10 +28,17 @@
+
+Termix मुफ़्त और ओपन सोर्स है। यदि आपको यह उपयोगी लगता है, तो सर्वर लागत और विकास समय में मदद के लिए [दान करें](https://donate.termix.site/)।
+
+
+
+
+
@@ -87,8 +94,8 @@ Termix एक ओपन-सोर्स, हमेशा के लिए मु
-**Docker प्रबंधन:**
-कंटेनर शुरू, बंद, पॉज़, हटाएँ। कंटेनर स्टैट्स देखें। docker exec टर्मिनल का उपयोग करके कंटेनर को नियंत्रित करें। इसे Portainer या Dockge की जगह लेने के लिए नहीं बनाया गया बल्कि कंटेनर बनाने की तुलना में उन्हें सरलता से प्रबंधित करने के लिए बनाया गया है।
+**Docker और Podman प्रबंधन:**
+कंटेनर शुरू, बंद, पॉज़, हटाएँ। कंटेनर स्टैट्स देखें। docker exec टर्मिनल का उपयोग करके कंटेनर को नियंत्रित करें। Docker और Podman दोनों को कंटेनर रनटाइम के रूप में सपोर्ट करता है। इसे Portainer या Dockge की जगह लेने के लिए नहीं बनाया गया बल्कि कंटेनर बनाने की तुलना में उन्हें सरलता से प्रबंधित करने के लिए बनाया गया है।
@@ -115,9 +122,37 @@ Termix एक ओपन-सोर्स, हमेशा के लिए मु
+**Tailscale एकीकरण:**
+अपने Tailscale नेटवर्क के डिवाइस सूचीबद्ध करें ताकि उन्हें जल्दी से होस्ट के रूप में जोड़ा जा सके, और Tailscale SSH को प्रमाणीकरण विधि के रूप में उपयोग करके कनेक्ट करें, जिससे आपके Tailscale ACL क्रेडेंशियल संग्रहीत किए बिना प्राधिकरण संभाल सकें।
+
+
+
+
**RBAC:**
भूमिकाएँ बनाएँ और उपयोगकर्ताओं/भूमिकाओं में होस्ट साझा करें।
+
+
+
+
+
+**सीरियल कनेक्शन:**
+सीरियल डिवाइस (राउटर, स्विच, माइक्रोकंट्रोलर आदि) से सीधे ब्राउज़र या डेस्कटॉप ऐप से कनेक्ट करें। बॉड रेट, डेटा बिट्स, स्टॉप बिट्स और पैरिटी कॉन्फ़िगर करें। समर्थित ब्राउज़र में Web Serial API या Electron ऐप में नेटिव बैकएंड का उपयोग करता है।
+
+
+
+
+**अलर्ट:**
+होस्ट मेट्रिक्स (CPU, मेमोरी, डिस्क आदि) पर थ्रेशोल्ड-आधारित अलर्ट नियम सेट करें और जब वे ट्रिगर हों तो ntfy या webhooks के माध्यम से सूचना पाएँ। इतिहास लॉग में सक्रिय और हल किए गए अलर्ट देखें।
+
+
+
+
+
+
+**होमपेज:**
+ड्रैग-एंड-ड्रॉप विजेट ग्रिड के साथ पूरी तरह से कस्टमाइज़ करने योग्य होमपेज। होस्ट स्टेटस, सर्विस लिंक, घड़ियाँ, नोट्स, RSS फ़ीड, मौसम, Docker कंटेनर, होस्ट मेट्रिक्स चार्ट, एम्बेडेड टर्मिनल, iframes और अन्य के लिए विजेट जोड़ें।
+
@@ -255,9 +290,9 @@ networks:
## दान करें
-Termix मुफ़्त और ओपन सोर्स है, इसमें कोई सदस्यता या भुगतान योजना नहीं है। यदि आपको यह उपयोगी लगता है, तो सर्वर लागत, डोमेन और विकास समय को कवर करने में मदद के लिए दान करने पर विचार करें।
+Termix मुफ़्त और ओपन सोर्स है। यदि आपको यह उपयोगी लगता है, तो सर्वर लागत और विकास समय में मदद के लिए [दान करें](https://donate.termix.site/)।
-[दान करें](https://donate.termix.site/)
+
@@ -303,6 +338,10 @@ Termix मुफ़्त और ओपन सोर्स है, इसमे
+
+
+
+
कुछ वीडियो और छवियाँ पुरानी हो सकती हैं या विशेषताओं को पूरी तरह से प्रदर्शित नहीं कर सकती हैं।
@@ -313,7 +352,7 @@ Termix मुफ़्त और ओपन सोर्स है, इसमे
## नियोजित विशेषताएँ
-सभी नियोजित विशेषताओं के लिए [प्रोजेक्ट्स](https://github.com/orgs/Termix-SSH/projects/2) देखें। यदि आप योगदान देना चाहते हैं, तो [योगदान](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md) देखें।
+सभी नियोजित विशेषताओं के लिए [प्रोजेक्ट्स](https://github.com/orgs/Termix-SSH/projects/5) देखें। यदि आप योगदान देना चाहते हैं, तो [योगदान](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md) देखें।
diff --git a/readme/README-IT.md b/readme/README-IT.md
index 2f622b22..278430ae 100644
--- a/readme/README-IT.md
+++ b/readme/README-IT.md
@@ -28,10 +28,17 @@
+
+Termix è gratuito e open source. Se lo trovi utile, considera di [donare](https://donate.termix.site/) per aiutare a coprire i costi del server e il tempo di sviluppo.
+
+
+
+
+
@@ -87,8 +94,8 @@ Gestisci i file direttamente sui server remoti con supporto per la visualizzazio
-**Gestione Docker:**
-Avvia, ferma, metti in pausa, rimuovi container. Visualizza le statistiche dei container. Controlla i container tramite terminale docker exec. Non e stato creato per sostituire Portainer o Dockge, ma piuttosto per gestire semplicemente i tuoi container rispetto alla loro creazione.
+**Gestione Docker e Podman:**
+Avvia, ferma, metti in pausa, rimuovi container. Visualizza le statistiche dei container. Controlla i container tramite terminale docker exec. Supporta sia Docker che Podman come runtime dei container. Non e stato creato per sostituire Portainer o Dockge, ma piuttosto per gestire semplicemente i tuoi container rispetto alla loro creazione.
@@ -115,9 +122,37 @@ Gestione utenti sicura con controlli amministrativi e supporto OIDC/LDAP/SSO (co
+**Integrazione Tailscale:**
+Elenca i dispositivi della tua rete Tailscale per aggiungerli rapidamente come host, e connettiti utilizzando Tailscale SSH come metodo di autenticazione, lasciando che le ACL della tua rete gestiscano l'autorizzazione senza memorizzare credenziali.
+
+
+
+
**RBAC:**
Crea ruoli e condividi host tra utenti/ruoli.
+
+
+
+
+
+**Connessioni Seriali:**
+Connettiti a dispositivi seriali (router, switch, microcontrollori, ecc.) direttamente dal browser o dall'app desktop. Configura baud rate, bit di dati, bit di stop e parita. Utilizza la Web Serial API nei browser supportati o un backend nativo nell'app Electron.
+
+
+
+
+**Avvisi:**
+Imposta regole di avviso basate su soglie per le metriche dell'host (CPU, memoria, disco, ecc.) e ricevi notifiche tramite ntfy o webhook quando si attivano. Visualizza gli avvisi attivi e risolti in un registro storico.
+
+
+
+
+
+
+**Homepage:**
+Una homepage completamente personalizzabile con una griglia di widget drag-and-drop. Aggiungi widget per lo stato dell'host, link ai servizi, orologi, note, feed RSS, meteo, container Docker, grafici delle metriche dell'host, terminali incorporati, iframe e altro ancora.
+
@@ -255,9 +290,9 @@ networks:
## Dona
-Termix è gratuito e open source senza abbonamenti o piani a pagamento. Se lo trovi utile, considera di fare una donazione per aiutare a coprire i costi del server, i domini e il tempo di sviluppo.
+Termix è gratuito e open source. Se lo trovi utile, considera di [donare](https://donate.termix.site/) per aiutare a coprire i costi del server e il tempo di sviluppo.
-[Dona](https://donate.termix.site/)
+
@@ -303,6 +338,10 @@ Termix è gratuito e open source senza abbonamenti o piani a pagamento. Se lo tr
+
+
+
+
Alcuni video e immagini potrebbero non essere aggiornati o potrebbero non mostrare perfettamente le funzionalita.
@@ -313,7 +352,7 @@ Termix è gratuito e open source senza abbonamenti o piani a pagamento. Se lo tr
## Funzionalita Pianificate
-Consulta [Progetti](https://github.com/orgs/Termix-SSH/projects/2) per tutte le funzionalita pianificate. Se desideri contribuire, consulta [Contribuire](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
+Consulta [Progetti](https://github.com/orgs/Termix-SSH/projects/5) per tutte le funzionalita pianificate. Se desideri contribuire, consulta [Contribuire](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
diff --git a/readme/README-JA.md b/readme/README-JA.md
index 83b142fd..8980fcae 100644
--- a/readme/README-JA.md
+++ b/readme/README-JA.md
@@ -28,10 +28,17 @@
+
+Termix は無料のオープンソースプロジェクトです。便利だと感じた場合は、サーバーコストと開発時間のために[寄付](https://donate.termix.site/)をご検討ください。
+
+
+
+
+
@@ -87,8 +94,8 @@ Termixは、オープンソースで永久無料のセルフホスト型オー
-**Docker管理:**
-コンテナの起動、停止、一時停止、削除。コンテナの統計情報を表示。docker execターミナルでコンテナを操作。PortainerやDockgeの代替ではなく、コンテナの作成よりも簡易的な管理を目的としています。
+**DockerおよびPodman管理:**
+コンテナの起動、停止、一時停止、削除。コンテナの統計情報を表示。docker execターミナルでコンテナを操作。DockerとPodmanの両方をコンテナランタイムとしてサポートしています。PortainerやDockgeの代替ではなく、コンテナの作成よりも簡易的な管理を目的としています。
@@ -115,9 +122,37 @@ Termixは、オープンソースで永久無料のセルフホスト型オー
+**Tailscaleインテグレーション:**
+TailnetのデバイスをリストしてホストとしてすばやくH追加し、Tailscale SSHを認証方法として使用して接続します。これにより、TailnetのACLが認証情報を保存せずに認可を処理します。
+
+
+
+
**RBAC:**
ロールを作成し、ユーザー/ロール間でホストを共有できます。
+
+
+
+
+
+**シリアル接続:**
+ブラウザまたはデスクトップアプリからシリアルデバイス(ルーター、スイッチ、マイクロコントローラーなど)に直接接続できます。ボーレート、データビット、ストップビット、パリティを設定できます。対応ブラウザではWeb Serial APIを使用し、Electronアプリではネイティブバックエンドを使用します。
+
+
+
+
+**アラート:**
+ホストメトリクス(CPU、メモリ、ディスクなど)に対してしきい値ベースのアラートルールを設定し、発動時にntfyまたはwebhookで通知を受け取れます。発動中および解決済みのアラートを履歴ログで確認できます。
+
+
+
+
+
+
+**ホームページ:**
+ドラッグ&ドロップのウィジェットグリッドを備えた完全カスタマイズ可能なホームページ。ホストステータス、サービスリンク、時計、メモ、RSSフィード、天気、Dockerコンテナ、ホストメトリクスグラフ、埋め込みターミナル、iframeなどのウィジェットを追加できます。
+
@@ -255,9 +290,9 @@ networks:
## 寄付
-Termix はサブスクリプションや有料プランのない、無料のオープンソースプロジェクトです。便利だと感じた場合は、サーバーコスト、ドメイン、開発時間をカバーするための寄付をご検討ください。
+Termix は無料のオープンソースプロジェクトです。便利だと感じた場合は、サーバーコストと開発時間のために[寄付](https://donate.termix.site/)をご検討ください。
-[寄付する](https://donate.termix.site/)
+
@@ -303,6 +338,10 @@ Termix はサブスクリプションや有料プランのない、無料のオ
+
+
+
+
動画や画像の一部は最新ではない場合や、機能を完全に紹介できていない場合があります。
@@ -313,7 +352,7 @@ Termix はサブスクリプションや有料プランのない、無料のオ
## 予定されている機能
-すべての予定機能については[Projects](https://github.com/orgs/Termix-SSH/projects/2)をご覧ください。コントリビュートをご希望の方は[Contributing](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md)をご覧ください。
+すべての予定機能については[Projects](https://github.com/orgs/Termix-SSH/projects/5)をご覧ください。コントリビュートをご希望の方は[Contributing](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md)をご覧ください。
diff --git a/readme/README-KO.md b/readme/README-KO.md
index 7fd2891d..4fee63ca 100644
--- a/readme/README-KO.md
+++ b/readme/README-KO.md
@@ -28,10 +28,17 @@
+
+Termix는 무료 오픈소스 프로젝트입니다. 유용하게 사용하고 있다면 서버 비용과 개발 시간을 위해 [후원](https://donate.termix.site/)을 고려해 주세요.
+
+
+
+
+
@@ -87,8 +94,8 @@ Termix는 오픈 소스이며 영구 무료인 셀프 호스팅 올인원 서버
-**Docker 관리:**
-컨테이너 시작, 중지, 일시 정지, 제거. 컨테이너 통계 보기. docker exec 터미널로 컨테이너 제어. Portainer나 Dockge를 대체하기 위한 것이 아니라 컨테이너 생성보다는 간편한 관리를 목적으로 합니다.
+**Docker 및 Podman 관리:**
+컨테이너 시작, 중지, 일시 정지, 제거. 컨테이너 통계 보기. docker exec 터미널로 컨테이너 제어. Docker와 Podman을 모두 컨테이너 런타임으로 지원. Portainer나 Dockge를 대체하기 위한 것이 아니라 컨테이너 생성보다는 간편한 관리를 목적으로 합니다.
@@ -115,9 +122,37 @@ Termix는 오픈 소스이며 영구 무료인 셀프 호스팅 올인원 서버
+**Tailscale 통합:**
+Tailscale 네트워크의 기기를 나열하여 호스트로 빠르게 추가하고, Tailscale SSH를 인증 방법으로 사용하여 연결함으로써 자격 증명을 저장하지 않고도 네트워크 ACL이 권한 부여를 처리하도록 합니다.
+
+
+
+
**RBAC:**
역할을 생성하고 사용자/역할 간에 호스트 공유.
+
+
+
+
+
+**시리얼 연결:**
+브라우저 또는 데스크톱 앱에서 직접 시리얼 장치(라우터, 스위치, 마이크로컨트롤러 등)에 연결. 보드레이트, 데이터 비트, 스톱 비트, 패리티 구성. 지원 브라우저에서는 Web Serial API를, Electron 앱에서는 네이티브 백엔드를 사용합니다.
+
+
+
+
+**알림:**
+호스트 메트릭(CPU, 메모리, 디스크 등)에 대한 임계값 기반 알림 규칙을 설정하고 트리거될 때 ntfy 또는 웹훅을 통해 알림 수신. 기록 로그에서 발생 중인 알림과 해결된 알림 확인.
+
+
+
+
+
+
+**홈페이지:**
+드래그 앤 드롭 위젯 그리드를 갖춘 완전 맞춤형 홈페이지. 호스트 상태, 서비스 링크, 시계, 메모, RSS 피드, 날씨, Docker 컨테이너, 호스트 메트릭 차트, 임베디드 터미널, iframe 등의 위젯 추가 가능.
+
@@ -255,9 +290,9 @@ networks:
## 후원
-Termix는 구독이나 유료 플랜 없이 무료 오픈소스 프로젝트입니다. 유용하게 사용하고 있다면 서버 비용, 도메인, 개발 시간을 지원하기 위한 후원을 고려해 주세요.
+Termix는 무료 오픈소스 프로젝트입니다. 유용하게 사용하고 있다면 서버 비용과 개발 시간을 위해 [후원](https://donate.termix.site/)을 고려해 주세요.
-[후원하기](https://donate.termix.site/)
+
@@ -303,6 +338,10 @@ Termix는 구독이나 유료 플랜 없이 무료 오픈소스 프로젝트입
+
+
+
+
일부 비디오 및 이미지는 최신이 아니거나 기능을 완벽하게 보여주지 않을 수 있습니다.
@@ -313,7 +352,7 @@ Termix는 구독이나 유료 플랜 없이 무료 오픈소스 프로젝트입
## 계획된 기능
-모든 계획된 기능은 [Projects](https://github.com/orgs/Termix-SSH/projects/2)를 참조하세요. 기여를 원하시면 [Contributing](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md)을 참조하세요.
+모든 계획된 기능은 [Projects](https://github.com/orgs/Termix-SSH/projects/5)를 참조하세요. 기여를 원하시면 [Contributing](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md)을 참조하세요.
diff --git a/readme/README-PT.md b/readme/README-PT.md
index 5325d6d9..47656bbd 100644
--- a/readme/README-PT.md
+++ b/readme/README-PT.md
@@ -28,10 +28,17 @@
+
+Termix é gratuito e de código aberto. Se o achar útil, considere [doar](https://donate.termix.site/) para ajudar a cobrir os custos de servidor e o tempo de desenvolvimento.
+
+
+
+
+
@@ -87,8 +94,8 @@ Gerencie arquivos diretamente em servidores remotos com suporte para visualizar
-**Gerenciamento de Docker:**
-Inicie, pare, pause, remova conteineres. Visualize estatisticas de conteineres. Controle conteineres usando o terminal Docker Exec. Nao foi feito para substituir Portainer ou Dockge, mas sim para simplesmente gerenciar seus conteineres em vez de cria-los.
+**Gerenciamento de Docker e Podman:**
+Inicie, pare, pause, remova conteineres. Visualize estatisticas de conteineres. Controle conteineres usando o terminal Docker Exec. Suporta Docker e Podman como ambiente de execucao de conteineres. Nao foi feito para substituir Portainer ou Dockge, mas sim para simplesmente gerenciar seus conteineres em vez de cria-los.
@@ -115,9 +122,37 @@ Gerenciamento seguro de usuarios com controles de administrador e suporte para O
+**Integracao com Tailscale:**
+Liste dispositivos da sua rede Tailscale para adicioná-los rapidamente como hosts, e conecte-se usando Tailscale SSH como metodo de autenticacao, deixando as ACLs da sua rede gerenciar a autorizacao sem armazenar credenciais.
+
+
+
+
**RBAC:**
Crie funcoes e compartilhe hosts entre usuarios/funcoes.
+
+
+
+
+
+**Conexoes Seriais:**
+Conecte-se a dispositivos seriais (roteadores, switches, microcontroladores, etc.) diretamente do navegador ou do aplicativo desktop. Configure taxa de baud, bits de dados, bits de parada e paridade. Usa a Web Serial API em navegadores suportados ou um backend nativo no aplicativo Electron.
+
+
+
+
+**Alertas:**
+Defina regras de alerta baseadas em limites para metricas do host (CPU, memoria, disco, etc.) e receba notificacoes via ntfy ou webhooks quando forem ativadas. Visualize alertas ativos e resolvidos em um historico de registros.
+
+
+
+
+
+
+**Pagina Inicial:**
+Uma pagina inicial totalmente personalizavel com uma grade de widgets de arrastar e soltar. Adicione widgets para status do host, links de servicos, relogios, notas, feeds RSS, clima, conteineres Docker, graficos de metricas do host, terminais incorporados, iframes e mais.
+
@@ -255,9 +290,9 @@ networks:
## Doar
-O Termix é gratuito e de código aberto, sem assinaturas ou planos pagos. Se o achar útil, considere fazer uma doação para ajudar a cobrir os custos de servidor, domínios e tempo de desenvolvimento.
+Termix é gratuito e de código aberto. Se o achar útil, considere [doar](https://donate.termix.site/) para ajudar a cobrir os custos de servidor e o tempo de desenvolvimento.
-[Doar](https://donate.termix.site/)
+
@@ -303,6 +338,10 @@ O Termix é gratuito e de código aberto, sem assinaturas ou planos pagos. Se o
+
+
+
+
Alguns videos e imagens podem estar desatualizados ou podem nao mostrar perfeitamente as funcionalidades.
@@ -313,7 +352,7 @@ O Termix é gratuito e de código aberto, sem assinaturas ou planos pagos. Se o
## Funcionalidades Planejadas
-Consulte [Projetos](https://github.com/orgs/Termix-SSH/projects/2) para todas as funcionalidades planejadas. Se voce deseja contribuir, consulte [Contribuir](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
+Consulte [Projetos](https://github.com/orgs/Termix-SSH/projects/5) para todas as funcionalidades planejadas. Se voce deseja contribuir, consulte [Contribuir](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
diff --git a/readme/README-RU.md b/readme/README-RU.md
index 8bc04f24..2ed46ed4 100644
--- a/readme/README-RU.md
+++ b/readme/README-RU.md
@@ -28,10 +28,17 @@
+
+Termix — бесплатный проект с открытым исходным кодом. Если он вам полезен, рассмотрите возможность [пожертвования](https://donate.termix.site/) для покрытия расходов на серверы и время разработки.
+
+
+
+
+
@@ -87,8 +94,8 @@ Termix - это платформа для управления серверам
-**Управление Docker:**
-Запуск, остановка, приостановка, удаление контейнеров. Просмотр статистики контейнеров. Управление контейнером через терминал docker exec. Не предназначен для замены Portainer или Dockge, а скорее для простого управления контейнерами по сравнению с их созданием.
+**Управление Docker и Podman:**
+Запуск, остановка, приостановка, удаление контейнеров. Просмотр статистики контейнеров. Управление контейнером через терминал docker exec. Поддерживает как Docker, так и Podman в качестве среды выполнения контейнеров. Не предназначен для замены Portainer или Dockge, а скорее для простого управления контейнерами по сравнению с их созданием.
@@ -115,9 +122,37 @@ Termix - это платформа для управления серверам
+**Интеграция с Tailscale:**
+Список устройств вашей сети Tailscale для быстрого добавления их в качестве хостов и подключение через Tailscale SSH в качестве метода аутентификации, позволяя ACL вашей сети управлять авторизацией без хранения учётных данных.
+
+
+
+
**RBAC:**
Создание ролей и предоставление общего доступа к хостам для пользователей/ролей.
+
+
+
+
+
+**Последовательные подключения:**
+Подключение к последовательным устройствам (маршрутизаторы, коммутаторы, микроконтроллеры и т. д.) напрямую из браузера или приложения для рабочего стола. Настройка скорости передачи данных, битов данных, стоп-битов и чётности. Использует Web Serial API в поддерживаемых браузерах или нативный бэкенд в приложении Electron.
+
+
+
+
+**Оповещения:**
+Настройте правила оповещений на основе пороговых значений для метрик хоста (CPU, память, диск и т. д.) и получайте уведомления через ntfy или вебхуки при их срабатывании. Просматривайте активные и разрешённые оповещения в журнале истории.
+
+
+
+
+
+
+**Домашняя страница:**
+Полностью настраиваемая домашняя страница с сеткой виджетов с перетаскиванием. Добавляйте виджеты для статуса хоста, ссылок на сервисы, часов, заметок, RSS-лент, погоды, контейнеров Docker, графиков метрик хоста, встроенных терминалов, iframe и многого другого.
+
@@ -255,9 +290,9 @@ networks:
## Пожертвование
-Termix — бесплатный проект с открытым исходным кодом без подписок и платных планов. Если вы находите его полезным, рассмотрите возможность пожертвования для покрытия расходов на серверы, домены и время разработки.
+Termix — бесплатный проект с открытым исходным кодом. Если он вам полезен, рассмотрите возможность [пожертвования](https://donate.termix.site/) для покрытия расходов на серверы и время разработки.
-[Пожертвовать](https://donate.termix.site/)
+
@@ -303,6 +338,10 @@ Termix — бесплатный проект с открытым исходны
+
+
+
+
Некоторые видео и изображения могут быть устаревшими или не полностью отражать функциональность.
@@ -313,7 +352,7 @@ Termix — бесплатный проект с открытым исходны
## Запланированные функции
-Смотрите [Проекты](https://github.com/orgs/Termix-SSH/projects/2) для просмотра всех запланированных функций. Если вы хотите внести вклад, смотрите [Участие в разработке](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
+Смотрите [Проекты](https://github.com/orgs/Termix-SSH/projects/5) для просмотра всех запланированных функций. Если вы хотите внести вклад, смотрите [Участие в разработке](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
diff --git a/readme/README-TR.md b/readme/README-TR.md
index e11b6a7d..571d0cc8 100644
--- a/readme/README-TR.md
+++ b/readme/README-TR.md
@@ -28,10 +28,17 @@
+
+Termix ücretsiz ve açık kaynaklıdır. Faydalı buluyorsanız, sunucu maliyetleri ve geliştirme süresine katkıda bulunmak için [bağış yapmayı](https://donate.termix.site/) düşünebilirsiniz.
+
+
+
+
+
@@ -87,8 +94,8 @@ Uzak sunuculardaki dosyalari dogrudan yonetin; kod, goruntu, ses ve video gorunt
-**Docker Yonetimi:**
-Konteynerleri baslatın, durdurun, duraklatın, kaldirin. Konteyner istatistiklerini goruntuleyin. Docker exec terminali kullanarak konteyneri kontrol edin. Portainer veya Dockge'nin yerini almak icin degil, konteynerlerinizi olusturmak yerine basitce yonetmek icin tasarlanmistir.
+**Docker ve Podman Yonetimi:**
+Konteynerleri baslatın, durdurun, duraklatın, kaldirin. Konteyner istatistiklerini goruntuleyin. Docker exec terminali kullanarak konteyneri kontrol edin. Docker ve Podman'i konteyner calisma ortami olarak destekler. Portainer veya Dockge'nin yerini almak icin degil, konteynerlerinizi olusturmak yerine basitce yonetmek icin tasarlanmistir.
@@ -115,9 +122,37 @@ Yonetici kontrolleri, OIDC/LDAP/SSO (erisim kontrollu) ve 2FA (TOTP) destegi ile
+**Tailscale Entegrasyonu:**
+Tailscale aginizdaki cihazlari listeleyerek hizlica ana bilgisayar olarak ekleyin ve kimlik dogrulama yontemi olarak Tailscale SSH kullanarak baglanin; bu sayede ag ACL'leriniz kimlik bilgileri depolamadan yetkilendirmeyi yonetir.
+
+
+
+
**RBAC:**
Roller olusturun ve ana bilgisayarlari kullanicilar/roller arasinda paylasin.
+
+
+
+
+
+**Seri Baglantilar:**
+Seri cihazlara (router, switch, mikrodenetleyici vb.) dogrudan tarayici veya masaustu uygulamasindan baglanin. Baud hizi, veri bitleri, durdurma bitleri ve parite yapilandirin. Desteklenen tarayicilarda Web Serial API, Electron uygulamasinda yerel arka ucu kullanir.
+
+
+
+
+**Uyarilar:**
+Ana bilgisayar metrikleri (CPU, bellek, disk vb.) icin esik tabanli uyari kurallari belirleyin ve tetiklendiklerinde ntfy veya webhook araciligiyla bildirim alin. Gecmis gunlugunde tetiklenen ve cozulen uyarilari goruntuleyin.
+
+
+
+
+
+
+**Ana Sayfa:**
+Surukleme ve birakma widget izgarasina sahip tamamen ozerlestirilebilir bir ana sayfa. Ana bilgisayar durumu, hizmet baglantilari, saatler, notlar, RSS besleme, hava durumu, Docker konteynerleri, ana bilgisayar metrik grafikleri, gomulu terminaller, iframe ve daha fazlasi icin widget ekleyin.
+
@@ -255,9 +290,9 @@ networks:
## Bağış Yapın
-Termix, abonelik veya ücretli plan olmayan ücretsiz ve açık kaynaklı bir projedir. Faydalı buluyorsanız, sunucu maliyetleri, alan adları ve geliştirme süresini karşılamaya yardımcı olmak için bağış yapmayı düşünebilirsiniz.
+Termix ücretsiz ve açık kaynaklıdır. Faydalı buluyorsanız, sunucu maliyetleri ve geliştirme süresine katkıda bulunmak için [bağış yapmayı](https://donate.termix.site/) düşünebilirsiniz.
-[Bağış Yap](https://donate.termix.site/)
+
@@ -303,6 +338,10 @@ Termix, abonelik veya ücretli plan olmayan ücretsiz ve açık kaynaklı bir pr
+
+
+
+
Bazi videolar ve gorseller guncel olmayabilir veya ozellikleri tam olarak yansitmayabilir.
@@ -313,7 +352,7 @@ Termix, abonelik veya ücretli plan olmayan ücretsiz ve açık kaynaklı bir pr
## Planlanan Ozellikler
-Tum planlanan ozellikler icin [Projeler](https://github.com/orgs/Termix-SSH/projects/2) sayfasina bakin. Katkida bulunmak istiyorsaniz, [Katkida Bulunma](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md) sayfasina bakin.
+Tum planlanan ozellikler icin [Projeler](https://github.com/orgs/Termix-SSH/projects/5) sayfasina bakin. Katkida bulunmak istiyorsaniz, [Katkida Bulunma](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md) sayfasina bakin.
diff --git a/readme/README-VI.md b/readme/README-VI.md
index 7779af67..c0038d8c 100644
--- a/readme/README-VI.md
+++ b/readme/README-VI.md
@@ -28,10 +28,17 @@
+
+Termix là dự án miễn phí và mã nguồn mở. Nếu bạn thấy hữu ích, hãy cân nhắc [quyên góp](https://donate.termix.site/) để giúp trang trải chi phí máy chủ và thời gian phát triển.
+
+
+
+
+
@@ -87,8 +94,8 @@ Quan ly tep truc tiep tren may chu tu xa voi ho tro xem va chinh sua ma, hinh an
-**Quan Ly Docker:**
-Khoi dong, dung, tam dung, xoa container. Xem thong ke container. Dieu khien container bang terminal docker exec. Khong duoc tao ra de thay the Portainer hay Dockge ma don gian la de quan ly container cua ban thay vi tao moi chung.
+**Quan Ly Docker va Podman:**
+Khoi dong, dung, tam dung, xoa container. Xem thong ke container. Dieu khien container bang terminal docker exec. Ho tro ca Docker va Podman lam moi truong chay container. Khong duoc tao ra de thay the Portainer hay Dockge ma don gian la de quan ly container cua ban thay vi tao moi chung.
@@ -115,9 +122,37 @@ Quan ly nguoi dung an toan voi quyen quan tri va ho tro OIDC/LDAP/SSO (co kiem s
+**Tich Hop Tailscale:**
+Liet ke cac thiet bi trong mang Tailscale de nhanh chong them vao lam may chu, va ket noi bang Tailscale SSH lam phuong thuc xac thuc, de cac ACL mang xu ly uy quyen ma khong can luu tru thong tin xac thuc.
+
+
+
+
**RBAC:**
Tao vai tro va chia se may chu giua nguoi dung/vai tro.
+
+
+
+
+
+**Ket Noi Noi Tiep:**
+Ket noi voi cac thiet bi noi tiep (router, switch, vi dieu khien, v.v.) truc tiep tu trinh duyet hoac ung dung may tinh. Cau hinh toc do baud, bit du lieu, bit dung va chan le. Su dung Web Serial API tren trinh duyet duoc ho tro hoac backend ban dia trong ung dung Electron.
+
+
+
+
+**Canh Bao:**
+Dat cac quy tac canh bao dua tren nguong cho chi so may chu (CPU, bo nho, o dia, v.v.) va nhan thong bao qua ntfy hoac webhook khi chung khi toa. Xem canh bao dang kich hoat va da giai quyet trong nhat ky lich su.
+
+
+
+
+
+
+**Trang Chu:**
+Trang chu co the tuy chinh hoan toan voi luoi widget keo va tha. Them widget cho trang thai may chu, lien ket dich vu, dong ho, ghi chu, feed RSS, thoi tiet, container Docker, bieu do chi so may chu, terminal nhung, iframe va nhieu hon nua.
+
@@ -255,9 +290,9 @@ networks:
## Quyên góp
-Termix là dự án miễn phí và mã nguồn mở, không có gói đăng ký hay trả phí. Nếu bạn thấy hữu ích, hãy cân nhắc quyên góp để giúp trang trải chi phí máy chủ, tên miền và thời gian phát triển.
+Termix là dự án miễn phí và mã nguồn mở. Nếu bạn thấy hữu ích, hãy cân nhắc [quyên góp](https://donate.termix.site/) để giúp trang trải chi phí máy chủ và thời gian phát triển.
-[Quyên góp](https://donate.termix.site/)
+
@@ -303,6 +338,10 @@ Termix là dự án miễn phí và mã nguồn mở, không có gói đăng ký
+
+
+
+
Mot so video va hinh anh co the da loi thoi hoac khong the hien chinh xac hoan toan cac tinh nang.
@@ -313,7 +352,7 @@ Termix là dự án miễn phí và mã nguồn mở, không có gói đăng ký
## Tinh Nang Du Kien
-Xem [Du An](https://github.com/orgs/Termix-SSH/projects/2) de biet tat ca cac tinh nang du kien. Neu ban muon dong gop, xem [Dong Gop](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
+Xem [Du An](https://github.com/orgs/Termix-SSH/projects/5) de biet tat ca cac tinh nang du kien. Neu ban muon dong gop, xem [Dong Gop](https://github.com/Termix-SSH/Termix/blob/main/CONTRIBUTING.md).
diff --git a/repo-images/Image 15.png b/repo-images/Image 15.png
new file mode 100644
index 0000000000000000000000000000000000000000..4180e3ba24ec6c2ae04a46cdc3e8ce81019bf791
GIT binary patch
literal 539961
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