release-2.6.0 (#1085)

* fix: general bug fixes

* fix: general qol additions

* ci(deps): bump actions/setup-node in the github-actions group (#1068)

Bumps the github-actions group with 1 update: [actions/setup-node](https://github.com/actions/setup-node).


Updates `actions/setup-node` from 6 to 7
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump the dev-patch-updates group with 28 updates (#1069)

Bumps the dev-patch-updates group with 28 updates:

| Package | From | To |
| --- | --- | --- |
| [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) | `2.5.2` | `2.5.4` |
| [@codemirror/view](https://github.com/codemirror/view) | `6.43.5` | `6.43.6` |
| [@radix-ui/react-accordion](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/accordion) | `1.2.15` | `1.2.17` |
| [@radix-ui/react-alert-dialog](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/alert-dialog) | `1.1.18` | `1.1.20` |
| [@radix-ui/react-checkbox](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/checkbox) | `1.3.6` | `1.3.8` |
| [@radix-ui/react-dialog](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/dialog) | `1.1.18` | `1.1.20` |
| [@radix-ui/react-dropdown-menu](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/dropdown-menu) | `2.1.19` | `2.1.21` |
| [@radix-ui/react-label](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/label) | `2.1.11` | `2.1.12` |
| [@radix-ui/react-popover](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/popover) | `1.1.18` | `1.1.20` |
| [@radix-ui/react-progress](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/progress) | `1.1.11` | `1.1.13` |
| [@radix-ui/react-scroll-area](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/scroll-area) | `1.2.13` | `1.2.15` |
| [@radix-ui/react-select](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/select) | `2.3.2` | `2.3.4` |
| [@radix-ui/react-separator](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/separator) | `1.1.11` | `1.1.12` |
| [@radix-ui/react-slider](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/slider) | `1.4.2` | `1.4.4` |
| [@radix-ui/react-switch](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/switch) | `1.3.2` | `1.3.4` |
| [@radix-ui/react-tabs](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/tabs) | `1.1.16` | `1.1.18` |
| [@radix-ui/react-tooltip](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/tooltip) | `1.2.11` | `1.2.13` |
| [@tailwindcss/vite](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/@tailwindcss-vite) | `4.3.2` | `4.3.3` |
| [@uiw/codemirror-extensions-langs](https://github.com/uiwjs/react-codemirror) | `4.25.10` | `4.25.11` |
| [@uiw/codemirror-theme-github](https://github.com/uiwjs/react-codemirror) | `4.25.10` | `4.25.11` |
| [@uiw/react-codemirror](https://github.com/uiwjs/react-codemirror) | `4.25.10` | `4.25.11` |
| [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8) | `4.1.9` | `4.1.10` |
| [@vitest/ui](https://github.com/vitest-dev/vitest/tree/HEAD/packages/ui) | `4.1.9` | `4.1.10` |
| [i18next](https://github.com/i18next/i18next) | `26.3.4` | `26.3.6` |
| [radix-ui](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/radix-ui) | `1.6.1` | `1.6.3` |
| [react-i18next](https://github.com/i18next/react-i18next) | `17.0.8` | `17.0.10` |
| [tailwindcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss) | `4.3.2` | `4.3.3` |
| [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `4.1.9` | `4.1.10` |


Updates `@biomejs/biome` from 2.5.2 to 2.5.4
- [Release notes](https://github.com/biomejs/biome/releases)
- [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md)
- [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.5.4/packages/@biomejs/biome)

Updates `@codemirror/view` from 6.43.5 to 6.43.6
- [Changelog](https://github.com/codemirror/view/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codemirror/view/commits)

Updates `@radix-ui/react-accordion` from 1.2.15 to 1.2.17
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/accordion/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/accordion)

Updates `@radix-ui/react-alert-dialog` from 1.1.18 to 1.1.20
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/alert-dialog/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/alert-dialog)

Updates `@radix-ui/react-checkbox` from 1.3.6 to 1.3.8
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/checkbox/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/checkbox)

Updates `@radix-ui/react-dialog` from 1.1.18 to 1.1.20
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/dialog/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/dialog)

Updates `@radix-ui/react-dropdown-menu` from 2.1.19 to 2.1.21
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/dropdown-menu/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/dropdown-menu)

Updates `@radix-ui/react-label` from 2.1.11 to 2.1.12
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/label/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/label)

Updates `@radix-ui/react-popover` from 1.1.18 to 1.1.20
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/popover/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/popover)

Updates `@radix-ui/react-progress` from 1.1.11 to 1.1.13
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/progress/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/progress)

Updates `@radix-ui/react-scroll-area` from 1.2.13 to 1.2.15
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/scroll-area/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/scroll-area)

Updates `@radix-ui/react-select` from 2.3.2 to 2.3.4
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/select/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/select)

Updates `@radix-ui/react-separator` from 1.1.11 to 1.1.12
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/separator/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/separator)

Updates `@radix-ui/react-slider` from 1.4.2 to 1.4.4
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/slider/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/slider)

Updates `@radix-ui/react-switch` from 1.3.2 to 1.3.4
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/switch/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/switch)

Updates `@radix-ui/react-tabs` from 1.1.16 to 1.1.18
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/tabs/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/tabs)

Updates `@radix-ui/react-tooltip` from 1.2.11 to 1.2.13
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/tooltip/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/tooltip)

Updates `@tailwindcss/vite` from 4.3.2 to 4.3.3
- [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.3/packages/@tailwindcss-vite)

Updates `@uiw/codemirror-extensions-langs` from 4.25.10 to 4.25.11
- [Release notes](https://github.com/uiwjs/react-codemirror/releases)
- [Commits](https://github.com/uiwjs/react-codemirror/compare/v4.25.10...v4.25.11)

Updates `@uiw/codemirror-theme-github` from 4.25.10 to 4.25.11
- [Release notes](https://github.com/uiwjs/react-codemirror/releases)
- [Commits](https://github.com/uiwjs/react-codemirror/compare/v4.25.10...v4.25.11)

Updates `@uiw/react-codemirror` from 4.25.10 to 4.25.11
- [Release notes](https://github.com/uiwjs/react-codemirror/releases)
- [Commits](https://github.com/uiwjs/react-codemirror/compare/v4.25.10...v4.25.11)

Updates `@vitest/coverage-v8` from 4.1.9 to 4.1.10
- [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.10/packages/coverage-v8)

Updates `@vitest/ui` from 4.1.9 to 4.1.10
- [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.10/packages/ui)

Updates `i18next` from 26.3.4 to 26.3.6
- [Release notes](https://github.com/i18next/i18next/releases)
- [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/i18next/compare/v26.3.4...v26.3.6)

Updates `radix-ui` from 1.6.1 to 1.6.3
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/radix-ui/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/radix-ui)

Updates `react-i18next` from 17.0.8 to 17.0.10
- [Changelog](https://github.com/i18next/react-i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/react-i18next/compare/v17.0.8...v17.0.10)

Updates `tailwindcss` from 4.3.2 to 4.3.3
- [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.3/packages/tailwindcss)

Updates `vitest` from 4.1.9 to 4.1.10
- [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.10/packages/vitest)

---
updated-dependencies:
- dependency-name: "@biomejs/biome"
  dependency-version: 2.5.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@codemirror/view"
  dependency-version: 6.43.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@radix-ui/react-accordion"
  dependency-version: 1.2.17
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@radix-ui/react-alert-dialog"
  dependency-version: 1.1.20
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@radix-ui/react-checkbox"
  dependency-version: 1.3.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@radix-ui/react-dialog"
  dependency-version: 1.1.20
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@radix-ui/react-dropdown-menu"
  dependency-version: 2.1.21
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@radix-ui/react-label"
  dependency-version: 2.1.12
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@radix-ui/react-popover"
  dependency-version: 1.1.20
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@radix-ui/react-progress"
  dependency-version: 1.1.13
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@radix-ui/react-scroll-area"
  dependency-version: 1.2.15
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@radix-ui/react-select"
  dependency-version: 2.3.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@radix-ui/react-separator"
  dependency-version: 1.1.12
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@radix-ui/react-slider"
  dependency-version: 1.4.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@radix-ui/react-switch"
  dependency-version: 1.3.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@radix-ui/react-tabs"
  dependency-version: 1.1.18
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@radix-ui/react-tooltip"
  dependency-version: 1.2.13
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@tailwindcss/vite"
  dependency-version: 4.3.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@uiw/codemirror-extensions-langs"
  dependency-version: 4.25.11
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@uiw/codemirror-theme-github"
  dependency-version: 4.25.11
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@uiw/react-codemirror"
  dependency-version: 4.25.11
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@vitest/coverage-v8"
  dependency-version: 4.1.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@vitest/ui"
  dependency-version: 4.1.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: i18next
  dependency-version: 26.3.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: radix-ui
  dependency-version: 1.6.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: react-i18next
  dependency-version: 17.0.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: tailwindcss
  dependency-version: 4.3.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: vitest
  dependency-version: 4.1.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump ws in the prod-patch-updates group (#1071)

Bumps the prod-patch-updates group with 1 update: [ws](https://github.com/websockets/ws).


Updates `ws` from 8.21.0 to 8.21.1
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.21.0...8.21.1)

---
updated-dependencies:
- dependency-name: ws
  dependency-version: 8.21.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump the major-updates group with 2 updates (#1072)

Bumps the major-updates group with 2 updates: [nanoid](https://github.com/ai/nanoid) and [typescript](https://github.com/microsoft/TypeScript).


Updates `nanoid` from 5.1.16 to 6.0.0
- [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.16...6.0.0)

Updates `typescript` from 6.0.3 to 7.0.2
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Commits](https://github.com/microsoft/TypeScript/commits)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: major-updates
- dependency-name: typescript
  dependency-version: 7.0.2
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: major-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix: general qol additions and new analytics/telemetrics feature

* fix: incorrect version sent to posthog

* feat: add multiplayer/shared sessions for terminal and guacd

* feat: rework Electron desktop app to run standalone-first with optional two-way sync to a remote Termix server

* Fix Guacamole tab visibility lifecycle (#1074)

Co-authored-by: default-student <default-student@github.com>
Co-authored-by: Luke Gustafson <88517757+LukeGus@users.noreply.github.com>

* fix(alerts): send channel config as object payload instead of JSON string (#1075)

* fix tmux-monitor tailscale issue (#1076)

* Initial plan

* fix(tmux-monitor): explicitly handle tailscale auth in PanePreview hostConfig

For Tailscale-auth hosts the pane-preview attach path was building the
Terminal hostConfig with only the generic spread of host fields.  This
could omit or mismap auth-critical details and trigger a plain TCP/SSH
reachability path that doesn't work with Tailscale-only SSH endpoints.

The fix branches on `host.authType === "tailscale"` and:
- Carries `authType: "tailscale"` explicitly so the backend always selects
  the Tailscale-aware PTY path regardless of how the host object evolves.
- Derives `port` from `host.sshPort ?? host.port` so Tailscale SSH
  endpoints on a non-default SSH port are reached correctly.
- Leaves all non-tailscale auth types on the unchanged code path.

Reattach (bumping instanceId + attachNonce) continues to work because
terminalHostConfig is recomputed on every render with the latest
instanceIdRef.current value.

* refactor(tmux-monitor): simplify tailscale port logic with extracted variable

Address code review feedback: extract resolvedPort into a local variable
to avoid the duplicated `host.sshPort ?? host.port` expression that was
assigned to both `port` and `sshPort`.  Restructure as an if/else block
instead of an IIFE for readability.

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* chore: run prettier

* chore: update beta release text

* fix: cant update credential of an RDP host

* feat: add custom key shortcuts

* feat: add support for MFA over SSH

* fix: Invalid websocket frame causing code 10006 crash triggering restart loop

* fix(net): correct SSRF blocklist false-positive blocking all outbound IPv4 (#1079)

* fix: correct IPv4-mapped-IPv6 blocklist entry blocking all outbound IPv4

::ffff:0:0/96 in the IPv6 blocklist matches every IPv4 address once
mapped, since Node's BlockList compares addresses in their mapped form
internally regardless of the declared family. This caused
safeOutboundFetch to reject all IPv4-resolved destinations as private,
breaking outbound requests (e.g. ntfy/webhook notifications) whenever
DNS resolved to IPv4. Replaced with individual mapped ranges mirroring
the existing IPv4 blocklist.

* test: cover isBlockedAddress and link the Node BlockList citation

Exports isBlockedAddress so its family-crossing behavior around
IPv4-mapped-IPv6 addresses can actually be asserted, instead of relying
on manual container debugging to notice a regression. Also swaps the
prior "Node's BlockList compares addresses in mapped form" comment for
one citing the documented example in the Node docs
(https://nodejs.org/api/net.html#class-netblocklist), since that
behavior isn't otherwise obvious from the addSubnet/check call sites.

Related: Termix-SSH/Support#1024

* refactor: derive IPv6 mirror from IPv4 list, split DNS error messages

Two follow-ups from review:

- The IPv6 blocklist previously hand-duplicated each IPv4 range as its
  IPv4-mapped-IPv6 equivalent. Nothing enforced the two stayed in sync,
  which is exactly how the original bug (a mismatched ::ffff:0:0/96
  entry blocking all IPv4) was introduced in the first place. Now
  derived from a single blockedIpv4Ranges list in one loop.

- The connect.lookup hook threw the same "Private destinations are not
  allowed" for both an empty DNS result and an actually-blocked address.
  An empty result is a resolution failure, not a privacy decision, and
  conflating the two is the same kind of opaque-error problem that made
  this bug slow to diagnose in production. Split into distinct messages.

Also extracted the lookup hook itself (createDnsLookupHook) so it can be
unit-tested against a fake resolver directly, instead of only through a
real fetch()/Agent call — the bug lived entirely in this callback, and
undici wraps any error thrown here as a generic "fetch failed" TypeError,
which is why isolating it matters for testability.

---------

Co-authored-by: brennanneoh <497569+brennanneoh@users.noreply.github.com>

* fix(ssh): do not offer chacha20-poly1305 without the native ssh2 binding (#1081)

The availability probe treated a working OpenSSL "chacha20" cipher as proof
that chacha20-poly1305@openssh.com is usable. It is not: ssh2 pure-JS
chacha20-poly1305 corrupts the transport, so the peer aborts the KEX
("incomplete message [preauth]") and the connection times out. Easy to hit
on jump-host connections whose target sshd negotiates chacha20-poly1305
first.

Only trust the native binding (sshcrypto.node); otherwise leave the cipher
disabled so filterCiphers() drops it and AES-GCM is negotiated instead.

Co-authored-by: XtraLarge <>

* fix: add Swiss German server layout (#1078)

* chore: update release notes

* feat: continue improving desktop app 2-way sync with logic fixes and a migration dialog

* fix: dekstop app showing auth form without syncing

* feat: create desktop auto sessions for existing setups

* feat: add electron backend killing

* fix: electron login and session related bugs and updated readme for v2.6.0

* chore: finalize release notes

* fix: click to expand hosts including extra bottom margin

* fix: desktop auth modal failing to log users in

* fix: desktop app failing to sync

* fix: reverse proxy causing sync error

* chore: lint, format, and bump version to 2.6.0

* chore: sync Crowdin translations for 2.6.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: L.H. <117188168+default-student@users.noreply.github.com>
Co-authored-by: default-student <default-student@github.com>
Co-authored-by: Brad Baker <xyzulu@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Brennan Neoh <brennanneoh@users.noreply.github.com>
Co-authored-by: brennanneoh <497569+brennanneoh@users.noreply.github.com>
Co-authored-by: XtraLarge <eMail@WilliWerres.de>
Co-authored-by: ZacharyZcR <zacharyzcr1984@gmail.com>
This commit is contained in:
Luke Gustafson
2026-07-26 18:47:27 -05:00
committed by GitHub
co-authored by XtraLarge <> dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> L.H. default-student Brad Baker copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Copilot Autofix powered by AI Brennan Neoh brennanneoh XtraLarge ZacharyZcR
parent cf3e2cb499
commit 1a26628a48
232 changed files with 50585 additions and 32016 deletions
+216 -3
View File
@@ -389,6 +389,45 @@
"noSavedServers": "Немає збережених серверів",
"removeServer": "Видалити"
},
"migrationNotice": {
"title": "Termix Desktop тепер працює автономно",
"body1": "Цей додаток раніше підключався безпосередньо до вашого сервера Termix. Його було перероблено, щоб він працював повністю самостійно, зберігаючи хости та облікові дані локально, щоб він міг працювати офлайн. Двостороння синхронізація з власним сервером Termix тепер необов'язкова.",
"body2": "Ваші існуючі хости та облікові дані все ще на {{url}}. Увімкніть віддалену синхронізацію та повторно підключіться до цього сервера, щоб відновити їх та підтримувати синхронізацію надалі.",
"dismiss": "Не зараз",
"setUpSync": "Налаштування віддаленої синхронізації"
},
"remoteSync": {
"title": "Віддалена синхронізація",
"description": "За бажанням, підключіть цей настільний додаток до окремого сервера Termix, щоб синхронізувати свої хости, облікові дані та фрагменти коду на всіх пристроях. Додаток завжди працює повністю офлайн, незалежно від того, чи підключено ви його до мережі.",
"notConnected": "Не підключено",
"connected": "Підключено",
"connectedTo": "Підключено до {{url}}",
"lastSynced": "Остання синхронізація {{time}}",
"neverSynced": "Ніколи не синхронізувалося",
"syncError": "Помилка синхронізації: {{message}}",
"needsReauth": "Термін дії входу минув",
"connectButton": "Підключення до сервера",
"disconnectButton": "Відключитися",
"syncNowButton": "Синхронізувати зараз",
"syncing": "Синхронізація...",
"serverUrl": "URL-адреса сервера",
"enterServerUrl": "Будь ласка, введіть URL-адресу сервера",
"mustIncludeProtocol": "URL-адреса сервера має починатися з http:// або https://",
"connectionTestFailed": "Не вдалося зв'язатися із сервером Termix за цією URL-адресою",
"allowInvalidCertificate": "Дозволити недійсний сертифікат",
"allowInvalidCertificateDesc": "Використовуйте лише для довірених серверів із власним хостингом та самопідписаними сертифікатами або сертифікатами IP-адреси.",
"savedServers": "Збережені сервери",
"removeServer": "Видалити",
"continueButton": "Продовжити",
"cancelButton": "Скасувати",
"signInTitle": "Увійти до {{url}}",
"originTitle": "Джерело з'єднання",
"originDescription": "Виберіть, звідки за замовчуванням створюються SSH-з’єднання. Це можна змінити для кожного хоста окремо.",
"originLocal": "Цей пристрій (локальна мережа)",
"originRemote": "Віддалений сервер",
"bannerReconnect": "Знову підключитися",
"bannerMessage": "Віддалена синхронізація потребує повторної автентифікації"
},
"versionCheck": {
"error": "Помилка перевірки версії",
"checkFailed": "Не вдалося перевірити наявність оновлень",
@@ -602,6 +641,7 @@
"overrideCredentialUsername": "Перезаписати ім'я користувача облікових даних",
"overrideCredentialUsernameDesc": "Використовуйте вказане вище ім'я користувача замість імені користувача облікових даних",
"oidcUsernameHint": "Використовуйте $oidc.preferred_username замість вашого імені користувача OIDC.",
"tailscaleUsernameHint": "Це має бути користувач Unix, якому надано ідентифікатор Tailscale у SSH ACL tailnet, не обов'язково root.",
"jumpHostChain": "Ланцюг хоста стрибка",
"portKnocking": "Порт-Нокінґ",
"addKnock": "Додати порт",
@@ -650,6 +690,11 @@
"delayAfterMs": "Затримка після (мс)",
"useSocks5Proxy": "Використовуйте проксі-сервер SOCKS5",
"useSocks5ProxyDesc": "Маршрутизувати з'єднання через проксі-сервер",
"connectionOrigin": "Джерело з'єднання",
"connectionOriginDesc": "Звідки походить SSH-з’єднання цього хоста. Замінює глобальне налаштування за замовчуванням для програми для робочого столу.",
"connectionOriginDefault": "Використовувати за замовчуванням",
"connectionOriginLocal": "Цей пристрій (локальна мережа)",
"connectionOriginRemote": "Віддалений сервер",
"proxyHost": "Проксі-хост",
"proxyPort": "Проксі-порт",
"proxyUsername": "Ім'я користувача проксі-сервера",
@@ -762,6 +807,14 @@
"customThemeBrightCyan": "Яскравий блакитний",
"customThemeBrightWhite": "Яскраво-білий",
"customThemeResetTooltip": "Скинути до налаштувань за замовчуванням",
"savedThemesLabel": "Збережені теми",
"saveAsGlobalTheme": "Зберегти як глобальну тему",
"saveGlobalThemeNamePrompt": "Введіть назву для цієї теми",
"saveGlobalThemeSuccess": "Тему збережено",
"saveGlobalThemeError": "Не вдалося зберегти тему",
"applyGlobalThemeTooltip": "Застосувати цю тему",
"deleteGlobalThemeTooltip": "Видалити цю тему",
"noSavedThemes": "Ще немає збережених тем",
"syntaxHighlightingLabel": "Підсвічування синтаксису",
"syntaxHighlightingDesc": "Розфарбувати вивід терміналу (помилки, шляхи, IP-адреси, позначки часу)",
"syntaxHighlightingCategories": "Виділіть категорії",
@@ -791,6 +844,8 @@
"enableAutoTmuxDesc": "Автоматично запускати або приєднуватися до сеансу tmux",
"enableSessionLogging": "Ведення журналу сеансу",
"enableSessionLoggingDesc": "Запис виводу сеансу терміналу для подальшого перегляду",
"allowSessionSharing": "Дозволити спільний доступ до сеансу",
"allowSessionSharingDesc": "Дозволити ділитися прямими трансляціями на цьому хості через посилання або з іншими користувачами",
"enableCommandHistory": "Історія команд",
"enableCommandHistoryDesc": "Запис команд, що виконуються в цьому терміналі, для історії та автозаповнення",
"linkClickBehaviorLabel": "Поведінка кліків посилань",
@@ -895,6 +950,7 @@
"proxmoxResultImported": "{{count}} імпортовано",
"proxmoxResultUpdated": "{{count}} оновлено",
"proxmoxResultFailed": "{{count}} не вдалося",
"proxmoxResultSkippedNoIp": "{{count}} пропущено (IP-адресу не знайдено)",
"proxmoxImportComplete": "Імпорт Proxmox завершено: {{summary}}",
"proxmoxDiscoveryFailed": "Не вдалося знайти",
"proxmoxImportFailed": "Імпорт не вдалося",
@@ -961,6 +1017,9 @@
"failedToSaveCredential": "Не вдалося зберегти облікові дані",
"credentialNameRequired": "Будь ласка, введіть ім'я для облікових даних",
"credentialAuthRequired": "Додайте пароль, ключ SSH або обидва",
"createCredentialFromHostBtn": "Створити облікові дані",
"createCredentialFromHostTitle": "Створити облікові дані від хоста",
"createCredentialFromHostDesc": "Створіть багаторазовий, спільний запис облікових даних, попередньо заповнений поточним іменем користувача, паролем та/або ключем SSH цього хоста.",
"backToHosts": "Назад до хостів",
"backToCredentials": "Повернутися до облікових даних",
"pinned": "Закріплено",
@@ -1000,6 +1059,9 @@
"folderNestingHint": "Використовуйте / для розділення рівнів та створення вкладених папок.",
"folderColor": "Колір",
"folderIcon": "Значок",
"folderCredential": "Посвідчення",
"folderCredentialNone": "Облікові дані не призначено",
"folderCredentialHint": "Хости в цій папці, які використовують автентифікацію \"Збережені облікові дані\" без вибраних власних облікових даних, успадкують ці дані.",
"folderPreview": "Попередній перегляд",
"folderNameFallback": "Папка без назви",
"createFolderButton": "Створити папку",
@@ -1181,6 +1243,10 @@
"filterTagsGroup": "Теги",
"shareHost": "Спільний хост",
"shareHostTitle": "Поділитися: {{name}}",
"shareFolder": "Спільний доступ до папки",
"shareFolderTitle": "Папка для спільного доступу: {{name}}",
"folderSharedSuccessfully": "Спільні хости {{count}} у папці",
"failedToShareFolder": "Не вдалося поділитися папкою",
"sharing": {
"loadError": "Не вдалося завантажити дані для спільного доступу. Спробуйте ще раз.",
"shareWithSection": "Поділитися з",
@@ -1221,6 +1287,7 @@
"shareWithCount": "Поділитися ({{count}})",
"currentAccess": "Поточний доступ",
"noAccessEntries": "Цей хост ще не був спільним",
"folderShareSummary": "Спільні хости {{shared}} з {{total}} у цій папці",
"grantedBy": "Надано",
"expires": "Термін дії закінчується",
"expired": "Термін дії минув",
@@ -1242,6 +1309,8 @@
"authMethod": "Метод автентифікації",
"authTypeDirect": "Прямий",
"authTypeCredential": "Посвідчення",
"authTypeNone": "Жоден",
"authTypeNoneDesc": "Облікові дані не зберігаються. Вам буде запропоновано ввести ім’я користувача та пароль щоразу під час підключення; вони не зберігаються.",
"selectCredential": "Виберіть облікові дані...",
"connectionSettings": "Налаштування підключення",
"displaySettings": "Налаштування дисплея",
@@ -1259,6 +1328,7 @@
"rdpPort": "RDP-порт",
"username": "Ім'я користувача",
"password": "Пароль",
"passwordSaved": "Пароль збережено, введіть, щоб змінити",
"domain": "Домен",
"securityMode": "Режим безпеки",
"colorDepth": "Глибина кольору",
@@ -1424,7 +1494,60 @@
"expiresIn": "Термін дії закінчується через {{duration}}",
"search": "Пошук з'єднань...",
"noSearchResults": "Немає з'єднань, що відповідають вашому пошуку",
"rename": "Перейменувати сеанс"
"rename": "Перейменувати сеанс",
"sectionSharedWithMe": "Поділилися зі мною",
"sharedBy": "Поділився користувачем {{username}}",
"join": "Приєднатися",
"sharedSessionLabel": "{{hostName}} (спільний доступ)"
},
"sessionSharing": {
"guestView": {
"loading": "Підключення до спільного сеансу...",
"linkInvalid": "Це посилання для спільного доступу недійсне, термін його дії минув або його було відкликано",
"rateLimited": "Забагато спроб, спробуйте ще раз пізніше",
"sessionEnded": "Цей сеанс завершився",
"readOnlyBadge": "Тільки перегляд"
},
"modalTitle": "Поділитися сеансом",
"shareButton": "Поділитися",
"notReadyToShare": "Сеанс ще не готовий до поширення",
"modeTab": {
"link": "Посилання",
"user": "Користувач"
},
"linkModeDescription": "Приєднатися може кожен, хто має це посилання, обліковий запис не потрібен.",
"userModeDescription": "Надайте доступ певному користувачеві, який уже має доступ до цього хоста. Якщо у нього ще немає доступу, спочатку надайте йому доступ до хоста або скористайтеся посиланням. Після надання доступу сеанс відображається на його вкладці «Підключення».",
"permissionLevel": {
"label": "Рівень дозволів",
"readOnly": "Тільки для читання",
"readOnlyDescription": "Можна дивитися сеанс у прямому ефірі, але не можна друкувати чи взаємодіяти.",
"readWrite": "Читання-запис",
"readWriteDescription": "Можна друкувати та взаємодіяти з сеансом так само, як і його власник."
},
"expiryLabel": "Термін дії посилання",
"createLinkButton": "Створити посилання",
"createShareButton": "Поділитися з користувачем",
"searchUsersPlaceholder": "Пошук користувачів...",
"noUsersFound": "Користувачів не знайдено",
"linkCreated": "Посилання для спільного доступу створено",
"linkCopied": "Посилання скопійовано в буфер обміну",
"copyLink": "Копіювати посилання",
"shareCreated": "Сеанс спільний. Він відображатиметься на вкладці «Зв’язки».",
"shareFailed": "Не вдалося створити спільний доступ",
"userLacksHostAccess": "Цей користувач ще не має доступу до цього хоста. Спочатку надайте йому доступ до хоста або скористайтеся посиланням.",
"activeShares": "Активні акції",
"noActiveShares": "Немає активних поширень для цього сеансу",
"revoke": "Скасувати",
"revokeConfirmTitle": "Скасувати цей спільний доступ?",
"revokeConfirmDescription": "Будь-хто, хто користується цим ресурсом спільного доступу, негайно втратить доступ.",
"revoked": "Акції анульовано",
"revokeFailed": "Не вдалося скасувати спільний доступ",
"joinCount": "{{count}} приєднатися",
"joinCount_other": "{{count}} приєднується",
"expiresAt": "Термін дії закінчується {{date}}",
"linkShareBadge": "Посилання",
"userShareBadge": "Користувач: {{username}}",
"loadSharesFailed": "Не вдалося завантажити активні спільні ресурси"
},
"guacamole": {
"connecting": "Підключення до сеансу {{type}}...",
@@ -1436,6 +1559,9 @@
"reconnect": "Знову підключитися",
"retry": "Повторити спробу",
"guacdUnavailable": "Служба віддаленого робочого столу (guacd) недоступна. Переконайтеся, що guacd запущено, доступно та правильно налаштовано в налаштуваннях адміністратора.",
"credentialPromptTitle": "Введіть облікові дані RDP",
"credentialPromptDescription": "Цей хост налаштовано на запит облікових даних під час підключення. Вони використовуються лише для цього сеансу та не зберігаються.",
"connect": "Підключитися",
"ctrlAltDel": "Ctrl+Alt+Del",
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
@@ -1461,7 +1587,9 @@
"reconnect": "Сеанс повторного підключення",
"collapse": "Згорнути панель інструментів",
"expand": "Розгорнути панель інструментів",
"dragHandle": "Перетягніть, щоб змінити положення"
"dragHandle": "Перетягніть, щоб змінити положення",
"switchToTrackpad": "Перехід у режим трекпада (перетягування для переміщення курсора, торкання для клацання)",
"switchToTouch": "Перемикання в сенсорний режим (торкайтеся безпосередньо там, де потрібно клацнути)"
}
},
"terminal": {
@@ -1508,6 +1636,11 @@
"totpRequired": "Потрібна двофакторна автентифікація",
"totpCodeLabel": "Код підтвердження",
"totpVerify": "Перевірити",
"mfaPromptRequired": "Потрібна автентифікація",
"mfaPushRequired": "Потрібна автентифікація push-повідомлень",
"mfaMenuPlaceholder": "Введіть свою відповідь",
"mfaWaitingApproval": "Очікування схвалення на вашому пристрої...",
"mfaSendRequest": "Надіслати запит",
"warpgateAuthRequired": "Потрібна автентифікація Warpgate",
"warpgateSecurityKey": "Ключ безпеки",
"warpgateAuthUrl": "URL-адреса автентифікації",
@@ -2277,7 +2410,8 @@
"resetCodeRateLimited": "Обмеження швидкості: Забагато спроб перевірки. Будь ласка, спробуйте пізніше.",
"resetCodeRateLimitedWithTime": "Обмеження швидкості: Забагато спроб перевірки. Будь ласка, зачекайте {{time}} секунд, перш ніж повторити спробу.",
"authTokenSaveFailed": "Не вдалося зберегти токен автентифікації",
"failedToLoadServer": "Не вдалося завантажити сервер"
"failedToLoadServer": "Не вдалося завантажити сервер",
"remoteServerRequired": "Потрібен віддалений сервер. Підключіться до віддаленого сервера в налаштуваннях, щоб використовувати цей тип підключення."
},
"messages": {
"registrationDisabled": "Реєстрацію нового облікового запису наразі вимкнено адміністратором. Будь ласка, увійдіть або зверніться до адміністратора.",
@@ -2644,6 +2778,17 @@
"sslSaveFailed": "Не вдалося зберегти налаштування SSL",
"sslRequiresDomain": "Домен та електронна адреса обов'язкові",
"sslInfoNote": "Після видачі сертифіката увімкніть SSL у змінних середовища (ENABLE_SSL=true) та перезапустіть Termix.",
"sslManualOption": "Інструкція (сертифікат завантаження)",
"sslManualCert": "Сертифікат (PEM)",
"sslManualCertPlaceholder": "-----ПОЧАТИ СЕРТИФІКАТ-----",
"sslManualKey": "Закритий ключ (PEM)",
"sslManualKeyPlaceholder": "-----ПОЧАТИ ПРИВАТНИЙ КЛЮЧ-----",
"sslManualDesc": "Вставте свій існуючий сертифікат і закритий ключ, включаючи повний ланцюжок, якщо цього вимагає ваш центр сертифікації.",
"sslManualUpload": "Сертифікат завантаження та встановлення",
"sslManualUploadLoading": "Завантаження сертифіката...",
"sslManualUploadSuccess": "Сертифікат успішно завантажено та встановлено",
"sslManualUploadFailed": "Не вдалося завантажити сертифікат",
"sslManualRequiresFields": "Потрібні сертифікат та закритий ключ",
"auditLogTotal": "{{total}} всього записів",
"auditLogEmpty": "Записів журналу аудиту не знайдено",
"auditLogSuccess": "Успіх",
@@ -2672,6 +2817,12 @@
"commandHistoryEnabled": "Історія команд",
"commandHistoryEnabledDesc": "Дозволити запис історії команд. Якщо вимкнено, історія не зберігається незалежно від налаштувань для кожного хоста.",
"updateCommandHistoryFailed": "Не вдалося оновити налаштування історії команд",
"analyticsEnabled": "Поділитися анонімною статистикою використання",
"analyticsEnabledDesc": "Надсилає анонімний щоденний підрахунок користувачів, хостів та використання функцій, щоб допомогти покращити Termix. Жодні особисті дані чи деталі з’єднання ніколи не включаються.",
"updateAnalyticsFailed": "Не вдалося оновити налаштування аналітики",
"sessionSharingGloballyEnabled": "Дозволити спільний доступ до сеансу",
"sessionSharingGloballyEnabledDesc": "Дозволити спільний доступ до сеансів терміналу, RDP, VNC та Telnet для всього екземпляра. Перевизначає кожен перемикач спільного доступу для кожного хоста, якщо його вимкнено.",
"updateSessionSharingFailed": "Не вдалося оновити налаштування спільного доступу до сеансу",
"sessionTimeout": "Час очікування сеансу",
"hours": "години",
"sessionTimeoutRange": "Мін. 1 год · Макс. 720 год",
@@ -3119,6 +3270,47 @@
"executionSuccess": "Успіх",
"executionFailed": "Не вдалося"
},
"keybindings": {
"title": "Комбінації клавіш",
"description": "Налаштуйте комбінації клавіш для копіювання, вставки та керування терміналом або прив’яжіть клавіші для надсилання тексту чи запуску фрагмента коду.",
"defaultsHeading": "Вбудовані комбінації клавіш",
"customHeading": "Користувацькі скорочення",
"addBinding": "Додати ярлик",
"addBindingTitle": "Додати ярлик",
"editBindingTitle": "Редагувати скорочення",
"loading": "Завантаження...",
"noCustomBindings": "Поки що немає користувацьких комбінацій клавіш",
"defaultBadge": "За замовчуванням",
"customizedBadge": "Налаштовані",
"customize": "Налаштувати",
"resetToDefault": "Скинути до заводських налаштувань",
"resetAllToDefaults": "Скинути всі налаштування до заводських",
"close": "Закрити",
"cancel": "Скасувати",
"saveBinding": "Зберегти ярлик",
"saveError": "Не вдалося зберегти комбінації клавіш",
"pressKeysToRecord": "Комбінація клавіш",
"pressKeysPlaceholder": "Клацайте та натискайте клавіші",
"recording": "Натискайте клавіші...",
"comboRequiredError": "Спочатку натисніть комбінацію клавіш",
"textRequiredError": "Введіть текст для надсилання",
"controlCodeRequiredError": "Введіть одну літеру для контрольного коду",
"snippetRequiredError": "Виберіть фрагмент",
"conflictWarning": "Ця комбінація вже пов'язана з {{combo}}. Збереження призведе до спрацювання обох.",
"actionLabel": "Дія",
"actionCopy": "Копіювати виділення",
"actionPaste": "Вставити",
"actionSendControlCode": "Надіслати сигнал Ctrl+літера",
"actionSendText": "Надіслати текст",
"actionRunSnippet": "Запустити існуючий фрагмент коду",
"controlCodeLabel": "Лист",
"textLabel": "Текст для надсилання",
"appendEnterLabel": "Натисніть Enter після відправлення",
"snippetLabel": "Фрагмент",
"selectSnippetPlaceholder": "Виберіть фрагмент",
"orphanedSnippetWarning": "фрагмент не знайдено",
"clipboardPermissionNote": "У деяких браузерах може запитуватися дозвіл на використання буфера обміну під час першого використання."
},
"userProfile": {
"donateTitle": "Збережи Термікс живим",
"donateDescription": "Termix безкоштовний, розміщується самостійно та створений лише кількома людьми у вільний час. Якщо це заощадило вам час або гроші, пожертва у криптовалюті допоможе йому продовжувати роботу.",
@@ -3134,6 +3326,7 @@
"sectionAppearance": "Зовнішній вигляд",
"sectionSecurity": "Безпека",
"sectionApiKeys": "Ключі API",
"sectionData": "Дані",
"sectionC2sTunnels": "Тунелі C2S",
"usernameLabel": "Ім'я користувача",
"roleLabel": "Роль",
@@ -3162,6 +3355,9 @@
"settingsTerminal": "Термінал",
"commandAutocomplete": "Автозаповнення команд",
"commandAutocompleteDesc": "Показувати автозаповнення під час введення тексту",
"keyboardShortcuts": "Комбінації клавіш",
"keyboardShortcutsDescription": "Налаштування скорочень команд, копіювання, вставки та інших команд у терміналі",
"manageShortcuts": "Керувати",
"terminalLinkBehavior": "Натисніть посилання терміналу",
"terminalLinkBehaviorDesc": "Поведінка за замовчуванням під час натискання посилань у терміналі",
"historyTracking": "Відстеження історії",
@@ -3228,6 +3424,22 @@
"apiKeyUsageHint": "Додайте свій ключ до",
"apiKeyUsageHintHeader": "заголовок.",
"apiKeyPermissionsHint": "Ключі успадковують дозволи користувача, який їх створює.",
"exportData": "Експорт моїх даних",
"exportDataDesc": "Завантажте резервну копію своїх хостів, облікових даних та налаштувань для перенесення на інший пристрій",
"export": "Експорт",
"exporting": "Експорт...",
"importData": "Імпортувати мої дані",
"importDataDesc": "Відновіть свої хости, облікові дані та налаштування з резервного файлу .sqlite",
"importDataSelected": "Вибрано: {{name}}",
"selectFile": "Виберіть файл",
"changeFile": "Зміна",
"import": "Імпорт",
"importing": "Імпорт...",
"exportSuccess": "Дані успішно експортовано",
"exportFailed": "Не вдалося експортувати дані",
"importSelectFile": "Спочатку виберіть файл",
"importCompleted": "Імпорт завершено: імпортовано {{total}} елементів, пропущено {{skipped}}",
"importFailed": "Помилка імпорту: {{error}}",
"roleUser": "Користувач",
"authMethodDual": "Подвійна автентифікація",
"authMethodOidc": "ОІДК",
@@ -3379,6 +3591,7 @@
"pageUp": "PgUp",
"pageDown": "ПгДн",
"delete": "Дел",
"paste": "Вставити",
"editQuickKeys": "Редагувати швидкі клавіші",
"quickKeysTitle": "Швидкі клавіші",
"quickKeysDesc": "Натисніть ×, щоб видалити. Підтримує до 8 символів.",