release-2.9.0 (#1532)

* fix(macos): skip single-instance lock in Mac App Store builds (#1454)

fix(macos): skip single-instance lock in Mac App Store builds

* Fix/backend optional sharp (#1455)

Fix/backend optional sharp

* feat: issue #1218 (#1218)

https://github.com/Termix-SSH/Support/issues/1218

* feat: Add option to use saved global custom theme under Connection Defaults (#1209)

https://github.com/Termix-SSH/Support/issues/1209

* feat: Add Fleet Sharing functionality to the UI (#1228)

https://github.com/Termix-SSH/Support/issues/1228

* feat: Add a list view to the Docker management page (#1237)

https://github.com/Termix-SSH/Support/issues/1237

* feat: issue #1264 (#1264)

https://github.com/Termix-SSH/Support/issues/1264

* feat: 2FA With FortiToken (#1288)

https://github.com/Termix-SSH/Support/issues/1288

* feat: Allow to disable showing paths to folders (#1274)

https://github.com/Termix-SSH/Support/issues/1274

* feat: Sync Network Graph between Desktop and Remote Server (#1245)

https://github.com/Termix-SSH/Support/issues/1245

* feat: Version Number on offline servers (#1291)

https://github.com/Termix-SSH/Support/issues/1291

* feat: Support OrbStack Docker socket path on macOS / Fix "Docker is not installed" on mac... (#1302)

https://github.com/Termix-SSH/Support/issues/1302

* feat: Sidebar host click should focus existing tab instead of opening a new connection (#1289)

https://github.com/Termix-SSH/Support/issues/1289

* fix: Search in side bar shows overlapping hosts when grouped by tags (#1303)

https://github.com/Termix-SSH/Support/issues/1303

* fix: I can't send a file on its own. (#1304)

https://github.com/Termix-SSH/Support/issues/1304

* fix: sudo password not autofilling (#1248)

https://github.com/Termix-SSH/Support/issues/1248

* fix: node-MainThread (#1300)

https://github.com/Termix-SSH/Support/issues/1300

* fix: Tunnel authentication with shared credentials (#1295)

https://github.com/Termix-SSH/Support/issues/1295

* fix: host key updates not syncing, metrics 404 race on first connect

Host key writes never bumped updatedAt so sync never picked them up.
Also retry the first metrics fetch briefly instead of failing right away.

* chore: increment ver

* feat: add category and icon fields to plugin manifest schema

* ci(deps): bump the github-actions group with 2 updates (#1458)

Bumps the github-actions group with 2 updates: [crowdin/github-action](https://github.com/crowdin/github-action) and [actions/github-script](https://github.com/actions/github-script).


Updates `crowdin/github-action` from 2 to 3
- [Release notes](https://github.com/crowdin/github-action/releases)
- [Commits](https://github.com/crowdin/github-action/compare/v2...v3)

Updates `actions/github-script` from 7 to 9
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v7...v9)

---
updated-dependencies:
- dependency-name: crowdin/github-action
  dependency-version: '3'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/github-script
  dependency-version: '9'
  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 13 updates (#1459)

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

| Package | From | To |
| --- | --- | --- |
| [@codemirror/commands](https://github.com/codemirror/commands) | `6.11.0` | `6.11.1` |
| [@codemirror/search](https://github.com/codemirror/search) | `6.7.1` | `6.7.2` |
| [@codemirror/view](https://github.com/codemirror/view) | `6.43.9` | `6.43.12` |
| [@testing-library/dom](https://github.com/testing-library/dom-testing-library) | `10.4.1` | `10.4.2` |
| [@testing-library/react](https://github.com/testing-library/react-testing-library) | `16.3.2` | `16.3.3` |
| [@testing-library/user-event](https://github.com/testing-library/user-event) | `14.6.5` | `14.6.7` |
| [@types/ssh2](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/ssh2) | `1.15.5` | `1.15.6` |
| [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react) | `6.1.0` | `6.1.1` |
| [cytoscape](https://github.com/cytoscape/cytoscape.js) | `3.34.1` | `3.34.3` |
| [eslint-plugin-react-refresh](https://github.com/ArnaudBarre/eslint-plugin-react-refresh) | `0.5.4` | `0.5.7` |
| [i18next](https://github.com/i18next/i18next) | `26.4.0` | `26.4.2` |
| [prettier](https://github.com/prettier/prettier) | `3.9.6` | `3.9.8` |
| [react-i18next](https://github.com/i18next/react-i18next) | `17.0.12` | `17.0.14` |


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

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

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

Updates `@testing-library/dom` from 10.4.1 to 10.4.2
- [Release notes](https://github.com/testing-library/dom-testing-library/releases)
- [Changelog](https://github.com/testing-library/dom-testing-library/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/dom-testing-library/compare/v10.4.1...v10.4.2)

Updates `@testing-library/react` from 16.3.2 to 16.3.3
- [Release notes](https://github.com/testing-library/react-testing-library/releases)
- [Changelog](https://github.com/testing-library/react-testing-library/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/react-testing-library/compare/v16.3.2...v16.3.3)

Updates `@testing-library/user-event` from 14.6.5 to 14.6.7
- [Release notes](https://github.com/testing-library/user-event/releases)
- [Changelog](https://github.com/testing-library/user-event/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/user-event/compare/v14.6.5...v14.6.7)

Updates `@types/ssh2` from 1.15.5 to 1.15.6
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/ssh2)

Updates `@vitejs/plugin-react` from 6.1.0 to 6.1.1
- [Release notes](https://github.com/vitejs/vite-plugin-react/releases)
- [Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite-plugin-react/commits/plugin-react@6.1.1/packages/plugin-react)

Updates `cytoscape` from 3.34.1 to 3.34.3
- [Release notes](https://github.com/cytoscape/cytoscape.js/releases)
- [Commits](https://github.com/cytoscape/cytoscape.js/compare/v3.34.1...v3.34.3)

Updates `eslint-plugin-react-refresh` from 0.5.4 to 0.5.7
- [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.4...v0.5.7)

Updates `i18next` from 26.4.0 to 26.4.2
- [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.4.0...v26.4.2)

Updates `prettier` from 3.9.6 to 3.9.8
- [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.9.6...3.9.8)

Updates `react-i18next` from 17.0.12 to 17.0.14
- [Changelog](https://github.com/i18next/react-i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/react-i18next/compare/v17.0.12...v17.0.14)

---
updated-dependencies:
- dependency-name: "@codemirror/commands"
  dependency-version: 6.11.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@codemirror/search"
  dependency-version: 6.7.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@codemirror/view"
  dependency-version: 6.43.12
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@testing-library/dom"
  dependency-version: 10.4.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@testing-library/react"
  dependency-version: 16.3.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@testing-library/user-event"
  dependency-version: 14.6.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@types/ssh2"
  dependency-version: 1.15.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@vitejs/plugin-react"
  dependency-version: 6.1.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: cytoscape
  dependency-version: 3.34.3
  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.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: i18next
  dependency-version: 26.4.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: prettier
  dependency-version: 3.9.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: react-i18next
  dependency-version: 17.0.14
  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 the prod-patch-updates group with 6 updates (#1461)

Bumps the prod-patch-updates group with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [@tanstack/react-virtual](https://github.com/TanStack/virtual/tree/HEAD/packages/react-virtual) | `3.14.10` | `3.14.13` |
| [compression](https://github.com/expressjs/compression) | `1.8.1` | `1.8.2` |
| [jose](https://github.com/panva/jose) | `6.2.9` | `6.2.12` |
| [jszip](https://github.com/Stuk/jszip) | `3.10.1` | `3.10.2` |
| [socks](https://github.com/JoshGlazebrook/socks) | `2.8.9` | `2.8.10` |
| [undici](https://github.com/nodejs/undici) | `8.10.0` | `8.10.2` |


Updates `@tanstack/react-virtual` from 3.14.10 to 3.14.13
- [Release notes](https://github.com/TanStack/virtual/releases)
- [Changelog](https://github.com/TanStack/virtual/blob/main/packages/react-virtual/CHANGELOG.md)
- [Commits](https://github.com/TanStack/virtual/commits/@tanstack/react-virtual@3.14.13/packages/react-virtual)

Updates `compression` from 1.8.1 to 1.8.2
- [Release notes](https://github.com/expressjs/compression/releases)
- [Changelog](https://github.com/expressjs/compression/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/compression/compare/v1.8.1...v1.8.2)

Updates `jose` from 6.2.9 to 6.2.12
- [Release notes](https://github.com/panva/jose/releases)
- [Changelog](https://github.com/panva/jose/blob/main/CHANGELOG.md)
- [Commits](https://github.com/panva/jose/compare/v6.2.9...v6.2.12)

Updates `jszip` from 3.10.1 to 3.10.2
- [Changelog](https://github.com/Stuk/jszip/blob/main/CHANGES.md)
- [Commits](https://github.com/Stuk/jszip/compare/v3.10.1...v3.10.2)

Updates `socks` from 2.8.9 to 2.8.10
- [Release notes](https://github.com/JoshGlazebrook/socks/releases)
- [Commits](https://github.com/JoshGlazebrook/socks/compare/2.8.9...2.8.10)

Updates `undici` from 8.10.0 to 8.10.2
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v8.10.0...v8.10.2)

---
updated-dependencies:
- dependency-name: "@tanstack/react-virtual"
  dependency-version: 3.14.13
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: compression
  dependency-version: 1.8.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: jose
  dependency-version: 6.2.12
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: jszip
  dependency-version: 3.10.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: socks
  dependency-version: 2.8.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: undici
  dependency-version: 8.10.2
  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>

* feat: add plugin worker protocol, bootstrap and plugin_storage table

Worker-side ctx proxy plus the message envelope it talks over, and the
per-plugin key/value table behind ctx.storage with migrations for all
three dialects. Rows cascade with the plugin so uninstalling leaves
nothing behind.

* feat: add allowlisted host view for plugins

An allowlist rather than a list of fields to strip. A blocklist rots:
the moment a new secret-bearing column lands on SSHHost it starts
leaking silently. With an allowlist a new field stays invisible to
plugins until someone adds it here on purpose.

* feat: add plugin loader with manifest validation and crash restart

Validates an unpacked plugin, spawns a worker_threads worker and manages
activate/deactivate/crash-restart with backoff, disabling after three
tries.

The crash counter clears only after a plugin stays up past a stability
window, not on activation. Clearing it on activation let a plugin that
activates cleanly and then dies restart forever, because every attempt
looked like the first.

loader.ts documents what the worker boundary does and does not buy you.
Node's permission model is process-wide, not per-worker, so it cannot be
enabled for plugin workers alone and is not claimed here.

* feat: route internal events through a shared plugin event bus

Formalises three ad-hoc publish paths that already existed:
automation-events.ts, metrics/automation-bridge.ts and the
hostSessionStatus singleton, now the host.session.status topic.

Both invariants the old code relied on are preserved. Publishing stays
fire-and-forget, so a failing subscriber cannot disturb a metrics poll or
a host delete. The automations engine subscribes to the bus at scheduler
start instead of being imported ad hoc, which keeps the edge
one-directional: automations may import repositories, hosts modules must
not import automations.

* feat: add plugin ctx broker with permission gate and audit

Every ctx call a plugin makes is checked against its granted
capabilities, performed by the main thread, then audited. A capability
must be both declared in the manifest and granted in the database, so
widening a plugin's reach always needs a new manifest the user can see,
never just a database row.

Audit attribution is set by the broker, not the plugin. The plugin
supplies only the details, so it cannot forge the actor.

fix: stop plugin workers seeing plaintext credentials in error messages

A failing ctx.ssh.connect handed the worker the raw error from deep in
the SSH stack, which embeds the connect config. The worker received, in
full:

  Authentication failed for root using password <SECRET>

where <SECRET> was the host's real plaintext password. Any plugin with
ssh.exec could read it by catching the error, defeating the whole point
of the handle-based ssh API.

Errors crossing the boundary are now sanitised. Only PluginFacingError
and PluginPermissionError, both authored here, reach a plugin verbatim;
anything else becomes a generic message and the real one goes to the
log. This uses a marker class rather than matching on message text,
because matching would start leaking again the moment an upstream error
happened to contain a familiar phrase.

Covered by credential-isolation.test.ts, which plants a sentinel in
every secret-bearing field and asserts it appears in no message the
worker ever receives.

* feat: dispatch plugin HTTP routes and wire the runtime into startup

ctx.http.route now registers a real router on the /plugin-api/:pluginId
dispatcher, replacing its stub 404. A plugin never touches the Express
req/res: it gets a plain summary and returns a plain object, so sockets
and session cookies stay on this side of the boundary. Authorization and
cookie headers are not forwarded.

Plugins load last in the start-up sequence, after the server is fully
wired, and are terminated before the database on shutdown so none can
outlive it. A plugin that fails to load cannot stop the backend.

fix: correct the nginx plugin-api location regex

The pattern was ^/plugin-api/(/.*)?$, which needs a double slash and so
never matched /plugin-api/<id>/<route>. Every other block in the file
uses ^/prefix(/.*)?$. Left alone, plugin routes would work in dev and
404 only behind Docker.

* fix: update stale SFTP transfer test for single file destination paths

The test still asserted transferToHost was called with the destination
directory "/srv". Commit 7727fa09 (#1304) deliberately changed a single
file transfer to send the full destination path including the filename,
so the correct expectation is "/srv/remote-1.txt". The code was right
and the test was left behind.

* feat: ship the ssh terminal as a first-party in-process plugin

Adds a hardcoded process:transport-owner tier so the terminal can own its
WebSocket server and ssh2 clients, which cannot cross the worker boundary.

* feat: ship docker as a first-party in-process plugin

Moves docker backend and frontend into plugins/docker/, alongside
ssh-terminal. Adds a host editor tab seam, a dashboard card registry,
and a build step to compile plugin backends into dist.

* feat: ship host metrics as a first-party in-process plugin

* fix: harden plugin auth and add per-request identity for ctx calls

Require auth on /plugin-api, gate plugin enable/grant/revoke routes
behind admin.plugins.manage, and thread the calling user through
worker ctx.hosts/ctx.ssh calls instead of always using the install
owner. Also adds the plugin permissions grant/revoke UI.

* feat: ship the AI assistant as a first-party in-process plugin

Moves src/backend/ai and its routes into plugins/ai, mounted through a
small /ai dispatcher instead of its own port. Moves the ai-enabled and
ai-private-endpoints settings routes out of user-settings-routes.ts
into the plugin too, so it has no leftover core dependents.

* fix: remove compiled plugin JS left behind in plugins/ after build

copy-bundled-plugins.cjs compiled and rewrote plugin backend TS in
place, copied it to dist/plugins, but never deleted the .js it had
just emitted next to the .ts sources. Those files shadowed the real
source for vitest/tsx and had import paths rewritten for the dist
layout, so tests failed with "Cannot find module" once a local build
had run. Affects docker, host-metrics and ai, not just this one.

* feat: add a permission-gated backend service registry for plugins

* feat: add a permission-gated UI action registry and action slots for plugins

Moves the terminal toolbar's hardwired AI button into a slot the ssh-terminal
plugin offers and the ai plugin fills, gated on ai.services.use.

* feat: add cross-plugin secret references gated by the provider's RBAC permission

* feat: move proxmox discovery/import/sync into a first-party plugin

* feat: move remote desktop (RDP/VNC/Telnet) into a first-party plugin

Relocates guacamole backend/frontend source and tests into
plugins/remote-desktop, following the ssh-terminal/docker pattern.

* feat: wire remote-desktop plugin into core and rename i18n keys

Repoints core consumers (dispatcher, collab, session-sharing, tab
utils) at the plugin, adds isTabTypeAvailable gating for rdp/vnc/
telnet, widens hostCapability to accept an array, and renames the
guacamole i18n namespace to remoteDesktop.

* feat: move fleets into a first-party plugin

* feat: move automations into a first-party plugin

* feat: remove legacy alert rules, superseded by automations

Alert rules already auto-migrate into automations on boot and the old
engine stands down after. Deletes the rule/firing tables, routes, UI,
and AI tools; keeps notification channels since automations still use
them. Also fixes automations' notify step, which still called the
deleted repository.

* feat: move network topology into a first-party plugin

* feat: move tailscale device discovery and settings into a plugin

Adds plugins/tailscale/ with a devices sidebar tab, admin settings
section, and re-auth dialog. Host Metrics tailscale manager card and
SSH re-auth banner parsing stay in core.

* feat: move workspaces into a first-party plugin

* feat: move web endpoint into a first-party plugin

* feat: collapse plugin runtime to one tier behind a plugin SDK

* feat: make each plugin an npm workspace built by a termix-plugin CLI

* feat: give plugins their own tables, migrations and sync entities

* feat: serve plugin http and websockets from one mount instead of ports

* feat: namespace plugin permissions and keep them valid when a plugin is off

* feat: give plugins their own settings and one settings screen

* feat: load plugin frontends at runtime and remove plugin ids from the shell

* fix: mock the ai status fetch in its activate test

* feat: add auth extension points and one ssh connect pipeline

* feat: rebuild workspaces as the reference plugin

* fix: gate second factors after SSO/LDAP behind an admin setting

* feat: convert network-topology to a full plugin (B1)

* feat: move snippets backend and tables into a plugin (B2)

* feat: point core frontend snippets pickers at the plugin (B2)

* feat: add the snippets plugin (B2)

* feat: wire ai, automations, fleets and ssh-terminal to snippets (B2)

* feat: finish the tailscale plugin, including SSH auth (B3)

* feat: finish the fleets plugin, add ctx.hosts and host-commands SDK (B4)

* chore: update proxmox and host-metrics locale files for B5

* chore: regenerate drizzle migration journals for B5

* feat: add ctx.hosts create/update to the SDK, host-metrics drops proxmox

* feat: move proxmox node stats and collectors into the proxmox plugin

* feat: drop proxmox host columns, use plugin settings instead

* feat: finish the fleets plugin, add ctx.hosts and host-commands SDK (B4)

* feat: convert file manager into a plugin

* feat: convert ssh tunnels into a plugin

* feat: finish the web-endpoint plugin, add ctx.desktop (B8)

* feat: move the terminal fully into the ssh-terminal plugin (B9)

* feat: convert tmux monitoring into a plugin (B10)

* feat: convert serial console into the serial plugin

* feat: move session sharing and collab rooms into a plugin (B12)

* feat: turn session recording into a plugin (B13)

* feat: finish the remote-desktop plugin (B14)

* feat: finish the host-metrics plugin, move host status into core (B16)

* feat: finish the docker plugin (B15)

* feat: finish the automations plugin (B17)

* feat: finish the ai plugin (B18)

* feat: finish the homepage plugin, move service links and canvas out of core (B19)

* feat: convert wake-on-lan into a plugin (B20)

* feat: convert secret-sources into a plugin (B20)

* feat: move totp and passkeys into plugins (C1)

* feat: move sso and ldap logins into plugins (C2)

* feat: move opkssh and warpgate into plugins (C3)

* feat: move step-ca into a plugin (C4)

* feat: move vault ssh signing into a plugin (C5)

* feat: move termix identity into a plugin (C6)

* feat: move acme certificate automation into a plugin (C7)

* fix: finish 2.9.0 plugin moves and get every check green

* chore: lock the plugin boundary and remove 2.9.0 leftovers

* fix: harden the plugin runtime after the 2.9.0 security review

* feat: add plugin packing, signing, pinned trust and tmxplug bundling

* test: prove 2.8 to 2.9.0 upgrade is lossless and fix what it caught

* fix: give bundled plugin backends a real require for CJS deps

* feat: improve UI loading time and jitter

* feat: show credential sharing inline in the sidebar like host sharing

* feat: tailscale add host and copy ip, fix outbound fetch hang and plugin ESM warning

* feat: unify alerts into one plugin, channels, rules and inbox

* feat: rebuild desktop sync as linked server accounts, fix packaged plugin bundles and UI scaling

* fix: set blacksmith builder cache-key to stop broken gha cache fallback

* fix: link plugin-sdk workspace before npm ci in docker deps stage

* fix: link every plugin workspace before npm ci in docker deps stage

* fix(host-metrics): exclude image and firmware filesystems (#1469)

* fix(host-metrics): exclude image and firmware filesystems

* fix(ci): build plugin SDK before dependent checks

* fix(mysql): keep plugin settings scope indexes within limits

* test(mysql): refresh reviewed plugin upgrade SQL

* fix(hosts): normalize imported jump host references (#1470)

* fix(hosts): normalize imported jump host references

* fix(ci): build plugin SDK before dependent checks

* fix(mysql): keep plugin settings scope indexes within limits

* test(mysql): refresh reviewed plugin upgrade SQL

* fix(ci): build plugin SDK and repair MySQL settings migration (#1471)

* fix(ci): build plugin SDK before dependent checks

* fix(mysql): keep plugin settings scope indexes within limits

* test(mysql): refresh reviewed plugin upgrade SQL

* fix(file-manager): keep inline rename consistent (#1476)

* fix(ci): build plugin SDK before dependent checks

* fix(mysql): keep plugin settings scope indexes within limits

* test(mysql): refresh reviewed plugin upgrade SQL

* fix(file-manager): keep inline rename consistent

* fix(dashboard): reuse translated host status labels (#1475)

* fix(ci): build plugin SDK before dependent checks

* fix(mysql): keep plugin settings scope indexes within limits

* test(mysql): refresh reviewed plugin upgrade SQL

* fix(dashboard): reuse translated host status labels

* docs(api): clarify local and remote version fields (#1474)

* fix(ci): build plugin SDK before dependent checks

* fix(mysql): keep plugin settings scope indexes within limits

* test(mysql): refresh reviewed plugin upgrade SQL

* docs(api): clarify local and remote version fields

* fix(file-manager): preserve selected backgrounds on hover (#1473)

* fix(ci): build plugin SDK before dependent checks

* fix(mysql): keep plugin settings scope indexes within limits

* test(mysql): refresh reviewed plugin upgrade SQL

* fix(file-manager): preserve selection feedback on hover

* test(ui): await dialog focus cleanup before teardown

* fix(file-manager): restore Ctrl/Cmd+F file search (#1472)

* fix(ci): build plugin SDK before dependent checks

* fix(mysql): keep plugin settings scope indexes within limits

* test(mysql): refresh reviewed plugin upgrade SQL

* fix(file-manager): focus file search with Ctrl or Cmd F

* fix: keep rounded-full circular in production builds

* fix(file-manager): cancel uploads and stop remote writes (#1477)

* fix(file-manager): cancel uploads and stop remote writes

* test(css): exercise the configured PostCSS plugin chain

* fix(file-manager): apply compact density to directory tree (#1478)

* fix(file-manager): apply compact density to directory tree

* test(css): exercise the configured PostCSS plugin chain

* fix(file-manager): preserve font sizes in compact mode (#1480)

* fix(file-manager): preserve font sizes in compact mode

* test(css): exercise the configured PostCSS plugin chain

* fix(file-manager): prioritize file names in narrow lists (#1483)

* fix(file-manager): prioritize file names in narrow lists

* test(css): exercise the configured PostCSS plugin chain

* test(identity): preserve full Ed25519 public keys in fixtures

* test(remote-desktop): always mutate ciphertext in tamper test

* fix(remote-desktop): probe the saved host guacd endpoint (#1481)

* fix(remote-desktop): probe the saved host guacd endpoint

* test(css): exercise the configured PostCSS plugin chain

* test(identity): preserve full Ed25519 public keys in fixtures

* fix(homepage): check allowlisted private service reachability (#1479)

* fix(homepage): check allowlisted private service reachability

* test(css): exercise the configured PostCSS plugin chain

* fix(remote-desktop): stop unsolicited clipboard read prompts (#1482)

* fix(remote-desktop): read clipboard only on explicit paste

* test(css): exercise the configured PostCSS plugin chain

* test(identity): preserve full Ed25519 public keys in fixtures

* test(remote-desktop): always mutate ciphertext in tamper test

* chore: simplify plugin readmes, changelogs and manifest descriptions

* fix: keep plugins loaded after signing in from the login screen

* fix(file-manager): accept OS file drops over existing rows (#1484)

* fix(file-manager): accept OS file drops over existing rows

* test(file-manager): target the file label in drop tests

* test(css): exercise the configured PostCSS plugin chain

* test(identity): preserve full Ed25519 public keys in fixtures

* test(remote-desktop): always mutate ciphertext in tamper test

* fix(file-manager): pipeline SFTP download reads (#1485)

* feat: add plugin sdk build to beta release

* fix: make link-store secret check immune to random base64 matches

* fix(ldap): preserve the directory-provided bind DN (#1486)

* fix: compute beta changes across dev branches and move the beta tag

* chore: untrack plugin sdk ARCHITECTURE.md

* chore: remove references to plugin sdk ARCHITECTURE.md

* feat: merge host editor terminal tabs into one owned by ssh-terminal

* feat: add descriptions to plugin host enable toggles

* fix(file-manager): show download progress in file viewers (#1487)

* fix(electron): signal backend readiness over IPC (#1488)

* fix(split-screen): allow layout creation without randomUUID (#1490)

* fix(split-screen): allow layout creation without randomUUID

* test(credentials): avoid flaky Ed25519 key generation

* fix(sidebar): remeasure visible rows after density changes (#1489)

* fix(sidebar): remeasure visible rows after density changes

* test(credentials): avoid flaky Ed25519 key generation

* feat(split-screen): redesign split screen with free-form panes and pane picker

* feat(sidebar): add host click behavior setting, default to always opening a new tab

* feat(snippets): redesign panel to match the host list

* fix(session-recording): stop session logs panel from refetching in a loop

* feat(snippets): bring back sharing, target hosts and drag reorder

* feat(ssh-terminal): add toolbar position, start state, display and fade host settings

* feat(ssh-terminal): add host setting to turn off password prompt auto-fill

* fix(ai): stop streams getting cut off and keep tool steps across messages

* feat: improve quick connect system using plugin api

* fix: proxmox guest sync, startup snippet sync, drop dead cloudflare ssh transport

* feat: move terminal settings, look and ssh tools into ssh-terminal plugin

* feat: move snippets, keybinding actions and macros out of core into plugins

* feat: store rdp, vnc and telnet logins in a generic per-protocol table

* feat: type the plugin host record, move plugin strings out of core and add lint checks

* feat: replace core homepage widget registry with generic extension points

* feat: plugins only see their own host settings in the browser

* feat: harden plugin api for separate repos, signed updates and generic external login

* fix: keep plugin css below core utilities and version plugin assets by their css

* fix: load and save plugin host settings in the editor and tighten settings row spacing

* feat: redesign user host defaults system

* fix(remote-desktop): release held keys on focus loss (#1498)

* fix(session-sharing): replace local presentation after takeover (#1500)

* fix(file-manager): refresh disk usage with directory listing (#1503)

* fix(ssh-terminal): preserve input order during local echo reconciliation (#1505)

* feat(remote-desktop): expose display zoom controls for RDP (#1514)

* fix(file-manager): restore text selection and copying in Markdown previews (#1518)

* fix(sidebar): keep compact host addresses visible (#1524)

* fix(hosts): preserve plugin settings when reopening the editor (#1499)

* fix(ssh-terminal): close owner sockets when presentations unmount (#1501)

* fix(session-sharing): synchronize shared SSH terminal dimensions (#1502)

* fix(session-sharing): show participant presence in member terminals (#1504)

* fix(ai): sync provider configuration and keys across linked devices (#1506)

* feat(ssh-terminal): add a duplicate tab action (#1510)

* feat(file-manager): allow disabling move-to-trash confirmation (#1511)

* feat(file-manager): default to the remote login directory (#1512)

* feat(homepage): synchronize canvas layouts across devices (#1515)

* fix(shortcuts): prevent accidental double-Shift palette activation (#1519)

* feat(tmux-monitor): add a unified multi-host view (#1520)

* fix(electron): allow explicit quit with active connections (#1522)

* feat: reconnect all disconnected terminal tabs (#1523)

* fix(hosts): preserve recording and sharing settings in the dev editor (#1509)

* fix(database): preserve RDP domain during startup backfill (#1497)

* feat(sidebar): add keyboard type-ahead search for hosts (#1516)

* feat(automations): add per-host maintenance mode and recurring schedules (#1521)

* feat(tmux): add a per-host mouse toggle and preserve remote configuration (#1513)

* fix(session-sharing): use the linked server for desktop meetings and links (#1507)

* fix(tunnels): support TOTP for client tunnels and jump hosts (#1508)

* fix: type errors and lint failures from merged plugin prs

* feat(installer): make the Windows desktop shortcut optional (#1517)

* fix: ship the plugin sdk and serialport in desktop builds

* fix: prefer ipv4 when resolving ssh host names (#1365)

* fix: readable terminal selection with the light theme (#1319)

* fix: build desktop session share links on the linked server (#1350)

* fix: show meeting guests to members and hide the roster from guests (#1353)

* fix: reliable file manager saves with sudo prompt and no base64 guessing (#1335)

* fix: show disk usage for the mount being browsed (#1347)

* fix: load the linux tray icon reliably and log tray failures (#1332)

* fix: cover the gnome close path in the desktop quit test (#1332)

* fix: prompt for jump host TOTP in terminal, file manager and metrics (#1312)

* feat: optional auto reconnect for dropped ssh sessions (#1321)

* feat: tighter compact host rows with inline tags (#1325)

* feat: bring back the alert feed homepage widget

* feat: pick target terminals for snippets again

* feat: share button on connection tabs again

* feat: recent servers list when linking the desktop app

* feat: list the fixed app shortcuts in keyboard shortcuts

* fix: translate hardcoded toasts and drop em dashes from ui text

* fix: translate leftover hardcoded ui text

* fix: toolbar files button opens at the shell directory again

* fix: show the remote desktop note under connection origin again

* chore: openapi docs for host metrics manager routes

* chore: openapi docs for undocumented routes and fix two broken doc blocks

* fix: declare terminal.listSessions use for snippet targets

* revert: drop the alert feed widget, the notification center replaces it

* feat: restore the alert feed homepage widget

* fix: quick connect fills the host address from plugin auth editors like tailscale

* fix: docker container logs use the base background color

* chore: fix dependabot alerts and apply safe npm upgrades

* fix: proxmox stats tab reads its switch from plugin host settings

* fix: count plugin ssh logins toward host status and stop showing reachable as a warning

* fix: hide the built-in source pill in the host editor

* fix: host status shows online when the port answers and only warns on a failed login

* fix: drop the top border on the split layout menu

* fix: host defaults tabs sit where the host editor puts them and skip plugins with nothing to default

* fix: remote desktop auth method labels and notes for vnc and telnet

* fix: add description to the web endpoint enable switch

* fix: crowdin sync and pretranslate for plugin locales, run plugin tests on release

* feat: move telemetry out of core into a usage statistics plugin

* fix: recent activity card no longer cuts off host names early

* fix: remove duplicate top border on split screen menu

* fix: redesign session log player controls, fix tooltip overlap and drop dot separators

* fix: file manager and sftp text showing raw i18n keys

* fix: onboarding rerunning for existing users after upgrading

* fix: fleet detail buttons overflowing the sidebar

* fix: session log player scrolling off screen horizontally

* fix: network graph lag when adding hosts and on every render

* fix: maintenance index name, shared host read only view and badge polling

* fix: meeting tabs, workspace tab merging, rail panels as tabs and general ui lag

* chore: update release notes

* fix: remote desktop tabs reconnecting on every tab switch

* fix: build plugin sdk before linting in release workflow

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

* chore: skip crowdin pre-translate in release for now

* chore: sync Crowdin translations for 2.9.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Koi <38512047+datlt4@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: ZacharyZcR <zacharyzcr1984@gmail.com>
This commit is contained in:
authored and GitHub committed 2026-10-01 16:20:16 -05:00
1 parent 49fa488093
commit 3643e7af97
4405 files changed
+1341026 -289722

No files matched your search

+7
View File
@@ -0,0 +1,7 @@
# Changelog
## 1.0.0
First release, bundled with Termix 2.9.0.
Adds RDP, VNC and Telnet sessions in the browser through guacd. Supports clipboard, file transfer through RDP drive redirection, session recording and session sharing.
+68
View File
@@ -0,0 +1,68 @@
# Remote Desktop
RDP, VNC and Telnet sessions in the browser, with clipboard, file transfer, recording and sharing.
## Features
- Connect to hosts over RDP, VNC or Telnet from any browser.
- Clipboard sync and file transfer through RDP drive redirection.
- Session recording and session sharing, when those plugins are on.
- Jump host support.
- Opens the Windows Remote Desktop client from the desktop app.
## Setup
This plugin needs guacd, the Apache Guacamole proxy that speaks RDP, VNC and Telnet. The Docker compose file runs it as its own `guacamole/guacd` container. This plugin never starts guacd itself.
Set where guacd runs with the guacd URL admin setting. The `GUACD_URL`, or `GUACD_HOST` and `GUACD_PORT`, environment variables override that setting.
Other environment variables:
| Variable | What it sets |
| ------------------------------ | --------------------------------------------------------------------------------- |
| `GUACD_TUNNEL_HOST` | The name guacd uses to reach Termix for a jump host tunnel. Defaults to `termix`. |
| `GUACD_RECORDING_PATH` | Where guacd writes recordings, as guacd sees it. |
| `GUACD_RECORDING_BACKEND_PATH` | The same folder, as Termix sees it. |
| `GUACD_DRIVE_PATH` | The root of each user's RDP drive folder on the guacd host. |
| `GUACAMOLE_ENCRYPTION_KEY` | The connection token key. Made from `JWT_SECRET` when not set. |
## Settings
### Admin
- Enable Remote Desktop: turn remote desktop on or off.
- guacd URL: where guacd runs, as `host:port`. Changes apply without a restart.
### User
- RDP defaults: color depth, resize method, wallpaper, font smoothing, audio, printing, drive redirection and clipboard. A host's own values win over these.
### Host
- RDP, VNC and Telnet: turn each protocol on and set its port.
- RDP security mode and Ignore certificate errors.
- guacd settings: every other guacd option.
- Remote desktop toolbar: show the toolbar in the session.
## Permissions
- `remote-desktop.sessions`: Let session sharing and recording reach your remote desktop sessions. Admins and users have it by default.
## Services
Provides to other plugins:
- `sessions.live` as `rdp`, `vnc` and `telnet`: find a live session so it can be shared.
Uses from other plugins:
- `recordings.writer` to record sessions. Optional.
- `tunnels.access` for single-hop jump host tunnels. Optional.
## Development
```bash
npm run build # build into dist/
npm run test # run this plugin's tests
npm run typecheck # type-check this plugin
```
+350
View File
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Connecting to {{type}} session...",
"checkingGuacd": "Checking remote desktop service...",
"requestingToken": "Requesting {{type}} connection token...",
"openingSession": "Opening {{type}} session...",
"negotiating": "Negotiating {{type}} handshake...",
"sessionReady": "{{type}} session ready",
"connectionError": "Connection error",
"connectionFailed": "Connection failed",
"failedToConnect": "Failed to get connection token",
"hostNotFound": "Host not found",
"noHostSelected": "No host selected",
"reconnect": "Reconnect",
"retry": "Retry",
"guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.",
"credentialPromptTitle": "Enter RDP Credentials",
"credentialPromptDescription": "This host is set to prompt for credentials on connect. They are used for this session only and are not saved.",
"connect": "Connect",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Drive Files",
"parent": "Parent directory",
"refresh": "Refresh",
"empty": "This folder is empty",
"listFailed": "Failed to read this folder",
"download": "Download",
"downloadFailed": "Failed to download {{name}}",
"upload": "Upload files",
"uploading": "Uploading {{name}}...",
"uploaded": "Uploaded {{name}}",
"uploadFailed": "Failed to upload {{name}}",
"uploadDisabled": "Uploads are disabled for this connection",
"driveUnavailable": "Enable RDP drive redirection before uploading files",
"driveNotWritable": "guacd cannot write to the drive folder. Check GUACD_DRIVE_PATH (or the host's Drive Path) and its permissions.",
"dropToUpload": "Drop files to upload"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Lock Screen)",
"winKey": "Windows Key",
"superL": "Super+L (Lock Screen)",
"superKey": "Super Key",
"super": "Super",
"cmdL": "Cmd+L (Lock Screen)",
"cmdKey": "Command Key",
"cmd": "Cmd",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Shift",
"win": "Win",
"stickyActive": "{{key}} (latched - click to release)",
"stickyInactive": "{{key}} (click to latch)",
"esc": "Escape",
"tab": "Tab",
"home": "Home",
"end": "End",
"pageUp": "Page Up",
"pageDown": "Page Down",
"arrowUp": "Arrow Up",
"arrowDown": "Arrow Down",
"arrowLeft": "Arrow Left",
"arrowRight": "Arrow Right",
"fnToggle": "Function Keys",
"reconnect": "Reconnect Session",
"collapse": "Collapse toolbar",
"expand": "Expand toolbar",
"dragHandle": "Drag to reposition",
"hide": "Hide for this session. To disable it permanently, open Host Settings → General → Connection Toolbar.",
"zoomIn": "Zoom in",
"zoomOut": "Zoom out",
"resetZoom": "Reset zoom",
"fitToScreen": "Fit to screen",
"switchToTrackpad": "Switch to trackpad mode (drag to move cursor, tap to click)",
"switchToTouch": "Switch to touch mode (tap directly where you want to click)"
},
"disabled": "Remote Desktop is turned off. An admin can turn it on in Settings."
},
"hosts": {
"nativeRdpOpened": "Opened in Windows Remote Desktop",
"nativeRdpFailed": "Failed to open Windows Remote Desktop",
"openNativeRdp": "Open in Windows Remote Desktop",
"guac": {
"toolbar": "Toolbar",
"connection": "Connection",
"authentication": "Authentication",
"storedCredential": "Stored Credential",
"authMethod": "Auth Method",
"authTypeDirect": "Direct",
"authTypeCredential": "Credential",
"authTypeNone": "None",
"authTypeNoneDesc": "No credentials are stored. You'll be prompted for a username and password each time you connect; they are not saved.",
"selectCredential": "Select a credential...",
"connectionSettings": "Connection Settings",
"displaySettings": "Display Settings",
"audioSettings": "Audio Settings",
"rdpPerformance": "RDP Performance",
"deviceRedirection": "Device Redirection",
"session": "Session",
"gateway": "Gateway",
"remoteApp": "RemoteApp",
"clipboard": "Clipboard",
"sessionRecording": "Session Recording",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "VNC Settings",
"terminalSettings": "Terminal Settings",
"rdpPort": "RDP Port",
"username": "Username",
"password": "Password",
"passwordSaved": "Password saved, type to change",
"domain": "Domain",
"securityMode": "Security Mode",
"width": "Width",
"height": "Height",
"dpi": "DPI",
"clientName": "Client Name",
"initialProgram": "Initial Program",
"serverLayout": "Server Layout",
"timezone": "Timezone",
"loadBalanceInfo": "Load Balance Info / Cookie",
"loadBalanceInfoDesc": "RD Connection Broker cookie for RDS farm load balancing (e.g. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacd Proxy",
"guacdHostname": "guacd Host",
"guacdHostnamePlaceholder": "Global default",
"guacdPort": "guacd Port",
"guacdProxyDesc": "Override the global guacd instance for this connection. Leave blank to use the globally configured guacd.",
"gatewayHostname": "Gateway Hostname",
"gatewayPort": "Gateway Port",
"gatewayUsername": "Gateway Username",
"gatewayPassword": "Gateway Password",
"gatewayDomain": "Gateway Domain",
"remoteAppProgram": "RemoteApp Program",
"workingDirectory": "Working Directory",
"arguments": "Arguments",
"normalizeLineEndings": "Normalize Line Endings",
"macAddress": "MAC Address",
"broadcastAddress": "Broadcast Address",
"udpPort": "UDP Port",
"waitTimeS": "Wait Time (s)",
"driveName": "Drive Name",
"drivePath": "Drive Path",
"ignoreCertificate": "Ignore Certificate",
"ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates",
"forceLossless": "Force Lossless",
"forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)",
"disableAudio": "Disable Audio",
"disableAudioDesc": "Mute all audio from the remote session",
"enableAudioInput": "Enable Audio Input (Microphone)",
"enableAudioInputDesc": "Forward local microphone to the remote session",
"wallpaper": "Wallpaper",
"wallpaperDesc": "Show desktop wallpaper (disabling improves performance)",
"theming": "Theming",
"themingDesc": "Enable visual themes and styles",
"fontSmoothing": "Font Smoothing",
"fontSmoothingDesc": "Enable ClearType font rendering",
"fullWindowDrag": "Full Window Drag",
"fullWindowDragDesc": "Show window contents while dragging",
"desktopComposition": "Desktop Composition",
"desktopCompositionDesc": "Enable Aero glass effects",
"menuAnimations": "Menu Animations",
"menuAnimationsDesc": "Enable menu fade and slide animations",
"disableBitmapCaching": "Disable Bitmap Caching",
"disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)",
"disableOffscreenCaching": "Disable Offscreen Caching",
"disableOffscreenCachingDesc": "Turn off offscreen cache",
"disableGlyphCaching": "Disable Glyph Caching",
"disableGlyphCachingDesc": "Turn off glyph cache",
"enableGfx": "Enable GFX",
"enableGfxDesc": "Use RemoteFX graphics pipeline",
"enablePrinting": "Enable Printing",
"enablePrintingDesc": "Redirect local printers to the remote session",
"enableDriveRedirection": "Enable Drive Redirection",
"enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session",
"createDrivePath": "Create Drive Path",
"createDrivePathDesc": "Automatically create the folder if it does not exist",
"disableDownload": "Disable Download",
"disableDownloadDesc": "Prevent downloading files from the remote session",
"disableUpload": "Disable Upload",
"disableUploadDesc": "Prevent uploading files to the remote session",
"enableTouch": "Enable Touch",
"enableTouchDesc": "Enable touch input forwarding",
"consoleSession": "Console Session",
"consoleSessionDesc": "Connect to the console (session 0) instead of a new session",
"sendWolPacket": "Send WOL Packet",
"sendWolPacketDesc": "Send a magic packet to wake this host before connecting",
"disableCopy": "Disable Copy",
"disableCopyDesc": "Prevent copying text from the remote session",
"disablePaste": "Disable Paste",
"disablePasteDesc": "Prevent pasting text into the remote session",
"createPathIfMissing": "Create Path if Missing",
"createPathIfMissingDesc": "Automatically create the recording directory",
"excludeOutput": "Exclude Output",
"excludeOutputDesc": "Do not record screen output (metadata only)",
"excludeMouse": "Exclude Mouse",
"excludeMouseDesc": "Do not record mouse movements",
"includeKeystrokes": "Include Keystrokes",
"includeKeystrokesDesc": "Record raw keystrokes in addition to screen output",
"vncPort": "VNC Port",
"vncPassword": "VNC Password",
"vncUsernameOptional": "Username (optional)",
"vncLeaveBlank": "Leave blank if not required",
"cursorMode": "Cursor Mode",
"swapRedBlue": "Swap Red/Blue",
"swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)",
"readOnly": "Read-only",
"readOnlyDesc": "View the remote screen without sending any input",
"telnetPort": "Telnet Port",
"terminalType": "Terminal Type",
"fontName": "Font Name",
"fontSize": "Font Size",
"colorScheme": "Color Scheme",
"backspaceKey": "Backspace Key",
"colorDepth": "Color Depth",
"resizeMethod": "Resize Method",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "Remote Desktop",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Virtual Network",
"unencryptedShell": "Unencrypted shell",
"remoteDesktopNotes": "Notes",
"userDefaultsNote": "Settings you leave unchanged follow your host defaults.",
"connectionOriginNote": "On the desktop app, RDP, VNC and Telnet use the remote server unless the host is set to This device, which needs a guacd this machine can reach. Set one in the Remote Desktop settings, or per host under guacd Proxy.",
"docsLink": "View docs",
"connectionOriginGuacamoleNote": "RDP, VNC and Telnet use the remote server unless you pick \"This device\", which needs a guacd this machine can reach. Set one in the guacd URL setting, or per host under guacd Proxy."
},
"onboarding": {
"feature_desktop": "Remote desktop",
"feature_desktop_desc": "Open RDP, VNC and Telnet sessions in a tab, same as SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Color Depth",
"description": "Used for RDP hosts that leave it on their default."
},
"resizeMethod": {
"label": "Resize Method",
"description": "How the remote screen follows the window size."
},
"resizeDisplayUpdate": "Display Update",
"resizeReconnect": "Reconnect",
"inherit": "Inherit",
"on": "On",
"off": "Off",
"forceLossless": {
"label": "Force Lossless",
"description": "Force lossless image encoding (higher quality, more bandwidth)"
},
"enableWallpaper": {
"label": "Wallpaper",
"description": "Show desktop wallpaper (disabling improves performance)"
},
"enableFontSmoothing": {
"label": "Font Smoothing",
"description": "Enable ClearType font rendering"
},
"enableDesktopComposition": {
"label": "Desktop Composition",
"description": "Enable Aero glass effects"
},
"disableAudio": {
"label": "Disable Audio",
"description": "Mute all audio from the remote session"
},
"enablePrinting": {
"label": "Enable Printing",
"description": "Redirect local printers to the remote session"
},
"enableDrive": {
"label": "Enable Drive Redirection",
"description": "Map a local folder as a drive in the remote session"
},
"disableCopy": {
"label": "Disable Copy",
"description": "Prevent copying text from the remote session"
},
"disablePaste": {
"label": "Disable Paste",
"description": "Prevent pasting text into the remote session"
},
"group": "Remote Desktop defaults",
"colorDepth16": "16-bit",
"colorDepth24": "24-bit",
"colorDepth32": "32-bit"
},
"admin": {
"enabled": {
"label": "Enable Remote Desktop",
"description": "Allow RDP, VNC and Telnet connections through guacd."
},
"guacdUrl": {
"label": "guacd URL",
"description": "Where guacd listens, as host:port. The GUACD_URL, GUACD_HOST and GUACD_PORT environment variables override this.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP port"
},
"vncPort": {
"label": "VNC port"
},
"telnetPort": {
"label": "Telnet port"
},
"rdpSecurity": {
"label": "RDP security mode"
},
"rdpIgnoreCert": {
"label": "Ignore certificate errors"
},
"guacamoleConfig": {
"label": "guacd settings"
},
"enableToolbar": {
"label": "Remote desktop toolbar",
"description": "Show the toolbar in RDP, VNC and Telnet sessions."
},
"displayGroup": "Display",
"displayDefaultsTab": "Remote Desktop"
}
},
"palette": {
"connectRdp": "Connect with RDP",
"connectVnc": "Connect with VNC",
"connectTelnet": "Connect with Telnet"
},
"permissions": {
"sessions": {
"title": "Share remote desktop sessions",
"description": "Let session sharing and collab rooms show this user's RDP, VNC and Telnet sessions to others."
}
},
"errors": {
"remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Verbind met {{type}} sessie...",
"checkingGuacd": "Kontroleer tans afstandwerkskermdiens...",
"requestingToken": "Versoek tans {{type}} verbindingstoken...",
"openingSession": "Maak {{type}} sessie oop...",
"negotiating": "Onderhandel {{type}} handdruk...",
"sessionReady": "{{type}} sessie gereed",
"connectionError": "Verbindingsfout",
"connectionFailed": "Verbinding het misluk",
"failedToConnect": "Kon nie verbindingstoken kry nie",
"hostNotFound": "Gasheer nie gevind nie",
"noHostSelected": "Geen gasheer gekies nie",
"reconnect": "Herkoppel",
"retry": "Probeer weer",
"guacdUnavailable": "Diens vir afstandwerkskerm (guacd) is nie beskikbaar nie. Maak asseblief seker dat guacd loop, toeganklik is en behoorlik in administrateurinstellings gekonfigureer is.",
"credentialPromptTitle": "Voer RDP-aanbewyse in",
"credentialPromptDescription": "Hierdie gasheer is ingestel om vir geloofsbriewe te vra tydens verbinding. Hulle word slegs vir hierdie sessie gebruik en word nie gestoor nie.",
"connect": "Verbind",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Drive-lêers",
"parent": "Ouergids",
"refresh": "Verfris",
"empty": "Hierdie vouer is leeg",
"listFailed": "Kon nie hierdie vouer lees nie",
"download": "Laai af",
"downloadFailed": "Kon nie {{name}} aflaai nie",
"upload": "Laai lêers op",
"uploading": "Laai tans {{name}} op ...",
"uploaded": "Opgelaai {{name}}",
"uploadFailed": "Kon nie {{name}} oplaai nie",
"uploadDisabled": "Oplaaie is gedeaktiveer vir hierdie verbinding",
"driveUnavailable": "Aktiveer RDP-skyfherleiding voordat lêers opgelaai word",
"driveNotWritable": "guacd kan nie na die skyf se lêergids skryf nie. Kontroleer GUACD_DRIVE_PATH (of die gasheer se skyfpad) en sy toestemmings.",
"dropToUpload": "Laat lêers los om op te laai"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Sluitskerm)",
"winKey": "Windows-sleutel",
"superL": "Super+L (Sluitskerm)",
"superKey": "Supersleutel",
"super": "Super",
"cmdL": "Cmd+L (Sluitskerm)",
"cmdKey": "Opdragsleutel",
"cmd": "Cmd",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Skof",
"win": "Wen",
"stickyActive": "{{key}} (vasgeklem - klik om los te maak)",
"stickyInactive": "{{key}} (klik om te sluit)",
"esc": "Ontsnapping",
"tab": "Oortjie",
"home": "Tuis",
"end": "Einde",
"pageUp": "Bladsy op",
"pageDown": "Bladsy af",
"arrowUp": "Pyltjie Op",
"arrowDown": "Pyltjie Af",
"arrowLeft": "Pyltjie links",
"arrowRight": "Pyltjie Regs",
"fnToggle": "Funksiesleutels",
"reconnect": "Herkoppel Sessie",
"collapse": "Vou nutsbalk in",
"expand": "Vou die nutsbalk uit",
"dragHandle": "Sleep om te herposisioneer",
"hide": "Versteek vir hierdie sessie. Om dit permanent te deaktiveer, maak Gasheerinstellings → Algemeen → Verbindingsnutsbalk oop.",
"zoomIn": "Zoem in",
"zoomOut": "Zoem uit",
"resetZoom": "Stel zoom terug",
"fitToScreen": "Pas by skerm",
"switchToTrackpad": "Skakel oor na raakvlakmodus (sleep om wyser te beweeg, tik om te klik)",
"switchToTouch": "Skakel oor na raakmodus (tik direk waar jy wil klik)"
},
"disabled": "Afstandslessenaar is afgeskakel. 'n Administrateur kan dit in Instellings aanskakel."
},
"hosts": {
"nativeRdpOpened": "Geopen in Windows Afstandslessenaar",
"nativeRdpFailed": "Kon nie Windows Remote Desktop oopmaak nie",
"openNativeRdp": "Maak oop in Windows Afstandslessenaar",
"guac": {
"toolbar": "Nutsbalk",
"connection": "Verbinding",
"authentication": "Verifikasie",
"storedCredential": "Gestoorde geloofsbriewe",
"authMethod": "Magtigingsmetode",
"authTypeDirect": "Direkte",
"authTypeCredential": "Geloofsbrief",
"authTypeNone": "Geen",
"authTypeNoneDesc": "Geen aanmeldingsbesonderhede word gestoor nie. Jy sal elke keer as jy koppel vir 'n gebruikersnaam en wagwoord gevra word; hulle word nie gestoor nie.",
"selectCredential": "Kies 'n geloofsbrief...",
"connectionSettings": "Verbindingsinstellings",
"displaySettings": "Skerminstellings",
"audioSettings": "Oudio-instellings",
"rdpPerformance": "RDP-prestasie",
"deviceRedirection": "Toestelherleiding",
"session": "Sessie",
"gateway": "Poort",
"remoteApp": "Afstandsprogram",
"clipboard": "Klembord",
"sessionRecording": "Sessie-opname",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "VNC-instellings",
"terminalSettings": "Terminaalinstellings",
"rdpPort": "RDP-poort",
"username": "Gebruikersnaam",
"password": "Wagwoord",
"passwordSaved": "Wagwoord gestoor, tik om te verander",
"domain": "Domein",
"securityMode": "Sekuriteitsmodus",
"width": "Breedte",
"height": "Hoogte",
"dpi": "DPI",
"clientName": "Kliëntnaam",
"initialProgram": "Aanvanklike Program",
"serverLayout": "Bedieneruitleg",
"timezone": "Tydsone",
"loadBalanceInfo": "Laaibalansinligting / Koekie",
"loadBalanceInfoDesc": "RD Connection Broker-koekie vir RDS-plaaslasbalansering (bv. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacd Proxy",
"guacdHostname": "guacd-gasheer",
"guacdHostnamePlaceholder": "Globale verstekwaarde",
"guacdPort": "guacd-hawe",
"guacdProxyDesc": "Ignoreer die globale guacd-instansie vir hierdie verbinding. Los leeg om die globaal gekonfigureerde guacd te gebruik.",
"gatewayHostname": "Gateway-gasheernaam",
"gatewayPort": "Gateway-poort",
"gatewayUsername": "Gateway-gebruikersnaam",
"gatewayPassword": "Gateway-wagwoord",
"gatewayDomain": "Gateway-domein",
"remoteAppProgram": "RemoteApp-program",
"workingDirectory": "Werkgids",
"arguments": "Argumente",
"normalizeLineEndings": "Normaliseer Lyn Eindigings",
"macAddress": "MAC-adres",
"broadcastAddress": "Uitsaaiadres",
"udpPort": "UDP-poort",
"waitTimeS": "Wagtyd (s)",
"driveName": "Skyfnaam",
"drivePath": "Rypad",
"ignoreCertificate": "Ignoreer Sertifikaat",
"ignoreCertificateDesc": "Laat verbindings na gashere met selfgetekende sertifikate toe",
"forceLossless": "Forseer verliesloos",
"forceLosslessDesc": "Forseer verlieslose beeldkodering (hoër gehalte, meer bandwydte)",
"disableAudio": "Deaktiveer oudio",
"disableAudioDesc": "Demp alle klank van die afstandsessie",
"enableAudioInput": "Aktiveer oudio-invoer (mikrofoon)",
"enableAudioInputDesc": "Stuur plaaslike mikrofoon na die afgeleë sessie aan",
"wallpaper": "Agtergrond",
"wallpaperDesc": "Wys lessenaar agtergrond (deaktivering verbeter werkverrigting)",
"theming": "Temavorming",
"themingDesc": "Aktiveer visuele temas en style",
"fontSmoothing": "Lettertipe-gladmaak",
"fontSmoothingDesc": "Aktiveer ClearType-lettertipe-weergawe",
"fullWindowDrag": "Volle Venster Sleep",
"fullWindowDragDesc": "Wys vensterinhoud terwyl jy sleep",
"desktopComposition": "Komposisie op die lessenaar",
"desktopCompositionDesc": "Aktiveer Aero-glaseffekte",
"menuAnimations": "Menu Animasies",
"menuAnimationsDesc": "Aktiveer menu-vervaag- en skuifanimasies",
"disableBitmapCaching": "Deaktiveer Bitmap-kasgeheue",
"disableBitmapCachingDesc": "Skakel bitmap-kasgeheue af (kan help met foute)",
"disableOffscreenCaching": "Deaktiveer buiteskerm-kasgeheue",
"disableOffscreenCachingDesc": "Skakel buiteskerm-kasgeheue af",
"disableGlyphCaching": "Deaktiveer glifkasgeheue",
"disableGlyphCachingDesc": "Skakel glifkasgeheue af",
"enableGfx": "Aktiveer GFX",
"enableGfxDesc": "Gebruik RemoteFX-grafikapyplyn",
"enablePrinting": "Aktiveer drukwerk",
"enablePrintingDesc": "Herlei plaaslike drukkers na die afgeleë sessie",
"enableDriveRedirection": "Aktiveer dryfherleiding",
"enableDriveRedirectionDesc": "Karaktereer 'n plaaslike vouer as 'n skyf in die afstandsessie",
"createDrivePath": "Skep 'n rylaanpad",
"createDrivePathDesc": "Skep die lêer outomaties as dit nie bestaan nie",
"disableDownload": "Deaktiveer aflaai",
"disableDownloadDesc": "Voorkom die aflaai van lêers vanaf die afgeleë sessie",
"disableUpload": "Deaktiveer oplaai",
"disableUploadDesc": "Voorkom die oplaai van lêers na die afgeleë sessie",
"enableTouch": "Aktiveer Aanraking",
"enableTouchDesc": "Aktiveer aanstuur van raakinvoer",
"consoleSession": "Konsolesessie",
"consoleSessionDesc": "Koppel aan die konsole (sessie 0) in plaas van 'n nuwe sessie",
"sendWolPacket": "Stuur WOL-pakket",
"sendWolPacketDesc": "Stuur 'n magiese pakket om hierdie gasheer wakker te maak voordat jy verbind",
"disableCopy": "Deaktiveer Kopieer",
"disableCopyDesc": "Voorkom die kopiëring van teks vanaf die afgeleë sessie",
"disablePaste": "Deaktiveer plak",
"disablePasteDesc": "Voorkom die plak van teks in die afgeleë sessie",
"createPathIfMissing": "Skep pad indien ontbreek",
"createPathIfMissingDesc": "Skep outomaties die opnamegids",
"excludeOutput": "Sluit Uitvoer Uit",
"excludeOutputDesc": "Moenie skermuitvoer opneem nie (slegs metadata)",
"excludeMouse": "Sluit Muis uit",
"excludeMouseDesc": "Moenie muisbewegings opneem nie",
"includeKeystrokes": "Sluit toetsaanslagen in",
"includeKeystrokesDesc": "Neem rou toetsaanslagen op benewens skermuitvoer",
"vncPort": "VNC-poort",
"vncPassword": "VNC-wagwoord",
"vncUsernameOptional": "Gebruikersnaam (opsioneel)",
"vncLeaveBlank": "Los leeg indien nie nodig nie",
"cursorMode": "Wysermodus",
"swapRedBlue": "Ruil Rooi/Blou",
"swapRedBlueDesc": "Ruil die rooi en blou kleurkanale om (maak sommige kleurprobleme reg)",
"readOnly": "Leesalleen",
"readOnlyDesc": "Bekyk die afstandskerm sonder om enige insette te stuur",
"telnetPort": "Telnet-poort",
"terminalType": "Terminaaltipe",
"fontName": "Lettertipe Naam",
"fontSize": "Lettergrootte",
"colorScheme": "Kleurskema",
"backspaceKey": "Terugspasie-sleutel",
"colorDepth": "Kleurdiepte",
"resizeMethod": "Vergrootingsmetode",
"driveNamePlaceholder": "Termix-rylaan"
},
"remoteDesktop": "Afstandslessenaar",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Virtuele Netwerk",
"unencryptedShell": "Ongeënkripteerde dop",
"remoteDesktopNotes": "Notas",
"userDefaultsNote": "Instellings wat jy onveranderd laat, volg jou gasheer se verstekwaardes.",
"connectionOriginNote": "Op die lessenaar-app gebruik RDP, VNC en Telnet die afstandbediener tensy die gasheer op Hierdie toestel gestel is, wat 'n guacd benodig wat hierdie masjien kan bereik. Stel een in die Afstandslessenaar-instellings, of per gasheer onder guacd Proxy.",
"docsLink": "Bekyk dokumente",
"connectionOriginGuacamoleNote": "RDP, VNC en Telnet gebruik die afstandbediener tensy jy \"Hierdie toestel\" kies, wat 'n guacd benodig wat hierdie masjien kan bereik. Stel een in die guacd URL-instelling, of per gasheer onder guacd Proxy."
},
"onboarding": {
"feature_desktop": "Afstandlessenaar",
"feature_desktop_desc": "Maak RDP-, VNC- en Telnet-sessies in 'n oortjie oop, dieselfde as SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Kleurdiepte",
"description": "Gebruik vir RDP-gashere wat dit op hul verstekwaarde laat."
},
"resizeMethod": {
"label": "Vergrootingsmetode",
"description": "Hoe die afstandskerm die venstergrootte volg."
},
"resizeDisplayUpdate": "Skermopdatering",
"resizeReconnect": "Herkoppel",
"inherit": "Erf",
"on": "Aan",
"off": "Af",
"forceLossless": {
"label": "Forseer verliesloos",
"description": "Forseer verlieslose beeldkodering (hoër gehalte, meer bandwydte)"
},
"enableWallpaper": {
"label": "Agtergrond",
"description": "Wys lessenaar agtergrond (deaktivering verbeter werkverrigting)"
},
"enableFontSmoothing": {
"label": "Lettertipe-gladmaak",
"description": "Aktiveer ClearType-lettertipe-weergawe"
},
"enableDesktopComposition": {
"label": "Komposisie op die lessenaar",
"description": "Aktiveer Aero-glaseffekte"
},
"disableAudio": {
"label": "Deaktiveer oudio",
"description": "Demp alle klank van die afstandsessie"
},
"enablePrinting": {
"label": "Aktiveer drukwerk",
"description": "Herlei plaaslike drukkers na die afgeleë sessie"
},
"enableDrive": {
"label": "Aktiveer dryfherleiding",
"description": "Karaktereer 'n plaaslike vouer as 'n skyf in die afstandsessie"
},
"disableCopy": {
"label": "Deaktiveer Kopieer",
"description": "Voorkom die kopiëring van teks vanaf die afgeleë sessie"
},
"disablePaste": {
"label": "Deaktiveer plak",
"description": "Voorkom die plak van teks in die afgeleë sessie"
},
"group": "Afstandslessenaar-verstekwaardes",
"colorDepth16": "16-bis",
"colorDepth24": "24-bis",
"colorDepth32": "32-bis"
},
"admin": {
"enabled": {
"label": "Aktiveer Afstandslessenaar",
"description": "Laat RDP-, VNC- en Telnet-verbindings deur guacd toe."
},
"guacdUrl": {
"label": "guacd-URL",
"description": "Waar guacd luister, as host:port. Die GUACD_URL, GUACD_HOST en GUACD_PORT omgewingveranderlikes oorskryf dit.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP-poort"
},
"vncPort": {
"label": "VNC-poort"
},
"telnetPort": {
"label": "Telnet-poort"
},
"rdpSecurity": {
"label": "RDP-sekuriteitsmodus"
},
"rdpIgnoreCert": {
"label": "Ignoreer sertifikaatfoute"
},
"guacamoleConfig": {
"label": "guacd-instellings"
},
"enableToolbar": {
"label": "Afstandswerkskerm-nutsbalk",
"description": "Wys die nutsbalk in RDP-, VNC- en Telnet-sessies."
},
"displayGroup": "Vertoon",
"displayDefaultsTab": "Afstandslessenaar"
}
},
"palette": {
"connectRdp": "Verbind met RDP",
"connectVnc": "Verbind met VNC",
"connectTelnet": "Verbind met Telnet"
},
"permissions": {
"sessions": {
"title": "Deel afstandlessenaarsessies",
"description": "Laat sessiedeling en samewerkingskamers hierdie gebruiker se RDP-, VNC- en Telnet-sessies aan ander wys."
}
},
"errors": {
"remoteServerRequired": "Afstandbediener word benodig. Koppel 'n afstandbediener in Instellings om hierdie verbindingstipe te gebruik."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "جارٍ الاتصال بجلسة {{type}}...",
"checkingGuacd": "جارٍ التحقق من خدمة سطح المكتب البعيد...",
"requestingToken": "طلب رمز اتصال {{type}}...",
"openingSession": "جارٍ فتح الجلسة {{type}}...",
"negotiating": "التفاوض على مصافحة {{type}}...",
"sessionReady": "{{type}} الجلسة جاهزة",
"connectionError": "خطأ في الاتصال",
"connectionFailed": "فشل الاتصال",
"failedToConnect": "فشل الحصول على رمز الاتصال",
"hostNotFound": "المضيف غير موجود",
"noHostSelected": "لم يتم تحديد أي جهاز مضيف",
"reconnect": "إعادة الاتصال",
"retry": "إعادة المحاولة",
"guacdUnavailable": "خدمة سطح المكتب البعيد (guacd) غير متاحة. يرجى التأكد من تشغيل guacd وإمكانية الوصول إليه وتكوينه بشكل صحيح في إعدادات المسؤول.",
"credentialPromptTitle": "أدخل بيانات اعتماد RDP",
"credentialPromptDescription": "تم ضبط هذا المضيف ليطلب بيانات الاعتماد عند الاتصال. تُستخدم هذه البيانات لهذه الجلسة فقط ولا يتم حفظها.",
"connect": "اتصال",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "ملفات محرك الأقراص",
"parent": "الدليل الرئيسي",
"refresh": "تحديث",
"empty": "هذا المجلد فارغ",
"listFailed": "فشل في قراءة هذا المجلد",
"download": "تنزيل",
"downloadFailed": "فشل تنزيل {{name}}",
"upload": "تحميل الملفات",
"uploading": "جارٍ رفع {{name}}...",
"uploaded": "تم التحميل {{name}}",
"uploadFailed": "فشل تحميل {{name}}",
"uploadDisabled": "تم تعطيل التحميلات لهذا الاتصال",
"driveUnavailable": "قم بتمكين إعادة توجيه محرك الأقراص RDP قبل تحميل الملفات",
"driveNotWritable": "لا يستطيع برنامج guacd الكتابة إلى مجلد القرص. تحقق من مسار محرك الأقراص GUACD_DRIVE_PATH (أو مسار محرك الأقراص الخاص بالجهاز المضيف) وصلاحياته.",
"dropToUpload": "قم بإسقاط الملفات لتحميلها"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (قفل الشاشة)",
"winKey": "مفتاح Windows",
"superL": "Super+L (شاشة القفل)",
"superKey": "مفتاح سوبر",
"super": "ممتاز",
"cmdL": "Cmd+L (قفل الشاشة)",
"cmdKey": "مفتاح الاختصار",
"cmd": "الأمر",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Shift",
"win": "Win",
"stickyActive": "{{key}} (مثبت - انقر لتحرير)",
"stickyInactive": "{{key}} (انقر للتثبيت)",
"esc": "Escape",
"tab": "Tab",
"home": "Home",
"end": "End",
"pageUp": "Page Up",
"pageDown": "Page Down",
"arrowUp": "Arrow Up",
"arrowDown": "Arrow Down",
"arrowLeft": "Arrow Left",
"arrowRight": "Arrow Right",
"fnToggle": "مفاتيح الوظائف",
"reconnect": "إعادة الاتصال بالجلسة",
"collapse": "طي شريط الأدوات",
"expand": "توسيع شريط الأدوات",
"dragHandle": "اسحب لتغيير الموضع",
"hide": "إخفاء هذه الميزة لهذه الجلسة. لتعطيلها نهائياً، افتح إعدادات المضيف ← عام ← شريط أدوات الاتصال.",
"zoomIn": "تكبير الصورة",
"zoomOut": "تصغير العرض",
"resetZoom": "إعادة ضبط التكبير",
"fitToScreen": "مناسب للشاشة",
"switchToTrackpad": "قم بالتبديل إلى وضع لوحة اللمس (اسحب لتحريك المؤشر، انقر للنقر)",
"switchToTouch": "قم بالتبديل إلى وضع اللمس (انقر مباشرة في المكان الذي تريد النقر فيه)"
},
"disabled": "تم إيقاف تشغيل سطح المكتب البعيد. يمكن للمسؤول تشغيله في الإعدادات."
},
"hosts": {
"nativeRdpOpened": "تم فتحه في سطح المكتب البعيد لنظام التشغيل Windows",
"nativeRdpFailed": "فشل فتح سطح المكتب البعيد لنظام التشغيل Windows",
"openNativeRdp": "افتح في سطح المكتب البعيد لنظام التشغيل Windows",
"guac": {
"toolbar": "شريط الأدوات",
"connection": "اتصال",
"authentication": "المصادقة",
"storedCredential": "بيانات الاعتماد المخزنة",
"authMethod": "طريقة المصادقة",
"authTypeDirect": "مباشر",
"authTypeCredential": "بيانات الاعتماد",
"authTypeNone": "لا شيء",
"authTypeNoneDesc": "لا يتم تخزين أي بيانات اعتماد. سيُطلب منك إدخال اسم المستخدم وكلمة المرور في كل مرة تتصل فيها؛ فهي غير محفوظة.",
"selectCredential": "اختر بيانات اعتماد...",
"connectionSettings": "إعدادات الاتصال",
"displaySettings": "إعدادات العرض",
"audioSettings": "إعدادات الصوت",
"rdpPerformance": "أداء RDP",
"deviceRedirection": "إعادة توجيه الأجهزة",
"session": "جلسة",
"gateway": "بوابة",
"remoteApp": "RemoteApp",
"clipboard": "الحافظة",
"sessionRecording": "تسجيل الجلسة",
"wakeOnLan": "الإيقاظ عبر الشبكة",
"vncSettings": "إعدادات VNC",
"terminalSettings": "إعدادات الطرفية",
"rdpPort": "منفذ RDP",
"username": "اسم المستخدم",
"password": "كلمة المرور",
"passwordSaved": "تم حفظ كلمة المرور، اكتب لتغييرها",
"domain": "مجال",
"securityMode": "وضع الأمان",
"width": "العرض",
"height": "الارتفاع",
"dpi": "DPI",
"clientName": "اسم العميل",
"initialProgram": "البرنامج الأولي",
"serverLayout": "تخطيط الخادم",
"timezone": "المنطقة الزمنية",
"loadBalanceInfo": "معلومات موازنة التحميل / ملف تعريف ارتباط",
"loadBalanceInfoDesc": "ملف تعريف ارتباط وسيط اتصال RD لموازنة تحميل مزرعة RDS (مثل: tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "وكيل guacd",
"guacdHostname": "مضيف guacd",
"guacdHostnamePlaceholder": "الافتراضي العام",
"guacdPort": "منفذ guacd",
"guacdProxyDesc": "تجاوز مثيل guacd العام لهذا الاتصال. اتركه فارغًا لاستخدام إعدادات guacd العمومية.",
"gatewayHostname": "اسم مضيف البوابة",
"gatewayPort": "منفذ البوابة",
"gatewayUsername": "اسم مستخدم البوابة",
"gatewayPassword": "كلمة مرور البوابة",
"gatewayDomain": "مجال البوابة",
"remoteAppProgram": "برنامج RemoteApp",
"workingDirectory": "دليل العمل",
"arguments": "الوسائط",
"normalizeLineEndings": "تطبيع نهايات الأسطر",
"macAddress": "عنوان MAC",
"broadcastAddress": "عنوان البث",
"udpPort": "منفذ UDP",
"waitTimeS": "وقت الانتظار (ث)",
"driveName": "اسم القرص",
"drivePath": "مسار القرص",
"ignoreCertificate": "تجاهل الشهادة",
"ignoreCertificateDesc": "السماح بالاتصال بالمضيفين بشهادات موقعة ذاتيًا",
"forceLossless": "فرض بدون فقدان",
"forceLosslessDesc": "فرض ترميز الصور بدون فقدان (جودة أعلى، استهلاك أكبر للنطاق الترددي)",
"disableAudio": "تعطيل الصوت",
"disableAudioDesc": "كتم كل الصوت من الجلسة البعيدة",
"enableAudioInput": "تمكين إدخال الصوت (الميكروفون)",
"enableAudioInputDesc": "إعادة توجيه الميكروفون المحلي إلى الجلسة البعيدة",
"wallpaper": "خلفية سطح المكتب",
"wallpaperDesc": "إظهار خلفية سطح المكتب (التعطيل يحسن الأداء)",
"theming": "السمات",
"themingDesc": "تمكين السمات والأنماط المرئية",
"fontSmoothing": "تنعيم الخطوط",
"fontSmoothingDesc": "تمكين عرض خطوط ClearType",
"fullWindowDrag": "سحب النافذة بالكامل",
"fullWindowDragDesc": "إظهار محتويات النافذة أثناء السحب",
"desktopComposition": "تركيب سطح المكتب",
"desktopCompositionDesc": "تمكين تأثيرات Aero الزجاجية",
"menuAnimations": "حركات القوائم",
"menuAnimationsDesc": "تمكين حركات التلاشي والانزلاق للقوائم",
"disableBitmapCaching": "تعطيل التخزين المؤقت للصور النقطية",
"disableBitmapCachingDesc": "إيقاف التخزين المؤقت للصور النقطية (قد يساعد في حل المشكلات)",
"disableOffscreenCaching": "تعطيل التخزين المؤقت خارج الشاشة",
"disableOffscreenCachingDesc": "إيقاف التخزين المؤقت خارج الشاشة",
"disableGlyphCaching": "تعطيل التخزين المؤقت للأشكال",
"disableGlyphCachingDesc": "إيقاف التخزين المؤقت للأشكال",
"enableGfx": "تمكين GFX",
"enableGfxDesc": "استخدام خط أنابيب رسوميات RemoteFX",
"enablePrinting": "تمكين الطباعة",
"enablePrintingDesc": "إعادة توجيه الطابعات المحلية إلى الجلسة البعيدة",
"enableDriveRedirection": "تمكين إعادة توجيه القرص",
"enableDriveRedirectionDesc": "تعيين مجلد محلي كقرص في الجلسة البعيدة",
"createDrivePath": "إنشاء مسار محرك الأقراص",
"createDrivePathDesc": "إنشاء المجلد تلقائياً إذا لم يكن موجوداً",
"disableDownload": "تعطيل التحميل",
"disableDownloadDesc": "منع تنزيل الملفات من الجلسة البعيدة",
"disableUpload": "تعطيل الرفع",
"disableUploadDesc": "منع رفع الملفات إلى الجلسة البعيدة",
"enableTouch": "تمكين اللمس",
"enableTouchDesc": "تمكين تمرير إدخالات اللمس",
"consoleSession": "جلسة وحدة التحكم",
"consoleSessionDesc": "الاتصال بوحدة التحكم (الجلسة 0) بدلاً من جلسة جديدة",
"sendWolPacket": "إرسال حزمة WOL",
"sendWolPacketDesc": "إرسال حزمة سحرية لإيقاظ هذا المضيف قبل الاتصال",
"disableCopy": "تعطيل النسخ",
"disableCopyDesc": "منع نسخ النص من الجلسة البعيدة",
"disablePaste": "تعطيل اللصق",
"disablePasteDesc": "منع لصق النص في الجلسة البعيدة",
"createPathIfMissing": "إنشاء المسار إذا كان مفقوداً",
"createPathIfMissingDesc": "إنشاء دليل التسجيل تلقائياً",
"excludeOutput": "استبعاد المخرجات",
"excludeOutputDesc": "عدم تسجيل مخرجات الشاشة (البيانات الوصفية فقط)",
"excludeMouse": "استبعاد الفأرة",
"excludeMouseDesc": "عدم تسجيل حركات الفأرة",
"includeKeystrokes": "تضمين ضغطات المفاتيح",
"includeKeystrokesDesc": "تسجيل ضغطات المفاتيح الخام بالإضافة إلى مخرجات الشاشة",
"vncPort": "منفذ VNC",
"vncPassword": "كلمة مرور VNC",
"vncUsernameOptional": "اسم المستخدم (اختياري)",
"vncLeaveBlank": "اتركه فارغاً إذا لم يكن مطلوباً",
"cursorMode": "وضع المؤشر",
"swapRedBlue": "تبديل الأحمر/الأزرق",
"swapRedBlueDesc": "تبديل قنوات اللون الأحمر والأزرق (يصلح بعض مشكلات الألوان)",
"readOnly": "للقراءة فقط",
"readOnlyDesc": "عرض الشاشة البعيدة دون إرسال أي إدخال",
"telnetPort": "منفذ Telnet",
"terminalType": "نوع الطرفية",
"fontName": "اسم الخط",
"fontSize": "حجم الخط",
"colorScheme": "نظام الألوان",
"backspaceKey": "مفتاح Backspace",
"colorDepth": "عمق اللون",
"resizeMethod": "طريقة تغيير الحجم",
"driveNamePlaceholder": "محرك Termix"
},
"remoteDesktop": "سطح المكتب البعيد",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "الشبكة الافتراضية",
"unencryptedShell": "shell غير مشفر",
"remoteDesktopNotes": "ملاحظات",
"userDefaultsNote": "تتبع الإعدادات التي تتركها دون تغيير الإعدادات الافتراضية للمضيف.",
"connectionOriginNote": "في تطبيق سطح المكتب، تستخدم بروتوكولات RDP وVNC وTelnet الخادم البعيد ما لم يتم ضبط المضيف على \"هذا الجهاز\"، الأمر الذي يتطلب عنوان IP يمكن لهذا الجهاز الوصول إليه. يمكنك تعيين عنوان IP في إعدادات سطح المكتب البعيد، أو لكل مضيف ضمن \"وكيل guacd\".",
"docsLink": "عرض المستندات",
"connectionOriginGuacamoleNote": "تستخدم بروتوكولات RDP وVNC وTelnet الخادم البعيد ما لم تختر \"هذا الجهاز\"، الأمر الذي يتطلب عنوان IP افتراضيًا (guacd) يمكن لهذا الجهاز الوصول إليه. يمكنك تعيين عنوان IP افتراضي في إعدادات عنوان URL الخاص بـ guacd، أو لكل مضيف ضمن إعدادات وكيل guacd."
},
"onboarding": {
"feature_desktop": "سطح المكتب البعيد",
"feature_desktop_desc": "افتح جلسات RDP وVNC وTelnet في علامة تبويب، تمامًا مثل SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "عمق اللون",
"description": "يُستخدم مع مضيفي بروتوكول سطح المكتب البعيد (RDP) الذين يتركونه على إعداداتهم الافتراضية."
},
"resizeMethod": {
"label": "طريقة تغيير الحجم",
"description": "كيف تتكيف الشاشة البعيدة مع حجم النافذة."
},
"resizeDisplayUpdate": "تحديث الشاشة",
"resizeReconnect": "إعادة الاتصال",
"inherit": "يرث",
"on": "مفعلة",
"off": "غير مفعلة",
"forceLossless": {
"label": "فرض بدون فقدان",
"description": "فرض ترميز الصور بدون فقدان (جودة أعلى، استهلاك أكبر للنطاق الترددي)"
},
"enableWallpaper": {
"label": "خلفية سطح المكتب",
"description": "إظهار خلفية سطح المكتب (التعطيل يحسن الأداء)"
},
"enableFontSmoothing": {
"label": "تنعيم الخطوط",
"description": "تمكين عرض خطوط ClearType"
},
"enableDesktopComposition": {
"label": "تركيب سطح المكتب",
"description": "تمكين تأثيرات Aero الزجاجية"
},
"disableAudio": {
"label": "تعطيل الصوت",
"description": "كتم كل الصوت من الجلسة البعيدة"
},
"enablePrinting": {
"label": "تمكين الطباعة",
"description": "إعادة توجيه الطابعات المحلية إلى الجلسة البعيدة"
},
"enableDrive": {
"label": "تمكين إعادة توجيه القرص",
"description": "تعيين مجلد محلي كقرص في الجلسة البعيدة"
},
"disableCopy": {
"label": "تعطيل النسخ",
"description": "منع نسخ النص من الجلسة البعيدة"
},
"disablePaste": {
"label": "تعطيل اللصق",
"description": "منع لصق النص في الجلسة البعيدة"
},
"group": "الإعدادات الافتراضية لسطح المكتب البعيد",
"colorDepth16": "16 بت",
"colorDepth24": "24 بت",
"colorDepth32": "32 بت"
},
"admin": {
"enabled": {
"label": "تمكين سطح المكتب البعيد",
"description": "السماح باتصالات RDP وVNC وTelnet عبر guacd."
},
"guacdUrl": {
"label": "رابط guacd",
"description": "حيث يستمع برنامج guacd، كعنوان المضيف:المنفذ. وتتجاوز متغيرات البيئة GUACD_URL و GUACD_HOST و GUACD_PORT هذا الإعداد.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "منفذ RDP"
},
"vncPort": {
"label": "منفذ VNC"
},
"telnetPort": {
"label": "منفذ Telnet"
},
"rdpSecurity": {
"label": "وضع أمان RDP"
},
"rdpIgnoreCert": {
"label": "تجاهل أخطاء الشهادة"
},
"guacamoleConfig": {
"label": "إعدادات guacd"
},
"enableToolbar": {
"label": "شريط أدوات سطح المكتب البعيد",
"description": "إظهار شريط الأدوات في جلسات RDP وVNC وTelnet."
},
"displayGroup": "عرض",
"displayDefaultsTab": "سطح المكتب البعيد"
}
},
"palette": {
"connectRdp": "الاتصال بـ RDP",
"connectVnc": "الاتصال بـ VNC",
"connectTelnet": "اتصل بشبكة Telnet"
},
"permissions": {
"sessions": {
"title": "مشاركة جلسات سطح المكتب البعيد",
"description": "اسمح لمشاركة الجلسات وغرف التعاون بعرض جلسات RDP وVNC وTelnet الخاصة بهذا المستخدم للآخرين."
}
},
"errors": {
"remoteServerRequired": "يلزم وجود خادم بعيد. قم بتوصيل خادم بعيد في الإعدادات لاستخدام نوع الاتصال هذا."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Свързване към сесия {{type}}...",
"checkingGuacd": "Проверка на услугата за отдалечен работен плот...",
"requestingToken": "Заявка за {{type}} токен за връзка...",
"openingSession": "Отваряне на {{type}} сесия...",
"negotiating": "Преговаряне {{type}} ръкостискане...",
"sessionReady": "{{type}} сесията е готова",
"connectionError": "Грешка във връзката",
"connectionFailed": "Връзката не бе успешна",
"failedToConnect": "Неуспешно получаване на токен за връзка",
"hostNotFound": "Хостът не е намерен",
"noHostSelected": "Няма избран хост",
"reconnect": "Свържете се отново",
"retry": "Опитай отново",
"guacdUnavailable": "Услугата за отдалечен работен плот (guacd) не е налична. Моля, уверете се, че guacd работи, е достъпна и е конфигурирана правилно в администраторските настройки.",
"credentialPromptTitle": "Въведете RDP идентификационни данни",
"credentialPromptDescription": "Този хост е настроен да изисква идентификационни данни при свързване. Те се използват само за тази сесия и не се запазват.",
"connect": "Свържете се",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Файлове на Диск",
"parent": "Родителска директория",
"refresh": "Обновяване",
"empty": "Тази папка е празна",
"listFailed": "Неуспешно четене на тази папка",
"download": "Изтегляне",
"downloadFailed": "Неуспешно изтегляне {{name}}",
"upload": "Качване на файлове",
"uploading": "Качване {{name}}...",
"uploaded": "Качено {{name}}",
"uploadFailed": "Качването не бе успешно {{name}}",
"uploadDisabled": "Качванията са деактивирани за тази връзка",
"driveUnavailable": "Активирайте пренасочването на RDP устройството, преди да качвате файлове",
"driveNotWritable": "guacd не може да записва в папката на устройството. Проверете GUACD_DRIVE_PATH (или пътя на устройството на хоста) и неговите разрешения.",
"dropToUpload": "Пуснете файловете, за да ги качите"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Заключен екран)",
"winKey": "Клавиш за Windows",
"superL": "Супер+L (Заключен екран)",
"superKey": "Супер ключ",
"super": "Супер",
"cmdL": "Cmd+L (Заключен екран)",
"cmdKey": "Команден ключ",
"cmd": "Команда",
"ctrl": "Ctrl",
"alt": "Алтернативно",
"shift": "Изместване",
"win": "Спечелете",
"stickyActive": "{{key}} (заключено - щракнете за освобождаване)",
"stickyInactive": "{{key}} (щракнете, за да заключите)",
"esc": "Бягство",
"tab": "Раздел",
"home": "Дом",
"end": "Край",
"pageUp": "Страница нагоре",
"pageDown": "Страница надолу",
"arrowUp": "Стрелка нагоре",
"arrowDown": "Стрелка надолу",
"arrowLeft": "Стрелка наляво",
"arrowRight": "Стрелка надясно",
"fnToggle": "Функционални клавиши",
"reconnect": "Сесия за повторно свързване",
"collapse": "Свиване на лентата с инструменти",
"expand": "Разгъване на лентата с инструменти",
"dragHandle": "Плъзнете, за да препозиционирате",
"hide": "Скриване за тази сесия. За да го деактивирате за постоянно, отворете Настройки на хоста → Общи → Лента с инструменти за връзка.",
"zoomIn": "Увеличаване",
"zoomOut": "Намаляване на мащаба",
"resetZoom": "Нулиране на мащаба",
"fitToScreen": "Побиране на екрана",
"switchToTrackpad": "Превключване в режим на тракпад (плъзнете, за да преместите курсора, докоснете, за да щракнете)",
"switchToTouch": "Превключване към режим на докосване (докоснете директно там, където искате да щракнете)"
},
"disabled": "Отдалеченият работен плот е изключен. Администратор може да го включи в „Настройки“."
},
"hosts": {
"nativeRdpOpened": "Отворено в отдалечен работен плот на Windows",
"nativeRdpFailed": "Неуспешно отваряне на отдалечения работен плот на Windows",
"openNativeRdp": "Отваряне в отдалечен работен плот на Windows",
"guac": {
"toolbar": "Лента с инструменти",
"connection": "Връзка",
"authentication": "Удостоверяване",
"storedCredential": "Съхранени идентификационни данни",
"authMethod": "Метод за удостоверяване",
"authTypeDirect": "Директно",
"authTypeCredential": "Пълномощия",
"authTypeNone": "Няма",
"authTypeNoneDesc": "Не се съхраняват идентификационни данни. Ще бъдете подканени да въведете потребителско име и парола всеки път, когато се свържете; те не се запазват.",
"selectCredential": "Изберете идентификационен номер...",
"connectionSettings": "Настройки за връзка",
"displaySettings": "Настройки на дисплея",
"audioSettings": "Аудио настройки",
"rdpPerformance": "Производителност на RDP",
"deviceRedirection": "Пренасочване на устройството",
"session": "Сесия",
"gateway": "Портал",
"remoteApp": "Отдалечено приложение",
"clipboard": "Клипборд",
"sessionRecording": "Запис на сесия",
"wakeOnLan": "Събуждане по локална мрежа",
"vncSettings": "VNC настройки",
"terminalSettings": "Настройки на терминала",
"rdpPort": "RDP порт",
"username": "Потребителско име",
"password": "Парола",
"passwordSaved": "Паролата е запазена, въведете, за да я промените",
"domain": "Домейн",
"securityMode": "Режим на сигурност",
"width": "Ширина",
"height": "Височина",
"dpi": "DPI",
"clientName": "Име на клиента",
"initialProgram": "Първоначална програма",
"serverLayout": "Разположение на сървъра",
"timezone": "Часова зона",
"loadBalanceInfo": "Информация за балансиране на натоварването / бисквитка",
"loadBalanceInfoDesc": "„бисквитка“ на RD Connection Broker за балансиране на натоварването на RDS фермата (напр. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacd прокси",
"guacdHostname": "guacd хост",
"guacdHostnamePlaceholder": "Глобално по подразбиране",
"guacdPort": "порт guacd",
"guacdProxyDesc": "Заменете глобалния екземпляр на guacd за тази връзка. Оставете празно, за да използвате глобално конфигурирания guacd.",
"gatewayHostname": "Име на хост на шлюз",
"gatewayPort": "Порт на шлюза",
"gatewayUsername": "Потребителско име на шлюз",
"gatewayPassword": "Парола за шлюз",
"gatewayDomain": "Домейн на шлюз",
"remoteAppProgram": "Програма за отдалечено приложение",
"workingDirectory": "Работна директория",
"arguments": "Аргументи",
"normalizeLineEndings": "Нормализиране на края на редовете",
"macAddress": "MAC адрес",
"broadcastAddress": "Адрес за излъчване",
"udpPort": "UDP порт",
"waitTimeS": "Време за изчакване (и)",
"driveName": "Име на устройството",
"drivePath": "Път на устройството",
"ignoreCertificate": "Игнориране на сертификата",
"ignoreCertificateDesc": "Разрешаване на връзки към хостове със самоподписани сертификати",
"forceLossless": "Принудително беззагубно",
"forceLosslessDesc": "Принудително кодиране на изображения без загуби (по-високо качество, по-голяма честотна лента)",
"disableAudio": "Деактивиране на звука",
"disableAudioDesc": "Изключване на звука от отдалечената сесия",
"enableAudioInput": "Активиране на аудио вход (микрофон)",
"enableAudioInputDesc": "Пренасочване на локалния микрофон към отдалечената сесия",
"wallpaper": "Тапет",
"wallpaperDesc": "Показване на тапет на работния плот (деактивирането му подобрява производителността)",
"theming": "Темиране",
"themingDesc": "Активиране на визуални теми и стилове",
"fontSmoothing": "Изглаждане на шрифта",
"fontSmoothingDesc": "Активиране на рендирането на шрифтове ClearType",
"fullWindowDrag": "Плъзгане на целия прозорец",
"fullWindowDragDesc": "Показване на съдържанието на прозореца при плъзгане",
"desktopComposition": "Композиция на работния плот",
"desktopCompositionDesc": "Активиране на ефектите на Aero Glass",
"menuAnimations": "Анимации на менюто",
"menuAnimationsDesc": "Активиране на анимации за избледняване на менюта и слайдове",
"disableBitmapCaching": "Деактивиране на кеширането на растерни изображения",
"disableBitmapCachingDesc": "Изключете кеша на растерните изображения (може да помогне с проблеми)",
"disableOffscreenCaching": "Деактивиране на кеширането извън екрана",
"disableOffscreenCachingDesc": "Изключване на кеша извън екрана",
"disableGlyphCaching": "Деактивиране на кеширането на глифи",
"disableGlyphCachingDesc": "Изключване на кеша на глифове",
"enableGfx": "Активиране на графични ефекти",
"enableGfxDesc": "Използвайте графичен конвейер RemoteFX",
"enablePrinting": "Активиране на печат",
"enablePrintingDesc": "Пренасочване на локални принтери към отдалечена сесия",
"enableDriveRedirection": "Активиране на пренасочване на устройството",
"enableDriveRedirectionDesc": "Карта на локална папка като устройство в отдалечена сесия",
"createDrivePath": "Създаване на път за устройство",
"createDrivePathDesc": "Автоматично създаване на папката, ако тя не съществува",
"disableDownload": "Деактивиране на изтеглянето",
"disableDownloadDesc": "Предотвратяване на изтеглянето на файлове от отдалечената сесия",
"disableUpload": "Деактивиране на качването",
"disableUploadDesc": "Предотвратяване на качването на файлове към отдалечената сесия",
"enableTouch": "Активиране на докосване",
"enableTouchDesc": "Активиране на пренасочване на сензорен вход",
"consoleSession": "Конзолна сесия",
"consoleSessionDesc": "Свържете се с конзолата (сесия 0) вместо с нова сесия",
"sendWolPacket": "Изпращане на WOL пакет",
"sendWolPacketDesc": "Изпратете магически пакет, за да събудите този хост преди свързване",
"disableCopy": "Деактивиране на копирането",
"disableCopyDesc": "Предотвратяване на копирането на текст от отдалечената сесия",
"disablePaste": "Деактивиране на поставянето",
"disablePasteDesc": "Предотвратяване на поставянето на текст в отдалечената сесия",
"createPathIfMissing": "Създаване на път, ако липсва",
"createPathIfMissingDesc": "Автоматично създаване на директорията за записи",
"excludeOutput": "Изключване на изхода",
"excludeOutputDesc": "Не записвайте изхода на екрана (само метаданни)",
"excludeMouse": "Изключване на мишката",
"excludeMouseDesc": "Не записвайте движенията на мишката",
"includeKeystrokes": "Включи клавишни комбинации",
"includeKeystrokesDesc": "Записване на сурови натискания на клавиши в допълнение към изхода на екрана",
"vncPort": "VNC порт",
"vncPassword": "VNC парола",
"vncUsernameOptional": "Потребителско име (по избор)",
"vncLeaveBlank": "Оставете празно, ако не е необходимо",
"cursorMode": "Режим на курсора",
"swapRedBlue": "Разменете червено/синьо",
"swapRedBlueDesc": "Разменете червените и сините цветови канали (коригира някои проблеми с цветовете)",
"readOnly": "Само за четене",
"readOnlyDesc": "Преглед на отдалечения екран без изпращане на каквито и да било входни данни",
"telnetPort": "Telnet порт",
"terminalType": "Тип терминал",
"fontName": "Име на шрифта",
"fontSize": "Размер на шрифта",
"colorScheme": "Цветова схема",
"backspaceKey": "Клавиш Backspace",
"colorDepth": "Дълбочина на цвета",
"resizeMethod": "Метод за преоразмеряване",
"driveNamePlaceholder": "Термикс Драйв"
},
"remoteDesktop": "Отдалечен работен плот",
"tabRdp": "ПРСР",
"tabVnc": "VNC",
"tabTelnet": "Телнет",
"virtualNetwork": "Виртуална мрежа",
"unencryptedShell": "Некриптирана обвивка",
"remoteDesktopNotes": "Бележки",
"userDefaultsNote": "Настройките, които оставяте непроменени, следват настройките по подразбиране на вашия хост.",
"connectionOriginNote": "В настолното приложение RDP, VNC и Telnet използват отдалечения сървър, освен ако хостът не е зададен на „Това устройство“, което изисква guacd, до който тази машина може да достигне. Задайте такъв в настройките на отдалечения работен плот или за всеки хост под guacd Proxy.",
"docsLink": "Преглед на документи",
"connectionOriginGuacamoleNote": "RDP, VNC и Telnet използват отдалечения сървър, освен ако не изберете „Това устройство“, което изисква guacd, до който тази машина може да достигне. Задайте такъв в настройката на guacd URL или за всеки хост под guacd Proxy."
},
"onboarding": {
"feature_desktop": "Отдалечен работен плот",
"feature_desktop_desc": "Отваряне на RDP, VNC и Telnet сесии в раздел, както при SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Дълбочина на цвета",
"description": "Използва се за RDP хостове, които го оставят по подразбиране."
},
"resizeMethod": {
"label": "Метод за преоразмеряване",
"description": "Как отдалеченият екран следва размера на прозореца."
},
"resizeDisplayUpdate": "Актуализация на дисплея",
"resizeReconnect": "Свържете се отново",
"inherit": "Наследяване",
"on": "Включено",
"off": "Изключено",
"forceLossless": {
"label": "Принудително беззагубно",
"description": "Принудително кодиране на изображения без загуби (по-високо качество, по-голяма честотна лента)"
},
"enableWallpaper": {
"label": "Тапет",
"description": "Показване на тапет на работния плот (деактивирането му подобрява производителността)"
},
"enableFontSmoothing": {
"label": "Изглаждане на шрифта",
"description": "Активиране на рендирането на шрифтове ClearType"
},
"enableDesktopComposition": {
"label": "Композиция на работния плот",
"description": "Активиране на ефектите на Aero Glass"
},
"disableAudio": {
"label": "Деактивиране на звука",
"description": "Изключване на звука от отдалечената сесия"
},
"enablePrinting": {
"label": "Активиране на печат",
"description": "Пренасочване на локални принтери към отдалечена сесия"
},
"enableDrive": {
"label": "Активиране на пренасочване на устройството",
"description": "Карта на локална папка като устройство в отдалечена сесия"
},
"disableCopy": {
"label": "Деактивиране на копирането",
"description": "Предотвратяване на копирането на текст от отдалечената сесия"
},
"disablePaste": {
"label": "Деактивиране на поставянето",
"description": "Предотвратяване на поставянето на текст в отдалечената сесия"
},
"group": "Настройки по подразбиране за отдалечен работен плот",
"colorDepth16": "16-битов",
"colorDepth24": "24-битов",
"colorDepth32": "32-битов"
},
"admin": {
"enabled": {
"label": "Активиране на отдалечен работен плот",
"description": "Разрешаване на RDP, VNC и Telnet връзки чрез guacd."
},
"guacdUrl": {
"label": "URL адрес на GUACD",
"description": "Където guacd слуша, като host:port. Променливите на средата GUACD_URL, GUACD_HOST и GUACD_PORT отменят това.",
"placeholder": "гуакд:4822"
}
},
"host": {
"enableRdp": {
"label": "ПРСР"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Телнет"
},
"rdpPort": {
"label": "RDP порт"
},
"vncPort": {
"label": "VNC порт"
},
"telnetPort": {
"label": "Telnet порт"
},
"rdpSecurity": {
"label": "Режим на защита на RDP"
},
"rdpIgnoreCert": {
"label": "Игнориране на грешки в сертификатите"
},
"guacamoleConfig": {
"label": "настройки на GUACD"
},
"enableToolbar": {
"label": "Лента с инструменти за отдалечен работен плот",
"description": "Показване на лентата с инструменти в RDP, VNC и Telnet сесии."
},
"displayGroup": "Дисплей",
"displayDefaultsTab": "Отдалечен работен плот"
}
},
"palette": {
"connectRdp": "Свържете се с RDP",
"connectVnc": "Свържете се с VNC",
"connectTelnet": "Свържете се с Telnet"
},
"permissions": {
"sessions": {
"title": "Споделяне на сесии за отдалечен работен плот",
"description": "Позволете на споделянето на сесии и стаите за сътрудничество да показват RDP, VNC и Telnet сесиите на този потребител на други."
}
},
"errors": {
"remoteServerRequired": "Изисква се отдалечен сървър. Свържете се с отдалечен сървър в „Настройки“, за да използвате този тип връзка."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Connecting to {{type}} session...",
"checkingGuacd": "Checking remote desktop service...",
"requestingToken": "Requesting {{type}} connection token...",
"openingSession": "Opening {{type}} session...",
"negotiating": "Negotiating {{type}} handshake...",
"sessionReady": "{{type}} session ready",
"connectionError": "Connection error",
"connectionFailed": "Connection failed",
"failedToConnect": "Failed to get connection token",
"hostNotFound": "Host not found",
"noHostSelected": "No host selected",
"reconnect": "Reconnect",
"retry": "Retry",
"guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.",
"credentialPromptTitle": "Enter RDP Credentials",
"credentialPromptDescription": "This host is set to prompt for credentials on connect. They are used for this session only and are not saved.",
"connect": "Connect",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Drive Files",
"parent": "Parent directory",
"refresh": "Refresh",
"empty": "This folder is empty",
"listFailed": "Failed to read this folder",
"download": "Download",
"downloadFailed": "Failed to download {{name}}",
"upload": "Upload files",
"uploading": "Uploading {{name}}...",
"uploaded": "Uploaded {{name}}",
"uploadFailed": "Failed to upload {{name}}",
"uploadDisabled": "Uploads are disabled for this connection",
"driveUnavailable": "Enable RDP drive redirection before uploading files",
"driveNotWritable": "guacd cannot write to the drive folder. Check GUACD_DRIVE_PATH (or the host's Drive Path) and its permissions.",
"dropToUpload": "Drop files to upload"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Lock Screen)",
"winKey": "Windows Key",
"superL": "Super+L (Lock Screen)",
"superKey": "Super Key",
"super": "Super",
"cmdL": "Cmd+L (Lock Screen)",
"cmdKey": "Command Key",
"cmd": "Cmd",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Shift",
"win": "Win",
"stickyActive": "{{key}} (latched - click to release)",
"stickyInactive": "{{key}} (click to latch)",
"esc": "Escape",
"tab": "Tab",
"home": "Home",
"end": "End",
"pageUp": "Page Up",
"pageDown": "Page Down",
"arrowUp": "Arrow Up",
"arrowDown": "Arrow Down",
"arrowLeft": "Arrow Left",
"arrowRight": "Arrow Right",
"fnToggle": "Function Keys",
"reconnect": "Reconnect Session",
"collapse": "Collapse toolbar",
"expand": "Expand toolbar",
"dragHandle": "Drag to reposition",
"hide": "Hide for this session. To disable it permanently, open Host Settings → General → Connection Toolbar.",
"zoomIn": "Zoom in",
"zoomOut": "Zoom out",
"resetZoom": "Reset zoom",
"fitToScreen": "Fit to screen",
"switchToTrackpad": "Switch to trackpad mode (drag to move cursor, tap to click)",
"switchToTouch": "Switch to touch mode (tap directly where you want to click)"
},
"disabled": "Remote Desktop is turned off. An admin can turn it on in Settings."
},
"hosts": {
"nativeRdpOpened": "Opened in Windows Remote Desktop",
"nativeRdpFailed": "Failed to open Windows Remote Desktop",
"openNativeRdp": "Open in Windows Remote Desktop",
"guac": {
"toolbar": "Toolbar",
"connection": "Connection",
"authentication": "Authentication",
"storedCredential": "Stored Credential",
"authMethod": "Auth Method",
"authTypeDirect": "Direct",
"authTypeCredential": "Credential",
"authTypeNone": "None",
"authTypeNoneDesc": "No credentials are stored. You'll be prompted for a username and password each time you connect; they are not saved.",
"selectCredential": "Select a credential...",
"connectionSettings": "Connection Settings",
"displaySettings": "Display Settings",
"audioSettings": "Audio Settings",
"rdpPerformance": "RDP Performance",
"deviceRedirection": "Device Redirection",
"session": "Session",
"gateway": "Gateway",
"remoteApp": "RemoteApp",
"clipboard": "Clipboard",
"sessionRecording": "Session Recording",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "VNC Settings",
"terminalSettings": "Terminal Settings",
"rdpPort": "RDP Port",
"username": "Username",
"password": "Password",
"passwordSaved": "Password saved, type to change",
"domain": "Domain",
"securityMode": "Security Mode",
"width": "Width",
"height": "Height",
"dpi": "DPI",
"clientName": "Client Name",
"initialProgram": "Initial Program",
"serverLayout": "Server Layout",
"timezone": "Timezone",
"loadBalanceInfo": "Load Balance Info / Cookie",
"loadBalanceInfoDesc": "RD Connection Broker cookie for RDS farm load balancing (e.g. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacd Proxy",
"guacdHostname": "guacd Host",
"guacdHostnamePlaceholder": "Global default",
"guacdPort": "guacd Port",
"guacdProxyDesc": "Override the global guacd instance for this connection. Leave blank to use the globally configured guacd.",
"gatewayHostname": "Gateway Hostname",
"gatewayPort": "Gateway Port",
"gatewayUsername": "Gateway Username",
"gatewayPassword": "Gateway Password",
"gatewayDomain": "Gateway Domain",
"remoteAppProgram": "RemoteApp Program",
"workingDirectory": "Working Directory",
"arguments": "Arguments",
"normalizeLineEndings": "Normalize Line Endings",
"macAddress": "MAC Address",
"broadcastAddress": "Broadcast Address",
"udpPort": "UDP Port",
"waitTimeS": "Wait Time (s)",
"driveName": "Drive Name",
"drivePath": "Drive Path",
"ignoreCertificate": "Ignore Certificate",
"ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates",
"forceLossless": "Force Lossless",
"forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)",
"disableAudio": "Disable Audio",
"disableAudioDesc": "Mute all audio from the remote session",
"enableAudioInput": "Enable Audio Input (Microphone)",
"enableAudioInputDesc": "Forward local microphone to the remote session",
"wallpaper": "Wallpaper",
"wallpaperDesc": "Show desktop wallpaper (disabling improves performance)",
"theming": "Theming",
"themingDesc": "Enable visual themes and styles",
"fontSmoothing": "Font Smoothing",
"fontSmoothingDesc": "Enable ClearType font rendering",
"fullWindowDrag": "Full Window Drag",
"fullWindowDragDesc": "Show window contents while dragging",
"desktopComposition": "Desktop Composition",
"desktopCompositionDesc": "Enable Aero glass effects",
"menuAnimations": "Menu Animations",
"menuAnimationsDesc": "Enable menu fade and slide animations",
"disableBitmapCaching": "Disable Bitmap Caching",
"disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)",
"disableOffscreenCaching": "Disable Offscreen Caching",
"disableOffscreenCachingDesc": "Turn off offscreen cache",
"disableGlyphCaching": "Disable Glyph Caching",
"disableGlyphCachingDesc": "Turn off glyph cache",
"enableGfx": "Enable GFX",
"enableGfxDesc": "Use RemoteFX graphics pipeline",
"enablePrinting": "Enable Printing",
"enablePrintingDesc": "Redirect local printers to the remote session",
"enableDriveRedirection": "Enable Drive Redirection",
"enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session",
"createDrivePath": "Create Drive Path",
"createDrivePathDesc": "Automatically create the folder if it does not exist",
"disableDownload": "Disable Download",
"disableDownloadDesc": "Prevent downloading files from the remote session",
"disableUpload": "Disable Upload",
"disableUploadDesc": "Prevent uploading files to the remote session",
"enableTouch": "Enable Touch",
"enableTouchDesc": "Enable touch input forwarding",
"consoleSession": "Console Session",
"consoleSessionDesc": "Connect to the console (session 0) instead of a new session",
"sendWolPacket": "Send WOL Packet",
"sendWolPacketDesc": "Send a magic packet to wake this host before connecting",
"disableCopy": "Disable Copy",
"disableCopyDesc": "Prevent copying text from the remote session",
"disablePaste": "Disable Paste",
"disablePasteDesc": "Prevent pasting text into the remote session",
"createPathIfMissing": "Create Path if Missing",
"createPathIfMissingDesc": "Automatically create the recording directory",
"excludeOutput": "Exclude Output",
"excludeOutputDesc": "Do not record screen output (metadata only)",
"excludeMouse": "Exclude Mouse",
"excludeMouseDesc": "Do not record mouse movements",
"includeKeystrokes": "Include Keystrokes",
"includeKeystrokesDesc": "Record raw keystrokes in addition to screen output",
"vncPort": "VNC Port",
"vncPassword": "VNC Password",
"vncUsernameOptional": "Username (optional)",
"vncLeaveBlank": "Leave blank if not required",
"cursorMode": "Cursor Mode",
"swapRedBlue": "Swap Red/Blue",
"swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)",
"readOnly": "Read-only",
"readOnlyDesc": "View the remote screen without sending any input",
"telnetPort": "Telnet Port",
"terminalType": "Terminal Type",
"fontName": "Font Name",
"fontSize": "Font Size",
"colorScheme": "Color Scheme",
"backspaceKey": "Backspace Key",
"colorDepth": "Color Depth",
"resizeMethod": "Resize Method",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "Remote Desktop",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Virtual Network",
"unencryptedShell": "Unencrypted shell",
"remoteDesktopNotes": "Notes",
"userDefaultsNote": "Settings you leave unchanged follow your host defaults.",
"connectionOriginNote": "On the desktop app, RDP, VNC and Telnet use the remote server unless the host is set to This device, which needs a guacd this machine can reach. Set one in the Remote Desktop settings, or per host under guacd Proxy.",
"docsLink": "View docs",
"connectionOriginGuacamoleNote": "RDP, VNC and Telnet use the remote server unless you pick \"This device\", which needs a guacd this machine can reach. Set one in the guacd URL setting, or per host under guacd Proxy."
},
"onboarding": {
"feature_desktop": "Remote desktop",
"feature_desktop_desc": "Open RDP, VNC and Telnet sessions in a tab, same as SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Color Depth",
"description": "Used for RDP hosts that leave it on their default."
},
"resizeMethod": {
"label": "Resize Method",
"description": "How the remote screen follows the window size."
},
"resizeDisplayUpdate": "Display Update",
"resizeReconnect": "Reconnect",
"inherit": "Inherit",
"on": "On",
"off": "Off",
"forceLossless": {
"label": "Force Lossless",
"description": "Force lossless image encoding (higher quality, more bandwidth)"
},
"enableWallpaper": {
"label": "Wallpaper",
"description": "Show desktop wallpaper (disabling improves performance)"
},
"enableFontSmoothing": {
"label": "Font Smoothing",
"description": "Enable ClearType font rendering"
},
"enableDesktopComposition": {
"label": "Desktop Composition",
"description": "Enable Aero glass effects"
},
"disableAudio": {
"label": "Disable Audio",
"description": "Mute all audio from the remote session"
},
"enablePrinting": {
"label": "Enable Printing",
"description": "Redirect local printers to the remote session"
},
"enableDrive": {
"label": "Enable Drive Redirection",
"description": "Map a local folder as a drive in the remote session"
},
"disableCopy": {
"label": "Disable Copy",
"description": "Prevent copying text from the remote session"
},
"disablePaste": {
"label": "Disable Paste",
"description": "Prevent pasting text into the remote session"
},
"group": "Remote Desktop defaults",
"colorDepth16": "16-bit",
"colorDepth24": "24-bit",
"colorDepth32": "32-bit"
},
"admin": {
"enabled": {
"label": "Enable Remote Desktop",
"description": "Allow RDP, VNC and Telnet connections through guacd."
},
"guacdUrl": {
"label": "guacd URL",
"description": "Where guacd listens, as host:port. The GUACD_URL, GUACD_HOST and GUACD_PORT environment variables override this.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP port"
},
"vncPort": {
"label": "VNC port"
},
"telnetPort": {
"label": "Telnet port"
},
"rdpSecurity": {
"label": "RDP security mode"
},
"rdpIgnoreCert": {
"label": "Ignore certificate errors"
},
"guacamoleConfig": {
"label": "guacd settings"
},
"enableToolbar": {
"label": "Remote desktop toolbar",
"description": "Show the toolbar in RDP, VNC and Telnet sessions."
},
"displayGroup": "Display",
"displayDefaultsTab": "Remote Desktop"
}
},
"palette": {
"connectRdp": "Connect with RDP",
"connectVnc": "Connect with VNC",
"connectTelnet": "Connect with Telnet"
},
"permissions": {
"sessions": {
"title": "Share remote desktop sessions",
"description": "Let session sharing and collab rooms show this user's RDP, VNC and Telnet sessions to others."
}
},
"errors": {
"remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Connectant a la sessió {{type}}...",
"checkingGuacd": "S'està comprovant el servei d'escriptori remot...",
"requestingToken": "Sol·licitant el testimoni de connexió {{type}}...",
"openingSession": "Obrint sessió {{type}}...",
"negotiating": "Negociant una {{type}} encaixada de mans...",
"sessionReady": "{{type}} sessió a punt",
"connectionError": "Error de connexió",
"connectionFailed": "La connexió ha fallat",
"failedToConnect": "No s'ha pogut obtenir el testimoni de connexió",
"hostNotFound": "No s'ha trobat l'amfitrió",
"noHostSelected": "No s'ha seleccionat cap amfitrió",
"reconnect": "Reconnecta",
"retry": "Torna-ho a intentar",
"guacdUnavailable": "El servei d'escriptori remot (guacd) no està disponible. Assegureu-vos que guacd estigui en execució, sigui accessible i estigui configurat correctament a la configuració d'administrador.",
"credentialPromptTitle": "Introduïu les credencials RDP",
"credentialPromptDescription": "Aquest amfitrió està configurat per demanar credencials en connectar-se. Només s'utilitzen per a aquesta sessió i no es desen.",
"connect": "Connecta",
"ctrlAltDel": "Ctrl+Alt+Supr",
"files": {
"title": "Fitxers de Drive",
"parent": "Directori principal",
"refresh": "Actualitza",
"empty": "Aquesta carpeta està buida",
"listFailed": "No s'ha pogut llegir aquesta carpeta",
"download": "Descarrega",
"downloadFailed": "No s'ha pogut descarregar {{name}}",
"upload": "Puja fitxers",
"uploading": "S'està carregant {{name}}...",
"uploaded": "Pujat {{name}}",
"uploadFailed": "No s'ha pogut carregar {{name}}",
"uploadDisabled": "Les càrregues estan desactivades per a aquesta connexió",
"driveUnavailable": "Activa la redirecció de la unitat RDP abans de carregar fitxers",
"driveNotWritable": "guacd no pot escriure a la carpeta de la unitat. Comproveu GUACD_DRIVE_PATH (o la ruta de la unitat de l'amfitrió) i els seus permisos.",
"dropToUpload": "Deixa anar els fitxers per pujar-los"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Supr",
"winL": "Win+L (Pantalla de bloqueig)",
"winKey": "Clau de Windows",
"superL": "Super+L (Pantalla de bloqueig)",
"superKey": "Superclau",
"super": "Súper",
"cmdL": "Cmd+L (Pantalla de bloqueig)",
"cmdKey": "Tecla de comandament",
"cmd": "Cmd",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Majúscules",
"win": "Guanyar",
"stickyActive": "{{key}} (bloquejat - feu clic per alliberar)",
"stickyInactive": "{{key}} (feu clic per bloquejar)",
"esc": "Escapada",
"tab": "Pestanya",
"home": "Inici",
"end": "Final",
"pageUp": "Pàgina amunt",
"pageDown": "Pàgina avall",
"arrowUp": "Fletxa amunt",
"arrowDown": "Fletxa avall",
"arrowLeft": "Fletxa esquerra",
"arrowRight": "Fletxa dreta",
"fnToggle": "Tecles de funció",
"reconnect": "Sessió de reconnexió",
"collapse": "Replega la barra d'eines",
"expand": "Expandeix la barra d'eines",
"dragHandle": "Arrossega per reposicionar",
"hide": "Amaga-ho per a aquesta sessió. Per desactivar-ho permanentment, obriu Configuració de l'amfitrió → General → Barra d'eines de connexió.",
"zoomIn": "Amplia la imatge",
"zoomOut": "Allunya",
"resetZoom": "Restableix el zoom",
"fitToScreen": "Ajusta a la pantalla",
"switchToTrackpad": "Canvia al mode trackpad (arrossega per moure el cursor, toca per fer clic)",
"switchToTouch": "Canvia al mode tàctil (toca directament on vols fer clic)"
},
"disabled": "L'escriptori remot està desactivat. Un administrador el pot activar a Configuració."
},
"hosts": {
"nativeRdpOpened": "Obert a l'escriptori remot de Windows",
"nativeRdpFailed": "No s'ha pogut obrir l'escriptori remot de Windows",
"openNativeRdp": "Obre a l'escriptori remot de Windows",
"guac": {
"toolbar": "Barra d'eines",
"connection": "Connexió",
"authentication": "Autenticació",
"storedCredential": "Credencial emmagatzemada",
"authMethod": "Mètode d'autenticació",
"authTypeDirect": "Directe",
"authTypeCredential": "Credencial",
"authTypeNone": "Cap",
"authTypeNoneDesc": "No s'emmagatzemen les credencials. Se us demanarà un nom d'usuari i una contrasenya cada vegada que us connecteu; no es desen.",
"selectCredential": "Selecciona una credencial...",
"connectionSettings": "Configuració de connexió",
"displaySettings": "Configuració de la pantalla",
"audioSettings": "Configuració d'àudio",
"rdpPerformance": "Rendiment de l'RDP",
"deviceRedirection": "Redirecció de dispositius",
"session": "Sessió",
"gateway": "Porta d'entrada",
"remoteApp": "Aplicació remota",
"clipboard": "Porta-retalls",
"sessionRecording": "Gravació de sessions",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "Configuració de VNC",
"terminalSettings": "Configuració del terminal",
"rdpPort": "Port RDP",
"username": "Nom d'usuari",
"password": "Contrasenya",
"passwordSaved": "Contrasenya desada, escriu per canviar-la",
"domain": "Domini",
"securityMode": "Mode de seguretat",
"width": "Amplada",
"height": "Alçada",
"dpi": "DPI",
"clientName": "Nom del client",
"initialProgram": "Programa inicial",
"serverLayout": "Disseny del servidor",
"timezone": "Zona horària",
"loadBalanceInfo": "Informació de balanç de càrrega / Galeta",
"loadBalanceInfoDesc": "Galeta del broker de connexió RD per a l'equilibri de càrrega de granges RDS (per exemple, tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "Proxy guacd",
"guacdHostname": "Host guacd",
"guacdHostnamePlaceholder": "Predeterminat global",
"guacdPort": "Port de Guacd",
"guacdProxyDesc": "Substitueix la instància global de guacd per a aquesta connexió. Deixa-ho en blanc per utilitzar el guacd configurat globalment.",
"gatewayHostname": "Nom de l'amfitrió de la passarel·la",
"gatewayPort": "Port de passarel·la",
"gatewayUsername": "Nom d'usuari de la passarel·la",
"gatewayPassword": "Contrasenya de la passarel·la",
"gatewayDomain": "Domini de passarel·la",
"remoteAppProgram": "Programa RemoteApp",
"workingDirectory": "Directori de treball",
"arguments": "Arguments",
"normalizeLineEndings": "Normalitzar els finals de línia",
"macAddress": "Adreça MAC",
"broadcastAddress": "Adreça de difusió",
"udpPort": "Port UDP",
"waitTimeS": "Temps d'espera (s)",
"driveName": "Nom de la unitat",
"drivePath": "Camí de conducció",
"ignoreCertificate": "Ignora el certificat",
"ignoreCertificateDesc": "Permet connexions a amfitrions amb certificats autosignats",
"forceLossless": "Força sense pèrdues",
"forceLosslessDesc": "Força la codificació d'imatges sense pèrdues (més qualitat, més amplada de banda)",
"disableAudio": "Desactiva l'àudio",
"disableAudioDesc": "Silencia tot l'àudio de la sessió remota",
"enableAudioInput": "Activa l'entrada d'àudio (micròfon)",
"enableAudioInputDesc": "Reenvia el micròfon local a la sessió remota",
"wallpaper": "Fons de pantalla",
"wallpaperDesc": "Mostra el fons de pantalla de l'escriptori (desactivar-lo millora el rendiment)",
"theming": "Tematització",
"themingDesc": "Habilita els temes i estils visuals",
"fontSmoothing": "Suavitzat de fonts",
"fontSmoothingDesc": "Activa la renderització de fonts ClearType",
"fullWindowDrag": "Arrossegar per tota la finestra",
"fullWindowDragDesc": "Mostra el contingut de la finestra mentre arrossegues",
"desktopComposition": "Composició d'escriptori",
"desktopCompositionDesc": "Activa els efectes Aero Glass",
"menuAnimations": "Animacions de menú",
"menuAnimationsDesc": "Activa les animacions de diapositives i esvaïment del menú",
"disableBitmapCaching": "Desactiva la memòria cau de mapes de bits",
"disableBitmapCachingDesc": "Desactiva la memòria cau de mapes de bits (pot ajudar amb errors)",
"disableOffscreenCaching": "Desactiva la memòria cau fora de pantalla",
"disableOffscreenCachingDesc": "Desactiva la memòria cau fora de pantalla",
"disableGlyphCaching": "Desactiva la memòria cau de glifs",
"disableGlyphCachingDesc": "Desactiva la memòria cau de glifos",
"enableGfx": "Activa els efectes especials",
"enableGfxDesc": "Utilitza el canal de gràfics RemoteFX",
"enablePrinting": "Habilita la impressió",
"enablePrintingDesc": "Redirigir les impressores locals a la sessió remota",
"enableDriveRedirection": "Activa la redirecció de la unitat",
"enableDriveRedirectionDesc": "Assignar una carpeta local com a unitat a la sessió remota",
"createDrivePath": "Crea una ruta de conducció",
"createDrivePathDesc": "Crea automàticament la carpeta si no existeix",
"disableDownload": "Desactiva la descàrrega",
"disableDownloadDesc": "Impedir la descàrrega de fitxers des de la sessió remota",
"disableUpload": "Desactiva la càrrega",
"disableUploadDesc": "Impedir la càrrega de fitxers a la sessió remota",
"enableTouch": "Activa el tacte",
"enableTouchDesc": "Activa el reenviament d'entrada tàctil",
"consoleSession": "Sessió de consola",
"consoleSessionDesc": "Connecta't a la consola (sessió 0) en comptes d'una nova sessió",
"sendWolPacket": "Enviar paquet WOL",
"sendWolPacketDesc": "Envia un paquet màgic per despertar aquest host abans de connectar-se",
"disableCopy": "Desactiva la còpia",
"disableCopyDesc": "Impedir la còpia de text des de la sessió remota",
"disablePaste": "Desactiva l'enganxament",
"disablePasteDesc": "Impedir enganxar text a la sessió remota",
"createPathIfMissing": "Crea un camí si falta",
"createPathIfMissingDesc": "Crea automàticament el directori d'enregistrament",
"excludeOutput": "Exclou la sortida",
"excludeOutputDesc": "No enregistrar la sortida de pantalla (només metadades)",
"excludeMouse": "Exclou el ratolí",
"excludeMouseDesc": "No enregistreu els moviments del ratolí",
"includeKeystrokes": "Inclou les pulsacions de tecles",
"includeKeystrokesDesc": "Enregistra les pulsacions de tecles en brut a més de la sortida de la pantalla",
"vncPort": "Port VNC",
"vncPassword": "Contrasenya de VNC",
"vncUsernameOptional": "Nom d'usuari (opcional)",
"vncLeaveBlank": "Deixeu-ho en blanc si no és necessari",
"cursorMode": "Mode del cursor",
"swapRedBlue": "Intercanvia vermell/blau",
"swapRedBlueDesc": "Intercanvia els canals de color vermell i blau (corregeix alguns problemes de color)",
"readOnly": "Només lectura",
"readOnlyDesc": "Visualitza la pantalla remota sense enviar cap entrada",
"telnetPort": "Port Telnet",
"terminalType": "Tipus de terminal",
"fontName": "Nom de la font",
"fontSize": "Mida de la lletra",
"colorScheme": "Esquema de colors",
"backspaceKey": "Tecla Retrocés",
"colorDepth": "Profunditat de color",
"resizeMethod": "Mètode de redimensionament",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "Escriptori remot",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Xarxa virtual",
"unencryptedShell": "Shell sense xifrar",
"remoteDesktopNotes": "Notes",
"userDefaultsNote": "La configuració que deixeu sense canvis segueix els valors predeterminats de l'amfitrió.",
"connectionOriginNote": "A l'aplicació d'escriptori, RDP, VNC i Telnet utilitzen el servidor remot tret que l'amfitrió estigui configurat com a Aquest dispositiu, que necessita un guacd al qual pugui arribar aquesta màquina. Configureu-ne un a la configuració de l'escriptori remot o per amfitrió a guacd Proxy.",
"docsLink": "Veure documents",
"connectionOriginGuacamoleNote": "RDP, VNC i Telnet utilitzen el servidor remot tret que seleccioneu \"Aquest dispositiu\", que necessita un guacd al qual aquesta màquina pugui arribar. Definiu-ne un a la configuració de l'URL guacd o per host a guacd Proxy."
},
"onboarding": {
"feature_desktop": "Escriptori remot",
"feature_desktop_desc": "Obriu sessions RDP, VNC i Telnet en una pestanya, igual que SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Profunditat de color",
"description": "S'utilitza per a hosts RDP que el deixen per defecte."
},
"resizeMethod": {
"label": "Mètode de redimensionament",
"description": "Com la pantalla remota segueix la mida de la finestra."
},
"resizeDisplayUpdate": "Actualització de la pantalla",
"resizeReconnect": "Reconnecta",
"inherit": "Heretar",
"on": "Activat",
"off": "Desactivat",
"forceLossless": {
"label": "Força sense pèrdues",
"description": "Força la codificació d'imatges sense pèrdues (més qualitat, més amplada de banda)"
},
"enableWallpaper": {
"label": "Fons de pantalla",
"description": "Mostra el fons de pantalla de l'escriptori (desactivar-lo millora el rendiment)"
},
"enableFontSmoothing": {
"label": "Suavitzat de fonts",
"description": "Activa la renderització de fonts ClearType"
},
"enableDesktopComposition": {
"label": "Composició d'escriptori",
"description": "Activa els efectes Aero Glass"
},
"disableAudio": {
"label": "Desactiva l'àudio",
"description": "Silencia tot l'àudio de la sessió remota"
},
"enablePrinting": {
"label": "Habilita la impressió",
"description": "Redirigir les impressores locals a la sessió remota"
},
"enableDrive": {
"label": "Activa la redirecció de la unitat",
"description": "Assignar una carpeta local com a unitat a la sessió remota"
},
"disableCopy": {
"label": "Desactiva la còpia",
"description": "Impedir la còpia de text des de la sessió remota"
},
"disablePaste": {
"label": "Desactiva l'enganxament",
"description": "Impedir enganxar text a la sessió remota"
},
"group": "Valors predeterminats d'escriptori remot",
"colorDepth16": "16 bits",
"colorDepth24": "24 bits",
"colorDepth32": "32 bits"
},
"admin": {
"enabled": {
"label": "Habilita l'escriptori remot",
"description": "Permet connexions RDP, VNC i Telnet a través de guacd."
},
"guacdUrl": {
"label": "URL de guacd",
"description": "On guacd escolta, com a host:port. Les variables d'entorn GUACD_URL, GUACD_HOST i GUACD_PORT ho anul·len.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "Port RDP"
},
"vncPort": {
"label": "Port VNC"
},
"telnetPort": {
"label": "Port Telnet"
},
"rdpSecurity": {
"label": "Mode de seguretat RDP"
},
"rdpIgnoreCert": {
"label": "Ignora els errors de certificat"
},
"guacamoleConfig": {
"label": "configuració de guacd"
},
"enableToolbar": {
"label": "Barra d'eines d'escriptori remot",
"description": "Mostra la barra d'eines a les sessions RDP, VNC i Telnet."
},
"displayGroup": "Pantalla",
"displayDefaultsTab": "Escriptori remot"
}
},
"palette": {
"connectRdp": "Connecta't amb RDP",
"connectVnc": "Connecta amb VNC",
"connectTelnet": "Connecta amb Telnet"
},
"permissions": {
"sessions": {
"title": "Compartir sessions d'escriptori remot",
"description": "Permet que les sales de compartició de sessions i col·laboració mostrin les sessions RDP, VNC i Telnet d'aquest usuari a altres persones."
}
},
"errors": {
"remoteServerRequired": "Cal un servidor remot. Connecteu un servidor remot a Configuració per utilitzar aquest tipus de connexió."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Připojování k relaci {{type}}...",
"checkingGuacd": "Kontrola služby vzdálené plochy...",
"requestingToken": "Žádost o token připojení {{type}}...",
"openingSession": "Otevírání {{type}} relace...",
"negotiating": "Vyjednávání {{type}} podání ruky...",
"sessionReady": "{{type}} relace připravena",
"connectionError": "Chyba připojení",
"connectionFailed": "Připojení se nezdařilo",
"failedToConnect": "Nepodařilo se získat token připojení",
"hostNotFound": "Hostitel nenalezen",
"noHostSelected": "Nebyl vybrán žádný hostitel",
"reconnect": "Znovu se připojte",
"retry": "Zkusit znovu",
"guacdUnavailable": "Služba vzdálené plochy (guacd) není k dispozici. Ujistěte se prosím, že je guacd spuštěná, přístupná a správně nakonfigurovaná v nastavení administrátora.",
"credentialPromptTitle": "Zadejte přihlašovací údaje RDP",
"credentialPromptDescription": "Tento hostitel je nastaven tak, aby při připojení vyžadoval přihlašovací údaje. Tyto přihlašovací údaje se používají pouze pro tuto relaci a neukládají se.",
"connect": "Připojit",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Soubory na Disku",
"parent": "Nadřazený adresář",
"refresh": "Obnovit",
"empty": "Tato složka je prázdná",
"listFailed": "Tuto složku se nepodařilo přečíst.",
"download": "Stáhnout",
"downloadFailed": "Stažení se nezdařilo {{name}}",
"upload": "Nahrát soubory",
"uploading": "Nahrávání {{name}}...",
"uploaded": "Nahráno {{name}}",
"uploadFailed": "Nahrávání se nezdařilo {{name}}",
"uploadDisabled": "Nahrávání je pro toto připojení zakázáno.",
"driveUnavailable": "Před nahráváním souborů povolte přesměrování disku RDP",
"driveNotWritable": "Příkaz guacd nemůže zapisovat do složky na disku. Zkontrolujte cestu k disku GUACD_DRIVE_PATH (nebo cestu k disku hostitele) a její oprávnění.",
"dropToUpload": "Přetáhněte soubory k nahrání"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Zamykání obrazovky)",
"winKey": "Klávesa Windows",
"superL": "Super+L (Zamykací obrazovka)",
"superKey": "Super klíč",
"super": "Super",
"cmdL": "Cmd+L (Zamykací obrazovka)",
"cmdKey": "Příkazový klíč",
"cmd": "Příkaz",
"ctrl": "Ctrl",
"alt": "Alternativní",
"shift": "Posun",
"win": "Vyhrát",
"stickyActive": "{{key}} (zajištěno - kliknutím uvolníte)",
"stickyInactive": "{{key}} (kliknutím zajistíte)",
"esc": "Uniknout",
"tab": "Záložka",
"home": "Domov",
"end": "Konec",
"pageUp": "Stránka nahoru",
"pageDown": "Stránka dolů",
"arrowUp": "Šipka nahoru",
"arrowDown": "Šipka dolů",
"arrowLeft": "Šipka vlevo",
"arrowRight": "Šipka vpravo",
"fnToggle": "Funkční klávesy",
"reconnect": "Znovu připojit relaci",
"collapse": "Sbalit panel nástrojů",
"expand": "Rozbalit panel nástrojů",
"dragHandle": "Přetažením změníte pozici",
"hide": "Skrýt pro tuto relaci. Chcete-li jej trvale zakázat, otevřete Nastavení hostitele → Obecné → Panel nástrojů pro připojení.",
"zoomIn": "Přiblížit",
"zoomOut": "Oddálit",
"resetZoom": "Obnovit zoom",
"fitToScreen": "Přizpůsobit obrazovce",
"switchToTrackpad": "Přepnout do režimu trackpadu (tažením přesunete kurzor, klepnutím kliknete)",
"switchToTouch": "Přepnout do dotykového režimu (klepněte přímo na místo, kam chcete kliknout)"
},
"disabled": "Vzdálená plocha je vypnutá. Správce ji může zapnout v Nastavení."
},
"hosts": {
"nativeRdpOpened": "Otevřeno ve Vzdálené ploše systému Windows",
"nativeRdpFailed": "Nepodařilo se otevřít Vzdálenou plochu systému Windows",
"openNativeRdp": "Otevřít ve Vzdálené ploše systému Windows",
"guac": {
"toolbar": "Panel nástrojů",
"connection": "Spojení",
"authentication": "Ověřování",
"storedCredential": "Uložené přihlašovací údaje",
"authMethod": "Metoda ověřování",
"authTypeDirect": "Řídit",
"authTypeCredential": "Přihlašovací údaj",
"authTypeNone": "Žádný",
"authTypeNoneDesc": "Nejsou uloženy žádné přihlašovací údaje. Při každém připojení budete vyzváni k zadání uživatelského jména a hesla; tyto údaje se neukládají.",
"selectCredential": "Vyberte pověření...",
"connectionSettings": "Nastavení připojení",
"displaySettings": "Nastavení displeje",
"audioSettings": "Nastavení zvuku",
"rdpPerformance": "Výkon RDP",
"deviceRedirection": "Přesměrování zařízení",
"session": "Zasedání",
"gateway": "Brána",
"remoteApp": "Vzdálená aplikace",
"clipboard": "Schránka",
"sessionRecording": "Nahrávání relace",
"wakeOnLan": "Probuzení přes LAN",
"vncSettings": "Nastavení VNC",
"terminalSettings": "Nastavení terminálu",
"rdpPort": "RDP port",
"username": "Uživatelské jméno",
"password": "Heslo",
"passwordSaved": "Heslo uloženo, zadejte heslo pro změnu",
"domain": "Doména",
"securityMode": "Bezpečnostní režim",
"width": "Šířka",
"height": "Výška",
"dpi": "DPI",
"clientName": "Jméno klienta",
"initialProgram": "Počáteční program",
"serverLayout": "Rozložení serveru",
"timezone": "Časové pásmo",
"loadBalanceInfo": "Informace o vyrovnávání zátěže / soubor cookie",
"loadBalanceInfoDesc": "Soubor cookie RD Connection Broker pro vyvažování zátěže farmy RDS (např. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "Proxy GUACD",
"guacdHostname": "Hostitel guacd",
"guacdHostnamePlaceholder": "Globální výchozí nastavení",
"guacdPort": "Port GUACD",
"guacdProxyDesc": "Pro toto připojení přepište globální instanci guacd. Pokud chcete použít globálně nakonfigurovaný guacd, ponechte pole prázdné.",
"gatewayHostname": "Název hostitele brány",
"gatewayPort": "Port brány",
"gatewayUsername": "Uživatelské jméno brány",
"gatewayPassword": "Heslo brány",
"gatewayDomain": "Doména brány",
"remoteAppProgram": "Program RemoteApp",
"workingDirectory": "Pracovní adresář",
"arguments": "Argumenty",
"normalizeLineEndings": "Normalizovat konce řádků",
"macAddress": "MAC adresa",
"broadcastAddress": "Vysílací adresa",
"udpPort": "UDP port",
"waitTimeS": "Čekací doba (s)",
"driveName": "Název disku",
"drivePath": "Cesta pohonu",
"ignoreCertificate": "Ignorovat certifikát",
"ignoreCertificateDesc": "Povolit připojení k hostitelům s certifikáty podepsanými držitelem",
"forceLossless": "Vynutit bezztrátové",
"forceLosslessDesc": "Vynucení bezztrátového kódování obrazu (vyšší kvalita, větší šířka pásma)",
"disableAudio": "Zakázat zvuk",
"disableAudioDesc": "Ztlumit veškerý zvuk ze vzdálené relace",
"enableAudioInput": "Povolit zvukový vstup (mikrofon)",
"enableAudioInputDesc": "Přesměrování lokálního mikrofonu do vzdálené relace",
"wallpaper": "Tapeta",
"wallpaperDesc": "Zobrazit tapetu plochy (zakázání zlepšuje výkon)",
"theming": "Témata",
"themingDesc": "Povolit vizuální motivy a styly",
"fontSmoothing": "Vyhlazování písma",
"fontSmoothingDesc": "Povolit vykreslování písma ClearType",
"fullWindowDrag": "Přetažení celého okna",
"fullWindowDragDesc": "Zobrazit obsah okna při přetahování",
"desktopComposition": "Složení plochy",
"desktopCompositionDesc": "Povolit efekty Aero Glass",
"menuAnimations": "Animace v menu",
"menuAnimationsDesc": "Povolit animace prolínání a snímání nabídek",
"disableBitmapCaching": "Zakázat ukládání bitmap do mezipaměti",
"disableBitmapCachingDesc": "Vypnout mezipaměť bitmap (může pomoci s chybami)",
"disableOffscreenCaching": "Zakázat ukládání do mezipaměti mimo obrazovku",
"disableOffscreenCachingDesc": "Vypnout mezipaměť mimo obrazovku",
"disableGlyphCaching": "Zakázat ukládání glyfů do mezipaměti",
"disableGlyphCachingDesc": "Vypnout mezipaměť glyfů",
"enableGfx": "Povolit grafické efekty",
"enableGfxDesc": "Použití grafického kanálu RemoteFX",
"enablePrinting": "Povolit tisk",
"enablePrintingDesc": "Přesměrování lokálních tiskáren na vzdálenou relaci",
"enableDriveRedirection": "Povolit přesměrování disku",
"enableDriveRedirectionDesc": "Namapování lokální složky jako jednotky ve vzdálené relaci",
"createDrivePath": "Vytvořit cestu k disku",
"createDrivePathDesc": "Automaticky vytvořit složku, pokud neexistuje",
"disableDownload": "Zakázat stahování",
"disableDownloadDesc": "Zabránit stahování souborů ze vzdálené relace",
"disableUpload": "Zakázat nahrávání",
"disableUploadDesc": "Zabránit nahrávání souborů do vzdálené relace",
"enableTouch": "Povolit dotyk",
"enableTouchDesc": "Povolit přesměrování dotykového vstupu",
"consoleSession": "Relace konzole",
"consoleSessionDesc": "Připojení ke konzoli (relace 0) namísto nové relace",
"sendWolPacket": "Odeslat paket WOL",
"sendWolPacketDesc": "Před připojením odešlete magický paket, který probudí tento hostitel.",
"disableCopy": "Zakázat kopírování",
"disableCopyDesc": "Zabránit kopírování textu ze vzdálené relace",
"disablePaste": "Zakázat vkládání",
"disablePasteDesc": "Zabránit vkládání textu do vzdálené relace",
"createPathIfMissing": "Vytvořit cestu, pokud chybí",
"createPathIfMissingDesc": "Automaticky vytvořit adresář pro nahrávání",
"excludeOutput": "Vyloučit výstup",
"excludeOutputDesc": "Nezaznamenávat výstup obrazovky (pouze metadata)",
"excludeMouse": "Vyloučit myš",
"excludeMouseDesc": "Nezaznamenávat pohyby myši",
"includeKeystrokes": "Zahrnout klávesové zkratky",
"includeKeystrokesDesc": "Zaznamenávat nezpracované stisky kláves kromě výstupu na obrazovku",
"vncPort": "VNC port",
"vncPassword": "Heslo VNC",
"vncUsernameOptional": "Uživatelské jméno (volitelné)",
"vncLeaveBlank": "Pokud není vyžadováno, nechte prázdné.",
"cursorMode": "Režim kurzoru",
"swapRedBlue": "Prohodit červenou/modrou",
"swapRedBlueDesc": "Prohoďte červený a modrý barevný kanál (opravuje některé problémy s barvami)",
"readOnly": "Pouze pro čtení",
"readOnlyDesc": "Zobrazení vzdálené obrazovky bez odesílání jakýchkoli vstupních dat",
"telnetPort": "Telnetový port",
"terminalType": "Typ terminálu",
"fontName": "Název písma",
"fontSize": "Velikost písma",
"colorScheme": "Barevné schéma",
"backspaceKey": "Klávesa Backspace",
"colorDepth": "Barevná hloubka",
"resizeMethod": "Metoda změny velikosti",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "Vzdálená plocha",
"tabRdp": "PRV",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Virtuální síť",
"unencryptedShell": "Nešifrovaný shell",
"remoteDesktopNotes": "Poznámky",
"userDefaultsNote": "Nastavení, která ponecháte beze změny, se řídí výchozími hodnotami vašeho hostitele.",
"connectionOriginNote": "V desktopové aplikaci používají RDP, VNC a Telnet vzdálený server, pokud není hostitel nastaven na Toto zařízení, které vyžaduje guacd, ke kterému má tento počítač přístup. Nastavte ho v nastavení Vzdálené plochy nebo pro každého hostitele v části guacd Proxy.",
"docsLink": "Zobrazit dokumenty",
"connectionOriginGuacamoleNote": "RDP, VNC a Telnet používají vzdálený server, pokud nevyberete možnost „Toto zařízení“, která vyžaduje GUACD, ke kterému má tento počítač přístup. Nastavte ho v nastavení URL GUACD nebo pro každého hostitele v části Proxy GUACD."
},
"onboarding": {
"feature_desktop": "Vzdálená plocha",
"feature_desktop_desc": "Otevírejte relace RDP, VNC a Telnet na kartě, stejně jako SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Barevná hloubka",
"description": "Používá se pro hostitele RDP, kteří jej ponechávají na výchozím nastavení."
},
"resizeMethod": {
"label": "Metoda změny velikosti",
"description": "Jak vzdálená obrazovka kopíruje velikost okna."
},
"resizeDisplayUpdate": "Aktualizace displeje",
"resizeReconnect": "Znovu se připojte",
"inherit": "Zdědit",
"on": "Na",
"off": "Vypnuto",
"forceLossless": {
"label": "Vynutit bezztrátové",
"description": "Vynucení bezztrátového kódování obrazu (vyšší kvalita, větší šířka pásma)"
},
"enableWallpaper": {
"label": "Tapeta",
"description": "Zobrazit tapetu plochy (zakázání zlepšuje výkon)"
},
"enableFontSmoothing": {
"label": "Vyhlazování písma",
"description": "Povolit vykreslování písma ClearType"
},
"enableDesktopComposition": {
"label": "Složení plochy",
"description": "Povolit efekty Aero Glass"
},
"disableAudio": {
"label": "Zakázat zvuk",
"description": "Ztlumit veškerý zvuk ze vzdálené relace"
},
"enablePrinting": {
"label": "Povolit tisk",
"description": "Přesměrování lokálních tiskáren na vzdálenou relaci"
},
"enableDrive": {
"label": "Povolit přesměrování disku",
"description": "Namapování lokální složky jako jednotky ve vzdálené relaci"
},
"disableCopy": {
"label": "Zakázat kopírování",
"description": "Zabránit kopírování textu ze vzdálené relace"
},
"disablePaste": {
"label": "Zakázat vkládání",
"description": "Zabránit vkládání textu do vzdálené relace"
},
"group": "Výchozí nastavení vzdálené plochy",
"colorDepth16": "16bitový",
"colorDepth24": "24bitový",
"colorDepth32": "32bitový"
},
"admin": {
"enabled": {
"label": "Povolit vzdálenou plochu",
"description": "Povolit připojení RDP, VNC a Telnet přes guacd."
},
"guacdUrl": {
"label": "URL GUACD",
"description": "Kde guacd naslouchá, jako host:port. Proměnné prostředí GUACD_URL, GUACD_HOST a GUACD_PORT toto přepíší.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "PRV"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP port"
},
"vncPort": {
"label": "VNC port"
},
"telnetPort": {
"label": "Telnetový port"
},
"rdpSecurity": {
"label": "Režim zabezpečení RDP"
},
"rdpIgnoreCert": {
"label": "Ignorovat chyby certifikátu"
},
"guacamoleConfig": {
"label": "nastavení GUACD"
},
"enableToolbar": {
"label": "Panel nástrojů Vzdálená plocha",
"description": "Zobrazit panel nástrojů v relacích RDP, VNC a Telnet."
},
"displayGroup": "Zobrazit",
"displayDefaultsTab": "Vzdálená plocha"
}
},
"palette": {
"connectRdp": "Připojení k RDP",
"connectVnc": "Spojte se s VNC",
"connectTelnet": "Připojení přes Telnet"
},
"permissions": {
"sessions": {
"title": "Sdílení relací vzdálené plochy",
"description": "Umožnit sdílení relací a místnostem pro spolupráci zobrazovat relace RDP, VNC a Telnet tohoto uživatele ostatním."
}
},
"errors": {
"remoteServerRequired": "Je vyžadován vzdálený server. Pro použití tohoto typu připojení se připojte ke vzdálenému serveru v Nastavení."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Forbinder til {{type}} session...",
"checkingGuacd": "Tjekker fjernskrivebordstjeneste...",
"requestingToken": "Anmoder om {{type}} forbindelsestoken...",
"openingSession": "Åbner {{type}} session...",
"negotiating": "Forhandler {{type}} håndtryk...",
"sessionReady": "{{type}} session klar",
"connectionError": "Forbindelsesfejl",
"connectionFailed": "Forbindelsen mislykkedes",
"failedToConnect": "Kunne ikke hente forbindelsestoken",
"hostNotFound": "Værten blev ikke fundet",
"noHostSelected": "Ingen vært valgt",
"reconnect": "Genopret forbindelse",
"retry": "Prøv igen",
"guacdUnavailable": "Fjernskrivebordstjenesten (guacd) er ikke tilgængelig. Sørg for, at guacd kører, er tilgængelig og konfigureret korrekt i administratorindstillingerne.",
"credentialPromptTitle": "Indtast RDP-legitimationsoplysninger",
"credentialPromptDescription": "Denne vært er indstillet til at bede om legitimationsoplysninger ved forbindelse. De bruges kun til denne session og gemmes ikke.",
"connect": "Forbinde",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Drev-filer",
"parent": "Forældreregister",
"refresh": "Opfriske",
"empty": "Denne mappe er tom",
"listFailed": "Kunne ikke læse denne mappe",
"download": "Download",
"downloadFailed": "Kunne ikke downloade {{name}}",
"upload": "Upload filer",
"uploading": "Uploader {{name}}...",
"uploaded": "Uploadet {{name}}",
"uploadFailed": "Kunne ikke uploade {{name}}",
"uploadDisabled": "Uploads er deaktiveret for denne forbindelse",
"driveUnavailable": "Aktivér omdirigering af RDP-drev før upload af filer",
"driveNotWritable": "guacd kan ikke skrive til drevmappen. Kontroller GUACD_DRIVE_PATH (eller værtens drevsti) og dens tilladelser.",
"dropToUpload": "Slip filer for at uploade"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Låseskærm)",
"winKey": "Windows-nøgle",
"superL": "Super+L (Låseskærm)",
"superKey": "Supernøgle",
"super": "Super",
"cmdL": "Cmd+L (Låseskærm)",
"cmdKey": "Kommandotast",
"cmd": "Kommando",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Flytte",
"win": "Vinde",
"stickyActive": "{{key}} (låst - klik for at slippe)",
"stickyInactive": "{{key}} (klik for at låse)",
"esc": "Flugt",
"tab": "Faneblad",
"home": "Hjem",
"end": "Ende",
"pageUp": "Side op",
"pageDown": "Side ned",
"arrowUp": "Pil op",
"arrowDown": "Pil ned",
"arrowLeft": "Pil til venstre",
"arrowRight": "Pil til højre",
"fnToggle": "Funktionstaster",
"reconnect": "Genopret forbindelse til sessionen",
"collapse": "Skjul værktøjslinjen",
"expand": "Udvid værktøjslinjen",
"dragHandle": "Træk for at flytte",
"hide": "Skjul for denne session. For at deaktivere det permanent skal du åbne Værtsindstillinger → Generelt → Forbindelsesværktøjslinje.",
"zoomIn": "Zoom ind",
"zoomOut": "Zoom ud",
"resetZoom": "Nulstil zoom",
"fitToScreen": "Tilpas til skærm",
"switchToTrackpad": "Skift til trackpad-tilstand (træk for at flytte markøren, tryk for at klikke)",
"switchToTouch": "Skift til berøringstilstand (tryk direkte der, hvor du vil klikke)"
},
"disabled": "Fjernskrivebord er slået fra. En administrator kan slå det til i Indstillinger."
},
"hosts": {
"nativeRdpOpened": "Åbnet i Windows Fjernskrivebord",
"nativeRdpFailed": "Kunne ikke åbne Windows Fjernskrivebord",
"openNativeRdp": "Åbn i Windows Fjernskrivebord",
"guac": {
"toolbar": "Værktøjslinje",
"connection": "Forbindelse",
"authentication": "Godkendelse",
"storedCredential": "Gemte legitimationsoplysninger",
"authMethod": "Godkendelsesmetode",
"authTypeDirect": "Direkte",
"authTypeCredential": "Legitimationsoplysninger",
"authTypeNone": "Ingen",
"authTypeNoneDesc": "Ingen loginoplysninger gemmes. Du vil blive bedt om et brugernavn og en adgangskode, hver gang du opretter forbindelse; de gemmes ikke.",
"selectCredential": "Vælg en legitimationsoplysninger...",
"connectionSettings": "Forbindelsesindstillinger",
"displaySettings": "Skærmindstillinger",
"audioSettings": "Lydindstillinger",
"rdpPerformance": "RDP-ydeevne",
"deviceRedirection": "Omdirigering af enhed",
"session": "Session",
"gateway": "Gateway",
"remoteApp": "Fjernbetjening",
"clipboard": "Udklipsholder",
"sessionRecording": "Sessionsoptagelse",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "VNC-indstillinger",
"terminalSettings": "Terminalindstillinger",
"rdpPort": "RDP-port",
"username": "Brugernavn",
"password": "Adgangskode",
"passwordSaved": "Adgangskode gemt, skriv for at ændre",
"domain": "Domæne",
"securityMode": "Sikkerhedstilstand",
"width": "Bredde",
"height": "Højde",
"dpi": "DPI",
"clientName": "Klientnavn",
"initialProgram": "Indledende program",
"serverLayout": "Serverlayout",
"timezone": "Tidszone",
"loadBalanceInfo": "Oplysninger om belastningsbalance / cookie",
"loadBalanceInfoDesc": "RD Connection Broker-cookie til belastningsbalancering af RDS-farm (f.eks. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacd Proxy",
"guacdHostname": "guacd-vært",
"guacdHostnamePlaceholder": "Global standard",
"guacdPort": "guacd Port",
"guacdProxyDesc": "Tilsidesæt den globale guacd-instans for denne forbindelse. Lad være tom for at bruge den globalt konfigurerede guacd.",
"gatewayHostname": "Gateway-værtsnavn",
"gatewayPort": "Gateway-porten",
"gatewayUsername": "Gateway-brugernavn",
"gatewayPassword": "Gateway-adgangskode",
"gatewayDomain": "Gateway-domæne",
"remoteAppProgram": "RemoteApp-programmet",
"workingDirectory": "Arbejdskatalog",
"arguments": "Argumenter",
"normalizeLineEndings": "Normaliser linjeafslutninger",
"macAddress": "MAC-adresse",
"broadcastAddress": "Udsendelsesadresse",
"udpPort": "UDP-port",
"waitTimeS": "Ventetid (s)",
"driveName": "Drevnavn",
"drivePath": "Kørselssti",
"ignoreCertificate": "Ignorer certifikat",
"ignoreCertificateDesc": "Tillad forbindelser til værter med selvsignerede certifikater",
"forceLossless": "Tving tabsfri",
"forceLosslessDesc": "Tving tabsfri billedkodning (højere kvalitet, mere båndbredde)",
"disableAudio": "Deaktiver lyd",
"disableAudioDesc": "Slå al lyd fra fra fjernsessionen",
"enableAudioInput": "Aktivér lydindgang (mikrofon)",
"enableAudioInputDesc": "Videresend den lokale mikrofon til den eksterne session",
"wallpaper": "Tapet",
"wallpaperDesc": "Vis skrivebordsbaggrund (deaktivering forbedrer ydeevnen)",
"theming": "Temaer",
"themingDesc": "Aktivér visuelle temaer og stilarter",
"fontSmoothing": "Skrifttypeudjævning",
"fontSmoothingDesc": "Aktivér gengivelse af ClearType-skrifttyper",
"fullWindowDrag": "Træk i hele vinduet",
"fullWindowDragDesc": "Vis vinduesindhold under træk",
"desktopComposition": "Skrivebordskomposition",
"desktopCompositionDesc": "Aktivér Aero Glass-effekter",
"menuAnimations": "Menuanimationer",
"menuAnimationsDesc": "Aktivér menuudtoning og slideanimationer",
"disableBitmapCaching": "Deaktiver bitmap-caching",
"disableBitmapCachingDesc": "Slå bitmap-cache fra (kan muligvis hjælpe med fejl)",
"disableOffscreenCaching": "Deaktiver cachelagring uden for skærmen",
"disableOffscreenCachingDesc": "Slå cache uden for skærmen fra",
"disableGlyphCaching": "Deaktiver glyf-cachelagring",
"disableGlyphCachingDesc": "Slå glyfcache fra",
"enableGfx": "Aktivér GFX",
"enableGfxDesc": "Brug RemoteFX-grafikpipeline",
"enablePrinting": "Aktivér udskrivning",
"enablePrintingDesc": "Omdiriger lokale printere til fjernsessionen",
"enableDriveRedirection": "Aktivér drevomdirigering",
"enableDriveRedirectionDesc": "Tilknyt en lokal mappe som et drev i den eksterne session",
"createDrivePath": "Opret drevsti",
"createDrivePathDesc": "Opret mappen automatisk, hvis den ikke findes",
"disableDownload": "Deaktiver download",
"disableDownloadDesc": "Forhindr download af filer fra fjernsessionen",
"disableUpload": "Deaktiver upload",
"disableUploadDesc": "Forhindr upload af filer til fjernsessionen",
"enableTouch": "Aktivér berøring",
"enableTouchDesc": "Aktivér videresendelse af berøringsinput",
"consoleSession": "Konsolsession",
"consoleSessionDesc": "Opret forbindelse til konsollen (session 0) i stedet for en ny session",
"sendWolPacket": "Send WOL-pakke",
"sendWolPacketDesc": "Send en magisk pakke for at vække denne vært før forbindelse",
"disableCopy": "Deaktiver kopiering",
"disableCopyDesc": "Forhindr kopiering af tekst fra fjernsessionen",
"disablePaste": "Deaktiver indsætning",
"disablePasteDesc": "Forhindr indsættelse af tekst i fjernsessionen",
"createPathIfMissing": "Opret sti, hvis den mangler",
"createPathIfMissingDesc": "Opret automatisk optagelsesmappen",
"excludeOutput": "Udeluk output",
"excludeOutputDesc": "Optag ikke skærmoutput (kun metadata)",
"excludeMouse": "Udeluk mus",
"excludeMouseDesc": "Optag ikke musebevægelser",
"includeKeystrokes": "Inkluder tastetryk",
"includeKeystrokesDesc": "Optag rå tastetryk ud over skærmoutput",
"vncPort": "VNC-port",
"vncPassword": "VNC-adgangskode",
"vncUsernameOptional": "Brugernavn (valgfrit)",
"vncLeaveBlank": "Lad stå tomt, hvis det ikke er nødvendigt",
"cursorMode": "Markørtilstand",
"swapRedBlue": "Skift rød/blå",
"swapRedBlueDesc": "Byt de røde og blå farvekanaler (løser nogle farveproblemer)",
"readOnly": "Skrivebeskyttet",
"readOnlyDesc": "Se fjernskærmen uden at sende input",
"telnetPort": "Telnet-port",
"terminalType": "Terminaltype",
"fontName": "Skrifttypenavn",
"fontSize": "Skriftstørrelse",
"colorScheme": "Farveskema",
"backspaceKey": "Tilbagetast",
"colorDepth": "Farvedybde",
"resizeMethod": "Ændre størrelse-metode",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "Fjernskrivebord",
"tabRdp": "Landdistriktsudviklingsplan",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Virtuelt netværk",
"unencryptedShell": "Ukrypteret skal",
"remoteDesktopNotes": "Noter",
"userDefaultsNote": "Indstillinger, du lader være uændrede, følger din værts standardindstillinger.",
"connectionOriginNote": "I desktop-appen bruger RDP, VNC og Telnet den eksterne server, medmindre værten er indstillet til Denne enhed, som kræver en guacd, som denne maskine kan nå. Angiv en i indstillingerne for Fjernskrivebord, eller pr. vært under guacd Proxy.",
"docsLink": "Se dokumenter",
"connectionOriginGuacamoleNote": "RDP, VNC og Telnet bruger den eksterne server, medmindre du vælger \"Denne enhed\", som kræver en guacd, som denne maskine kan nå. Angiv en i guacd URL-indstillingen eller pr. vært under guacd Proxy."
},
"onboarding": {
"feature_desktop": "Fjernskrivebord",
"feature_desktop_desc": "Åbn RDP-, VNC- og Telnet-sessioner i en fane, ligesom med SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Farvedybde",
"description": "Bruges til RDP-værter, der lader den være på standardværdien."
},
"resizeMethod": {
"label": "Ændre størrelse-metode",
"description": "Hvordan fjernskærmen følger vinduets størrelse."
},
"resizeDisplayUpdate": "Opdatering af display",
"resizeReconnect": "Genopret forbindelse",
"inherit": "Arve",
"on": "På",
"off": "Slukket",
"forceLossless": {
"label": "Tving tabsfri",
"description": "Tving tabsfri billedkodning (højere kvalitet, mere båndbredde)"
},
"enableWallpaper": {
"label": "Tapet",
"description": "Vis skrivebordsbaggrund (deaktivering forbedrer ydeevnen)"
},
"enableFontSmoothing": {
"label": "Skrifttypeudjævning",
"description": "Aktivér gengivelse af ClearType-skrifttyper"
},
"enableDesktopComposition": {
"label": "Skrivebordskomposition",
"description": "Aktivér Aero Glass-effekter"
},
"disableAudio": {
"label": "Deaktiver lyd",
"description": "Slå al lyd fra fra fjernsessionen"
},
"enablePrinting": {
"label": "Aktivér udskrivning",
"description": "Omdiriger lokale printere til fjernsessionen"
},
"enableDrive": {
"label": "Aktivér drevomdirigering",
"description": "Tilknyt en lokal mappe som et drev i den eksterne session"
},
"disableCopy": {
"label": "Deaktiver kopiering",
"description": "Forhindr kopiering af tekst fra fjernsessionen"
},
"disablePaste": {
"label": "Deaktiver indsætning",
"description": "Forhindr indsættelse af tekst i fjernsessionen"
},
"group": "Standardindstillinger for fjernskrivebord",
"colorDepth16": "16-bit",
"colorDepth24": "24-bit",
"colorDepth32": "32-bit"
},
"admin": {
"enabled": {
"label": "Aktivér fjernskrivebord",
"description": "Tillad RDP-, VNC- og Telnet-forbindelser via guacd."
},
"guacdUrl": {
"label": "guacd-URL",
"description": "Hvor guacd lytter som host:port. Miljøvariablerne GUACD_URL, GUACD_HOST og GUACD_PORT tilsidesætter dette.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "Landdistriktsudviklingsplan"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP-port"
},
"vncPort": {
"label": "VNC-port"
},
"telnetPort": {
"label": "Telnet-port"
},
"rdpSecurity": {
"label": "RDP-sikkerhedstilstand"
},
"rdpIgnoreCert": {
"label": "Ignorer certifikatfejl"
},
"guacamoleConfig": {
"label": "guacd-indstillinger"
},
"enableToolbar": {
"label": "Værktøjslinjen til fjernskrivebord",
"description": "Vis værktøjslinjen i RDP-, VNC- og Telnet-sessioner."
},
"displayGroup": "Vise",
"displayDefaultsTab": "Fjernskrivebord"
}
},
"palette": {
"connectRdp": "Opret forbindelse med RDP",
"connectVnc": "Opret forbindelse med VNC",
"connectTelnet": "Opret forbindelse med Telnet"
},
"permissions": {
"sessions": {
"title": "Del fjernskrivebordssessioner",
"description": "Lad sessionsdeling og samarbejdsrum vise denne brugers RDP-, VNC- og Telnet-sessioner til andre."
}
},
"errors": {
"remoteServerRequired": "Fjernserver kræves. Opret forbindelse til en fjernserver i Indstillinger for at bruge denne forbindelsestype."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Verbinde mit {{type}}-Sitzung...",
"checkingGuacd": "Überprüfung des Remotedesktopdienstes...",
"requestingToken": "Verbindungstoken {{type}} wird angefordert...",
"openingSession": "Öffne {{type}} Sitzung...",
"negotiating": "Verhandlung {{type}} Handschlag...",
"sessionReady": "{{type}} Sitzung bereit",
"connectionError": "Verbindungsfehler",
"connectionFailed": "Verbindung fehlgeschlagen",
"failedToConnect": "Fehler beim Abrufen des Verbindungstokens",
"hostNotFound": "Host nicht gefunden",
"noHostSelected": "Kein Host ausgewählt",
"reconnect": "Erneut verbinden",
"retry": "Erneut versuchen",
"guacdUnavailable": "Der Remote-Desktop-Dienst (guacd) ist nicht verfügbar. Bitte stellen Sie sicher, dass guacd ausgeführt wird, erreichbar ist und in den Admin-Einstellungen richtig konfiguriert ist.",
"credentialPromptTitle": "Geben Sie die RDP-Anmeldeinformationen ein.",
"credentialPromptDescription": "Dieser Host ist so konfiguriert, dass er beim Verbindungsaufbau zur Eingabe von Anmeldeinformationen auffordert. Diese werden nur für diese Sitzung verwendet und nicht gespeichert.",
"connect": "Verbinden",
"ctrlAltDel": "Strg+Alt+Entf",
"files": {
"title": "Laufwerksdateien",
"parent": "Übergeordnetes Verzeichnis",
"refresh": "Aktualisieren",
"empty": "Dieser Ordner ist leer",
"listFailed": "Dieser Ordner konnte nicht gelesen werden",
"download": "Herunterladen",
"downloadFailed": "Download fehlgeschlagen {{name}}",
"upload": "Dateien hochladen",
"uploading": "Lade {{name}} hoch...",
"uploaded": "Hochgeladen {{name}}",
"uploadFailed": "Hochladen fehlgeschlagen {{name}}",
"uploadDisabled": "Uploads sind für diese Verbindung deaktiviert.",
"driveUnavailable": "Aktivieren Sie die RDP-Laufwerksumleitung, bevor Sie Dateien hochladen.",
"driveNotWritable": "guacd kann nicht in den Laufwerksordner schreiben. Überprüfen Sie GUACD_DRIVE_PATH (oder den Laufwerkspfad des Hosts) und dessen Berechtigungen.",
"dropToUpload": "Dateien zum Hochladen per Drag & Drop ablegen"
},
"toolbar": {
"ctrlAltDel": "Strg+Alt+Entf",
"winL": "Win+L (Bildschirm sperren)",
"winKey": "Windows-Taste",
"superL": "Super+L (Sperrbildschirm)",
"superKey": "Super Key",
"super": "Super",
"cmdL": "Cmd+L (Sperrbildschirm)",
"cmdKey": "Befehlstaste",
"cmd": "Cmd",
"ctrl": "Strg",
"alt": "Alt",
"shift": "Umschalt",
"win": "Win",
"stickyActive": "{{key}} (eingerastet – zum Lösen klicken)",
"stickyInactive": "{{key}} (zum Einrasten klicken)",
"esc": "Escape",
"tab": "Tab",
"home": "Startseite",
"end": "Ende",
"pageUp": "Bild auf",
"pageDown": "Bild ab",
"arrowUp": "Pfeil nach oben",
"arrowDown": "Pfeil nach unten",
"arrowLeft": "Pfeil nach links",
"arrowRight": "Pfeil nach rechts",
"fnToggle": "Funktionstasten",
"reconnect": "Sitzung erneut verbinden",
"collapse": "Symbolleiste einklappen",
"expand": "Symbolleiste ausklappen",
"dragHandle": "Zum Neupositionieren ziehen",
"hide": "Für diese Sitzung ausblenden. Um es dauerhaft zu deaktivieren, öffnen Sie Host-Einstellungen → Allgemein → Verbindungssymbolleiste.",
"zoomIn": "Vergrößern",
"zoomOut": "Herauszoomen",
"resetZoom": "Zoom zurücksetzen",
"fitToScreen": "An Bildschirm anpassen",
"switchToTrackpad": "In den Trackpad-Modus wechseln (Zum Bewegen des Cursors ziehen, zum Klicken tippen)",
"switchToTouch": "Wechseln Sie in den Touch-Modus (tippen Sie direkt auf die Stelle, an der Sie klicken möchten)."
},
"disabled": "Die Remotedesktopverbindung ist deaktiviert. Ein Administrator kann sie in den Einstellungen aktivieren."
},
"hosts": {
"nativeRdpOpened": "In Windows-Remotedesktop geöffnet",
"nativeRdpFailed": "Windows-Remotedesktop konnte nicht geöffnet werden",
"openNativeRdp": "In Windows-Remotedesktop öffnen",
"guac": {
"toolbar": "Symbolleiste",
"connection": "Verbindung",
"authentication": "Authentifizierung",
"storedCredential": "Gespeicherte Anmeldeinformation",
"authMethod": "Authentifizierungsmethode",
"authTypeDirect": "Direkt",
"authTypeCredential": "Anmeldeinformation",
"authTypeNone": "Keine",
"authTypeNoneDesc": "Es werden keine Zugangsdaten gespeichert. Sie werden bei jeder Verbindung zur Eingabe eines Benutzernamens und eines Passworts aufgefordert; diese werden nicht gespeichert.",
"selectCredential": "Anmeldeinformation auswählen...",
"connectionSettings": "Verbindungseinstellungen",
"displaySettings": "Anzeigeeinstellungen",
"audioSettings": "Audioeinstellungen",
"rdpPerformance": "RDP-Leistung",
"deviceRedirection": "Geräteumleitung",
"session": "Sitzung",
"gateway": "Gateway",
"remoteApp": "RemoteApp",
"clipboard": "Zwischenablage",
"sessionRecording": "Sitzungsaufzeichnung",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "VNC-Einstellungen",
"terminalSettings": "Terminaleinstellungen",
"rdpPort": "RDP-Port",
"username": "Benutzername",
"password": "Passwort",
"passwordSaved": "Passwort gespeichert, zum Ändern eingeben",
"domain": "Domäne",
"securityMode": "Sicherheitsmodus",
"width": "Breite",
"height": "Höhe",
"dpi": "DPI",
"clientName": "Client-Name",
"initialProgram": "Startprogramm",
"serverLayout": "Tastaturlayout",
"timezone": "Zeitzone",
"loadBalanceInfo": "Lastverteilungsinfo / Cookie",
"loadBalanceInfoDesc": "RD-Verbindungsbroker-Cookie für den Lastausgleich einer RDS-Farm (z.B. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacd-Proxy",
"guacdHostname": "guacd-Host",
"guacdHostnamePlaceholder": "Globaler Standard",
"guacdPort": "guacd-Port",
"guacdProxyDesc": "Überschreibt die globale guacd-Instanz für diese Verbindung. Leer lassen, um die global konfigurierte guacd zu verwenden.",
"gatewayHostname": "Gateway-Hostname",
"gatewayPort": "Gateway-Port",
"gatewayUsername": "Gateway-Benutzername",
"gatewayPassword": "Gateway-Passwort",
"gatewayDomain": "Gateway-Domäne",
"remoteAppProgram": "RemoteApp-Programm",
"workingDirectory": "Arbeitsverzeichnis",
"arguments": "Argumente",
"normalizeLineEndings": "Zeilenenden normalisieren",
"macAddress": "MAC-Adresse",
"broadcastAddress": "Broadcast-Adresse",
"udpPort": "UDP-Port",
"waitTimeS": "Wartezeit (s)",
"driveName": "Laufwerksname",
"drivePath": "Laufwerkspfad",
"ignoreCertificate": "Zertifikat ignorieren",
"ignoreCertificateDesc": "Verbindungen zu Hosts mit selbstsignierten Zertifikaten erlauben",
"forceLossless": "Verlustfreie Übertragung erzwingen",
"forceLosslessDesc": "Verlustfreie Bildcodierung erzwingen (höhere Qualität, mehr Bandbreite)",
"disableAudio": "Audio deaktivieren",
"disableAudioDesc": "Gesamten Ton der Remote-Sitzung stummschalten",
"enableAudioInput": "Audioeingabe aktivieren (Mikrofon)",
"enableAudioInputDesc": "Lokales Mikrofon an die Remote-Sitzung weiterleiten",
"wallpaper": "Hintergrundbild",
"wallpaperDesc": "Desktop-Hintergrundbild anzeigen (Deaktivierung verbessert die Leistung)",
"theming": "Designs",
"themingDesc": "Visuelle Designs und Stile aktivieren",
"fontSmoothing": "Schriftglättung",
"fontSmoothingDesc": "ClearType-Schriftdarstellung aktivieren",
"fullWindowDrag": "Vollständiges Fensterziehen",
"fullWindowDragDesc": "Fensterinhalt beim Ziehen anzeigen",
"desktopComposition": "Desktopkomposition",
"desktopCompositionDesc": "Aero-Glaseffekte aktivieren",
"menuAnimations": "Menüanimationen",
"menuAnimationsDesc": "Ein- und Ausblendeanimationen für Menüs aktivieren",
"disableBitmapCaching": "Bitmap-Caching deaktivieren",
"disableBitmapCachingDesc": "Bitmap-Cache deaktivieren (kann bei Grafikfehlern helfen)",
"disableOffscreenCaching": "Offscreen-Caching deaktivieren",
"disableOffscreenCachingDesc": "Offscreen-Cache deaktivieren",
"disableGlyphCaching": "Glyphen-Caching deaktivieren",
"disableGlyphCachingDesc": "Glyphen-Cache deaktivieren",
"enableGfx": "GFX aktivieren",
"enableGfxDesc": "RemoteFX-Grafikpipeline verwenden",
"enablePrinting": "Drucken aktivieren",
"enablePrintingDesc": "Lokale Drucker an die Remote-Sitzung umleiten",
"enableDriveRedirection": "Laufwerksumleitung aktivieren",
"enableDriveRedirectionDesc": "Lokalen Ordner als Laufwerk in der Remote-Sitzung einbinden",
"createDrivePath": "Laufwerkspfad erstellen",
"createDrivePathDesc": "Ordner automatisch erstellen, falls nicht vorhanden",
"disableDownload": "Download deaktivieren",
"disableDownloadDesc": "Herunterladen von Dateien aus der entfernten Sitzung verhindern",
"disableUpload": "Upload deaktivieren",
"disableUploadDesc": "Hochladen von Dateien in die entfernte Sitzung verhindern",
"enableTouch": "Touch aktivieren",
"enableTouchDesc": "Touch-Eingabeweiterleitung aktivieren",
"consoleSession": "Konsolensitzung",
"consoleSessionDesc": "Mit der Konsole (Sitzung 0) statt mit einer neuen Sitzung verbinden",
"sendWolPacket": "WOL-Paket senden",
"sendWolPacketDesc": "Magic Packet senden, um diesen Host vor dem Verbinden aufzuwecken",
"disableCopy": "Kopieren deaktivieren",
"disableCopyDesc": "Kopieren von Text aus der entfernten Sitzung verhindern",
"disablePaste": "Einfügen deaktivieren",
"disablePasteDesc": "Einfügen von Text in die entfernte Sitzung verhindern",
"createPathIfMissing": "Pfad erstellen, falls nicht vorhanden",
"createPathIfMissingDesc": "Aufzeichnungsverzeichnis automatisch erstellen",
"excludeOutput": "Ausgabe ausschließen",
"excludeOutputDesc": "Bildschirmausgabe nicht aufzeichnen (nur Metadaten)",
"excludeMouse": "Maus ausschließen",
"excludeMouseDesc": "Mausbewegungen nicht aufzeichnen",
"includeKeystrokes": "Tastatureingaben einschließen",
"includeKeystrokesDesc": "Rohe Tastatureingaben zusätzlich zur Bildschirmausgabe aufzeichnen",
"vncPort": "VNC-Port",
"vncPassword": "VNC-Passwort",
"vncUsernameOptional": "Benutzername (optional)",
"vncLeaveBlank": "Leer lassen, falls nicht erforderlich",
"cursorMode": "Cursor-Modus",
"swapRedBlue": "Rot/Blau tauschen",
"swapRedBlueDesc": "Die roten und blauen Farbkanäle tauschen (behebt einige Farbprobleme)",
"readOnly": "Schreibgeschützt",
"readOnlyDesc": "Bildschirm anzeigen, ohne Eingaben zu senden",
"telnetPort": "Telnet-Port",
"terminalType": "Terminaltyp",
"fontName": "Schriftname",
"fontSize": "Schriftgröße",
"colorScheme": "Farbschema",
"backspaceKey": "Backspace-Taste",
"colorDepth": "Farbtiefe",
"resizeMethod": "Skalierungsmethode",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "Remotedesktop",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Virtuelles Netzwerk",
"unencryptedShell": "Unverschlüsselte Shell",
"remoteDesktopNotes": "Notizen",
"userDefaultsNote": "Einstellungen, die Sie unverändert lassen, verwenden die Standardeinstellungen Ihres Hostsystems.",
"connectionOriginNote": "In der Desktop-App verwenden RDP, VNC und Telnet den Remote-Server, es sei denn, der Host ist auf „Dieses Gerät“ eingestellt. In diesem Fall wird ein guacd-Server benötigt, den dieser Rechner erreichen kann. Legen Sie einen solchen Server in den Remote-Desktop-Einstellungen oder pro Host unter „guacd-Proxy“ fest.",
"docsLink": "Dokumentation anzeigen",
"connectionOriginGuacamoleNote": "RDP, VNC und Telnet verwenden den Remote-Server, es sei denn, Sie wählen „Dieses Gerät“. Dafür ist ein guacd-Server erforderlich, den dieser Rechner erreichen kann. Legen Sie einen solchen Server in den guacd-URL-Einstellungen oder pro Host unter „guacd-Proxy“ fest."
},
"onboarding": {
"feature_desktop": "Remote-Desktop",
"feature_desktop_desc": "Öffnen Sie RDP-, VNC- und Telnet-Sitzungen in einem Tab, genau wie bei SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Farbtiefe",
"description": "Wird für RDP-Hosts verwendet, die die Standardeinstellung beibehalten."
},
"resizeMethod": {
"label": "Skalierungsmethode",
"description": "Wie sich der Bildschirm der Fernbedienung an die Fenstergröße anpasst."
},
"resizeDisplayUpdate": "Anzeigeaktualisierung",
"resizeReconnect": "Erneut verbinden",
"inherit": "Erben",
"on": "Ein",
"off": "Aus",
"forceLossless": {
"label": "Verlustfreie Übertragung erzwingen",
"description": "Verlustfreie Bildcodierung erzwingen (höhere Qualität, mehr Bandbreite)"
},
"enableWallpaper": {
"label": "Hintergrundbild",
"description": "Desktop-Hintergrundbild anzeigen (Deaktivierung verbessert die Leistung)"
},
"enableFontSmoothing": {
"label": "Schriftglättung",
"description": "ClearType-Schriftdarstellung aktivieren"
},
"enableDesktopComposition": {
"label": "Desktopkomposition",
"description": "Aero-Glaseffekte aktivieren"
},
"disableAudio": {
"label": "Audio deaktivieren",
"description": "Gesamten Ton der Remote-Sitzung stummschalten"
},
"enablePrinting": {
"label": "Drucken aktivieren",
"description": "Lokale Drucker an die Remote-Sitzung umleiten"
},
"enableDrive": {
"label": "Laufwerksumleitung aktivieren",
"description": "Lokalen Ordner als Laufwerk in der Remote-Sitzung einbinden"
},
"disableCopy": {
"label": "Kopieren deaktivieren",
"description": "Kopieren von Text aus der entfernten Sitzung verhindern"
},
"disablePaste": {
"label": "Einfügen deaktivieren",
"description": "Einfügen von Text in die entfernte Sitzung verhindern"
},
"group": "Standardeinstellungen für Remotedesktop",
"colorDepth16": "16-Bit",
"colorDepth24": "24-Bit",
"colorDepth32": "32-Bit"
},
"admin": {
"enabled": {
"label": "Remotedesktop aktivieren",
"description": "RDP-, VNC- und Telnet-Verbindungen über guacd zulassen."
},
"guacdUrl": {
"label": "guacd-URL",
"description": "Der Hostname „guacd“ gibt an, wo er lauscht. Die Umgebungsvariablen GUACD_URL, GUACD_HOST und GUACD_PORT überschreiben diese Angabe.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP-Port"
},
"vncPort": {
"label": "VNC-Port"
},
"telnetPort": {
"label": "Telnet-Port"
},
"rdpSecurity": {
"label": "RDP-Sicherheitsmodus"
},
"rdpIgnoreCert": {
"label": "Zertifikatsfehler ignorieren"
},
"guacamoleConfig": {
"label": "guacd-Einstellungen"
},
"enableToolbar": {
"label": "Symbolleiste für Remote-Desktop",
"description": "Die Symbolleiste wird in RDP-, VNC- und Telnet-Sitzungen angezeigt."
},
"displayGroup": "Anzeige",
"displayDefaultsTab": "Remotedesktop"
}
},
"palette": {
"connectRdp": "Mit RDP verbinden",
"connectVnc": "Mit VNC verbinden",
"connectTelnet": "Mit Telnet verbinden"
},
"permissions": {
"sessions": {
"title": "Remote-Desktop-Sitzungen freigeben",
"description": "Erlauben Sie, dass die RDP-, VNC- und Telnet-Sitzungen dieses Benutzers anderen Benutzern angezeigt werden, wenn diese die Sitzungsfreigabe und Kollaborationsräume nutzen."
}
},
"errors": {
"remoteServerRequired": "Remote-Server erforderlich. Um diesen Verbindungstyp zu verwenden, verbinden Sie einen Remote-Server in den Einstellungen."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Σύνδεση σε συνεδρία {{type}}...",
"checkingGuacd": "Έλεγχος υπηρεσίας απομακρυσμένης επιφάνειας εργασίας...",
"requestingToken": "Αίτημα διακριτικού σύνδεσης {{type}}...",
"openingSession": "Άνοιγμα συνεδρίας {{type}}...",
"negotiating": "Διαπραγμάτευση χειραψίας {{type}}...",
"sessionReady": "{{type}} συνεδρία έτοιμη",
"connectionError": "Σφάλμα σύνδεσης",
"connectionFailed": "Η σύνδεση απέτυχε",
"failedToConnect": "Αποτυχία λήψης διακριτικού σύνδεσης",
"hostNotFound": "Δεν βρέθηκε κεντρικός υπολογιστής",
"noHostSelected": "Δεν έχει επιλεγεί κεντρικός υπολογιστής",
"reconnect": "Επανασύνδεση",
"retry": "Δοκιμάζω πάλι",
"guacdUnavailable": "Η υπηρεσία απομακρυσμένης επιφάνειας εργασίας (guacd) δεν είναι διαθέσιμη. Βεβαιωθείτε ότι το guacd εκτελείται και είναι προσβάσιμο, καθώς και ότι έχει ρυθμιστεί σωστά στις ρυθμίσεις διαχειριστή.",
"credentialPromptTitle": "Εισαγάγετε τα διαπιστευτήρια RDP",
"credentialPromptDescription": "Αυτός ο κεντρικός υπολογιστής έχει ρυθμιστεί να ζητά διαπιστευτήρια κατά τη σύνδεση. Χρησιμοποιούνται μόνο για αυτήν την περίοδο σύνδεσης και δεν αποθηκεύονται.",
"connect": "Συνδέω",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Αρχεία μονάδας δίσκου",
"parent": "Γονικός κατάλογος",
"refresh": "Φρεσκάρω",
"empty": "Αυτός ο φάκελος είναι κενός",
"listFailed": "Αποτυχία ανάγνωσης αυτού του φακέλου",
"download": "Λήψη",
"downloadFailed": "Αποτυχία λήψης {{name}}",
"upload": "Μεταφόρτωση αρχείων",
"uploading": "Μεταφόρτωση {{name}}...",
"uploaded": "Μεταφορτώθηκε {{name}}",
"uploadFailed": "Αποτυχία μεταφόρτωσης {{name}}",
"uploadDisabled": "Οι μεταφορτώσεις είναι απενεργοποιημένες για αυτήν τη σύνδεση",
"driveUnavailable": "Ενεργοποίηση ανακατεύθυνσης μονάδας δίσκου RDP πριν από την αποστολή αρχείων",
"driveNotWritable": "Το guacd δεν μπορεί να γράψει στον φάκελο της μονάδας δίσκου. Ελέγξτε το GUACD_DRIVE_PATH (ή τη διαδρομή μονάδας δίσκου του κεντρικού υπολογιστή) και τα δικαιώματά του.",
"dropToUpload": "Αποθέστε αρχεία για μεταφόρτωση"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Κλείδωμα οθόνης)",
"winKey": "Κλειδί των Windows",
"superL": "Super+L (Κλείδωμα οθόνης)",
"superKey": "Σούπερ Κλειδί",
"super": "Σούπερ",
"cmdL": "Cmd+L (Κλείδωμα οθόνης)",
"cmdKey": "Πλήκτρο εντολών",
"cmd": "Εκτελεστής",
"ctrl": "Ctrl",
"alt": "Εναλλακτική",
"shift": "Αλλαγή",
"win": "Νίκη",
"stickyActive": "{{key}} (κλειδωμένο - κάντε κλικ για απελευθέρωση)",
"stickyInactive": "{{key}} (κάντε κλικ για να κλειδώσετε)",
"esc": "Διαφυγή",
"tab": "Αυτί",
"home": "Σπίτι",
"end": "Τέλος",
"pageUp": "Σελίδα προς τα πάνω",
"pageDown": "Σελίδα προς τα κάτω",
"arrowUp": "Βέλος προς τα πάνω",
"arrowDown": "Βέλος προς τα κάτω",
"arrowLeft": "Βέλος αριστερά",
"arrowRight": "Βέλος δεξιά",
"fnToggle": "Πλήκτρα λειτουργιών",
"reconnect": "Επανασύνδεση περιόδου σύνδεσης",
"collapse": "Σύμπτυξη γραμμής εργαλείων",
"expand": "Ανάπτυξη γραμμής εργαλείων",
"dragHandle": "Σύρετε για αλλαγή θέσης",
"hide": "Απόκρυψη για αυτήν την περίοδο λειτουργίας. Για να την απενεργοποιήσετε μόνιμα, ανοίξτε τις Ρυθμίσεις κεντρικού υπολογιστή → Γενικά → Γραμμή εργαλείων σύνδεσης.",
"zoomIn": "Μεγέθυνση",
"zoomOut": "Σμίκρυνση",
"resetZoom": "Επαναφορά ζουμ",
"fitToScreen": "Προσαρμογή στην οθόνη",
"switchToTrackpad": "Μετάβαση σε λειτουργία trackpad (σύρσιμο για μετακίνηση του κέρσορα, άγγιγμα για κλικ)",
"switchToTouch": "Μετάβαση σε λειτουργία αφής (πατήστε απευθείας εκεί που θέλετε να κάνετε κλικ)"
},
"disabled": "Η Απομακρυσμένη επιφάνεια εργασίας είναι απενεργοποιημένη. Ένας διαχειριστής μπορεί να την ενεργοποιήσει στις Ρυθμίσεις."
},
"hosts": {
"nativeRdpOpened": "Άνοιξε στην Απομακρυσμένη επιφάνεια εργασίας των Windows",
"nativeRdpFailed": "Αποτυχία ανοίγματος της Απομακρυσμένης επιφάνειας εργασίας των Windows",
"openNativeRdp": "Άνοιγμα στην Απομακρυσμένη επιφάνεια εργασίας των Windows",
"guac": {
"toolbar": "Γραμμή εργαλείων",
"connection": "Σύνδεση",
"authentication": "Πιστοποίηση",
"storedCredential": "Αποθηκευμένα διαπιστευτήρια",
"authMethod": "Μέθοδος Εξουσιοδότησης",
"authTypeDirect": "Απευθείας",
"authTypeCredential": "Πιστοποιητικό",
"authTypeNone": "Κανένας",
"authTypeNoneDesc": "Δεν αποθηκεύονται διαπιστευτήρια. Θα σας ζητείται όνομα χρήστη και κωδικός πρόσβασης κάθε φορά που συνδέεστε. Δεν αποθηκεύονται.",
"selectCredential": "Επιλέξτε ένα πιστοποιητικό...",
"connectionSettings": "Ρυθμίσεις σύνδεσης",
"displaySettings": "Ρυθμίσεις οθόνης",
"audioSettings": "Ρυθμίσεις ήχου",
"rdpPerformance": "Απόδοση RDP",
"deviceRedirection": "Ανακατεύθυνση συσκευής",
"session": "Συνεδρίαση",
"gateway": "Είσοδος πυλών",
"remoteApp": "Απομακρυσμένη εφαρμογή",
"clipboard": "Πρόχειρο",
"sessionRecording": "Ηχογράφηση συνεδρίας",
"wakeOnLan": "Αφύπνιση μέσω LAN",
"vncSettings": "Ρυθμίσεις VNC",
"terminalSettings": "Ρυθμίσεις τερματικού",
"rdpPort": "Θύρα RDP",
"username": "Όνομα χρήστη",
"password": "Σύνθημα",
"passwordSaved": "Ο κωδικός πρόσβασης αποθηκεύτηκε, πληκτρολογήστε για να τον αλλάξετε",
"domain": "Πεδίο ορισμού",
"securityMode": "Λειτουργία ασφαλείας",
"width": "Πλάτος",
"height": "Υψος",
"dpi": "DPI",
"clientName": "Όνομα πελάτη",
"initialProgram": "Αρχικό Πρόγραμμα",
"serverLayout": "Διάταξη διακομιστή",
"timezone": "Ζώνη ώρας",
"loadBalanceInfo": "Πληροφορίες ισορροπίας φορτίου / Cookie",
"loadBalanceInfoDesc": "Cookie RD Connection Broker για εξισορρόπηση φόρτου συστοιχίας RDS (π.χ. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacd Proxy",
"guacdHostname": "οικοδεσπότης guacd",
"guacdHostnamePlaceholder": "Παγκόσμια προεπιλογή",
"guacdPort": "Λιμάνι Guacd",
"guacdProxyDesc": "Παρακάμψτε την καθολική παρουσία guacd για αυτήν τη σύνδεση. Αφήστε το κενό για να χρησιμοποιήσετε την καθολικά διαμορφωμένη παρουσία guacd.",
"gatewayHostname": "Όνομα κεντρικού υπολογιστή πύλης",
"gatewayPort": "Λιμάνι Πύλης",
"gatewayUsername": "Όνομα χρήστη πύλης",
"gatewayPassword": "Κωδικός πρόσβασης πύλης",
"gatewayDomain": "Τομέας πύλης",
"remoteAppProgram": "Πρόγραμμα RemoteApp",
"workingDirectory": "Κατάλογος εργασίας",
"arguments": "Επιχειρήματα",
"normalizeLineEndings": "Κανονικοποίηση τελών γραμμών",
"macAddress": "Διεύθυνση MAC",
"broadcastAddress": "Διεύθυνση μετάδοσης",
"udpPort": "Θύρα UDP",
"waitTimeS": "Χρόνος αναμονής (s)",
"driveName": "Όνομα μονάδας δίσκου",
"drivePath": "Μονοπάτι οδήγησης",
"ignoreCertificate": "Αγνόηση πιστοποιητικού",
"ignoreCertificateDesc": "Επιτρέψτε συνδέσεις σε κεντρικούς υπολογιστές με αυτο-υπογεγραμμένα πιστοποιητικά",
"forceLossless": "Δύναμη χωρίς απώλειες",
"forceLosslessDesc": "Επιβολή κωδικοποίησης εικόνας χωρίς απώλειες (υψηλότερη ποιότητα, μεγαλύτερο εύρος ζώνης)",
"disableAudio": "Απενεργοποίηση ήχου",
"disableAudioDesc": "Σίγαση όλου του ήχου από την απομακρυσμένη συνεδρία",
"enableAudioInput": "Ενεργοποίηση εισόδου ήχου (Μικρόφωνο)",
"enableAudioInputDesc": "Προώθηση τοπικού μικροφώνου στην απομακρυσμένη συνεδρία",
"wallpaper": "Ταπετσαρία",
"wallpaperDesc": "Εμφάνιση ταπετσαρίας επιφάνειας εργασίας (η απενεργοποίηση βελτιώνει την απόδοση)",
"theming": "Θέμα",
"themingDesc": "Ενεργοποίηση οπτικών θεμάτων και στυλ",
"fontSmoothing": "Εξομάλυνση γραμματοσειράς",
"fontSmoothingDesc": "Ενεργοποίηση απόδοσης γραμματοσειράς ClearType",
"fullWindowDrag": "Πλήρες σύρσιμο παραθύρου",
"fullWindowDragDesc": "Εμφάνιση περιεχομένων παραθύρου κατά τη μεταφορά",
"desktopComposition": "Σύνθεση επιφάνειας εργασίας",
"desktopCompositionDesc": "Ενεργοποίηση εφέ Aero glass",
"menuAnimations": "Κινήσεις μενού",
"menuAnimationsDesc": "Ενεργοποίηση κινούμενων εικόνων fade και slide μενού",
"disableBitmapCaching": "Απενεργοποίηση προσωρινής αποθήκευσης bitmap",
"disableBitmapCachingDesc": "Απενεργοποίηση της προσωρινής μνήμης bitmap (μπορεί να βοηθήσει με προβλήματα)",
"disableOffscreenCaching": "Απενεργοποίηση προσωρινής αποθήκευσης εκτός οθόνης",
"disableOffscreenCachingDesc": "Απενεργοποίηση προσωρινής μνήμης εκτός οθόνης",
"disableGlyphCaching": "Απενεργοποίηση προσωρινής αποθήκευσης γλύφων",
"disableGlyphCachingDesc": "Απενεργοποίηση προσωρινής μνήμης glyph",
"enableGfx": "Ενεργοποίηση GFX",
"enableGfxDesc": "Χρήση αγωγού γραφικών RemoteFX",
"enablePrinting": "Ενεργοποίηση εκτύπωσης",
"enablePrintingDesc": "Ανακατεύθυνση τοπικών εκτυπωτών στην απομακρυσμένη συνεδρία",
"enableDriveRedirection": "Ενεργοποίηση ανακατεύθυνσης μονάδας δίσκου",
"enableDriveRedirectionDesc": "Αντιστοίχιση τοπικού φακέλου ως μονάδας δίσκου στην απομακρυσμένη συνεδρία",
"createDrivePath": "Δημιουργία διαδρομής μονάδας δίσκου",
"createDrivePathDesc": "Αυτόματη δημιουργία του φακέλου εάν δεν υπάρχει",
"disableDownload": "Απενεργοποίηση λήψης",
"disableDownloadDesc": "Αποτροπή λήψης αρχείων από την απομακρυσμένη συνεδρία",
"disableUpload": "Απενεργοποίηση μεταφόρτωσης",
"disableUploadDesc": "Αποτροπή μεταφόρτωσης αρχείων στην απομακρυσμένη συνεδρία",
"enableTouch": "Ενεργοποίηση αφής",
"enableTouchDesc": "Ενεργοποίηση προώθησης εισόδου αφής",
"consoleSession": "Συνεδρία κονσόλας",
"consoleSessionDesc": "Σύνδεση στην κονσόλα (περίοδος λειτουργίας 0) αντί για νέα περίοδο λειτουργίας",
"sendWolPacket": "Αποστολή πακέτου WOL",
"sendWolPacketDesc": "Στείλτε ένα μαγικό πακέτο για να αφυπνίσετε αυτόν τον κεντρικό υπολογιστή πριν από τη σύνδεση",
"disableCopy": "Απενεργοποίηση αντιγραφής",
"disableCopyDesc": "Αποτροπή αντιγραφής κειμένου από την απομακρυσμένη συνεδρία",
"disablePaste": "Απενεργοποίηση επικόλλησης",
"disablePasteDesc": "Αποτροπή επικόλλησης κειμένου στην απομακρυσμένη συνεδρία",
"createPathIfMissing": "Δημιουργία διαδρομής εάν λείπει",
"createPathIfMissingDesc": "Αυτόματη δημιουργία του καταλόγου ηχογράφησης",
"excludeOutput": "Εξαίρεση εξόδου",
"excludeOutputDesc": "Μην καταγράφετε την έξοδο οθόνης (μόνο μεταδεδομένα)",
"excludeMouse": "Εξαίρεση ποντικιού",
"excludeMouseDesc": "Μην καταγράφετε τις κινήσεις του ποντικιού",
"includeKeystrokes": "Συμπερίληψη πληκτρολογήσεων",
"includeKeystrokesDesc": "Εγγραφή ακατέργαστων πληκτρολογήσεων εκτός από την έξοδο οθόνης",
"vncPort": "Θύρα VNC",
"vncPassword": "Κωδικός πρόσβασης VNC",
"vncUsernameOptional": "Όνομα χρήστη (προαιρετικό)",
"vncLeaveBlank": "Αφήστε κενό εάν δεν απαιτείται",
"cursorMode": "Λειτουργία δρομέα",
"swapRedBlue": "Εναλλαγή Κόκκινου/Μπλε",
"swapRedBlueDesc": "Εναλλαγή των καναλιών κόκκινου και μπλε χρώματος (διορθώνει ορισμένα προβλήματα χρώματος)",
"readOnly": "Μόνο για ανάγνωση",
"readOnlyDesc": "Δείτε την οθόνη του τηλεχειριστηρίου χωρίς να στείλετε καμία πληροφορία",
"telnetPort": "Θύρα Telnet",
"terminalType": "Τύπος τερματικού",
"fontName": "Όνομα γραμματοσειράς",
"fontSize": "Μέγεθος γραμματοσειράς",
"colorScheme": "Χρωματικό σχέδιο",
"backspaceKey": "Πλήκτρο Backspace",
"colorDepth": "Βάθος χρώματος",
"resizeMethod": "Μέθοδος αλλαγής μεγέθους",
"driveNamePlaceholder": "Οδός Τέρμιξ"
},
"remoteDesktop": "Απομακρυσμένη επιφάνεια εργασίας",
"tabRdp": "ΠΑΑ",
"tabVnc": "VNC",
"tabTelnet": "Τέλνετ",
"virtualNetwork": "Εικονικό Δίκτυο",
"unencryptedShell": "Μη κρυπτογραφημένο κέλυφος",
"remoteDesktopNotes": "Σημειώσεις",
"userDefaultsNote": "Οι ρυθμίσεις που αφήνετε αμετάβλητες ακολουθούν τις προεπιλογές του κεντρικού υπολογιστή σας.",
"connectionOriginNote": "Στην εφαρμογή για υπολογιστές, τα RDP, VNC και Telnet χρησιμοποιούν τον απομακρυσμένο διακομιστή, εκτός εάν ο κεντρικός υπολογιστής έχει οριστεί σε Αυτή η συσκευή, η οποία απαιτεί ένα guacd που μπορεί να φτάσει σε αυτό το μηχάνημα. Ορίστε ένα στις ρυθμίσεις Απομακρυσμένης επιφάνειας εργασίας ή ανά κεντρικό υπολογιστή στην ενότητα Guacd Proxy.",
"docsLink": "Προβολή εγγράφων",
"connectionOriginGuacamoleNote": "Τα RDP, VNC και Telnet χρησιμοποιούν τον απομακρυσμένο διακομιστή, εκτός εάν επιλέξετε \"Αυτή η συσκευή\", κάτι που απαιτεί ένα guacd στο οποίο μπορεί να φτάσει αυτό το μηχάνημα. Ορίστε ένα στη ρύθμιση URL guacd ή ανά κεντρικό υπολογιστή στην ενότητα Guacd Proxy."
},
"onboarding": {
"feature_desktop": "Απομακρυσμένη επιφάνεια εργασίας",
"feature_desktop_desc": "Ανοίξτε τις συνεδρίες RDP, VNC και Telnet σε μια καρτέλα, όπως και το SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Βάθος χρώματος",
"description": "Χρησιμοποιείται για κεντρικούς υπολογιστές RDP που το αφήνουν στην προεπιλεγμένη τιμή τους."
},
"resizeMethod": {
"label": "Μέθοδος αλλαγής μεγέθους",
"description": "Πώς η απομακρυσμένη οθόνη ακολουθεί το μέγεθος του παραθύρου."
},
"resizeDisplayUpdate": "Ενημέρωση οθόνης",
"resizeReconnect": "Επανασύνδεση",
"inherit": "Κληρονομώ",
"on": "Επί",
"off": "Μακριά από",
"forceLossless": {
"label": "Δύναμη χωρίς απώλειες",
"description": "Επιβολή κωδικοποίησης εικόνας χωρίς απώλειες (υψηλότερη ποιότητα, μεγαλύτερο εύρος ζώνης)"
},
"enableWallpaper": {
"label": "Ταπετσαρία",
"description": "Εμφάνιση ταπετσαρίας επιφάνειας εργασίας (η απενεργοποίηση βελτιώνει την απόδοση)"
},
"enableFontSmoothing": {
"label": "Εξομάλυνση γραμματοσειράς",
"description": "Ενεργοποίηση απόδοσης γραμματοσειράς ClearType"
},
"enableDesktopComposition": {
"label": "Σύνθεση επιφάνειας εργασίας",
"description": "Ενεργοποίηση εφέ Aero glass"
},
"disableAudio": {
"label": "Απενεργοποίηση ήχου",
"description": "Σίγαση όλου του ήχου από την απομακρυσμένη συνεδρία"
},
"enablePrinting": {
"label": "Ενεργοποίηση εκτύπωσης",
"description": "Ανακατεύθυνση τοπικών εκτυπωτών στην απομακρυσμένη συνεδρία"
},
"enableDrive": {
"label": "Ενεργοποίηση ανακατεύθυνσης μονάδας δίσκου",
"description": "Αντιστοίχιση τοπικού φακέλου ως μονάδας δίσκου στην απομακρυσμένη συνεδρία"
},
"disableCopy": {
"label": "Απενεργοποίηση αντιγραφής",
"description": "Αποτροπή αντιγραφής κειμένου από την απομακρυσμένη συνεδρία"
},
"disablePaste": {
"label": "Απενεργοποίηση επικόλλησης",
"description": "Αποτροπή επικόλλησης κειμένου στην απομακρυσμένη συνεδρία"
},
"group": "Προεπιλογές απομακρυσμένης επιφάνειας εργασίας",
"colorDepth16": "16-bit",
"colorDepth24": "24-bit",
"colorDepth32": "32-bit"
},
"admin": {
"enabled": {
"label": "Ενεργοποίηση απομακρυσμένης επιφάνειας εργασίας",
"description": "Επιτρέψτε συνδέσεις RDP, VNC και Telnet μέσω του guacd."
},
"guacdUrl": {
"label": "URL guacd",
"description": "Όπου το guacd εκτελεί ακρόαση, ως host:port. Οι μεταβλητές περιβάλλοντος GUACD_URL, GUACD_HOST και GUACD_PORT το παρακάμπτουν.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "ΠΑΑ"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Τέλνετ"
},
"rdpPort": {
"label": "Θύρα RDP"
},
"vncPort": {
"label": "Θύρα VNC"
},
"telnetPort": {
"label": "Θύρα Telnet"
},
"rdpSecurity": {
"label": "Λειτουργία ασφαλείας RDP"
},
"rdpIgnoreCert": {
"label": "Αγνόηση σφαλμάτων πιστοποιητικών"
},
"guacamoleConfig": {
"label": "ρυθμίσεις guacd"
},
"enableToolbar": {
"label": "Γραμμή εργαλείων απομακρυσμένης επιφάνειας εργασίας",
"description": "Εμφάνιση της γραμμής εργαλείων σε συνεδρίες RDP, VNC και Telnet."
},
"displayGroup": "Επίδειξη",
"displayDefaultsTab": "Απομακρυσμένη επιφάνεια εργασίας"
}
},
"palette": {
"connectRdp": "Σύνδεση με το RDP",
"connectVnc": "Σύνδεση με VNC",
"connectTelnet": "Σύνδεση με Telnet"
},
"permissions": {
"sessions": {
"title": "Κοινή χρήση περιόδων λειτουργίας απομακρυσμένης επιφάνειας εργασίας",
"description": "Να επιτρέπεται η κοινή χρήση συνεδριών και οι χώροι συνεργασίας να εμφανίζουν τις συνεδρίες RDP, VNC και Telnet αυτού του χρήστη σε άλλους."
}
},
"errors": {
"remoteServerRequired": "Απαιτείται απομακρυσμένος διακομιστής. Συνδέστε έναν απομακρυσμένο διακομιστή στις Ρυθμίσεις για να χρησιμοποιήσετε αυτόν τον τύπο σύνδεσης."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Conectando a la sesión {{type}}...",
"checkingGuacd": "Comprobando el servicio de escritorio remoto...",
"requestingToken": "Solicitando el token de conexión {{type}}...",
"openingSession": "Abriendo {{type}} sesión...",
"negotiating": "Negociando el apretón de manos {{type}}...",
"sessionReady": "{{type}} sesión lista",
"connectionError": "Error de conexión",
"connectionFailed": "Conexión fallida",
"failedToConnect": "Error al obtener el token de conexión",
"hostNotFound": "Host no encontrado",
"noHostSelected": "Ningún host seleccionado",
"reconnect": "Reconectar",
"retry": "Reintentar",
"guacdUnavailable": "El servicio de escritorio remoto (guacd) no está disponible. Por favor, asegúrate de que guacd se está ejecutando, es accesible y está correctamente configurado en los ajustes de administración.",
"credentialPromptTitle": "Introduzca las credenciales de RDP",
"credentialPromptDescription": "Este host está configurado para solicitar credenciales al conectarse. Estas se utilizan únicamente para esta sesión y no se guardan.",
"connect": "Conectar",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Archivos de Drive",
"parent": "Directorio principal",
"refresh": "Actualizar",
"empty": "Esta carpeta está vacía",
"listFailed": "No se pudo leer esta carpeta.",
"download": "Descargar",
"downloadFailed": "Error al descargar {{name}}",
"upload": "Subir archivos",
"uploading": "Subiendo {{name}}...",
"uploaded": "Subido {{name}}",
"uploadFailed": "Error al cargar {{name}}",
"uploadDisabled": "Las cargas están deshabilitadas para esta conexión.",
"driveUnavailable": "Habilite la redirección de unidades RDP antes de cargar archivos.",
"driveNotWritable": "guacd no puede escribir en la carpeta de la unidad. Compruebe GUACD_DRIVE_PATH (o la ruta de la unidad del host) y sus permisos.",
"dropToUpload": "Arrastra y suelta los archivos para subirlos"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Bloquear pantalla)",
"winKey": "Tecla Windows",
"superL": "Super+L (Pantalla de bloqueo)",
"superKey": "Súper llave",
"super": "Súper",
"cmdL": "Cmd+L (Pantalla de bloqueo)",
"cmdKey": "Tecla de comando",
"cmd": "Comando",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Mayús",
"win": "Win",
"stickyActive": "{{key}} (activado - clic para soltar)",
"stickyInactive": "{{key}} (clic para activar)",
"esc": "Escape",
"tab": "Tab",
"home": "Inicio",
"end": "Fin",
"pageUp": "Re Pág",
"pageDown": "Av Pág",
"arrowUp": "Flecha arriba",
"arrowDown": "Flecha abajo",
"arrowLeft": "Flecha izquierda",
"arrowRight": "Flecha derecha",
"fnToggle": "Teclas de función",
"reconnect": "Reconectar sesión",
"collapse": "Contraer barra de herramientas",
"expand": "Expandir barra de herramientas",
"dragHandle": "Arrastrar para reposicionar",
"hide": "Ocultar para esta sesión. Para desactivarlo permanentemente, abra Configuración del host → General → Barra de herramientas de conexión.",
"zoomIn": "Dar un golpe de zoom",
"zoomOut": "Aleja la vista",
"resetZoom": "Restablecer zoom",
"fitToScreen": "Ajustar a la pantalla",
"switchToTrackpad": "Cambia al modo panel táctil (arrastra para mover el cursor, toca para hacer clic).",
"switchToTouch": "Cambia al modo táctil (pulsa directamente donde quieras hacer clic)."
},
"disabled": "El Escritorio remoto está desactivado. Un administrador puede activarlo en la configuración."
},
"hosts": {
"nativeRdpOpened": "Abierto en Escritorio remoto de Windows",
"nativeRdpFailed": "No se pudo abrir el Escritorio remoto de Windows.",
"openNativeRdp": "Abrir en Escritorio remoto de Windows",
"guac": {
"toolbar": "Barra de herramientas",
"connection": "Conexión",
"authentication": "Autenticación",
"storedCredential": "Credencial almacenada",
"authMethod": "Método de autenticación",
"authTypeDirect": "Directa",
"authTypeCredential": "Credencial",
"authTypeNone": "Ninguna",
"authTypeNoneDesc": "No se almacenan credenciales. Se le solicitará un nombre de usuario y una contraseña cada vez que se conecte; estos no se guardan.",
"selectCredential": "Seleccionar una credencial...",
"connectionSettings": "Configuración de conexión",
"displaySettings": "Configuración de pantalla",
"audioSettings": "Configuración de audio",
"rdpPerformance": "Rendimiento RDP",
"deviceRedirection": "Redirección de dispositivos",
"session": "Sesión",
"gateway": "Gateway",
"remoteApp": "RemoteApp",
"clipboard": "Portapapeles",
"sessionRecording": "Grabación de sesión",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "Configuración de VNC",
"terminalSettings": "Configuración de terminal",
"rdpPort": "Puerto RDP",
"username": "Nombre de usuario",
"password": "Contraseña",
"passwordSaved": "Contraseña guardada, escriba para cambiarla",
"domain": "Dominio",
"securityMode": "Modo de seguridad",
"width": "Ancho",
"height": "Alto",
"dpi": "DPI",
"clientName": "Nombre del cliente",
"initialProgram": "Programa inicial",
"serverLayout": "Disposición del teclado",
"timezone": "Zona horaria",
"loadBalanceInfo": "Información de balanceo de carga / Cookie",
"loadBalanceInfoDesc": "Cookie del agente de conexiones a Escritorio Remoto para el balanceo de carga de la granja RDS (p. ej. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "Proxy de guacd",
"guacdHostname": "Host de guacd",
"guacdHostnamePlaceholder": "Predeterminado global",
"guacdPort": "Puerto de guacd",
"guacdProxyDesc": "Sobrescribir la instancia global de guacd para esta conexión. Dejar en blanco para usar la configuración global de guacd.",
"gatewayHostname": "Nombre de host de la puerta de enlace",
"gatewayPort": "Puerto de la puerta de enlace",
"gatewayUsername": "Nombre de usuario de la puerta de enlace",
"gatewayPassword": "Contraseña de la puerta de enlace",
"gatewayDomain": "Dominio de la puerta de enlace",
"remoteAppProgram": "Programa RemoteApp",
"workingDirectory": "Directorio de trabajo",
"arguments": "Argumentos",
"normalizeLineEndings": "Normalizar finales de línea",
"macAddress": "Dirección MAC",
"broadcastAddress": "Dirección de difusión",
"udpPort": "Puerto UDP",
"waitTimeS": "Tiempo de espera (s)",
"driveName": "Nombre de la unidad",
"drivePath": "Ruta de la unidad",
"ignoreCertificate": "Ignorar certificado",
"ignoreCertificateDesc": "Permitir conexiones a hosts con certificados autofirmados",
"forceLossless": "Forzar sin pérdida",
"forceLosslessDesc": "Forzar codificación de imagen sin pérdida (más calidad, más ancho de banda)",
"disableAudio": "Desactivar audio",
"disableAudioDesc": "Silenciar todo el audio de la sesión remota",
"enableAudioInput": "Activar entrada de audio (micrófono)",
"enableAudioInputDesc": "Reenviar el micrófono local a la sesión remota",
"wallpaper": "Fondo de pantalla",
"wallpaperDesc": "Mostrar fondo de escritorio (desactivarlo mejora el rendimiento)",
"theming": "Temas",
"themingDesc": "Activar temas y estilos visuales",
"fontSmoothing": "Suavizado de fuentes",
"fontSmoothingDesc": "Activar renderizado de fuentes ClearType",
"fullWindowDrag": "Arrastre de ventana completa",
"fullWindowDragDesc": "Mostrar el contenido de la ventana al arrastrar",
"desktopComposition": "Composición de escritorio",
"desktopCompositionDesc": "Activar efectos Aero Glass",
"menuAnimations": "Animaciones de menú",
"menuAnimationsDesc": "Activar animaciones de fundido y deslizamiento de menú",
"disableBitmapCaching": "Desactivar caché de mapa de bits",
"disableBitmapCachingDesc": "Desactivar la caché de mapa de bits (puede ayudar con fallos)",
"disableOffscreenCaching": "Desactivar caché fuera de pantalla",
"disableOffscreenCachingDesc": "Desactivar la caché fuera de pantalla",
"disableGlyphCaching": "Desactivar caché de glifos",
"disableGlyphCachingDesc": "Desactivar la caché de glifos",
"enableGfx": "Activar GFX",
"enableGfxDesc": "Usar la canalización de gráficos RemoteFX",
"enablePrinting": "Activar impresión",
"enablePrintingDesc": "Redirigir las impresoras locales a la sesión remota",
"enableDriveRedirection": "Activar redirección de unidades",
"enableDriveRedirectionDesc": "Asignar una carpeta local como una unidad en la sesión remota",
"createDrivePath": "Crear ruta de unidad",
"createDrivePathDesc": "Crear automáticamente la carpeta si no existe",
"disableDownload": "Deshabilitar descarga",
"disableDownloadDesc": "Impedir la descarga de archivos de la sesión remota",
"disableUpload": "Deshabilitar subida",
"disableUploadDesc": "Impedir la subida de archivos a la sesión remota",
"enableTouch": "Habilitar táctil",
"enableTouchDesc": "Habilitar el reenvío de entrada táctil",
"consoleSession": "Sesión de consola",
"consoleSessionDesc": "Conectar a la consola (sesión 0) en lugar de una nueva sesión",
"sendWolPacket": "Enviar paquete WOL",
"sendWolPacketDesc": "Enviar un paquete mágico para encender este equipo antes de conectar",
"disableCopy": "Deshabilitar copia",
"disableCopyDesc": "Impedir la copia de texto de la sesión remota",
"disablePaste": "Deshabilitar pegado",
"disablePasteDesc": "Impedir el pegado de texto en la sesión remota",
"createPathIfMissing": "Crear ruta si no existe",
"createPathIfMissingDesc": "Crear automáticamente el directorio de grabación",
"excludeOutput": "Excluir salida",
"excludeOutputDesc": "No grabar la salida de pantalla (solo metadatos)",
"excludeMouse": "Excluir ratón",
"excludeMouseDesc": "No grabar los movimientos del ratón",
"includeKeystrokes": "Incluir pulsaciones de teclas",
"includeKeystrokesDesc": "Grabar las pulsaciones de teclas en bruto además de la salida de pantalla",
"vncPort": "Puerto VNC",
"vncPassword": "Contraseña VNC",
"vncUsernameOptional": "Nombre de usuario (opcional)",
"vncLeaveBlank": "Dejar en blanco si no es necesario",
"cursorMode": "Modo de cursor",
"swapRedBlue": "Intercambiar rojo/azul",
"swapRedBlueDesc": "Intercambiar los canales de color rojo y azul (corrige algunos problemas de color)",
"readOnly": "Solo lectura",
"readOnlyDesc": "Ver la pantalla remota sin enviar ninguna entrada",
"telnetPort": "Puerto Telnet",
"terminalType": "Tipo de terminal",
"fontName": "Nombre de fuente",
"fontSize": "Tamaño de fuente",
"colorScheme": "Esquema de color",
"backspaceKey": "Tecla de retroceso",
"colorDepth": "Profundidad de color",
"resizeMethod": "Método de redimensionamiento",
"driveNamePlaceholder": "Unidad Termix"
},
"remoteDesktop": "Escritorio remoto",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Red virtual",
"unencryptedShell": "Shell sin cifrar",
"remoteDesktopNotes": "Notas",
"userDefaultsNote": "Los ajustes que no modifiques seguirán la configuración predeterminada de tu servidor.",
"connectionOriginNote": "En la aplicación de escritorio, RDP, VNC y Telnet usan el servidor remoto a menos que el host esté configurado como Este dispositivo, que necesita un proxy guacd al que esta máquina pueda acceder. Configure uno en la configuración de Escritorio remoto o por host en Proxy guacd.",
"docsLink": "Ver documentación",
"connectionOriginGuacamoleNote": "RDP, VNC y Telnet utilizan el servidor remoto a menos que seleccione \"Este dispositivo\", en cuyo caso se necesita un servidor guacd al que esta máquina pueda acceder. Configure uno en la configuración de URL de guacd o por host en la configuración de proxy de guacd."
},
"onboarding": {
"feature_desktop": "Escritorio remoto",
"feature_desktop_desc": "Abre sesiones RDP, VNC y Telnet en una pestaña, igual que con SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Profundidad de color",
"description": "Se utiliza para hosts RDP que lo dejan en su configuración predeterminada."
},
"resizeMethod": {
"label": "Método de redimensionamiento",
"description": "Cómo la pantalla remota se ajusta al tamaño de la ventana."
},
"resizeDisplayUpdate": "Actualización de pantalla",
"resizeReconnect": "Reconectar",
"inherit": "Heredar",
"on": "Activado",
"off": "Desactivado",
"forceLossless": {
"label": "Forzar sin pérdida",
"description": "Forzar codificación de imagen sin pérdida (más calidad, más ancho de banda)"
},
"enableWallpaper": {
"label": "Fondo de pantalla",
"description": "Mostrar fondo de escritorio (desactivarlo mejora el rendimiento)"
},
"enableFontSmoothing": {
"label": "Suavizado de fuentes",
"description": "Activar renderizado de fuentes ClearType"
},
"enableDesktopComposition": {
"label": "Composición de escritorio",
"description": "Activar efectos Aero Glass"
},
"disableAudio": {
"label": "Desactivar audio",
"description": "Silenciar todo el audio de la sesión remota"
},
"enablePrinting": {
"label": "Activar impresión",
"description": "Redirigir las impresoras locales a la sesión remota"
},
"enableDrive": {
"label": "Activar redirección de unidades",
"description": "Asignar una carpeta local como una unidad en la sesión remota"
},
"disableCopy": {
"label": "Deshabilitar copia",
"description": "Impedir la copia de texto de la sesión remota"
},
"disablePaste": {
"label": "Deshabilitar pegado",
"description": "Impedir el pegado de texto en la sesión remota"
},
"group": "Configuración predeterminada de Escritorio remoto",
"colorDepth16": "16 bits",
"colorDepth24": "24 bits",
"colorDepth32": "32 bits"
},
"admin": {
"enabled": {
"label": "Habilitar Escritorio remoto",
"description": "Permitir conexiones RDP, VNC y Telnet a través de guacd."
},
"guacdUrl": {
"label": "URL de guacd",
"description": "Donde guacd escucha, como host:puerto. Las variables de entorno GUACD_URL, GUACD_HOST y GUACD_PORT anulan esto.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "Puerto RDP"
},
"vncPort": {
"label": "puerto VNC"
},
"telnetPort": {
"label": "Puerto Telnet"
},
"rdpSecurity": {
"label": "Modo de seguridad RDP"
},
"rdpIgnoreCert": {
"label": "Ignorar errores de certificado"
},
"guacamoleConfig": {
"label": "Configuración de guacd"
},
"enableToolbar": {
"label": "Barra de herramientas de escritorio remoto",
"description": "Mostrar la barra de herramientas en las sesiones RDP, VNC y Telnet."
},
"displayGroup": "Mostrar",
"displayDefaultsTab": "Escritorio remoto"
}
},
"palette": {
"connectRdp": "Conéctese mediante RDP",
"connectVnc": "Conéctate con VNC",
"connectTelnet": "Conéctese con Telnet"
},
"permissions": {
"sessions": {
"title": "Compartir sesiones de escritorio remoto",
"description": "Permitir que las salas de colaboración y de uso compartido de sesiones muestren las sesiones RDP, VNC y Telnet de este usuario a otros."
}
},
"errors": {
"remoteServerRequired": "Se requiere un servidor remoto. Conecte un servidor remoto en Configuración para usar este tipo de conexión."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Yhdistetään {{type}} -istuntoon...",
"checkingGuacd": "Tarkistetaan etätyöpöytäpalvelua...",
"requestingToken": "Pyydetään {{type}} yhteystunnusta...",
"openingSession": "Avataan {{type}} -istuntoa...",
"negotiating": "Neuvottelen {{type}} kädenpuristuksesta...",
"sessionReady": "{{type}} istunto valmis",
"connectionError": "Yhteysvirhe",
"connectionFailed": "Yhteys epäonnistui",
"failedToConnect": "Yhteystunnuksen hakeminen epäonnistui",
"hostNotFound": "Isäntää ei löydy",
"noHostSelected": "Ei valittua isäntää",
"reconnect": "Yhdistä uudelleen",
"retry": "Yritä uudelleen",
"guacdUnavailable": "Etätyöpöytäpalvelu (guacd) ei ole käytettävissä. Varmista, että guacd on käynnissä, käytettävissä ja määritetty oikein järjestelmänvalvojan asetuksissa.",
"credentialPromptTitle": "Syötä RDP-tunnistetiedot",
"credentialPromptDescription": "Tämä isäntä on asetettu kysymään tunnistetietoja yhteyden muodostamisen yhteydessä. Niitä käytetään vain tässä istunnossa, eikä niitä tallenneta.",
"connect": "Yhdistä",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Drive-tiedostot",
"parent": "Ylähakemisto",
"refresh": "Päivitä",
"empty": "Tämä kansio on tyhjä",
"listFailed": "Tämän kansion lukeminen epäonnistui",
"download": "Lataa",
"downloadFailed": "Lataus epäonnistui {{name}}",
"upload": "Lataa tiedostoja",
"uploading": "Ladataan {{name}}...",
"uploaded": "Ladattu {{name}}",
"uploadFailed": "Lataus epäonnistui {{name}}",
"uploadDisabled": "Lähetykset on poistettu käytöstä tällä yhteydellä",
"driveUnavailable": "Ota RDP-aseman uudelleenohjaus käyttöön ennen tiedostojen lataamista",
"driveNotWritable": "guacd ei voi kirjoittaa asemakansioon. Tarkista GUACD_DRIVE_PATH (tai isännän asemapolku) ja sen käyttöoikeudet.",
"dropToUpload": "Pudota tiedostot ladattavaksi"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Lukitusnäyttö)",
"winKey": "Windows-näppäin",
"superL": "Super+L (Lukitusnäyttö)",
"superKey": "Superavain",
"super": "Super",
"cmdL": "Cmd+L (Lukitusnäyttö)",
"cmdKey": "Komento-näppäin",
"cmd": "Komento",
"ctrl": "Ctrl",
"alt": "Vaihtoehto",
"shift": "Siirtää",
"win": "Voittaa",
"stickyActive": "{{key}} (lukittu - napsauta vapauttaaksesi)",
"stickyInactive": "{{key}} (napsauta lukitaksesi)",
"esc": "Paeta",
"tab": "Välilehti",
"home": "Kotiin",
"end": "Loppu",
"pageUp": "Sivu ylös",
"pageDown": "Sivu alas",
"arrowUp": "Nuoli ylös",
"arrowDown": "Nuoli alas",
"arrowLeft": "Nuoli vasemmalle",
"arrowRight": "Nuoli oikealle",
"fnToggle": "Toimintonäppäimet",
"reconnect": "Yhdistä istunto uudelleen",
"collapse": "Pienennä työkalupalkki",
"expand": "Laajenna työkalupalkkia",
"dragHandle": "Vedä siirtääksesi",
"hide": "Piilota tältä istunnolta. Voit poistaa sen käytöstä pysyvästi avaamalla Isäntäasetukset → Yleiset → Yhteystyökalurivi.",
"zoomIn": "Lähennä",
"zoomOut": "Loitonna",
"resetZoom": "Palauta zoomaus",
"fitToScreen": "Sovita näyttöön",
"switchToTrackpad": "Vaihda ohjauslevytilaan (vedä siirtääksesi kohdistinta, napauta napsauttamalla)",
"switchToTouch": "Vaihda kosketustilaan (napauta suoraan haluamaasi kohtaan)"
},
"disabled": "Etätyöpöytä on poistettu käytöstä. Ylläpitäjä voi ottaa sen käyttöön asetuksissa."
},
"hosts": {
"nativeRdpOpened": "Avattu Windowsin etätyöpöydällä",
"nativeRdpFailed": "Windowsin etätyöpöydän avaaminen epäonnistui",
"openNativeRdp": "Avaa Windowsin etätyöpöydällä",
"guac": {
"toolbar": "Työkalurivi",
"connection": "Yhteys",
"authentication": "Todennus",
"storedCredential": "Tallennetut tunnistetiedot",
"authMethod": "Todennustapa",
"authTypeDirect": "Suoraan",
"authTypeCredential": "Valtakirja",
"authTypeNone": "Ei mitään",
"authTypeNoneDesc": "Tunnuksia ei tallenneta. Sinulta kysytään käyttäjätunnusta ja salasanaa aina, kun muodostat yhteyden; niitä ei tallenneta.",
"selectCredential": "Valitse tunnistetiedot...",
"connectionSettings": "Yhteysasetukset",
"displaySettings": "Näyttöasetukset",
"audioSettings": "Ääniasetukset",
"rdpPerformance": "RDP-suorituskyky",
"deviceRedirection": "Laitteen uudelleenohjaus",
"session": "Istunto",
"gateway": "Yhdyskäytävä",
"remoteApp": "Etäsovellus",
"clipboard": "Leikepöytä",
"sessionRecording": "Istunnon tallennus",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "VNC-asetukset",
"terminalSettings": "Pääteasetukset",
"rdpPort": "RDP-portti",
"username": "Käyttäjätunnus",
"password": "Salasana",
"passwordSaved": "Salasana tallennettu, kirjoita muuttaaksesi",
"domain": "Verkkotunnus",
"securityMode": "Suojaustila",
"width": "Leveys",
"height": "Korkeus",
"dpi": "DPI",
"clientName": "Asiakkaan nimi",
"initialProgram": "Alkuperäinen ohjelma",
"serverLayout": "Palvelimen asettelu",
"timezone": "Aikavyöhyke",
"loadBalanceInfo": "Kuormituksen tasapainotustiedot / eväste",
"loadBalanceInfoDesc": "RD Connection Broker -eväste RDS-farmin kuormituksen tasapainottamista varten (esim. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacd-välityspalvelin",
"guacdHostname": "guacd-isäntä",
"guacdHostnamePlaceholder": "Globaali oletusarvo",
"guacdPort": "guacdin satama",
"guacdProxyDesc": "Ohita tämän yhteyden globaali guacd-instanssi. Jätä tyhjäksi käyttääksesi globaalisti määritettyä guacd-instanssia.",
"gatewayHostname": "Yhdyskäytävän isäntänimi",
"gatewayPort": "Yhdyskäytäväportti",
"gatewayUsername": "Yhdyskäytävän käyttäjätunnus",
"gatewayPassword": "Yhdyskäytävän salasana",
"gatewayDomain": "Yhdyskäytävän verkkotunnus",
"remoteAppProgram": "RemoteApp-ohjelma",
"workingDirectory": "Työhakemisto",
"arguments": "Argumentit",
"normalizeLineEndings": "Normalisoi rivinpäätteet",
"macAddress": "MAC-osoite",
"broadcastAddress": "Lähetysosoite",
"udpPort": "UDP-portti",
"waitTimeS": "Odotusaika (s)",
"driveName": "Aseman nimi",
"drivePath": "Ajopolku",
"ignoreCertificate": "Ohita varmenne",
"ignoreCertificateDesc": "Salli yhteydet itse allekirjoitettuja varmenteita käyttäviin isäntiin",
"forceLossless": "Pakota häviötön",
"forceLosslessDesc": "Pakota häviötön kuvan koodaus (korkeampi laatu, enemmän kaistanleveyttä)",
"disableAudio": "Poista ääni käytöstä",
"disableAudioDesc": "Mykistä kaikki etäistunnon äänet",
"enableAudioInput": "Ota äänitulo käyttöön (mikrofoni)",
"enableAudioInputDesc": "Lähetä paikallinen mikrofoni etäistuntoon",
"wallpaper": "Taustakuva",
"wallpaperDesc": "Näytä työpöydän taustakuva (poistaminen käytöstä parantaa suorituskykyä)",
"theming": "Teemat",
"themingDesc": "Ota käyttöön visuaaliset teemat ja tyylit",
"fontSmoothing": "Fontin pehmennys",
"fontSmoothingDesc": "Ota ClearType-fonttien renderöinti käyttöön",
"fullWindowDrag": "Koko ikkunan vetäminen",
"fullWindowDragDesc": "Näytä ikkunan sisältö vetämisen aikana",
"desktopComposition": "Työpöydän sommittelu",
"desktopCompositionDesc": "Ota Aero Glass -tehosteet käyttöön",
"menuAnimations": "Valikkoanimaatiot",
"menuAnimationsDesc": "Ota käyttöön valikoiden häivytys ja dia-animaatiot",
"disableBitmapCaching": "Poista bittikarttavälimuisti käytöstä",
"disableBitmapCachingDesc": "Poista bittikarttavälimuisti käytöstä (saattaa auttaa häiriöiden kanssa)",
"disableOffscreenCaching": "Poista käytöstä näytön ulkopuolinen välimuisti",
"disableOffscreenCachingDesc": "Poista käytöstä näytön ulkopuolinen välimuisti",
"disableGlyphCaching": "Poista käytöstä glyfien välimuisti",
"disableGlyphCachingDesc": "Poista merkkijonojen välimuisti käytöstä",
"enableGfx": "Ota käyttöön näytönsäästäjä",
"enableGfxDesc": "Käytä RemoteFX-grafiikkaputkea",
"enablePrinting": "Ota tulostus käyttöön",
"enablePrintingDesc": "Ohjaa paikalliset tulostimet etäistuntoon",
"enableDriveRedirection": "Ota aseman uudelleenohjaus käyttöön",
"enableDriveRedirectionDesc": "Yhdistä paikallinen kansio asemaksi etäistunnossa",
"createDrivePath": "Luo ajopolku",
"createDrivePathDesc": "Luo kansio automaattisesti, jos sitä ei ole olemassa",
"disableDownload": "Poista lataus käytöstä",
"disableDownloadDesc": "Estä tiedostojen lataaminen etäistunnosta",
"disableUpload": "Poista lähetys käytöstä",
"disableUploadDesc": "Estä tiedostojen lataaminen etäistuntoon",
"enableTouch": "Ota kosketus käyttöön",
"enableTouchDesc": "Ota kosketussyötteen edelleenlähetys käyttöön",
"consoleSession": "Konsoli-istunto",
"consoleSessionDesc": "Yhdistä konsoliin (istunto 0) uuden istunnon sijaan",
"sendWolPacket": "Lähetä WOL-paketti",
"sendWolPacketDesc": "Lähetä taikapaketti herättääksesi tämän isännän ennen yhteyden muodostamista",
"disableCopy": "Poista kopiointi käytöstä",
"disableCopyDesc": "Estä tekstin kopioiminen etäistunnosta",
"disablePaste": "Poista liittäminen käytöstä",
"disablePasteDesc": "Estä tekstin liittäminen etäistuntoon",
"createPathIfMissing": "Luo polku, jos se puuttuu",
"createPathIfMissingDesc": "Luo tallennushakemisto automaattisesti",
"excludeOutput": "Sulje pois tuloste",
"excludeOutputDesc": "Älä tallenna näytön tulostetta (vain metatiedot)",
"excludeMouse": "Sulje hiiri pois",
"excludeMouseDesc": "Älä tallenna hiiren liikkeitä",
"includeKeystrokes": "Sisällytä näppäinpainallukset",
"includeKeystrokesDesc": "Tallenna raakat näppäinpainallukset näytön tulosteen lisäksi",
"vncPort": "VNC-portti",
"vncPassword": "VNC-salasana",
"vncUsernameOptional": "Käyttäjätunnus (valinnainen)",
"vncLeaveBlank": "Jätä tyhjäksi, jos ei pakollinen",
"cursorMode": "Kohdistimen tila",
"swapRedBlue": "Vaihda punainen/sininen",
"swapRedBlueDesc": "Vaihda punaisen ja sinisen värikanavan paikkaa (korjaa joitakin väriongelmia)",
"readOnly": "Vain lukuoikeus",
"readOnlyDesc": "Katso etänäyttöä lähettämättä mitään syötettä",
"telnetPort": "Telnet-portti",
"terminalType": "Päätetyyppi",
"fontName": "Fontin nimi",
"fontSize": "Fonttikoko",
"colorScheme": "Värimaailma",
"backspaceKey": "Askelpalautin-näppäin",
"colorDepth": "Värisyvyys",
"resizeMethod": "Koon muuttamismenetelmä",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "Etätyöpöytä",
"tabRdp": "Maaseudun kehittämisohjelma",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Virtuaaliverkko",
"unencryptedShell": "Salaamaton kuori",
"remoteDesktopNotes": "Muistiinpanoja",
"userDefaultsNote": "Muuttamatta jätetyt asetukset noudattavat isäntäsi oletusasetuksia.",
"connectionOriginNote": "Työpöytäsovelluksessa RDP, VNC ja Telnet käyttävät etäpalvelinta, ellei isäntäasetukseksi ole asetettu Tämä laite, jolloin tarvitaan GUACD, johon tämä kone voi tavoittaa. Aseta sellainen etätyöpöytäasetuksissa tai isäntäkohtaisesti GUACD-välityspalvelimen alla.",
"docsLink": "Näytä dokumentit",
"connectionOriginGuacamoleNote": "RDP, VNC ja Telnet käyttävät etäpalvelinta, ellet valitse \"Tämä laite\", jolloin tarvitset GUACD:n, johon tämä kone voi tavoittaa. Aseta sellainen GUACD:n URL-asetuksissa tai isäntäkohtaisesti GUACD-välityspalvelimen alla."
},
"onboarding": {
"feature_desktop": "Etätyöpöytä",
"feature_desktop_desc": "Avaa RDP-, VNC- ja Telnet-istunnot välilehdellä, samalla tavalla kuin SSH:ssa."
},
"settings": {
"user": {
"colorDepth": {
"label": "Värisyvyys",
"description": "Käytetään RDP-isännille, jotka jättävät sen oletusarvoiseksi."
},
"resizeMethod": {
"label": "Koon muuttamismenetelmä",
"description": "Miten etänäyttö seuraa ikkunan kokoa."
},
"resizeDisplayUpdate": "Näyttöpäivitys",
"resizeReconnect": "Yhdistä uudelleen",
"inherit": "Periä",
"on": "Päällä",
"off": "Pois",
"forceLossless": {
"label": "Pakota häviötön",
"description": "Pakota häviötön kuvan koodaus (korkeampi laatu, enemmän kaistanleveyttä)"
},
"enableWallpaper": {
"label": "Taustakuva",
"description": "Näytä työpöydän taustakuva (poistaminen käytöstä parantaa suorituskykyä)"
},
"enableFontSmoothing": {
"label": "Fontin pehmennys",
"description": "Ota ClearType-fonttien renderöinti käyttöön"
},
"enableDesktopComposition": {
"label": "Työpöydän sommittelu",
"description": "Ota Aero Glass -tehosteet käyttöön"
},
"disableAudio": {
"label": "Poista ääni käytöstä",
"description": "Mykistä kaikki etäistunnon äänet"
},
"enablePrinting": {
"label": "Ota tulostus käyttöön",
"description": "Ohjaa paikalliset tulostimet etäistuntoon"
},
"enableDrive": {
"label": "Ota aseman uudelleenohjaus käyttöön",
"description": "Yhdistä paikallinen kansio asemaksi etäistunnossa"
},
"disableCopy": {
"label": "Poista kopiointi käytöstä",
"description": "Estä tekstin kopioiminen etäistunnosta"
},
"disablePaste": {
"label": "Poista liittäminen käytöstä",
"description": "Estä tekstin liittäminen etäistuntoon"
},
"group": "Etätyöpöydän oletusasetukset",
"colorDepth16": "16-bittinen",
"colorDepth24": "24-bittinen",
"colorDepth32": "32-bittinen"
},
"admin": {
"enabled": {
"label": "Ota etätyöpöytä käyttöön",
"description": "Salli RDP-, VNC- ja Telnet-yhteydet guacdin kautta."
},
"guacdUrl": {
"label": "guacd-URL-osoite",
"description": "Missä guacd kuuntelee, muodossa host:port. GUACD_URL-, GUACD_HOST- ja GUACD_PORT-ympäristömuuttujat korvaavat tämän.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "Maaseudun kehittämisohjelma"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP-portti"
},
"vncPort": {
"label": "VNC-portti"
},
"telnetPort": {
"label": "Telnet-portti"
},
"rdpSecurity": {
"label": "RDP-suojaustila"
},
"rdpIgnoreCert": {
"label": "Ohita varmennevirheet"
},
"guacamoleConfig": {
"label": "guacd-asetukset"
},
"enableToolbar": {
"label": "Etätyöpöydän työkalupalkki",
"description": "Näytä työkalupalkki RDP-, VNC- ja Telnet-istunnoissa."
},
"displayGroup": "Näyttö",
"displayDefaultsTab": "Etätyöpöytä"
}
},
"palette": {
"connectRdp": "Yhdistä RDP:hen",
"connectVnc": "Yhdistä VNC:hen",
"connectTelnet": "Yhdistä Telnetillä"
},
"permissions": {
"sessions": {
"title": "Jaa etätyöpöytäistuntoja",
"description": "Salli istuntojen jakamisen ja yhteistyöhuoneiden näyttää tämän käyttäjän RDP-, VNC- ja Telnet-istunnot muille."
}
},
"errors": {
"remoteServerRequired": "Etäpalvelin vaaditaan. Yhdistä etäpalvelin asetuksissa käyttääksesi tätä yhteystyyppiä."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Connexion à la session {{type}}...",
"checkingGuacd": "Vérification du service de bureau à distance...",
"requestingToken": "Demande de jeton de connexion {{type}}...",
"openingSession": "Ouverture de la session {{type}}...",
"negotiating": "Negotiating {{type}} handshake...",
"sessionReady": "{{type}} session prête",
"connectionError": "Erreur de connexion",
"connectionFailed": "Échec de la connexion",
"failedToConnect": "Échec de l'obtention du jeton de connexion",
"hostNotFound": "Hôte introuvable",
"noHostSelected": "Aucun hôte sélectionné",
"reconnect": "Reconnecter",
"retry": "Réessayer",
"guacdUnavailable": "Le service de bureau à distance (guacd) n'est pas disponible. Veuillez vous assurer que guacd est en cours d'exécution, accessible et correctement configuré dans les paramètres administrateur.",
"credentialPromptTitle": "Saisir les informations d'identification RDP",
"credentialPromptDescription": "Cet hôte est configuré pour demander des identifiants lors de la connexion. Ces identifiants sont utilisés uniquement pour cette session et ne sont pas enregistrés.",
"connect": "Se connecter",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Fichiers Drive",
"parent": "Répertoire parent",
"refresh": "Actualiser",
"empty": "Ce dossier est vide",
"listFailed": "Impossible de lire ce dossier",
"download": "Télécharger",
"downloadFailed": "Échec du téléchargement {{name}}",
"upload": "Téléverser des fichiers",
"uploading": "Téléversement de {{name}}...",
"uploaded": "Téléchargé {{name}}",
"uploadFailed": "Échec du chargement {{name}}",
"uploadDisabled": "Les chargements sont désactivés pour cette connexion.",
"driveUnavailable": "Activez la redirection du lecteur RDP avant de télécharger les fichiers",
"driveNotWritable": "guacd ne peut pas écrire dans le dossier du lecteur. Vérifiez la variable d'environnement GUACD_DRIVE_PATH (ou le chemin d'accès au lecteur de l'hôte) et ses autorisations.",
"dropToUpload": "Déposez les fichiers à télécharger"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Verrouiller l'écran)",
"winKey": "Touche Windows",
"superL": "Super+L (Écran de verrouillage)",
"superKey": "Super Clé",
"super": "Super",
"cmdL": "Cmd+L (Écran de verrouillage)",
"cmdKey": "Touche de commande",
"cmd": "Commande",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Maj",
"win": "Win",
"stickyActive": "{{key}} (verrouillé - cliquer pour libérer)",
"stickyInactive": "{{key}} (cliquer pour verrouiller)",
"esc": "Échap",
"tab": "Tab",
"home": "Début",
"end": "Fin",
"pageUp": "Page préc.",
"pageDown": "Page suiv.",
"arrowUp": "Flèche haut",
"arrowDown": "Flèche bas",
"arrowLeft": "Flèche gauche",
"arrowRight": "Flèche droite",
"fnToggle": "Touches de fonction",
"reconnect": "Reconnecter la session",
"collapse": "Réduire la barre d'outils",
"expand": "Développer la barre d'outils",
"dragHandle": "Glisser pour repositionner",
"hide": "Masquer cette option pour cette session. Pour la désactiver définitivement, ouvrez Paramètres de l'hôte → Général → Barre d'outils de connexion.",
"zoomIn": "Zoomer",
"zoomOut": "Dézoomer",
"resetZoom": "Réinitialiser le zoom",
"fitToScreen": "Ajuster à l'écran",
"switchToTrackpad": "Passez en mode pavé tactile (glissez pour déplacer le curseur, appuyez pour cliquer)",
"switchToTouch": "Passez en mode tactile (appuyez directement à l'endroit où vous souhaitez cliquer)."
},
"disabled": "Le Bureau à distance est désactivé. Un administrateur peut l'activer dans les paramètres."
},
"hosts": {
"nativeRdpOpened": "Ouvert dans Bureau à distance Windows",
"nativeRdpFailed": "Impossible d'ouvrir le Bureau à distance Windows",
"openNativeRdp": "Ouvrir dans le Bureau à distance Windows",
"guac": {
"toolbar": "barre d'outils",
"connection": "Connexion",
"authentication": "Authentification",
"storedCredential": "Identifiant enregistré",
"authMethod": "Méthode d'authentification",
"authTypeDirect": "Direct",
"authTypeCredential": "Identifiant",
"authTypeNone": "Aucune",
"authTypeNoneDesc": "Aucune information d'identification n'est stockée. Un nom d'utilisateur et un mot de passe vous seront demandés à chaque connexion ; ils ne sont pas enregistrés.",
"selectCredential": "Sélectionner un identifiant...",
"connectionSettings": "Paramètres de connexion",
"displaySettings": "Paramètres d'affichage",
"audioSettings": "Paramètres audio",
"rdpPerformance": "Performances RDP",
"deviceRedirection": "Redirection de périphériques",
"session": "Session",
"gateway": "Passerelle",
"remoteApp": "RemoteApp",
"clipboard": "Presse-papiers",
"sessionRecording": "Enregistrement de session",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "Paramètres VNC",
"terminalSettings": "Paramètres du terminal",
"rdpPort": "Port RDP",
"username": "Nom d'utilisateur",
"password": "Mot de passe",
"passwordSaved": "Mot de passe enregistré, saisissez-le pour le modifier",
"domain": "Domaine",
"securityMode": "Mode de sécurité",
"width": "Largeur",
"height": "Hauteur",
"dpi": "DPI",
"clientName": "Nom du client",
"initialProgram": "Programme initial",
"serverLayout": "Disposition du clavier",
"timezone": "Fuseau horaire",
"loadBalanceInfo": "Infos d'équilibrage de charge / Cookie",
"loadBalanceInfoDesc": "Cookie du RD Connection Broker pour l'équilibrage de charge d'une ferme RDS (ex. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "Proxy guacd",
"guacdHostname": "Hôte guacd",
"guacdHostnamePlaceholder": "Valeur globale par défaut",
"guacdPort": "Port guacd",
"guacdProxyDesc": "Remplacer l'instance guacd globale pour cette connexion. Laisser vide pour utiliser le guacd configuré globalement.",
"gatewayHostname": "Nom d'hôte de la passerelle",
"gatewayPort": "Port de la passerelle",
"gatewayUsername": "Nom d'utilisateur de la passerelle",
"gatewayPassword": "Mot de passe de la passerelle",
"gatewayDomain": "Domaine de la passerelle",
"remoteAppProgram": "Programme RemoteApp",
"workingDirectory": "Répertoire de travail",
"arguments": "Arguments",
"normalizeLineEndings": "Normaliser les fins de ligne",
"macAddress": "Adresse MAC",
"broadcastAddress": "Adresse de diffusion",
"udpPort": "Port UDP",
"waitTimeS": "Délai d'attente (s)",
"driveName": "Nom du lecteur",
"drivePath": "Chemin du lecteur",
"ignoreCertificate": "Ignorer le certificat",
"ignoreCertificateDesc": "Autoriser les connexions aux hôtes avec des certificats auto-signés",
"forceLossless": "Forcer sans perte",
"forceLosslessDesc": "Forcer l'encodage d'image sans perte (meilleure qualité, plus de bande passante)",
"disableAudio": "Désactiver l'audio",
"disableAudioDesc": "Couper tout le son de la session distante",
"enableAudioInput": "Activer l'entrée audio (microphone)",
"enableAudioInputDesc": "Transférer le microphone local vers la session distante",
"wallpaper": "Fond d'écran",
"wallpaperDesc": "Afficher le fond d'écran du bureau (désactiver cette option améliore les performances)",
"theming": "Thèmes",
"themingDesc": "Activer les thèmes et styles visuels",
"fontSmoothing": "Lissage des polices",
"fontSmoothingDesc": "Activer le rendu des polices ClearType",
"fullWindowDrag": "Afficher le contenu de la fenêtre lors du déplacement",
"fullWindowDragDesc": "Afficher le contenu de la fenêtre lors du déplacement",
"desktopComposition": "Composition du bureau",
"desktopCompositionDesc": "Activer les effets Aero Glass",
"menuAnimations": "Animations des menus",
"menuAnimationsDesc": "Activer les animations de fondu et de glissement des menus",
"disableBitmapCaching": "Désactiver le cache bitmap",
"disableBitmapCachingDesc": "Désactiver le cache bitmap (peut aider en cas de problèmes graphiques)",
"disableOffscreenCaching": "Désactiver le cache hors écran",
"disableOffscreenCachingDesc": "Désactiver le cache hors écran",
"disableGlyphCaching": "Désactiver le cache des glyphes",
"disableGlyphCachingDesc": "Désactiver le cache des glyphes",
"enableGfx": "Activer GFX",
"enableGfxDesc": "Utiliser le pipeline graphique RemoteFX",
"enablePrinting": "Activer l'impression",
"enablePrintingDesc": "Rediriger les imprimantes locales vers la session distante",
"enableDriveRedirection": "Activer la redirection de lecteur",
"enableDriveRedirectionDesc": "Monter un dossier local en tant que lecteur dans la session distante",
"createDrivePath": "Créer le chemin du lecteur",
"createDrivePathDesc": "Créer automatiquement le dossier s'il n'existe pas",
"disableDownload": "Désactiver le téléchargement",
"disableDownloadDesc": "Empêcher le téléchargement des fichiers depuis la session distante",
"disableUpload": "Désactiver le téléversement",
"disableUploadDesc": "Empêcher le téléversement de fichiers vers la session distante",
"enableTouch": "Activer le tactile",
"enableTouchDesc": "Activer la transmission des entrées tactiles",
"consoleSession": "Session console",
"consoleSessionDesc": "Se connecter à la console (session 0) au lieu d'une nouvelle session",
"sendWolPacket": "Envoyer un paquet WOL",
"sendWolPacketDesc": "Envoyer un paquet magique pour réveiller cet hôte avant la connexion",
"disableCopy": "Désactiver la copie",
"disableCopyDesc": "Empêcher la copie de texte depuis la session distante",
"disablePaste": "Désactiver le collage",
"disablePasteDesc": "Empêcher le collage de texte dans la session distante",
"createPathIfMissing": "Créer le chemin si absent",
"createPathIfMissingDesc": "Créer automatiquement le répertoire d'enregistrement",
"excludeOutput": "Exclure la sortie",
"excludeOutputDesc": "Ne pas enregistrer l'affichage écran (métadonnées uniquement)",
"excludeMouse": "Exclure la souris",
"excludeMouseDesc": "Ne pas enregistrer les mouvements de la souris",
"includeKeystrokes": "Inclure les frappes",
"includeKeystrokesDesc": "Enregistrer les frappes brutes en plus de l'affichage écran",
"vncPort": "Port VNC",
"vncPassword": "Mot de passe VNC",
"vncUsernameOptional": "Nom d'utilisateur (facultatif)",
"vncLeaveBlank": "Laisser vide si non requis",
"cursorMode": "Mode curseur",
"swapRedBlue": "Échanger rouge/bleu",
"swapRedBlueDesc": "Échanger les canaux de couleur rouge et bleu (corrige certains problèmes de couleur)",
"readOnly": "Lecture seule",
"readOnlyDesc": "Afficher l'écran distant sans envoyer d'entrée",
"telnetPort": "Port Telnet",
"terminalType": "Type de terminal",
"fontName": "Nom de la police",
"fontSize": "Taille de la police",
"colorScheme": "Schéma de couleurs",
"backspaceKey": "Touche Backspace",
"colorDepth": "Profondeur de couleur",
"resizeMethod": "Méthode de redimensionnement",
"driveNamePlaceholder": "Lecteur Termix"
},
"remoteDesktop": "Bureau à distance",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Réseau virtuel",
"unencryptedShell": "Shell non chiffré",
"remoteDesktopNotes": "Notes",
"userDefaultsNote": "Les paramètres que vous laissez inchangés suivent les valeurs par défaut de votre hôte.",
"connectionOriginNote": "Sur l'application de bureau, RDP, VNC et Telnet utilisent le serveur distant, sauf si l'hôte est configuré sur « Cet appareil », auquel cas un proxy guacd accessible par cette machine est nécessaire. Configurez-en un dans les paramètres du Bureau à distance ou pour chaque hôte dans les paramètres du proxy guacd.",
"docsLink": "Voir la documentation",
"connectionOriginGuacamoleNote": "RDP, VNC et Telnet utilisent le serveur distant, sauf si vous sélectionnez « Cet appareil », auquel cas un serveur guacd accessible par cette machine est nécessaire. Configurez-en un dans les paramètres d'URL guacd ou par hôte dans les paramètres de proxy guacd."
},
"onboarding": {
"feature_desktop": "Bureau à distance",
"feature_desktop_desc": "Ouvrez les sessions RDP, VNC et Telnet dans un onglet, de la même manière que SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Profondeur de couleur",
"description": "Utilisé pour les hôtes RDP qui conservent leurs paramètres par défaut."
},
"resizeMethod": {
"label": "Méthode de redimensionnement",
"description": "Comment l'écran distant s'adapte à la taille de la fenêtre."
},
"resizeDisplayUpdate": "Mise à jour de l'affichage",
"resizeReconnect": "Reconnecter",
"inherit": "Hériter",
"on": "Activée",
"off": "Désactivée",
"forceLossless": {
"label": "Forcer sans perte",
"description": "Forcer l'encodage d'image sans perte (meilleure qualité, plus de bande passante)"
},
"enableWallpaper": {
"label": "Fond d'écran",
"description": "Afficher le fond d'écran du bureau (désactiver cette option améliore les performances)"
},
"enableFontSmoothing": {
"label": "Lissage des polices",
"description": "Activer le rendu des polices ClearType"
},
"enableDesktopComposition": {
"label": "Composition du bureau",
"description": "Activer les effets Aero Glass"
},
"disableAudio": {
"label": "Désactiver l'audio",
"description": "Couper tout le son de la session distante"
},
"enablePrinting": {
"label": "Activer l'impression",
"description": "Rediriger les imprimantes locales vers la session distante"
},
"enableDrive": {
"label": "Activer la redirection de lecteur",
"description": "Monter un dossier local en tant que lecteur dans la session distante"
},
"disableCopy": {
"label": "Désactiver la copie",
"description": "Empêcher la copie de texte depuis la session distante"
},
"disablePaste": {
"label": "Désactiver le collage",
"description": "Empêcher le collage de texte dans la session distante"
},
"group": "Paramètres par défaut du Bureau à distance",
"colorDepth16": "16 bits",
"colorDepth24": "24 bits",
"colorDepth32": "32 bits"
},
"admin": {
"enabled": {
"label": "Activer le bureau à distance",
"description": "Autoriser les connexions RDP, VNC et Telnet via guacd."
},
"guacdUrl": {
"label": "URL guacd",
"description": "L'adresse d'écoute de guacd est indiquée par l'hôte et le port. Les variables d'environnement GUACD_URL, GUACD_HOST et GUACD_PORT prévalent sur ce comportement.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "Port RDP"
},
"vncPort": {
"label": "port VNC"
},
"telnetPort": {
"label": "Port Telnet"
},
"rdpSecurity": {
"label": "Mode de sécurité RDP"
},
"rdpIgnoreCert": {
"label": "Ignorer les erreurs de certificat"
},
"guacamoleConfig": {
"label": "paramètres guacd"
},
"enableToolbar": {
"label": "barre d'outils de bureau à distance",
"description": "Afficher la barre d'outils dans les sessions RDP, VNC et Telnet."
},
"displayGroup": "Afficher",
"displayDefaultsTab": "Bureau à distance"
}
},
"palette": {
"connectRdp": "Se connecter via RDP",
"connectVnc": "Se connecter avec VNC",
"connectTelnet": "Se connecter avec Telnet"
},
"permissions": {
"sessions": {
"title": "Sessions de bureau à distance partagées",
"description": "Autoriser le partage de session et les salles de collaboration à afficher les sessions RDP, VNC et Telnet de cet utilisateur aux autres."
}
},
"errors": {
"remoteServerRequired": "Serveur distant requis. Connectez un serveur distant dans les paramètres pour utiliser ce type de connexion."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "מתחבר לסשן {{type}}...",
"checkingGuacd": "בודק שירות שולחן עבודה מרוחק...",
"requestingToken": "מבקש אסימון חיבור {{type}}...",
"openingSession": "פותח סשן {{type}}...",
"negotiating": "מנהל משא ומתן על לחיצת יד {{type}}...",
"sessionReady": "{{type}} מוכנה לסשן",
"connectionError": "שגיאת חיבור",
"connectionFailed": "החיבור נכשל",
"failedToConnect": "נכשל בקבלת אסימון החיבור",
"hostNotFound": "המארח לא נמצא",
"noHostSelected": "לא נבחר מארח",
"reconnect": "התחבר מחדש",
"retry": "נסה שוב",
"guacdUnavailable": "שירות שולחן עבודה מרוחק (guacd) אינו זמין. אנא ודא ש-guacd פועל, נגיש ומוגדר כראוי בהגדרות המנהל.",
"credentialPromptTitle": "הזן אישורי RDP",
"credentialPromptDescription": "מארח זה מוגדר לבקש אישורים בעת התחברות. הם משמשים עבור הפעלה זו בלבד ואינם נשמרים.",
"connect": "לְחַבֵּר",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "קבצי דרייב",
"parent": "ספריית הורים",
"refresh": "לְרַעֲנֵן",
"empty": "תיקייה זו ריקה",
"listFailed": "נכשלה קריאת התיקייה הזו",
"download": "הורדה",
"downloadFailed": "נכשל בהורדת {{name}}",
"upload": "העלאת קבצים",
"uploading": "מעלה {{name}}...",
"uploaded": "הועלה {{name}}",
"uploadFailed": "נכשלה העלאת {{name}}",
"uploadDisabled": "העלאות מושבתות עבור חיבור זה",
"driveUnavailable": "הפעל ניתוב מחדש של כונן RDP לפני העלאת קבצים",
"driveNotWritable": "ל-guacd אין אפשרות לכתוב לתיקיית הכונן. בדוק את GUACD_DRIVE_PATH (או את נתיב הכונן של המארח) ואת ההרשאות שלו.",
"dropToUpload": "שחררו קבצים להעלאה"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (מסך נעילה)",
"winKey": "מפתח חלונות",
"superL": "סופר+L (מסך נעילה)",
"superKey": "סופר קי",
"super": "סוּפֶּר",
"cmdL": "Cmd+L (מסך נעילה)",
"cmdKey": "מקש פקודה",
"cmd": "פקודה",
"ctrl": "קונטרול",
"alt": "אלט",
"shift": "מִשׁמֶרֶת",
"win": "לְנַצֵחַ",
"stickyActive": "{{key}} (נעול - לחץ לשחרור)",
"stickyInactive": "{{key}} (לחץ כדי לנעול)",
"esc": "לִברוֹחַ",
"tab": "טאב",
"home": "בַּיִת",
"end": "סוֹף",
"pageUp": "עמוד למעלה",
"pageDown": "עמוד למטה",
"arrowUp": "חץ למעלה",
"arrowDown": "חץ למטה",
"arrowLeft": "חץ שמאלה",
"arrowRight": "חץ ימינה",
"fnToggle": "מקשי פונקציה",
"reconnect": "חיבור מחדש של הפעלת המושב",
"collapse": "כיווץ סרגל הכלים",
"expand": "הרחב את סרגל הכלים",
"dragHandle": "גרור כדי למקם מחדש",
"hide": "הסתר עבור הפעלה זו. כדי להשבית זאת לצמיתות, פתח את הגדרות המארח ← כללי ← סרגל הכלים לחיבור.",
"zoomIn": "לְהִתְמַקֵד",
"zoomOut": "התרחקות",
"resetZoom": "איפוס זום",
"fitToScreen": "התאמה למסך",
"switchToTrackpad": "מעבר למצב משטח עקיבה (גרור כדי להזיז את הסמן, הקש כדי ללחוץ)",
"switchToTouch": "מעבר למצב מגע (הקש ישירות במקום שברצונך ללחוץ)"
},
"disabled": "שולחן עבודה מרוחק כבוי. מנהל יכול להפעיל אותו בהגדרות."
},
"hosts": {
"nativeRdpOpened": "נפתח בשולחן עבודה מרוחק של Windows",
"nativeRdpFailed": "נכשלה פתיחת שולחן עבודה מרוחק של Windows",
"openNativeRdp": "פתח בשולחן עבודה מרוחק של Windows",
"guac": {
"toolbar": "סרגל כלים",
"connection": "קֶשֶׁר",
"authentication": "אימות",
"storedCredential": "אישורים מאוחסנים",
"authMethod": "שיטת אימות",
"authTypeDirect": "יָשִׁיר",
"authTypeCredential": "תְעוּדָה",
"authTypeNone": "אַף לֹא אֶחָד",
"authTypeNoneDesc": "לא נשמרים פרטי גישה. תתבקש להזין שם משתמש וסיסמה בכל פעם שתתחבר; הם לא נשמרים.",
"selectCredential": "בחר אישור...",
"connectionSettings": "הגדרות חיבור",
"displaySettings": "הגדרות תצוגה",
"audioSettings": "הגדרות שמע",
"rdpPerformance": "ביצועי RDP",
"deviceRedirection": "ניתוב מחדש של מכשירים",
"session": "מוֹשָׁב",
"gateway": "כְּנִיסָה",
"remoteApp": "אפליקציה מרחוק",
"clipboard": "לוח כתיבה",
"sessionRecording": "הקלטת סשן",
"wakeOnLan": "התעוררות ברשת LAN",
"vncSettings": "הגדרות VNC",
"terminalSettings": "הגדרות מסוף",
"rdpPort": "יציאת RDP",
"username": "שם משתמש",
"password": "סִיסמָה",
"passwordSaved": "הסיסמה נשמרה, הקלד כדי לשנות",
"domain": "תְחוּם",
"securityMode": "מצב אבטחה",
"width": "רוֹחַב",
"height": "גוֹבַה",
"dpi": "DPI",
"clientName": "שם הלקוח",
"initialProgram": "תוכנית ראשונית",
"serverLayout": "פריסת שרת",
"timezone": "אזור זמן",
"loadBalanceInfo": "מידע על מאזן עומס / קובץ Cookie",
"loadBalanceInfoDesc": "קובץ cookie של RD Connection Broker עבור איזון עומסים של חוות RDS (לדוגמה tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "פרוקסי של guacd",
"guacdHostname": "מארח guacd",
"guacdHostnamePlaceholder": "ברירת מחדל גלובלית",
"guacdPort": "נמל גואקד",
"guacdProxyDesc": "עקוף את מופע ה-guacd הגלובלי עבור חיבור זה. השאר ריק כדי להשתמש ב-guacd שתצורתו הגלובלית.",
"gatewayHostname": "שם מארח השער",
"gatewayPort": "שער היציאה",
"gatewayUsername": "שם משתמש של השער",
"gatewayPassword": "סיסמת השער",
"gatewayDomain": "דומיין שער",
"remoteAppProgram": "תוכנית RemoteApp",
"workingDirectory": "ספריית עבודה",
"arguments": "טיעונים",
"normalizeLineEndings": "נרמול סיומות שורות",
"macAddress": "כתובת MAC",
"broadcastAddress": "כתובת שידור",
"udpPort": "יציאת UDP",
"waitTimeS": "זמן המתנה (שניות)",
"driveName": "שם הכונן",
"drivePath": "נתיב הנסיעה",
"ignoreCertificate": "התעלם מהתעודה",
"ignoreCertificateDesc": "אפשר חיבורים למארחים עם אישורים חתומים עצמית",
"forceLossless": "כפיית אובדן נתונים",
"forceLosslessDesc": "כפיית קידוד תמונה ללא אובדן נתונים (איכות גבוהה יותר, רוחב פס גדול יותר)",
"disableAudio": "השבתת שמע",
"disableAudioDesc": "השתקת כל האודיו מההפעלה המרוחקת",
"enableAudioInput": "הפעלת קלט שמע (מיקרופון)",
"enableAudioInputDesc": "העברת המיקרופון המקומי לסשן המרוחק",
"wallpaper": "טָפֵּט",
"wallpaperDesc": "הצגת טפט שולחן עבודה (השבתה משפרת את הביצועים)",
"theming": "ת'מינג",
"themingDesc": "הפעלת ערכות נושא וסגנונות חזותיים",
"fontSmoothing": "החלקת גופנים",
"fontSmoothingDesc": "הפעלת עיבוד גופני ClearType",
"fullWindowDrag": "גרירת חלון מלאה",
"fullWindowDragDesc": "הצגת תוכן החלון בעת גרירה",
"desktopComposition": "קומפוזיציה לשולחן העבודה",
"desktopCompositionDesc": "הפעלת אפקטים של Aero Glass",
"menuAnimations": "אנימציות תפריטים",
"menuAnimationsDesc": "הפעלת אנימציות של דהייה והחלקה בתפריט",
"disableBitmapCaching": "השבתת אחסון במטמון של מפת סיביות",
"disableBitmapCachingDesc": "כבה את מטמון הסיביות (עשוי לעזור עם תקלות)",
"disableOffscreenCaching": "השבתת אחסון במטמון מחוץ למסך",
"disableOffscreenCachingDesc": "כבה את המטמון מחוץ למסך",
"disableGlyphCaching": "השבתת אחסון גליפים במטמון",
"disableGlyphCachingDesc": "כבה את מטמון הגליפים",
"enableGfx": "הפעלת GFX",
"enableGfxDesc": "השתמש בצינור הגרפיקה של RemoteFX",
"enablePrinting": "הפעל הדפסה",
"enablePrintingDesc": "ניתוב מחדש של מדפסות מקומיות להפעלה מרוחקת",
"enableDriveRedirection": "הפעל ניתוב מחדש של כונן",
"enableDriveRedirectionDesc": "מיפוי תיקייה מקומית ככונן בהפעלה מרוחקת",
"createDrivePath": "צור נתיב כונן",
"createDrivePathDesc": "צור את התיקייה באופן אוטומטי אם היא אינה קיימת",
"disableDownload": "השבתת הורדה",
"disableDownloadDesc": "מניעת הורדת קבצים מההפעלה המרוחקת",
"disableUpload": "השבתת העלאה",
"disableUploadDesc": "מניעת העלאת קבצים לסשן מרחוק",
"enableTouch": "הפעל מגע",
"enableTouchDesc": "הפעל העברת קלט מגע",
"consoleSession": "סשן קונסולה",
"consoleSessionDesc": "התחברות לקונסולה (סשן 0) במקום סשן חדש",
"sendWolPacket": "שלח חבילת WOL",
"sendWolPacketDesc": "שלח חבילת קסם כדי להעיר את המארח הזה לפני התחברות",
"disableCopy": "השבתת העתקה",
"disableCopyDesc": "מניעת העתקת טקסט מההפעלה המרוחקת",
"disablePaste": "השבתת הדבקה",
"disablePasteDesc": "מניעת הדבקת טקסט לתוך ההפעלה המרוחקת",
"createPathIfMissing": "צור נתיב אם חסר",
"createPathIfMissingDesc": "צור אוטומטית את ספריית ההקלטה",
"excludeOutput": "אי הכללת פלט",
"excludeOutputDesc": "אל תקליטו פלט מסך (מטא-נתונים בלבד)",
"excludeMouse": "אי הכללת עכבר",
"excludeMouseDesc": "אל תתעדו תנועות עכבר",
"includeKeystrokes": "כלול הקשות מקשים",
"includeKeystrokesDesc": "הקלטת הקשות גולמיות בנוסף לפלט המסך",
"vncPort": "יציאת VNC",
"vncPassword": "סיסמת VNC",
"vncUsernameOptional": "שם משתמש (אופציונלי)",
"vncLeaveBlank": "השאר ריק אם לא נדרש",
"cursorMode": "מצב סמן",
"swapRedBlue": "החלפת אדום/כחול",
"swapRedBlueDesc": "החלפת ערוצי הצבע האדום והכחול (תיקון של כמה בעיות צבע)",
"readOnly": "לקריאה בלבד",
"readOnlyDesc": "צפה במסך השלט הרחוק מבלי לשלוח קלט כלשהו",
"telnetPort": "יציאת טלנט",
"terminalType": "סוג הטרמינל",
"fontName": "שם הגופן",
"fontSize": "גודל גופן",
"colorScheme": "ערכת צבעים",
"backspaceKey": "מקש Backspace",
"colorDepth": "עומק צבע",
"resizeMethod": "שיטת שינוי גודל",
"driveNamePlaceholder": "כונן טרמיקס"
},
"remoteDesktop": "שולחן עבודה מרוחק",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "טלנט",
"virtualNetwork": "רשת וירטואלית",
"unencryptedShell": "מעטפת לא מוצפנת",
"remoteDesktopNotes": "הערות",
"userDefaultsNote": "הגדרות שאתה משאיר ללא שינוי עוקבות אחר הגדרות ברירת המחדל של המארח.",
"connectionOriginNote": "באפליקציית שולחן העבודה, RDP, VNC ו-Telnet משתמשים בשרת המרוחק אלא אם כן המארח מוגדר ל-This device, אשר זקוק ל-guacd שאליו מכונה זו יכולה להגיע. הגדר אחד בהגדרות שולחן העבודה המרוחק, או לכל מארח תחת guacd Proxy.",
"docsLink": "הצג מסמכים",
"connectionOriginGuacamoleNote": "RDP, VNC ו-Telnet משתמשים בשרת המרוחק אלא אם כן תבחרו באפשרות \"התקן זה\", אשר דורש גישה ל-guacd. הגדר קוד כזה בהגדרת כתובת ה-URL של guacd, או לכל מחשב מארח תחת Proxy של guacd."
},
"onboarding": {
"feature_desktop": "שולחן עבודה מרוחק",
"feature_desktop_desc": "פתחו הפעלות RDP, VNC ו-Telnet בכרטיסייה, בדומה ל-SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "עומק צבע",
"description": "משמש עבור מארחי RDP שמשאירים אותו כברירת מחדל."
},
"resizeMethod": {
"label": "שיטת שינוי גודל",
"description": "כיצד מסך השלט עוקב אחר גודל החלון."
},
"resizeDisplayUpdate": "עדכון תצוגה",
"resizeReconnect": "התחבר מחדש",
"inherit": "לָרֶשֶׁת",
"on": "עַל",
"off": "כבוי",
"forceLossless": {
"label": "כפה ללא אובדן",
"description": "כפיית קידוד תמונה ללא אובדן נתונים (איכות גבוהה יותר, רוחב פס גדול יותר)"
},
"enableWallpaper": {
"label": "טָפֵּט",
"description": "הצגת טפט שולחן עבודה (השבתה משפרת את הביצועים)"
},
"enableFontSmoothing": {
"label": "החלקת גופנים",
"description": "הפעלת עיבוד גופני ClearType"
},
"enableDesktopComposition": {
"label": "קומפוזיציה לשולחן העבודה",
"description": "הפעלת אפקטים של Aero Glass"
},
"disableAudio": {
"label": "השבתת שמע",
"description": "השתקת כל האודיו מההפעלה המרוחקת"
},
"enablePrinting": {
"label": "הפעל הדפסה",
"description": "ניתוב מחדש של מדפסות מקומיות להפעלה מרוחקת"
},
"enableDrive": {
"label": "הפעל ניתוב מחדש של כונן",
"description": "מיפוי תיקייה מקומית ככונן בהפעלה מרוחקת"
},
"disableCopy": {
"label": "השבתת העתקה",
"description": "מניעת העתקת טקסט מההפעלה המרוחקת"
},
"disablePaste": {
"label": "השבתת הדבקה",
"description": "מניעת הדבקת טקסט לתוך ההפעלה המרוחקת"
},
"group": "ברירות מחדל של שולחן עבודה מרוחק",
"colorDepth16": "16 סיביות",
"colorDepth24": "24 סיביות",
"colorDepth32": "32 סיביות"
},
"admin": {
"enabled": {
"label": "הפעלת שולחן עבודה מרוחק",
"description": "אפשר חיבורי RDP, VNC ו-Telnet דרך guacd."
},
"guacdUrl": {
"label": "כתובת אתר של guacd",
"description": "כאשר guacd מאזין, כ-host:port. משתני הסביבה GUACD_URL, GUACD_HOST ו-GUACD_PORT עוקפים זאת.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "טלנט"
},
"rdpPort": {
"label": "יציאת RDP"
},
"vncPort": {
"label": "יציאת VNC"
},
"telnetPort": {
"label": "יציאת טלנט"
},
"rdpSecurity": {
"label": "מצב אבטחה של RDP"
},
"rdpIgnoreCert": {
"label": "התעלם משגיאות אישור"
},
"guacamoleConfig": {
"label": "הגדרות guacd"
},
"enableToolbar": {
"label": "סרגל הכלים של שולחן עבודה מרוחק",
"description": "הצג את סרגל הכלים בהפעלות RDP, VNC ו-Telnet."
},
"displayGroup": "לְהַצִיג",
"displayDefaultsTab": "שולחן עבודה מרוחק"
}
},
"palette": {
"connectRdp": "התחבר עם RDP",
"connectVnc": "התחבר עם VNC",
"connectTelnet": "התחבר עם טלנט"
},
"permissions": {
"sessions": {
"title": "שיתוף הפעלות שולחן עבודה מרוחק",
"description": "אפשר לשיתוף סשנים וחדרי שיתוף פעולה להציג את סשנים של RDP, VNC ו-Telnet של משתמש זה לאחרים."
}
},
"errors": {
"remoteServerRequired": "נדרש שרת מרוחק. יש להתחבר לשרת מרוחק בהגדרות כדי להשתמש בסוג חיבור זה."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "{{type}} सत्र से कनेक्ट हो रहा है...",
"checkingGuacd": "Checking remote desktop service...",
"requestingToken": "Requesting {{type}} connection token...",
"openingSession": "Opening {{type}} session...",
"negotiating": "Negotiating {{type}} handshake...",
"sessionReady": "{{type}} session ready",
"connectionError": "कनेक्शन त्रुटि",
"connectionFailed": "कनेक्शन विफल",
"failedToConnect": "कनेक्शन टोकन प्राप्त करने में विफल",
"hostNotFound": "होस्ट नहीं मिला",
"noHostSelected": "कोई होस्ट चयनित नहीं है",
"reconnect": "पुनः कनेक्ट करें",
"retry": "पुनः प्रयास करें",
"guacdUnavailable": "रिमोट डेस्कटॉप सेवा (guacd) उपलब्ध नहीं है। कृपया सुनिश्चित करें कि guacd चल रहा है और पहुंच योग्य है और एडमिन सेटिंग्स में ठीक से कॉन्फ़िगर किया गया है।",
"credentialPromptTitle": "Enter RDP Credentials",
"credentialPromptDescription": "This host is set to prompt for credentials on connect. They are used for this session only and are not saved.",
"connect": "कनेक्ट",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Drive Files",
"parent": "Parent directory",
"refresh": "ताज़ा करें",
"empty": "यह फ़ोल्डर खाली है",
"listFailed": "Failed to read this folder",
"download": "डाउनलोड",
"downloadFailed": "Failed to download {{name}}",
"upload": "Upload files",
"uploading": "{{name}} अपलोड हो रहा है...",
"uploaded": "Uploaded {{name}}",
"uploadFailed": "Failed to upload {{name}}",
"uploadDisabled": "Uploads are disabled for this connection",
"driveUnavailable": "Enable RDP drive redirection before uploading files",
"driveNotWritable": "guacd cannot write to the drive folder. Check GUACD_DRIVE_PATH (or the host's Drive Path) and its permissions.",
"dropToUpload": "Drop files to upload"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (लॉक स्क्रीन)",
"winKey": "विंडोज की",
"superL": "Super+L (Lock Screen)",
"superKey": "Super Key",
"super": "Super",
"cmdL": "Cmd+L (Lock Screen)",
"cmdKey": "Command Key",
"cmd": "Cmd",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Shift",
"win": "Win",
"stickyActive": "{{key}} (लैच्ड - रिलीज़ करने के लिए क्लिक करें)",
"stickyInactive": "{{key}} (लैच करने के लिए क्लिक करें)",
"esc": "Escape",
"tab": "Tab",
"home": "Home",
"end": "एंड",
"pageUp": "Page Up",
"pageDown": "Page Down",
"arrowUp": "ऊपर तीर",
"arrowDown": "नीचे तीर",
"arrowLeft": "बाएँ तीर",
"arrowRight": "दाएँ तीर",
"fnToggle": "फ़ंक्शन कुंजियाँ",
"reconnect": "सत्र पुनः कनेक्ट करें",
"collapse": "टूलबार समेटें",
"expand": "टूलबार विस्तारित करें",
"dragHandle": "स्थान बदलने के लिए खींचें",
"hide": "Hide for this session. To disable it permanently, open Host Settings → General → Connection Toolbar.",
"zoomIn": "Zoom in",
"zoomOut": "Zoom out",
"resetZoom": "Reset zoom",
"fitToScreen": "Fit to screen",
"switchToTrackpad": "Switch to trackpad mode (drag to move cursor, tap to click)",
"switchToTouch": "Switch to touch mode (tap directly where you want to click)"
},
"disabled": "Remote Desktop is turned off. An admin can turn it on in Settings."
},
"hosts": {
"nativeRdpOpened": "Opened in Windows Remote Desktop",
"nativeRdpFailed": "Failed to open Windows Remote Desktop",
"openNativeRdp": "Open in Windows Remote Desktop",
"guac": {
"toolbar": "Toolbar",
"connection": "कनेक्शन",
"authentication": "प्रमाणीकरण",
"storedCredential": "संग्रहीत क्रेडेंशियल",
"authMethod": "प्रमाणीकरण विधि",
"authTypeDirect": "सीधा",
"authTypeCredential": "क्रेडेंशियल",
"authTypeNone": "कोई नहीं",
"authTypeNoneDesc": "No credentials are stored. You'll be prompted for a username and password each time you connect; they are not saved.",
"selectCredential": "एक क्रेडेंशियल चुनें...",
"connectionSettings": "कनेक्शन सेटिंग्स",
"displaySettings": "प्रदर्शन सेटिंग्स",
"audioSettings": "ऑडियो सेटिंग्स",
"rdpPerformance": "RDP प्रदर्शन",
"deviceRedirection": "डिवाइस पुनर्निर्देशन",
"session": "सत्र",
"gateway": "गेटवे",
"remoteApp": "RemoteApp",
"clipboard": "क्लिपबोर्ड",
"sessionRecording": "सत्र रिकॉर्डिंग",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "VNC सेटिंग्स",
"terminalSettings": "टर्मिनल सेटिंग्स",
"rdpPort": "RDP पोर्ट",
"username": "उपयोगकर्ता नाम",
"password": "पासवर्ड",
"passwordSaved": "Password saved, type to change",
"domain": "डोमेन",
"securityMode": "सुरक्षा मोड",
"width": "चौड़ाई",
"height": "ऊंचाई",
"dpi": "DPI",
"clientName": "क्लाइंट नाम",
"initialProgram": "आरंभिक प्रोग्राम",
"serverLayout": "सर्वर लेआउट",
"timezone": "समय क्षेत्र",
"loadBalanceInfo": "लोड बैलेंस जानकारी / कुकी",
"loadBalanceInfoDesc": "RDS फ़ार्म लोड संतुलन के लिए RD कनेक्शन ब्रोकर कुकी (उदा. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacd प्रॉक्सी",
"guacdHostname": "guacd होस्ट",
"guacdHostnamePlaceholder": "वैश्विक डिफ़ॉल्ट",
"guacdPort": "guacd पोर्ट",
"guacdProxyDesc": "इस कनेक्शन के लिए वैश्विक guacd इंस्टेंस को अधिलेखित करें। वैश्विक रूप से कॉन्फ़िगर किए गए guacd का उपयोग करने के लिए खाली छोड़ें।",
"gatewayHostname": "गेटवे होस्टनाम",
"gatewayPort": "गेटवे पोर्ट",
"gatewayUsername": "गेटवे उपयोगकर्ता नाम",
"gatewayPassword": "गेटवे पासवर्ड",
"gatewayDomain": "गेटवे डोमेन",
"remoteAppProgram": "RemoteApp प्रोग्राम",
"workingDirectory": "कार्यशील निर्देशिका",
"arguments": "तर्क",
"normalizeLineEndings": "लाइन अंत सामान्यीकृत करें",
"macAddress": "MAC पता",
"broadcastAddress": "प्रसारण पता",
"udpPort": "UDP पोर्ट",
"waitTimeS": "प्रतीक्षा समय (s)",
"driveName": "ड्राइव का नाम",
"drivePath": "ड्राइव पथ",
"ignoreCertificate": "प्रमाणपत्र अनदेखा करें",
"ignoreCertificateDesc": "स्व-हस्ताक्षरित प्रमाणपत्र वाले होस्ट से कनेक्शन की अनुमति दें",
"forceLossless": "दोषरहित बाध्य करें",
"forceLosslessDesc": "दोषरहित छवि एन्कोडिंग बाध्य करें (उच्च गुणवत्ता, अधिक बैंडविड्थ)",
"disableAudio": "ऑडियो अक्षम करें",
"disableAudioDesc": "दूरस्थ सत्र से सभी ऑडियो म्यूट करें",
"enableAudioInput": "ऑडियो इनपुट सक्षम करें (माइक्रोफ़ोन)",
"enableAudioInputDesc": "स्थानीय माइक्रोफ़ोन को दूरस्थ सत्र में अग्रेषित करें",
"wallpaper": "वॉलपेपर",
"wallpaperDesc": "डेस्कटॉप वॉलपेपर दिखाएं (अक्षम करने से प्रदर्शन बेहतर होता है)",
"theming": "थीमिंग",
"themingDesc": "दृश्य थीम और शैलियाँ सक्षम करें",
"fontSmoothing": "फ़ॉन्ट स्मूथिंग",
"fontSmoothingDesc": "ClearType फ़ॉन्ट रेंडरिंग सक्षम करें",
"fullWindowDrag": "पूर्ण विंडो ड्रैग",
"fullWindowDragDesc": "ड्रैग करते समय विंडो सामग्री दिखाएं",
"desktopComposition": "डेस्कटॉप कंपोज़िशन",
"desktopCompositionDesc": "Aero ग्लास प्रभाव सक्षम करें",
"menuAnimations": "मेन्यू एनिमेशन",
"menuAnimationsDesc": "मेन्यू फेड और स्लाइड एनिमेशन सक्षम करें",
"disableBitmapCaching": "बिटमैप कैशिंग अक्षम करें",
"disableBitmapCachingDesc": "बिटमैप कैश बंद करें (गड़बड़ियों में मदद कर सकता है)",
"disableOffscreenCaching": "ऑफस्क्रीन कैशिंग अक्षम करें",
"disableOffscreenCachingDesc": "ऑफस्क्रीन कैश बंद करें",
"disableGlyphCaching": "ग्लिफ़ कैशिंग अक्षम करें",
"disableGlyphCachingDesc": "ग्लिफ़ कैश बंद करें",
"enableGfx": "GFX सक्षम करें",
"enableGfxDesc": "RemoteFX ग्राफ़िक्स पाइपलाइन का उपयोग करें",
"enablePrinting": "प्रिंटिंग सक्षम करें",
"enablePrintingDesc": "स्थानीय प्रिंटर को दूरस्थ सत्र में पुनर्निर्देशित करें",
"enableDriveRedirection": "ड्राइव पुनर्निर्देशन सक्षम करें",
"enableDriveRedirectionDesc": "स्थानीय फ़ोल्डर को दूरस्थ सत्र में ड्राइव के रूप में मैप करें",
"createDrivePath": "ड्राइव पथ बनाएँ",
"createDrivePathDesc": "यदि फ़ोल्डर मौजूद न हो तो स्वचालित रूप से बनाएँ",
"disableDownload": "डाउनलोड अक्षम करें",
"disableDownloadDesc": "रिमोट सत्र से फ़ाइलें डाउनलोड करना रोकें",
"disableUpload": "अपलोड अक्षम करें",
"disableUploadDesc": "रिमोट सत्र पर फ़ाइलें अपलोड करना रोकें",
"enableTouch": "टच सक्षम करें",
"enableTouchDesc": "टच इनपुट अग्रेषण सक्षम करें",
"consoleSession": "कंसोल सत्र",
"consoleSessionDesc": "नए सत्र के बजाय कंसोल (सत्र 0) से कनेक्ट करें",
"sendWolPacket": "WOL पैकेट भेजें",
"sendWolPacketDesc": "कनेक्ट करने से पहले इस होस्ट को जगाने के लिए मैजिक पैकेट भेजें",
"disableCopy": "कॉपी अक्षम करें",
"disableCopyDesc": "रिमोट सत्र से टेक्स्ट कॉपी करना रोकें",
"disablePaste": "पेस्ट अक्षम करें",
"disablePasteDesc": "रिमोट सत्र में टेक्स्ट पेस्ट करना रोकें",
"createPathIfMissing": "यदि पथ मौजूद न हो तो बनाएँ",
"createPathIfMissingDesc": "रिकॉर्डिंग निर्देशिका स्वचालित रूप से बनाएँ",
"excludeOutput": "आउटपुट बहिष्कृत करें",
"excludeOutputDesc": "स्क्रीन आउटपुट रिकॉर्ड न करें (केवल मेटाडेटा)",
"excludeMouse": "माउस बहिष्कृत करें",
"excludeMouseDesc": "माउस गतिविधियाँ रिकॉर्ड न करें",
"includeKeystrokes": "कीस्ट्रोक्स शामिल करें",
"includeKeystrokesDesc": "स्क्रीन आउटपुट के अतिरिक्त कच्चे कीस्ट्रोक्स रिकॉर्ड करें",
"vncPort": "VNC पोर्ट",
"vncPassword": "VNC पासवर्ड",
"vncUsernameOptional": "उपयोगकर्ता नाम (वैकल्पिक)",
"vncLeaveBlank": "यदि आवश्यक न हो तो खाली छोड़ें",
"cursorMode": "कर्सर मोड",
"swapRedBlue": "लाल/नीला अदल-बदल करें",
"swapRedBlueDesc": "लाल और नीले रंग चैनलों को अदल-बदल करें (कुछ रंग समस्याओं को ठीक करता है)",
"readOnly": "केवल-पठनीय",
"readOnlyDesc": "बिना कोई इनपुट भेजे रिमोट स्क्रीन देखें",
"telnetPort": "टेलनेट पोर्ट",
"terminalType": "टर्मिनल प्रकार",
"fontName": "फ़ॉन्ट नाम",
"fontSize": "फ़ॉन्ट आकार",
"colorScheme": "रंग योजना",
"backspaceKey": "बैकस्पेस कुंजी",
"colorDepth": "रंग गहराई",
"resizeMethod": "रीसाइज़ विधि",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "दूरस्थ डेस्कटॉप",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "वर्चुअल नेटवर्क",
"unencryptedShell": "अनएन्क्रिप्टेड शेल",
"remoteDesktopNotes": "नोट्स",
"userDefaultsNote": "Settings you leave unchanged follow your host defaults.",
"connectionOriginNote": "On the desktop app, RDP, VNC and Telnet use the remote server unless the host is set to This device, which needs a guacd this machine can reach. Set one in the Remote Desktop settings, or per host under guacd Proxy.",
"docsLink": "दस्तावेज़ देखें",
"connectionOriginGuacamoleNote": "RDP, VNC and Telnet use the remote server unless you pick \"This device\", which needs a guacd this machine can reach. Set one in the guacd URL setting, or per host under guacd Proxy."
},
"onboarding": {
"feature_desktop": "Remote desktop",
"feature_desktop_desc": "Open RDP, VNC and Telnet sessions in a tab, same as SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "रंग गहराई",
"description": "Used for RDP hosts that leave it on their default."
},
"resizeMethod": {
"label": "रीसाइज़ विधि",
"description": "How the remote screen follows the window size."
},
"resizeDisplayUpdate": "Display Update",
"resizeReconnect": "पुनः कनेक्ट करें",
"inherit": "Inherit",
"on": "चालू",
"off": "बंद",
"forceLossless": {
"label": "दोषरहित बाध्य करें",
"description": "दोषरहित छवि एन्कोडिंग बाध्य करें (उच्च गुणवत्ता, अधिक बैंडविड्थ)"
},
"enableWallpaper": {
"label": "वॉलपेपर",
"description": "डेस्कटॉप वॉलपेपर दिखाएं (अक्षम करने से प्रदर्शन बेहतर होता है)"
},
"enableFontSmoothing": {
"label": "फ़ॉन्ट स्मूथिंग",
"description": "ClearType फ़ॉन्ट रेंडरिंग सक्षम करें"
},
"enableDesktopComposition": {
"label": "डेस्कटॉप कंपोज़िशन",
"description": "Aero ग्लास प्रभाव सक्षम करें"
},
"disableAudio": {
"label": "ऑडियो अक्षम करें",
"description": "दूरस्थ सत्र से सभी ऑडियो म्यूट करें"
},
"enablePrinting": {
"label": "प्रिंटिंग सक्षम करें",
"description": "स्थानीय प्रिंटर को दूरस्थ सत्र में पुनर्निर्देशित करें"
},
"enableDrive": {
"label": "ड्राइव पुनर्निर्देशन सक्षम करें",
"description": "स्थानीय फ़ोल्डर को दूरस्थ सत्र में ड्राइव के रूप में मैप करें"
},
"disableCopy": {
"label": "कॉपी अक्षम करें",
"description": "रिमोट सत्र से टेक्स्ट कॉपी करना रोकें"
},
"disablePaste": {
"label": "पेस्ट अक्षम करें",
"description": "रिमोट सत्र में टेक्स्ट पेस्ट करना रोकें"
},
"group": "Remote Desktop defaults",
"colorDepth16": "16-bit",
"colorDepth24": "24-bit",
"colorDepth32": "32-bit"
},
"admin": {
"enabled": {
"label": "Enable Remote Desktop",
"description": "Allow RDP, VNC and Telnet connections through guacd."
},
"guacdUrl": {
"label": "guacd URL",
"description": "Where guacd listens, as host:port. The GUACD_URL, GUACD_HOST and GUACD_PORT environment variables override this.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP port"
},
"vncPort": {
"label": "VNC port"
},
"telnetPort": {
"label": "Telnet port"
},
"rdpSecurity": {
"label": "RDP security mode"
},
"rdpIgnoreCert": {
"label": "Ignore certificate errors"
},
"guacamoleConfig": {
"label": "guacd settings"
},
"enableToolbar": {
"label": "Remote desktop toolbar",
"description": "Show the toolbar in RDP, VNC and Telnet sessions."
},
"displayGroup": "Display",
"displayDefaultsTab": "दूरस्थ डेस्कटॉप"
}
},
"palette": {
"connectRdp": "Connect with RDP",
"connectVnc": "Connect with VNC",
"connectTelnet": "Connect with Telnet"
},
"permissions": {
"sessions": {
"title": "Share remote desktop sessions",
"description": "Let session sharing and collab rooms show this user's RDP, VNC and Telnet sessions to others."
}
},
"errors": {
"remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Kapcsolódás ehhez: {{type}} munkamenet...",
"checkingGuacd": "Távoli asztal szolgáltatás ellenőrzése...",
"requestingToken": "{{type}} kapcsolati token kérése...",
"openingSession": "{{type}} munkamenet megnyitása...",
"negotiating": "Kézfogásról tárgyalunk... {{type}}",
"sessionReady": "{{type}} munkamenet kész",
"connectionError": "Csatlakozási hiba",
"connectionFailed": "Kapcsolódás sikertelen",
"failedToConnect": "Nem sikerült lekérni a kapcsolati tokent",
"hostNotFound": "Nem található gazdagép",
"noHostSelected": "Nincs kiválasztva gazdagép",
"reconnect": "Újracsatlakozás",
"retry": "Újrapróbálkozás",
"guacdUnavailable": "A távoli asztali szolgáltatás (guacd) nem érhető el. Kérjük, győződjön meg róla, hogy a guacd fut, elérhető és megfelelően van konfigurálva az adminisztrátori beállításokban.",
"credentialPromptTitle": "RDP hitelesítő adatok megadása",
"credentialPromptDescription": "Ez a gazdagép úgy van beállítva, hogy csatlakozáskor hitelesítő adatokat kérjen. Ezeket csak ehhez a munkamenethez használjuk, és nem kerülnek mentésre.",
"connect": "Csatlakozás",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Drive-fájlok",
"parent": "Szülőkönyvtár",
"refresh": "Frissítés",
"empty": "Ez a mappa üres",
"listFailed": "Nem sikerült beolvasni ezt a mappát",
"download": "Letöltés",
"downloadFailed": "Nem sikerült letölteni {{name}}",
"upload": "Fájlok feltöltése",
"uploading": "Feltöltés {{name}}...",
"uploaded": "Feltöltve {{name}}",
"uploadFailed": "Nem sikerült feltölteni {{name}}",
"uploadDisabled": "A feltöltések le vannak tiltva ennél a kapcsolatnál.",
"driveUnavailable": "RDP meghajtó átirányításának engedélyezése fájlok feltöltése előtt",
"driveNotWritable": "A guacd nem tud írni a meghajtó mappájába. Ellenőrizd a GUACD_DRIVE_PATH-ot (vagy a gazdagép meghajtó elérési útját) és a hozzá tartozó jogosultságokat.",
"dropToUpload": "Fájlok húzása a feltöltéshez"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Képernyőzár)",
"winKey": "Windows billentyű",
"superL": "Super+L (Képernyőzár)",
"superKey": "Szuperkulcs",
"super": "Szuper",
"cmdL": "Cmd+L (Képernyő lezárása)",
"cmdKey": "Parancsbillentyű",
"cmd": "Parancs",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Váltás",
"win": "Győzelem",
"stickyActive": "{{key}} (reteszelve - kattintson a kioldáshoz)",
"stickyInactive": "{{key}} (kattintson a rögzítéshez)",
"esc": "Menekülés",
"tab": "Fül",
"home": "Otthon",
"end": "Vége",
"pageUp": "Oldal fel",
"pageDown": "Oldal le",
"arrowUp": "Felfelé nyíl",
"arrowDown": "Lefelé mutató nyíl",
"arrowLeft": "Balra nyíl",
"arrowRight": "Jobbra nyíl",
"fnToggle": "Funkcióbillentyűk",
"reconnect": "Munkamenet újracsatlakoztatása",
"collapse": "Eszköztár összecsukása",
"expand": "Eszköztár kibontása",
"dragHandle": "Húzással áthelyezhető",
"hide": "Elrejtés ehhez a munkamenethez. A végleges letiltáshoz nyissa meg a Gazdagép beállításai → Általános → Kapcsolat eszköztár menüpontot.",
"zoomIn": "Nagyítás",
"zoomOut": "Kicsinyítés",
"resetZoom": "Nagyítás visszaállítása",
"fitToScreen": "Képernyőhöz igazítás",
"switchToTrackpad": "Váltás trackpad módra (húzással mozgathatja a kurzort, koppintással kattinthat)",
"switchToTouch": "Váltás érintőképernyős módra (koppintson közvetlenül oda, ahová kattintani szeretne)"
},
"disabled": "A Távoli asztal ki van kapcsolva. Egy rendszergazda bekapcsolhatja a Beállításokban."
},
"hosts": {
"nativeRdpOpened": "Megnyitva a Windows Távoli asztal alkalmazásban",
"nativeRdpFailed": "Nem sikerült megnyitni a Windows távoli asztalt",
"openNativeRdp": "Megnyitás a Windows távoli asztalon",
"guac": {
"toolbar": "Eszköztár",
"connection": "Kapcsolat",
"authentication": "Hitelesítés",
"storedCredential": "Tárolt hitelesítő adatok",
"authMethod": "Aut. módszer",
"authTypeDirect": "Közvetlen",
"authTypeCredential": "Hitelesítő adat",
"authTypeNone": "Egyik sem",
"authTypeNoneDesc": "Nincsenek tárolt hitelesítő adatok. Minden csatlakozáskor meg kell adnia a felhasználónevet és a jelszót; ezek nem kerülnek mentésre.",
"selectCredential": "Válasszon hitelesítő adatot...",
"connectionSettings": "Kapcsolati beállítások",
"displaySettings": "Kijelzőbeállítások",
"audioSettings": "Hangbeállítások",
"rdpPerformance": "RDP-teljesítmény",
"deviceRedirection": "Eszközátirányítás",
"session": "Ülés",
"gateway": "Átjáró",
"remoteApp": "RemoteApp",
"clipboard": "Vágólap",
"sessionRecording": "Munkamenet-felvétel",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "VNC-beállítások",
"terminalSettings": "Terminálbeállítások",
"rdpPort": "RDP-port",
"username": "Felhasználónév",
"password": "Jelszó",
"passwordSaved": "Jelszó mentve, gépelje be a módosításhoz",
"domain": "Domain",
"securityMode": "Biztonsági mód",
"width": "Szélesség",
"height": "Magasság",
"dpi": "DPI",
"clientName": "Ügyfél neve",
"initialProgram": "Kezdeti program",
"serverLayout": "Szerver elrendezése",
"timezone": "Időzóna",
"loadBalanceInfo": "Terheléselosztási információk / Süti",
"loadBalanceInfoDesc": "RD Connection Broker süti az RDS farm terheléselosztásához (pl. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacd proxy",
"guacdHostname": "guacd Host",
"guacdHostnamePlaceholder": "Globális alapértelmezett",
"guacdPort": "guacd kikötő",
"guacdProxyDesc": "Írja felül a globális guacd példányt ehhez a kapcsolathoz. Hagyja üresen a globálisan konfigurált guacd használatához.",
"gatewayHostname": "Átjáró állomásneve",
"gatewayPort": "Átjáró kikötő",
"gatewayUsername": "Átjáró felhasználóneve",
"gatewayPassword": "Átjáró jelszava",
"gatewayDomain": "Átjáró domain",
"remoteAppProgram": "RemoteApp program",
"workingDirectory": "Munkakönyvtár",
"arguments": "érvek",
"normalizeLineEndings": "Sorvégek normalizálása",
"macAddress": "MAC-cím",
"broadcastAddress": "Sugárzási cím",
"udpPort": "UDP port",
"waitTimeS": "Várakozási idő (s)",
"driveName": "Meghajtó neve",
"drivePath": "Útvonal",
"ignoreCertificate": "Tanúsítvány figyelmen kívül hagyása",
"ignoreCertificateDesc": "Kapcsolódás engedélyezése önaláírt tanúsítványokkal rendelkező gazdagépekhez",
"forceLossless": "Veszteségmentes erő",
"forceLosslessDesc": "Veszteségmentes képkódolás kényszerítése (jobb minőség, nagyobb sávszélesség)",
"disableAudio": "Hang letiltása",
"disableAudioDesc": "Némítsa el az összes hangot a távoli munkamenetből",
"enableAudioInput": "Hangbemenet engedélyezése (mikrofon)",
"enableAudioInputDesc": "Helyi mikrofon továbbítása a távoli munkamenetbe",
"wallpaper": "Tapéta",
"wallpaperDesc": "Asztali háttérkép megjelenítése (a letiltás javítja a teljesítményt)",
"theming": "Témázás",
"themingDesc": "Vizuális témák és stílusok engedélyezése",
"fontSmoothing": "Betűsimítás",
"fontSmoothingDesc": "ClearType betűtípus-megjelenítés engedélyezése",
"fullWindowDrag": "Teljes ablak húzása",
"fullWindowDragDesc": "Ablak tartalmának megjelenítése húzás közben",
"desktopComposition": "Asztali kompozíció",
"desktopCompositionDesc": "Aero Glass effektek engedélyezése",
"menuAnimations": "Menüanimációk",
"menuAnimationsDesc": "Menü elhalványulásának és diaanimációk engedélyezése",
"disableBitmapCaching": "Bitképes gyorsítótár letiltása",
"disableBitmapCachingDesc": "Kapcsold ki a bitképes gyorsítótárat (segíthet a hibákon)",
"disableOffscreenCaching": "Képernyőn kívüli gyorsítótár letiltása",
"disableOffscreenCachingDesc": "Kapcsolja ki a képernyőn kívüli gyorsítótárat",
"disableGlyphCaching": "Karakterjel-gyorsítótár letiltása",
"disableGlyphCachingDesc": "Kapcsolja ki a karakterjel-gyorsítótárat",
"enableGfx": "GFX engedélyezése",
"enableGfxDesc": "RemoteFX grafikus folyamatsor használata",
"enablePrinting": "Nyomtatás engedélyezése",
"enablePrintingDesc": "Helyi nyomtatók átirányítása a távoli munkamenetre",
"enableDriveRedirection": "Meghajtó átirányításának engedélyezése",
"enableDriveRedirectionDesc": "Helyi mappa meghajtóként való leképezése a távoli munkamenetben",
"createDrivePath": "Útvonal létrehozása",
"createDrivePathDesc": "Automatikusan létrehozza a mappát, ha az nem létezik",
"disableDownload": "Letöltés letiltása",
"disableDownloadDesc": "Fájlok letöltésének megakadályozása a távoli munkamenetből",
"disableUpload": "Feltöltés letiltása",
"disableUploadDesc": "Fájlok távoli munkamenetbe való feltöltésének megakadályozása",
"enableTouch": "Érintés engedélyezése",
"enableTouchDesc": "Érintéses bevitel továbbításának engedélyezése",
"consoleSession": "Konzol munkamenet",
"consoleSessionDesc": "Csatlakozás a konzolhoz (0. munkamenet) egy új munkamenet helyett",
"sendWolPacket": "WOL csomag küldése",
"sendWolPacketDesc": "Küldjön egy varázslatos csomagot a gazdagép felébresztéséhez a csatlakozás előtt",
"disableCopy": "Másolás letiltása",
"disableCopyDesc": "Szöveg másolásának megakadályozása a távoli munkamenetből",
"disablePaste": "Beillesztés letiltása",
"disablePasteDesc": "Szöveg beillesztésének megakadályozása a távoli munkamenetbe",
"createPathIfMissing": "Útvonal létrehozása, ha hiányzik",
"createPathIfMissingDesc": "Felvételi könyvtár automatikus létrehozása",
"excludeOutput": "Kimenet kizárása",
"excludeOutputDesc": "Ne rögzítse a képernyőkimenetet (csak metaadatok)",
"excludeMouse": "Egér kizárása",
"excludeMouseDesc": "Ne rögzítse az egér mozgását",
"includeKeystrokes": "Billentyűleütések belefoglalása",
"includeKeystrokesDesc": "Nyers billentyűleütések rögzítése a képernyőn megjelenő kimenet mellett",
"vncPort": "VNC port",
"vncPassword": "VNC jelszó",
"vncUsernameOptional": "Felhasználónév (opcionális)",
"vncLeaveBlank": "Hagyja üresen, ha nem szükséges",
"cursorMode": "Kurzor mód",
"swapRedBlue": "Piros/Kék csere",
"swapRedBlueDesc": "Cserélje fel a piros és kék színcsatornákat (javít néhány színproblémát)",
"readOnly": "Csak olvasható",
"readOnlyDesc": "Távoli képernyő megtekintése bemenet küldése nélkül",
"telnetPort": "Telnet port",
"terminalType": "Terminál típusa",
"fontName": "Betűtípus neve",
"fontSize": "Betűméret",
"colorScheme": "Színséma",
"backspaceKey": "Visszatörlés billentyű",
"colorDepth": "Színmélység",
"resizeMethod": "Átméretezési módszer",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "Távoli asztal",
"tabRdp": "Vidékfejlesztési Program",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Virtuális hálózat",
"unencryptedShell": "Titkosítatlan shell",
"remoteDesktopNotes": "Megjegyzések",
"userDefaultsNote": "A változatlanul hagyott beállítások a gazdagép alapértelmezett beállításait követik.",
"connectionOriginNote": "Az asztali alkalmazásban az RDP, a VNC és a Telnet a távoli szervert használja, kivéve, ha a gazdagép értéke „Ez az eszköz”, amelyhez egy olyan GUACD-re van szükség, amelyet ez a gép el tud érni. Állítson be egyet a Távoli asztal beállításaiban, vagy gazdagépenként a GUACD Proxy alatt.",
"docsLink": "Dokumentumok megtekintése",
"connectionOriginGuacamoleNote": "Az RDP, a VNC és a Telnet a távoli szervert használja, kivéve, ha az „Ez az eszköz” lehetőséget választod, amelyhez egy olyan GUACD-re van szükség, amelyet ez a gép el tud érni. Állíts be egyet a GUACD URL-beállításaiban, vagy hosztonként a GUACD Proxy alatt."
},
"onboarding": {
"feature_desktop": "Távoli asztal",
"feature_desktop_desc": "Nyisson meg RDP, VNC és Telnet munkameneteket egy lapon, ugyanúgy, mint az SSH-n."
},
"settings": {
"user": {
"colorDepth": {
"label": "Színmélység",
"description": "Azoknál az RDP-gazdagépeknél használatos, amelyek az alapértelmezett értéken hagyják."
},
"resizeMethod": {
"label": "Átméretezési módszer",
"description": "Hogyan követi a távoli képernyő az ablak méretét."
},
"resizeDisplayUpdate": "Kijelzőfrissítés",
"resizeReconnect": "Újracsatlakozás",
"inherit": "Örököl",
"on": "On",
"off": "Le",
"forceLossless": {
"label": "Veszteségmentes erő",
"description": "Veszteségmentes képkódolás kényszerítése (jobb minőség, nagyobb sávszélesség)"
},
"enableWallpaper": {
"label": "Tapéta",
"description": "Asztali háttérkép megjelenítése (a letiltás javítja a teljesítményt)"
},
"enableFontSmoothing": {
"label": "Betűsimítás",
"description": "ClearType betűtípus-megjelenítés engedélyezése"
},
"enableDesktopComposition": {
"label": "Asztali kompozíció",
"description": "Aero Glass effektek engedélyezése"
},
"disableAudio": {
"label": "Hang letiltása",
"description": "Némítsa el az összes hangot a távoli munkamenetből"
},
"enablePrinting": {
"label": "Nyomtatás engedélyezése",
"description": "Helyi nyomtatók átirányítása a távoli munkamenetre"
},
"enableDrive": {
"label": "Meghajtó átirányításának engedélyezése",
"description": "Helyi mappa meghajtóként való leképezése a távoli munkamenetben"
},
"disableCopy": {
"label": "Másolás letiltása",
"description": "Szöveg másolásának megakadályozása a távoli munkamenetből"
},
"disablePaste": {
"label": "Beillesztés letiltása",
"description": "Szöveg beillesztésének megakadályozása a távoli munkamenetbe"
},
"group": "Távoli asztal alapértelmezett beállításai",
"colorDepth16": "16 bites",
"colorDepth24": "24 bites",
"colorDepth32": "32 bites"
},
"admin": {
"enabled": {
"label": "Távoli asztal engedélyezése",
"description": "RDP, VNC és Telnet kapcsolatok engedélyezése GUACD-n keresztül."
},
"guacdUrl": {
"label": "guacd URL",
"description": "Ahol a guacd figyel, host:portként. A GUACD_URL, GUACD_HOST és GUACD_PORT környezeti változók felülírják ezt.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "Vidékfejlesztési Program"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP-port"
},
"vncPort": {
"label": "VNC-port"
},
"telnetPort": {
"label": "Telnet port"
},
"rdpSecurity": {
"label": "RDP biztonsági mód"
},
"rdpIgnoreCert": {
"label": "Tanúsítványhibák figyelmen kívül hagyása"
},
"guacamoleConfig": {
"label": "guacd beállítások"
},
"enableToolbar": {
"label": "Távoli asztal eszköztár",
"description": "Eszköztár megjelenítése RDP, VNC és Telnet munkamenetekben."
},
"displayGroup": "Kijelző",
"displayDefaultsTab": "Távoli asztal"
}
},
"palette": {
"connectRdp": "Kapcsolódjon az RDP-hez",
"connectVnc": "Csatlakozás VNC-vel",
"connectTelnet": "Csatlakozás Telnet-hez"
},
"permissions": {
"sessions": {
"title": "Távoli asztali munkamenetek megosztása",
"description": "A munkamenet-megosztási és együttműködési szobák megjeleníthetik a felhasználó RDP-, VNC- és Telnet-munkameneteit mások számára."
}
},
"errors": {
"remoteServerRequired": "Távoli szerver szükséges. Csatlakoztasson egy távoli szervert a Beállításokban a kapcsolattípus használatához."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Connecting to {{type}} session...",
"checkingGuacd": "Checking remote desktop service...",
"requestingToken": "Requesting {{type}} connection token...",
"openingSession": "Opening {{type}} session...",
"negotiating": "Negotiating {{type}} handshake...",
"sessionReady": "{{type}} session ready",
"connectionError": "Connection error",
"connectionFailed": "Connection failed",
"failedToConnect": "Failed to get connection token",
"hostNotFound": "Host not found",
"noHostSelected": "No host selected",
"reconnect": "Reconnect",
"retry": "Retry",
"guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.",
"credentialPromptTitle": "Enter RDP Credentials",
"credentialPromptDescription": "This host is set to prompt for credentials on connect. They are used for this session only and are not saved.",
"connect": "Connect",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Drive Files",
"parent": "Parent directory",
"refresh": "Refresh",
"empty": "This folder is empty",
"listFailed": "Failed to read this folder",
"download": "Download",
"downloadFailed": "Failed to download {{name}}",
"upload": "Upload files",
"uploading": "Uploading {{name}}...",
"uploaded": "Uploaded {{name}}",
"uploadFailed": "Failed to upload {{name}}",
"uploadDisabled": "Uploads are disabled for this connection",
"driveUnavailable": "Enable RDP drive redirection before uploading files",
"driveNotWritable": "guacd cannot write to the drive folder. Check GUACD_DRIVE_PATH (or the host's Drive Path) and its permissions.",
"dropToUpload": "Drop files to upload"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Lock Screen)",
"winKey": "Windows Key",
"superL": "Super+L (Lock Screen)",
"superKey": "Super Key",
"super": "Super",
"cmdL": "Cmd+L (Lock Screen)",
"cmdKey": "Command Key",
"cmd": "Cmd",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Shift",
"win": "Win",
"stickyActive": "{{key}} (latched - click to release)",
"stickyInactive": "{{key}} (click to latch)",
"esc": "Escape",
"tab": "Tab",
"home": "Home",
"end": "End",
"pageUp": "Page Up",
"pageDown": "Page Down",
"arrowUp": "Arrow Up",
"arrowDown": "Arrow Down",
"arrowLeft": "Arrow Left",
"arrowRight": "Arrow Right",
"fnToggle": "Function Keys",
"reconnect": "Reconnect Session",
"collapse": "Collapse toolbar",
"expand": "Expand toolbar",
"dragHandle": "Drag to reposition",
"hide": "Hide for this session. To disable it permanently, open Host Settings → General → Connection Toolbar.",
"zoomIn": "Zoom in",
"zoomOut": "Zoom out",
"resetZoom": "Reset zoom",
"fitToScreen": "Fit to screen",
"switchToTrackpad": "Switch to trackpad mode (drag to move cursor, tap to click)",
"switchToTouch": "Switch to touch mode (tap directly where you want to click)"
},
"disabled": "Remote Desktop is turned off. An admin can turn it on in Settings."
},
"hosts": {
"nativeRdpOpened": "Opened in Windows Remote Desktop",
"nativeRdpFailed": "Failed to open Windows Remote Desktop",
"openNativeRdp": "Open in Windows Remote Desktop",
"guac": {
"toolbar": "Toolbar",
"connection": "Connection",
"authentication": "Authentication",
"storedCredential": "Stored Credential",
"authMethod": "Auth Method",
"authTypeDirect": "Direct",
"authTypeCredential": "Credential",
"authTypeNone": "None",
"authTypeNoneDesc": "No credentials are stored. You'll be prompted for a username and password each time you connect; they are not saved.",
"selectCredential": "Select a credential...",
"connectionSettings": "Connection Settings",
"displaySettings": "Display Settings",
"audioSettings": "Audio Settings",
"rdpPerformance": "RDP Performance",
"deviceRedirection": "Device Redirection",
"session": "Session",
"gateway": "Gateway",
"remoteApp": "RemoteApp",
"clipboard": "Clipboard",
"sessionRecording": "Session Recording",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "VNC Settings",
"terminalSettings": "Terminal Settings",
"rdpPort": "RDP Port",
"username": "Nama pengguna",
"password": "Password",
"passwordSaved": "Password saved, type to change",
"domain": "Domain",
"securityMode": "Security Mode",
"width": "Width",
"height": "Height",
"dpi": "DPI",
"clientName": "Client Name",
"initialProgram": "Initial Program",
"serverLayout": "Server Layout",
"timezone": "Timezone",
"loadBalanceInfo": "Load Balance Info / Cookie",
"loadBalanceInfoDesc": "RD Connection Broker cookie for RDS farm load balancing (e.g. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacd Proxy",
"guacdHostname": "guacd Host",
"guacdHostnamePlaceholder": "Global default",
"guacdPort": "guacd Port",
"guacdProxyDesc": "Override the global guacd instance for this connection. Leave blank to use the globally configured guacd.",
"gatewayHostname": "Gateway Hostname",
"gatewayPort": "Gateway Port",
"gatewayUsername": "Gateway Username",
"gatewayPassword": "Gateway Password",
"gatewayDomain": "Gateway Domain",
"remoteAppProgram": "RemoteApp Program",
"workingDirectory": "Working Directory",
"arguments": "Arguments",
"normalizeLineEndings": "Normalize Line Endings",
"macAddress": "Alamat MAC",
"broadcastAddress": "Broadcast Address",
"udpPort": "UDP Port",
"waitTimeS": "Wait Time (s)",
"driveName": "Drive Name",
"drivePath": "Drive Path",
"ignoreCertificate": "Ignore Certificate",
"ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates",
"forceLossless": "Force Lossless",
"forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)",
"disableAudio": "Disable Audio",
"disableAudioDesc": "Mute all audio from the remote session",
"enableAudioInput": "Enable Audio Input (Microphone)",
"enableAudioInputDesc": "Forward local microphone to the remote session",
"wallpaper": "Wallpaper",
"wallpaperDesc": "Show desktop wallpaper (disabling improves performance)",
"theming": "Theming",
"themingDesc": "Enable visual themes and styles",
"fontSmoothing": "Font Smoothing",
"fontSmoothingDesc": "Enable ClearType font rendering",
"fullWindowDrag": "Full Window Drag",
"fullWindowDragDesc": "Show window contents while dragging",
"desktopComposition": "Desktop Composition",
"desktopCompositionDesc": "Enable Aero glass effects",
"menuAnimations": "Menu Animations",
"menuAnimationsDesc": "Enable menu fade and slide animations",
"disableBitmapCaching": "Disable Bitmap Caching",
"disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)",
"disableOffscreenCaching": "Disable Offscreen Caching",
"disableOffscreenCachingDesc": "Turn off offscreen cache",
"disableGlyphCaching": "Disable Glyph Caching",
"disableGlyphCachingDesc": "Turn off glyph cache",
"enableGfx": "Enable GFX",
"enableGfxDesc": "Use RemoteFX graphics pipeline",
"enablePrinting": "Enable Printing",
"enablePrintingDesc": "Redirect local printers to the remote session",
"enableDriveRedirection": "Enable Drive Redirection",
"enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session",
"createDrivePath": "Create Drive Path",
"createDrivePathDesc": "Automatically create the folder if it does not exist",
"disableDownload": "Disable Download",
"disableDownloadDesc": "Prevent downloading files from the remote session",
"disableUpload": "Disable Upload",
"disableUploadDesc": "Prevent uploading files to the remote session",
"enableTouch": "Enable Touch",
"enableTouchDesc": "Enable touch input forwarding",
"consoleSession": "Console Session",
"consoleSessionDesc": "Connect to the console (session 0) instead of a new session",
"sendWolPacket": "Send WOL Packet",
"sendWolPacketDesc": "Send a magic packet to wake this host before connecting",
"disableCopy": "Disable Copy",
"disableCopyDesc": "Prevent copying text from the remote session",
"disablePaste": "Disable Paste",
"disablePasteDesc": "Prevent pasting text into the remote session",
"createPathIfMissing": "Create Path if Missing",
"createPathIfMissingDesc": "Automatically create the recording directory",
"excludeOutput": "Exclude Output",
"excludeOutputDesc": "Do not record screen output (metadata only)",
"excludeMouse": "Exclude Mouse",
"excludeMouseDesc": "Do not record mouse movements",
"includeKeystrokes": "Include Keystrokes",
"includeKeystrokesDesc": "Record raw keystrokes in addition to screen output",
"vncPort": "VNC Port",
"vncPassword": "VNC Password",
"vncUsernameOptional": "Username (optional)",
"vncLeaveBlank": "Leave blank if not required",
"cursorMode": "Cursor Mode",
"swapRedBlue": "Swap Red/Blue",
"swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)",
"readOnly": "Read-only",
"readOnlyDesc": "View the remote screen without sending any input",
"telnetPort": "Telnet Port",
"terminalType": "Terminal Type",
"fontName": "Font Name",
"fontSize": "Font Size",
"colorScheme": "Color Scheme",
"backspaceKey": "Backspace Key",
"colorDepth": "Color Depth",
"resizeMethod": "Resize Method",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "Remote Desktop",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Virtual Network",
"unencryptedShell": "Unencrypted shell",
"remoteDesktopNotes": "Notes",
"userDefaultsNote": "Settings you leave unchanged follow your host defaults.",
"connectionOriginNote": "On the desktop app, RDP, VNC and Telnet use the remote server unless the host is set to This device, which needs a guacd this machine can reach. Set one in the Remote Desktop settings, or per host under guacd Proxy.",
"docsLink": "View docs",
"connectionOriginGuacamoleNote": "RDP, VNC and Telnet use the remote server unless you pick \"This device\", which needs a guacd this machine can reach. Set one in the guacd URL setting, or per host under guacd Proxy."
},
"onboarding": {
"feature_desktop": "Remote desktop",
"feature_desktop_desc": "Open RDP, VNC and Telnet sessions in a tab, same as SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Color Depth",
"description": "Used for RDP hosts that leave it on their default."
},
"resizeMethod": {
"label": "Resize Method",
"description": "How the remote screen follows the window size."
},
"resizeDisplayUpdate": "Display Update",
"resizeReconnect": "Reconnect",
"inherit": "Inherit",
"on": "On",
"off": "Off",
"forceLossless": {
"label": "Force Lossless",
"description": "Force lossless image encoding (higher quality, more bandwidth)"
},
"enableWallpaper": {
"label": "Wallpaper",
"description": "Show desktop wallpaper (disabling improves performance)"
},
"enableFontSmoothing": {
"label": "Font Smoothing",
"description": "Enable ClearType font rendering"
},
"enableDesktopComposition": {
"label": "Desktop Composition",
"description": "Enable Aero glass effects"
},
"disableAudio": {
"label": "Disable Audio",
"description": "Mute all audio from the remote session"
},
"enablePrinting": {
"label": "Enable Printing",
"description": "Redirect local printers to the remote session"
},
"enableDrive": {
"label": "Enable Drive Redirection",
"description": "Map a local folder as a drive in the remote session"
},
"disableCopy": {
"label": "Disable Copy",
"description": "Prevent copying text from the remote session"
},
"disablePaste": {
"label": "Disable Paste",
"description": "Prevent pasting text into the remote session"
},
"group": "Remote Desktop defaults",
"colorDepth16": "16-bit",
"colorDepth24": "24-bit",
"colorDepth32": "32-bit"
},
"admin": {
"enabled": {
"label": "Enable Remote Desktop",
"description": "Allow RDP, VNC and Telnet connections through guacd."
},
"guacdUrl": {
"label": "guacd URL",
"description": "Where guacd listens, as host:port. The GUACD_URL, GUACD_HOST and GUACD_PORT environment variables override this.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP port"
},
"vncPort": {
"label": "VNC port"
},
"telnetPort": {
"label": "Telnet port"
},
"rdpSecurity": {
"label": "RDP security mode"
},
"rdpIgnoreCert": {
"label": "Ignore certificate errors"
},
"guacamoleConfig": {
"label": "guacd settings"
},
"enableToolbar": {
"label": "Remote desktop toolbar",
"description": "Show the toolbar in RDP, VNC and Telnet sessions."
},
"displayGroup": "Display",
"displayDefaultsTab": "Remote Desktop"
}
},
"palette": {
"connectRdp": "Connect with RDP",
"connectVnc": "Connect with VNC",
"connectTelnet": "Connect with Telnet"
},
"permissions": {
"sessions": {
"title": "Share remote desktop sessions",
"description": "Let session sharing and collab rooms show this user's RDP, VNC and Telnet sessions to others."
}
},
"errors": {
"remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Connessione alla sessione {{type}} in corso...",
"checkingGuacd": "Verifica del servizio desktop remoto...",
"requestingToken": "Richiesta del token di connessione {{type}}...",
"openingSession": "Apertura della sessione {{type}}...",
"negotiating": "Negoziazione della stretta di mano {{type}}...",
"sessionReady": "{{type}} sessione pronta",
"connectionError": "Errore di connessione",
"connectionFailed": "Connessione non riuscita",
"failedToConnect": "Impossibile ottenere il token di connessione",
"hostNotFound": "Host non trovato",
"noHostSelected": "Nessun host selezionato",
"reconnect": "Riconnetti",
"retry": "Riprova",
"guacdUnavailable": "Il servizio desktop remoto (guacd) non è disponibile. Assicurati che guacd sia in esecuzione, accessibile e configurato correttamente nelle impostazioni di amministrazione.",
"credentialPromptTitle": "Inserisci le credenziali RDP",
"credentialPromptDescription": "Questo host è configurato per richiedere le credenziali al momento della connessione. Tali credenziali vengono utilizzate solo per questa sessione e non vengono salvate.",
"connect": "Connetti",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "File di Drive",
"parent": "Cartella principale",
"refresh": "Aggiorna",
"empty": "Questa cartella è vuota",
"listFailed": "Impossibile leggere questa cartella",
"download": "Scarica",
"downloadFailed": "Impossibile scaricare {{name}}",
"upload": "Carica i file",
"uploading": "Caricamento di {{name}}...",
"uploaded": "Caricato {{name}}",
"uploadFailed": "Impossibile caricare {{name}}",
"uploadDisabled": "Il caricamento è disabilitato per questa connessione.",
"driveUnavailable": "Abilita il reindirizzamento delle unità RDP prima di caricare i file",
"driveNotWritable": "guacd non può scrivere nella cartella dell'unità. Verificare GUACD_DRIVE_PATH (o il percorso dell'unità host) e le relative autorizzazioni.",
"dropToUpload": "Trascina i file da caricare"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Blocca schermo)",
"winKey": "Tasto Windows",
"superL": "Super+L (Schermata di blocco)",
"superKey": "Super Chiave",
"super": "Super",
"cmdL": "Cmd+L (Blocca schermo)",
"cmdKey": "Tasto di comando",
"cmd": "Comando",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Maiusc",
"win": "Win",
"stickyActive": "{{key}} (bloccato - clicca per rilasciare)",
"stickyInactive": "{{key}} (clicca per bloccare)",
"esc": "Esc",
"tab": "Tab",
"home": "Home",
"end": "Fine",
"pageUp": "Pag Su",
"pageDown": "Pag Giù",
"arrowUp": "Freccia Su",
"arrowDown": "Freccia Giù",
"arrowLeft": "Freccia Sinistra",
"arrowRight": "Freccia Destra",
"fnToggle": "Tasti funzione",
"reconnect": "Riconnetti sessione",
"collapse": "Comprimi barra degli strumenti",
"expand": "Espandi barra degli strumenti",
"dragHandle": "Trascina per riposizionare",
"hide": "Nascondi per questa sessione. Per disabilitarlo in modo permanente, apri Impostazioni host → Generale → Barra degli strumenti di connessione.",
"zoomIn": "Ingrandisci",
"zoomOut": "Riduci lo zoom",
"resetZoom": "Ripristina lo zoom",
"fitToScreen": "Adatto allo schermo",
"switchToTrackpad": "Passa alla modalità trackpad (trascina per spostare il cursore, tocca per fare clic)",
"switchToTouch": "Passa alla modalità touch (tocca direttamente il punto in cui desideri fare clic)"
},
"disabled": "Desktop remoto è disattivato. Un amministratore può attivarlo nelle Impostazioni."
},
"hosts": {
"nativeRdpOpened": "Aperto in Desktop remoto di Windows",
"nativeRdpFailed": "Impossibile aprire Desktop remoto di Windows",
"openNativeRdp": "Apri in Desktop remoto di Windows",
"guac": {
"toolbar": "barra degli strumenti",
"connection": "Connessione",
"authentication": "Autenticazione",
"storedCredential": "Credenziale memorizzata",
"authMethod": "Metodo di autenticazione",
"authTypeDirect": "Diretta",
"authTypeCredential": "Credenziale",
"authTypeNone": "Nessuno",
"authTypeNoneDesc": "Non vengono memorizzate credenziali. Ti verrà richiesto di inserire nome utente e password ogni volta che effettuerai la connessione; questi dati non verranno salvati.",
"selectCredential": "Seleziona una credenziale...",
"connectionSettings": "Impostazioni di connessione",
"displaySettings": "Impostazioni schermo",
"audioSettings": "Impostazioni audio",
"rdpPerformance": "Prestazioni RDP",
"deviceRedirection": "Reindirizzamento dispositivi",
"session": "Sessione",
"gateway": "Gateway",
"remoteApp": "RemoteApp",
"clipboard": "Appunti",
"sessionRecording": "Registrazione sessione",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "Impostazioni VNC",
"terminalSettings": "Impostazioni terminale",
"rdpPort": "Porta RDP",
"username": "Nome utente",
"password": "Password",
"passwordSaved": "Password salvata, digita per modificarla",
"domain": "Dominio",
"securityMode": "Modalità di sicurezza",
"width": "Larghezza",
"height": "Altezza",
"dpi": "DPI",
"clientName": "Nome client",
"initialProgram": "Programma iniziale",
"serverLayout": "Layout del server",
"timezone": "Fuso orario",
"loadBalanceInfo": "Info bilanciamento carico / Cookie",
"loadBalanceInfoDesc": "Cookie del RD Connection Broker per il bilanciamento del carico della farm RDS (es. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "Proxy guacd",
"guacdHostname": "Host guacd",
"guacdHostnamePlaceholder": "Default globale",
"guacdPort": "Porta guacd",
"guacdProxyDesc": "Sostituisci l'istanza guacd globale per questa connessione. Lascia vuoto per utilizzare il guacd configurato globalmente.",
"gatewayHostname": "Hostname del gateway",
"gatewayPort": "Porta del gateway",
"gatewayUsername": "Nome utente gateway",
"gatewayPassword": "Password gateway",
"gatewayDomain": "Dominio gateway",
"remoteAppProgram": "Programma RemoteApp",
"workingDirectory": "Directory di lavoro",
"arguments": "Argomenti",
"normalizeLineEndings": "Normalizza terminazioni di riga",
"macAddress": "Indirizzo MAC",
"broadcastAddress": "Indirizzo di broadcast",
"udpPort": "Porta UDP",
"waitTimeS": "Tempo di attesa (s)",
"driveName": "Nome unità",
"drivePath": "Percorso unità",
"ignoreCertificate": "Ignora certificato",
"ignoreCertificateDesc": "Consente connessioni a host con certificati autofirmati",
"forceLossless": "Forza lossless",
"forceLosslessDesc": "Forza la codifica lossless delle immagini (qualità più alta, maggiore utilizzo di banda)",
"disableAudio": "Disabilita audio",
"disableAudioDesc": "Disattiva tutto l'audio dalla sessione remota",
"enableAudioInput": "Abilita ingresso audio (microfono)",
"enableAudioInputDesc": "Inoltra il microfono locale alla sessione remota",
"wallpaper": "Sfondo",
"wallpaperDesc": "Mostra lo sfondo del desktop (disabilitarlo migliora le prestazioni)",
"theming": "Temi",
"themingDesc": "Abilita temi e stili visivi",
"fontSmoothing": "Smussamento caratteri",
"fontSmoothingDesc": "Abilita il rendering dei caratteri ClearType",
"fullWindowDrag": "Trascinamento finestra intera",
"fullWindowDragDesc": "Mostra il contenuto della finestra durante il trascinamento",
"desktopComposition": "Composizione desktop",
"desktopCompositionDesc": "Abilita gli effetti Aero Glass",
"menuAnimations": "Animazioni menu",
"menuAnimationsDesc": "Abilita animazioni di dissolvenza e scorrimento dei menu",
"disableBitmapCaching": "Disabilita cache bitmap",
"disableBitmapCachingDesc": "Disattiva la cache bitmap (può essere utile per risolvere anomalie)",
"disableOffscreenCaching": "Disabilita cache fuori schermo",
"disableOffscreenCachingDesc": "Disattiva la cache fuori schermo",
"disableGlyphCaching": "Disabilita cache glifi",
"disableGlyphCachingDesc": "Disattiva la cache dei glifi",
"enableGfx": "Abilita GFX",
"enableGfxDesc": "Usa la pipeline grafica RemoteFX",
"enablePrinting": "Abilita stampa",
"enablePrintingDesc": "Reindirizza le stampanti locali alla sessione remota",
"enableDriveRedirection": "Abilita reindirizzamento unità",
"enableDriveRedirectionDesc": "Mappa una cartella locale come unità nella sessione remota",
"createDrivePath": "Crea percorso drive",
"createDrivePathDesc": "Crea automaticamente la cartella se non esiste",
"disableDownload": "Disabilita download",
"disableDownloadDesc": "Impedisce il download di file dalla sessione remota",
"disableUpload": "Disabilita upload",
"disableUploadDesc": "Impedisce il caricamento di file sulla sessione remota",
"enableTouch": "Abilita input tattile",
"enableTouchDesc": "Abilita l'inoltro dell'input tattile",
"consoleSession": "Sessione console",
"consoleSessionDesc": "Connetti alla console (sessione 0) invece di una nuova sessione",
"sendWolPacket": "Invia pacchetto WOL",
"sendWolPacketDesc": "Invia un pacchetto magico per riattivare questo host prima della connessione",
"disableCopy": "Disabilita copia",
"disableCopyDesc": "Impedisce la copia di testo dalla sessione remota",
"disablePaste": "Disabilita incolla",
"disablePasteDesc": "Impedisce di incollare testo nella sessione remota",
"createPathIfMissing": "Crea percorso se mancante",
"createPathIfMissingDesc": "Crea automaticamente la directory di registrazione",
"excludeOutput": "Escludi output",
"excludeOutputDesc": "Non registrare l'output dello schermo (solo metadati)",
"excludeMouse": "Escludi mouse",
"excludeMouseDesc": "Non registrare i movimenti del mouse",
"includeKeystrokes": "Includi tasti premuti",
"includeKeystrokesDesc": "Registra i tasti premuti non elaborati in aggiunta all'output dello schermo",
"vncPort": "Porta VNC",
"vncPassword": "Password VNC",
"vncUsernameOptional": "Nome utente (opzionale)",
"vncLeaveBlank": "Lascia vuoto se non richiesto",
"cursorMode": "Modalità cursore",
"swapRedBlue": "Scambia Rosso/Blu",
"swapRedBlueDesc": "Scambia i canali di colore rosso e blu (corregge alcuni problemi di colore)",
"readOnly": "Sola lettura",
"readOnlyDesc": "Visualizza lo schermo remoto senza inviare alcun input",
"telnetPort": "Porta Telnet",
"terminalType": "Tipo terminale",
"fontName": "Nome font",
"fontSize": "Dimensione font",
"colorScheme": "Schema colori",
"backspaceKey": "Tasto Backspace",
"colorDepth": "Profondità colore",
"resizeMethod": "Metodo di ridimensionamento",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "Desktop remoto",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Rete virtuale",
"unencryptedShell": "Shell non crittografata",
"remoteDesktopNotes": "Note",
"userDefaultsNote": "Le impostazioni che lasci invariate seguiranno le impostazioni predefinite del tuo provider di hosting.",
"connectionOriginNote": "Nell'applicazione desktop, RDP, VNC e Telnet utilizzano il server remoto a meno che l'host non sia impostato su \"Questo dispositivo\", che richiede un GUACD raggiungibile da questa macchina. Impostalo nelle impostazioni di Desktop remoto oppure per ogni host in Proxy GUACD.",
"docsLink": "Visualizza documentazione",
"connectionOriginGuacamoleNote": "RDP, VNC e Telnet utilizzano il server remoto a meno che non si selezioni \"Questo dispositivo\", che richiede un GUACD raggiungibile da questa macchina. È possibile impostarlo nelle impostazioni dell'URL di GUACD oppure per ogni host in \"Proxy di GUACD\"."
},
"onboarding": {
"feature_desktop": "Desktop remoto",
"feature_desktop_desc": "Apri le sessioni RDP, VNC e Telnet in una scheda, proprio come per SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Profondità colore",
"description": "Utilizzato per gli host RDP che mantengono l'impostazione predefinita."
},
"resizeMethod": {
"label": "Metodo di ridimensionamento",
"description": "Come lo schermo remoto si adatta alle dimensioni della finestra."
},
"resizeDisplayUpdate": "Aggiornamento della visualizzazione",
"resizeReconnect": "Riconnetti",
"inherit": "Ereditare",
"on": "Attivo",
"off": "Disattivo",
"forceLossless": {
"label": "Forza lossless",
"description": "Forza la codifica lossless delle immagini (qualità più alta, maggiore utilizzo di banda)"
},
"enableWallpaper": {
"label": "Sfondo",
"description": "Mostra lo sfondo del desktop (disabilitarlo migliora le prestazioni)"
},
"enableFontSmoothing": {
"label": "Smussamento caratteri",
"description": "Abilita il rendering dei caratteri ClearType"
},
"enableDesktopComposition": {
"label": "Composizione desktop",
"description": "Abilita gli effetti Aero Glass"
},
"disableAudio": {
"label": "Disabilita audio",
"description": "Disattiva tutto l'audio dalla sessione remota"
},
"enablePrinting": {
"label": "Abilita stampa",
"description": "Reindirizza le stampanti locali alla sessione remota"
},
"enableDrive": {
"label": "Abilita reindirizzamento unità",
"description": "Mappa una cartella locale come unità nella sessione remota"
},
"disableCopy": {
"label": "Disabilita copia",
"description": "Impedisce la copia di testo dalla sessione remota"
},
"disablePaste": {
"label": "Disabilita incolla",
"description": "Impedisce di incollare testo nella sessione remota"
},
"group": "Impostazioni predefinite di Desktop remoto",
"colorDepth16": "16 bit",
"colorDepth24": "24 bit",
"colorDepth32": "32 bit"
},
"admin": {
"enabled": {
"label": "Abilita Desktop remoto",
"description": "Consenti connessioni RDP, VNC e Telnet tramite guacd."
},
"guacdUrl": {
"label": "URL guacd",
"description": "Dove guacd è in ascolto, come host:porta. Le variabili d'ambiente GUACD_URL, GUACD_HOST e GUACD_PORT sovrascrivono questa impostazione.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "porta RDP"
},
"vncPort": {
"label": "porta VNC"
},
"telnetPort": {
"label": "Porta Telnet"
},
"rdpSecurity": {
"label": "Modalità di sicurezza RDP"
},
"rdpIgnoreCert": {
"label": "Ignora gli errori del certificato"
},
"guacamoleConfig": {
"label": "impostazioni guacd"
},
"enableToolbar": {
"label": "Barra degli strumenti del desktop remoto",
"description": "Mostra la barra degli strumenti nelle sessioni RDP, VNC e Telnet."
},
"displayGroup": "Display",
"displayDefaultsTab": "Desktop remoto"
}
},
"palette": {
"connectRdp": "Connettiti tramite RDP",
"connectVnc": "Connettiti tramite VNC",
"connectTelnet": "Connettiti con Telnet"
},
"permissions": {
"sessions": {
"title": "Condividere sessioni di desktop remoto",
"description": "Consenti la condivisione di sessioni e le stanze di collaborazione per mostrare le sessioni RDP, VNC e Telnet di questo utente agli altri."
}
},
"errors": {
"remoteServerRequired": "È necessario un server remoto. Per utilizzare questo tipo di connessione, collega un server remoto nelle Impostazioni."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "{{type}} セッションに接続中...",
"checkingGuacd": "リモートデスクトップサービスを確認しています...",
"requestingToken": "{{type}} 接続トークンを要求しています...",
"openingSession": "{{type}} セッションを開始しています...",
"negotiating": "交渉中 {{type}} 握手...",
"sessionReady": "{{type}} セッション準備完了",
"connectionError": "接続エラー",
"connectionFailed": "接続に失敗しました",
"failedToConnect": "接続トークンの取得に失敗しました",
"hostNotFound": "ホストが見つかりません",
"noHostSelected": "ホストが選択されていません",
"reconnect": "再接続",
"retry": "再試行",
"guacdUnavailable": "リモートデスクトップサービス (guacd) が利用できません。guacd が実行中でアクセス可能であり、管理者設定で正しく構成されていることを確認してください。",
"credentialPromptTitle": "RDP認証情報を入力してください",
"credentialPromptDescription": "このホストは、接続時に認証情報の入力を求めるように設定されています。認証情報は今回のセッションのみで使用され、保存されません。",
"connect": "接続",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "ドライブファイル",
"parent": "親ディレクトリ",
"refresh": "再読み込み",
"empty": "このフォルダーは空です",
"listFailed": "このフォルダの読み取りに失敗しました",
"download": "ダウンロード",
"downloadFailed": "ダウンロードに失敗しました {{name}}",
"upload": "ファイルをアップロードする",
"uploading": "{{name}} をアップロード中...",
"uploaded": "アップロード済み {{name}}",
"uploadFailed": "アップロードに失敗しました {{name}}",
"uploadDisabled": "この接続ではアップロードが無効になっています。",
"driveUnavailable": "ファイルをアップロードする前に、RDPドライブリダイレクトを有効にしてください。",
"driveNotWritable": "guacd はドライブフォルダに書き込むことができません。GUACD_DRIVE_PATH(またはホストのドライブパス)とそのアクセス許可を確認してください。",
"dropToUpload": "アップロードするファイルをドロップしてください"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (画面ロック)",
"winKey": "Windows キー",
"superL": "Super+L(ロック画面)",
"superKey": "スーパーキー",
"super": "素晴らしい",
"cmdL": "Cmd+L(画面ロック)",
"cmdKey": "コマンドキー",
"cmd": "コマンド",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Shift",
"win": "Win",
"stickyActive": "{{key}}(ラッチ中 - クリックで解除)",
"stickyInactive": "{{key}}(クリックでラッチ)",
"esc": "Escape",
"tab": "Tab",
"home": "Home",
"end": "エンド",
"pageUp": "Page Up",
"pageDown": "Page Down",
"arrowUp": "↑",
"arrowDown": "↓",
"arrowLeft": "←",
"arrowRight": "→",
"fnToggle": "ファンクションキー",
"reconnect": "セッション再接続",
"collapse": "ツールバーを折りたたむ",
"expand": "ツールバーを展開",
"dragHandle": "ドラッグで位置変更",
"hide": "このセッション中は非表示にします。完全に無効にするには、ホスト設定 → 一般 → 接続ツールバーを開いてください。",
"zoomIn": "ズームイン",
"zoomOut": "ズームアウト",
"resetZoom": "ズームをリセット",
"fitToScreen": "画面に合わせる",
"switchToTrackpad": "トラックパッドモードに切り替える(ドラッグでカーソルを移動、タップでクリック)",
"switchToTouch": "タッチモードに切り替える(クリックしたい場所を直接タップする)"
},
"disabled": "リモートデスクトップは無効になっています。管理者は設定画面で有効にできます。"
},
"hosts": {
"nativeRdpOpened": "Windowsリモートデスクトップで開きました",
"nativeRdpFailed": "Windowsリモートデスクトップを開くことができませんでした",
"openNativeRdp": "Windowsリモートデスクトップで開く",
"guac": {
"toolbar": "ツールバー",
"connection": "接続",
"authentication": "認証",
"storedCredential": "保存された認証情報",
"authMethod": "認証方法",
"authTypeDirect": "直接",
"authTypeCredential": "認証情報",
"authTypeNone": "すべて解除",
"authTypeNoneDesc": "認証情報は保存されません。接続するたびにユーザー名とパスワードの入力を求められますが、それらは保存されません。",
"selectCredential": "認証情報を選択...",
"connectionSettings": "接続設定",
"displaySettings": "表示設定",
"audioSettings": "オーディオ設定",
"rdpPerformance": "RDP パフォーマンス",
"deviceRedirection": "デバイスリダイレクト",
"session": "セッション",
"gateway": "ゲートウェイ",
"remoteApp": "RemoteApp",
"clipboard": "クリップボード",
"sessionRecording": "セッション記録",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "VNC設定",
"terminalSettings": "ターミナル設定",
"rdpPort": "RDPポート",
"username": "ユーザー名",
"password": "パスワード",
"passwordSaved": "パスワードが保存されました。変更するには入力してください。",
"domain": "ドメイン",
"securityMode": "セキュリティモード",
"width": "幅",
"height": "高さ",
"dpi": "DPI",
"clientName": "クライアント名",
"initialProgram": "初期プログラム",
"serverLayout": "サーバーレイアウト",
"timezone": "タイムゾーン",
"loadBalanceInfo": "負荷分散情報 / Cookie",
"loadBalanceInfoDesc": "RDSファームの負荷分散用RD接続ブローカーCookie (例: tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacdプロキシ",
"guacdHostname": "guacdホスト",
"guacdHostnamePlaceholder": "全体のデフォルト",
"guacdPort": "guacdポート",
"guacdProxyDesc": "この接続に対してグローバル guacd インスタンスを上書きします。空白にするとグローバルに設定された guacd を使用します。",
"gatewayHostname": "ゲートウェイホスト名",
"gatewayPort": "ゲートウェイポート",
"gatewayUsername": "ゲートウェイユーザー名",
"gatewayPassword": "ゲートウェイパスワード",
"gatewayDomain": "ゲートウェイドメイン",
"remoteAppProgram": "RemoteAppプログラム",
"workingDirectory": "作業ディレクトリ",
"arguments": "引数",
"normalizeLineEndings": "改行コードの正規化",
"macAddress": "MACアドレス",
"broadcastAddress": "ブロードキャストアドレス",
"udpPort": "UDPポート",
"waitTimeS": "待機時間(秒)",
"driveName": "ドライブ名",
"drivePath": "ドライブパス",
"ignoreCertificate": "証明書を無視",
"ignoreCertificateDesc": "自己署名証明書を持つホストへの接続を許可する",
"forceLossless": "ロスレスを強制",
"forceLosslessDesc": "ロスレス画像エンコーディングを強制する(高品質だが帯域幅を多く消費)",
"disableAudio": "オーディオを無効化",
"disableAudioDesc": "リモートセッションの音声をすべてミュートする",
"enableAudioInput": "音声入力(マイク)を有効化",
"enableAudioInputDesc": "ローカルマイクをリモートセッションに転送する",
"wallpaper": "壁紙",
"wallpaperDesc": "デスクトップ壁紙を表示する(無効にするとパフォーマンスが向上)",
"theming": "テーマ",
"themingDesc": "ビジュアルテーマとスタイルを有効にする",
"fontSmoothing": "フォントスムージング",
"fontSmoothingDesc": "ClearTypeフォントレンダリングを有効にする",
"fullWindowDrag": "フルウィンドウドラッグ",
"fullWindowDragDesc": "ウィンドウドラッグ中に内容を表示する",
"desktopComposition": "デスクトップコンポジション",
"desktopCompositionDesc": "Aero Glass効果を有効にする",
"menuAnimations": "メニューアニメーション",
"menuAnimationsDesc": "メニューのフェードおよびスライドアニメーションを有効にする",
"disableBitmapCaching": "ビットマップキャッシュを無効化",
"disableBitmapCachingDesc": "ビットマップキャッシュをオフにする(不具合の解決に役立つ場合がある)",
"disableOffscreenCaching": "オフスクリーンキャッシュを無効化",
"disableOffscreenCachingDesc": "オフスクリーンキャッシュをオフにする",
"disableGlyphCaching": "グリフキャッシュを無効化",
"disableGlyphCachingDesc": "グリフキャッシュをオフにする",
"enableGfx": "GFXを有効化",
"enableGfxDesc": "RemoteFXグラフィックスパイプラインを使用する",
"enablePrinting": "印刷を有効化",
"enablePrintingDesc": "ローカルプリンターをリモートセッションにリダイレクトする",
"enableDriveRedirection": "ドライブリダイレクションを有効化",
"enableDriveRedirectionDesc": "リモートセッションでローカルフォルダーをドライブとしてマッピングする",
"createDrivePath": "ドライブパスを作成",
"createDrivePathDesc": "フォルダが存在しない場合に自動的に作成します",
"disableDownload": "ダウンロードを無効にする",
"disableDownloadDesc": "リモートセッションからファイルをダウンロードできないようにします",
"disableUpload": "アップロードを無効にする",
"disableUploadDesc": "リモートセッションにファイルをアップロードできないようにします",
"enableTouch": "タッチを有効にする",
"enableTouchDesc": "タッチ入力転送を有効にします",
"consoleSession": "コンソールセッション",
"consoleSessionDesc": "新しいセッションではなくコンソール(セッション0)に接続します",
"sendWolPacket": "WOLパケットを送信",
"sendWolPacketDesc": "接続前にこのホストを起動するためのマジックパケットを送信します",
"disableCopy": "コピーを無効にする",
"disableCopyDesc": "リモートセッションからのテキストコピーを禁止します",
"disablePaste": "貼り付けを無効にする",
"disablePasteDesc": "リモートセッションへのテキスト貼り付けを禁止します",
"createPathIfMissing": "パスが存在しない場合に作成",
"createPathIfMissingDesc": "レコーディングディレクトリが存在しない場合に自動的に作成します",
"excludeOutput": "出力を除外",
"excludeOutputDesc": "画面出力を記録しません(メタデータのみ)",
"excludeMouse": "マウスを除外",
"excludeMouseDesc": "マウスの動きを記録しません",
"includeKeystrokes": "キーストロークを含める",
"includeKeystrokesDesc": "画面出力に加えて生のキーストロークを記録します",
"vncPort": "VNCポート",
"vncPassword": "VNCパスワード",
"vncUsernameOptional": "ユーザー名(オプション)",
"vncLeaveBlank": "必要ない場合は空白のままにしてください",
"cursorMode": "カーソルモード",
"swapRedBlue": "赤/青を入れ替え",
"swapRedBlueDesc": "赤と青の色チャネルを入れ替えます(色の問題を修正)",
"readOnly": "読み取り専用",
"readOnlyDesc": "入力を送信せずにリモート画面を表示します",
"telnetPort": "Telnetポート",
"terminalType": "端末タイプ",
"fontName": "フォント名",
"fontSize": "フォントサイズ",
"colorScheme": "カラースキーム",
"backspaceKey": "バックスペースキー",
"colorDepth": "色深度",
"resizeMethod": "リサイズ方法",
"driveNamePlaceholder": "ターミックスドライブ"
},
"remoteDesktop": "リモートデスクトップ",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "仮想ネットワーク",
"unencryptedShell": "暗号化されていないシェル",
"remoteDesktopNotes": "メモ",
"userDefaultsNote": "変更しない設定は、ホストのデフォルト設定に従います。",
"connectionOriginNote": "デスクトップアプリでは、ホストが「このデバイス」に設定されていない限り、RDP、VNC、Telnetはリモートサーバーを使用します。「このデバイス」に設定するには、このマシンがアクセスできるguacdが必要です。リモートデスクトップの設定、またはguacdプロキシでホストごとに設定してください。",
"docsLink": "ドキュメントを表示",
"connectionOriginGuacamoleNote": "RDP、VNC、Telnetは、リモートサーバーを使用します。「このデバイス」を選択した場合は、このマシンからアクセスできるguacdが必要です。guacd URL設定で設定するか、guacdプロキシでホストごとに設定してください。"
},
"onboarding": {
"feature_desktop": "リモートデスクトップ",
"feature_desktop_desc": "RDP、VNC、Telnetのセッションは、SSHと同様にタブで開きます。"
},
"settings": {
"user": {
"colorDepth": {
"label": "色深度",
"description": "デフォルト設定のままになっているRDPホストで使用されます。"
},
"resizeMethod": {
"label": "リサイズ方法",
"description": "リモート画面がウィンドウサイズに追従する仕組み。"
},
"resizeDisplayUpdate": "ディスプレイの更新",
"resizeReconnect": "再接続",
"inherit": "継承する",
"on": "オン",
"off": "オフ",
"forceLossless": {
"label": "ロスレスを強制",
"description": "ロスレス画像エンコーディングを強制する(高品質だが帯域幅を多く消費)"
},
"enableWallpaper": {
"label": "壁紙",
"description": "デスクトップ壁紙を表示する(無効にするとパフォーマンスが向上)"
},
"enableFontSmoothing": {
"label": "フォントスムージング",
"description": "ClearTypeフォントレンダリングを有効にする"
},
"enableDesktopComposition": {
"label": "デスクトップコンポジション",
"description": "Aero Glass効果を有効にする"
},
"disableAudio": {
"label": "オーディオを無効化",
"description": "リモートセッションの音声をすべてミュートする"
},
"enablePrinting": {
"label": "印刷を有効化",
"description": "ローカルプリンターをリモートセッションにリダイレクトする"
},
"enableDrive": {
"label": "ドライブリダイレクションを有効化",
"description": "リモートセッションでローカルフォルダーをドライブとしてマッピングする"
},
"disableCopy": {
"label": "コピーを無効にする",
"description": "リモートセッションからのテキストコピーを禁止します"
},
"disablePaste": {
"label": "貼り付けを無効にする",
"description": "リモートセッションへのテキスト貼り付けを禁止します"
},
"group": "リモートデスクトップのデフォルト設定",
"colorDepth16": "16ビット",
"colorDepth24": "24ビット",
"colorDepth32": "32ビット"
},
"admin": {
"enabled": {
"label": "リモートデスクトップを有効にする",
"description": "guacd を介して RDP、VNC、および Telnet 接続を許可する。"
},
"guacdUrl": {
"label": "guacd URL",
"description": "guacd がリッスンする場所をホスト:ポートとして指定します。GUACD_URL、GUACD_HOST、GUACD_PORT 環境変数によって、この設定は上書きされます。",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDPポート"
},
"vncPort": {
"label": "VNCポート"
},
"telnetPort": {
"label": "Telnetポート"
},
"rdpSecurity": {
"label": "RDPセキュリティモード"
},
"rdpIgnoreCert": {
"label": "証明書エラーを無視する"
},
"guacamoleConfig": {
"label": "guacd 設定"
},
"enableToolbar": {
"label": "リモートデスクトップツールバー",
"description": "RDP、VNC、Telnetセッションでツールバーを表示する。"
},
"displayGroup": "画面",
"displayDefaultsTab": "リモートデスクトップ"
}
},
"palette": {
"connectRdp": "RDPで接続する",
"connectVnc": "VNCで接続する",
"connectTelnet": "Telnetで接続する"
},
"permissions": {
"sessions": {
"title": "リモートデスクトップセッションを共有する",
"description": "セッション共有とコラボレーションルームで、このユーザーのRDP、VNC、およびTelnetセッションを他のユーザーに表示できるようにする。"
}
},
"errors": {
"remoteServerRequired": "リモートサーバーが必要です。この接続タイプを使用するには、設定でリモートサーバーを接続してください。"
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "{{type}} 세션에 연결 중...",
"checkingGuacd": "원격 데스크톱 서비스를 확인하는 중...",
"requestingToken": "{{type}} 연결 토큰을 요청하는 중...",
"openingSession": "{{type}} 세션을 여는 중...",
"negotiating": "협상 중 {{type}} 악수...",
"sessionReady": "{{type}} 세션 준비 완료",
"connectionError": "연결 오류",
"connectionFailed": "연결 실패",
"failedToConnect": "연결 토큰을 가져오지 못했습니다.",
"hostNotFound": "호스트를 찾을 수 없음",
"noHostSelected": "선택된 호스트가 없습니다",
"reconnect": "재연결",
"retry": "재시도",
"guacdUnavailable": "원격 데스크톱 서비스(guacd)를 사용할 수 없습니다. guacd가 실행 중이고 접근 가능하며 관리자 설정에서 올바르게 구성되었는지 확인하세요.",
"credentialPromptTitle": "RDP 자격 증명을 입력하십시오.",
"credentialPromptDescription": "이 호스트는 연결 시 자격 증명을 입력하라는 메시지를 표시하도록 설정되어 있습니다. 입력된 자격 증명은 해당 세션 동안에만 사용되며 저장되지 않습니다.",
"connect": "연결",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "드라이브 파일",
"parent": "상위 디렉토리",
"refresh": "새로 고침",
"empty": "이 폴더는 비어 있습니다",
"listFailed": "이 폴더를 읽는 데 실패했습니다.",
"download": "다운로드",
"downloadFailed": "{{name}} 다운로드에 실패했습니다.",
"upload": "파일 업로드",
"uploading": "{{name}} 업로드 중...",
"uploaded": "업로드됨 {{name}}",
"uploadFailed": "{{name}} 업로드에 실패했습니다.",
"uploadDisabled": "이 연결에서는 업로드가 비활성화되어 있습니다.",
"driveUnavailable": "파일을 업로드하기 전에 RDP 드라이브 리디렉션을 활성화하십시오.",
"driveNotWritable": "guacd가 드라이브 폴더에 쓰기 권한이 없습니다. GUACD_DRIVE_PATH(또는 호스트의 드라이브 경로)와 해당 권한을 확인하십시오.",
"dropToUpload": "업로드할 파일을 드롭하세요"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (화면 잠금)",
"winKey": "Windows 키",
"superL": "Super+L (잠금 화면)",
"superKey": "슈퍼 키",
"super": "감독자",
"cmdL": "Cmd+L (화면 잠금)",
"cmdKey": "명령 키",
"cmd": "명령",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Shift",
"win": "Win",
"stickyActive": "{{key}} (고정됨 - 클릭하여 해제)",
"stickyInactive": "{{key}} (클릭하여 고정)",
"esc": "Esc",
"tab": "Tab",
"home": "홈",
"end": "End",
"pageUp": "Page Up",
"pageDown": "Page Down",
"arrowUp": "위쪽 화살표",
"arrowDown": "아래쪽 화살표",
"arrowLeft": "왼쪽 화살표",
"arrowRight": "오른쪽 화살표",
"fnToggle": "기능 키",
"reconnect": "세션 재연결",
"collapse": "도구 모음 접기",
"expand": "도구 모음 펼치기",
"dragHandle": "드래그하여 위치 이동",
"hide": "이번 세션 동안에는 숨깁니다. 영구적으로 비활성화하려면 호스트 설정 → 일반 → 연결 도구 모음을 여세요.",
"zoomIn": "확대",
"zoomOut": "축소하기",
"resetZoom": "확대/축소 초기화",
"fitToScreen": "화면에 맞추기",
"switchToTrackpad": "트랙패드 모드로 전환하세요 (드래그하여 커서 이동, 탭하여 클릭).",
"switchToTouch": "터치 모드로 전환하세요 (클릭하려는 위치를 직접 탭하세요)."
},
"disabled": "원격 데스크톱 기능이 꺼져 있습니다. 관리자는 설정에서 이 기능을 켤 수 있습니다."
},
"hosts": {
"nativeRdpOpened": "Windows 원격 데스크톱에서 열림",
"nativeRdpFailed": "Windows 원격 데스크톱을 열지 못했습니다.",
"openNativeRdp": "Windows 원격 데스크톱에서 열기",
"guac": {
"toolbar": "툴바",
"connection": "연결",
"authentication": "인증",
"storedCredential": "저장된 자격 증명",
"authMethod": "인증 방법",
"authTypeDirect": "직접",
"authTypeCredential": "자격 증명",
"authTypeNone": "전체 해제",
"authTypeNoneDesc": "사용자 이름은 저장되지 않습니다. 접속할 때마다 사용자 이름과 비밀번호를 입력하라는 메시지가 표시되며, 해당 정보는 저장되지 않습니다.",
"selectCredential": "자격 증명 선택...",
"connectionSettings": "연결 설정",
"displaySettings": "화면 설정",
"audioSettings": "오디오 설정",
"rdpPerformance": "RDP 성능",
"deviceRedirection": "장치 리디렉션",
"session": "세션",
"gateway": "게이트웨이",
"remoteApp": "RemoteApp",
"clipboard": "클립보드",
"sessionRecording": "세션 녹화",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "VNC 설정",
"terminalSettings": "터미널 설정",
"rdpPort": "RDP 포트",
"username": "사용자 이름",
"password": "비밀번호",
"passwordSaved": "비밀번호가 저장되었습니다. 변경하려면 입력하세요.",
"domain": "도메인",
"securityMode": "보안 모드",
"width": "너비",
"height": "높이",
"dpi": "DPI",
"clientName": "클라이언트 이름",
"initialProgram": "초기 프로그램",
"serverLayout": "서버 레이아웃",
"timezone": "시간대",
"loadBalanceInfo": "로드 밸런스 정보 / 쿠키",
"loadBalanceInfoDesc": "RDS 팜 로드 밸런싱을 위한 RD 연결 브로커 쿠키 (예: tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacd 프록시",
"guacdHostname": "guacd 호스트",
"guacdHostnamePlaceholder": "전역 기본값",
"guacdPort": "guacd 포트",
"guacdProxyDesc": "이 연결에 대해 전역 guacd 인스턴스를 재정의합니다. 공란으로 두면 전역으로 구성된 guacd를 사용합니다.",
"gatewayHostname": "게이트웨이 호스트 이름",
"gatewayPort": "게이트웨이 포트",
"gatewayUsername": "게이트웨이 사용자 이름",
"gatewayPassword": "게이트웨이 비밀번호",
"gatewayDomain": "게이트웨이 도메인",
"remoteAppProgram": "RemoteApp 프로그램",
"workingDirectory": "작업 디렉터리",
"arguments": "인수",
"normalizeLineEndings": "줄 끝 정규화",
"macAddress": "MAC 주소",
"broadcastAddress": "브로드캐스트 주소",
"udpPort": "UDP 포트",
"waitTimeS": "대기 시간(초)",
"driveName": "드라이브 이름",
"drivePath": "드라이브 경로",
"ignoreCertificate": "인증서 무시",
"ignoreCertificateDesc": "자체 서명 인증서가 있는 호스트에 연결 허용",
"forceLossless": "무손실 강제",
"forceLosslessDesc": "무손실 이미지 인코딩 강제 (더 높은 품질, 더 많은 대역폭)",
"disableAudio": "오디오 비활성화",
"disableAudioDesc": "원격 세션의 모든 오디오 음소거",
"enableAudioInput": "오디오 입력 활성화 (마이크)",
"enableAudioInputDesc": "로컬 마이크를 원격 세션으로 전달",
"wallpaper": "배경 화면",
"wallpaperDesc": "바탕 화면 배경 표시 (비활성화 시 성능 향상)",
"theming": "테마",
"themingDesc": "비주얼 테마 및 스타일 활성화",
"fontSmoothing": "글꼴 다듬기",
"fontSmoothingDesc": "ClearType 글꼴 렌더링 사용",
"fullWindowDrag": "전체 창 끌기",
"fullWindowDragDesc": "드래그하는 동안 창 내용 표시",
"desktopComposition": "데스크톱 구성",
"desktopCompositionDesc": "Aero 글래스 효과 활성화",
"menuAnimations": "메뉴 애니메이션",
"menuAnimationsDesc": "메뉴 페이드 및 슬라이드 애니메이션 활성화",
"disableBitmapCaching": "비트맵 캐싱 비활성화",
"disableBitmapCachingDesc": "비트맵 캐시 끄기 (오류 해결에 도움이 될 수 있음)",
"disableOffscreenCaching": "오프스크린 캐싱 비활성화",
"disableOffscreenCachingDesc": "오프스크린 캐시 끄기",
"disableGlyphCaching": "글리프 캐싱 비활성화",
"disableGlyphCachingDesc": "글리프 캐시 끄기",
"enableGfx": "GFX 활성화",
"enableGfxDesc": "RemoteFX 그래픽 파이프라인 사용",
"enablePrinting": "인쇄 활성화",
"enablePrintingDesc": "로컬 프린터를 원격 세션으로 리디렉션",
"enableDriveRedirection": "드라이브 리디렉션 활성화",
"enableDriveRedirectionDesc": "로컬 폴더를 원격 세션에서 드라이브로 매핑",
"createDrivePath": "드라이브 경로 생성",
"createDrivePathDesc": "폴더가 없으면 자동 생성",
"disableDownload": "다운로드 비활성화",
"disableDownloadDesc": "원격 세션에서 파일 다운로드 방지",
"disableUpload": "업로드 비활성화",
"disableUploadDesc": "원격 세션으로 파일 업로드 방지",
"enableTouch": "터치 활성화",
"enableTouchDesc": "터치 입력 전달 활성화",
"consoleSession": "콘솔 세션",
"consoleSessionDesc": "새 세션 대신 콘솔(세션 0)에 연결",
"sendWolPacket": "WOL 패킷 전송",
"sendWolPacketDesc": "연결 전에 호스트를 깨우기 위해 매직 패킷 전송",
"disableCopy": "복사 비활성화",
"disableCopyDesc": "원격 세션에서 텍스트 복사 방지",
"disablePaste": "붙여넣기 비활성화",
"disablePasteDesc": "원격 세션으로 텍스트 붙여넣기 방지",
"createPathIfMissing": "경로가 없으면 생성",
"createPathIfMissingDesc": "녹화 디렉터리 자동 생성",
"excludeOutput": "출력 제외",
"excludeOutputDesc": "화면 출력 기록 안 함(메타데이터만)",
"excludeMouse": "마우스 제외",
"excludeMouseDesc": "마우스 움직임 기록 안 함",
"includeKeystrokes": "키 입력 포함",
"includeKeystrokesDesc": "화면 출력 외에 원시 키 입력 기록",
"vncPort": "VNC 포트",
"vncPassword": "VNC 비밀번호",
"vncUsernameOptional": "사용자 이름(선택 사항)",
"vncLeaveBlank": "필요하지 않으면 비워 두세요",
"cursorMode": "커서 모드",
"swapRedBlue": "적색/청색 교환",
"swapRedBlueDesc": "적색과 청색 채널을 교환(일부 색상 문제 해결)",
"readOnly": "읽기 전용",
"readOnlyDesc": "입력을 보내지 않고 원격 화면 보기",
"telnetPort": "Telnet 포트",
"terminalType": "터미널 유형",
"fontName": "글꼴 이름",
"fontSize": "글꼴 크기",
"colorScheme": "색 구성표",
"backspaceKey": "백스페이스 키",
"colorDepth": "색 농도",
"resizeMethod": "크기 조정 방법",
"driveNamePlaceholder": "터믹스 드라이브"
},
"remoteDesktop": "원격 데스크톱",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "텔넷",
"virtualNetwork": "가상 네트워크",
"unencryptedShell": "암호화되지 않은 셸",
"remoteDesktopNotes": "메모",
"userDefaultsNote": "설정을 변경하지 않고 그대로 두면 호스트의 기본 설정이 적용됩니다.",
"connectionOriginNote": "데스크톱 앱에서 RDP, VNC 및 Telnet은 호스트가 \"이 장치\"로 설정되어 있지 않은 한 원격 서버를 사용합니다. \"이 장치\"로 설정된 경우 해당 장치가 연결할 수 있는 guacd 프록시가 필요합니다. 원격 데스크톱 설정 또는 호스트별 guacd 프록시 설정에서 guacd 프록시를 설정하십시오.",
"docsLink": "문서 보기",
"connectionOriginGuacamoleNote": "RDP, VNC, Telnet은 \"이 장치\"를 선택하지 않는 한 원격 서버를 사용합니다. \"이 장치\"를 선택하려면 해당 장치가 연결할 수 있는 guacd가 필요합니다. guacd URL 설정에서 또는 guacd 프록시 설정에서 호스트별로 guacd를 설정하십시오."
},
"onboarding": {
"feature_desktop": "원격 데스크톱",
"feature_desktop_desc": "SSH와 마찬가지로 RDP, VNC 및 Telnet 세션을 탭에서 열 수 있습니다."
},
"settings": {
"user": {
"colorDepth": {
"label": "색 농도",
"description": "기본 설정으로 유지되는 RDP 호스트에 사용됩니다."
},
"resizeMethod": {
"label": "크기 조정 방법",
"description": "원격 화면이 창 크기에 따라 어떻게 변하는지."
},
"resizeDisplayUpdate": "디스플레이 업데이트",
"resizeReconnect": "재연결",
"inherit": "상속하다",
"on": "켜짐",
"off": "꺼짐",
"forceLossless": {
"label": "무손실 강제",
"description": "무손실 이미지 인코딩 강제 (더 높은 품질, 더 많은 대역폭)"
},
"enableWallpaper": {
"label": "배경 화면",
"description": "바탕 화면 배경 표시 (비활성화 시 성능 향상)"
},
"enableFontSmoothing": {
"label": "글꼴 다듬기",
"description": "ClearType 글꼴 렌더링 사용"
},
"enableDesktopComposition": {
"label": "데스크톱 구성",
"description": "Aero 글래스 효과 활성화"
},
"disableAudio": {
"label": "오디오 비활성화",
"description": "원격 세션의 모든 오디오 음소거"
},
"enablePrinting": {
"label": "인쇄 활성화",
"description": "로컬 프린터를 원격 세션으로 리디렉션"
},
"enableDrive": {
"label": "드라이브 리디렉션 활성화",
"description": "로컬 폴더를 원격 세션에서 드라이브로 매핑"
},
"disableCopy": {
"label": "복사 비활성화",
"description": "원격 세션에서 텍스트 복사 방지"
},
"disablePaste": {
"label": "붙여넣기 비활성화",
"description": "원격 세션으로 텍스트 붙여넣기 방지"
},
"group": "원격 데스크톱 기본 설정",
"colorDepth16": "16비트",
"colorDepth24": "24비트",
"colorDepth32": "32비트"
},
"admin": {
"enabled": {
"label": "원격 데스크톱 활성화",
"description": "guacd를 통해 RDP, VNC 및 Telnet 연결을 허용합니다."
},
"guacdUrl": {
"label": "guacd URL",
"description": "guacd가 수신 대기하는 주소는 호스트:포트 형식입니다. GUACD_URL, GUACD_HOST 및 GUACD_PORT 환경 변수가 이 값을 재정의합니다.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP 포트"
},
"vncPort": {
"label": "VNC 포트"
},
"telnetPort": {
"label": "텔넷 포트"
},
"rdpSecurity": {
"label": "RDP 보안 모드"
},
"rdpIgnoreCert": {
"label": "인증서 오류를 무시하세요"
},
"guacamoleConfig": {
"label": "guacd 설정"
},
"enableToolbar": {
"label": "원격 데스크톱 툴바",
"description": "RDP, VNC 및 Telnet 세션에서 툴바를 표시합니다."
},
"displayGroup": "표시하다",
"displayDefaultsTab": "원격 데스크톱"
}
},
"palette": {
"connectRdp": "RDP에 연결",
"connectVnc": "VNC로 연결",
"connectTelnet": "텔넷으로 연결"
},
"permissions": {
"sessions": {
"title": "원격 데스크톱 세션을 공유하세요",
"description": "세션 공유 및 공동 작업 공간에서 이 사용자의 RDP, VNC 및 Telnet 세션을 다른 사용자에게 표시할 수 있도록 허용합니다."
}
},
"errors": {
"remoteServerRequired": "원격 서버가 필요합니다. 이 연결 유형을 사용하려면 설정에서 원격 서버를 연결하세요."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Verbinding maken met de {{type}} sessie...",
"checkingGuacd": "De externe bureaubladservice controleren...",
"requestingToken": "Verzoek om {{type}} verbindingstoken...",
"openingSession": "Sessie {{type}} openen...",
"negotiating": "Onderhandelen over een {{type}} handdruk...",
"sessionReady": "{{type}} sessie gereed",
"connectionError": "Verbindingsfout",
"connectionFailed": "Verbinding mislukt",
"failedToConnect": "Verbindingstoken kon niet worden verkregen.",
"hostNotFound": "Host niet gevonden",
"noHostSelected": "Geen host geselecteerd",
"reconnect": "Opnieuw verbinding maken",
"retry": "Opnieuw proberen",
"guacdUnavailable": "De service voor extern bureaublad (guacd) is niet beschikbaar. Zorg ervoor dat guacd actief en toegankelijk is en correct is geconfigureerd in de beheerdersinstellingen.",
"credentialPromptTitle": "Voer de RDP-inloggegevens in.",
"credentialPromptDescription": "Deze host is zo ingesteld dat er bij het verbinden om inloggegevens wordt gevraagd. Deze worden alleen voor deze sessie gebruikt en niet opgeslagen.",
"connect": "Verbinden",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Drive-bestanden",
"parent": "Hoofdmap",
"refresh": "Vernieuwen",
"empty": "Deze map is leeg.",
"listFailed": "Het lezen van deze map is mislukt.",
"download": "Download",
"downloadFailed": "Downloaden mislukt {{name}}",
"upload": "Bestanden uploaden",
"uploading": "Bezig met uploaden {{name}}...",
"uploaded": "Geüpload {{name}}",
"uploadFailed": "Uploaden mislukt {{name}}",
"uploadDisabled": "Uploaden is uitgeschakeld voor deze verbinding.",
"driveUnavailable": "Schakel RDP-schijfomleiding in voordat u bestanden uploadt.",
"driveNotWritable": "guacd kan niet naar de schijfmap schrijven. Controleer GUACD_DRIVE_PATH (of het schijfpad van de host) en de bijbehorende machtigingen.",
"dropToUpload": "Sleep de bestanden hierheen om ze te uploaden."
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Vergrendelscherm)",
"winKey": "Windows-toets",
"superL": "Super+L (Vergrendelscherm)",
"superKey": "Supersleutel",
"super": "Super",
"cmdL": "Cmd+L (Scherm vergrendelen)",
"cmdKey": "Commando-toets",
"cmd": "Commando",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Verschuiving",
"win": "Winnen",
"stickyActive": "{{key}} (vergrendeld - klik om te ontgrendelen)",
"stickyInactive": "{{key}} (klik om te vergrendelen)",
"esc": "Ontsnappen",
"tab": "Tab",
"home": "Thuis",
"end": "Einde",
"pageUp": "Pagina omhoog",
"pageDown": "Pagina omlaag",
"arrowUp": "Pijl omhoog",
"arrowDown": "Pijl omlaag",
"arrowLeft": "Pijl naar links",
"arrowRight": "Pijl naar rechts",
"fnToggle": "Functietoetsen",
"reconnect": "Herverbindingssessie",
"collapse": "Werkbalk inklappen",
"expand": "Werkbalk uitklappen",
"dragHandle": "Sleep om te verplaatsen",
"hide": "Voor deze sessie verbergen. Om het permanent uit te schakelen, opent u Hostinstellingen → Algemeen → Verbindingswerkbalk.",
"zoomIn": "Zoom in",
"zoomOut": "Uitzoomen",
"resetZoom": "Zoom opnieuw instellen",
"fitToScreen": "Aan het scherm aanpassen",
"switchToTrackpad": "Schakel over naar de trackpadmodus (sleep om de cursor te verplaatsen, tik om te klikken).",
"switchToTouch": "Schakel over naar de aanraakmodus (tik direct op de plek waar u wilt klikken)."
},
"disabled": "Extern bureaublad is uitgeschakeld. Een beheerder kan dit inschakelen in de instellingen."
},
"hosts": {
"nativeRdpOpened": "Geopend in Windows Remote Desktop",
"nativeRdpFailed": "Het openen van Windows Remote Desktop is mislukt.",
"openNativeRdp": "Openen in Windows Extern bureaublad",
"guac": {
"toolbar": "Werkbalk",
"connection": "Verbinding",
"authentication": "Authenticatie",
"storedCredential": "Opgeslagen inloggegevens",
"authMethod": "Authenticatiemethode",
"authTypeDirect": "Direct",
"authTypeCredential": "Referentie",
"authTypeNone": "Geen",
"authTypeNoneDesc": "Er worden geen inloggegevens opgeslagen. U wordt bij elke verbinding gevraagd om een gebruikersnaam en wachtwoord; deze worden niet bewaard.",
"selectCredential": "Selecteer een certificaat...",
"connectionSettings": "Verbindingsinstellingen",
"displaySettings": "Weergave-instellingen",
"audioSettings": "Audio-instellingen",
"rdpPerformance": "RDP-prestaties",
"deviceRedirection": "Apparaatomleiding",
"session": "Sessie",
"gateway": "Gateway",
"remoteApp": "RemoteApp",
"clipboard": "Klembord",
"sessionRecording": "Sessieopname",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "VNC-instellingen",
"terminalSettings": "Terminalinstellingen",
"rdpPort": "RDP-poort",
"username": "Gebruikersnaam",
"password": "Wachtwoord",
"passwordSaved": "Wachtwoord opgeslagen, typ om te wijzigen",
"domain": "Domein",
"securityMode": "Beveiligingsmodus",
"width": "Breedte",
"height": "Hoogte",
"dpi": "DPI",
"clientName": "Klantnaam",
"initialProgram": "Initiële programma",
"serverLayout": "Serverindeling",
"timezone": "Tijdzone",
"loadBalanceInfo": "Informatie over load balancing / Cookie",
"loadBalanceInfoDesc": "RD Connection Broker-cookie voor load balancing van RDS-farms (bijv. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacd-proxy",
"guacdHostname": "guacd Host",
"guacdHostnamePlaceholder": "Globale standaard",
"guacdPort": "bewakingshaven",
"guacdProxyDesc": "Overschrijf de globale guacd-instantie voor deze verbinding. Laat dit veld leeg om de globaal geconfigureerde guacd te gebruiken.",
"gatewayHostname": "Gateway-hostnaam",
"gatewayPort": "Gateway-poort",
"gatewayUsername": "Gateway-gebruikersnaam",
"gatewayPassword": "Gateway-wachtwoord",
"gatewayDomain": "Gateway-domein",
"remoteAppProgram": "RemoteApp-programma",
"workingDirectory": "Werkmap",
"arguments": "Argumenten",
"normalizeLineEndings": "Normaliseer regeleinden",
"macAddress": "MAC-adres",
"broadcastAddress": "Uitzendadres",
"udpPort": "UDP-poort",
"waitTimeS": "Wachttijd (s)",
"driveName": "Schijfnaam",
"drivePath": "Rijpad",
"ignoreCertificate": "Certificaat negeren",
"ignoreCertificateDesc": "Sta verbindingen toe met hosts die zelfondertekende certificaten hebben.",
"forceLossless": "Krachtverliesloos",
"forceLosslessDesc": "Forceer verliesvrije beeldcodering (hogere kwaliteit, meer bandbreedte)",
"disableAudio": "Geluid uitschakelen",
"disableAudioDesc": "Demp alle audio van de externe sessie.",
"enableAudioInput": "Audio-ingang (microfoon) inschakelen",
"enableAudioInputDesc": "Stuur de lokale microfoon door naar de externe sessie.",
"wallpaper": "Behang",
"wallpaperDesc": "Toon bureaubladachtergrond (uitschakelen verbetert de prestaties)",
"theming": "Thema",
"themingDesc": "Schakel visuele thema's en stijlen in.",
"fontSmoothing": "Lettertype-vloeiendheid",
"fontSmoothingDesc": "Schakel ClearType-lettertypeweergave in",
"fullWindowDrag": "Volledig venster slepen",
"fullWindowDragDesc": "Toon de inhoud van het venster tijdens het slepen.",
"desktopComposition": "Bureaubladcompositie",
"desktopCompositionDesc": "Schakel Aero-glaseffecten in",
"menuAnimations": "Menu-animaties",
"menuAnimationsDesc": "Schakel menu-vervagings- en schuifanimaties in.",
"disableBitmapCaching": "Bitmapcaching uitschakelen",
"disableBitmapCachingDesc": "Schakel de bitmapcache uit (dit kan helpen bij storingen).",
"disableOffscreenCaching": "Offscreen-caching uitschakelen",
"disableOffscreenCachingDesc": "Schakel de offscreen cache uit.",
"disableGlyphCaching": "Glyph-caching uitschakelen",
"disableGlyphCachingDesc": "Schakel de glyph-cache uit.",
"enableGfx": "Schakel GFX in",
"enableGfxDesc": "Gebruik de RemoteFX-grafische pipeline",
"enablePrinting": "Afdrukken inschakelen",
"enablePrintingDesc": "Lokale printers doorverwijzen naar de externe sessie",
"enableDriveRedirection": "Schakel schijfomleiding in",
"enableDriveRedirectionDesc": "Een lokale map toewijzen als schijfstation in de externe sessie.",
"createDrivePath": "Maak een rijpad aan",
"createDrivePathDesc": "Maak de map automatisch aan als deze nog niet bestaat.",
"disableDownload": "Download uitschakelen",
"disableDownloadDesc": "Voorkom het downloaden van bestanden vanuit de externe sessie.",
"disableUpload": "Uploaden uitschakelen",
"disableUploadDesc": "Voorkom het uploaden van bestanden naar de externe sessie.",
"enableTouch": "Aanraakbediening inschakelen",
"enableTouchDesc": "Schakel het doorsturen van aanraakinvoer in.",
"consoleSession": "Consolesessie",
"consoleSessionDesc": "Maak verbinding met de console (sessie 0) in plaats van een nieuwe sessie te starten.",
"sendWolPacket": "Verzend WOL-pakket",
"sendWolPacketDesc": "Verzend een magic packet om deze host te activeren voordat u verbinding maakt.",
"disableCopy": "Kopiëren uitschakelen",
"disableCopyDesc": "Voorkom het kopiëren van tekst vanuit de externe sessie.",
"disablePaste": "Plakken uitschakelen",
"disablePasteDesc": "Voorkom dat tekst in de externe sessie wordt geplakt.",
"createPathIfMissing": "Maak een pad aan als dit ontbreekt.",
"createPathIfMissingDesc": "Maak automatisch de opnamemap aan.",
"excludeOutput": "Uitvoer uitsluiten",
"excludeOutputDesc": "Schermuitvoer niet opnemen (alleen metadata).",
"excludeMouse": "Muis uitsluiten",
"excludeMouseDesc": "Neem geen muisbewegingen op.",
"includeKeystrokes": "Inclusief toetsaanslagen",
"includeKeystrokesDesc": "Registreer naast de schermuitvoer ook onbewerkte toetsaanslagen.",
"vncPort": "VNC-poort",
"vncPassword": "VNC-wachtwoord",
"vncUsernameOptional": "Gebruikersnaam (optioneel)",
"vncLeaveBlank": "Laat dit veld leeg als het niet verplicht is.",
"cursorMode": "Cursormodus",
"swapRedBlue": "Wissel rood/blauw om.",
"swapRedBlueDesc": "Wissel de rode en blauwe kleurkanalen om (dit lost enkele kleurproblemen op).",
"readOnly": "Alleen-lezen",
"readOnlyDesc": "Bekijk het externe scherm zonder invoer te verzenden.",
"telnetPort": "Telnet-poort",
"terminalType": "Terminaltype",
"fontName": "Lettertypenaam",
"fontSize": "Lettergrootte",
"colorScheme": "Kleurenschema",
"backspaceKey": "Backspace-toets",
"colorDepth": "Kleurdiepte",
"resizeMethod": "Formaatwijzigingsmethode",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "Extern bureaublad",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Virtueel netwerk",
"unencryptedShell": "Niet-versleutelde shell",
"remoteDesktopNotes": "Notities",
"userDefaultsNote": "Instellingen die u ongewijzigd laat, volgen de standaardinstellingen van uw host.",
"connectionOriginNote": "In de desktop-app gebruiken RDP, VNC en Telnet de externe server, tenzij de host is ingesteld op 'Dit apparaat'. In dat geval is een guacd-proxy nodig die deze machine kan bereiken. Stel er een in via de instellingen voor Extern bureaublad of per host onder guacd-proxy.",
"docsLink": "Documenten bekijken",
"connectionOriginGuacamoleNote": "RDP, VNC en Telnet gebruiken de externe server, tenzij u 'Dit apparaat' selecteert. In dat geval is een guacd-server nodig die deze machine kan bereiken. Stel er een in bij de guacd-URL-instellingen of per host onder guacd-proxy."
},
"onboarding": {
"feature_desktop": "Extern bureaublad",
"feature_desktop_desc": "Open RDP-, VNC- en Telnet-sessies in een tabblad, net als bij SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Kleurdiepte",
"description": "Gebruikt voor RDP-hosts die dit standaard ingeschakeld laten."
},
"resizeMethod": {
"label": "Formaatwijzigingsmethode",
"description": "Hoe het scherm op afstand de venstergrootte aanpast."
},
"resizeDisplayUpdate": "Weergave bijwerken",
"resizeReconnect": "Opnieuw verbinding maken",
"inherit": "Erven",
"on": "Op",
"off": "Uit",
"forceLossless": {
"label": "Krachtverliesloos",
"description": "Forceer verliesvrije beeldcodering (hogere kwaliteit, meer bandbreedte)"
},
"enableWallpaper": {
"label": "Behang",
"description": "Toon bureaubladachtergrond (uitschakelen verbetert de prestaties)"
},
"enableFontSmoothing": {
"label": "Lettertype-vloeiendheid",
"description": "Schakel ClearType-lettertypeweergave in"
},
"enableDesktopComposition": {
"label": "Bureaubladcompositie",
"description": "Schakel Aero-glaseffecten in"
},
"disableAudio": {
"label": "Geluid uitschakelen",
"description": "Demp alle audio van de externe sessie."
},
"enablePrinting": {
"label": "Afdrukken inschakelen",
"description": "Lokale printers doorverwijzen naar de externe sessie"
},
"enableDrive": {
"label": "Schakel schijfomleiding in",
"description": "Een lokale map toewijzen als schijfstation in de externe sessie."
},
"disableCopy": {
"label": "Kopiëren uitschakelen",
"description": "Voorkom het kopiëren van tekst vanuit de externe sessie."
},
"disablePaste": {
"label": "Plakken uitschakelen",
"description": "Voorkom dat tekst in de externe sessie wordt geplakt."
},
"group": "Standaardinstellingen voor Remote Desktop",
"colorDepth16": "16-bit",
"colorDepth24": "24-bit",
"colorDepth32": "32-bit"
},
"admin": {
"enabled": {
"label": "Schakel Extern bureaublad in.",
"description": "Sta RDP-, VNC- en Telnet-verbindingen toe via guacd."
},
"guacdUrl": {
"label": "guacd URL",
"description": "Waar guacd luistert, als host:poort. De omgevingsvariabelen GUACD_URL, GUACD_HOST en GUACD_PORT overschrijven dit.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP-poort"
},
"vncPort": {
"label": "VNC-poort"
},
"telnetPort": {
"label": "Telnet-poort"
},
"rdpSecurity": {
"label": "RDP-beveiligingsmodus"
},
"rdpIgnoreCert": {
"label": "Certificaatfouten negeren"
},
"guacamoleConfig": {
"label": "GUACD-instellingen"
},
"enableToolbar": {
"label": "Werkbalk voor extern bureaublad",
"description": "Toon de werkbalk in RDP-, VNC- en Telnet-sessies."
},
"displayGroup": "Weergave",
"displayDefaultsTab": "Extern bureaublad"
}
},
"palette": {
"connectRdp": "Verbinden via RDP",
"connectVnc": "Verbinden met VNC",
"connectTelnet": "Verbinden via Telnet"
},
"permissions": {
"sessions": {
"title": "Externe bureaubladsessies delen",
"description": "Zorg ervoor dat sessiedeling en samenwerkingsruimtes de RDP-, VNC- en Telnet-sessies van deze gebruiker aan anderen kunnen tonen."
}
},
"errors": {
"remoteServerRequired": "Externe server vereist. Verbind een externe server in de instellingen om dit verbindingstype te gebruiken."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Connecting to {{type}} session...",
"checkingGuacd": "Checking remote desktop service...",
"requestingToken": "Requesting {{type}} connection token...",
"openingSession": "Opening {{type}} session...",
"negotiating": "Negotiating {{type}} handshake...",
"sessionReady": "{{type}} session ready",
"connectionError": "Connection error",
"connectionFailed": "Tilkobling mislyktes",
"failedToConnect": "Failed to get connection token",
"hostNotFound": "Host not found",
"noHostSelected": "Ingen vert valgt",
"reconnect": "Koble til på nytt",
"retry": "Prøv igjen",
"guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.",
"credentialPromptTitle": "Enter RDP Credentials",
"credentialPromptDescription": "This host is set to prompt for credentials on connect. They are used for this session only and are not saved.",
"connect": "Koble til",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Drive Files",
"parent": "Parent directory",
"refresh": "Oppdater",
"empty": "Denne mappen er tom",
"listFailed": "Failed to read this folder",
"download": "Last ned",
"downloadFailed": "Failed to download {{name}}",
"upload": "Upload files",
"uploading": "Laster opp {{name}}...",
"uploaded": "Uploaded {{name}}",
"uploadFailed": "Failed to upload {{name}}",
"uploadDisabled": "Uploads are disabled for this connection",
"driveUnavailable": "Enable RDP drive redirection before uploading files",
"driveNotWritable": "guacd cannot write to the drive folder. Check GUACD_DRIVE_PATH (or the host's Drive Path) and its permissions.",
"dropToUpload": "Drop files to upload"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Lock Screen)",
"winKey": "Windows Key",
"superL": "Super+L (Lock Screen)",
"superKey": "Super Key",
"super": "Super",
"cmdL": "Cmd+L (Lock Screen)",
"cmdKey": "Command Key",
"cmd": "Cmd",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Shift",
"win": "Win",
"stickyActive": "{{key}} (latched - click to release)",
"stickyInactive": "{{key}} (click to latch)",
"esc": "Escape",
"tab": "Tab",
"home": "Hjem",
"end": "End",
"pageUp": "Page Up",
"pageDown": "Page Down",
"arrowUp": "Arrow Up",
"arrowDown": "Arrow Down",
"arrowLeft": "Arrow Left",
"arrowRight": "Arrow Right",
"fnToggle": "Function Keys",
"reconnect": "Reconnect Session",
"collapse": "Collapse toolbar",
"expand": "Expand toolbar",
"dragHandle": "Drag to reposition",
"hide": "Hide for this session. To disable it permanently, open Host Settings → General → Connection Toolbar.",
"zoomIn": "Zoom in",
"zoomOut": "Zoom out",
"resetZoom": "Reset zoom",
"fitToScreen": "Fit to screen",
"switchToTrackpad": "Switch to trackpad mode (drag to move cursor, tap to click)",
"switchToTouch": "Switch to touch mode (tap directly where you want to click)"
},
"disabled": "Remote Desktop is turned off. An admin can turn it on in Settings."
},
"hosts": {
"nativeRdpOpened": "Opened in Windows Remote Desktop",
"nativeRdpFailed": "Failed to open Windows Remote Desktop",
"openNativeRdp": "Open in Windows Remote Desktop",
"guac": {
"toolbar": "Toolbar",
"connection": "Tilkobling",
"authentication": "Autentisering",
"storedCredential": "Stored Credential",
"authMethod": "Auth Method",
"authTypeDirect": "Direct",
"authTypeCredential": "Legitimasjon",
"authTypeNone": "Ingen",
"authTypeNoneDesc": "No credentials are stored. You'll be prompted for a username and password each time you connect; they are not saved.",
"selectCredential": "Select a credential...",
"connectionSettings": "Connection Settings",
"displaySettings": "Display Settings",
"audioSettings": "Audio Settings",
"rdpPerformance": "RDP Performance",
"deviceRedirection": "Device Redirection",
"session": "Session",
"gateway": "Gateway",
"remoteApp": "RemoteApp",
"clipboard": "Clipboard",
"sessionRecording": "Session Recording",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "VNC Settings",
"terminalSettings": "Terminal Settings",
"rdpPort": "RDP Port",
"username": "Brukernavn",
"password": "Passord",
"passwordSaved": "Password saved, type to change",
"domain": "Domain",
"securityMode": "Security Mode",
"width": "Width",
"height": "Height",
"dpi": "DPI",
"clientName": "Client Name",
"initialProgram": "Initial Program",
"serverLayout": "Server Layout",
"timezone": "Timezone",
"loadBalanceInfo": "Load Balance Info / Cookie",
"loadBalanceInfoDesc": "RD Connection Broker cookie for RDS farm load balancing (e.g. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacd Proxy",
"guacdHostname": "guacd Host",
"guacdHostnamePlaceholder": "Global default",
"guacdPort": "guacd Port",
"guacdProxyDesc": "Override the global guacd instance for this connection. Leave blank to use the globally configured guacd.",
"gatewayHostname": "Gateway Hostname",
"gatewayPort": "Gateway Port",
"gatewayUsername": "Gateway Username",
"gatewayPassword": "Gateway Password",
"gatewayDomain": "Gateway Domain",
"remoteAppProgram": "RemoteApp Program",
"workingDirectory": "Working Directory",
"arguments": "Arguments",
"normalizeLineEndings": "Normalize Line Endings",
"macAddress": "MAC Address",
"broadcastAddress": "Broadcast Address",
"udpPort": "UDP Port",
"waitTimeS": "Wait Time (s)",
"driveName": "Drive Name",
"drivePath": "Drive Path",
"ignoreCertificate": "Ignore Certificate",
"ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates",
"forceLossless": "Force Lossless",
"forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)",
"disableAudio": "Disable Audio",
"disableAudioDesc": "Mute all audio from the remote session",
"enableAudioInput": "Enable Audio Input (Microphone)",
"enableAudioInputDesc": "Forward local microphone to the remote session",
"wallpaper": "Wallpaper",
"wallpaperDesc": "Show desktop wallpaper (disabling improves performance)",
"theming": "Theming",
"themingDesc": "Enable visual themes and styles",
"fontSmoothing": "Font Smoothing",
"fontSmoothingDesc": "Enable ClearType font rendering",
"fullWindowDrag": "Full Window Drag",
"fullWindowDragDesc": "Show window contents while dragging",
"desktopComposition": "Desktop Composition",
"desktopCompositionDesc": "Enable Aero glass effects",
"menuAnimations": "Menu Animations",
"menuAnimationsDesc": "Enable menu fade and slide animations",
"disableBitmapCaching": "Disable Bitmap Caching",
"disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)",
"disableOffscreenCaching": "Disable Offscreen Caching",
"disableOffscreenCachingDesc": "Turn off offscreen cache",
"disableGlyphCaching": "Disable Glyph Caching",
"disableGlyphCachingDesc": "Turn off glyph cache",
"enableGfx": "Enable GFX",
"enableGfxDesc": "Use RemoteFX graphics pipeline",
"enablePrinting": "Enable Printing",
"enablePrintingDesc": "Redirect local printers to the remote session",
"enableDriveRedirection": "Enable Drive Redirection",
"enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session",
"createDrivePath": "Create Drive Path",
"createDrivePathDesc": "Automatically create the folder if it does not exist",
"disableDownload": "Disable Download",
"disableDownloadDesc": "Prevent downloading files from the remote session",
"disableUpload": "Disable Upload",
"disableUploadDesc": "Prevent uploading files to the remote session",
"enableTouch": "Enable Touch",
"enableTouchDesc": "Enable touch input forwarding",
"consoleSession": "Console Session",
"consoleSessionDesc": "Connect to the console (session 0) instead of a new session",
"sendWolPacket": "Send WOL Packet",
"sendWolPacketDesc": "Send a magic packet to wake this host before connecting",
"disableCopy": "Disable Copy",
"disableCopyDesc": "Prevent copying text from the remote session",
"disablePaste": "Disable Paste",
"disablePasteDesc": "Prevent pasting text into the remote session",
"createPathIfMissing": "Create Path if Missing",
"createPathIfMissingDesc": "Automatically create the recording directory",
"excludeOutput": "Exclude Output",
"excludeOutputDesc": "Do not record screen output (metadata only)",
"excludeMouse": "Exclude Mouse",
"excludeMouseDesc": "Do not record mouse movements",
"includeKeystrokes": "Include Keystrokes",
"includeKeystrokesDesc": "Record raw keystrokes in addition to screen output",
"vncPort": "VNC Port",
"vncPassword": "VNC Password",
"vncUsernameOptional": "Username (optional)",
"vncLeaveBlank": "Leave blank if not required",
"cursorMode": "Cursor Mode",
"swapRedBlue": "Swap Red/Blue",
"swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)",
"readOnly": "Read-only",
"readOnlyDesc": "View the remote screen without sending any input",
"telnetPort": "Telnet Port",
"terminalType": "Terminal Type",
"fontName": "Font Name",
"fontSize": "Skriftstørrelse",
"colorScheme": "Color Scheme",
"backspaceKey": "Backspace Key",
"colorDepth": "Color Depth",
"resizeMethod": "Resize Method",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "Remote Desktop",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Virtual Network",
"unencryptedShell": "Unencrypted shell",
"remoteDesktopNotes": "Notes",
"userDefaultsNote": "Settings you leave unchanged follow your host defaults.",
"connectionOriginNote": "On the desktop app, RDP, VNC and Telnet use the remote server unless the host is set to This device, which needs a guacd this machine can reach. Set one in the Remote Desktop settings, or per host under guacd Proxy.",
"docsLink": "View docs",
"connectionOriginGuacamoleNote": "RDP, VNC and Telnet use the remote server unless you pick \"This device\", which needs a guacd this machine can reach. Set one in the guacd URL setting, or per host under guacd Proxy."
},
"onboarding": {
"feature_desktop": "Remote desktop",
"feature_desktop_desc": "Open RDP, VNC and Telnet sessions in a tab, same as SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Color Depth",
"description": "Used for RDP hosts that leave it on their default."
},
"resizeMethod": {
"label": "Resize Method",
"description": "How the remote screen follows the window size."
},
"resizeDisplayUpdate": "Display Update",
"resizeReconnect": "Koble til på nytt",
"inherit": "Inherit",
"on": "On",
"off": "Off",
"forceLossless": {
"label": "Force Lossless",
"description": "Force lossless image encoding (higher quality, more bandwidth)"
},
"enableWallpaper": {
"label": "Wallpaper",
"description": "Show desktop wallpaper (disabling improves performance)"
},
"enableFontSmoothing": {
"label": "Font Smoothing",
"description": "Enable ClearType font rendering"
},
"enableDesktopComposition": {
"label": "Desktop Composition",
"description": "Enable Aero glass effects"
},
"disableAudio": {
"label": "Disable Audio",
"description": "Mute all audio from the remote session"
},
"enablePrinting": {
"label": "Enable Printing",
"description": "Redirect local printers to the remote session"
},
"enableDrive": {
"label": "Enable Drive Redirection",
"description": "Map a local folder as a drive in the remote session"
},
"disableCopy": {
"label": "Disable Copy",
"description": "Prevent copying text from the remote session"
},
"disablePaste": {
"label": "Disable Paste",
"description": "Prevent pasting text into the remote session"
},
"group": "Remote Desktop defaults",
"colorDepth16": "16-bit",
"colorDepth24": "24-bit",
"colorDepth32": "32-bit"
},
"admin": {
"enabled": {
"label": "Enable Remote Desktop",
"description": "Allow RDP, VNC and Telnet connections through guacd."
},
"guacdUrl": {
"label": "guacd URL",
"description": "Where guacd listens, as host:port. The GUACD_URL, GUACD_HOST and GUACD_PORT environment variables override this.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP port"
},
"vncPort": {
"label": "VNC port"
},
"telnetPort": {
"label": "Telnet port"
},
"rdpSecurity": {
"label": "RDP security mode"
},
"rdpIgnoreCert": {
"label": "Ignore certificate errors"
},
"guacamoleConfig": {
"label": "guacd settings"
},
"enableToolbar": {
"label": "Remote desktop toolbar",
"description": "Show the toolbar in RDP, VNC and Telnet sessions."
},
"displayGroup": "Display",
"displayDefaultsTab": "Remote Desktop"
}
},
"palette": {
"connectRdp": "Connect with RDP",
"connectVnc": "Connect with VNC",
"connectTelnet": "Connect with Telnet"
},
"permissions": {
"sessions": {
"title": "Share remote desktop sessions",
"description": "Let session sharing and collab rooms show this user's RDP, VNC and Telnet sessions to others."
}
},
"errors": {
"remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Łączenie z sesją {{type}}...",
"checkingGuacd": "Checking remote desktop service...",
"requestingToken": "Requesting {{type}} connection token...",
"openingSession": "Opening {{type}} session...",
"negotiating": "Negotiating {{type}} handshake...",
"sessionReady": "{{type}} session ready",
"connectionError": "Błąd połączenia",
"connectionFailed": "Połączenie nieudane",
"failedToConnect": "Nie udało się uzyskać tokena połączenia",
"hostNotFound": "Nie znaleziono hosta",
"noHostSelected": "Nie wybrano hosta",
"reconnect": "Połącz ponownie",
"retry": "Ponów",
"guacdUnavailable": "Usługa pulpitu zdalnego (guacd) jest niedostępna. Upewnij się, że usługa guacd jest uruchomiona, dostępna i poprawnie skonfigurowana w ustawieniach administratora.",
"credentialPromptTitle": "Wprowadź poświadczenia RDP",
"credentialPromptDescription": "Ten host jest ustawiony tak, aby pytać o poświadczenia przy łączeniu. Są używane tylko w tej sesji i nie są zapisywane.",
"connect": "Połączenie",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Pliki dysku",
"parent": "Katalog nadrzędny",
"refresh": "Odśwież",
"empty": "Ten folder jest pusty",
"listFailed": "Nie udało się odczytać tego folderu",
"download": "Pobierz",
"downloadFailed": "Nie udało się pobrać {{name}}",
"upload": "Prześlij pliki",
"uploading": "Przesyłanie {{name}}...",
"uploaded": "Przesłano {{name}}",
"uploadFailed": "Nie udało się przesłać {{name}}",
"uploadDisabled": "Przesyłanie plików jest wyłączone dla tego połączenia",
"driveUnavailable": "Włącz przekierowanie dysku RDP przed przesłaniem plików",
"driveNotWritable": "guacd cannot write to the drive folder. Check GUACD_DRIVE_PATH (or the host's Drive Path) and its permissions.",
"dropToUpload": "Upuść pliki, aby je przesłać"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (zablokuj ekran)",
"winKey": "Klawisz Windows",
"superL": "Super+L (Lock Screen)",
"superKey": "Super Key",
"super": "Super",
"cmdL": "Cmd+L (Lock Screen)",
"cmdKey": "Command Key",
"cmd": "Cmd",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Shift",
"win": "Win",
"stickyActive": "{{key}} (zatrzaśnięty - kliknij, aby zwolnić)",
"stickyInactive": "{{key}} (kliknij, aby zatrzasnąć)",
"esc": "Esc",
"tab": "Tab",
"home": "Home",
"end": "End",
"pageUp": "Page Up",
"pageDown": "Page Down",
"arrowUp": "Strzałka w górę",
"arrowDown": "Strzałka w dół",
"arrowLeft": "Strzałka w lewo",
"arrowRight": "Strzałka w prawo",
"fnToggle": "Klawisze funkcyjne",
"reconnect": "Połącz sesję ponownie",
"collapse": "Zwiń pasek narzędzi",
"expand": "Rozwiń pasek narzędzi",
"dragHandle": "Przeciągnij, aby zmienić położenie",
"hide": "Hide for this session. To disable it permanently, open Host Settings → General → Connection Toolbar.",
"zoomIn": "Zoom in",
"zoomOut": "Zoom out",
"resetZoom": "Reset zoom",
"fitToScreen": "Fit to screen",
"switchToTrackpad": "Przełącz na tryb gładzika (przeciągnij, aby przesunąć kursor, dotknij, aby kliknąć)",
"switchToTouch": "Przełącz na tryb dotykowy (dotknij bezpośrednio miejsca, w którym chcesz kliknąć)"
},
"disabled": "Remote Desktop is turned off. An admin can turn it on in Settings."
},
"hosts": {
"nativeRdpOpened": "Otwarto w Pulpicie zdalnym systemu Windows",
"nativeRdpFailed": "Nie udało się otworzyć pulpitu zdalnego systemu Windows",
"openNativeRdp": "Otwórz w Pulpicie zdalnym systemu Windows",
"guac": {
"toolbar": "Toolbar",
"connection": "Połączenie",
"authentication": "Uwierzytelnianie",
"storedCredential": "Zapisane poświadczenie",
"authMethod": "Metoda uwierzytelniania",
"authTypeDirect": "Bezpośredni",
"authTypeCredential": "Poświadczenie",
"authTypeNone": "Brak",
"authTypeNoneDesc": "Żadne poświadczenia nie są przechowywane. Przy każdym połączeniu pojawi się prośba o nazwę użytkownika i hasło; nie są zapisywane.",
"selectCredential": "Wybierz poświadczenie...",
"connectionSettings": "Ustawienia połączenia",
"displaySettings": "Ustawienia wyświetlania",
"audioSettings": "Ustawienia audio",
"rdpPerformance": "Wydajność RDP",
"deviceRedirection": "Przekierowanie urządzenia",
"session": "Sesja",
"gateway": "Brama",
"remoteApp": "RemoteApp",
"clipboard": "Schowek",
"sessionRecording": "Nagrywanie sesji",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "Ustawienia VNC",
"terminalSettings": "Ustawienia terminala",
"rdpPort": "Port RDP",
"username": "Nazwa użytkownika",
"password": "Hasło",
"passwordSaved": "Hasło zapisane, wpisz, aby zmienić",
"domain": "Domena",
"securityMode": "Tryb bezpieczeństwa",
"width": "Szerokość",
"height": "Wysokość",
"dpi": "DPI",
"clientName": "Nazwa klienta",
"initialProgram": "Program początkowy",
"serverLayout": "Układ serwera",
"timezone": "Strefa czasowa",
"loadBalanceInfo": "Informacje o równoważeniu obciążenia / cookie",
"loadBalanceInfoDesc": "Plik cookie brokera połączeń usług pulpitu zdalnego do równoważenia obciążenia farmy usług pulpitu zdalnego (np. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "Serwer proxy guacd",
"guacdHostname": "Host guacd",
"guacdHostnamePlaceholder": "Globalne ustawienie domyślne",
"guacdPort": "Port guacd",
"guacdProxyDesc": "Zastąp globalną instancję guacd dla tego połączenia. Pozostaw puste, aby użyć globalnie skonfigurowanego guacd.",
"gatewayHostname": "Nazwa hosta bramy",
"gatewayPort": "Port bramy",
"gatewayUsername": "Nazwa użytkownika bramy",
"gatewayPassword": "Hasło bramy",
"gatewayDomain": "Domena bramy",
"remoteAppProgram": "Program RemoteApp",
"workingDirectory": "Katalog roboczy",
"arguments": "Argumenty",
"normalizeLineEndings": "Normalizuj zakończenia linii",
"macAddress": "Adres MAC",
"broadcastAddress": "Adres rozgłoszeniowy",
"udpPort": "Port UDP",
"waitTimeS": "Czas oczekiwania (s)",
"driveName": "Nazwa dysku",
"drivePath": "Ścieżka dysku",
"ignoreCertificate": "Ignoruj certyfikat",
"ignoreCertificateDesc": "Zezwalaj na połączenia z hostami z certyfikatami samopodpisanymi",
"forceLossless": "Wymuś bezstratność",
"forceLosslessDesc": "Wymuś bezstratne kodowanie obrazu (wyższa jakość, większa przepustowość)",
"disableAudio": "Wyłącz dźwięk",
"disableAudioDesc": "Wycisz cały dźwięk z sesji zdalnej",
"enableAudioInput": "Włącz wejście audio (mikrofon)",
"enableAudioInputDesc": "Przekaż lokalny mikrofon do sesji zdalnej",
"wallpaper": "Tapeta",
"wallpaperDesc": "Pokaż tapetę pulpitu (wyłączenie poprawia wydajność)",
"theming": "Motywy",
"themingDesc": "Włącz motywy i style wizualne",
"fontSmoothing": "Wygładzanie czcionek",
"fontSmoothingDesc": "Włącz renderowanie czcionek ClearType",
"fullWindowDrag": "Przeciąganie pełnego okna",
"fullWindowDragDesc": "Pokaż zawartość okna podczas przeciągania",
"desktopComposition": "Kompozycja pulpitu",
"desktopCompositionDesc": "Włącz efekty Aero Glass",
"menuAnimations": "Animacje menu",
"menuAnimationsDesc": "Włącz animacje zanikania i wysuwania menu",
"disableBitmapCaching": "Wyłącz buforowanie bitmap",
"disableBitmapCachingDesc": "Wyłącz pamięć podręczną bitmap (może pomóc przy artefaktach)",
"disableOffscreenCaching": "Wyłącz buforowanie poza ekranem",
"disableOffscreenCachingDesc": "Wyłącz pamięć podręczną poza ekranem",
"disableGlyphCaching": "Wyłącz buforowanie glifów",
"disableGlyphCachingDesc": "Wyłącz pamięć podręczną glifów",
"enableGfx": "Włącz GFX",
"enableGfxDesc": "Użyj potoku graficznego RemoteFX",
"enablePrinting": "Włącz drukowanie",
"enablePrintingDesc": "Przekieruj drukarki lokalne do sesji zdalnej",
"enableDriveRedirection": "Włącz przekierowanie dysku",
"enableDriveRedirectionDesc": "Mapuj folder lokalny jako dysk w sesji zdalnej",
"createDrivePath": "Utwórz ścieżkę dysku",
"createDrivePathDesc": "Automatycznie utwórz folder, jeśli nie istnieje",
"disableDownload": "Wyłącz pobieranie",
"disableDownloadDesc": "Zapobiegaj pobieraniu plików z sesji zdalnej",
"disableUpload": "Wyłącz przesyłanie",
"disableUploadDesc": "Zapobiegaj przesyłaniu plików do sesji zdalnej",
"enableTouch": "Włącz dotyk",
"enableTouchDesc": "Włącz przekazywanie wejścia dotykowego",
"consoleSession": "Sesja konsoli",
"consoleSessionDesc": "Połącz się z konsolą (sesja 0) zamiast z nową sesją",
"sendWolPacket": "Wyślij pakiet WOL",
"sendWolPacketDesc": "Wyślij magic packet, aby obudzić tego hosta przed połączeniem",
"disableCopy": "Wyłącz kopiowanie",
"disableCopyDesc": "Zapobiegaj kopiowaniu tekstu z sesji zdalnej",
"disablePaste": "Wyłącz wklejanie",
"disablePasteDesc": "Zapobiegaj wklejaniu tekstu do sesji zdalnej",
"createPathIfMissing": "Utwórz ścieżkę, jeśli nie istnieje",
"createPathIfMissingDesc": "Automatycznie utwórz katalog nagrań",
"excludeOutput": "Wyklucz obraz",
"excludeOutputDesc": "Nie nagrywaj obrazu ekranu (tylko metadane)",
"excludeMouse": "Wyklucz mysz",
"excludeMouseDesc": "Nie nagrywaj ruchów myszy",
"includeKeystrokes": "Uwzględnij naciśnięcia klawiszy",
"includeKeystrokesDesc": "Rejestruj surowe naciśnięcia klawiszy oprócz obrazu ekranu",
"vncPort": "Port VNC",
"vncPassword": "Hasło VNC",
"vncUsernameOptional": "Nazwa użytkownika (opcjonalnie)",
"vncLeaveBlank": "Pozostaw puste, jeśli nie jest wymagane",
"cursorMode": "Tryb kursora",
"swapRedBlue": "Zamień czerwony/niebieski",
"swapRedBlueDesc": "Zamień kanały kolorów czerwonego i niebieskiego (rozwiązuje niektóre problemy z kolorami)",
"readOnly": "Tylko do odczytu",
"readOnlyDesc": "Oglądaj zdalny ekran bez wysyłania jakiegokolwiek wejścia",
"telnetPort": "Port Telnet",
"terminalType": "Typ terminala",
"fontName": "Nazwa czcionki",
"fontSize": "Rozmiar czcionki",
"colorScheme": "Schemat kolorów",
"backspaceKey": "Klawisz Backspace",
"colorDepth": "Głębia koloru",
"resizeMethod": "Metoda zmiany rozmiaru",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "Pulpit zdalny",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Sieć wirtualna",
"unencryptedShell": "Niezaszyfrowana powłoka",
"remoteDesktopNotes": "Notatki",
"userDefaultsNote": "Settings you leave unchanged follow your host defaults.",
"connectionOriginNote": "On the desktop app, RDP, VNC and Telnet use the remote server unless the host is set to This device, which needs a guacd this machine can reach. Set one in the Remote Desktop settings, or per host under guacd Proxy.",
"docsLink": "Zobacz dokumentację",
"connectionOriginGuacamoleNote": "RDP, VNC and Telnet use the remote server unless you pick \"This device\", which needs a guacd this machine can reach. Set one in the guacd URL setting, or per host under guacd Proxy."
},
"onboarding": {
"feature_desktop": "Zdalny pulpit",
"feature_desktop_desc": "Otwieraj sesje RDP, VNC i Telnet w karcie, tak samo jak SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Głębia koloru",
"description": "Used for RDP hosts that leave it on their default."
},
"resizeMethod": {
"label": "Metoda zmiany rozmiaru",
"description": "How the remote screen follows the window size."
},
"resizeDisplayUpdate": "Aktualizacja ekranu",
"resizeReconnect": "Połącz ponownie",
"inherit": "Dziedzicz",
"on": "Wł.",
"off": "Wył.",
"forceLossless": {
"label": "Wymuś bezstratność",
"description": "Wymuś bezstratne kodowanie obrazu (wyższa jakość, większa przepustowość)"
},
"enableWallpaper": {
"label": "Tapeta",
"description": "Pokaż tapetę pulpitu (wyłączenie poprawia wydajność)"
},
"enableFontSmoothing": {
"label": "Wygładzanie czcionek",
"description": "Włącz renderowanie czcionek ClearType"
},
"enableDesktopComposition": {
"label": "Kompozycja pulpitu",
"description": "Włącz efekty Aero Glass"
},
"disableAudio": {
"label": "Wyłącz dźwięk",
"description": "Wycisz cały dźwięk z sesji zdalnej"
},
"enablePrinting": {
"label": "Włącz drukowanie",
"description": "Przekieruj drukarki lokalne do sesji zdalnej"
},
"enableDrive": {
"label": "Włącz przekierowanie dysku",
"description": "Mapuj folder lokalny jako dysk w sesji zdalnej"
},
"disableCopy": {
"label": "Wyłącz kopiowanie",
"description": "Zapobiegaj kopiowaniu tekstu z sesji zdalnej"
},
"disablePaste": {
"label": "Wyłącz wklejanie",
"description": "Zapobiegaj wklejaniu tekstu do sesji zdalnej"
},
"group": "Remote Desktop defaults",
"colorDepth16": "16-bit",
"colorDepth24": "24-bit",
"colorDepth32": "32-bit"
},
"admin": {
"enabled": {
"label": "Enable Remote Desktop",
"description": "Allow RDP, VNC and Telnet connections through guacd."
},
"guacdUrl": {
"label": "Adres URL guacd",
"description": "Where guacd listens, as host:port. The GUACD_URL, GUACD_HOST and GUACD_PORT environment variables override this.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP port"
},
"vncPort": {
"label": "VNC port"
},
"telnetPort": {
"label": "Telnet port"
},
"rdpSecurity": {
"label": "RDP security mode"
},
"rdpIgnoreCert": {
"label": "Ignore certificate errors"
},
"guacamoleConfig": {
"label": "guacd settings"
},
"enableToolbar": {
"label": "Remote desktop toolbar",
"description": "Show the toolbar in RDP, VNC and Telnet sessions."
},
"displayGroup": "Wyświetlanie",
"displayDefaultsTab": "Pulpit zdalny"
}
},
"palette": {
"connectRdp": "Connect with RDP",
"connectVnc": "Connect with VNC",
"connectTelnet": "Connect with Telnet"
},
"permissions": {
"sessions": {
"title": "Share remote desktop sessions",
"description": "Let session sharing and collab rooms show this user's RDP, VNC and Telnet sessions to others."
}
},
"errors": {
"remoteServerRequired": "Wymagany jest serwer zdalny. Połącz się z serwerem zdalnym w Ustawieniach, aby korzystać z tego typu połączenia."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Connecting to {{type}} session...",
"checkingGuacd": "Checking remote desktop service...",
"requestingToken": "Requesting {{type}} connection token...",
"openingSession": "Opening {{type}} session...",
"negotiating": "Negotiating {{type}} handshake...",
"sessionReady": "{{type}} session ready",
"connectionError": "Connection error",
"connectionFailed": "Connection failed",
"failedToConnect": "Failed to get connection token",
"hostNotFound": "Host not found",
"noHostSelected": "No host selected",
"reconnect": "Reconnect",
"retry": "Retry",
"guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.",
"credentialPromptTitle": "Enter RDP Credentials",
"credentialPromptDescription": "This host is set to prompt for credentials on connect. They are used for this session only and are not saved.",
"connect": "Conectar",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Drive Files",
"parent": "Parent directory",
"refresh": "Atualizar",
"empty": "This folder is empty",
"listFailed": "Failed to read this folder",
"download": "Download",
"downloadFailed": "Failed to download {{name}}",
"upload": "Upload files",
"uploading": "Uploading {{name}}...",
"uploaded": "Uploaded {{name}}",
"uploadFailed": "Failed to upload {{name}}",
"uploadDisabled": "Uploads are disabled for this connection",
"driveUnavailable": "Enable RDP drive redirection before uploading files",
"driveNotWritable": "guacd cannot write to the drive folder. Check GUACD_DRIVE_PATH (or the host's Drive Path) and its permissions.",
"dropToUpload": "Drop files to upload"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Lock Screen)",
"winKey": "Windows Key",
"superL": "Super+L (Lock Screen)",
"superKey": "Super Key",
"super": "Super",
"cmdL": "Cmd+L (Lock Screen)",
"cmdKey": "Command Key",
"cmd": "Cmd",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Shift",
"win": "Win",
"stickyActive": "{{key}} (latched - click to release)",
"stickyInactive": "{{key}} (click to latch)",
"esc": "Escape",
"tab": "Tab",
"home": "Inicio",
"end": "End",
"pageUp": "Page Up",
"pageDown": "Page Down",
"arrowUp": "Arrow Up",
"arrowDown": "Arrow Down",
"arrowLeft": "Arrow Left",
"arrowRight": "Arrow Right",
"fnToggle": "Function Keys",
"reconnect": "Reconnect Session",
"collapse": "Collapse toolbar",
"expand": "Expand toolbar",
"dragHandle": "Drag to reposition",
"hide": "Hide for this session. To disable it permanently, open Host Settings → General → Connection Toolbar.",
"zoomIn": "Zoom in",
"zoomOut": "Zoom out",
"resetZoom": "Reset zoom",
"fitToScreen": "Fit to screen",
"switchToTrackpad": "Switch to trackpad mode (drag to move cursor, tap to click)",
"switchToTouch": "Switch to touch mode (tap directly where you want to click)"
},
"disabled": "Remote Desktop is turned off. An admin can turn it on in Settings."
},
"hosts": {
"nativeRdpOpened": "Opened in Windows Remote Desktop",
"nativeRdpFailed": "Failed to open Windows Remote Desktop",
"openNativeRdp": "Open in Windows Remote Desktop",
"guac": {
"toolbar": "Toolbar",
"connection": "Conexão",
"authentication": "Autenticação",
"storedCredential": "Credencial Armazenado",
"authMethod": "Método de Autenticação",
"authTypeDirect": "Direct",
"authTypeCredential": "Credencial",
"authTypeNone": "Nenhum",
"authTypeNoneDesc": "No credentials are stored. You'll be prompted for a username and password each time you connect; they are not saved.",
"selectCredential": "Selecione uma credencial...",
"connectionSettings": "Connection Settings",
"displaySettings": "Display Settings",
"audioSettings": "Audio Settings",
"rdpPerformance": "RDP Performance",
"deviceRedirection": "Device Redirection",
"session": "Session",
"gateway": "Gateway",
"remoteApp": "RemoteApp",
"clipboard": "Clipboard",
"sessionRecording": "Session Recording",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "VNC Settings",
"terminalSettings": "Terminal Settings",
"rdpPort": "RDP Port",
"username": "Nome de usuário",
"password": "Senha",
"passwordSaved": "Password saved, type to change",
"domain": "Domain",
"securityMode": "Security Mode",
"width": "Width",
"height": "Height",
"dpi": "DPI",
"clientName": "Client Name",
"initialProgram": "Initial Program",
"serverLayout": "Server Layout",
"timezone": "Timezone",
"loadBalanceInfo": "Load Balance Info / Cookie",
"loadBalanceInfoDesc": "RD Connection Broker cookie for RDS farm load balancing (e.g. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacd Proxy",
"guacdHostname": "guacd Host",
"guacdHostnamePlaceholder": "Global default",
"guacdPort": "guacd Port",
"guacdProxyDesc": "Override the global guacd instance for this connection. Leave blank to use the globally configured guacd.",
"gatewayHostname": "Gateway Hostname",
"gatewayPort": "Gateway Port",
"gatewayUsername": "Gateway Username",
"gatewayPassword": "Gateway Password",
"gatewayDomain": "Gateway Domain",
"remoteAppProgram": "RemoteApp Program",
"workingDirectory": "Working Directory",
"arguments": "Arguments",
"normalizeLineEndings": "Normalize Line Endings",
"macAddress": "Endereço MAC",
"broadcastAddress": "Broadcast Address",
"udpPort": "UDP Port",
"waitTimeS": "Wait Time (s)",
"driveName": "Drive Name",
"drivePath": "Drive Path",
"ignoreCertificate": "Ignore Certificate",
"ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates",
"forceLossless": "Force Lossless",
"forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)",
"disableAudio": "Disable Audio",
"disableAudioDesc": "Mute all audio from the remote session",
"enableAudioInput": "Enable Audio Input (Microphone)",
"enableAudioInputDesc": "Forward local microphone to the remote session",
"wallpaper": "Wallpaper",
"wallpaperDesc": "Show desktop wallpaper (disabling improves performance)",
"theming": "Theming",
"themingDesc": "Enable visual themes and styles",
"fontSmoothing": "Font Smoothing",
"fontSmoothingDesc": "Enable ClearType font rendering",
"fullWindowDrag": "Full Window Drag",
"fullWindowDragDesc": "Show window contents while dragging",
"desktopComposition": "Desktop Composition",
"desktopCompositionDesc": "Enable Aero glass effects",
"menuAnimations": "Menu Animations",
"menuAnimationsDesc": "Enable menu fade and slide animations",
"disableBitmapCaching": "Disable Bitmap Caching",
"disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)",
"disableOffscreenCaching": "Disable Offscreen Caching",
"disableOffscreenCachingDesc": "Turn off offscreen cache",
"disableGlyphCaching": "Disable Glyph Caching",
"disableGlyphCachingDesc": "Turn off glyph cache",
"enableGfx": "Enable GFX",
"enableGfxDesc": "Use RemoteFX graphics pipeline",
"enablePrinting": "Enable Printing",
"enablePrintingDesc": "Redirect local printers to the remote session",
"enableDriveRedirection": "Enable Drive Redirection",
"enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session",
"createDrivePath": "Create Drive Path",
"createDrivePathDesc": "Automatically create the folder if it does not exist",
"disableDownload": "Disable Download",
"disableDownloadDesc": "Prevent downloading files from the remote session",
"disableUpload": "Disable Upload",
"disableUploadDesc": "Prevent uploading files to the remote session",
"enableTouch": "Enable Touch",
"enableTouchDesc": "Enable touch input forwarding",
"consoleSession": "Console Session",
"consoleSessionDesc": "Connect to the console (session 0) instead of a new session",
"sendWolPacket": "Send WOL Packet",
"sendWolPacketDesc": "Send a magic packet to wake this host before connecting",
"disableCopy": "Disable Copy",
"disableCopyDesc": "Prevent copying text from the remote session",
"disablePaste": "Disable Paste",
"disablePasteDesc": "Prevent pasting text into the remote session",
"createPathIfMissing": "Create Path if Missing",
"createPathIfMissingDesc": "Automatically create the recording directory",
"excludeOutput": "Exclude Output",
"excludeOutputDesc": "Do not record screen output (metadata only)",
"excludeMouse": "Exclude Mouse",
"excludeMouseDesc": "Do not record mouse movements",
"includeKeystrokes": "Include Keystrokes",
"includeKeystrokesDesc": "Record raw keystrokes in addition to screen output",
"vncPort": "VNC Port",
"vncPassword": "VNC Password",
"vncUsernameOptional": "Username (optional)",
"vncLeaveBlank": "Leave blank if not required",
"cursorMode": "Cursor Mode",
"swapRedBlue": "Swap Red/Blue",
"swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)",
"readOnly": "Read-only",
"readOnlyDesc": "View the remote screen without sending any input",
"telnetPort": "Telnet Port",
"terminalType": "Terminal Type",
"fontName": "Font Name",
"fontSize": "Tamanho da Fonte",
"colorScheme": "Color Scheme",
"backspaceKey": "Backspace Key",
"colorDepth": "Color Depth",
"resizeMethod": "Resize Method",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "Área de trabalho remota",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Rede Virtual",
"unencryptedShell": "Shell não criptografado",
"remoteDesktopNotes": "Notes",
"userDefaultsNote": "Settings you leave unchanged follow your host defaults.",
"connectionOriginNote": "On the desktop app, RDP, VNC and Telnet use the remote server unless the host is set to This device, which needs a guacd this machine can reach. Set one in the Remote Desktop settings, or per host under guacd Proxy.",
"docsLink": "Documentação",
"connectionOriginGuacamoleNote": "RDP, VNC and Telnet use the remote server unless you pick \"This device\", which needs a guacd this machine can reach. Set one in the guacd URL setting, or per host under guacd Proxy."
},
"onboarding": {
"feature_desktop": "Remote desktop",
"feature_desktop_desc": "Open RDP, VNC and Telnet sessions in a tab, same as SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Color Depth",
"description": "Used for RDP hosts that leave it on their default."
},
"resizeMethod": {
"label": "Resize Method",
"description": "How the remote screen follows the window size."
},
"resizeDisplayUpdate": "Display Update",
"resizeReconnect": "Reconnect",
"inherit": "Inherit",
"on": "On",
"off": "Off",
"forceLossless": {
"label": "Force Lossless",
"description": "Force lossless image encoding (higher quality, more bandwidth)"
},
"enableWallpaper": {
"label": "Wallpaper",
"description": "Show desktop wallpaper (disabling improves performance)"
},
"enableFontSmoothing": {
"label": "Font Smoothing",
"description": "Enable ClearType font rendering"
},
"enableDesktopComposition": {
"label": "Desktop Composition",
"description": "Enable Aero glass effects"
},
"disableAudio": {
"label": "Disable Audio",
"description": "Mute all audio from the remote session"
},
"enablePrinting": {
"label": "Enable Printing",
"description": "Redirect local printers to the remote session"
},
"enableDrive": {
"label": "Enable Drive Redirection",
"description": "Map a local folder as a drive in the remote session"
},
"disableCopy": {
"label": "Disable Copy",
"description": "Prevent copying text from the remote session"
},
"disablePaste": {
"label": "Disable Paste",
"description": "Prevent pasting text into the remote session"
},
"group": "Remote Desktop defaults",
"colorDepth16": "16-bit",
"colorDepth24": "24-bit",
"colorDepth32": "32-bit"
},
"admin": {
"enabled": {
"label": "Enable Remote Desktop",
"description": "Allow RDP, VNC and Telnet connections through guacd."
},
"guacdUrl": {
"label": "guacd URL",
"description": "Where guacd listens, as host:port. The GUACD_URL, GUACD_HOST and GUACD_PORT environment variables override this.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP port"
},
"vncPort": {
"label": "VNC port"
},
"telnetPort": {
"label": "Telnet port"
},
"rdpSecurity": {
"label": "RDP security mode"
},
"rdpIgnoreCert": {
"label": "Ignore certificate errors"
},
"guacamoleConfig": {
"label": "guacd settings"
},
"enableToolbar": {
"label": "Remote desktop toolbar",
"description": "Show the toolbar in RDP, VNC and Telnet sessions."
},
"displayGroup": "Display",
"displayDefaultsTab": "Área de trabalho remota"
}
},
"palette": {
"connectRdp": "Connect with RDP",
"connectVnc": "Connect with VNC",
"connectTelnet": "Connect with Telnet"
},
"permissions": {
"sessions": {
"title": "Share remote desktop sessions",
"description": "Let session sharing and collab rooms show this user's RDP, VNC and Telnet sessions to others."
}
},
"errors": {
"remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "A ligar à sessão {{type}}...",
"checkingGuacd": "Checking remote desktop service...",
"requestingToken": "Requesting {{type}} connection token...",
"openingSession": "Opening {{type}} session...",
"negotiating": "Negotiating {{type}} handshake...",
"sessionReady": "{{type}} session ready",
"connectionError": "Erro de ligação",
"connectionFailed": "Falha na ligação",
"failedToConnect": "Falha ao obter o token de ligação",
"hostNotFound": "Servidor não encontrado",
"noHostSelected": "Nenhum servidor selecionado",
"reconnect": "Reconectar",
"retry": "Repetir",
"guacdUnavailable": "O serviço de ambiente de trabalho remoto (guacd) não está disponível. Certifique-se de que o guacd está em execução, acessível e devidamente configurado nas definições de administração.",
"credentialPromptTitle": "Enter RDP Credentials",
"credentialPromptDescription": "This host is set to prompt for credentials on connect. They are used for this session only and are not saved.",
"connect": "Ligar",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Drive Files",
"parent": "Parent directory",
"refresh": "Atualizar",
"empty": "Esta pasta está vazia",
"listFailed": "Failed to read this folder",
"download": "Descarregar",
"downloadFailed": "Failed to download {{name}}",
"upload": "Upload files",
"uploading": "A enviar {{name}}...",
"uploaded": "Uploaded {{name}}",
"uploadFailed": "Failed to upload {{name}}",
"uploadDisabled": "Uploads are disabled for this connection",
"driveUnavailable": "Enable RDP drive redirection before uploading files",
"driveNotWritable": "guacd cannot write to the drive folder. Check GUACD_DRIVE_PATH (or the host's Drive Path) and its permissions.",
"dropToUpload": "Drop files to upload"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Bloquear ecrã)",
"winKey": "Tecla Windows",
"superL": "Super+L (Lock Screen)",
"superKey": "Super Key",
"super": "Super",
"cmdL": "Cmd+L (Lock Screen)",
"cmdKey": "Command Key",
"cmd": "Cmd",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Shift",
"win": "Win",
"stickyActive": "{{key}} (bloqueado - clique para libertar)",
"stickyInactive": "{{key}} (clique para bloquear)",
"esc": "Escape",
"tab": "Tab",
"home": "Home",
"end": "Fim",
"pageUp": "Page Up",
"pageDown": "Page Down",
"arrowUp": "Seta para Cima",
"arrowDown": "Seta para Baixo",
"arrowLeft": "Seta para a Esquerda",
"arrowRight": "Seta para a Direita",
"fnToggle": "Teclas de Função",
"reconnect": "Reconectar Sessão",
"collapse": "Recolher barra",
"expand": "Expandir barra",
"dragHandle": "Arraste para reposicionar",
"hide": "Hide for this session. To disable it permanently, open Host Settings → General → Connection Toolbar.",
"zoomIn": "Zoom in",
"zoomOut": "Zoom out",
"resetZoom": "Reset zoom",
"fitToScreen": "Fit to screen",
"switchToTrackpad": "Switch to trackpad mode (drag to move cursor, tap to click)",
"switchToTouch": "Switch to touch mode (tap directly where you want to click)"
},
"disabled": "Remote Desktop is turned off. An admin can turn it on in Settings."
},
"hosts": {
"nativeRdpOpened": "Opened in Windows Remote Desktop",
"nativeRdpFailed": "Failed to open Windows Remote Desktop",
"openNativeRdp": "Open in Windows Remote Desktop",
"guac": {
"toolbar": "Toolbar",
"connection": "Ligação",
"authentication": "Autenticação",
"storedCredential": "Credencial Armazenada",
"authMethod": "Método de Autenticação",
"authTypeDirect": "Direta",
"authTypeCredential": "Credencial",
"authTypeNone": "Nenhuma",
"authTypeNoneDesc": "No credentials are stored. You'll be prompted for a username and password each time you connect; they are not saved.",
"selectCredential": "Selecionar uma credencial...",
"connectionSettings": "Definições de Ligação",
"displaySettings": "Definições de Ecrã",
"audioSettings": "Definições de Áudio",
"rdpPerformance": "Desempenho do RDP",
"deviceRedirection": "Redirecionamento de Dispositivos",
"session": "Sessão",
"gateway": "Gateway",
"remoteApp": "RemoteApp",
"clipboard": "Área de Transferência",
"sessionRecording": "Gravação de Sessão",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "Definições VNC",
"terminalSettings": "Definições do Terminal",
"rdpPort": "Porta RDP",
"username": "Nome de utilizador",
"password": "Palavra-passe",
"passwordSaved": "Password saved, type to change",
"domain": "Domínio",
"securityMode": "Modo de segurança",
"width": "Largura",
"height": "Altura",
"dpi": "DPI",
"clientName": "Nome do cliente",
"initialProgram": "Programa inicial",
"serverLayout": "Layout do servidor",
"timezone": "Fuso horário",
"loadBalanceInfo": "Informação de balanceamento de carga / Cookie",
"loadBalanceInfoDesc": "Cookie do RD Connection Broker para balanceamento de carga em farms RDS (ex: tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "Proxy guacd",
"guacdHostname": "Host guacd",
"guacdHostnamePlaceholder": "Padrão global",
"guacdPort": "Porta guacd",
"guacdProxyDesc": "Substituir a instância guacd global para esta ligação. Deixe em branco para usar o guacd configurado globalmente.",
"gatewayHostname": "Nome de host do gateway",
"gatewayPort": "Porta do gateway",
"gatewayUsername": "Nome de utilizador do gateway",
"gatewayPassword": "Palavra-passe do gateway",
"gatewayDomain": "Domínio do gateway",
"remoteAppProgram": "Programa RemoteApp",
"workingDirectory": "Diretório de trabalho",
"arguments": "Argumentos",
"normalizeLineEndings": "Normalizar fins de linha",
"macAddress": "Endereço MAC",
"broadcastAddress": "Endereço de broadcast",
"udpPort": "Porta UDP",
"waitTimeS": "Tempo de Espera (s)",
"driveName": "Nome da Unidade",
"drivePath": "Caminho da Unidade",
"ignoreCertificate": "Ignorar Certificado",
"ignoreCertificateDesc": "Permitir ligações a hosts com certificados auto-assinados",
"forceLossless": "Forçar sem perdas",
"forceLosslessDesc": "Forçar codificação de imagem sem perdas (maior qualidade, mais largura de banda)",
"disableAudio": "Desativar Áudio",
"disableAudioDesc": "Silenciar todo o áudio da sessão remota",
"enableAudioInput": "Ativar Entrada de Áudio (Microfone)",
"enableAudioInputDesc": "Encaminhar microfone local para a sessão remota",
"wallpaper": "Fundo de ecrã",
"wallpaperDesc": "Mostrar fundo de ecrã (desativar melhora o desempenho)",
"theming": "Temas",
"themingDesc": "Ativar temas e estilos visuais",
"fontSmoothing": "Suavização de Fontes",
"fontSmoothingDesc": "Ativar renderização de fontes ClearType",
"fullWindowDrag": "Arrasto de Janela Completo",
"fullWindowDragDesc": "Mostrar conteúdo da janela ao arrastar",
"desktopComposition": "Composição do ambiente de trabalho",
"desktopCompositionDesc": "Ativar efeitos de transparência Aero",
"menuAnimations": "Animações de Menu",
"menuAnimationsDesc": "Ativar animações de desvanecimento e deslizamento de menus",
"disableBitmapCaching": "Desativar cache de bitmaps",
"disableBitmapCachingDesc": "Desativar a cache de bitmaps (pode ajudar com falhas)",
"disableOffscreenCaching": "Desativar cache fora do ecrã",
"disableOffscreenCachingDesc": "Desativar a cache fora do ecrã",
"disableGlyphCaching": "Desativar cache de glifos",
"disableGlyphCachingDesc": "Desativar a cache de glifos",
"enableGfx": "Ativar GFX",
"enableGfxDesc": "Usar pipeline gráfico RemoteFX",
"enablePrinting": "Ativar Impressão",
"enablePrintingDesc": "Redirecionar impressoras locais para a sessão remota",
"enableDriveRedirection": "Ativar Redirecionamento de Unidades",
"enableDriveRedirectionDesc": "Mapear uma pasta local como unidade na sessão remota",
"createDrivePath": "Criar Caminho de Unidade",
"createDrivePathDesc": "Criar automaticamente a pasta se não existir",
"disableDownload": "Desativar Download",
"disableDownloadDesc": "Impedir o download de ficheiros da sessão remota",
"disableUpload": "Desativar Upload",
"disableUploadDesc": "Impedir o envio de ficheiros para a sessão remota",
"enableTouch": "Ativar Toque",
"enableTouchDesc": "Ativar o reencaminhamento de entrada tátil",
"consoleSession": "Sessão de Consola",
"consoleSessionDesc": "Ligar à consola (sessão 0) em vez de uma nova sessão",
"sendWolPacket": "Enviar Pacote WOL",
"sendWolPacketDesc": "Enviar um pacote mágico para despertar este host antes de ligar",
"disableCopy": "Desativar Cópia",
"disableCopyDesc": "Impedir a cópia de texto da sessão remota",
"disablePaste": "Desativar Colagem",
"disablePasteDesc": "Impedir a colagem de texto na sessão remota",
"createPathIfMissing": "Criar caminho se não existir",
"createPathIfMissingDesc": "Criar automaticamente o diretório de gravação",
"excludeOutput": "Excluir Saída",
"excludeOutputDesc": "Não gravar a saída do ecrã (apenas metadados)",
"excludeMouse": "Excluir Rato",
"excludeMouseDesc": "Não gravar os movimentos do rato",
"includeKeystrokes": "Incluir Teclas",
"includeKeystrokesDesc": "Gravar as teclas premidas em bruto, além da saída do ecrã",
"vncPort": "Porta VNC",
"vncPassword": "Palavra-passe VNC",
"vncUsernameOptional": "Nome de utilizador (opcional)",
"vncLeaveBlank": "Deixar em branco se não for necessário",
"cursorMode": "Modo do Cursor",
"swapRedBlue": "Trocar Vermelho/Azul",
"swapRedBlueDesc": "Trocar os canais de cor vermelha e azul (corrige alguns problemas de cor)",
"readOnly": "Apenas leitura",
"readOnlyDesc": "Visualizar o ecrã remoto sem enviar qualquer entrada",
"telnetPort": "Porta Telnet",
"terminalType": "Tipo de Terminal",
"fontName": "Nome da Fonte",
"fontSize": "Tamanho da Fonte",
"colorScheme": "Esquema de Cores",
"backspaceKey": "Tecla Backspace",
"colorDepth": "Profundidade de cor",
"resizeMethod": "Método de redimensionamento",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "Ambiente de trabalho remoto",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Rede Virtual",
"unencryptedShell": "Shell não encriptado",
"remoteDesktopNotes": "Notas",
"userDefaultsNote": "Settings you leave unchanged follow your host defaults.",
"connectionOriginNote": "On the desktop app, RDP, VNC and Telnet use the remote server unless the host is set to This device, which needs a guacd this machine can reach. Set one in the Remote Desktop settings, or per host under guacd Proxy.",
"docsLink": "Ver documentação",
"connectionOriginGuacamoleNote": "RDP, VNC and Telnet use the remote server unless you pick \"This device\", which needs a guacd this machine can reach. Set one in the guacd URL setting, or per host under guacd Proxy."
},
"onboarding": {
"feature_desktop": "Remote desktop",
"feature_desktop_desc": "Open RDP, VNC and Telnet sessions in a tab, same as SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Profundidade de cor",
"description": "Used for RDP hosts that leave it on their default."
},
"resizeMethod": {
"label": "Método de redimensionamento",
"description": "How the remote screen follows the window size."
},
"resizeDisplayUpdate": "Display Update",
"resizeReconnect": "Reconectar",
"inherit": "Inherit",
"on": "Ativado",
"off": "Desativado",
"forceLossless": {
"label": "Forçar sem perdas",
"description": "Forçar codificação de imagem sem perdas (maior qualidade, mais largura de banda)"
},
"enableWallpaper": {
"label": "Fundo de ecrã",
"description": "Mostrar fundo de ecrã (desativar melhora o desempenho)"
},
"enableFontSmoothing": {
"label": "Suavização de Fontes",
"description": "Ativar renderização de fontes ClearType"
},
"enableDesktopComposition": {
"label": "Composição do ambiente de trabalho",
"description": "Ativar efeitos de transparência Aero"
},
"disableAudio": {
"label": "Desativar Áudio",
"description": "Silenciar todo o áudio da sessão remota"
},
"enablePrinting": {
"label": "Ativar Impressão",
"description": "Redirecionar impressoras locais para a sessão remota"
},
"enableDrive": {
"label": "Ativar Redirecionamento de Unidades",
"description": "Mapear uma pasta local como unidade na sessão remota"
},
"disableCopy": {
"label": "Desativar Cópia",
"description": "Impedir a cópia de texto da sessão remota"
},
"disablePaste": {
"label": "Desativar Colagem",
"description": "Impedir a colagem de texto na sessão remota"
},
"group": "Remote Desktop defaults",
"colorDepth16": "16-bit",
"colorDepth24": "24-bit",
"colorDepth32": "32-bit"
},
"admin": {
"enabled": {
"label": "Enable Remote Desktop",
"description": "Allow RDP, VNC and Telnet connections through guacd."
},
"guacdUrl": {
"label": "URL do guacd",
"description": "Where guacd listens, as host:port. The GUACD_URL, GUACD_HOST and GUACD_PORT environment variables override this.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP port"
},
"vncPort": {
"label": "VNC port"
},
"telnetPort": {
"label": "Telnet port"
},
"rdpSecurity": {
"label": "RDP security mode"
},
"rdpIgnoreCert": {
"label": "Ignore certificate errors"
},
"guacamoleConfig": {
"label": "guacd settings"
},
"enableToolbar": {
"label": "Remote desktop toolbar",
"description": "Show the toolbar in RDP, VNC and Telnet sessions."
},
"displayGroup": "Display",
"displayDefaultsTab": "Ambiente de trabalho remoto"
}
},
"palette": {
"connectRdp": "Connect with RDP",
"connectVnc": "Connect with VNC",
"connectTelnet": "Connect with Telnet"
},
"permissions": {
"sessions": {
"title": "Share remote desktop sessions",
"description": "Let session sharing and collab rooms show this user's RDP, VNC and Telnet sessions to others."
}
},
"errors": {
"remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Se conectează la sesiunea {{type}}...",
"checkingGuacd": "Se verifică serviciul desktop la distanță...",
"requestingToken": "Se solicită tokenul de conexiune {{type}}...",
"openingSession": "Se deschide sesiunea {{type}}...",
"negotiating": "Se negociază o strângere de mână {{type}}...",
"sessionReady": "{{type}} sesiune gata",
"connectionError": "Eroare de conexiune",
"connectionFailed": "Conexiunea a eșuat",
"failedToConnect": "Nu s-a putut obține token-ul de conexiune",
"hostNotFound": "Gazdă negăsită",
"noHostSelected": "Nicio gazdă selectată",
"reconnect": "Reconectați-vă",
"retry": "Reîncercați",
"guacdUnavailable": "Serviciul desktop la distanță (guacd) nu este disponibil. Asigurați-vă că guacd rulează, este accesibil și configurat corect în setările de administrare.",
"credentialPromptTitle": "Introduceți acreditările RDP",
"credentialPromptDescription": "Această gazdă este configurată să solicite acreditări la conectare. Acestea sunt utilizate doar pentru această sesiune și nu sunt salvate.",
"connect": "Conectează-te",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Fișiere Drive",
"parent": "Directorul părinte",
"refresh": "Reîmprospăta",
"empty": "Acest folder este gol",
"listFailed": "Nu s-a putut citi acest folder",
"download": "Descărcare",
"downloadFailed": "Descărcarea {{name}} a eșuat",
"upload": "Încărcați fișiere",
"uploading": "Se încarcă {{name}}...",
"uploaded": "Încărcat {{name}}",
"uploadFailed": "Nu s-a putut încărca {{name}}",
"uploadDisabled": "Încărcările sunt dezactivate pentru această conexiune",
"driveUnavailable": "Activați redirecționarea unității RDP înainte de încărcarea fișierelor",
"driveNotWritable": "guacd nu poate scrie în folderul unității. Verificați GUACD_DRIVE_PATH (sau calea unității gazdă) și permisiunile acesteia.",
"dropToUpload": "Plasați fișierele pentru încărcare"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Ecran de blocare)",
"winKey": "Cheie Windows",
"superL": "Super+L (Ecran de blocare)",
"superKey": "Super Cheie",
"super": "Super",
"cmdL": "Cmd+L (Ecran de blocare)",
"cmdKey": "Tastă de comandă",
"cmd": "Comandă",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Schimbare",
"win": "Câştiga",
"stickyActive": "{{key}} (blocat - clic pentru eliberare)",
"stickyInactive": "{{key}} (clic pentru blocare)",
"esc": "Evadare",
"tab": "Tab",
"home": "Acasă",
"end": "Sfârşit",
"pageUp": "Pagină sus",
"pageDown": "Pagină în jos",
"arrowUp": "Săgeată în sus",
"arrowDown": "Săgeată în jos",
"arrowLeft": "Săgeată stânga",
"arrowRight": "Săgeată dreapta",
"fnToggle": "Taste funcționale",
"reconnect": "Sesiune de reconectare",
"collapse": "Restrângeți bara de instrumente",
"expand": "Extindeți bara de instrumente",
"dragHandle": "Trageți pentru a repoziționa",
"hide": "Ascundeți pentru această sesiune. Pentru a o dezactiva definitiv, deschideți Setări gazdă → General → Bara de instrumente conexiune.",
"zoomIn": "Măriți",
"zoomOut": "Micșorare",
"resetZoom": "Resetare zoom",
"fitToScreen": "Potrivire la ecran",
"switchToTrackpad": "Comutare la modul trackpad (tragere pentru mutarea cursorului, atingere pentru clic)",
"switchToTouch": "Comutare la modul tactil (atingere direct unde doriți să faceți clic)"
},
"disabled": "Desktopul la distanță este dezactivat. Un administrator îl poate activa în Setări."
},
"hosts": {
"nativeRdpOpened": "Deschis în Desktop la distanță Windows",
"nativeRdpFailed": "Nu s-a putut deschide Desktop la distanță Windows",
"openNativeRdp": "Deschideți în Desktop la distanță Windows",
"guac": {
"toolbar": "Bara de instrumente",
"connection": "Conexiune",
"authentication": "Autentificare",
"storedCredential": "Acreditare stocată",
"authMethod": "Metodă de autentificare",
"authTypeDirect": "Direct",
"authTypeCredential": "Acreditare",
"authTypeNone": "Nici unul",
"authTypeNoneDesc": "Nu sunt stocate acreditări. Vi se va solicita un nume de utilizator și o parolă de fiecare dată când vă conectați; acestea nu sunt salvate.",
"selectCredential": "Selectați o acreditare...",
"connectionSettings": "Setări de conectare",
"displaySettings": "Setări de afișare",
"audioSettings": "Setări audio",
"rdpPerformance": "Performanța RDP",
"deviceRedirection": "Redirecționare dispozitiv",
"session": "Sesiune",
"gateway": "Poartă de acces",
"remoteApp": "RemoteApp",
"clipboard": "Clipboard",
"sessionRecording": "Înregistrarea sesiunii",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "Setări VNC",
"terminalSettings": "Setări terminal",
"rdpPort": "Port RDP",
"username": "Nume de utilizator",
"password": "Parolă",
"passwordSaved": "Parolă salvată, tastați pentru a o modifica",
"domain": "Domeniu",
"securityMode": "Mod de securitate",
"width": "Lăţime",
"height": "Înălţime",
"dpi": "DPI",
"clientName": "Numele clientului",
"initialProgram": "Program inițial",
"serverLayout": "Aspectul serverului",
"timezone": "Fus orar",
"loadBalanceInfo": "Informații despre echilibrul încărcării / Cookie",
"loadBalanceInfoDesc": "Cookie-ul RD Connection Broker pentru echilibrarea încărcării fermei RDS (de exemplu, tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "Proxy guacd",
"guacdHostname": "Gazdă guacd",
"guacdHostnamePlaceholder": "Implicit global",
"guacdPort": "Portul Guacd",
"guacdProxyDesc": "Suprascrie instanța globală guacd pentru această conexiune. Lasă câmpul necompletat pentru a utiliza guacd-ul configurat global.",
"gatewayHostname": "Nume gazdă gateway",
"gatewayPort": "Portul Gateway",
"gatewayUsername": "Nume de utilizator Gateway",
"gatewayPassword": "Parolă gateway",
"gatewayDomain": "Domeniu Gateway",
"remoteAppProgram": "Programul RemoteApp",
"workingDirectory": "Director de lucru",
"arguments": "Argumente",
"normalizeLineEndings": "Normalizarea sfârșitului de linie",
"macAddress": "Adresă MAC",
"broadcastAddress": "Adresă de difuzare",
"udpPort": "Port UDP",
"waitTimeS": "Timp de așteptare (s)",
"driveName": "Nume unitate",
"drivePath": "Cale de acces",
"ignoreCertificate": "Ignoră certificatul",
"ignoreCertificateDesc": "Permiteți conexiuni la gazde cu certificate autosemnate",
"forceLossless": "Forța fără pierderi",
"forceLosslessDesc": "Forțează codificarea imaginilor fără pierderi (calitate mai mare, lățime de bandă mai mare)",
"disableAudio": "Dezactivați sunetul",
"disableAudioDesc": "Dezactivați tot sunetul din sesiunea la distanță",
"enableAudioInput": "Activare intrare audio (microfon)",
"enableAudioInputDesc": "Redirecționează microfonul local către sesiunea la distanță",
"wallpaper": "Tapet",
"wallpaperDesc": "Afișează imaginea de fundal a desktopului (dezactivarea îmbunătățește performanța)",
"theming": "Tematizare",
"themingDesc": "Activează teme și stiluri vizuale",
"fontSmoothing": "Netezirea fonturilor",
"fontSmoothingDesc": "Activează randarea fonturilor ClearType",
"fullWindowDrag": "Tragere pe toată ferestra",
"fullWindowDragDesc": "Afișează conținutul ferestrei la tragere",
"desktopComposition": "Compoziție desktop",
"desktopCompositionDesc": "Activează efectele Aero Glass",
"menuAnimations": "Animații de meniu",
"menuAnimationsDesc": "Activează estomparea meniului și animațiile diapozitivelor",
"disableBitmapCaching": "Dezactivați memorarea în cache a bitmap-urilor",
"disableBitmapCachingDesc": "Dezactivați memoria cache bitmap (poate ajuta la rezolvarea erorilor)",
"disableOffscreenCaching": "Dezactivați memoria cache în afara ecranului",
"disableOffscreenCachingDesc": "Dezactivați memoria cache în afara ecranului",
"disableGlyphCaching": "Dezactivați memoria cache a glifelor",
"disableGlyphCachingDesc": "Dezactivați memoria cache a glifurilor",
"enableGfx": "Activează efectele grafice",
"enableGfxDesc": "Folosește canalul grafic RemoteFX",
"enablePrinting": "Activează imprimarea",
"enablePrintingDesc": "Redirecționează imprimantele locale către sesiunea la distanță",
"enableDriveRedirection": "Activați redirecționarea unității",
"enableDriveRedirectionDesc": "Maparea unui folder local ca unitate în sesiunea la distanță",
"createDrivePath": "Creați o cale de acces",
"createDrivePathDesc": "Creează automat folderul dacă acesta nu există",
"disableDownload": "Dezactivați descărcarea",
"disableDownloadDesc": "Împiedicați descărcarea fișierelor din sesiunea la distanță",
"disableUpload": "Dezactivați încărcarea",
"disableUploadDesc": "Împiedicați încărcarea fișierelor în sesiunea la distanță",
"enableTouch": "Activează atingerea",
"enableTouchDesc": "Activează redirecționarea prin introducerea tactilă",
"consoleSession": "Sesiune consolă",
"consoleSessionDesc": "Conectați-vă la consolă (sesiunea 0) în loc de o sesiune nouă",
"sendWolPacket": "Trimiteți pachetul WOL",
"sendWolPacketDesc": "Trimiteți un pachet magic pentru a activa această gazdă înainte de conectare",
"disableCopy": "Dezactivați copierea",
"disableCopyDesc": "Împiedicați copierea textului din sesiunea la distanță",
"disablePaste": "Dezactivați lipirea",
"disablePasteDesc": "Împiedicați lipirea textului în sesiunea la distanță",
"createPathIfMissing": "Creați o cale dacă lipsește",
"createPathIfMissingDesc": "Creați automat directorul de înregistrare",
"excludeOutput": "Excludeți ieșirea",
"excludeOutputDesc": "Nu înregistrați ieșirea ecranului (doar metadate)",
"excludeMouse": "Excludeți mouse-ul",
"excludeMouseDesc": "Nu înregistrați mișcările mouse-ului",
"includeKeystrokes": "Includeți apăsările de taste",
"includeKeystrokesDesc": "Înregistrați apăsările de taste brute pe lângă ieșirea de pe ecran",
"vncPort": "Port VNC",
"vncPassword": "Parolă VNC",
"vncUsernameOptional": "Nume de utilizator (opțional)",
"vncLeaveBlank": "Lăsați necompletat dacă nu este necesar",
"cursorMode": "Mod cursor",
"swapRedBlue": "Schimbă roșu/albastru",
"swapRedBlueDesc": "Schimbarea canalelor de culoare roșu și albastru (rezolvă unele probleme de culoare)",
"readOnly": "Doar citire",
"readOnlyDesc": "Vizualizați ecranul de la distanță fără a trimite nicio intrare",
"telnetPort": "Port Telnet",
"terminalType": "Tipul terminalului",
"fontName": "Nume font",
"fontSize": "Dimensiunea fontului",
"colorScheme": "Schemă de culori",
"backspaceKey": "Tasta Backspace",
"colorDepth": "Adâncimea culorii",
"resizeMethod": "Metodă de redimensionare",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "Desktop la distanță",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Rețea virtuală",
"unencryptedShell": "Shell necriptat",
"remoteDesktopNotes": "Note",
"userDefaultsNote": "Setările pe care le lăsați neschimbate respectă valorile implicite ale gazdei.",
"connectionOriginNote": "În aplicația desktop, RDP, VNC și Telnet folosesc serverul la distanță, cu excepția cazului în care gazda este setată la Acest dispozitiv, care necesită un guacd pe care acest computer îl poate accesa. Setați unul în setările Desktop la distanță sau pentru fiecare gazdă în secțiunea Proxy guacd.",
"docsLink": "Vizualizați documentele",
"connectionOriginGuacamoleNote": "RDP, VNC și Telnet folosesc serverul la distanță, cu excepția cazului în care selectați „Acest dispozitiv”, care necesită un guacd pe care această mașină îl poate accesa. Setați unul în setarea URL guacd sau pentru fiecare gazdă în guacd Proxy."
},
"onboarding": {
"feature_desktop": "Desktop la distanță",
"feature_desktop_desc": "Deschideți sesiuni RDP, VNC și Telnet într-o filă, la fel ca în cazul SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Adâncimea culorii",
"description": "Folosit pentru gazdele RDP care îl lasă pe setările implicite."
},
"resizeMethod": {
"label": "Metodă de redimensionare",
"description": "Cum urmărește ecranul de la distanță dimensiunea ferestrei."
},
"resizeDisplayUpdate": "Actualizare afișare",
"resizeReconnect": "Reconectați-vă",
"inherit": "Moşteni",
"on": "Pe",
"off": "Dezactivat",
"forceLossless": {
"label": "Forța fără pierderi",
"description": "Forțează codificarea imaginilor fără pierderi (calitate mai mare, lățime de bandă mai mare)"
},
"enableWallpaper": {
"label": "Tapet",
"description": "Afișează imaginea de fundal a desktopului (dezactivarea îmbunătățește performanța)"
},
"enableFontSmoothing": {
"label": "Netezirea fonturilor",
"description": "Activează randarea fonturilor ClearType"
},
"enableDesktopComposition": {
"label": "Compoziție desktop",
"description": "Activează efectele Aero Glass"
},
"disableAudio": {
"label": "Dezactivați sunetul",
"description": "Dezactivați tot sunetul din sesiunea la distanță"
},
"enablePrinting": {
"label": "Activează imprimarea",
"description": "Redirecționează imprimantele locale către sesiunea la distanță"
},
"enableDrive": {
"label": "Activați redirecționarea unității",
"description": "Maparea unui folder local ca unitate în sesiunea la distanță"
},
"disableCopy": {
"label": "Dezactivați copierea",
"description": "Împiedicați copierea textului din sesiunea la distanță"
},
"disablePaste": {
"label": "Dezactivați lipirea",
"description": "Împiedicați lipirea textului în sesiunea la distanță"
},
"group": "Setări implicite Desktop la distanță",
"colorDepth16": "16 biți",
"colorDepth24": "24 de biți",
"colorDepth32": "32 de biți"
},
"admin": {
"enabled": {
"label": "Activează Desktop la distanță",
"description": "Permite conexiuni RDP, VNC și Telnet prin guacd."
},
"guacdUrl": {
"label": "URL-ul guacd",
"description": "Unde guacd ascultă, ca host:port. Variabilele de mediu GUACD_URL, GUACD_HOST și GUACD_PORT suprascriu acest lucru.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "Port RDP"
},
"vncPort": {
"label": "Port VNC"
},
"telnetPort": {
"label": "Port Telnet"
},
"rdpSecurity": {
"label": "Mod de securitate RDP"
},
"rdpIgnoreCert": {
"label": "Ignorați erorile de certificat"
},
"guacamoleConfig": {
"label": "setări guacd"
},
"enableToolbar": {
"label": "Bara de instrumente desktop la distanță",
"description": "Afișează bara de instrumente în sesiunile RDP, VNC și Telnet."
},
"displayGroup": "Afişa",
"displayDefaultsTab": "Desktop la distanță"
}
},
"palette": {
"connectRdp": "Conectați-vă cu RDP",
"connectVnc": "Conectați-vă cu VNC",
"connectTelnet": "Conectați-vă cu Telnet"
},
"permissions": {
"sessions": {
"title": "Partajați sesiuni desktop la distanță",
"description": "Permiteți ca partajarea sesiunilor și camerele de colaborare să arate sesiunile RDP, VNC și Telnet ale acestui utilizator către ceilalți."
}
},
"errors": {
"remoteServerRequired": "Este necesar un server la distanță. Conectați un server la distanță în Setări pentru a utiliza acest tip de conexiune."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Подключение к сессии {{type}}...",
"checkingGuacd": "Checking remote desktop service...",
"requestingToken": "Requesting {{type}} connection token...",
"openingSession": "Opening {{type}} session...",
"negotiating": "Negotiating {{type}} handshake...",
"sessionReady": "{{type}} session ready",
"connectionError": "Ошибка подключения",
"connectionFailed": "Ошибка подключения",
"failedToConnect": "Не удалось получить токен подключения",
"hostNotFound": "Хост не найден",
"noHostSelected": "Хост не выбран",
"reconnect": "Переподключиться",
"retry": "Повторить",
"guacdUnavailable": "Служба удалённого рабочего стола (guacd) недоступна. Убедитесь, что guacd запущен, доступен и правильно настроен в настройках администратора.",
"credentialPromptTitle": "Enter RDP Credentials",
"credentialPromptDescription": "This host is set to prompt for credentials on connect. They are used for this session only and are not saved.",
"connect": "Соединить",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Drive Files",
"parent": "Parent directory",
"refresh": "Обновить",
"empty": "Эта папка пуста",
"listFailed": "Failed to read this folder",
"download": "Скачать",
"downloadFailed": "Failed to download {{name}}",
"upload": "Upload files",
"uploading": "Загрузка {{name}}...",
"uploaded": "Uploaded {{name}}",
"uploadFailed": "Failed to upload {{name}}",
"uploadDisabled": "Uploads are disabled for this connection",
"driveUnavailable": "Enable RDP drive redirection before uploading files",
"driveNotWritable": "guacd cannot write to the drive folder. Check GUACD_DRIVE_PATH (or the host's Drive Path) and its permissions.",
"dropToUpload": "Drop files to upload"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Блокировка экрана)",
"winKey": "Клавиша Windows",
"superL": "Super+L (Lock Screen)",
"superKey": "Super Key",
"super": "Super",
"cmdL": "Cmd+L (Lock Screen)",
"cmdKey": "Command Key",
"cmd": "Cmd",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Shift",
"win": "Win",
"stickyActive": "{{key}} (зафиксирована – кликните для отключения)",
"stickyInactive": "{{key}} (кликните для фиксации)",
"esc": "Escape",
"tab": "Tab",
"home": "Главная",
"end": "End",
"pageUp": "Page Up",
"pageDown": "Page Down",
"arrowUp": "Вверх",
"arrowDown": "Вниз",
"arrowLeft": "Влево",
"arrowRight": "Вправо",
"fnToggle": "Функциональные клавиши",
"reconnect": "Переподключить сеанс",
"collapse": "Свернуть панель",
"expand": "Развернуть панель",
"dragHandle": "Перетащите для перемещения",
"hide": "Hide for this session. To disable it permanently, open Host Settings → General → Connection Toolbar.",
"zoomIn": "Zoom in",
"zoomOut": "Zoom out",
"resetZoom": "Reset zoom",
"fitToScreen": "Fit to screen",
"switchToTrackpad": "Switch to trackpad mode (drag to move cursor, tap to click)",
"switchToTouch": "Switch to touch mode (tap directly where you want to click)"
},
"disabled": "Remote Desktop is turned off. An admin can turn it on in Settings."
},
"hosts": {
"nativeRdpOpened": "Opened in Windows Remote Desktop",
"nativeRdpFailed": "Failed to open Windows Remote Desktop",
"openNativeRdp": "Open in Windows Remote Desktop",
"guac": {
"toolbar": "Toolbar",
"connection": "Подключение",
"authentication": "Аутентификация",
"storedCredential": "Сохраненные учетные данные",
"authMethod": "Метод аутентификации",
"authTypeDirect": "Прямой",
"authTypeCredential": "Учетные данные",
"authTypeNone": "Ни одного",
"authTypeNoneDesc": "No credentials are stored. You'll be prompted for a username and password each time you connect; they are not saved.",
"selectCredential": "Выберите учетные данные...",
"connectionSettings": "Параметры подключения",
"displaySettings": "Параметры отображения",
"audioSettings": "Параметры звука",
"rdpPerformance": "Производительность RDP",
"deviceRedirection": "Перенаправление устройств",
"session": "Сессия",
"gateway": "Шлюз",
"remoteApp": "RemoteApp",
"clipboard": "Буфер обмена",
"sessionRecording": "Запись сессии",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "Параметры VNC",
"terminalSettings": "Параметры терминала",
"rdpPort": "Порт RDP",
"username": "Имя пользователя",
"password": "Пароль",
"passwordSaved": "Password saved, type to change",
"domain": "Домен",
"securityMode": "Режим безопасности",
"width": "Ширина",
"height": "Высота",
"dpi": "DPI",
"clientName": "Имя клиента",
"initialProgram": "Начальная программа",
"serverLayout": "Раскладка сервера",
"timezone": "Часовой пояс",
"loadBalanceInfo": "Информация о балансировке нагрузки / Cookie",
"loadBalanceInfoDesc": "Cookie брокера подключений к удаленному рабочему столу для балансировки нагрузки фермы RDS (например, tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "Прокси guacd",
"guacdHostname": "Хост guacd",
"guacdHostnamePlaceholder": "Глобальное значение по умолчанию",
"guacdPort": "Порт guacd",
"guacdProxyDesc": "Переопределить глобальный экземпляр guacd для этого соединения. Оставьте пустым для использования глобально настроенного guacd.",
"gatewayHostname": "Имя хоста шлюза",
"gatewayPort": "Порт шлюза",
"gatewayUsername": "Имя пользователя шлюза",
"gatewayPassword": "Пароль шлюза",
"gatewayDomain": "Домен шлюза",
"remoteAppProgram": "Программа RemoteApp",
"workingDirectory": "Рабочий каталог",
"arguments": "Аргументы",
"normalizeLineEndings": "Нормализовать окончания строк",
"macAddress": "MAC-адрес",
"broadcastAddress": "Широковещательный адрес",
"udpPort": "Порт UDP",
"waitTimeS": "Время ожидания (с)",
"driveName": "Имя диска",
"drivePath": "Путь к диску",
"ignoreCertificate": "Игнорировать сертификат",
"ignoreCertificateDesc": "Разрешить подключения к хостам с самоподписанными сертификатами",
"forceLossless": "Принудительно без потерь",
"forceLosslessDesc": "Принудительное кодирование изображений без потерь (более высокое качество, больший расход трафика)",
"disableAudio": "Отключить звук",
"disableAudioDesc": "Отключить весь звук в удаленном сеансе",
"enableAudioInput": "Включить аудиовход (микрофон)",
"enableAudioInputDesc": "Передавать локальный микрофон в удаленный сеанс",
"wallpaper": "Обои",
"wallpaperDesc": "Показывать обои рабочего стола (отключение повышает производительность)",
"theming": "Темы",
"themingDesc": "Включить визуальные темы и стили",
"fontSmoothing": "Сглаживание шрифтов",
"fontSmoothingDesc": "Включить рендеринг шрифтов ClearType",
"fullWindowDrag": "Перетаскивание окон с содержимым",
"fullWindowDragDesc": "Показывать содержимое окна при перетаскивании",
"desktopComposition": "Композиция рабочего стола",
"desktopCompositionDesc": "Включить эффекты Aero Glass",
"menuAnimations": "Анимации меню",
"menuAnimationsDesc": "Включить анимацию исчезновения и сдвига меню",
"disableBitmapCaching": "Отключить кеширование растровых изображений",
"disableBitmapCachingDesc": "Отключить кеш растровых изображений (может помочь при сбоях)",
"disableOffscreenCaching": "Отключить внеэкранное кеширование",
"disableOffscreenCachingDesc": "Отключить внеэкранный кеш",
"disableGlyphCaching": "Отключить кеширование глифов",
"disableGlyphCachingDesc": "Отключить кеш глифов",
"enableGfx": "Включить GFX",
"enableGfxDesc": "Использовать графический конвейер RemoteFX",
"enablePrinting": "Включить печать",
"enablePrintingDesc": "Перенаправлять локальные принтеры в удаленный сеанс",
"enableDriveRedirection": "Включить перенаправление дисков",
"enableDriveRedirectionDesc": "Подключать локальную папку как диск в удаленном сеансе",
"createDrivePath": "Создать путь к диску",
"createDrivePathDesc": "Автоматически создать папку, если она не существует",
"disableDownload": "Отключить загрузку",
"disableDownloadDesc": "Запретить загрузку файлов из удаленного сеанса",
"disableUpload": "Отключить выгрузку",
"disableUploadDesc": "Запретить выгрузку файлов в удаленный сеанс",
"enableTouch": "Включить сенсорный ввод",
"enableTouchDesc": "Включить передачу сенсорного ввода",
"consoleSession": "Консольный сеанс",
"consoleSessionDesc": "Подключиться к консоли (сеанс 0) вместо создания нового сеанса",
"sendWolPacket": "Отправить WOL-пакет",
"sendWolPacketDesc": "Отправить magic-пакет для пробуждения хоста перед подключением",
"disableCopy": "Отключить копирование",
"disableCopyDesc": "Запретить копирование текста из удаленного сеанса",
"disablePaste": "Отключить вставку",
"disablePasteDesc": "Запретить вставку текста в удаленный сеанс",
"createPathIfMissing": "Создать путь, если отсутствует",
"createPathIfMissingDesc": "Автоматически создать каталог для записей",
"excludeOutput": "Исключить вывод",
"excludeOutputDesc": "Не записывать вывод экрана (только метаданные)",
"excludeMouse": "Исключить мышь",
"excludeMouseDesc": "Не записывать движения мыши",
"includeKeystrokes": "Записывать нажатия",
"includeKeystrokesDesc": "Записывать необработанные нажатия клавиш в дополнение к выводу экрана",
"vncPort": "Порт VNC",
"vncPassword": "Пароль VNC",
"vncUsernameOptional": "Имя пользователя (необязательно)",
"vncLeaveBlank": "Оставьте пустым, если не требуется",
"cursorMode": "Режим курсора",
"swapRedBlue": "Поменять красный/синий",
"swapRedBlueDesc": "Обменять красный и синий цветовые каналы (исправляет некоторые проблемы с цветом)",
"readOnly": "Только чтение",
"readOnlyDesc": "Просмотр удаленного экрана без отправки ввода",
"telnetPort": "Порт Telnet",
"terminalType": "Тип терминала",
"fontName": "Название шрифта",
"fontSize": "Размер шрифта",
"colorScheme": "Цветовая схема",
"backspaceKey": "Клавиша Backspace",
"colorDepth": "Глубина цвета",
"resizeMethod": "Метод изменения размера",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "Удаленный рабочий стол",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Виртуальная сеть",
"unencryptedShell": "Незашифрованная оболочка",
"remoteDesktopNotes": "Заметки",
"userDefaultsNote": "Settings you leave unchanged follow your host defaults.",
"connectionOriginNote": "On the desktop app, RDP, VNC and Telnet use the remote server unless the host is set to This device, which needs a guacd this machine can reach. Set one in the Remote Desktop settings, or per host under guacd Proxy.",
"docsLink": "Документация",
"connectionOriginGuacamoleNote": "RDP, VNC and Telnet use the remote server unless you pick \"This device\", which needs a guacd this machine can reach. Set one in the guacd URL setting, or per host under guacd Proxy."
},
"onboarding": {
"feature_desktop": "Remote desktop",
"feature_desktop_desc": "Open RDP, VNC and Telnet sessions in a tab, same as SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Глубина цвета",
"description": "Used for RDP hosts that leave it on their default."
},
"resizeMethod": {
"label": "Метод изменения размера",
"description": "How the remote screen follows the window size."
},
"resizeDisplayUpdate": "Display Update",
"resizeReconnect": "Переподключиться",
"inherit": "Inherit",
"on": "Включено",
"off": "Отключено",
"forceLossless": {
"label": "Принудительно без потерь",
"description": "Принудительное кодирование изображений без потерь (более высокое качество, больший расход трафика)"
},
"enableWallpaper": {
"label": "Обои",
"description": "Показывать обои рабочего стола (отключение повышает производительность)"
},
"enableFontSmoothing": {
"label": "Сглаживание шрифтов",
"description": "Включить рендеринг шрифтов ClearType"
},
"enableDesktopComposition": {
"label": "Композиция рабочего стола",
"description": "Включить эффекты Aero Glass"
},
"disableAudio": {
"label": "Отключить звук",
"description": "Отключить весь звук в удаленном сеансе"
},
"enablePrinting": {
"label": "Включить печать",
"description": "Перенаправлять локальные принтеры в удаленный сеанс"
},
"enableDrive": {
"label": "Включить перенаправление дисков",
"description": "Подключать локальную папку как диск в удаленном сеансе"
},
"disableCopy": {
"label": "Отключить копирование",
"description": "Запретить копирование текста из удаленного сеанса"
},
"disablePaste": {
"label": "Отключить вставку",
"description": "Запретить вставку текста в удаленный сеанс"
},
"group": "Remote Desktop defaults",
"colorDepth16": "16-bit",
"colorDepth24": "24-bit",
"colorDepth32": "32-bit"
},
"admin": {
"enabled": {
"label": "Enable Remote Desktop",
"description": "Allow RDP, VNC and Telnet connections through guacd."
},
"guacdUrl": {
"label": "URL guacd",
"description": "Where guacd listens, as host:port. The GUACD_URL, GUACD_HOST and GUACD_PORT environment variables override this.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP port"
},
"vncPort": {
"label": "VNC port"
},
"telnetPort": {
"label": "Telnet port"
},
"rdpSecurity": {
"label": "RDP security mode"
},
"rdpIgnoreCert": {
"label": "Ignore certificate errors"
},
"guacamoleConfig": {
"label": "guacd settings"
},
"enableToolbar": {
"label": "Remote desktop toolbar",
"description": "Show the toolbar in RDP, VNC and Telnet sessions."
},
"displayGroup": "Display",
"displayDefaultsTab": "Удаленный рабочий стол"
}
},
"palette": {
"connectRdp": "Connect with RDP",
"connectVnc": "Connect with VNC",
"connectTelnet": "Connect with Telnet"
},
"permissions": {
"sessions": {
"title": "Share remote desktop sessions",
"description": "Let session sharing and collab rooms show this user's RDP, VNC and Telnet sessions to others."
}
},
"errors": {
"remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Connecting to {{type}} session...",
"checkingGuacd": "Checking remote desktop service...",
"requestingToken": "Requesting {{type}} connection token...",
"openingSession": "Opening {{type}} session...",
"negotiating": "Negotiating {{type}} handshake...",
"sessionReady": "{{type}} session ready",
"connectionError": "Connection error",
"connectionFailed": "Connection failed",
"failedToConnect": "Failed to get connection token",
"hostNotFound": "Host not found",
"noHostSelected": "No host selected",
"reconnect": "Reconnect",
"retry": "Retry",
"guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.",
"credentialPromptTitle": "Enter RDP Credentials",
"credentialPromptDescription": "This host is set to prompt for credentials on connect. They are used for this session only and are not saved.",
"connect": "Connect",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Drive Files",
"parent": "Parent directory",
"refresh": "Refresh",
"empty": "This folder is empty",
"listFailed": "Failed to read this folder",
"download": "Download",
"downloadFailed": "Failed to download {{name}}",
"upload": "Upload files",
"uploading": "Uploading {{name}}...",
"uploaded": "Uploaded {{name}}",
"uploadFailed": "Failed to upload {{name}}",
"uploadDisabled": "Uploads are disabled for this connection",
"driveUnavailable": "Enable RDP drive redirection before uploading files",
"driveNotWritable": "guacd cannot write to the drive folder. Check GUACD_DRIVE_PATH (or the host's Drive Path) and its permissions.",
"dropToUpload": "Drop files to upload"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Lock Screen)",
"winKey": "Windows Key",
"superL": "Super+L (Lock Screen)",
"superKey": "Super Key",
"super": "Super",
"cmdL": "Cmd+L (Lock Screen)",
"cmdKey": "Command Key",
"cmd": "Cmd",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Shift",
"win": "Win",
"stickyActive": "{{key}} (latched - click to release)",
"stickyInactive": "{{key}} (click to latch)",
"esc": "Escape",
"tab": "Tab",
"home": "Home",
"end": "End",
"pageUp": "Page Up",
"pageDown": "Page Down",
"arrowUp": "Arrow Up",
"arrowDown": "Arrow Down",
"arrowLeft": "Arrow Left",
"arrowRight": "Arrow Right",
"fnToggle": "Function Keys",
"reconnect": "Reconnect Session",
"collapse": "Collapse toolbar",
"expand": "Expand toolbar",
"dragHandle": "Drag to reposition",
"hide": "Hide for this session. To disable it permanently, open Host Settings → General → Connection Toolbar.",
"zoomIn": "Zoom in",
"zoomOut": "Zoom out",
"resetZoom": "Reset zoom",
"fitToScreen": "Fit to screen",
"switchToTrackpad": "Switch to trackpad mode (drag to move cursor, tap to click)",
"switchToTouch": "Switch to touch mode (tap directly where you want to click)"
},
"disabled": "Remote Desktop is turned off. An admin can turn it on in Settings."
},
"hosts": {
"nativeRdpOpened": "Opened in Windows Remote Desktop",
"nativeRdpFailed": "Failed to open Windows Remote Desktop",
"openNativeRdp": "Open in Windows Remote Desktop",
"guac": {
"toolbar": "Toolbar",
"connection": "Connection",
"authentication": "Authentication",
"storedCredential": "Stored Credential",
"authMethod": "Auth Method",
"authTypeDirect": "Direct",
"authTypeCredential": "Credential",
"authTypeNone": "None",
"authTypeNoneDesc": "No credentials are stored. You'll be prompted for a username and password each time you connect; they are not saved.",
"selectCredential": "Select a credential...",
"connectionSettings": "Connection Settings",
"displaySettings": "Display Settings",
"audioSettings": "Audio Settings",
"rdpPerformance": "RDP Performance",
"deviceRedirection": "Device Redirection",
"session": "Session",
"gateway": "Gateway",
"remoteApp": "RemoteApp",
"clipboard": "Clipboard",
"sessionRecording": "Session Recording",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "VNC Settings",
"terminalSettings": "Terminal Settings",
"rdpPort": "RDP Port",
"username": "Username",
"password": "Password",
"passwordSaved": "Password saved, type to change",
"domain": "Domain",
"securityMode": "Security Mode",
"width": "Width",
"height": "Height",
"dpi": "DPI",
"clientName": "Client Name",
"initialProgram": "Initial Program",
"serverLayout": "Server Layout",
"timezone": "Timezone",
"loadBalanceInfo": "Load Balance Info / Cookie",
"loadBalanceInfoDesc": "RD Connection Broker cookie for RDS farm load balancing (e.g. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacd Proxy",
"guacdHostname": "guacd Host",
"guacdHostnamePlaceholder": "Global default",
"guacdPort": "guacd Port",
"guacdProxyDesc": "Override the global guacd instance for this connection. Leave blank to use the globally configured guacd.",
"gatewayHostname": "Gateway Hostname",
"gatewayPort": "Gateway Port",
"gatewayUsername": "Gateway Username",
"gatewayPassword": "Gateway Password",
"gatewayDomain": "Gateway Domain",
"remoteAppProgram": "RemoteApp Program",
"workingDirectory": "Working Directory",
"arguments": "Arguments",
"normalizeLineEndings": "Normalize Line Endings",
"macAddress": "MAC Address",
"broadcastAddress": "Broadcast Address",
"udpPort": "UDP Port",
"waitTimeS": "Wait Time (s)",
"driveName": "Drive Name",
"drivePath": "Drive Path",
"ignoreCertificate": "Ignore Certificate",
"ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates",
"forceLossless": "Force Lossless",
"forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)",
"disableAudio": "Disable Audio",
"disableAudioDesc": "Mute all audio from the remote session",
"enableAudioInput": "Enable Audio Input (Microphone)",
"enableAudioInputDesc": "Forward local microphone to the remote session",
"wallpaper": "Wallpaper",
"wallpaperDesc": "Show desktop wallpaper (disabling improves performance)",
"theming": "Theming",
"themingDesc": "Enable visual themes and styles",
"fontSmoothing": "Font Smoothing",
"fontSmoothingDesc": "Enable ClearType font rendering",
"fullWindowDrag": "Full Window Drag",
"fullWindowDragDesc": "Show window contents while dragging",
"desktopComposition": "Desktop Composition",
"desktopCompositionDesc": "Enable Aero glass effects",
"menuAnimations": "Menu Animations",
"menuAnimationsDesc": "Enable menu fade and slide animations",
"disableBitmapCaching": "Disable Bitmap Caching",
"disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)",
"disableOffscreenCaching": "Disable Offscreen Caching",
"disableOffscreenCachingDesc": "Turn off offscreen cache",
"disableGlyphCaching": "Disable Glyph Caching",
"disableGlyphCachingDesc": "Turn off glyph cache",
"enableGfx": "Enable GFX",
"enableGfxDesc": "Use RemoteFX graphics pipeline",
"enablePrinting": "Enable Printing",
"enablePrintingDesc": "Redirect local printers to the remote session",
"enableDriveRedirection": "Enable Drive Redirection",
"enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session",
"createDrivePath": "Create Drive Path",
"createDrivePathDesc": "Automatically create the folder if it does not exist",
"disableDownload": "Disable Download",
"disableDownloadDesc": "Prevent downloading files from the remote session",
"disableUpload": "Disable Upload",
"disableUploadDesc": "Prevent uploading files to the remote session",
"enableTouch": "Enable Touch",
"enableTouchDesc": "Enable touch input forwarding",
"consoleSession": "Console Session",
"consoleSessionDesc": "Connect to the console (session 0) instead of a new session",
"sendWolPacket": "Send WOL Packet",
"sendWolPacketDesc": "Send a magic packet to wake this host before connecting",
"disableCopy": "Disable Copy",
"disableCopyDesc": "Prevent copying text from the remote session",
"disablePaste": "Disable Paste",
"disablePasteDesc": "Prevent pasting text into the remote session",
"createPathIfMissing": "Create Path if Missing",
"createPathIfMissingDesc": "Automatically create the recording directory",
"excludeOutput": "Exclude Output",
"excludeOutputDesc": "Do not record screen output (metadata only)",
"excludeMouse": "Exclude Mouse",
"excludeMouseDesc": "Do not record mouse movements",
"includeKeystrokes": "Include Keystrokes",
"includeKeystrokesDesc": "Record raw keystrokes in addition to screen output",
"vncPort": "VNC Port",
"vncPassword": "VNC Password",
"vncUsernameOptional": "Username (optional)",
"vncLeaveBlank": "Leave blank if not required",
"cursorMode": "Cursor Mode",
"swapRedBlue": "Swap Red/Blue",
"swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)",
"readOnly": "Read-only",
"readOnlyDesc": "View the remote screen without sending any input",
"telnetPort": "Telnet Port",
"terminalType": "Terminal Type",
"fontName": "Font Name",
"fontSize": "Font Size",
"colorScheme": "Color Scheme",
"backspaceKey": "Backspace Key",
"colorDepth": "Color Depth",
"resizeMethod": "Resize Method",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "Remote Desktop",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Virtual Network",
"unencryptedShell": "Unencrypted shell",
"remoteDesktopNotes": "Notes",
"userDefaultsNote": "Settings you leave unchanged follow your host defaults.",
"connectionOriginNote": "On the desktop app, RDP, VNC and Telnet use the remote server unless the host is set to This device, which needs a guacd this machine can reach. Set one in the Remote Desktop settings, or per host under guacd Proxy.",
"docsLink": "View docs",
"connectionOriginGuacamoleNote": "RDP, VNC and Telnet use the remote server unless you pick \"This device\", which needs a guacd this machine can reach. Set one in the guacd URL setting, or per host under guacd Proxy."
},
"onboarding": {
"feature_desktop": "Remote desktop",
"feature_desktop_desc": "Open RDP, VNC and Telnet sessions in a tab, same as SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Color Depth",
"description": "Used for RDP hosts that leave it on their default."
},
"resizeMethod": {
"label": "Resize Method",
"description": "How the remote screen follows the window size."
},
"resizeDisplayUpdate": "Display Update",
"resizeReconnect": "Reconnect",
"inherit": "Inherit",
"on": "On",
"off": "Off",
"forceLossless": {
"label": "Force Lossless",
"description": "Force lossless image encoding (higher quality, more bandwidth)"
},
"enableWallpaper": {
"label": "Wallpaper",
"description": "Show desktop wallpaper (disabling improves performance)"
},
"enableFontSmoothing": {
"label": "Font Smoothing",
"description": "Enable ClearType font rendering"
},
"enableDesktopComposition": {
"label": "Desktop Composition",
"description": "Enable Aero glass effects"
},
"disableAudio": {
"label": "Disable Audio",
"description": "Mute all audio from the remote session"
},
"enablePrinting": {
"label": "Enable Printing",
"description": "Redirect local printers to the remote session"
},
"enableDrive": {
"label": "Enable Drive Redirection",
"description": "Map a local folder as a drive in the remote session"
},
"disableCopy": {
"label": "Disable Copy",
"description": "Prevent copying text from the remote session"
},
"disablePaste": {
"label": "Disable Paste",
"description": "Prevent pasting text into the remote session"
},
"group": "Remote Desktop defaults",
"colorDepth16": "16-bit",
"colorDepth24": "24-bit",
"colorDepth32": "32-bit"
},
"admin": {
"enabled": {
"label": "Enable Remote Desktop",
"description": "Allow RDP, VNC and Telnet connections through guacd."
},
"guacdUrl": {
"label": "guacd URL",
"description": "Where guacd listens, as host:port. The GUACD_URL, GUACD_HOST and GUACD_PORT environment variables override this.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP port"
},
"vncPort": {
"label": "VNC port"
},
"telnetPort": {
"label": "Telnet port"
},
"rdpSecurity": {
"label": "RDP security mode"
},
"rdpIgnoreCert": {
"label": "Ignore certificate errors"
},
"guacamoleConfig": {
"label": "guacd settings"
},
"enableToolbar": {
"label": "Remote desktop toolbar",
"description": "Show the toolbar in RDP, VNC and Telnet sessions."
},
"displayGroup": "Display",
"displayDefaultsTab": "Remote Desktop"
}
},
"palette": {
"connectRdp": "Connect with RDP",
"connectVnc": "Connect with VNC",
"connectTelnet": "Connect with Telnet"
},
"permissions": {
"sessions": {
"title": "Share remote desktop sessions",
"description": "Let session sharing and collab rooms show this user's RDP, VNC and Telnet sessions to others."
}
},
"errors": {
"remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Connecting to {{type}} session...",
"checkingGuacd": "Checking remote desktop service...",
"requestingToken": "Requesting {{type}} connection token...",
"openingSession": "Opening {{type}} session...",
"negotiating": "Negotiating {{type}} handshake...",
"sessionReady": "{{type}} session ready",
"connectionError": "Connection error",
"connectionFailed": "Connection failed",
"failedToConnect": "Failed to get connection token",
"hostNotFound": "Host not found",
"noHostSelected": "No host selected",
"reconnect": "Reconnect",
"retry": "Retry",
"guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.",
"credentialPromptTitle": "Enter RDP Credentials",
"credentialPromptDescription": "This host is set to prompt for credentials on connect. They are used for this session only and are not saved.",
"connect": "Connect",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Drive Files",
"parent": "Parent directory",
"refresh": "Refresh",
"empty": "This folder is empty",
"listFailed": "Failed to read this folder",
"download": "Download",
"downloadFailed": "Failed to download {{name}}",
"upload": "Upload files",
"uploading": "Uploading {{name}}...",
"uploaded": "Uploaded {{name}}",
"uploadFailed": "Failed to upload {{name}}",
"uploadDisabled": "Uploads are disabled for this connection",
"driveUnavailable": "Enable RDP drive redirection before uploading files",
"driveNotWritable": "guacd cannot write to the drive folder. Check GUACD_DRIVE_PATH (or the host's Drive Path) and its permissions.",
"dropToUpload": "Drop files to upload"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Lock Screen)",
"winKey": "Windows Key",
"superL": "Super+L (Lock Screen)",
"superKey": "Super Key",
"super": "Super",
"cmdL": "Cmd+L (Lock Screen)",
"cmdKey": "Command Key",
"cmd": "Cmd",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Shift",
"win": "Win",
"stickyActive": "{{key}} (latched - click to release)",
"stickyInactive": "{{key}} (click to latch)",
"esc": "Escape",
"tab": "Tab",
"home": "Home",
"end": "End",
"pageUp": "Page Up",
"pageDown": "Page Down",
"arrowUp": "Arrow Up",
"arrowDown": "Arrow Down",
"arrowLeft": "Arrow Left",
"arrowRight": "Arrow Right",
"fnToggle": "Function Keys",
"reconnect": "Reconnect Session",
"collapse": "Collapse toolbar",
"expand": "Expand toolbar",
"dragHandle": "Drag to reposition",
"hide": "Hide for this session. To disable it permanently, open Host Settings → General → Connection Toolbar.",
"zoomIn": "Zoom in",
"zoomOut": "Zoom out",
"resetZoom": "Reset zoom",
"fitToScreen": "Fit to screen",
"switchToTrackpad": "Switch to trackpad mode (drag to move cursor, tap to click)",
"switchToTouch": "Switch to touch mode (tap directly where you want to click)"
},
"disabled": "Remote Desktop is turned off. An admin can turn it on in Settings."
},
"hosts": {
"nativeRdpOpened": "Opened in Windows Remote Desktop",
"nativeRdpFailed": "Failed to open Windows Remote Desktop",
"openNativeRdp": "Open in Windows Remote Desktop",
"guac": {
"toolbar": "Toolbar",
"connection": "Connection",
"authentication": "Authentication",
"storedCredential": "Stored Credential",
"authMethod": "Auth Method",
"authTypeDirect": "Direct",
"authTypeCredential": "Credential",
"authTypeNone": "None",
"authTypeNoneDesc": "No credentials are stored. You'll be prompted for a username and password each time you connect; they are not saved.",
"selectCredential": "Select a credential...",
"connectionSettings": "Connection Settings",
"displaySettings": "Display Settings",
"audioSettings": "Audio Settings",
"rdpPerformance": "RDP Performance",
"deviceRedirection": "Device Redirection",
"session": "Session",
"gateway": "Gateway",
"remoteApp": "RemoteApp",
"clipboard": "Clipboard",
"sessionRecording": "Session Recording",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "VNC Settings",
"terminalSettings": "Terminal Settings",
"rdpPort": "RDP Port",
"username": "Username",
"password": "Password",
"passwordSaved": "Password saved, type to change",
"domain": "Domain",
"securityMode": "Security Mode",
"width": "Width",
"height": "Height",
"dpi": "DPI",
"clientName": "Client Name",
"initialProgram": "Initial Program",
"serverLayout": "Server Layout",
"timezone": "Timezone",
"loadBalanceInfo": "Load Balance Info / Cookie",
"loadBalanceInfoDesc": "RD Connection Broker cookie for RDS farm load balancing (e.g. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacd Proxy",
"guacdHostname": "guacd Host",
"guacdHostnamePlaceholder": "Global default",
"guacdPort": "guacd Port",
"guacdProxyDesc": "Override the global guacd instance for this connection. Leave blank to use the globally configured guacd.",
"gatewayHostname": "Gateway Hostname",
"gatewayPort": "Gateway Port",
"gatewayUsername": "Gateway Username",
"gatewayPassword": "Gateway Password",
"gatewayDomain": "Gateway Domain",
"remoteAppProgram": "RemoteApp Program",
"workingDirectory": "Working Directory",
"arguments": "Arguments",
"normalizeLineEndings": "Normalize Line Endings",
"macAddress": "MAC Address",
"broadcastAddress": "Broadcast Address",
"udpPort": "UDP Port",
"waitTimeS": "Wait Time (s)",
"driveName": "Drive Name",
"drivePath": "Drive Path",
"ignoreCertificate": "Ignore Certificate",
"ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates",
"forceLossless": "Force Lossless",
"forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)",
"disableAudio": "Disable Audio",
"disableAudioDesc": "Mute all audio from the remote session",
"enableAudioInput": "Enable Audio Input (Microphone)",
"enableAudioInputDesc": "Forward local microphone to the remote session",
"wallpaper": "Wallpaper",
"wallpaperDesc": "Show desktop wallpaper (disabling improves performance)",
"theming": "Theming",
"themingDesc": "Enable visual themes and styles",
"fontSmoothing": "Font Smoothing",
"fontSmoothingDesc": "Enable ClearType font rendering",
"fullWindowDrag": "Full Window Drag",
"fullWindowDragDesc": "Show window contents while dragging",
"desktopComposition": "Desktop Composition",
"desktopCompositionDesc": "Enable Aero glass effects",
"menuAnimations": "Menu Animations",
"menuAnimationsDesc": "Enable menu fade and slide animations",
"disableBitmapCaching": "Disable Bitmap Caching",
"disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)",
"disableOffscreenCaching": "Disable Offscreen Caching",
"disableOffscreenCachingDesc": "Turn off offscreen cache",
"disableGlyphCaching": "Disable Glyph Caching",
"disableGlyphCachingDesc": "Turn off glyph cache",
"enableGfx": "Enable GFX",
"enableGfxDesc": "Use RemoteFX graphics pipeline",
"enablePrinting": "Enable Printing",
"enablePrintingDesc": "Redirect local printers to the remote session",
"enableDriveRedirection": "Enable Drive Redirection",
"enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session",
"createDrivePath": "Create Drive Path",
"createDrivePathDesc": "Automatically create the folder if it does not exist",
"disableDownload": "Disable Download",
"disableDownloadDesc": "Prevent downloading files from the remote session",
"disableUpload": "Disable Upload",
"disableUploadDesc": "Prevent uploading files to the remote session",
"enableTouch": "Enable Touch",
"enableTouchDesc": "Enable touch input forwarding",
"consoleSession": "Console Session",
"consoleSessionDesc": "Connect to the console (session 0) instead of a new session",
"sendWolPacket": "Send WOL Packet",
"sendWolPacketDesc": "Send a magic packet to wake this host before connecting",
"disableCopy": "Disable Copy",
"disableCopyDesc": "Prevent copying text from the remote session",
"disablePaste": "Disable Paste",
"disablePasteDesc": "Prevent pasting text into the remote session",
"createPathIfMissing": "Create Path if Missing",
"createPathIfMissingDesc": "Automatically create the recording directory",
"excludeOutput": "Exclude Output",
"excludeOutputDesc": "Do not record screen output (metadata only)",
"excludeMouse": "Exclude Mouse",
"excludeMouseDesc": "Do not record mouse movements",
"includeKeystrokes": "Include Keystrokes",
"includeKeystrokesDesc": "Record raw keystrokes in addition to screen output",
"vncPort": "VNC Port",
"vncPassword": "VNC Password",
"vncUsernameOptional": "Username (optional)",
"vncLeaveBlank": "Leave blank if not required",
"cursorMode": "Cursor Mode",
"swapRedBlue": "Swap Red/Blue",
"swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)",
"readOnly": "Read-only",
"readOnlyDesc": "View the remote screen without sending any input",
"telnetPort": "Telnet Port",
"terminalType": "Terminal Type",
"fontName": "Font Name",
"fontSize": "Font Size",
"colorScheme": "Color Scheme",
"backspaceKey": "Backspace Key",
"colorDepth": "Color Depth",
"resizeMethod": "Resize Method",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "Remote Desktop",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Virtual Network",
"unencryptedShell": "Unencrypted shell",
"remoteDesktopNotes": "Notes",
"userDefaultsNote": "Settings you leave unchanged follow your host defaults.",
"connectionOriginNote": "On the desktop app, RDP, VNC and Telnet use the remote server unless the host is set to This device, which needs a guacd this machine can reach. Set one in the Remote Desktop settings, or per host under guacd Proxy.",
"docsLink": "View docs",
"connectionOriginGuacamoleNote": "RDP, VNC and Telnet use the remote server unless you pick \"This device\", which needs a guacd this machine can reach. Set one in the guacd URL setting, or per host under guacd Proxy."
},
"onboarding": {
"feature_desktop": "Remote desktop",
"feature_desktop_desc": "Open RDP, VNC and Telnet sessions in a tab, same as SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Color Depth",
"description": "Used for RDP hosts that leave it on their default."
},
"resizeMethod": {
"label": "Resize Method",
"description": "How the remote screen follows the window size."
},
"resizeDisplayUpdate": "Display Update",
"resizeReconnect": "Reconnect",
"inherit": "Inherit",
"on": "On",
"off": "Off",
"forceLossless": {
"label": "Force Lossless",
"description": "Force lossless image encoding (higher quality, more bandwidth)"
},
"enableWallpaper": {
"label": "Wallpaper",
"description": "Show desktop wallpaper (disabling improves performance)"
},
"enableFontSmoothing": {
"label": "Font Smoothing",
"description": "Enable ClearType font rendering"
},
"enableDesktopComposition": {
"label": "Desktop Composition",
"description": "Enable Aero glass effects"
},
"disableAudio": {
"label": "Disable Audio",
"description": "Mute all audio from the remote session"
},
"enablePrinting": {
"label": "Enable Printing",
"description": "Redirect local printers to the remote session"
},
"enableDrive": {
"label": "Enable Drive Redirection",
"description": "Map a local folder as a drive in the remote session"
},
"disableCopy": {
"label": "Disable Copy",
"description": "Prevent copying text from the remote session"
},
"disablePaste": {
"label": "Disable Paste",
"description": "Prevent pasting text into the remote session"
},
"group": "Remote Desktop defaults",
"colorDepth16": "16-bit",
"colorDepth24": "24-bit",
"colorDepth32": "32-bit"
},
"admin": {
"enabled": {
"label": "Enable Remote Desktop",
"description": "Allow RDP, VNC and Telnet connections through guacd."
},
"guacdUrl": {
"label": "guacd URL",
"description": "Where guacd listens, as host:port. The GUACD_URL, GUACD_HOST and GUACD_PORT environment variables override this.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP port"
},
"vncPort": {
"label": "VNC port"
},
"telnetPort": {
"label": "Telnet port"
},
"rdpSecurity": {
"label": "RDP security mode"
},
"rdpIgnoreCert": {
"label": "Ignore certificate errors"
},
"guacamoleConfig": {
"label": "guacd settings"
},
"enableToolbar": {
"label": "Remote desktop toolbar",
"description": "Show the toolbar in RDP, VNC and Telnet sessions."
},
"displayGroup": "Display",
"displayDefaultsTab": "Remote Desktop"
}
},
"palette": {
"connectRdp": "Connect with RDP",
"connectVnc": "Connect with VNC",
"connectTelnet": "Connect with Telnet"
},
"permissions": {
"sessions": {
"title": "Share remote desktop sessions",
"description": "Let session sharing and collab rooms show this user's RDP, VNC and Telnet sessions to others."
}
},
"errors": {
"remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Connecting to {{type}} session...",
"checkingGuacd": "Checking remote desktop service...",
"requestingToken": "Requesting {{type}} connection token...",
"openingSession": "Opening {{type}} session...",
"negotiating": "Negotiating {{type}} handshake...",
"sessionReady": "{{type}} session ready",
"connectionError": "Connection error",
"connectionFailed": "Connection failed",
"failedToConnect": "Failed to get connection token",
"hostNotFound": "Host not found",
"noHostSelected": "No host selected",
"reconnect": "Reconnect",
"retry": "Retry",
"guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.",
"credentialPromptTitle": "Enter RDP Credentials",
"credentialPromptDescription": "This host is set to prompt for credentials on connect. They are used for this session only and are not saved.",
"connect": "Connect",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Drive Files",
"parent": "Parent directory",
"refresh": "Refresh",
"empty": "This folder is empty",
"listFailed": "Failed to read this folder",
"download": "Download",
"downloadFailed": "Failed to download {{name}}",
"upload": "Upload files",
"uploading": "Uploading {{name}}...",
"uploaded": "Uploaded {{name}}",
"uploadFailed": "Failed to upload {{name}}",
"uploadDisabled": "Uploads are disabled for this connection",
"driveUnavailable": "Enable RDP drive redirection before uploading files",
"driveNotWritable": "guacd cannot write to the drive folder. Check GUACD_DRIVE_PATH (or the host's Drive Path) and its permissions.",
"dropToUpload": "Drop files to upload"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Lock Screen)",
"winKey": "Windows Key",
"superL": "Super+L (Lock Screen)",
"superKey": "Super Key",
"super": "Super",
"cmdL": "Cmd+L (Lock Screen)",
"cmdKey": "Command Key",
"cmd": "Cmd",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Shift",
"win": "Win",
"stickyActive": "{{key}} (latched - click to release)",
"stickyInactive": "{{key}} (click to latch)",
"esc": "Escape",
"tab": "Tab",
"home": "Home",
"end": "End",
"pageUp": "Page Up",
"pageDown": "Page Down",
"arrowUp": "Arrow Up",
"arrowDown": "Arrow Down",
"arrowLeft": "Arrow Left",
"arrowRight": "Arrow Right",
"fnToggle": "Function Keys",
"reconnect": "Reconnect Session",
"collapse": "Collapse toolbar",
"expand": "Expand toolbar",
"dragHandle": "Drag to reposition",
"hide": "Hide for this session. To disable it permanently, open Host Settings → General → Connection Toolbar.",
"zoomIn": "Zoom in",
"zoomOut": "Zoom out",
"resetZoom": "Reset zoom",
"fitToScreen": "Fit to screen",
"switchToTrackpad": "Switch to trackpad mode (drag to move cursor, tap to click)",
"switchToTouch": "Switch to touch mode (tap directly where you want to click)"
},
"disabled": "Remote Desktop is turned off. An admin can turn it on in Settings."
},
"hosts": {
"nativeRdpOpened": "Opened in Windows Remote Desktop",
"nativeRdpFailed": "Failed to open Windows Remote Desktop",
"openNativeRdp": "Open in Windows Remote Desktop",
"guac": {
"toolbar": "Toolbar",
"connection": "Connection",
"authentication": "Authentication",
"storedCredential": "Stored Credential",
"authMethod": "Auth Method",
"authTypeDirect": "Direct",
"authTypeCredential": "Credential",
"authTypeNone": "None",
"authTypeNoneDesc": "No credentials are stored. You'll be prompted for a username and password each time you connect; they are not saved.",
"selectCredential": "Select a credential...",
"connectionSettings": "Connection Settings",
"displaySettings": "Display Settings",
"audioSettings": "Audio Settings",
"rdpPerformance": "RDP Performance",
"deviceRedirection": "Device Redirection",
"session": "Session",
"gateway": "Gateway",
"remoteApp": "RemoteApp",
"clipboard": "Clipboard",
"sessionRecording": "Session Recording",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "VNC Settings",
"terminalSettings": "Terminal Settings",
"rdpPort": "RDP Port",
"username": "Username",
"password": "Password",
"passwordSaved": "Password saved, type to change",
"domain": "Domain",
"securityMode": "Security Mode",
"width": "Width",
"height": "Height",
"dpi": "DPI",
"clientName": "Client Name",
"initialProgram": "Initial Program",
"serverLayout": "Server Layout",
"timezone": "Timezone",
"loadBalanceInfo": "Load Balance Info / Cookie",
"loadBalanceInfoDesc": "RD Connection Broker cookie for RDS farm load balancing (e.g. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacd Proxy",
"guacdHostname": "guacd Host",
"guacdHostnamePlaceholder": "Global default",
"guacdPort": "guacd Port",
"guacdProxyDesc": "Override the global guacd instance for this connection. Leave blank to use the globally configured guacd.",
"gatewayHostname": "Gateway Hostname",
"gatewayPort": "Gateway Port",
"gatewayUsername": "Gateway Username",
"gatewayPassword": "Gateway Password",
"gatewayDomain": "Gateway Domain",
"remoteAppProgram": "RemoteApp Program",
"workingDirectory": "Working Directory",
"arguments": "Arguments",
"normalizeLineEndings": "Normalize Line Endings",
"macAddress": "MAC Address",
"broadcastAddress": "Broadcast Address",
"udpPort": "UDP Port",
"waitTimeS": "Wait Time (s)",
"driveName": "Drive Name",
"drivePath": "Drive Path",
"ignoreCertificate": "Ignore Certificate",
"ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates",
"forceLossless": "Force Lossless",
"forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)",
"disableAudio": "Disable Audio",
"disableAudioDesc": "Mute all audio from the remote session",
"enableAudioInput": "Enable Audio Input (Microphone)",
"enableAudioInputDesc": "Forward local microphone to the remote session",
"wallpaper": "Wallpaper",
"wallpaperDesc": "Show desktop wallpaper (disabling improves performance)",
"theming": "Theming",
"themingDesc": "Enable visual themes and styles",
"fontSmoothing": "Font Smoothing",
"fontSmoothingDesc": "Enable ClearType font rendering",
"fullWindowDrag": "Full Window Drag",
"fullWindowDragDesc": "Show window contents while dragging",
"desktopComposition": "Desktop Composition",
"desktopCompositionDesc": "Enable Aero glass effects",
"menuAnimations": "Menu Animations",
"menuAnimationsDesc": "Enable menu fade and slide animations",
"disableBitmapCaching": "Disable Bitmap Caching",
"disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)",
"disableOffscreenCaching": "Disable Offscreen Caching",
"disableOffscreenCachingDesc": "Turn off offscreen cache",
"disableGlyphCaching": "Disable Glyph Caching",
"disableGlyphCachingDesc": "Turn off glyph cache",
"enableGfx": "Enable GFX",
"enableGfxDesc": "Use RemoteFX graphics pipeline",
"enablePrinting": "Enable Printing",
"enablePrintingDesc": "Redirect local printers to the remote session",
"enableDriveRedirection": "Enable Drive Redirection",
"enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session",
"createDrivePath": "Create Drive Path",
"createDrivePathDesc": "Automatically create the folder if it does not exist",
"disableDownload": "Disable Download",
"disableDownloadDesc": "Prevent downloading files from the remote session",
"disableUpload": "Disable Upload",
"disableUploadDesc": "Prevent uploading files to the remote session",
"enableTouch": "Enable Touch",
"enableTouchDesc": "Enable touch input forwarding",
"consoleSession": "Console Session",
"consoleSessionDesc": "Connect to the console (session 0) instead of a new session",
"sendWolPacket": "Send WOL Packet",
"sendWolPacketDesc": "Send a magic packet to wake this host before connecting",
"disableCopy": "Disable Copy",
"disableCopyDesc": "Prevent copying text from the remote session",
"disablePaste": "Disable Paste",
"disablePasteDesc": "Prevent pasting text into the remote session",
"createPathIfMissing": "Create Path if Missing",
"createPathIfMissingDesc": "Automatically create the recording directory",
"excludeOutput": "Exclude Output",
"excludeOutputDesc": "Do not record screen output (metadata only)",
"excludeMouse": "Exclude Mouse",
"excludeMouseDesc": "Do not record mouse movements",
"includeKeystrokes": "Include Keystrokes",
"includeKeystrokesDesc": "Record raw keystrokes in addition to screen output",
"vncPort": "VNC Port",
"vncPassword": "VNC Password",
"vncUsernameOptional": "Username (optional)",
"vncLeaveBlank": "Leave blank if not required",
"cursorMode": "Cursor Mode",
"swapRedBlue": "Swap Red/Blue",
"swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)",
"readOnly": "Read-only",
"readOnlyDesc": "View the remote screen without sending any input",
"telnetPort": "Telnet Port",
"terminalType": "Terminal Type",
"fontName": "Font Name",
"fontSize": "Font Size",
"colorScheme": "Color Scheme",
"backspaceKey": "Backspace Key",
"colorDepth": "Color Depth",
"resizeMethod": "Resize Method",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "Remote Desktop",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Virtual Network",
"unencryptedShell": "Unencrypted shell",
"remoteDesktopNotes": "Notes",
"userDefaultsNote": "Settings you leave unchanged follow your host defaults.",
"connectionOriginNote": "On the desktop app, RDP, VNC and Telnet use the remote server unless the host is set to This device, which needs a guacd this machine can reach. Set one in the Remote Desktop settings, or per host under guacd Proxy.",
"docsLink": "View docs",
"connectionOriginGuacamoleNote": "RDP, VNC and Telnet use the remote server unless you pick \"This device\", which needs a guacd this machine can reach. Set one in the guacd URL setting, or per host under guacd Proxy."
},
"onboarding": {
"feature_desktop": "Remote desktop",
"feature_desktop_desc": "Open RDP, VNC and Telnet sessions in a tab, same as SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Color Depth",
"description": "Used for RDP hosts that leave it on their default."
},
"resizeMethod": {
"label": "Resize Method",
"description": "How the remote screen follows the window size."
},
"resizeDisplayUpdate": "Display Update",
"resizeReconnect": "Reconnect",
"inherit": "Inherit",
"on": "On",
"off": "Off",
"forceLossless": {
"label": "Force Lossless",
"description": "Force lossless image encoding (higher quality, more bandwidth)"
},
"enableWallpaper": {
"label": "Wallpaper",
"description": "Show desktop wallpaper (disabling improves performance)"
},
"enableFontSmoothing": {
"label": "Font Smoothing",
"description": "Enable ClearType font rendering"
},
"enableDesktopComposition": {
"label": "Desktop Composition",
"description": "Enable Aero glass effects"
},
"disableAudio": {
"label": "Disable Audio",
"description": "Mute all audio from the remote session"
},
"enablePrinting": {
"label": "Enable Printing",
"description": "Redirect local printers to the remote session"
},
"enableDrive": {
"label": "Enable Drive Redirection",
"description": "Map a local folder as a drive in the remote session"
},
"disableCopy": {
"label": "Disable Copy",
"description": "Prevent copying text from the remote session"
},
"disablePaste": {
"label": "Disable Paste",
"description": "Prevent pasting text into the remote session"
},
"group": "Remote Desktop defaults",
"colorDepth16": "16-bit",
"colorDepth24": "24-bit",
"colorDepth32": "32-bit"
},
"admin": {
"enabled": {
"label": "Enable Remote Desktop",
"description": "Allow RDP, VNC and Telnet connections through guacd."
},
"guacdUrl": {
"label": "guacd URL",
"description": "Where guacd listens, as host:port. The GUACD_URL, GUACD_HOST and GUACD_PORT environment variables override this.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP port"
},
"vncPort": {
"label": "VNC port"
},
"telnetPort": {
"label": "Telnet port"
},
"rdpSecurity": {
"label": "RDP security mode"
},
"rdpIgnoreCert": {
"label": "Ignore certificate errors"
},
"guacamoleConfig": {
"label": "guacd settings"
},
"enableToolbar": {
"label": "Remote desktop toolbar",
"description": "Show the toolbar in RDP, VNC and Telnet sessions."
},
"displayGroup": "Display",
"displayDefaultsTab": "Remote Desktop"
}
},
"palette": {
"connectRdp": "Connect with RDP",
"connectVnc": "Connect with VNC",
"connectTelnet": "Connect with Telnet"
},
"permissions": {
"sessions": {
"title": "Share remote desktop sessions",
"description": "Let session sharing and collab rooms show this user's RDP, VNC and Telnet sessions to others."
}
},
"errors": {
"remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "{{type}} oturumuna bağlanılıyor...",
"checkingGuacd": "Checking remote desktop service...",
"requestingToken": "Requesting {{type}} connection token...",
"openingSession": "Opening {{type}} session...",
"negotiating": "Negotiating {{type}} handshake...",
"sessionReady": "{{type}} session ready",
"connectionError": "Bağlantı hatası",
"connectionFailed": "Bağlantı başarısız",
"failedToConnect": "Bağlantı belirteci alınamadı",
"hostNotFound": "Sunucu bulunamadı",
"noHostSelected": "Sunucu seçilmedi",
"reconnect": "Yeniden Bağlan",
"retry": "Yeniden dene",
"guacdUnavailable": "Uzak masaüstü hizmeti (guacd) kullanılamıyor. Lütfen guacd'nin çalıştığından, erişilebilir olduğundan ve yönetici ayarlarında düzgün yapılandırıldığından emin olun.",
"credentialPromptTitle": "Enter RDP Credentials",
"credentialPromptDescription": "This host is set to prompt for credentials on connect. They are used for this session only and are not saved.",
"connect": "Bağlan",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Drive Files",
"parent": "Parent directory",
"refresh": "Yenile",
"empty": "Bu klasör boş",
"listFailed": "Failed to read this folder",
"download": "İndir",
"downloadFailed": "Failed to download {{name}}",
"upload": "Upload files",
"uploading": "{{name}} yükleniyor...",
"uploaded": "Uploaded {{name}}",
"uploadFailed": "Failed to upload {{name}}",
"uploadDisabled": "Uploads are disabled for this connection",
"driveUnavailable": "Enable RDP drive redirection before uploading files",
"driveNotWritable": "guacd cannot write to the drive folder. Check GUACD_DRIVE_PATH (or the host's Drive Path) and its permissions.",
"dropToUpload": "Drop files to upload"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Ekranı Kilitle)",
"winKey": "Windows Tuşu",
"superL": "Super+L (Lock Screen)",
"superKey": "Super Key",
"super": "Super",
"cmdL": "Cmd+L (Lock Screen)",
"cmdKey": "Command Key",
"cmd": "Cmd",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Shift",
"win": "Win",
"stickyActive": "{{key}} (sabitlenmiş - bırakmak için tıklayın)",
"stickyInactive": "{{key}} (sabitlemek için tıklayın)",
"esc": "Escape",
"tab": "Tab",
"home": "Home",
"end": "End",
"pageUp": "Page Up",
"pageDown": "Page Down",
"arrowUp": "Yukarı Ok",
"arrowDown": "Aşağı Ok",
"arrowLeft": "Sol Ok",
"arrowRight": "Sağ Ok",
"fnToggle": "Fonksiyon Tuşları",
"reconnect": "Oturumu Yeniden Bağla",
"collapse": "Araç çubuğunu daralt",
"expand": "Araç çubuğunu genişlet",
"dragHandle": "Yeniden konumlandırmak için sürükle",
"hide": "Hide for this session. To disable it permanently, open Host Settings → General → Connection Toolbar.",
"zoomIn": "Zoom in",
"zoomOut": "Zoom out",
"resetZoom": "Reset zoom",
"fitToScreen": "Fit to screen",
"switchToTrackpad": "Switch to trackpad mode (drag to move cursor, tap to click)",
"switchToTouch": "Switch to touch mode (tap directly where you want to click)"
},
"disabled": "Remote Desktop is turned off. An admin can turn it on in Settings."
},
"hosts": {
"nativeRdpOpened": "Opened in Windows Remote Desktop",
"nativeRdpFailed": "Failed to open Windows Remote Desktop",
"openNativeRdp": "Open in Windows Remote Desktop",
"guac": {
"toolbar": "Toolbar",
"connection": "Bağlantı",
"authentication": "Kimlik doğrulama",
"storedCredential": "Kayıtlı kimlik bilgisi",
"authMethod": "Kimlik doğrulama yöntemi",
"authTypeDirect": "Doğrudan",
"authTypeCredential": "Kimlik bilgisi",
"authTypeNone": "Yok",
"authTypeNoneDesc": "No credentials are stored. You'll be prompted for a username and password each time you connect; they are not saved.",
"selectCredential": "Bir kimlik bilgisi seçin...",
"connectionSettings": "Bağlantı ayarları",
"displaySettings": "Görüntü ayarları",
"audioSettings": "Ses ayarları",
"rdpPerformance": "RDP performansı",
"deviceRedirection": "Cihaz yönlendirme",
"session": "Oturum",
"gateway": "Ağ geçidi",
"remoteApp": "RemoteApp",
"clipboard": "Pano",
"sessionRecording": "Oturum kaydı",
"wakeOnLan": "LAN üzerinden uyandırma",
"vncSettings": "VNC ayarları",
"terminalSettings": "Terminal ayarları",
"rdpPort": "RDP Portu",
"username": "Kullanıcı Adı",
"password": "Parola",
"passwordSaved": "Password saved, type to change",
"domain": "Etki Alanı",
"securityMode": "Güvenlik Modu",
"width": "Genişlik",
"height": "Yükseklik",
"dpi": "DPI",
"clientName": "İstemci Adı",
"initialProgram": "Başlangıç Programı",
"serverLayout": "Sunucu Düzeni",
"timezone": "Saat Dilimi",
"loadBalanceInfo": "Yük Dengeleme Bilgisi / Çerez",
"loadBalanceInfoDesc": "RDS grubu yük dengelemesi için RD Bağlantı Aracısı çerezi (ör. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacd Proxy",
"guacdHostname": "guacd Sunucusu",
"guacdHostnamePlaceholder": "Genel varsayılan",
"guacdPort": "guacd Portu",
"guacdProxyDesc": "Bu bağlantı için genel guacd örneğini geçersiz kıl. Genel olarak yapılandırılmış guacd'yi kullanmak için boş bırak.",
"gatewayHostname": "Ağ Geçidi Ana Bilgisayar Adı",
"gatewayPort": "Ağ Geçidi Portu",
"gatewayUsername": "Ağ Geçidi Kullanıcı Adı",
"gatewayPassword": "Ağ Geçidi Parolası",
"gatewayDomain": "Ağ Geçidi Etki Alanı",
"remoteAppProgram": "RemoteApp Programı",
"workingDirectory": "Çalışma Dizini",
"arguments": "Argümanlar",
"normalizeLineEndings": "Satır Sonlarını Normalleştir",
"macAddress": "MAC Adresi",
"broadcastAddress": "Yayın Adresi",
"udpPort": "UDP Portu",
"waitTimeS": "Bekleme Süresi (sn)",
"driveName": "Sürücü Adı",
"drivePath": "Sürücü Yolu",
"ignoreCertificate": "Sertifikayı Yoksay",
"ignoreCertificateDesc": "Kendinden imzalı sertifikalara sahip sunuculara bağlantılara izin ver",
"forceLossless": "Kayıpsız Zorla",
"forceLosslessDesc": "Kayıpsız görüntü kodlamasını zorla (daha yüksek kalite, daha fazla bant genişliği)",
"disableAudio": "Sesi Devre Dışı Bırak",
"disableAudioDesc": "Uzak oturumdaki tüm sesleri kapat",
"enableAudioInput": "Ses Girişini Etkinleştir (Mikrofon)",
"enableAudioInputDesc": "Yerel mikrofonu uzak oturuma yönlendir",
"wallpaper": "Duvar Kağıdı",
"wallpaperDesc": "Masaüstü duvar kağıdını göster (devre dışı bırakmak performansı artırır)",
"theming": "Tema",
"themingDesc": "Görsel temaları ve stilleri etkinleştir",
"fontSmoothing": "Yazı Tipi Yumuşatma",
"fontSmoothingDesc": "ClearType yazı tipi işlemeyi etkinleştir",
"fullWindowDrag": "Tam Pencere Sürükleme",
"fullWindowDragDesc": "Sürüklerken pencere içeriğini göster",
"desktopComposition": "Masaüstü Birleştirme",
"desktopCompositionDesc": "Aero cam efektlerini etkinleştir",
"menuAnimations": "Menü Animasyonları",
"menuAnimationsDesc": "Menü solma ve kayma animasyonlarını etkinleştir",
"disableBitmapCaching": "Bitmap Önbelleğini Devre Dışı Bırak",
"disableBitmapCachingDesc": "Bitmap önbelleğini kapat (grafik bozulmalarına yardımcı olabilir)",
"disableOffscreenCaching": "Ekran Dışı Önbelleğini Devre Dışı Bırak",
"disableOffscreenCachingDesc": "Ekran dışı önbelleğini kapat",
"disableGlyphCaching": "Glif Önbelleğini Devre Dışı Bırak",
"disableGlyphCachingDesc": "Glif önbelleğini kapat",
"enableGfx": "GFX'i Etkinleştir",
"enableGfxDesc": "RemoteFX grafik işlem hattını kullan",
"enablePrinting": "Yazdırmayı Etkinleştir",
"enablePrintingDesc": "Yerel yazıcıları uzak oturuma yönlendir",
"enableDriveRedirection": "Sürücü Yönlendirmesini Etkinleştir",
"enableDriveRedirectionDesc": "Yerel bir klasörü uzak oturumda sürücü olarak eşleştir",
"createDrivePath": "Sürücü Yolu Oluştur",
"createDrivePathDesc": "Klasör mevcut değilse otomatik oluştur",
"disableDownload": "İndirmeyi Devre Dışı Bırak",
"disableDownloadDesc": "Uzak oturumdan dosya indirmeyi engelle",
"disableUpload": "Yüklemeyi Devre Dışı Bırak",
"disableUploadDesc": "Uzak oturuma dosya yüklemeyi engelle",
"enableTouch": "Dokunmayı Etkinleştir",
"enableTouchDesc": "Dokunma girişi iletimini etkinleştir",
"consoleSession": "Konsol Oturumu",
"consoleSessionDesc": "Yeni oturum yerine konsola (oturum 0) bağlan",
"sendWolPacket": "WOL Paketi Gönder",
"sendWolPacketDesc": "Bağlanmadan önce bu sunucuyu uyandırmak için sihirli paket gönder",
"disableCopy": "Kopyalamayı Devre Dışı Bırak",
"disableCopyDesc": "Uzak oturumdan metin kopyalamayı engelle",
"disablePaste": "Yapıştırmayı Devre Dışı Bırak",
"disablePasteDesc": "Uzak oturuma metin yapıştırmayı engelle",
"createPathIfMissing": "Yol Yoksa Oluştur",
"createPathIfMissingDesc": "Kayıt dizinini otomatik oluştur",
"excludeOutput": "Çıktıyı Hariç Tut",
"excludeOutputDesc": "Ekran çıktısını kaydetme (yalnızca meta veri)",
"excludeMouse": "Fareyi Hariç Tut",
"excludeMouseDesc": "Fare hareketlerini kaydetme",
"includeKeystrokes": "Tuş Vuruşlarını Dahil Et",
"includeKeystrokesDesc": "Ekran çıktısına ek olarak ham tuş vuruşlarını kaydet",
"vncPort": "VNC Portu",
"vncPassword": "VNC Parolası",
"vncUsernameOptional": "Kullanıcı adı (isteğe bağlı)",
"vncLeaveBlank": "Gerekli değilse boş bırakın",
"cursorMode": "İmleç Modu",
"swapRedBlue": "Kırmızı ve Maviyi Değiştir",
"swapRedBlueDesc": "Kırmızı ve mavi renk kanallarını değiştir (bazı renk sorunlarını giderir)",
"readOnly": "Salt okunur",
"readOnlyDesc": "Hiçbir giriş göndermeden uzak ekranı görüntüle",
"telnetPort": "Telnet Portu",
"terminalType": "Terminal Türü",
"fontName": "Yazı Tipi Adı",
"fontSize": "Yazı Tipi Boyutu",
"colorScheme": "Renk Şeması",
"backspaceKey": "Geri Silme Tuşu",
"colorDepth": "Renk Derinliği",
"resizeMethod": "Yeniden Boyutlandırma Yöntemi",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "Uzak Masaüstü",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Sanal Ağ",
"unencryptedShell": "Şifrelenmemiş Shell",
"remoteDesktopNotes": "Notlar",
"userDefaultsNote": "Settings you leave unchanged follow your host defaults.",
"connectionOriginNote": "On the desktop app, RDP, VNC and Telnet use the remote server unless the host is set to This device, which needs a guacd this machine can reach. Set one in the Remote Desktop settings, or per host under guacd Proxy.",
"docsLink": "Dokümanları görüntüle",
"connectionOriginGuacamoleNote": "RDP, VNC and Telnet use the remote server unless you pick \"This device\", which needs a guacd this machine can reach. Set one in the guacd URL setting, or per host under guacd Proxy."
},
"onboarding": {
"feature_desktop": "Remote desktop",
"feature_desktop_desc": "Open RDP, VNC and Telnet sessions in a tab, same as SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Renk Derinliği",
"description": "Used for RDP hosts that leave it on their default."
},
"resizeMethod": {
"label": "Yeniden Boyutlandırma Yöntemi",
"description": "How the remote screen follows the window size."
},
"resizeDisplayUpdate": "Display Update",
"resizeReconnect": "Yeniden Bağlan",
"inherit": "Inherit",
"on": "Açık",
"off": "Kapalı",
"forceLossless": {
"label": "Kayıpsız Zorla",
"description": "Kayıpsız görüntü kodlamasını zorla (daha yüksek kalite, daha fazla bant genişliği)"
},
"enableWallpaper": {
"label": "Duvar Kağıdı",
"description": "Masaüstü duvar kağıdını göster (devre dışı bırakmak performansı artırır)"
},
"enableFontSmoothing": {
"label": "Yazı Tipi Yumuşatma",
"description": "ClearType yazı tipi işlemeyi etkinleştir"
},
"enableDesktopComposition": {
"label": "Masaüstü Birleştirme",
"description": "Aero cam efektlerini etkinleştir"
},
"disableAudio": {
"label": "Sesi Devre Dışı Bırak",
"description": "Uzak oturumdaki tüm sesleri kapat"
},
"enablePrinting": {
"label": "Yazdırmayı Etkinleştir",
"description": "Yerel yazıcıları uzak oturuma yönlendir"
},
"enableDrive": {
"label": "Sürücü Yönlendirmesini Etkinleştir",
"description": "Yerel bir klasörü uzak oturumda sürücü olarak eşleştir"
},
"disableCopy": {
"label": "Kopyalamayı Devre Dışı Bırak",
"description": "Uzak oturumdan metin kopyalamayı engelle"
},
"disablePaste": {
"label": "Yapıştırmayı Devre Dışı Bırak",
"description": "Uzak oturuma metin yapıştırmayı engelle"
},
"group": "Remote Desktop defaults",
"colorDepth16": "16-bit",
"colorDepth24": "24-bit",
"colorDepth32": "32-bit"
},
"admin": {
"enabled": {
"label": "Enable Remote Desktop",
"description": "Allow RDP, VNC and Telnet connections through guacd."
},
"guacdUrl": {
"label": "guacd URL'si",
"description": "Where guacd listens, as host:port. The GUACD_URL, GUACD_HOST and GUACD_PORT environment variables override this.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP port"
},
"vncPort": {
"label": "VNC port"
},
"telnetPort": {
"label": "Telnet port"
},
"rdpSecurity": {
"label": "RDP security mode"
},
"rdpIgnoreCert": {
"label": "Ignore certificate errors"
},
"guacamoleConfig": {
"label": "guacd settings"
},
"enableToolbar": {
"label": "Remote desktop toolbar",
"description": "Show the toolbar in RDP, VNC and Telnet sessions."
},
"displayGroup": "Display",
"displayDefaultsTab": "Uzak Masaüstü"
}
},
"palette": {
"connectRdp": "Connect with RDP",
"connectVnc": "Connect with VNC",
"connectTelnet": "Connect with Telnet"
},
"permissions": {
"sessions": {
"title": "Share remote desktop sessions",
"description": "Let session sharing and collab rooms show this user's RDP, VNC and Telnet sessions to others."
}
},
"errors": {
"remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Connecting to {{type}} session...",
"checkingGuacd": "Checking remote desktop service...",
"requestingToken": "Requesting {{type}} connection token...",
"openingSession": "Opening {{type}} session...",
"negotiating": "Negotiating {{type}} handshake...",
"sessionReady": "{{type}} session ready",
"connectionError": "Connection error",
"connectionFailed": "Connection failed",
"failedToConnect": "Failed to get connection token",
"hostNotFound": "Host not found",
"noHostSelected": "No host selected",
"reconnect": "Reconnect",
"retry": "Retry",
"guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.",
"credentialPromptTitle": "Enter RDP Credentials",
"credentialPromptDescription": "This host is set to prompt for credentials on connect. They are used for this session only and are not saved.",
"connect": "Connect",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Drive Files",
"parent": "Parent directory",
"refresh": "Refresh",
"empty": "This folder is empty",
"listFailed": "Failed to read this folder",
"download": "Download",
"downloadFailed": "Failed to download {{name}}",
"upload": "Upload files",
"uploading": "Uploading {{name}}...",
"uploaded": "Uploaded {{name}}",
"uploadFailed": "Failed to upload {{name}}",
"uploadDisabled": "Uploads are disabled for this connection",
"driveUnavailable": "Enable RDP drive redirection before uploading files",
"driveNotWritable": "guacd cannot write to the drive folder. Check GUACD_DRIVE_PATH (or the host's Drive Path) and its permissions.",
"dropToUpload": "Drop files to upload"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Lock Screen)",
"winKey": "Windows Key",
"superL": "Super+L (Lock Screen)",
"superKey": "Super Key",
"super": "Super",
"cmdL": "Cmd+L (Lock Screen)",
"cmdKey": "Command Key",
"cmd": "Cmd",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Shift",
"win": "Win",
"stickyActive": "{{key}} (latched - click to release)",
"stickyInactive": "{{key}} (click to latch)",
"esc": "Escape",
"tab": "Tab",
"home": "Home",
"end": "End",
"pageUp": "Page Up",
"pageDown": "Page Down",
"arrowUp": "Arrow Up",
"arrowDown": "Arrow Down",
"arrowLeft": "Arrow Left",
"arrowRight": "Arrow Right",
"fnToggle": "Function Keys",
"reconnect": "Reconnect Session",
"collapse": "Collapse toolbar",
"expand": "Expand toolbar",
"dragHandle": "Drag to reposition",
"hide": "Hide for this session. To disable it permanently, open Host Settings → General → Connection Toolbar.",
"zoomIn": "Zoom in",
"zoomOut": "Zoom out",
"resetZoom": "Reset zoom",
"fitToScreen": "Fit to screen",
"switchToTrackpad": "Switch to trackpad mode (drag to move cursor, tap to click)",
"switchToTouch": "Switch to touch mode (tap directly where you want to click)"
},
"disabled": "Remote Desktop is turned off. An admin can turn it on in Settings."
},
"hosts": {
"nativeRdpOpened": "Opened in Windows Remote Desktop",
"nativeRdpFailed": "Failed to open Windows Remote Desktop",
"openNativeRdp": "Open in Windows Remote Desktop",
"guac": {
"toolbar": "Toolbar",
"connection": "Connection",
"authentication": "Authentication",
"storedCredential": "Stored Credential",
"authMethod": "Auth Method",
"authTypeDirect": "Direct",
"authTypeCredential": "Credential",
"authTypeNone": "None",
"authTypeNoneDesc": "No credentials are stored. You'll be prompted for a username and password each time you connect; they are not saved.",
"selectCredential": "Select a credential...",
"connectionSettings": "Connection Settings",
"displaySettings": "Display Settings",
"audioSettings": "Audio Settings",
"rdpPerformance": "RDP Performance",
"deviceRedirection": "Device Redirection",
"session": "Session",
"gateway": "Gateway",
"remoteApp": "RemoteApp",
"clipboard": "Clipboard",
"sessionRecording": "Session Recording",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "VNC Settings",
"terminalSettings": "Terminal Settings",
"rdpPort": "RDP Port",
"username": "Username",
"password": "Password",
"passwordSaved": "Password saved, type to change",
"domain": "Domain",
"securityMode": "Security Mode",
"width": "Width",
"height": "Height",
"dpi": "DPI",
"clientName": "Client Name",
"initialProgram": "Initial Program",
"serverLayout": "Server Layout",
"timezone": "Timezone",
"loadBalanceInfo": "Load Balance Info / Cookie",
"loadBalanceInfoDesc": "RD Connection Broker cookie for RDS farm load balancing (e.g. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacd Proxy",
"guacdHostname": "guacd Host",
"guacdHostnamePlaceholder": "Global default",
"guacdPort": "guacd Port",
"guacdProxyDesc": "Override the global guacd instance for this connection. Leave blank to use the globally configured guacd.",
"gatewayHostname": "Gateway Hostname",
"gatewayPort": "Gateway Port",
"gatewayUsername": "Gateway Username",
"gatewayPassword": "Gateway Password",
"gatewayDomain": "Gateway Domain",
"remoteAppProgram": "RemoteApp Program",
"workingDirectory": "Working Directory",
"arguments": "Arguments",
"normalizeLineEndings": "Normalize Line Endings",
"macAddress": "MAC Address",
"broadcastAddress": "Broadcast Address",
"udpPort": "UDP Port",
"waitTimeS": "Wait Time (s)",
"driveName": "Drive Name",
"drivePath": "Drive Path",
"ignoreCertificate": "Ignore Certificate",
"ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates",
"forceLossless": "Force Lossless",
"forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)",
"disableAudio": "Disable Audio",
"disableAudioDesc": "Mute all audio from the remote session",
"enableAudioInput": "Enable Audio Input (Microphone)",
"enableAudioInputDesc": "Forward local microphone to the remote session",
"wallpaper": "Wallpaper",
"wallpaperDesc": "Show desktop wallpaper (disabling improves performance)",
"theming": "Theming",
"themingDesc": "Enable visual themes and styles",
"fontSmoothing": "Font Smoothing",
"fontSmoothingDesc": "Enable ClearType font rendering",
"fullWindowDrag": "Full Window Drag",
"fullWindowDragDesc": "Show window contents while dragging",
"desktopComposition": "Desktop Composition",
"desktopCompositionDesc": "Enable Aero glass effects",
"menuAnimations": "Menu Animations",
"menuAnimationsDesc": "Enable menu fade and slide animations",
"disableBitmapCaching": "Disable Bitmap Caching",
"disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)",
"disableOffscreenCaching": "Disable Offscreen Caching",
"disableOffscreenCachingDesc": "Turn off offscreen cache",
"disableGlyphCaching": "Disable Glyph Caching",
"disableGlyphCachingDesc": "Turn off glyph cache",
"enableGfx": "Enable GFX",
"enableGfxDesc": "Use RemoteFX graphics pipeline",
"enablePrinting": "Enable Printing",
"enablePrintingDesc": "Redirect local printers to the remote session",
"enableDriveRedirection": "Enable Drive Redirection",
"enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session",
"createDrivePath": "Create Drive Path",
"createDrivePathDesc": "Automatically create the folder if it does not exist",
"disableDownload": "Disable Download",
"disableDownloadDesc": "Prevent downloading files from the remote session",
"disableUpload": "Disable Upload",
"disableUploadDesc": "Prevent uploading files to the remote session",
"enableTouch": "Enable Touch",
"enableTouchDesc": "Enable touch input forwarding",
"consoleSession": "Console Session",
"consoleSessionDesc": "Connect to the console (session 0) instead of a new session",
"sendWolPacket": "Send WOL Packet",
"sendWolPacketDesc": "Send a magic packet to wake this host before connecting",
"disableCopy": "Disable Copy",
"disableCopyDesc": "Prevent copying text from the remote session",
"disablePaste": "Disable Paste",
"disablePasteDesc": "Prevent pasting text into the remote session",
"createPathIfMissing": "Create Path if Missing",
"createPathIfMissingDesc": "Automatically create the recording directory",
"excludeOutput": "Exclude Output",
"excludeOutputDesc": "Do not record screen output (metadata only)",
"excludeMouse": "Exclude Mouse",
"excludeMouseDesc": "Do not record mouse movements",
"includeKeystrokes": "Include Keystrokes",
"includeKeystrokesDesc": "Record raw keystrokes in addition to screen output",
"vncPort": "VNC Port",
"vncPassword": "VNC Password",
"vncUsernameOptional": "Username (optional)",
"vncLeaveBlank": "Leave blank if not required",
"cursorMode": "Cursor Mode",
"swapRedBlue": "Swap Red/Blue",
"swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)",
"readOnly": "Read-only",
"readOnlyDesc": "View the remote screen without sending any input",
"telnetPort": "Telnet Port",
"terminalType": "Terminal Type",
"fontName": "Font Name",
"fontSize": "Font Size",
"colorScheme": "Color Scheme",
"backspaceKey": "Backspace Key",
"colorDepth": "Color Depth",
"resizeMethod": "Resize Method",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "Remote Desktop",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Virtual Network",
"unencryptedShell": "Unencrypted shell",
"remoteDesktopNotes": "Notes",
"userDefaultsNote": "Settings you leave unchanged follow your host defaults.",
"connectionOriginNote": "On the desktop app, RDP, VNC and Telnet use the remote server unless the host is set to This device, which needs a guacd this machine can reach. Set one in the Remote Desktop settings, or per host under guacd Proxy.",
"docsLink": "View docs",
"connectionOriginGuacamoleNote": "RDP, VNC and Telnet use the remote server unless you pick \"This device\", which needs a guacd this machine can reach. Set one in the guacd URL setting, or per host under guacd Proxy."
},
"onboarding": {
"feature_desktop": "Remote desktop",
"feature_desktop_desc": "Open RDP, VNC and Telnet sessions in a tab, same as SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Color Depth",
"description": "Used for RDP hosts that leave it on their default."
},
"resizeMethod": {
"label": "Resize Method",
"description": "How the remote screen follows the window size."
},
"resizeDisplayUpdate": "Display Update",
"resizeReconnect": "Reconnect",
"inherit": "Inherit",
"on": "On",
"off": "Off",
"forceLossless": {
"label": "Force Lossless",
"description": "Force lossless image encoding (higher quality, more bandwidth)"
},
"enableWallpaper": {
"label": "Wallpaper",
"description": "Show desktop wallpaper (disabling improves performance)"
},
"enableFontSmoothing": {
"label": "Font Smoothing",
"description": "Enable ClearType font rendering"
},
"enableDesktopComposition": {
"label": "Desktop Composition",
"description": "Enable Aero glass effects"
},
"disableAudio": {
"label": "Disable Audio",
"description": "Mute all audio from the remote session"
},
"enablePrinting": {
"label": "Enable Printing",
"description": "Redirect local printers to the remote session"
},
"enableDrive": {
"label": "Enable Drive Redirection",
"description": "Map a local folder as a drive in the remote session"
},
"disableCopy": {
"label": "Disable Copy",
"description": "Prevent copying text from the remote session"
},
"disablePaste": {
"label": "Disable Paste",
"description": "Prevent pasting text into the remote session"
},
"group": "Remote Desktop defaults",
"colorDepth16": "16-bit",
"colorDepth24": "24-bit",
"colorDepth32": "32-bit"
},
"admin": {
"enabled": {
"label": "Enable Remote Desktop",
"description": "Allow RDP, VNC and Telnet connections through guacd."
},
"guacdUrl": {
"label": "guacd URL",
"description": "Where guacd listens, as host:port. The GUACD_URL, GUACD_HOST and GUACD_PORT environment variables override this.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP port"
},
"vncPort": {
"label": "VNC port"
},
"telnetPort": {
"label": "Telnet port"
},
"rdpSecurity": {
"label": "RDP security mode"
},
"rdpIgnoreCert": {
"label": "Ignore certificate errors"
},
"guacamoleConfig": {
"label": "guacd settings"
},
"enableToolbar": {
"label": "Remote desktop toolbar",
"description": "Show the toolbar in RDP, VNC and Telnet sessions."
},
"displayGroup": "Display",
"displayDefaultsTab": "Remote Desktop"
}
},
"palette": {
"connectRdp": "Connect with RDP",
"connectVnc": "Connect with VNC",
"connectTelnet": "Connect with Telnet"
},
"permissions": {
"sessions": {
"title": "Share remote desktop sessions",
"description": "Let session sharing and collab rooms show this user's RDP, VNC and Telnet sessions to others."
}
},
"errors": {
"remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "Đang kết nối đến phiên {{type}}...",
"checkingGuacd": "Checking remote desktop service...",
"requestingToken": "Requesting {{type}} connection token...",
"openingSession": "Opening {{type}} session...",
"negotiating": "Negotiating {{type}} handshake...",
"sessionReady": "{{type}} session ready",
"connectionError": "Lỗi kết nối",
"connectionFailed": "Kết nối thất bại",
"failedToConnect": "Không lấy được mã token kết nối",
"hostNotFound": "Không tìm thấy máy chủ",
"noHostSelected": "Chưa chọn máy chủ",
"reconnect": "Kết nối lại",
"retry": "Thử lại",
"guacdUnavailable": "Dịch vụ máy tính từ xa (guacd) không khả dụng. Vui lòng đảm bảo guacd đang chạy, có thể truy cập và được cấu hình đúng trong cài đặt quản trị.",
"credentialPromptTitle": "Enter RDP Credentials",
"credentialPromptDescription": "This host is set to prompt for credentials on connect. They are used for this session only and are not saved.",
"connect": "Kết nối",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Drive Files",
"parent": "Parent directory",
"refresh": "Làm mới",
"empty": "Thư mục này trống",
"listFailed": "Failed to read this folder",
"download": "Tải xuống",
"downloadFailed": "Failed to download {{name}}",
"upload": "Upload files",
"uploading": "Đang tải lên {{name}}...",
"uploaded": "Uploaded {{name}}",
"uploadFailed": "Failed to upload {{name}}",
"uploadDisabled": "Uploads are disabled for this connection",
"driveUnavailable": "Enable RDP drive redirection before uploading files",
"driveNotWritable": "guacd cannot write to the drive folder. Check GUACD_DRIVE_PATH (or the host's Drive Path) and its permissions.",
"dropToUpload": "Drop files to upload"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Khóa màn hình)",
"winKey": "Phím Windows",
"superL": "Super+L (Lock Screen)",
"superKey": "Super Key",
"super": "Super",
"cmdL": "Cmd+L (Lock Screen)",
"cmdKey": "Command Key",
"cmd": "Cmd",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Shift",
"win": "Win",
"stickyActive": "{{key}} (đang giữ - nhấn để nhả)",
"stickyInactive": "{{key}} (nhấn để giữ)",
"esc": "Escape",
"tab": "Tab",
"home": "Home",
"end": "End",
"pageUp": "Page Up",
"pageDown": "Page Down",
"arrowUp": "Mũi tên lên",
"arrowDown": "Mũi tên xuống",
"arrowLeft": "Mũi tên trái",
"arrowRight": "Mũi tên phải",
"fnToggle": "Phím chức năng",
"reconnect": "Kết nối lại phiên",
"collapse": "Thu gọn thanh công cụ",
"expand": "Mở rộng thanh công cụ",
"dragHandle": "Kéo để di chuyển",
"hide": "Hide for this session. To disable it permanently, open Host Settings → General → Connection Toolbar.",
"zoomIn": "Zoom in",
"zoomOut": "Zoom out",
"resetZoom": "Reset zoom",
"fitToScreen": "Fit to screen",
"switchToTrackpad": "Switch to trackpad mode (drag to move cursor, tap to click)",
"switchToTouch": "Switch to touch mode (tap directly where you want to click)"
},
"disabled": "Remote Desktop is turned off. An admin can turn it on in Settings."
},
"hosts": {
"nativeRdpOpened": "Opened in Windows Remote Desktop",
"nativeRdpFailed": "Failed to open Windows Remote Desktop",
"openNativeRdp": "Open in Windows Remote Desktop",
"guac": {
"toolbar": "Toolbar",
"connection": "Kết nối",
"authentication": "Xác thực",
"storedCredential": "Thông tin xác thực đã lưu",
"authMethod": "Phương thức xác thực",
"authTypeDirect": "Trực tiếp",
"authTypeCredential": "Thông tin xác thực",
"authTypeNone": "Không chọn",
"authTypeNoneDesc": "No credentials are stored. You'll be prompted for a username and password each time you connect; they are not saved.",
"selectCredential": "Chọn thông tin xác thực...",
"connectionSettings": "Cài đặt kết nối",
"displaySettings": "Cài đặt hiển thị",
"audioSettings": "Cài đặt âm thanh",
"rdpPerformance": "Hiệu suất RDP",
"deviceRedirection": "Chuyển hướng thiết bị",
"session": "Phiên làm việc",
"gateway": "Gateway",
"remoteApp": "RemoteApp",
"clipboard": "Clipboard",
"sessionRecording": "Ghi phiên",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "Cài đặt VNC",
"terminalSettings": "Cài đặt Terminal",
"rdpPort": "Cổng RDP",
"username": "Tên người dùng",
"password": "Mật khẩu",
"passwordSaved": "Password saved, type to change",
"domain": "Miền",
"securityMode": "Chế độ bảo mật",
"width": "Chiều rộng",
"height": "Chiều cao",
"dpi": "DPI",
"clientName": "Tên máy khách",
"initialProgram": "Chương trình ban đầu",
"serverLayout": "Bố cục máy chủ",
"timezone": "Múi giờ",
"loadBalanceInfo": "Thông tin cân bằng tải / Cookie",
"loadBalanceInfoDesc": "Cookie RD Connection Broker dùng cho cân bằng tải cụm RDS (vd: tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "Proxy guacd",
"guacdHostname": "Máy chủ guacd",
"guacdHostnamePlaceholder": "Mặc định toàn cục",
"guacdPort": "Cổng guacd",
"guacdProxyDesc": "Ghi đè phiên bản guacd toàn cục cho kết nối này. Để trống để sử dụng guacd được cấu hình toàn cục.",
"gatewayHostname": "Tên máy chủ gateway",
"gatewayPort": "Cổng gateway",
"gatewayUsername": "Tên người dùng gateway",
"gatewayPassword": "Mật khẩu gateway",
"gatewayDomain": "Miền gateway",
"remoteAppProgram": "Chương trình RemoteApp",
"workingDirectory": "Thư mục làm việc",
"arguments": "Tham số",
"normalizeLineEndings": "Chuẩn hóa kết thúc dòng",
"macAddress": "Địa chỉ MAC",
"broadcastAddress": "Địa chỉ broadcast",
"udpPort": "Cổng UDP",
"waitTimeS": "Thời gian chờ (s)",
"driveName": "Tên ổ đĩa",
"drivePath": "Đường dẫn ổ đĩa",
"ignoreCertificate": "Bỏ qua chứng chỉ",
"ignoreCertificateDesc": "Cho phép kết nối đến máy chủ có chứng chỉ tự ký",
"forceLossless": "Ép buộc Lossless",
"forceLosslessDesc": "Ép buộc mã hóa ảnh không mất dữ liệu (chất lượng cao hơn, tốn băng thông)",
"disableAudio": "Tắt âm thanh",
"disableAudioDesc": "Tắt tất cả âm thanh từ phiên từ xa",
"enableAudioInput": "Bật đầu vào âm thanh (micro)",
"enableAudioInputDesc": "Chuyển tiếp micro cục bộ đến phiên từ xa",
"wallpaper": "Hình nền",
"wallpaperDesc": "Hiển thị hình nền desktop (tắt để cải thiện hiệu suất)",
"theming": "Chủ đề",
"themingDesc": "Bật chủ đề và kiểu dáng trực quan",
"fontSmoothing": "Làm mịn phông chữ",
"fontSmoothingDesc": "Bật kết xuất phông chữ ClearType",
"fullWindowDrag": "Kéo toàn bộ cửa sổ",
"fullWindowDragDesc": "Hiển thị nội dung cửa sổ khi kéo",
"desktopComposition": "Tổ hợp màn hình nền",
"desktopCompositionDesc": "Bật hiệu ứng kính Aero",
"menuAnimations": "Hiệu ứng menu",
"menuAnimationsDesc": "Bật hiệu ứng mờ và trượt của menu",
"disableBitmapCaching": "Tắt bộ nhớ đệm bitmap",
"disableBitmapCachingDesc": "Tắt bộ nhớ đệm bitmap (có thể giúp tránh lỗi hiển thị)",
"disableOffscreenCaching": "Tắt bộ nhớ đệm ngoài màn hình",
"disableOffscreenCachingDesc": "Tắt bộ nhớ đệm ngoài màn hình",
"disableGlyphCaching": "Tắt bộ nhớ đệm glyph",
"disableGlyphCachingDesc": "Tắt bộ nhớ đệm glyph",
"enableGfx": "Bật GFX",
"enableGfxDesc": "Sử dụng pipeline đồ họa RemoteFX",
"enablePrinting": "Bật in",
"enablePrintingDesc": "Chuyển hướng máy in cục bộ đến phiên từ xa",
"enableDriveRedirection": "Bật chuyển hướng ổ đĩa",
"enableDriveRedirectionDesc": "Ánh xạ thư mục cục bộ thành ổ đĩa trong phiên từ xa",
"createDrivePath": "Tạo đường dẫn ổ đĩa",
"createDrivePathDesc": "Tự động tạo thư mục nếu chưa tồn tại",
"disableDownload": "Tắt tải xuống",
"disableDownloadDesc": "Ngăn tải file từ phiên từ xa",
"disableUpload": "Tắt tải lên",
"disableUploadDesc": "Ngăn tải file lên phiên từ xa",
"enableTouch": "Bật cảm ứng",
"enableTouchDesc": "Bật chuyển tiếp đầu vào cảm ứng",
"consoleSession": "Phiên console",
"consoleSessionDesc": "Kết nối tới console (phiên 0) thay vì phiên mới",
"sendWolPacket": "Gửi gói WOL",
"sendWolPacketDesc": "Gửi gói magic để đánh thức máy trước khi kết nối",
"disableCopy": "Tắt sao chép",
"disableCopyDesc": "Ngăn sao chép văn bản từ phiên từ xa",
"disablePaste": "Tắt dán",
"disablePasteDesc": "Ngăn dán văn bản vào phiên từ xa",
"createPathIfMissing": "Tạo đường dẫn nếu thiếu",
"createPathIfMissingDesc": "Tự động tạo thư mục ghi hình",
"excludeOutput": "Loại trừ đầu ra",
"excludeOutputDesc": "Không ghi hình màn hình (chỉ metadata)",
"excludeMouse": "Loại trừ chuột",
"excludeMouseDesc": "Không ghi lại chuyển động chuột",
"includeKeystrokes": "Bao gồm thao tác phím",
"includeKeystrokesDesc": "Ghi lại thao tác phím thô cùng với đầu ra màn hình",
"vncPort": "Cổng VNC",
"vncPassword": "Mật khẩu VNC",
"vncUsernameOptional": "Tên người dùng (tùy chọn)",
"vncLeaveBlank": "Để trống nếu không cần",
"cursorMode": "Chế độ con trỏ",
"swapRedBlue": "Hoán đổi Đỏ/Xanh",
"swapRedBlueDesc": "Hoán đổi kênh màu đỏ và xanh (khắc phục một số lỗi màu)",
"readOnly": "Chỉ đọc",
"readOnlyDesc": "Xem màn hình từ xa mà không gửi đầu vào",
"telnetPort": "Cổng Telnet",
"terminalType": "Loại terminal",
"fontName": "Tên phông chữ",
"fontSize": "Cỡ chữ",
"colorScheme": "Bảng màu",
"backspaceKey": "Phím Backspace",
"colorDepth": "Độ sâu màu",
"resizeMethod": "Phương thức thay đổi kích thước",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "Máy tính từ xa",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "Mạng ảo",
"unencryptedShell": "Shell không mã hóa",
"remoteDesktopNotes": "Ghi chú",
"userDefaultsNote": "Settings you leave unchanged follow your host defaults.",
"connectionOriginNote": "On the desktop app, RDP, VNC and Telnet use the remote server unless the host is set to This device, which needs a guacd this machine can reach. Set one in the Remote Desktop settings, or per host under guacd Proxy.",
"docsLink": "Xem tài liệu",
"connectionOriginGuacamoleNote": "RDP, VNC and Telnet use the remote server unless you pick \"This device\", which needs a guacd this machine can reach. Set one in the guacd URL setting, or per host under guacd Proxy."
},
"onboarding": {
"feature_desktop": "Remote desktop",
"feature_desktop_desc": "Open RDP, VNC and Telnet sessions in a tab, same as SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "Độ sâu màu",
"description": "Used for RDP hosts that leave it on their default."
},
"resizeMethod": {
"label": "Phương thức thay đổi kích thước",
"description": "How the remote screen follows the window size."
},
"resizeDisplayUpdate": "Display Update",
"resizeReconnect": "Kết nối lại",
"inherit": "Inherit",
"on": "Bật",
"off": "Tắt",
"forceLossless": {
"label": "Ép buộc Lossless",
"description": "Ép buộc mã hóa ảnh không mất dữ liệu (chất lượng cao hơn, tốn băng thông)"
},
"enableWallpaper": {
"label": "Hình nền",
"description": "Hiển thị hình nền desktop (tắt để cải thiện hiệu suất)"
},
"enableFontSmoothing": {
"label": "Làm mịn phông chữ",
"description": "Bật kết xuất phông chữ ClearType"
},
"enableDesktopComposition": {
"label": "Tổ hợp màn hình nền",
"description": "Bật hiệu ứng kính Aero"
},
"disableAudio": {
"label": "Tắt âm thanh",
"description": "Tắt tất cả âm thanh từ phiên từ xa"
},
"enablePrinting": {
"label": "Bật in",
"description": "Chuyển hướng máy in cục bộ đến phiên từ xa"
},
"enableDrive": {
"label": "Bật chuyển hướng ổ đĩa",
"description": "Ánh xạ thư mục cục bộ thành ổ đĩa trong phiên từ xa"
},
"disableCopy": {
"label": "Tắt sao chép",
"description": "Ngăn sao chép văn bản từ phiên từ xa"
},
"disablePaste": {
"label": "Tắt dán",
"description": "Ngăn dán văn bản vào phiên từ xa"
},
"group": "Remote Desktop defaults",
"colorDepth16": "16-bit",
"colorDepth24": "24-bit",
"colorDepth32": "32-bit"
},
"admin": {
"enabled": {
"label": "Enable Remote Desktop",
"description": "Allow RDP, VNC and Telnet connections through guacd."
},
"guacdUrl": {
"label": "URL guacd",
"description": "Where guacd listens, as host:port. The GUACD_URL, GUACD_HOST and GUACD_PORT environment variables override this.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP port"
},
"vncPort": {
"label": "VNC port"
},
"telnetPort": {
"label": "Telnet port"
},
"rdpSecurity": {
"label": "RDP security mode"
},
"rdpIgnoreCert": {
"label": "Ignore certificate errors"
},
"guacamoleConfig": {
"label": "guacd settings"
},
"enableToolbar": {
"label": "Remote desktop toolbar",
"description": "Show the toolbar in RDP, VNC and Telnet sessions."
},
"displayGroup": "Display",
"displayDefaultsTab": "Máy tính từ xa"
}
},
"palette": {
"connectRdp": "Connect with RDP",
"connectVnc": "Connect with VNC",
"connectTelnet": "Connect with Telnet"
},
"permissions": {
"sessions": {
"title": "Share remote desktop sessions",
"description": "Let session sharing and collab rooms show this user's RDP, VNC and Telnet sessions to others."
}
},
"errors": {
"remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "正在连接到 {{type}} 会话...",
"checkingGuacd": "Checking remote desktop service...",
"requestingToken": "Requesting {{type}} connection token...",
"openingSession": "Opening {{type}} session...",
"negotiating": "Negotiating {{type}} handshake...",
"sessionReady": "{{type}} session ready",
"connectionError": "连接错误",
"connectionFailed": "连接失败",
"failedToConnect": "获取连接令牌失败",
"hostNotFound": "未找到主机",
"noHostSelected": "未选择主机",
"reconnect": "重新连接",
"retry": "重试",
"guacdUnavailable": "远程桌面服务 (guacd) 不可用。请确保 guacd 正在运行并可访问,且在管理设置中正确配置。",
"credentialPromptTitle": "Enter RDP Credentials",
"credentialPromptDescription": "This host is set to prompt for credentials on connect. They are used for this session only and are not saved.",
"connect": "连接",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Drive Files",
"parent": "Parent directory",
"refresh": "刷新",
"empty": "此文件夹为空",
"listFailed": "Failed to read this folder",
"download": "下载",
"downloadFailed": "Failed to download {{name}}",
"upload": "Upload files",
"uploading": "正在上传 {{name}}...",
"uploaded": "Uploaded {{name}}",
"uploadFailed": "Failed to upload {{name}}",
"uploadDisabled": "Uploads are disabled for this connection",
"driveUnavailable": "Enable RDP drive redirection before uploading files",
"driveNotWritable": "guacd cannot write to the drive folder. Check GUACD_DRIVE_PATH (or the host's Drive Path) and its permissions.",
"dropToUpload": "Drop files to upload"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (锁定屏幕)",
"winKey": "Windows 键",
"superL": "Super+L (Lock Screen)",
"superKey": "Super Key",
"super": "Super",
"cmdL": "Cmd+L (Lock Screen)",
"cmdKey": "Command Key",
"cmd": "Cmd",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Shift",
"win": "Win",
"stickyActive": "{{key}}(已锁定 - 点击释放)",
"stickyInactive": "{{key}}(点击锁定)",
"esc": "Escape",
"tab": "Tab",
"home": "Home",
"end": "End",
"pageUp": "Page Up",
"pageDown": "Page Down",
"arrowUp": "Arrow Up",
"arrowDown": "Arrow Down",
"arrowLeft": "Arrow Left",
"arrowRight": "Arrow Right",
"fnToggle": "功能键",
"reconnect": "重新连接会话",
"collapse": "折叠工具栏",
"expand": "展开工具栏",
"dragHandle": "拖动以重新定位",
"hide": "Hide for this session. To disable it permanently, open Host Settings → General → Connection Toolbar.",
"zoomIn": "Zoom in",
"zoomOut": "Zoom out",
"resetZoom": "Reset zoom",
"fitToScreen": "Fit to screen",
"switchToTrackpad": "切换到触控板模式(拖动移动光标,轻点进行点击)",
"switchToTouch": "Switch to touch mode (tap directly where you want to click)"
},
"disabled": "Remote Desktop is turned off. An admin can turn it on in Settings."
},
"hosts": {
"nativeRdpOpened": "Opened in Windows Remote Desktop",
"nativeRdpFailed": "Failed to open Windows Remote Desktop",
"openNativeRdp": "Open in Windows Remote Desktop",
"guac": {
"toolbar": "Toolbar",
"connection": "连接",
"authentication": "认证",
"storedCredential": "已存储凭据",
"authMethod": "认证方法",
"authTypeDirect": "直接",
"authTypeCredential": "凭据",
"authTypeNone": "无",
"authTypeNoneDesc": "No credentials are stored. You'll be prompted for a username and password each time you connect; they are not saved.",
"selectCredential": "选择凭据...",
"connectionSettings": "连接设置",
"displaySettings": "显示设置",
"audioSettings": "音频设置",
"rdpPerformance": "RDP 性能",
"deviceRedirection": "设备重定向",
"session": "会话",
"gateway": "网关",
"remoteApp": "RemoteApp",
"clipboard": "剪贴板",
"sessionRecording": "会话录制",
"wakeOnLan": "网络唤醒",
"vncSettings": "VNC 设置",
"terminalSettings": "终端设置",
"rdpPort": "RDP 端口",
"username": "用户名",
"password": "密码",
"passwordSaved": "Password saved, type to change",
"domain": "域",
"securityMode": "安全模式",
"width": "宽度",
"height": "高度",
"dpi": "DPI",
"clientName": "客户端名称",
"initialProgram": "初始程序",
"serverLayout": "服务器布局",
"timezone": "时区",
"loadBalanceInfo": "负载均衡信息 / Cookie",
"loadBalanceInfoDesc": "RD 连接代理 Cookie,用于 RDS 场负载均衡(例如 tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacd 代理",
"guacdHostname": "guacd 主机",
"guacdHostnamePlaceholder": "全局默认",
"guacdPort": "guacd 端口",
"guacdProxyDesc": "为此连接覆盖全局 guacd 实例。留空则使用全局配置的 guacd。",
"gatewayHostname": "网关主机名",
"gatewayPort": "网关端口",
"gatewayUsername": "网关用户名",
"gatewayPassword": "网关密码",
"gatewayDomain": "网关域",
"remoteAppProgram": "RemoteApp 程序",
"workingDirectory": "工作目录",
"arguments": "参数",
"normalizeLineEndings": "规范化行尾",
"macAddress": "MAC 地址",
"broadcastAddress": "广播地址",
"udpPort": "UDP 端口",
"waitTimeS": "等待时间(秒)",
"driveName": "驱动器名称",
"drivePath": "驱动器路径",
"ignoreCertificate": "忽略证书",
"ignoreCertificateDesc": "允许连接到具有自签名证书的主机",
"forceLossless": "强制无损",
"forceLosslessDesc": "强制无损图像编码(质量更高,占用更多带宽)",
"disableAudio": "禁用音频",
"disableAudioDesc": "将远程会话的所有音频静音",
"enableAudioInput": "启用音频输入(麦克风)",
"enableAudioInputDesc": "将本地麦克风转发到远程会话",
"wallpaper": "壁纸",
"wallpaperDesc": "显示桌面壁纸(禁用可提高性能)",
"theming": "主题",
"themingDesc": "启用视觉主题和样式",
"fontSmoothing": "字体平滑",
"fontSmoothingDesc": "启用 ClearType 字体渲染",
"fullWindowDrag": "全窗口拖动",
"fullWindowDragDesc": "拖动时显示窗口内容",
"desktopComposition": "桌面合成",
"desktopCompositionDesc": "启用 Aero 玻璃效果",
"menuAnimations": "菜单动画",
"menuAnimationsDesc": "启用菜单淡入淡出与滑动动画",
"disableBitmapCaching": "禁用位图缓存",
"disableBitmapCachingDesc": "关闭位图缓存(可能有助于解决显示故障)",
"disableOffscreenCaching": "禁用离屏缓存",
"disableOffscreenCachingDesc": "关闭离屏缓存",
"disableGlyphCaching": "禁用字形缓存",
"disableGlyphCachingDesc": "关闭字形缓存",
"enableGfx": "启用 GFX",
"enableGfxDesc": "使用 RemoteFX 图形管道",
"enablePrinting": "启用打印",
"enablePrintingDesc": "将本地打印机重定向到远程会话",
"enableDriveRedirection": "启用驱动器重定向",
"enableDriveRedirectionDesc": "将本地文件夹映射为远程会话中的驱动器",
"createDrivePath": "创建驱动器路径",
"createDrivePathDesc": "如果文件夹不存在,则自动创建",
"disableDownload": "禁用下载",
"disableDownloadDesc": "禁止从远程会话下载文件",
"disableUpload": "禁用上传",
"disableUploadDesc": "禁止上传文件到远程会话",
"enableTouch": "启用触控",
"enableTouchDesc": "启用触控输入转发",
"consoleSession": "控制台会话",
"consoleSessionDesc": "连接到控制台(会话 0)而不是新会话",
"sendWolPacket": "发送 WOL 数据包",
"sendWolPacketDesc": "在连接前发送魔术包以唤醒此主机",
"disableCopy": "禁用复制",
"disableCopyDesc": "禁止从远程会话复制文本",
"disablePaste": "禁用粘贴",
"disablePasteDesc": "禁止粘贴文本到远程会话",
"createPathIfMissing": "如果路径不存在则创建",
"createPathIfMissingDesc": "自动创建录制目录",
"excludeOutput": "排除输出",
"excludeOutputDesc": "不录制屏幕输出(仅元数据)",
"excludeMouse": "排除鼠标",
"excludeMouseDesc": "不录制鼠标移动",
"includeKeystrokes": "包含击键",
"includeKeystrokesDesc": "除了屏幕输出外,还录制原始击键",
"vncPort": "VNC 端口",
"vncPassword": "VNC 密码",
"vncUsernameOptional": "用户名 (可选)",
"vncLeaveBlank": "如不需要请留空",
"cursorMode": "光标模式",
"swapRedBlue": "交换红/蓝",
"swapRedBlueDesc": "交换红蓝色彩通道 (可修复某些色彩问题)",
"readOnly": "只读",
"readOnlyDesc": "仅查看远程屏幕,不发送任何输入",
"telnetPort": "Telnet 端口",
"terminalType": "终端类型",
"fontName": "字体名称",
"fontSize": "字体大小",
"colorScheme": "配色方案",
"backspaceKey": "退格键",
"colorDepth": "颜色深度",
"resizeMethod": "调整大小方法",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "远程桌面",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "虚拟网络",
"unencryptedShell": "未加密 Shell",
"remoteDesktopNotes": "笔记",
"userDefaultsNote": "Settings you leave unchanged follow your host defaults.",
"connectionOriginNote": "On the desktop app, RDP, VNC and Telnet use the remote server unless the host is set to This device, which needs a guacd this machine can reach. Set one in the Remote Desktop settings, or per host under guacd Proxy.",
"docsLink": "查看文档",
"connectionOriginGuacamoleNote": "RDP, VNC and Telnet use the remote server unless you pick \"This device\", which needs a guacd this machine can reach. Set one in the guacd URL setting, or per host under guacd Proxy."
},
"onboarding": {
"feature_desktop": "Remote desktop",
"feature_desktop_desc": "像 SSH 一样,在标签页中直接打开 RDP、VNC 和 Telnet 会话。"
},
"settings": {
"user": {
"colorDepth": {
"label": "颜色深度",
"description": "Used for RDP hosts that leave it on their default."
},
"resizeMethod": {
"label": "调整大小方法",
"description": "How the remote screen follows the window size."
},
"resizeDisplayUpdate": "Display Update",
"resizeReconnect": "重新连接",
"inherit": "Inherit",
"on": "开启",
"off": "关闭",
"forceLossless": {
"label": "强制无损",
"description": "强制无损图像编码(质量更高,占用更多带宽)"
},
"enableWallpaper": {
"label": "壁纸",
"description": "显示桌面壁纸(禁用可提高性能)"
},
"enableFontSmoothing": {
"label": "字体平滑",
"description": "启用 ClearType 字体渲染"
},
"enableDesktopComposition": {
"label": "桌面合成",
"description": "启用 Aero 玻璃效果"
},
"disableAudio": {
"label": "禁用音频",
"description": "将远程会话的所有音频静音"
},
"enablePrinting": {
"label": "启用打印",
"description": "将本地打印机重定向到远程会话"
},
"enableDrive": {
"label": "启用驱动器重定向",
"description": "将本地文件夹映射为远程会话中的驱动器"
},
"disableCopy": {
"label": "禁用复制",
"description": "禁止从远程会话复制文本"
},
"disablePaste": {
"label": "禁用粘贴",
"description": "禁止粘贴文本到远程会话"
},
"group": "Remote Desktop defaults",
"colorDepth16": "16-bit",
"colorDepth24": "24-bit",
"colorDepth32": "32-bit"
},
"admin": {
"enabled": {
"label": "Enable Remote Desktop",
"description": "Allow RDP, VNC and Telnet connections through guacd."
},
"guacdUrl": {
"label": "guacd URL",
"description": "Where guacd listens, as host:port. The GUACD_URL, GUACD_HOST and GUACD_PORT environment variables override this.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP port"
},
"vncPort": {
"label": "VNC port"
},
"telnetPort": {
"label": "Telnet port"
},
"rdpSecurity": {
"label": "RDP security mode"
},
"rdpIgnoreCert": {
"label": "Ignore certificate errors"
},
"guacamoleConfig": {
"label": "guacd settings"
},
"enableToolbar": {
"label": "Remote desktop toolbar",
"description": "Show the toolbar in RDP, VNC and Telnet sessions."
},
"displayGroup": "Display",
"displayDefaultsTab": "远程桌面"
}
},
"palette": {
"connectRdp": "Connect with RDP",
"connectVnc": "Connect with VNC",
"connectTelnet": "Connect with Telnet"
},
"permissions": {
"sessions": {
"title": "Share remote desktop sessions",
"description": "Let session sharing and collab rooms show this user's RDP, VNC and Telnet sessions to others."
}
},
"errors": {
"remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type."
}
}
@@ -0,0 +1,350 @@
{
"remoteDesktop": {
"connecting": "正在連線至 {{type}} 工作階段…",
"checkingGuacd": "Checking remote desktop service...",
"requestingToken": "Requesting {{type}} connection token...",
"openingSession": "Opening {{type}} session...",
"negotiating": "Negotiating {{type}} handshake...",
"sessionReady": "{{type}} session ready",
"connectionError": "連線錯誤",
"connectionFailed": "連線失敗",
"failedToConnect": "無法取得連線權杖",
"hostNotFound": "找不到主機",
"noHostSelected": "未選擇主機",
"reconnect": "重新連線",
"retry": "重試",
"guacdUnavailable": "遠端桌面服務 (guacd) 無法使用。請確保 guacd 正在執行且可存取,並已在管理員設定中正確設定。",
"credentialPromptTitle": "Enter RDP Credentials",
"credentialPromptDescription": "This host is set to prompt for credentials on connect. They are used for this session only and are not saved.",
"connect": "連線",
"ctrlAltDel": "Ctrl+Alt+Del",
"files": {
"title": "Drive Files",
"parent": "Parent directory",
"refresh": "重新整理",
"empty": "此資料夾為空",
"listFailed": "Failed to read this folder",
"download": "下載",
"downloadFailed": "Failed to download {{name}}",
"upload": "Upload files",
"uploading": "正在上傳 {{name}}...",
"uploaded": "Uploaded {{name}}",
"uploadFailed": "Failed to upload {{name}}",
"uploadDisabled": "Uploads are disabled for this connection",
"driveUnavailable": "Enable RDP drive redirection before uploading files",
"driveNotWritable": "guacd cannot write to the drive folder. Check GUACD_DRIVE_PATH (or the host's Drive Path) and its permissions.",
"dropToUpload": "Drop files to upload"
},
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (鎖定螢幕)",
"winKey": "Windows 鍵",
"superL": "Super+L (Lock Screen)",
"superKey": "Super Key",
"super": "Super",
"cmdL": "Cmd+L (Lock Screen)",
"cmdKey": "Command Key",
"cmd": "Cmd",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Shift",
"win": "Win",
"stickyActive": "{{key}} (已鎖定 - 點擊釋放)",
"stickyInactive": "{{key}} (點擊鎖定)",
"esc": "Escape",
"tab": "Tab",
"home": "Home",
"end": "End",
"pageUp": "Page Up",
"pageDown": "Page Down",
"arrowUp": "上箭頭",
"arrowDown": "下箭頭",
"arrowLeft": "左箭頭",
"arrowRight": "右箭頭",
"fnToggle": "功能鍵",
"reconnect": "重新連線工作階段",
"collapse": "收合工具列",
"expand": "展開工具列",
"dragHandle": "拖曳以重新定位",
"hide": "Hide for this session. To disable it permanently, open Host Settings → General → Connection Toolbar.",
"zoomIn": "Zoom in",
"zoomOut": "Zoom out",
"resetZoom": "Reset zoom",
"fitToScreen": "Fit to screen",
"switchToTrackpad": "Switch to trackpad mode (drag to move cursor, tap to click)",
"switchToTouch": "Switch to touch mode (tap directly where you want to click)"
},
"disabled": "Remote Desktop is turned off. An admin can turn it on in Settings."
},
"hosts": {
"nativeRdpOpened": "Opened in Windows Remote Desktop",
"nativeRdpFailed": "Failed to open Windows Remote Desktop",
"openNativeRdp": "Open in Windows Remote Desktop",
"guac": {
"toolbar": "Toolbar",
"connection": "連線",
"authentication": "驗證",
"storedCredential": "已儲存的憑證",
"authMethod": "驗證方法",
"authTypeDirect": "直接",
"authTypeCredential": "憑證",
"authTypeNone": "取消全選",
"authTypeNoneDesc": "No credentials are stored. You'll be prompted for a username and password each time you connect; they are not saved.",
"selectCredential": "選取憑證…",
"connectionSettings": "連線設定",
"displaySettings": "顯示設定",
"audioSettings": "音訊設定",
"rdpPerformance": "RDP 效能",
"deviceRedirection": "裝置重新導向",
"session": "工作階段",
"gateway": "閘道",
"remoteApp": "RemoteApp",
"clipboard": "剪貼簿",
"sessionRecording": "工作階段錄製",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "VNC 設定",
"terminalSettings": "終端機設定",
"rdpPort": "RDP 連接埠",
"username": "使用者名稱",
"password": "密碼",
"passwordSaved": "Password saved, type to change",
"domain": "網域",
"securityMode": "安全模式",
"width": "寬度",
"height": "高度",
"dpi": "DPI",
"clientName": "用戶端名稱",
"initialProgram": "初始程式",
"serverLayout": "伺服器鍵盤配置",
"timezone": "時區",
"loadBalanceInfo": "負載平衡資訊 / Cookie",
"loadBalanceInfoDesc": "用於 RDS 伺服器陣列負載平衡的 RD 連線代理 Cookie (例如 tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacd 代理",
"guacdHostname": "guacd 主機",
"guacdHostnamePlaceholder": "全域預設",
"guacdPort": "guacd 連接埠",
"guacdProxyDesc": "覆寫此連線的全域 guacd 執行個體。留空以使用全域設定的 guacd。",
"gatewayHostname": "閘道主機名稱",
"gatewayPort": "閘道連接埠",
"gatewayUsername": "閘道使用者名稱",
"gatewayPassword": "閘道密碼",
"gatewayDomain": "閘道網域",
"remoteAppProgram": "RemoteApp 程式",
"workingDirectory": "工作目錄",
"arguments": "引數",
"normalizeLineEndings": "正規化行尾符號",
"macAddress": "MAC 位址",
"broadcastAddress": "廣播位址",
"udpPort": "UDP 連接埠",
"waitTimeS": "等待時間 (秒)",
"driveName": "磁碟機名稱",
"drivePath": "磁碟機路徑",
"ignoreCertificate": "忽略憑證",
"ignoreCertificateDesc": "允許連線至使用自行簽署憑證的主機",
"forceLossless": "強制無損",
"forceLosslessDesc": "強制使用無損影像編碼(品質較高,頻寬需求較大)",
"disableAudio": "停用音訊",
"disableAudioDesc": "將遠端工作階段的音訊全部靜音",
"enableAudioInput": "啟用音訊輸入(麥克風)",
"enableAudioInputDesc": "將本機麥克風轉送至遠端工作階段",
"wallpaper": "桌面背景",
"wallpaperDesc": "顯示桌面背景(停用可提升效能)",
"theming": "佈景主題",
"themingDesc": "啟用視覺主題與樣式",
"fontSmoothing": "字型平滑處理",
"fontSmoothingDesc": "啟用 ClearType 字型轉譯",
"fullWindowDrag": "全視窗拖曳",
"fullWindowDragDesc": "拖曳時顯示視窗內容",
"desktopComposition": "桌面合成",
"desktopCompositionDesc": "啟用 Aero 玻璃效果",
"menuAnimations": "選單動畫",
"menuAnimationsDesc": "啟用選單淡入淡出及滑動動畫",
"disableBitmapCaching": "停用點陣圖快取",
"disableBitmapCachingDesc": "關閉點陣圖快取(可能有助於解決顯示問題)",
"disableOffscreenCaching": "停用螢幕外快取",
"disableOffscreenCachingDesc": "關閉螢幕外快取",
"disableGlyphCaching": "停用字符快取",
"disableGlyphCachingDesc": "關閉字符快取",
"enableGfx": "啟用 GFX",
"enableGfxDesc": "使用 RemoteFX 圖形管線",
"enablePrinting": "啟用列印功能",
"enablePrintingDesc": "將本機印表機重新導向至遠端工作階段",
"enableDriveRedirection": "啟用磁碟機重新導向",
"enableDriveRedirectionDesc": "在遠端工作階段中將本機資料夾對應為磁碟機",
"createDrivePath": "建立磁碟機路徑",
"createDrivePathDesc": "如果資料夾不存在則自動建立",
"disableDownload": "停用下載",
"disableDownloadDesc": "防止從遠端工作階段下載檔案",
"disableUpload": "停用上傳",
"disableUploadDesc": "防止將檔案上傳至遠端工作階段",
"enableTouch": "啟用觸控",
"enableTouchDesc": "啟用觸控輸入轉送",
"consoleSession": "主控台工作階段",
"consoleSessionDesc": "連線至主控台 (工作階段 0) 而非新工作階段",
"sendWolPacket": "傳送 WOL 封包",
"sendWolPacketDesc": "在連線前傳送魔術封包以喚醒此主機",
"disableCopy": "停用複製",
"disableCopyDesc": "防止從遠端工作階段複製文字",
"disablePaste": "停用貼上",
"disablePasteDesc": "防止將文字貼上至遠端工作階段",
"createPathIfMissing": "若路徑不存在則建立",
"createPathIfMissingDesc": "自動建立錄製目錄",
"excludeOutput": "排除輸出",
"excludeOutputDesc": "不錄製螢幕輸出 (僅中繼資料)",
"excludeMouse": "排除滑鼠",
"excludeMouseDesc": "不錄製滑鼠移動",
"includeKeystrokes": "包含按鍵輸入",
"includeKeystrokesDesc": "除了螢幕輸出外,也錄製原始按鍵輸入",
"vncPort": "VNC 連接埠",
"vncPassword": "VNC 密碼",
"vncUsernameOptional": "使用者名稱 (選用)",
"vncLeaveBlank": "若不需要則留空",
"cursorMode": "游標模式",
"swapRedBlue": "交換紅/藍",
"swapRedBlueDesc": "交換紅色與藍色色版 (修正某些色彩問題)",
"readOnly": "唯讀",
"readOnlyDesc": "僅檢視遠端螢幕,不傳送任何輸入",
"telnetPort": "Telnet 連接埠",
"terminalType": "終端機類型",
"fontName": "字型名稱",
"fontSize": "字型大小",
"colorScheme": "色彩配置",
"backspaceKey": "退格鍵",
"colorDepth": "色彩深度",
"resizeMethod": "調整尺寸方法",
"driveNamePlaceholder": "Termix Drive"
},
"remoteDesktop": "遠端桌面",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTelnet": "Telnet",
"virtualNetwork": "虛擬網路",
"unencryptedShell": "未加密殼層",
"remoteDesktopNotes": "筆記",
"userDefaultsNote": "Settings you leave unchanged follow your host defaults.",
"connectionOriginNote": "On the desktop app, RDP, VNC and Telnet use the remote server unless the host is set to This device, which needs a guacd this machine can reach. Set one in the Remote Desktop settings, or per host under guacd Proxy.",
"docsLink": "檢視文件",
"connectionOriginGuacamoleNote": "RDP, VNC and Telnet use the remote server unless you pick \"This device\", which needs a guacd this machine can reach. Set one in the guacd URL setting, or per host under guacd Proxy."
},
"onboarding": {
"feature_desktop": "Remote desktop",
"feature_desktop_desc": "Open RDP, VNC and Telnet sessions in a tab, same as SSH."
},
"settings": {
"user": {
"colorDepth": {
"label": "色彩深度",
"description": "Used for RDP hosts that leave it on their default."
},
"resizeMethod": {
"label": "調整尺寸方法",
"description": "How the remote screen follows the window size."
},
"resizeDisplayUpdate": "Display Update",
"resizeReconnect": "重新連線",
"inherit": "Inherit",
"on": "開啟",
"off": "關閉",
"forceLossless": {
"label": "強制無損",
"description": "強制使用無損影像編碼(品質較高,頻寬需求較大)"
},
"enableWallpaper": {
"label": "桌面背景",
"description": "顯示桌面背景(停用可提升效能)"
},
"enableFontSmoothing": {
"label": "字型平滑處理",
"description": "啟用 ClearType 字型轉譯"
},
"enableDesktopComposition": {
"label": "桌面合成",
"description": "啟用 Aero 玻璃效果"
},
"disableAudio": {
"label": "停用音訊",
"description": "將遠端工作階段的音訊全部靜音"
},
"enablePrinting": {
"label": "啟用列印功能",
"description": "將本機印表機重新導向至遠端工作階段"
},
"enableDrive": {
"label": "啟用磁碟機重新導向",
"description": "在遠端工作階段中將本機資料夾對應為磁碟機"
},
"disableCopy": {
"label": "停用複製",
"description": "防止從遠端工作階段複製文字"
},
"disablePaste": {
"label": "停用貼上",
"description": "防止將文字貼上至遠端工作階段"
},
"group": "Remote Desktop defaults",
"colorDepth16": "16-bit",
"colorDepth24": "24-bit",
"colorDepth32": "32-bit"
},
"admin": {
"enabled": {
"label": "Enable Remote Desktop",
"description": "Allow RDP, VNC and Telnet connections through guacd."
},
"guacdUrl": {
"label": "guacd URL",
"description": "Where guacd listens, as host:port. The GUACD_URL, GUACD_HOST and GUACD_PORT environment variables override this.",
"placeholder": "guacd:4822"
}
},
"host": {
"enableRdp": {
"label": "RDP"
},
"enableVnc": {
"label": "VNC"
},
"enableTelnet": {
"label": "Telnet"
},
"rdpPort": {
"label": "RDP port"
},
"vncPort": {
"label": "VNC port"
},
"telnetPort": {
"label": "Telnet port"
},
"rdpSecurity": {
"label": "RDP security mode"
},
"rdpIgnoreCert": {
"label": "Ignore certificate errors"
},
"guacamoleConfig": {
"label": "guacd settings"
},
"enableToolbar": {
"label": "Remote desktop toolbar",
"description": "Show the toolbar in RDP, VNC and Telnet sessions."
},
"displayGroup": "Display",
"displayDefaultsTab": "遠端桌面"
}
},
"palette": {
"connectRdp": "Connect with RDP",
"connectVnc": "Connect with VNC",
"connectTelnet": "Connect with Telnet"
},
"permissions": {
"sessions": {
"title": "Share remote desktop sessions",
"description": "Let session sharing and collab rooms show this user's RDP, VNC and Telnet sessions to others."
}
},
"errors": {
"remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type."
}
}
+739
View File
@@ -0,0 +1,739 @@
{
"id": "remote-desktop",
"name": "Remote Desktop",
"version": "1.0.0",
"description": "RDP, VNC and Telnet sessions in the browser, with clipboard, file transfer, recording and sharing.",
"author": {
"name": "Termix"
},
"license": "MIT",
"repository": "https://github.com/Termix-SSH/Termix",
"category": "Terminal",
"icon": "Monitor",
"engine": {
"termix": ">=2.9.0",
"api": "1"
},
"capabilities": [
"hosts:read",
"credentials:read",
"credentials:use",
"ssh:connect",
"network:serve",
"desktop:window",
"ui:surface",
"users:impersonate"
],
"optionalDependencies": {
"session-recording": "^1.0.0",
"tunnels": "^1.0.0"
},
"provides": [
{
"service": "sessions.live",
"version": "1.0.0",
"permission": "remote-desktop.sessions",
"names": ["rdp", "vnc", "telnet"]
}
],
"requires": [
{
"service": "recordings.writer",
"versionRange": "^1.1.0",
"optional": true
},
{
"service": "tunnels.access",
"versionRange": "^1.0.0",
"optional": true
}
],
"contributes": {
"tabs": [
{
"id": "rdp",
"titleKey": "hosts.tabRdp",
"icon": "Monitor",
"openFrom": ["host-context-menu", "palette"]
},
{
"id": "vnc",
"titleKey": "hosts.tabVnc",
"icon": "MousePointerClick",
"openFrom": ["host-context-menu", "palette"]
},
{
"id": "telnet",
"titleKey": "hosts.tabTelnet",
"icon": "Terminal",
"openFrom": ["host-context-menu", "palette"]
}
],
"actionSlots": [
{
"id": "remote-desktop.toolbar",
"accepts": ["button"]
}
],
"protocols": [
{
"id": "rdp",
"credentialFields": [
{
"key": "domain"
}
],
"defaultPort": 3389,
"hostLoginFallback": ["username", "password"]
},
{
"id": "vnc",
"defaultPort": 5900,
"hostLoginFallback": ["password"]
},
{
"id": "telnet",
"defaultPort": 23,
"hostLoginFallback": ["password"]
}
],
"guest": true,
"permissions": [
{
"name": "sessions",
"titleKey": "permissions.sessions.title",
"descriptionKey": "permissions.sessions.description",
"defaultRoles": ["admin", "user"]
}
],
"settings": {
"admin": [
{
"key": "enabled",
"type": "boolean",
"labelKey": "settings.admin.enabled.label",
"descriptionKey": "settings.admin.enabled.description",
"default": true
},
{
"key": "guacdUrl",
"type": "string",
"labelKey": "settings.admin.guacdUrl.label",
"descriptionKey": "settings.admin.guacdUrl.description",
"placeholderKey": "settings.admin.guacdUrl.placeholder",
"requires": "enabled",
"default": ""
}
],
"user": [
{
"key": "colorDepth",
"type": "select",
"labelKey": "settings.user.colorDepth.label",
"descriptionKey": "settings.user.colorDepth.description",
"group": "settings.user.group",
"options": [
{
"value": "inherit",
"labelKey": "settings.user.inherit"
},
{
"value": "16",
"labelKey": "settings.user.colorDepth16"
},
{
"value": "24",
"labelKey": "settings.user.colorDepth24"
},
{
"value": "32",
"labelKey": "settings.user.colorDepth32"
}
],
"default": "inherit",
"hidden": true
},
{
"key": "resizeMethod",
"type": "select",
"labelKey": "settings.user.resizeMethod.label",
"descriptionKey": "settings.user.resizeMethod.description",
"group": "settings.user.group",
"options": [
{
"value": "inherit",
"labelKey": "settings.user.inherit"
},
{
"value": "display-update",
"labelKey": "settings.user.resizeDisplayUpdate"
},
{
"value": "reconnect",
"labelKey": "settings.user.resizeReconnect"
}
],
"default": "inherit",
"hidden": true
},
{
"key": "forceLossless",
"type": "select",
"labelKey": "settings.user.forceLossless.label",
"descriptionKey": "settings.user.forceLossless.description",
"group": "settings.user.group",
"options": [
{
"value": "inherit",
"labelKey": "settings.user.inherit"
},
{
"value": "on",
"labelKey": "settings.user.on"
},
{
"value": "off",
"labelKey": "settings.user.off"
}
],
"default": "inherit",
"hidden": true
},
{
"key": "enableWallpaper",
"type": "select",
"labelKey": "settings.user.enableWallpaper.label",
"descriptionKey": "settings.user.enableWallpaper.description",
"group": "settings.user.group",
"options": [
{
"value": "inherit",
"labelKey": "settings.user.inherit"
},
{
"value": "on",
"labelKey": "settings.user.on"
},
{
"value": "off",
"labelKey": "settings.user.off"
}
],
"default": "inherit",
"hidden": true
},
{
"key": "enableFontSmoothing",
"type": "select",
"labelKey": "settings.user.enableFontSmoothing.label",
"descriptionKey": "settings.user.enableFontSmoothing.description",
"group": "settings.user.group",
"options": [
{
"value": "inherit",
"labelKey": "settings.user.inherit"
},
{
"value": "on",
"labelKey": "settings.user.on"
},
{
"value": "off",
"labelKey": "settings.user.off"
}
],
"default": "inherit",
"hidden": true
},
{
"key": "enableDesktopComposition",
"type": "select",
"labelKey": "settings.user.enableDesktopComposition.label",
"descriptionKey": "settings.user.enableDesktopComposition.description",
"group": "settings.user.group",
"options": [
{
"value": "inherit",
"labelKey": "settings.user.inherit"
},
{
"value": "on",
"labelKey": "settings.user.on"
},
{
"value": "off",
"labelKey": "settings.user.off"
}
],
"default": "inherit",
"hidden": true
},
{
"key": "disableAudio",
"type": "select",
"labelKey": "settings.user.disableAudio.label",
"descriptionKey": "settings.user.disableAudio.description",
"group": "settings.user.group",
"options": [
{
"value": "inherit",
"labelKey": "settings.user.inherit"
},
{
"value": "on",
"labelKey": "settings.user.on"
},
{
"value": "off",
"labelKey": "settings.user.off"
}
],
"default": "inherit",
"hidden": true
},
{
"key": "enablePrinting",
"type": "select",
"labelKey": "settings.user.enablePrinting.label",
"descriptionKey": "settings.user.enablePrinting.description",
"group": "settings.user.group",
"options": [
{
"value": "inherit",
"labelKey": "settings.user.inherit"
},
{
"value": "on",
"labelKey": "settings.user.on"
},
{
"value": "off",
"labelKey": "settings.user.off"
}
],
"default": "inherit",
"hidden": true
},
{
"key": "enableDrive",
"type": "select",
"labelKey": "settings.user.enableDrive.label",
"descriptionKey": "settings.user.enableDrive.description",
"group": "settings.user.group",
"options": [
{
"value": "inherit",
"labelKey": "settings.user.inherit"
},
{
"value": "on",
"labelKey": "settings.user.on"
},
{
"value": "off",
"labelKey": "settings.user.off"
}
],
"default": "inherit",
"hidden": true
},
{
"key": "disableCopy",
"type": "select",
"labelKey": "settings.user.disableCopy.label",
"descriptionKey": "settings.user.disableCopy.description",
"group": "settings.user.group",
"options": [
{
"value": "inherit",
"labelKey": "settings.user.inherit"
},
{
"value": "on",
"labelKey": "settings.user.on"
},
{
"value": "off",
"labelKey": "settings.user.off"
}
],
"default": "inherit",
"hidden": true
},
{
"key": "disablePaste",
"type": "select",
"labelKey": "settings.user.disablePaste.label",
"descriptionKey": "settings.user.disablePaste.description",
"group": "settings.user.group",
"options": [
{
"value": "inherit",
"labelKey": "settings.user.inherit"
},
{
"value": "on",
"labelKey": "settings.user.on"
},
{
"value": "off",
"labelKey": "settings.user.off"
}
],
"default": "inherit",
"hidden": true
}
],
"host": {
"fields": [
{
"key": "enableRdp",
"type": "boolean",
"labelKey": "settings.host.enableRdp.label",
"default": false,
"hidden": true,
"defaultable": false
},
{
"key": "enableVnc",
"type": "boolean",
"labelKey": "settings.host.enableVnc.label",
"default": false,
"hidden": true,
"defaultable": false
},
{
"key": "enableTelnet",
"type": "boolean",
"labelKey": "settings.host.enableTelnet.label",
"default": false,
"hidden": true,
"defaultable": false
},
{
"key": "rdpPort",
"type": "number",
"labelKey": "settings.host.rdpPort.label",
"min": 1,
"max": 65535,
"default": 3389,
"hidden": true,
"defaultable": false
},
{
"key": "vncPort",
"type": "number",
"labelKey": "settings.host.vncPort.label",
"min": 1,
"max": 65535,
"default": 5900,
"hidden": true,
"defaultable": false
},
{
"key": "telnetPort",
"type": "number",
"labelKey": "settings.host.telnetPort.label",
"min": 1,
"max": 65535,
"default": 23,
"hidden": true,
"defaultable": false
},
{
"key": "rdpSecurity",
"type": "string",
"labelKey": "settings.host.rdpSecurity.label",
"default": "",
"hidden": true
},
{
"key": "rdpIgnoreCert",
"type": "boolean",
"labelKey": "settings.host.rdpIgnoreCert.label",
"default": false,
"hidden": true
},
{
"key": "guacamoleConfig",
"type": "json",
"labelKey": "settings.host.guacamoleConfig.label",
"default": {},
"hidden": true,
"shareRead": "edit",
"secretKeys": ["gateway-password"],
"defaultable": false
},
{
"key": "enableToolbar",
"type": "boolean",
"labelKey": "settings.host.enableToolbar.label",
"descriptionKey": "settings.host.enableToolbar.description",
"default": true,
"hidden": true
},
{
"key": "colorDepth",
"type": "select",
"labelKey": "settings.user.colorDepth.label",
"descriptionKey": "settings.user.colorDepth.description",
"group": "settings.host.displayGroup",
"options": [
{
"value": "inherit",
"labelKey": "settings.user.inherit"
},
{
"value": "16",
"labelKey": "settings.user.colorDepth16"
},
{
"value": "24",
"labelKey": "settings.user.colorDepth24"
},
{
"value": "32",
"labelKey": "settings.user.colorDepth32"
}
],
"default": "inherit",
"personal": true
},
{
"key": "resizeMethod",
"type": "select",
"labelKey": "settings.user.resizeMethod.label",
"descriptionKey": "settings.user.resizeMethod.description",
"group": "settings.host.displayGroup",
"options": [
{
"value": "inherit",
"labelKey": "settings.user.inherit"
},
{
"value": "display-update",
"labelKey": "settings.user.resizeDisplayUpdate"
},
{
"value": "reconnect",
"labelKey": "settings.user.resizeReconnect"
}
],
"default": "inherit",
"personal": true
},
{
"key": "forceLossless",
"type": "select",
"labelKey": "settings.user.forceLossless.label",
"descriptionKey": "settings.user.forceLossless.description",
"group": "settings.host.displayGroup",
"options": [
{
"value": "inherit",
"labelKey": "settings.user.inherit"
},
{
"value": "on",
"labelKey": "settings.user.on"
},
{
"value": "off",
"labelKey": "settings.user.off"
}
],
"default": "inherit",
"personal": true
},
{
"key": "enableWallpaper",
"type": "select",
"labelKey": "settings.user.enableWallpaper.label",
"descriptionKey": "settings.user.enableWallpaper.description",
"group": "settings.host.displayGroup",
"options": [
{
"value": "inherit",
"labelKey": "settings.user.inherit"
},
{
"value": "on",
"labelKey": "settings.user.on"
},
{
"value": "off",
"labelKey": "settings.user.off"
}
],
"default": "inherit",
"personal": true
},
{
"key": "enableFontSmoothing",
"type": "select",
"labelKey": "settings.user.enableFontSmoothing.label",
"descriptionKey": "settings.user.enableFontSmoothing.description",
"group": "settings.host.displayGroup",
"options": [
{
"value": "inherit",
"labelKey": "settings.user.inherit"
},
{
"value": "on",
"labelKey": "settings.user.on"
},
{
"value": "off",
"labelKey": "settings.user.off"
}
],
"default": "inherit",
"personal": true
},
{
"key": "enableDesktopComposition",
"type": "select",
"labelKey": "settings.user.enableDesktopComposition.label",
"descriptionKey": "settings.user.enableDesktopComposition.description",
"group": "settings.host.displayGroup",
"options": [
{
"value": "inherit",
"labelKey": "settings.user.inherit"
},
{
"value": "on",
"labelKey": "settings.user.on"
},
{
"value": "off",
"labelKey": "settings.user.off"
}
],
"default": "inherit",
"personal": true
},
{
"key": "disableAudio",
"type": "select",
"labelKey": "settings.user.disableAudio.label",
"descriptionKey": "settings.user.disableAudio.description",
"group": "settings.host.displayGroup",
"options": [
{
"value": "inherit",
"labelKey": "settings.user.inherit"
},
{
"value": "on",
"labelKey": "settings.user.on"
},
{
"value": "off",
"labelKey": "settings.user.off"
}
],
"default": "inherit",
"personal": true
},
{
"key": "enablePrinting",
"type": "select",
"labelKey": "settings.user.enablePrinting.label",
"descriptionKey": "settings.user.enablePrinting.description",
"group": "settings.host.displayGroup",
"options": [
{
"value": "inherit",
"labelKey": "settings.user.inherit"
},
{
"value": "on",
"labelKey": "settings.user.on"
},
{
"value": "off",
"labelKey": "settings.user.off"
}
],
"default": "inherit",
"personal": true
},
{
"key": "enableDrive",
"type": "select",
"labelKey": "settings.user.enableDrive.label",
"descriptionKey": "settings.user.enableDrive.description",
"group": "settings.host.displayGroup",
"options": [
{
"value": "inherit",
"labelKey": "settings.user.inherit"
},
{
"value": "on",
"labelKey": "settings.user.on"
},
{
"value": "off",
"labelKey": "settings.user.off"
}
],
"default": "inherit",
"personal": true
},
{
"key": "disableCopy",
"type": "select",
"labelKey": "settings.user.disableCopy.label",
"descriptionKey": "settings.user.disableCopy.description",
"group": "settings.host.displayGroup",
"options": [
{
"value": "inherit",
"labelKey": "settings.user.inherit"
},
{
"value": "on",
"labelKey": "settings.user.on"
},
{
"value": "off",
"labelKey": "settings.user.off"
}
],
"default": "inherit",
"personal": true
},
{
"key": "disablePaste",
"type": "select",
"labelKey": "settings.user.disablePaste.label",
"descriptionKey": "settings.user.disablePaste.description",
"group": "settings.host.displayGroup",
"options": [
{
"value": "inherit",
"labelKey": "settings.user.inherit"
},
{
"value": "on",
"labelKey": "settings.user.on"
},
{
"value": "off",
"labelKey": "settings.user.off"
}
],
"default": "inherit",
"personal": true
}
]
}
},
"uses": ["session.remoteDisplay", "wakeOnLan.macAddress"]
},
"locales": "locales",
"platforms": ["linux", "win32", "darwin"]
}
+40
View File
@@ -0,0 +1,40 @@
{
"name": "@termix-plugin/remote-desktop",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"build": "termix-plugin build",
"test": "termix-plugin test",
"typecheck": "tsc -p .",
"validate": "termix-plugin validate"
},
"dependencies": {
"guacamole-common-js": "^1.5.0",
"guacamole-lite": "^1.2.0",
"lucide-react": "^1.33.0"
},
"devDependencies": {
"@termix/plugin-sdk": "^1.0.0",
"@testing-library/dom": "^10.4.2",
"@testing-library/react": "^16.3.3",
"@types/node": "^26.2.0",
"@types/react": "^19.2.18",
"@types/react-dom": "^19.2.4",
"jsdom": "^30.0.1",
"vitest": "^4.1.11"
},
"peerDependencies": {
"@termix/plugin-sdk": "^1.0.0",
"express": "^5.2.1",
"react": "^19.2.8",
"react-i18next": "^17.0.14",
"sonner": "^2.0.8",
"ssh2": "^1.17.0"
},
"termix": {
"patches": [
"guacamole-common-js-flush"
]
}
}
@@ -0,0 +1,396 @@
const fs = require("fs");
const path = require("path");
// Run by `termix-plugin build` with TERMIX_PATCH_ROOT set to the plugin, so
// the package resolves the way the plugin's bundle will resolve it.
function packageDir(name) {
const root = process.env.TERMIX_PATCH_ROOT || process.cwd();
// Walked by hand: a package whose exports map hides package.json cannot be
// found with require.resolve.
for (let dir = path.resolve(root); ; dir = path.dirname(dir)) {
const candidate = path.join(dir, "node_modules", name);
if (fs.existsSync(path.join(candidate, "package.json"))) return candidate;
if (path.dirname(dir) === dir) return path.join(root, "node_modules", name);
}
}
const guacdClientPath = path.join(
packageDir("guacamole-lite"),
"lib",
"GuacdClient.js",
);
const cryptPath = path.join(packageDir("guacamole-lite"), "lib", "Crypt.js");
const clientConnectionPath = path.join(
packageDir("guacamole-lite"),
"lib",
"ClientConnection.js",
);
const serverPath = path.join(packageDir("guacamole-lite"), "lib", "Server.js");
if (
!fs.existsSync(guacdClientPath) ||
!fs.existsSync(cryptPath) ||
!fs.existsSync(clientConnectionPath) ||
!fs.existsSync(serverPath)
) {
console.log("[patch-guacamole-lite] File not found, skipping");
process.exit(0);
}
// Every patch below is required for correctness: protocol negotiation, the
// guacd 1.6.0 name handshake, dynamic argument answering, UTF-8 tokens and
// read-only joins. If an upstream release moves an anchor string, silently
// skipping would ship a Termix that looks fine and then drops VNC/RDP sessions
// at runtime, so a missing anchor has to stop the install instead.
function missingAnchor(patch) {
console.error(
`[patch-guacamole-lite] ${patch} anchor not found in guacamole-lite. ` +
"The upstream file has changed and this patch no longer applies — " +
"update scripts/patch-guacamole-lite.cjs to match the new source.",
);
process.exit(1);
}
let guacdClientContent = fs.readFileSync(guacdClientPath, "utf8");
let cryptContent = fs.readFileSync(cryptPath, "utf8");
let clientConnectionContent = fs.readFileSync(clientConnectionPath, "utf8");
let serverContent = fs.readFileSync(serverPath, "utf8");
// Patch 1: protocol version negotiation.
// guacamole-lite originally only accepted 1.0.0/1.1.0. Support the protocol
// versions Termix can handle, and conservatively answer future 1.x versions as
// VERSION_1_5_0 so guacd still sees support for `require`/`name` without us
// claiming support for unknown instructions.
const oldVersionBlock =
" if (version === '1_0_0' || version === '1_1_0') {\n" +
" protocolVersion = version;\n" +
" } else {\n" +
" protocolVersion = '1_1_0';\n" +
" }";
const oldPatchedVersionBlock =
" if (version === '1_0_0' || version === '1_1_0' || version === '1_3_0' || version === '1_5_0') {\n" +
" protocolVersion = version;\n" +
" } else {\n" +
" protocolVersion = '1_1_0';\n" +
" }";
const newVersionBlock =
" if (version === '1_0_0' || version === '1_1_0' || version === '1_3_0' || version === '1_5_0') {\n" +
" protocolVersion = version;\n" +
" } else if (/^1_\\d+_0$/.test(version)) {\n" +
" protocolVersion = '1_5_0';\n" +
" } else {\n" +
" protocolVersion = '1_1_0';\n" +
" }";
// Patch 2: timezone instruction must be sent for all protocols >= 1.1.0, not just 1.1.0
const oldTimezone = "if (protocolVersion === '1_1_0') {";
const newTimezone = "if (protocolVersion !== '1_0_0') {";
// Patch 3: send the `name` handshake instruction for all protocol versions >= 1.1.0.
// The Guacamole protocol added `name` in 1.3.0, but guacd 1.6.0 began requiring it
// during the VNC handshake even when negotiating VERSION_1_1_0, causing connections to
// silently drop right after "User joined". Sending it for all non-1.0.0 sessions is
// harmless (guacd ignores unknown handshake instructions for older versions). See
// Termix-SSH/Support#567 and #734.
const oldConnect =
" this.sendInstruction(['connect'].concat(connectArgs));";
const oldNameConnect =
" if (protocolVersion === '1_3_0' || protocolVersion === '1_5_0') {\n" +
" this.sendInstruction(['name', this.connectionSettings.name || 'guacamole-lite']);\n" +
" }\n" +
"\n" +
" this.sendInstruction(['connect'].concat(connectArgs));";
const newConnect =
" if (protocolVersion !== '1_0_0') {\n" +
" this.sendInstruction(['name', this.connectionSettings.name || 'guacamole-lite']);\n" +
" }\n" +
"\n" +
" this.sendInstruction(['connect'].concat(connectArgs));";
// Patch 4: answer guacd's dynamic argument requests locally.
// macOS Screen Sharing can request VNC username/password through the
// post-handshake `required`/`require` flow. guacamole-lite forwards those
// instructions to the browser, but Termix already keeps the credentials in the
// server-side token and the browser does not provide an onrequired handler.
const oldSendBuffer =
" this.lastActivity = Date.now();\n" + " this.sendBuffer = '';";
const newSendBuffer =
" this.lastActivity = Date.now();\n" +
" this.sendBuffer = '';\n" +
" this.nextArgumentStreamIndex = 0;";
const oldSendInstructionBlock =
" sendInstruction(instruction) {\n" +
" // convert every element in the instruction array to a string. convert null to an empty string\n" +
" instruction = instruction.map((element) => {\n" +
" if (element === null || element === undefined) {\n" +
" return '';\n" +
" }\n" +
" return String(element);\n" +
" });\n" +
"\n" +
" const instructionString = GuacamoleParser.toInstruction(instruction);\n" +
" this.send(instructionString);\n" +
" }\n";
const newSendInstructionBlock =
oldSendInstructionBlock +
"\n" +
" sendArgumentValue(name, value) {\n" +
" const stream = this.nextArgumentStreamIndex++;\n" +
" this.sendInstruction(['argv', stream, 'text/plain', name]);\n" +
" this.sendInstruction(['blob', stream, Buffer.from(String(value ?? ''), 'utf8').toString('base64')]);\n" +
" this.sendInstruction(['end', stream]);\n" +
" }\n" +
"\n" +
" sendRequiredArguments(params) {\n" +
" params.forEach((name) => {\n" +
" this.sendArgumentValue(name, this.connectionSettings[name]);\n" +
" });\n" +
" }\n";
const oldReadyHandler =
' // Handle "ready" instruction\n' +
" if (opcode === 'ready') {";
const newReadyHandler =
" // Handle dynamic argument requests from guacd\n" +
" if (opcode === 'required' || opcode === 'require') {\n" +
" this.sendRequiredArguments(params);\n" +
" return;\n" +
" }\n" +
"\n" +
oldReadyHandler;
let patched = false;
if (!guacdClientContent.includes("} else if (/^1_\\d+_0$/.test(version)) {")) {
if (guacdClientContent.includes(oldPatchedVersionBlock)) {
guacdClientContent = guacdClientContent.replace(
oldPatchedVersionBlock,
newVersionBlock,
);
} else if (guacdClientContent.includes(oldVersionBlock)) {
guacdClientContent = guacdClientContent.replace(
oldVersionBlock,
newVersionBlock,
);
} else {
missingAnchor("Version check");
}
patched = true;
}
if (!guacdClientContent.includes(newTimezone)) {
if (!guacdClientContent.includes(oldTimezone)) {
missingAnchor("Timezone");
}
guacdClientContent = guacdClientContent.replace(oldTimezone, newTimezone);
patched = true;
}
if (!guacdClientContent.includes(newConnect)) {
if (guacdClientContent.includes(oldNameConnect)) {
guacdClientContent = guacdClientContent.replace(oldNameConnect, newConnect);
} else if (guacdClientContent.includes(oldConnect)) {
guacdClientContent = guacdClientContent.replace(oldConnect, newConnect);
} else {
missingAnchor("Connect");
}
patched = true;
}
if (!guacdClientContent.includes("this.nextArgumentStreamIndex = 0;")) {
if (!guacdClientContent.includes(oldSendBuffer)) {
missingAnchor("Argument stream index");
}
guacdClientContent = guacdClientContent.replace(oldSendBuffer, newSendBuffer);
patched = true;
}
if (!guacdClientContent.includes("sendRequiredArguments(params) {")) {
if (!guacdClientContent.includes(oldSendInstructionBlock)) {
missingAnchor("Required argument helper");
}
guacdClientContent = guacdClientContent.replace(
oldSendInstructionBlock,
newSendInstructionBlock,
);
patched = true;
}
if (
!guacdClientContent.includes("opcode === 'required' || opcode === 'require'")
) {
if (!guacdClientContent.includes(oldReadyHandler)) {
missingAnchor("Required opcode");
}
guacdClientContent = guacdClientContent.replace(
oldReadyHandler,
newReadyHandler,
);
patched = true;
}
// Patch 5: guacamole-lite decrypts token JSON through ASCII/binary strings,
// which corrupts IV/ciphertext bytes and non-ASCII connection settings such as
// RDP/VNC passwords with umlauts. Keep the encrypted fields as Buffers and
// decode the plaintext JSON as UTF-8.
const oldDecryptBlock =
" let encoded = JSON.parse(this.constructor.base64decode(encodedString));\n" +
"\n" +
" encoded.iv = this.constructor.base64decode(encoded.iv);\n" +
" encoded.value = this.constructor.base64decode(encoded.value, 'binary');\n" +
"\n" +
" const decipher = Crypto.createDecipheriv(this.cypher, this.key, encoded.iv);\n" +
"\n" +
" let decrypted = decipher.update(encoded.value, 'binary', 'ascii');\n" +
" decrypted += decipher.final('ascii');";
const oldPartiallyPatchedDecryptBlock =
" let encoded = JSON.parse(this.constructor.base64decode(encodedString));\n" +
"\n" +
" encoded.iv = this.constructor.base64decode(encoded.iv);\n" +
" encoded.value = this.constructor.base64decode(encoded.value, 'binary');\n" +
"\n" +
" const decipher = Crypto.createDecipheriv(this.cypher, this.key, encoded.iv);\n" +
"\n" +
" let decrypted = decipher.update(encoded.value, 'binary', 'utf8');\n" +
" decrypted += decipher.final('utf8');";
const newDecryptBlock =
" const encoded = JSON.parse(Buffer.from(encodedString, 'base64').toString('utf8'));\n" +
"\n" +
" const iv = Buffer.from(encoded.iv, 'base64');\n" +
" const value = Buffer.from(encoded.value, 'base64');\n" +
"\n" +
" const decipher = Crypto.createDecipheriv(this.cypher, this.key, iv);\n" +
"\n" +
" let decrypted = decipher.update(value, undefined, 'utf8');\n" +
" decrypted += decipher.final('utf8');";
if (!cryptContent.includes(newDecryptBlock)) {
if (cryptContent.includes(oldDecryptBlock)) {
cryptContent = cryptContent.replace(oldDecryptBlock, newDecryptBlock);
} else if (cryptContent.includes(oldPartiallyPatchedDecryptBlock)) {
cryptContent = cryptContent.replace(
oldPartiallyPatchedDecryptBlock,
newDecryptBlock,
);
} else {
missingAnchor("UTF-8 token decrypt");
}
patched = true;
}
// Patch 7: drop client-to-guacd input instructions from read-only session-share
// joins. guacd has no native read-only enforcement in the versions this project
// targets, so Termix must gate here. Denylist (not allowlist) on purpose: an
// unrecognized opcode is far more likely to be protocol plumbing (sync, blob,
// clipboard streams) than a new input vector, so failing open is the safer
// default for a client we already control.
const oldSendMessageToGuacd =
" sendMessageToGuacd(message) {\n" +
" this.lastActivity = Date.now();\n" +
" this.logger.log(LOGLEVEL.DEBUG, '[ >>> # ] Received from WS: ```' + message + '```');\n" +
"\n" +
" if (this.guacdClient) {\n" +
" this.guacdClient.send(message, true);\n" +
" }\n" +
" }";
const newSendMessageToGuacd =
" sendMessageToGuacd(message) {\n" +
" this.lastActivity = Date.now();\n" +
" this.logger.log(LOGLEVEL.DEBUG, '[ >>> # ] Received from WS: ```' + message + '```');\n" +
"\n" +
" if (this.isReadOnlyJoin() && this.isInputInstruction(message)) {\n" +
" return;\n" +
" }\n" +
"\n" +
" if (this.guacdClient) {\n" +
" this.guacdClient.send(message, true);\n" +
" }\n" +
" }\n" +
"\n" +
" isReadOnlyJoin() {\n" +
" const connection = this.connectionSettings && this.connectionSettings.connection;\n" +
" return !!(connection && connection.join && connection.readOnly === true);\n" +
" }\n" +
"\n" +
" // Termix-only read-only gate, not part of the vendored library: extracts just\n" +
" // the leading opcode from a raw '<len>.<opcode>,...;' instruction without the\n" +
" // overhead of a full stateful parse.\n" +
" isInputInstruction(message) {\n" +
" const dot = message.indexOf('.');\n" +
" if (dot === -1) return false;\n" +
" const len = parseInt(message.substring(0, dot), 10);\n" +
" if (isNaN(len)) return false;\n" +
" const opcode = message.substring(dot + 1, dot + 1 + len);\n" +
" return ['mouse', 'key', 'touch', 'size'].includes(opcode);\n" +
" }";
if (!clientConnectionContent.includes("isReadOnlyJoin()")) {
if (!clientConnectionContent.includes(oldSendMessageToGuacd)) {
missingAnchor("sendMessageToGuacd");
}
clientConnectionContent = clientConnectionContent.replace(
oldSendMessageToGuacd,
newSendMessageToGuacd,
);
patched = true;
}
// Patch 8: mergeConnectionOptions only preserves `join` across the settings
// merge, dropping Termix's `readOnly` flag before sendMessageToGuacd can see it.
const oldPreserveJoin =
" // For join connections, preserve the join property\n" +
" if (this.connectionSettings.connection.join) {\n" +
" compiledSettings.join = this.connectionSettings.connection.join;\n" +
" }";
const newPreserveJoin =
" // For join connections, preserve the join property\n" +
" if (this.connectionSettings.connection.join) {\n" +
" compiledSettings.join = this.connectionSettings.connection.join;\n" +
" compiledSettings.readOnly = this.connectionSettings.connection.readOnly === true;\n" +
" }";
if (!clientConnectionContent.includes("compiledSettings.readOnly")) {
if (!clientConnectionContent.includes(oldPreserveJoin)) {
missingAnchor("join-preserve");
}
clientConnectionContent = clientConnectionContent.replace(
oldPreserveJoin,
newPreserveJoin,
);
patched = true;
}
// Patch 9: ClientConnection closes malformed-token WebSockets in its
// constructor, but Server.newConnection still called connect() afterwards.
// That dereferenced the absent connection settings and turned one bad token
// into an unhandled rejection that could terminate the backend process.
const oldConnectionSetup =
" newConnection.on('ready', async (clientConnection) => {";
const newConnectionSetup =
" if (!newConnection.connectionSettings || !newConnection.connectionSettings.connection) {\n" +
" return;\n" +
" }\n" +
"\n" +
oldConnectionSetup;
if (!serverContent.includes("!newConnection.connectionSettings.connection")) {
if (!serverContent.includes(oldConnectionSetup)) {
missingAnchor("invalid-token connection guard");
}
serverContent = serverContent.replace(oldConnectionSetup, newConnectionSetup);
patched = true;
}
if (!patched) {
console.log("[patch-guacamole-lite] Already patched");
process.exit(0);
}
fs.writeFileSync(guacdClientPath, guacdClientContent);
fs.writeFileSync(cryptPath, cryptContent);
fs.writeFileSync(clientConnectionPath, clientConnectionContent);
fs.writeFileSync(serverPath, serverContent);
console.log(
"[patch-guacamole-lite] Patched protocol negotiation, name handshake, required arguments, UTF-8 token decrypt, read-only joins, and malformed-token handling",
);
@@ -0,0 +1,29 @@
export const GUACD_DRIVE_PATH_ENV = "GUACD_DRIVE_PATH";
const DEFAULT_DRIVE_ROOT = "/drive";
/**
* Fills in where guacd keeps the files behind RDP drive redirection.
*
* The folder lives on the guacd host, not on Termix's - with the stock
* compose it is a directory in the shared termix-data volume, configured
* through GUACD_DRIVE_PATH. Each user gets a folder of their own underneath:
* a shared drive would show everyone's uploads to everyone else. A host that
* names its own drive-path keeps it.
*/
export function withDriveSettings(
guacConfig: Record<string, unknown>,
userId: string,
env: NodeJS.ProcessEnv = process.env,
): Record<string, unknown> {
if (!guacConfig["enable-drive"] || guacConfig["drive-path"]) {
return guacConfig;
}
const root = (
env[GUACD_DRIVE_PATH_ENV]?.trim() || DEFAULT_DRIVE_ROOT
).replace(/\/+$/, "");
return {
...guacConfig,
"drive-path": `${root}/${userId}`,
"create-drive-path": true,
};
}
@@ -0,0 +1,335 @@
import GuacamoleLite from "guacamole-lite";
import fs from "fs";
import path from "path";
import type { IncomingMessage } from "http";
import type { Duplex } from "stream";
import type {
GuacamoleRecordingMetadata,
GuacamoleTokenService,
TermixGuacMeta,
} from "./token-service.js";
import type { GuacdOptions } from "./guacd-config.js";
import type { RemoteSessions } from "./sessions.js";
import { errorMessage, type RemoteDesktopLogger } from "./log.js";
/** The recordings.writer service, when the session-recording plugin provides it. */
export interface RecordingsWriter {
enabledFor: (hostId: number) => Promise<boolean>;
createFinished: (input: {
hostId: number;
userId: string;
startedAt: string;
endedAt: string;
duration: number | null;
recordingPath: string;
protocol: string;
format: string;
}) => Promise<{ id: number }>;
}
const DATA_DIR = process.env.DATA_DIR || "./db/data";
/** Where the backend reads guacd's recordings from. */
export function recordingsDir(env: NodeJS.ProcessEnv = process.env): string {
return (
env.GUACD_RECORDING_BACKEND_PATH ||
path.join(DATA_DIR, "session_recordings", "guacamole")
);
}
type ClientConnection = {
guacamoleConnectionId?: string;
connectionSettings?: {
connection?: { type?: string; join?: string; readOnly?: boolean };
recording?: GuacamoleRecordingMetadata;
termixMeta?: TermixGuacMeta;
};
close?: () => void;
};
const clientOptions = (
tokens: GuacamoleTokenService,
log: RemoteDesktopLogger,
) => ({
crypt: {
cypher: "AES-256-CBC",
key: tokens.getEncryptionKey(),
},
log: {
level: "ERRORS",
stdLog: (...args: unknown[]) => log.info(args.join(" ")),
errorLog: (...args: unknown[]) => log.error(args.join(" ")),
},
allowedUnencryptedConnectionSettings: {
rdp: ["width", "height", "dpi"],
vnc: ["width", "height"],
telnet: ["width", "height"],
},
connectionDefaultSettings: {
rdp: {
security: "any",
"ignore-cert": true,
"enable-wallpaper": false,
"enable-font-smoothing": true,
"enable-desktop-composition": false,
"disable-audio": false,
"enable-drive": false,
"resize-method": "display-update",
width: 1280,
height: 720,
dpi: 96,
audio: ["audio/L16"],
},
vnc: {
"swap-red-blue": false,
cursor: "remote",
width: 1280,
height: 720,
// macOS Screen Sharing negotiates its VNC security type over several
// round trips (RFB type 30 -> 33/36/2/35) and can fail the first
// attempt; retrying lets guacd's VNC client re-establish instead of
// guacd giving up immediately (Support#1063).
autoretry: 2,
},
telnet: {
"terminal-type": "xterm-256color",
},
},
});
export interface GuacamoleServerDeps {
log: RemoteDesktopLogger;
tokens: GuacamoleTokenService;
sessions: RemoteSessions;
guacd: () => GuacdOptions;
recordings: () => RecordingsWriter | null;
/** A socket event has no request behind it, so the row is written as the session's user. */
asUser: <T>(userId: string, fn: () => Promise<T>) => Promise<T>;
/** Called when a primary session opens; returns what to release on close. */
onOpen?: (hostId: number) => () => void;
}
export interface GuacamoleServer {
handleUpgrade: (
request: IncomingMessage,
socket: Duplex,
head: Buffer,
) => void;
/** Rebuilds the server so a changed guacd address takes effect. */
restart: () => void;
/** Closes a session and everyone watching it. */
endSession: (guacamoleConnectionId: string) => boolean;
close: () => void;
}
/**
* guacamole-lite in noServer mode: core owns the listening socket and hands
* upgrades from /plugin-ws/remote-desktop/display to its WebSocketServer.
*/
export function createGuacamoleServer(
deps: GuacamoleServerDeps,
): GuacamoleServer {
const { log, sessions } = deps;
let server: GuacamoleLite | null = null;
const persistRecording = async (connection: ClientConnection) => {
const recording = connection.connectionSettings?.recording;
if (!recording) return;
const dir = recordingsDir();
const resolvedPath = path.resolve(dir, recording.path);
if (!resolvedPath.startsWith(`${path.resolve(dir)}${path.sep}`)) return;
// guacd may flush or rename the recording just after the socket closes.
for (
let attempt = 0;
attempt < 10 && !fs.existsSync(resolvedPath);
attempt++
) {
await new Promise((resolve) => setTimeout(resolve, 100));
}
if (!fs.existsSync(resolvedPath)) {
log.warn("Guacamole recording file was not found", {
operation: "guac_recording_missing",
hostId: recording.hostId,
path: resolvedPath,
guacdPath: recording.guacdPath ?? dir,
hint:
"guacd writes the recording to guacdPath, the backend reads it from path. " +
"When guacd runs in its own container these must be the same volume: set " +
"GUACD_RECORDING_PATH to guacd's mount point and GUACD_RECORDING_BACKEND_PATH to this one.",
});
return;
}
const writer = await deps.asUser(recording.userId, async () =>
deps.recordings(),
);
if (!writer) {
log.warn(
"Session Recording is off; the recording was kept on disk only",
{
operation: "guac_recording_no_writer",
hostId: recording.hostId,
},
);
return;
}
const endedAt = new Date();
const startedAt = new Date(recording.startedAt);
await deps.asUser(recording.userId, () =>
writer.createFinished({
hostId: recording.hostId,
userId: recording.userId,
startedAt: startedAt.toISOString(),
endedAt: endedAt.toISOString(),
duration: Math.max(
0,
Math.floor((endedAt.getTime() - startedAt.getTime()) / 1000),
),
recordingPath: resolvedPath,
protocol: recording.protocol,
format: "guacamole",
}),
);
};
const build = (): GuacamoleLite => {
// guacamole-lite fills in port 8080 unless it sees a server key, and ws
// refuses a port next to noServer, so the port is cleared explicitly.
const guacamole = new GuacamoleLite(
{ noServer: true, port: undefined },
deps.guacd(),
clientOptions(deps.tokens, log),
);
// It also installs its own SIGTERM and SIGINT handlers. Core decides
// when the process stops and closes this through deactivate instead.
const handlers = guacamole as unknown as {
sigTermHandler?: () => void;
sigIntHandler?: () => void;
};
if (handlers.sigTermHandler) {
process.off("SIGTERM", handlers.sigTermHandler);
}
if (handlers.sigIntHandler) {
process.off("SIGINT", handlers.sigIntHandler);
}
guacamole.on("open", (connection: ClientConnection) => {
const meta = connection.connectionSettings?.termixMeta;
const guacamoleConnectionId = connection.guacamoleConnectionId;
const isJoin = !!connection.connectionSettings?.connection?.join;
if (isJoin || !meta || !guacamoleConnectionId) return;
const release = deps.onOpen?.(meta.hostId);
sessions.opened(meta, guacamoleConnectionId, release ? [release] : []);
});
guacamole.on("close", (connection: ClientConnection) => {
const meta = connection.connectionSettings?.termixMeta;
const isJoin = !!connection.connectionSettings?.connection?.join;
if (!isJoin && meta) sessions.closed(meta.termixConnectId);
persistRecording(connection).catch((error) => {
log.error("Failed to persist Guacamole recording", {
operation: "guac_recording_persist_error",
error: errorMessage(error),
});
});
});
guacamole.on("error", (connection: ClientConnection, error: Error) => {
log.error("Guacamole connection error", {
operation: "guac_connection_error",
type: connection.connectionSettings?.connection?.type,
error: errorMessage(error),
});
});
return guacamole;
};
const closeServer = () => {
if (!server) return;
try {
server.close();
} catch (error) {
log.error("Error closing the Guacamole server", {
error: errorMessage(error),
});
}
server = null;
};
server = build();
return {
handleUpgrade(request, socket, head) {
// The route is public, so the token is the only credential: refuse
// anything forged, edited or expired before guacamole-lite reads it.
const token = new URL(
request.url ?? "/",
"http://localhost",
).searchParams.get("token");
if (!token || !deps.tokens.verifyToken(token)) {
try {
socket.write(
"HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n",
);
} catch {
// The peer may already be gone.
}
socket.destroy();
return;
}
const wss = (
server as unknown as {
webSocketServer?: {
handleUpgrade: (
request: IncomingMessage,
socket: Duplex,
head: Buffer,
done: (ws: unknown) => void,
) => void;
emit: (event: string, ...args: unknown[]) => void;
};
} | null
)?.webSocketServer;
if (!wss) {
socket.destroy();
return;
}
wss.handleUpgrade(request, socket, head, (ws) => {
wss.emit("connection", ws, request);
});
},
restart() {
if (!server) return;
closeServer();
server = build();
},
endSession(guacamoleConnectionId) {
const active = (
server as unknown as {
activeConnections?: Map<unknown, ClientConnection>;
} | null
)?.activeConnections;
if (!active) return false;
let closed = false;
for (const connection of [...active.values()]) {
const join = connection.connectionSettings?.connection?.join;
if (
connection.guacamoleConnectionId === guacamoleConnectionId ||
join === guacamoleConnectionId
) {
connection.close?.();
closed = true;
}
}
return closed;
},
close: closeServer,
};
}
@@ -0,0 +1,86 @@
export type GuacdOptions = {
host: string;
port: number;
};
const DEFAULT_GUACD_OPTIONS: GuacdOptions = {
host: "localhost",
port: 4822,
};
function parsePort(value: string | undefined, fallback: number) {
const port = parseInt(value || "", 10);
return Number.isFinite(port) ? port : fallback;
}
export function parseGuacdUrl(
value: string,
fallback: GuacdOptions = DEFAULT_GUACD_OPTIONS,
): GuacdOptions {
const raw = value.trim();
if (!raw) {
return fallback;
}
if (raw.includes("://")) {
try {
const url = new URL(raw);
return {
host: url.hostname || fallback.host,
port: parsePort(url.port, fallback.port),
};
} catch {
return fallback;
}
}
const bracketedIpv6 = raw.match(/^\[([^\]]+)\](?::(\d+))?$/);
if (bracketedIpv6) {
return {
host: bracketedIpv6[1],
port: parsePort(bracketedIpv6[2], fallback.port),
};
}
const [host, port] = raw.split(":");
return {
host: host || fallback.host,
port: parsePort(port, fallback.port),
};
}
export function getGuacdEnvOptions(): GuacdOptions | null {
if (process.env.GUACD_URL) {
return parseGuacdUrl(process.env.GUACD_URL);
}
if (!process.env.GUACD_HOST && !process.env.GUACD_PORT) {
return null;
}
return {
host: process.env.GUACD_HOST || DEFAULT_GUACD_OPTIONS.host,
port: parsePort(process.env.GUACD_PORT, DEFAULT_GUACD_OPTIONS.port),
};
}
export function resolveGuacdOptions(dbUrl?: string | null): GuacdOptions {
const envOptions = getGuacdEnvOptions();
if (envOptions) {
return envOptions;
}
if (dbUrl) {
return parseGuacdUrl(dbUrl);
}
return DEFAULT_GUACD_OPTIONS;
}
export function formatGuacdOptions(options: GuacdOptions): string {
return `${options.host}:${options.port}`;
}
export function getDefaultGuacdUrl(): string {
return formatGuacdOptions(resolveGuacdOptions());
}
@@ -0,0 +1,236 @@
import type { PluginContext } from "@termix/plugin-sdk/backend";
export type RemoteProtocol = "rdp" | "vnc" | "telnet";
export const PLUGIN_ID = "remote-desktop";
export interface RemoteDesktopHostSettings {
enableRdp: boolean;
enableVnc: boolean;
enableTelnet: boolean;
rdpPort: number;
vncPort: number;
telnetPort: number;
rdpSecurity: string;
rdpIgnoreCert: boolean;
guacamoleConfig: Record<string, unknown>;
enableToolbar: boolean;
}
export const ENABLE_KEY: Record<
RemoteProtocol,
keyof RemoteDesktopHostSettings
> = { rdp: "enableRdp", vnc: "enableVnc", telnet: "enableTelnet" };
export const PORT_KEY: Record<RemoteProtocol, keyof RemoteDesktopHostSettings> =
{ rdp: "rdpPort", vnc: "vncPort", telnet: "telnetPort" };
export const DEFAULT_PORT: Record<RemoteProtocol, number> = {
rdp: 3389,
vnc: 5900,
telnet: 23,
};
export function isRemoteProtocol(value: unknown): value is RemoteProtocol {
return value === "rdp" || value === "vnc" || value === "telnet";
}
function asPort(value: unknown): number | undefined {
const port = typeof value === "string" ? Number(value) : value;
return typeof port === "number" &&
Number.isInteger(port) &&
port > 0 &&
port <= 65535
? port
: undefined;
}
function asObject(value: unknown): Record<string, unknown> | undefined {
let parsed = value;
if (typeof value === "string") {
try {
parsed = JSON.parse(value);
} catch {
return undefined;
}
}
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
? (parsed as Record<string, unknown>)
: undefined;
}
function asBool(value: unknown): boolean | undefined {
if (typeof value === "boolean") return value;
if (value === 1 || value === "1" || value === "true") return true;
if (value === 0 || value === "0" || value === "false") return false;
return undefined;
}
export async function readHostSettings(
ctx: PluginContext,
hostId: number,
): Promise<RemoteDesktopHostSettings> {
const values = await ctx.settings.getAll("host", hostId);
return {
enableRdp: values.enableRdp === true,
enableVnc: values.enableVnc === true,
enableTelnet: values.enableTelnet === true,
rdpPort: asPort(values.rdpPort) ?? DEFAULT_PORT.rdp,
vncPort: asPort(values.vncPort) ?? DEFAULT_PORT.vnc,
telnetPort: asPort(values.telnetPort) ?? DEFAULT_PORT.telnet,
rdpSecurity:
typeof values.rdpSecurity === "string" ? values.rdpSecurity : "",
rdpIgnoreCert: values.rdpIgnoreCert === true,
guacamoleConfig: asObject(values.guacamoleConfig) ?? {},
enableToolbar: values.enableToolbar !== false,
};
}
/**
* Turns an imported host row into this plugin's host settings.
*
* A Termix export carries them under pluginSettings["remote-desktop"]. Older
* exports, other tools and a plugin creating a host (proxmox) use the flat
* fields the host row had before 2.9.0, including the connectionType-only
* shape of hosts from before the per-protocol switches existed.
*/
export function normalizeImportedHost(
raw: Record<string, unknown>,
): Record<string, unknown> | null {
const nested =
asObject(asObject(raw.pluginSettings)?.[PLUGIN_ID]) ??
({} as Record<string, unknown>);
const pick = (key: string, legacy?: string) =>
nested[key] !== undefined
? nested[key]
: raw[key] !== undefined
? raw[key]
: legacy !== undefined
? raw[legacy]
: undefined;
const out: Record<string, unknown> = {};
for (const protocol of ["rdp", "vnc", "telnet"] as const) {
const key = ENABLE_KEY[protocol];
const flag = asBool(pick(key));
if (flag !== undefined) out[key] = flag;
const port = asPort(pick(PORT_KEY[protocol]));
if (port !== undefined) out[PORT_KEY[protocol]] = port;
}
const anyFlag = ["enableRdp", "enableVnc", "enableTelnet"].some(
(key) => out[key] === true,
);
if (!anyFlag && isRemoteProtocol(raw.connectionType)) {
out[ENABLE_KEY[raw.connectionType]] = true;
const port = asPort(raw.port);
if (port !== undefined && out[PORT_KEY[raw.connectionType]] === undefined) {
out[PORT_KEY[raw.connectionType]] = port;
}
}
const security = pick("rdpSecurity", "security");
if (typeof security === "string" && security) out.rdpSecurity = security;
const ignoreCert = asBool(pick("rdpIgnoreCert", "ignoreCert"));
if (ignoreCert !== undefined) out.rdpIgnoreCert = ignoreCert;
const config = asObject(pick("guacamoleConfig"));
if (config && Object.keys(config).length > 0) out.guacamoleConfig = config;
const toolbar = asBool(pick("enableToolbar", "enableTerminalToolbar"));
if (toolbar !== undefined) out.enableToolbar = toolbar;
return Object.keys(out).length > 0 ? out : null;
}
/**
* Registered as "remote-desktop.hostPayloadLegacy": each protocol's login
* in the flat 2.8 shape (rdpUser, rdpDomain, vncAuthType and so on), for
* Termix-Mobile until it reads protocolAuth. Built from the payload's own
* protocolAuth, which core already reduced for a shared recipient.
*/
export function hostPayloadLegacy(
_values: Record<string, unknown>,
host: Record<string, unknown>,
): Record<string, unknown> | null {
const logins = asObject(host.protocolAuth);
if (!logins) return null;
const out: Record<string, unknown> = {};
for (const protocol of ["rdp", "vnc", "telnet"] as const) {
const login = asObject(logins[protocol]);
if (!login) continue;
out[`${protocol}AuthType`] = login.authType;
if (login.credentialId !== undefined) {
out[`${protocol}CredentialId`] = login.credentialId;
}
if (typeof login.username === "string" && login.username) {
out[`${protocol}User`] = login.username;
}
if (typeof login.hasPassword === "boolean") {
const name = protocol.charAt(0).toUpperCase() + protocol.slice(1);
out[`has${name}Password`] = login.hasPassword;
}
const domain = asObject(login.fields)?.domain;
if (protocol === "rdp" && typeof domain === "string" && domain) {
out.rdpDomain = domain;
out.domain = domain;
}
}
return Object.keys(out).length > 0 ? out : null;
}
/** Display field key to the guacd parameter it sets. */
const USER_DEFAULT_PARAMS: Record<string, string> = {
colorDepth: "color-depth",
resizeMethod: "resize-method",
forceLossless: "force-lossless",
enableWallpaper: "enable-wallpaper",
enableFontSmoothing: "enable-font-smoothing",
enableDesktopComposition: "enable-desktop-composition",
disableAudio: "disable-audio",
enablePrinting: "enable-printing",
enableDrive: "enable-drive",
disableCopy: "disable-copy",
disablePaste: "disable-paste",
};
/**
* Display settings as guacd parameters. "inherit" leaves a parameter to the
* host and guacd; on and off become booleans.
*/
export function userDefaultParams(
values: Record<string, unknown>,
): Record<string, unknown> {
const params: Record<string, unknown> = {};
for (const [key, param] of Object.entries(USER_DEFAULT_PARAMS)) {
const value = values[key];
if (value === undefined || value === null || value === "inherit") continue;
if (key === "colorDepth") {
const depth = Number(value);
if (Number.isInteger(depth) && depth > 0) params[param] = depth;
} else if (key === "resizeMethod") {
if (typeof value === "string" && value) params[param] = value;
} else if (value === "on" || value === "off") {
params[param] = value === "on";
}
}
return params;
}
/**
* The display settings a session runs with, as guacd parameters: the host's
* as this user sees them (their own defaults while the host follows its
* defaults), or with no host, this user's defaults.
*/
export async function readDisplayDefaults(
ctx: PluginContext,
userId: string,
hostId?: number,
): Promise<Record<string, unknown>> {
const values: Record<string, unknown> = {};
for (const key of Object.keys(USER_DEFAULT_PARAMS)) {
values[key] =
hostId === undefined
? await ctx.settings.getHostDefault(userId, key)
: await ctx.settings.getHostFor(hostId, userId, key);
}
return userDefaultParams(values);
}
+122
View File
@@ -0,0 +1,122 @@
import type { PluginContext } from "@termix/plugin-sdk/backend";
import { GuacamoleTokenService } from "./token-service.js";
import { RemoteSessions } from "./sessions.js";
import {
createGuacamoleServer,
type RecordingsWriter,
} from "./guacamole-server.js";
import { resolveGuacdOptions } from "./guacd-config.js";
import { registerRoutes } from "./routes.js";
import { createLiveSessions } from "./live-sessions.js";
import {
hostPayloadLegacy,
normalizeImportedHost,
PORT_KEY,
readHostSettings,
} from "./host-settings.js";
import { createLogger } from "./log.js";
export async function activate(ctx: PluginContext) {
const log = createLogger(ctx.log);
const tokens = new GuacamoleTokenService(log);
const sessions = new RemoteSessions();
ctx.disposables.add(() => sessions.clear());
// The admin setting, unless GUACD_URL / GUACD_HOST / GUACD_PORT override it.
let guacdUrl = (await ctx.settings.get<string>("guacdUrl")) ?? "";
const guacd = () => resolveGuacdOptions(guacdUrl || undefined);
const enabled = async () =>
(await ctx.settings.get<boolean>("enabled")) !== false;
// Optional: without session-recording nothing is recorded.
const recordings = (): RecordingsWriter | null => {
const service =
ctx.services.get<Partial<RecordingsWriter>>("recordings.writer");
return typeof service.createFinished === "function" &&
typeof service.enabledFor === "function"
? (service as RecordingsWriter)
: null;
};
// guacamole-lite prints every new connection to stdout.
const consoleLog = console.log;
console.log = (...args: unknown[]) => {
if (
typeof args[0] === "string" &&
args[0].startsWith("New client connection")
)
return;
consoleLog(...args);
};
ctx.disposables.add(() => {
console.log = consoleLog;
});
const server = createGuacamoleServer({
log,
tokens,
sessions,
guacd,
recordings,
asUser: (userId, fn) => ctx.asUser(userId, fn),
onOpen: (hostId) => ctx.hosts.trackSession(hostId),
});
ctx.disposables.add(() => server.close());
ctx.settings.onChange("guacdUrl", (value) => {
guacdUrl = typeof value === "string" ? value : "";
server.restart();
});
registerRoutes(ctx.http.router(), {
ctx,
log,
tokens,
sessions,
guacd,
enabled,
recordings,
});
// guacamole-lite owns its own WebSocketServer, so it takes the raw upgrade.
// Public because a display authenticates with the encrypted, single-use
// token minted by an authenticated route, not with a session JWT.
ctx.ws.upgrade(
"/display",
(request, socket, head) =>
server.handleUpgrade(
request as Parameters<typeof server.handleUpgrade>[0],
socket as Parameters<typeof server.handleUpgrade>[1],
head as Buffer,
),
{ public: true },
);
for (const protocol of ["rdp", "vnc", "telnet"] as const) {
ctx.services.provide(
"sessions.live",
createLiveSessions(protocol, {
sessions,
tokens,
endSession: server.endSession,
}),
{ name: protocol },
);
}
ctx.registry.provide(
"remote-desktop.hostImportNormalizer",
normalizeImportedHost,
);
ctx.registry.provide("remote-desktop.hostPayloadLegacy", hostPayloadLegacy);
// Core's status dot pings these hosts on the protocol's own port.
for (const protocol of ["rdp", "vnc", "telnet"] as const) {
ctx.hosts.status.registerPort(protocol, async (hostId) => {
const settings = await readHostSettings(ctx, hostId);
return settings[PORT_KEY[protocol]] as number;
});
}
}
export async function deactivate() {}
@@ -0,0 +1,15 @@
const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "::1"]);
export function resolveJumpTunnelEndpoint(
guacdHost: string,
tunnelHost = process.env.GUACD_TUNNEL_HOST,
): { bindHost: string; advertisedHost: string } {
if (LOOPBACK_HOSTS.has(guacdHost.toLowerCase())) {
return { bindHost: "127.0.0.1", advertisedHost: "127.0.0.1" };
}
return {
bindHost: "0.0.0.0",
advertisedHost: tunnelHost?.trim() || "termix",
};
}
@@ -0,0 +1,145 @@
import net from "net";
import type { Client } from "ssh2";
import type { PluginContext } from "@termix/plugin-sdk/backend";
import type { RemoteDesktopLogger } from "./log.js";
/** The tunnels plugin's service, as ctx.services.get("tunnels.access") returns it. */
interface TunnelsAccess {
forward(
sourceHostId: number,
target: { targetHost: string; targetPort: number; bindHost?: string },
options?: { name?: string; idleTimeoutMs?: number },
): Promise<{
bindHost: string;
bindPort: number;
close: () => Promise<void>;
}>;
}
/** The tunnels plugin keeps these names for on-demand forwards. */
const TUNNEL_NAME_PREFIX = "web:";
/** A forward nothing is using closes itself after this long. */
const FORWARD_IDLE_TIMEOUT_MS = 10 * 60 * 1000;
export interface JumpTunnel {
port: number;
close: () => void;
}
export interface JumpTunnelRequest {
jumpHosts: Array<{ hostId: number }>;
targetHost: string;
targetPort: number;
bindHost: string;
/** Names the forward, so each session gets one of its own. */
connectId: string;
}
/**
* A local port guacd can dial that reaches the target through the host's
* jump hosts.
*
* One hop is exactly what the tunnels plugin's forward does, so that is used
* when the plugin is running and the user may use it. A chain, or no tunnels
* plugin, goes through ctx.ssh.jumpChain with a listener of our own. Either
* way the caller closes it when the session ends.
*/
export async function openJumpTunnel(
ctx: PluginContext,
log: RemoteDesktopLogger,
request: JumpTunnelRequest,
): Promise<JumpTunnel> {
if (request.jumpHosts.length === 1) {
const viaTunnels = await forwardThroughTunnels(ctx, log, request);
if (viaTunnels) return viaTunnels;
}
return forwardThroughChain(ctx, request);
}
async function forwardThroughTunnels(
ctx: PluginContext,
log: RemoteDesktopLogger,
request: JumpTunnelRequest,
): Promise<JumpTunnel | null> {
const tunnels = ctx.services.get<Partial<TunnelsAccess>>("tunnels.access");
if (typeof tunnels.forward !== "function") return null;
const hopId = request.jumpHosts[0].hostId;
try {
const handle = await tunnels.forward(
hopId,
{
targetHost: request.targetHost,
targetPort: request.targetPort,
bindHost: request.bindHost,
},
{
name: `${TUNNEL_NAME_PREFIX}${hopId}:rd-${request.connectId}`,
idleTimeoutMs: FORWARD_IDLE_TIMEOUT_MS,
},
);
return {
port: handle.bindPort,
close: () => void handle.close().catch(() => undefined),
};
} catch (error) {
// A user without the tunnels permission still reaches their host.
log.warn("Tunnels forward failed, using a jump chain instead", {
operation: "guac_tunnels_forward",
hopId,
error: error instanceof Error ? error.message : String(error),
});
return null;
}
}
async function forwardThroughChain(
ctx: PluginContext,
request: JumpTunnelRequest,
): Promise<JumpTunnel> {
// The chain dials the first hop through that hop's own SOCKS5 settings;
// the target host's proxy config does not apply to it.
const { client, dispose } = await ctx.ssh.jumpChain<Client>(
request.jumpHosts,
);
const sockets = new Set<net.Socket>();
const server = net.createServer((socket) => {
sockets.add(socket);
socket.once("close", () => sockets.delete(socket));
client.forwardOut(
"127.0.0.1",
0,
request.targetHost,
request.targetPort,
(error, stream) => {
if (error) {
socket.destroy();
return;
}
socket.pipe(stream).pipe(socket);
},
);
});
let port: number;
try {
port = await new Promise<number>((resolve, reject) => {
server.once("error", reject);
server.listen(0, request.bindHost, () =>
resolve((server.address() as net.AddressInfo).port),
);
});
} catch (error) {
await dispose();
throw error;
}
return {
port,
close: () => {
for (const socket of sockets) socket.destroy();
server.close();
void dispose();
},
};
}
@@ -0,0 +1,65 @@
import type { GuacamoleTokenService } from "./token-service.js";
import type { RemoteSessions } from "./sessions.js";
import type { RemoteProtocol } from "./host-settings.js";
/** sessions.live v1, as session-sharing reads it (plugins/session-sharing/src/backend/live.ts). */
export interface LiveSessionInfo {
id: string;
userId: string;
hostId: number;
hostName: string;
isConnected: boolean;
createdAt: number;
tabInstanceId: string | null;
}
export interface RemoteLiveSessions {
getSession: (sessionId: string) => LiveSessionInfo | null;
ownerEndSession: (sessionId: string, reason: string) => void;
createViewerToken: (sessionId: string, readOnly: boolean) => string;
}
/**
* One provider per protocol. A session id is guacd's own connection id,
* which is what a viewer's join token names.
*/
export function createLiveSessions(
protocol: RemoteProtocol,
deps: {
sessions: RemoteSessions;
tokens: GuacamoleTokenService;
endSession: (guacamoleConnectionId: string) => boolean;
},
): RemoteLiveSessions {
const find = (sessionId: string) => {
const session = deps.sessions.byGuacamole(sessionId);
return session && session.protocol === protocol ? session : null;
};
return {
getSession(sessionId) {
const session = find(sessionId);
if (!session) return null;
return {
id: session.guacamoleConnectionId,
userId: session.ownerUserId,
hostId: session.hostId,
hostName: session.hostName,
isConnected: true,
createdAt: session.openedAt,
tabInstanceId: session.tabInstanceId,
};
},
ownerEndSession(sessionId) {
if (find(sessionId)) deps.endSession(sessionId);
},
createViewerToken(sessionId, readOnly) {
if (!find(sessionId)) {
throw new Error(`No live ${protocol} session ${sessionId}`);
}
return deps.tokens.createJoinToken(sessionId, readOnly);
},
};
}
+30
View File
@@ -0,0 +1,30 @@
import type { PluginLogger } from "@termix/plugin-sdk/backend";
export type Meta = Record<string, unknown>;
export interface RemoteDesktopLogger {
info: (message: string, meta?: Meta) => void;
warn: (message: string, meta?: Meta) => void;
error: (message: string, meta?: Meta) => void;
}
function withMeta(message: string, meta?: Meta): string {
return meta && Object.keys(meta).length > 0
? `${message} ${JSON.stringify(meta)}`
: message;
}
/** ctx.log with the structured context the old guac logger carried. */
export function createLogger(log: PluginLogger): RemoteDesktopLogger {
return {
info: (message, meta) => log.info(withMeta(message, meta)),
warn: (message, meta) => log.warn(withMeta(message, meta)),
error: (message, meta) => log.error(withMeta(message, meta)),
};
}
export function errorMessage(error: unknown, fallback = "Unknown error") {
if (error instanceof Error && error.message) return error.message;
if (typeof error === "string" && error) return error;
return fallback;
}
@@ -0,0 +1,97 @@
import net from "net";
const RFB_BANNER_LENGTH = 12;
const VNC_AUTH_SECURITY_TYPE = 2;
const MACOS_RFB_BANNER = Buffer.from("RFB 003.889\n", "ascii");
const STANDARD_RFB_BANNER = Buffer.from("RFB 003.008\n", "ascii");
export interface VncCompatibilityProxy {
port: number;
close: () => void;
}
export async function createMacosVncCompatibilityProxy({
targetHost,
targetPort,
bindHost,
}: {
targetHost: string;
targetPort: number;
bindHost: string;
}): Promise<VncCompatibilityProxy> {
const sockets = new Set<net.Socket>();
const server = net.createServer((client) => {
const upstream = net.createConnection(targetPort, targetHost);
sockets.add(client);
sockets.add(upstream);
const forget = (socket: net.Socket) => sockets.delete(socket);
client.once("close", () => forget(client));
upstream.once("close", () => forget(upstream));
client.once("error", () => upstream.destroy());
upstream.once("error", () => client.destroy());
client.pipe(upstream);
let pending = Buffer.alloc(0);
const forwardMacosSecurityTypes = (chunk: Buffer) => {
pending = Buffer.concat([pending, chunk]);
if (pending.length < 1) return;
const securityTypeCount = pending[0];
if (securityTypeCount === 0) {
upstream.off("data", forwardMacosSecurityTypes);
client.write(pending);
upstream.pipe(client);
return;
}
if (pending.length < 1 + securityTypeCount) return;
upstream.off("data", forwardMacosSecurityTypes);
const securityTypes = pending.subarray(1, 1 + securityTypeCount);
if (securityTypes.includes(VNC_AUTH_SECURITY_TYPE)) {
client.write(Buffer.from([1, VNC_AUTH_SECURITY_TYPE]));
} else {
client.write(pending.subarray(0, 1 + securityTypeCount));
}
client.write(pending.subarray(1 + securityTypeCount));
upstream.pipe(client);
};
const forwardServerBanner = (chunk: Buffer) => {
pending = Buffer.concat([pending, chunk]);
if (pending.length < RFB_BANNER_LENGTH) return;
upstream.off("data", forwardServerBanner);
const banner = pending.subarray(0, RFB_BANNER_LENGTH);
const remainder = pending.subarray(RFB_BANNER_LENGTH);
if (banner.equals(MACOS_RFB_BANNER)) {
client.write(STANDARD_RFB_BANNER);
pending = Buffer.alloc(0);
upstream.on("data", forwardMacosSecurityTypes);
if (remainder.length > 0) forwardMacosSecurityTypes(remainder);
} else {
client.write(banner);
client.write(remainder);
upstream.pipe(client);
}
};
upstream.on("data", forwardServerBanner);
});
const port = await new Promise<number>((resolve, reject) => {
server.once("error", reject);
server.listen(0, bindHost, () => {
server.off("error", reject);
resolve((server.address() as net.AddressInfo).port);
});
});
server.unref();
return {
port,
close: () => {
server.close();
for (const socket of sockets) socket.destroy();
},
};
}
@@ -0,0 +1,36 @@
interface RdpSettingsInput {
port: number;
domain?: string;
security?: string;
ignoreCert: boolean;
guacConfig: Record<string, unknown>;
guacdOverrides: Record<string, unknown>;
}
export function buildRdpSettings({
port,
domain,
security,
ignoreCert,
guacConfig,
guacdOverrides,
}: RdpSettingsInput): Record<string, unknown> {
return {
...guacConfig,
port,
domain,
...(security === undefined ? {} : { security }),
"ignore-cert": ignoreCert,
...guacdOverrides,
};
}
export function resolveRdpDomain(
authType: string | null,
promptedDomain: unknown,
storedDomain: string,
): string {
return authType === "none" && typeof promptedDomain === "string"
? promptedDomain
: storedDomain;
}
@@ -0,0 +1,22 @@
/**
* Merges Termix's recording bookkeeping into a host's guacd settings.
*
* Location and filename are not the host's to choose: recordings are indexed by
* them for playback, and the backend refuses to read anything outside its
* recordings directory. What a recording *contains* is a host-level decision, so
* those flags are only defaulted, never overwritten.
*/
export function withRecordingSettings(
guacConfig: Record<string, unknown>,
recordingPath: string,
recordingName: string,
): Record<string, unknown> {
return {
...guacConfig,
"recording-path": recordingPath,
"recording-name": recordingName,
"create-recording-path": true,
"recording-exclude-output": guacConfig["recording-exclude-output"] ?? false,
"recording-include-keys": guacConfig["recording-include-keys"] ?? true,
};
}
@@ -0,0 +1,655 @@
import crypto from "crypto";
import net from "net";
import path from "path";
import type { Request, Response, Router } from "express";
import type { PluginContext } from "@termix/plugin-sdk/backend";
import {
isServerOwnedSetting,
type GuacamoleTokenService,
} from "./token-service.js";
import type { GuacdOptions } from "./guacd-config.js";
import type { RemoteSessions } from "./sessions.js";
import type { RecordingsWriter } from "./guacamole-server.js";
import { recordingsDir } from "./guacamole-server.js";
import { withRecordingSettings } from "./recording-settings.js";
import { withDriveSettings } from "./drive-settings.js";
import { resolveJumpTunnelEndpoint } from "./jump-tunnel-endpoint.js";
import { buildRdpSettings, resolveRdpDomain } from "./rdp-settings.js";
import { createMacosVncCompatibilityProxy } from "./macos-vnc-proxy.js";
import { openJumpTunnel } from "./jump-tunnel.js";
import {
DEFAULT_PORT,
ENABLE_KEY,
PORT_KEY,
isRemoteProtocol,
readHostSettings,
readDisplayDefaults,
type RemoteProtocol,
} from "./host-settings.js";
import { errorMessage, type RemoteDesktopLogger } from "./log.js";
export interface RouteDeps {
ctx: PluginContext;
log: RemoteDesktopLogger;
tokens: GuacamoleTokenService;
sessions: RemoteSessions;
guacd: () => GuacdOptions;
enabled: () => Promise<boolean>;
recordings: () => RecordingsWriter | null;
}
function actor(ctx: PluginContext, res: Response): string | null {
const userId = ctx.currentActor();
if (!userId) res.status(401).json({ error: "Authentication required" });
return userId ?? null;
}
function probeGuacd({ host, port }: GuacdOptions): Promise<boolean> {
return new Promise((resolve) => {
const socket = new net.Socket();
socket.setTimeout(3000);
const done = (ok: boolean) => {
socket.destroy();
resolve(ok);
};
socket.once("connect", () => done(true));
socket.once("timeout", () => done(false));
socket.once("error", () => done(false));
socket.connect(port, host);
});
}
/** guacd reads these settings as given; the editor stores "auto" for its default. */
function cleanGuacConfig(raw: Record<string, unknown>): {
config: Record<string, unknown>;
guacdOverrides: { guacdHost?: string; guacdPort?: number };
} {
const config: Record<string, unknown> = {};
for (const [key, value] of Object.entries(raw)) {
if (value !== "auto") config[key] = value;
}
const guacdHost =
typeof config["guacd-hostname"] === "string" && config["guacd-hostname"]
? (config["guacd-hostname"] as string)
: undefined;
const guacdPort = config["guacd-port"]
? parseInt(String(config["guacd-port"]), 10) || undefined
: undefined;
delete config["guacd-hostname"];
delete config["guacd-port"];
if (config.dpi != null) {
const dpi = parseInt(String(config.dpi), 10);
config.dpi = Number.isFinite(dpi) && dpi > 0 ? dpi : undefined;
}
return {
config,
guacdOverrides: {
...(guacdHost ? { guacdHost } : {}),
...(guacdPort ? { guacdPort } : {}),
},
};
}
export function registerRoutes(router: Router, deps: RouteDeps): void {
const { ctx, log, tokens, sessions } = deps;
const refuseWhenOff = async (res: Response): Promise<boolean> => {
if (await deps.enabled()) return false;
res.status(403).json({ error: "Remote Desktop is turned off" });
return true;
};
/**
* @openapi
* /plugin-api/remote-desktop/connection/{connectId}:
* get:
* summary: Look up guacd's id for a session
* description: Returns the guacamole connection id for a connect request, once guacd has opened it. Only the session's owner gets an answer.
* tags:
* - Remote Desktop
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: connectId
* required: true
* schema:
* type: string
* responses:
* 200:
* description: The id, or null while guacd has not opened the session.
* 401:
* description: Not signed in.
*/
router.get("/connection/:connectId", (req: Request, res: Response) => {
const userId = actor(ctx, res);
if (!userId) return;
const session = sessions.byConnect(String(req.params.connectId));
res.json({
guacamoleConnectionId:
session?.ownerUserId === userId ? session.guacamoleConnectionId : null,
});
});
/**
* @openapi
* /plugin-api/remote-desktop/token:
* post:
* summary: Mint a connection token for an address
* description: Creates an encrypted guacamole-lite token for connection details the caller supplies, used by Quick Connect. No saved host is involved.
* tags:
* - Remote Desktop
* security:
* - bearerAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - type
* - hostname
* properties:
* type:
* type: string
* enum: [rdp, vnc, telnet]
* hostname:
* type: string
* port:
* type: integer
* username:
* type: string
* password:
* type: string
* domain:
* type: string
* responses:
* 200:
* description: The encrypted token.
* 400:
* description: Missing or invalid fields.
* 403:
* description: Remote Desktop is turned off.
* 500:
* description: Server error.
*/
router.post("/token", async (req: Request, res: Response) => {
try {
if (await refuseWhenOff(res)) return;
const { type, hostname, port, username, password, domain, ...raw } =
(req.body ?? {}) as Record<string, unknown>;
if (!type || !hostname) {
return res
.status(400)
.json({ error: "Missing required fields: type and hostname" });
}
if (!isRemoteProtocol(type)) {
return res.status(400).json({
error: "Invalid connection type. Must be rdp, vnc, or telnet",
});
}
const options: Record<string, unknown> = {};
for (const [key, value] of Object.entries(raw)) {
// A quick connect names a host, never where guacd is or what paths
// it writes to: those would let any user aim guacd anywhere.
if (isServerOwnedSetting(key)) continue;
if (value !== "auto") options[key] = value;
}
const host = String(hostname);
const user = username ? String(username) : "";
const pass = password ? String(password) : "";
const targetPort = Number(port) || DEFAULT_PORT[type];
const userId = ctx.currentActor();
if (type === "rdp" && userId) {
for (const [key, value] of Object.entries(
await readDisplayDefaults(ctx, userId),
)) {
if (isServerOwnedSetting(key)) continue;
if (options[key] === undefined) options[key] = value;
}
}
let token: string;
if (type === "rdp") {
token = tokens.createRdpToken(host, user, pass, {
port: targetPort,
domain: domain ? String(domain) : undefined,
...options,
});
} else if (type === "vnc") {
token = tokens.createVncToken(host, user || undefined, pass, {
port: targetPort,
...options,
});
} else {
token = tokens.createTelnetToken(host, user, pass, {
port: targetPort,
...options,
});
}
res.json({ token });
} catch (error) {
log.error("Failed to generate a connection token", {
operation: "guac_token_error",
error: errorMessage(error),
});
res.status(500).json({ error: "Failed to generate connection token" });
}
});
/**
* @openapi
* /plugin-api/remote-desktop/connect-host/{hostId}:
* post:
* summary: Mint a connection token for a saved host
* description: Resolves the host's RDP, VNC or Telnet login for the caller (a shared recipient gets only what was shared with them), opens a jump tunnel when the host has jump hosts, and returns an encrypted guacamole-lite token.
* tags:
* - Remote Desktop
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: hostId
* required: true
* schema:
* type: integer
* requestBody:
* content:
* application/json:
* schema:
* type: object
* properties:
* protocol:
* type: string
* enum: [rdp, vnc, telnet]
* promptedUsername:
* type: string
* promptedPassword:
* type: string
* promptedDomain:
* type: string
* tabInstanceId:
* type: string
* responses:
* 200:
* description: The token and the id to look the session up by.
* 400:
* description: Invalid host id, or the protocol is off for this host.
* 403:
* description: Remote Desktop is turned off.
* 404:
* description: Host not found or no access.
* 500:
* description: The tunnel or the token could not be set up.
*/
router.post("/connect-host/:hostId", async (req: Request, res: Response) => {
try {
const userId = actor(ctx, res);
if (!userId) return;
if (await refuseWhenOff(res)) return;
const hostId = Number.parseInt(String(req.params.hostId), 10);
if (!hostId) return res.status(400).json({ error: "Invalid host ID" });
const body = (req.body ?? {}) as Record<string, unknown>;
const settings = await readHostSettings(ctx, hostId);
const requested = body.protocol;
const protocol: RemoteProtocol | undefined = isRemoteProtocol(requested)
? requested
: (["rdp", "vnc", "telnet"] as const).find(
(candidate) => settings[ENABLE_KEY[candidate]],
);
if (!protocol) {
return res
.status(400)
.json({ error: "Remote Desktop is not enabled for this host." });
}
const target = await ctx.credentials.resolveHostProtocol(
hostId,
protocol,
);
if (!target) {
return res.status(404).json({ error: "Host not found" });
}
if (!settings[ENABLE_KEY[protocol]]) {
return res.status(400).json({
error: `${protocol.toUpperCase()} is not enabled for this host.`,
});
}
// The display settings sit under whatever the host's guacd settings say.
const { config, guacdOverrides } = cleanGuacConfig({
...(protocol === "rdp"
? await readDisplayDefaults(ctx, userId, hostId)
: {}),
...settings.guacamoleConfig,
});
let guacConfig = config;
const promptForLogin =
protocol === "rdp" && target.auth.authType === "none";
const username = promptForLogin
? String(body.promptedUsername || "")
: target.auth.username;
const password = promptForLogin
? String(body.promptedPassword || "")
: target.auth.password;
const domain = resolveRdpDomain(
target.auth.authType,
body.promptedDomain,
target.auth.fields.domain ?? "",
);
let hostname = target.host.ip;
let port =
(settings[PORT_KEY[protocol]] as number) || DEFAULT_PORT[protocol];
const termixConnectId = crypto.randomUUID();
const cleanups: Array<() => void> = [];
const jumpHosts = target.host.jumpHosts;
const needsVncProxy = protocol === "vnc" && !username;
const endpoint =
jumpHosts.length > 0 || needsVncProxy
? resolveJumpTunnelEndpoint(
guacdOverrides.guacdHost || deps.guacd().host,
)
: null;
try {
if (jumpHosts.length > 0 && endpoint) {
const tunnel = await openJumpTunnel(ctx, log, {
jumpHosts,
targetHost: hostname,
targetPort: port,
bindHost: endpoint.bindHost,
connectId: termixConnectId,
});
cleanups.push(tunnel.close);
hostname = endpoint.advertisedHost;
port = tunnel.port;
}
if (needsVncProxy && endpoint) {
const proxy = await createMacosVncCompatibilityProxy({
targetHost: hostname,
targetPort: port,
bindHost: endpoint.bindHost,
});
cleanups.push(proxy.close);
hostname = endpoint.advertisedHost;
port = proxy.port;
}
} catch (error) {
for (const cleanup of cleanups) cleanup();
log.error("Failed to reach the host through its jump hosts", {
operation: "guac_ssh_tunnel_error",
hostId,
error: errorMessage(error),
});
return res
.status(500)
.json({ error: "Failed to establish SSH tunnel to remote host" });
}
sessions.park(termixConnectId, cleanups);
const recordings = deps.recordings();
const recordingEnabled =
protocol !== "vnc" &&
!!recordings &&
(await recordings.enabledFor(hostId));
const recordingName = `${crypto.randomUUID()}.guac`;
const guacdRecordingPath =
process.env.GUACD_RECORDING_PATH ||
process.env.GUACD_RECORDING_BACKEND_PATH ||
path.resolve(recordingsDir());
const recording = recordingEnabled
? {
hostId,
userId,
protocol,
path: recordingName,
guacdPath: guacdRecordingPath,
startedAt: new Date().toISOString(),
}
: undefined;
if (recordingEnabled) {
guacConfig = withRecordingSettings(
guacConfig,
guacdRecordingPath,
recordingName,
);
}
const termixMeta = {
termixConnectId,
hostId,
hostName: target.host.name || target.host.ip,
ownerUserId: userId,
protocol,
tabInstanceId:
typeof body.tabInstanceId === "string" ? body.tabInstanceId : null,
};
let token: string;
if (protocol === "rdp") {
token = tokens.createRdpToken(
hostname,
username,
password,
buildRdpSettings({
port,
domain,
security: settings.rdpSecurity || undefined,
ignoreCert: settings.rdpIgnoreCert,
guacConfig: withDriveSettings(guacConfig, userId),
guacdOverrides,
}),
recording,
termixMeta,
);
} else if (protocol === "vnc") {
token = tokens.createVncToken(
hostname,
username || undefined,
password,
{ port, ...guacConfig, ...guacdOverrides },
recording,
termixMeta,
);
} else {
token = tokens.createTelnetToken(
hostname,
username,
password,
{ port, ...guacConfig, ...guacdOverrides },
recording,
termixMeta,
);
}
await ctx.audit.record({
action: `${protocol}_connect`,
resourceType: "host",
resourceId: String(hostId),
resourceName: `${hostname}:${port}`,
success: true,
});
res.json({ token, termixConnectId, guacamoleConnectionId: null });
} catch (error) {
log.error("Failed to generate a connection token for a host", {
operation: "guac_host_token_error",
error: errorMessage(error),
});
res.status(500).json({ error: "Failed to generate connection token" });
}
});
/**
* @openapi
* /plugin-api/remote-desktop/status:
* get:
* summary: Remote Desktop status
* description: Whether Remote Desktop is turned on, and whether guacd answers at the configured address.
* tags:
* - Remote Desktop
* security:
* - bearerAuth: []
* parameters:
* - in: query
* name: probe
* schema:
* type: string
* description: Set to 0 to skip dialing guacd.
* - in: query
* name: hostId
* schema:
* type: integer
* description: Saved host whose guacd settings should be checked; requires protocol and access to that host.
* - in: query
* name: protocol
* schema:
* type: string
* enum: [rdp, vnc, telnet]
* responses:
* 200:
* description: The status.
* 500:
* description: The check failed.
*/
router.get("/status", async (req: Request, res: Response) => {
try {
const enabled = await deps.enabled();
let guacd = deps.guacd();
if (req.query.hostId !== undefined) {
const userId = actor(ctx, res);
if (!userId) return;
const hostId = Number(req.query.hostId);
const protocol = req.query.protocol;
if (
!Number.isSafeInteger(hostId) ||
hostId <= 0 ||
!isRemoteProtocol(protocol)
) {
return res.status(400).json({ error: "Invalid host ID or protocol" });
}
const target = await ctx.credentials.resolveHostProtocol(
hostId,
protocol,
);
if (!target) return res.status(404).json({ error: "Host not found" });
const settings = await readHostSettings(ctx, hostId);
if (!settings[ENABLE_KEY[protocol]]) {
return res
.status(400)
.json({ error: "Protocol is not enabled for this host" });
}
const { guacdOverrides } = cleanGuacConfig({
...(protocol === "rdp"
? await readDisplayDefaults(ctx, userId, hostId)
: {}),
...settings.guacamoleConfig,
});
guacd = {
host: guacdOverrides.guacdHost || guacd.host,
port: guacdOverrides.guacdPort || guacd.port,
};
}
const probe = enabled && req.query.probe !== "0";
const reachable = probe ? await probeGuacd(guacd) : false;
res.json({
enabled,
guacd: {
host: guacd.host,
port: guacd.port,
status: reachable ? "connected" : "disconnected",
},
websocket: {
path: "/plugin-ws/remote-desktop/display",
status: "running",
},
});
} catch (error) {
log.error("Failed to check Remote Desktop status", {
operation: "guac_status_error",
error: errorMessage(error),
});
res.status(500).json({ error: "Failed to check status" });
}
});
/**
* @openapi
* /plugin-api/remote-desktop/native-rdp:
* get:
* summary: Whether the native RDP client can be opened
* description: True only in the Windows desktop app.
* tags:
* - Remote Desktop
* security:
* - bearerAuth: []
* responses:
* 200:
* description: Availability.
* post:
* summary: Open the native RDP client
* description: Opens the operating system's own RDP client (mstsc) for an address. The password is never passed; the client asks for it. Windows desktop app only.
* tags:
* - Remote Desktop
* security:
* - bearerAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - host
* properties:
* host:
* type: string
* port:
* type: integer
* username:
* type: string
* domain:
* type: string
* responses:
* 200:
* description: Whether the client opened.
* 400:
* description: Missing host.
* 404:
* description: Not running in the desktop app.
*/
router.get("/native-rdp", (_req: Request, res: Response) => {
res.json({
available: ctx.desktop.available() && process.platform === "win32",
});
});
router.post("/native-rdp", async (req: Request, res: Response) => {
if (!ctx.desktop.available()) {
return res.status(404).json({ error: "Only in the desktop app" });
}
const body = (req.body ?? {}) as Record<string, unknown>;
if (typeof body.host !== "string" || !body.host) {
return res.status(400).json({ error: "Missing host" });
}
try {
const result = await ctx.desktop.launchExternalClient({
protocol: "rdp",
host: body.host,
port: Number(body.port) || DEFAULT_PORT.rdp,
username: typeof body.username === "string" ? body.username : undefined,
domain: typeof body.domain === "string" ? body.domain : undefined,
});
res.json(result);
} catch (error) {
res.json({ success: false, error: errorMessage(error) });
}
});
}
@@ -0,0 +1,116 @@
import type { TermixGuacMeta } from "./token-service.js";
export interface RemoteSession {
guacamoleConnectionId: string;
termixConnectId: string;
hostId: number;
hostName: string;
ownerUserId: string;
protocol: TermixGuacMeta["protocol"];
tabInstanceId: string | null;
openedAt: number;
}
type Cleanup = () => void;
/** How long a minted token's tunnel waits for guacd to connect through it. */
export const PENDING_TIMEOUT_MS = 5 * 60 * 1000;
/**
* Live guacd sessions and what each one holds open.
*
* A connect request builds its jump tunnel or VNC proxy before guacd dials
* in, so those resources are parked under the request's connect id and move
* onto the session when it opens. They are released when the session
* closes, when guacd never shows up, or when the plugin stops.
*/
export class RemoteSessions {
private readonly pending = new Map<
string,
{ cleanups: Cleanup[]; timer: ReturnType<typeof setTimeout> }
>();
private readonly byConnectId = new Map<string, RemoteSession>();
private readonly byGuacamoleId = new Map<string, RemoteSession>();
private readonly cleanupsByConnectId = new Map<string, Cleanup[]>();
constructor(private readonly pendingTimeoutMs = PENDING_TIMEOUT_MS) {}
/** Resources for a connection that guacd has not opened yet. */
park(termixConnectId: string, cleanups: Cleanup[]): void {
if (cleanups.length === 0) return;
const timer = setTimeout(() => {
this.pending.delete(termixConnectId);
run(cleanups);
}, this.pendingTimeoutMs);
timer.unref?.();
this.pending.set(termixConnectId, { cleanups, timer });
}
opened(
meta: TermixGuacMeta,
guacamoleConnectionId: string,
extra: Cleanup[] = [],
): RemoteSession {
const session: RemoteSession = {
guacamoleConnectionId,
termixConnectId: meta.termixConnectId,
hostId: meta.hostId,
hostName: meta.hostName,
ownerUserId: meta.ownerUserId,
protocol: meta.protocol,
tabInstanceId: meta.tabInstanceId ?? null,
openedAt: Date.now(),
};
const parked = this.pending.get(meta.termixConnectId);
if (parked) {
clearTimeout(parked.timer);
this.pending.delete(meta.termixConnectId);
}
this.cleanupsByConnectId.set(meta.termixConnectId, [
...(parked?.cleanups ?? []),
...extra,
]);
this.byConnectId.set(meta.termixConnectId, session);
this.byGuacamoleId.set(guacamoleConnectionId, session);
return session;
}
closed(termixConnectId: string): void {
const session = this.byConnectId.get(termixConnectId);
if (session) this.byGuacamoleId.delete(session.guacamoleConnectionId);
this.byConnectId.delete(termixConnectId);
run(this.cleanupsByConnectId.get(termixConnectId) ?? []);
this.cleanupsByConnectId.delete(termixConnectId);
}
byConnect(termixConnectId: string): RemoteSession | null {
return this.byConnectId.get(termixConnectId) ?? null;
}
byGuacamole(guacamoleConnectionId: string): RemoteSession | null {
return this.byGuacamoleId.get(guacamoleConnectionId) ?? null;
}
/** Releases everything, for deactivate. */
clear(): void {
for (const { cleanups, timer } of this.pending.values()) {
clearTimeout(timer);
run(cleanups);
}
this.pending.clear();
for (const cleanups of this.cleanupsByConnectId.values()) run(cleanups);
this.cleanupsByConnectId.clear();
this.byConnectId.clear();
this.byGuacamoleId.clear();
}
}
function run(cleanups: Cleanup[]): void {
for (const cleanup of cleanups) {
try {
cleanup();
} catch {
// One failed close must not keep the rest open.
}
}
}
@@ -0,0 +1,318 @@
import crypto from "crypto";
interface TokenLogger {
warn: (message: string, meta?: Record<string, unknown>) => void;
error: (message: string, meta?: Record<string, unknown>) => void;
}
export interface GuacamoleConnectionSettings {
type?: "rdp" | "vnc" | "telnet";
join?: string;
readOnly?: boolean;
guacdHost?: string;
guacdPort?: number;
settings: {
hostname?: string;
port?: number;
username?: string;
password?: string;
domain?: string;
width?: number;
height?: number;
dpi?: number;
security?: string;
"ignore-cert"?: boolean;
"disable-auth"?: boolean;
"enable-wallpaper"?: boolean;
"enable-drive"?: boolean;
"drive-path"?: string;
"create-drive-path"?: boolean;
"swap-red-blue"?: boolean;
cursor?: string;
"terminal-type"?: string;
[key: string]: unknown;
};
}
export interface TermixGuacMeta {
termixConnectId: string;
hostId: number;
hostName: string;
ownerUserId: string;
protocol: "rdp" | "vnc" | "telnet";
tabInstanceId: string | null;
}
export interface GuacamoleToken {
connection: GuacamoleConnectionSettings;
recording?: GuacamoleRecordingMetadata;
termixMeta?: TermixGuacMeta;
/** When the token stops opening displays, in ms since the epoch. */
exp?: number;
}
export interface GuacamoleRecordingMetadata {
hostId: number;
userId: string;
protocol: "rdp" | "vnc" | "telnet";
path: string;
/** Directory guacd was told to write into; differs from the backend's view
* when guacd runs in its own container. */
guacdPath?: string;
startedAt: string;
}
const CIPHER = "aes-256-cbc";
const KEY_LENGTH = 32;
/**
* How long a token opens a display for. Short, because a token for a saved
* host carries its password and ends up in the display URL.
*/
export const TOKEN_TTL_MS = 5 * 60 * 1000;
/**
* Settings the server decides, never the caller: where guacd is, and every
* path guacd writes or reads on its own filesystem.
*/
export function isServerOwnedSetting(key: string): boolean {
return (
key.startsWith("guacd") ||
key.endsWith("-path") ||
key.startsWith("create-") ||
key.startsWith("recording-") ||
key.startsWith("typescript-")
);
}
const silentLogger: TokenLogger = { warn: () => {}, error: () => {} };
/**
* Encrypts the connection tokens guacamole-lite decrypts. Built in activate;
* the key is derived from the environment, so a restart keeps it.
*/
export class GuacamoleTokenService {
private encryptionKey: Buffer;
private macKey: Buffer;
constructor(
private readonly log: TokenLogger = silentLogger,
private readonly now: () => number = Date.now,
) {
this.encryptionKey = this.initializeKey();
this.macKey = crypto
.createHmac("sha256", this.encryptionKey)
.update("termix-guacamole-token-mac")
.digest();
}
private initializeKey(): Buffer {
const existingKey = process.env.GUACAMOLE_ENCRYPTION_KEY;
if (existingKey) {
if (existingKey.length === 64 && /^[0-9a-fA-F]+$/.test(existingKey)) {
return Buffer.from(existingKey, "hex");
}
if (existingKey.length === KEY_LENGTH) {
return Buffer.from(existingKey, "utf8");
}
}
// A token lives for seconds between issue and connect, so a fresh key
// per boot is enough and nothing is derived from core's own secrets.
return crypto.randomBytes(KEY_LENGTH);
}
getEncryptionKey(): Buffer {
return this.encryptionKey;
}
/**
* guacamole-lite only knows AES-CBC, which does not notice tampering, so
* the ciphertext also carries an HMAC that verifyToken checks before
* guacamole-lite ever sees the token.
*/
encryptToken(tokenObject: GuacamoleToken): string {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(CIPHER, this.encryptionKey, iv);
let encrypted = cipher.update(
JSON.stringify({ ...tokenObject, exp: this.now() + TOKEN_TTL_MS }),
"utf8",
"base64",
);
encrypted += cipher.final("base64");
const data = {
iv: iv.toString("base64"),
value: encrypted,
mac: this.mac(iv.toString("base64"), encrypted),
};
return Buffer.from(JSON.stringify(data)).toString("base64");
}
private mac(iv: string, value: string): string {
return crypto
.createHmac("sha256", this.macKey)
.update(`${iv}.${value}`)
.digest("base64");
}
/**
* The token if it is one this server minted, untouched and not expired,
* else null. The gate in front of guacamole-lite.
*/
verifyToken(token: string): GuacamoleToken | null {
let data: { iv?: unknown; value?: unknown; mac?: unknown };
try {
data = JSON.parse(Buffer.from(token, "base64").toString("utf8"));
} catch {
return null;
}
if (
typeof data?.iv !== "string" ||
typeof data.value !== "string" ||
typeof data.mac !== "string"
) {
return null;
}
const expected = Buffer.from(this.mac(data.iv, data.value), "base64");
const given = Buffer.from(data.mac, "base64");
if (
expected.length !== given.length ||
!crypto.timingSafeEqual(expected, given)
) {
return null;
}
const decoded = this.decryptToken(token);
if (!decoded || typeof decoded.exp !== "number") return null;
return decoded.exp > this.now() ? decoded : null;
}
decryptToken(token: string): GuacamoleToken | null {
try {
const data = JSON.parse(Buffer.from(token, "base64").toString("utf8"));
const iv = Buffer.from(data.iv, "base64");
const decipher = crypto.createDecipheriv(CIPHER, this.encryptionKey, iv);
let decrypted = decipher.update(data.value, "base64", "utf8");
decrypted += decipher.final("utf8");
return JSON.parse(decrypted) as GuacamoleToken;
} catch (error) {
this.log.error("Failed to decrypt guacamole token", {
operation: "guac_token_decrypt_error",
error: error instanceof Error ? error.message : String(error),
});
return null;
}
}
createRdpToken(
hostname: string,
username: string,
password: string,
options: Partial<GuacamoleConnectionSettings["settings"]> & {
guacdHost?: string;
guacdPort?: number;
} = {},
recording?: GuacamoleRecordingMetadata,
termixMeta?: TermixGuacMeta,
): string {
const { guacdHost, guacdPort, ...settingsOptions } = options;
const token: GuacamoleToken = {
connection: {
type: "rdp",
...(guacdHost ? { guacdHost } : {}),
...(guacdPort ? { guacdPort } : {}),
settings: {
hostname,
...(username ? { username } : {}),
...(password ? { password } : {}),
port: 3389,
"ignore-cert": true,
...(!username && !password ? { "disable-auth": true } : {}),
...settingsOptions,
},
},
recording,
termixMeta,
};
return this.encryptToken(token);
}
createVncToken(
hostname: string,
username?: string,
password?: string,
options: Partial<GuacamoleConnectionSettings["settings"]> & {
guacdHost?: string;
guacdPort?: number;
} = {},
recording?: GuacamoleRecordingMetadata,
termixMeta?: TermixGuacMeta,
): string {
const { guacdHost, guacdPort, ...settingsOptions } = options;
const token: GuacamoleToken = {
connection: {
type: "vnc",
...(guacdHost ? { guacdHost } : {}),
...(guacdPort ? { guacdPort } : {}),
settings: {
hostname,
...(username ? { username } : {}),
password,
port: 5900,
...settingsOptions,
},
},
recording,
termixMeta,
};
return this.encryptToken(token);
}
createTelnetToken(
hostname: string,
username?: string,
password?: string,
options: Partial<GuacamoleConnectionSettings["settings"]> & {
guacdHost?: string;
guacdPort?: number;
} = {},
recording?: GuacamoleRecordingMetadata,
termixMeta?: TermixGuacMeta,
): string {
const { guacdHost, guacdPort, ...settingsOptions } = options;
const token: GuacamoleToken = {
connection: {
type: "telnet",
...(guacdHost ? { guacdHost } : {}),
...(guacdPort ? { guacdPort } : {}),
settings: {
hostname,
username,
password,
port: 23,
...settingsOptions,
},
},
recording,
termixMeta,
};
return this.encryptToken(token);
}
// join tokens never carry recording params - only the primary connection's
// token should write recording-path/recording-name to guacd.
createJoinToken(guacamoleConnectionId: string, readOnly: boolean): string {
const token: GuacamoleToken = {
connection: {
join: guacamoleConnectionId,
readOnly,
settings: {},
},
};
return this.encryptToken(token);
}
}
@@ -0,0 +1,653 @@
import { watchGuacamoleConnectionId } from "./guacamole-session-id";
import React, {
useState,
useEffect,
useRef,
useCallback,
useImperativeHandle,
} from "react";
import type Guacamole from "guacamole-common-js";
import { toast } from "sonner";
import {
GuacamoleDisplay,
type GuacamoleDisplayHandle,
type GuacamoleTouchMode,
} from "./GuacamoleDisplay.tsx";
import { getGuacamoleTokenFromHost, getGuacdStatus } from "./guacamole-api";
import { readConfiguredDimension } from "./guacamole-display-size.ts";
import { getGuacamoleToken } from "./guacamole-api";
import {
useHost,
useTranslation,
useConnectionRetry,
logActivity,
} from "@termix/plugin-sdk/frontend";
import {
Button,
ConnectionLogProvider,
ConnectionScreen,
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
Input,
PasswordInput,
isElectron,
linkedServerUrl,
resolveConnectionOrigin,
useConnectionLog,
type ConnectionOrigin,
} from "@termix/plugin-sdk/ui";
import type { GuacamoleConfig } from "./guacamole-config";
import {
errorMessage,
hostRemoteOptions,
type Protocol,
rdpDomain,
type RemoteHostLogin,
} from "./host-remote";
import { GuacamoleToolbar } from "./GuacamoleToolbar.tsx";
import { GuacamoleFileBrowser } from "./GuacamoleFileBrowser.tsx";
import { describeUploadError } from "./guacamole-filesystem.ts";
import { canUploadToRdpDrive } from "./guacamole-file-drop.ts";
import { needsRdpCredentialPrompt } from "./rdp-credential-prompt";
interface GuacamoleAppProps {
hostId?: string;
tabId?: string;
protocol?: "rdp" | "vnc" | "telnet";
isVisible?: boolean;
/** A quick-connect host: never saved, so the token is minted from its fields. */
quickConnectHost?: GuacamoleQuickHost;
}
/**
* What a session needs from its host. A saved host brings its options; a
* quick-connect host has no row, so it also carries its address and login.
*/
export interface GuacamoleHostConfig {
name?: string;
ip: string;
connectionType?: Protocol;
domain?: string;
guacamoleConfig?: GuacamoleConfig;
rdpAuthType?: string;
authOverrides?: RemoteHostLogin["authOverrides"];
syncId?: string | null;
connectionOrigin?: "local" | "remote" | null;
showToolbar?: boolean;
port?: number;
username?: string;
password?: string;
}
export type GuacamoleQuickHost = GuacamoleHostConfig;
function savedHostConfig(
host: RemoteHostLogin,
protocol: Protocol | undefined,
): GuacamoleHostConfig {
const options = hostRemoteOptions(host);
return {
name: host.name ?? undefined,
ip: host.ip ?? "",
connectionType:
protocol ??
(options.enableRdp ? "rdp" : options.enableVnc ? "vnc" : "telnet"),
domain: rdpDomain(host),
guacamoleConfig: options.guacamoleConfig,
rdpAuthType: host.protocolAuth?.rdp?.authType,
authOverrides: host.authOverrides,
syncId: host.syncId,
connectionOrigin: host.connectionOrigin,
showToolbar: options.enableToolbar,
};
}
export interface GuacamoleAppHandle {
disconnect: () => void;
isConnected: () => boolean;
/** The tab bar's refresh. The shell calls `refresh` on every tab switch. */
reconnect: () => void;
}
const GuacamoleApp = React.forwardRef<GuacamoleAppHandle, GuacamoleAppProps>(
function GuacamoleApp(
{ hostId, tabId, protocol, isVisible = true, quickConnectHost },
ref,
) {
const { t } = useTranslation();
const savedHost = useHost(quickConnectHost ? undefined : hostId);
const hostConfig: GuacamoleHostConfig | null = quickConnectHost
? quickConnectHost
: savedHost
? savedHostConfig(savedHost as RemoteHostLogin, protocol)
: null;
// The host list loads with the shell; a tab restored before it arrives
// waits for it instead of reporting the host missing.
const [waited, setWaited] = useState(false);
useEffect(() => {
const timer = setTimeout(() => setWaited(true), 3000);
return () => clearTimeout(timer);
}, []);
const loading = !hostConfig && !!hostId && !waited;
if (loading) {
return (
<div className="relative w-full h-full">
<ConnectionScreen status="connecting" message={t("common.loading")} />
</div>
);
}
if (!hostConfig || !hostId) {
return (
<div className="relative w-full h-full">
<ConnectionScreen
status="disconnected"
message={t("remoteDesktop.hostNotFound")}
/>
</div>
);
}
return (
<ConnectionLogProvider>
<GuacamoleAppInner
hostId={quickConnectHost ? 0 : parseInt(hostId, 10)}
hostConfig={hostConfig}
hostName={hostConfig.name || hostConfig.ip || String(hostId ?? "")}
tabId={tabId}
protocol={protocol}
isVisible={isVisible}
ref={ref}
/>
</ConnectionLogProvider>
);
},
);
interface GuacamoleAppInnerProps {
hostId: number;
hostConfig: GuacamoleHostConfig;
hostName: string;
tabId?: string;
protocol?: "rdp" | "vnc" | "telnet";
isVisible: boolean;
}
const GuacamoleAppInner = React.forwardRef<
GuacamoleAppHandle,
GuacamoleAppInnerProps
>(function GuacamoleAppInner(
{ hostId, hostConfig, hostName, tabId, protocol, isVisible },
ref,
) {
const { t } = useTranslation();
const { addLog, clearLogs } = useConnectionLog();
const [token, setToken] = useState<string | null>(null);
const [guacamoleConnectionId, setGuacamoleConnectionId] = useState<
string | null
>(null);
const [sessionLookup, setSessionLookup] = useState<{
id: string;
origin: ConnectionOrigin;
} | null>(null);
const [error, setError] = useState<string | null>(null);
const [connectionError, setConnectionError] = useState<string | null>(null);
const [isDisplayReady, setIsDisplayReady] = useState(false);
const [touchMode, setTouchMode] = useState<GuacamoleTouchMode | null>(() =>
typeof window !== "undefined" &&
(navigator.maxTouchPoints > 0 || "ontouchstart" in window)
? "touchscreen"
: null,
);
useEffect(() => {
if (!isDisplayReady || !sessionLookup) return;
return watchGuacamoleConnectionId(
sessionLookup.id,
sessionLookup.origin,
setGuacamoleConnectionId,
);
}, [isDisplayReady, sessionLookup]);
const displayRef = useRef<GuacamoleDisplayHandle>(null);
const [displayZoom, setDisplayZoom] = useState(1);
const [filesystem, setFilesystem] = useState<Guacamole.Object | null>(null);
const [fileBrowserOpen, setFileBrowserOpen] = useState(false);
const [toolbarHidden, setToolbarHidden] = useState(false);
const [pendingUploads, setPendingUploads] = useState<File[]>([]);
const guacConfig = hostConfig.guacamoleConfig ?? {};
const allowUpload = guacConfig.disableUpload !== true;
const allowDownload = guacConfig.disableDownload !== true;
// Prefer the browsable filesystem's current directory. guacd may expose the
// RDP drive only through the connection-level file stream, in which case the
// standard direct upload still lands in the redirected drive.
const handleDropFiles = useCallback(
(files: File[]) => {
if (filesystem) {
setPendingUploads(files);
setFileBrowserOpen(true);
return;
}
void (async () => {
for (const file of files) {
try {
const display = displayRef.current;
if (!display) throw new Error("RDP session is not ready");
await display.uploadFile(file);
toast.success(
t("remoteDesktop.files.uploaded", { name: file.name }),
);
} catch (error) {
toast.error(
describeUploadError(error, (key) =>
t(`remoteDesktop.files.${key}`, { name: file.name }),
),
);
}
}
})();
},
[filesystem, t],
);
const handleDropUnavailable = useCallback(() => {
toast.error(
t(
allowUpload
? "remoteDesktop.files.driveUnavailable"
: "remoteDesktop.files.uploadDisabled",
),
);
}, [allowUpload, t]);
const resolvedProtocolForConnect = (protocol ??
hostConfig.connectionType ??
"rdp") as "rdp" | "vnc" | "telnet";
const needsCredentialPrompt = needsRdpCredentialPrompt({
protocol: resolvedProtocolForConnect,
rdpAuthType: hostConfig.rdpAuthType,
authOverrides: hostConfig.authOverrides,
});
const [promptedCredentials, setPromptedCredentials] = useState<{
username: string;
password: string;
domain: string;
} | null>(null);
const [promptOpen, setPromptOpen] = useState(needsCredentialPrompt);
const [promptUsername, setPromptUsername] = useState("");
const [promptPassword, setPromptPassword] = useState("");
const [promptDomain, setPromptDomain] = useState(hostConfig.domain ?? "");
// Assigned below, once handleReconnect exists; the shell's tab refresh calls it.
const reconnectRef = useRef<() => void>(() => {});
useImperativeHandle(ref, () => ({
disconnect: () => displayRef.current?.disconnect(),
isConnected: () => displayRef.current?.isConnected() === true,
reconnect: () => reconnectRef.current(),
}));
const fetchToken = useCallback(async (): Promise<void> => {
setToken(null);
setIsDisplayReady(false);
setSessionLookup(null);
setGuacamoleConnectionId(null);
setError(null);
// Outside Electron there is only one backend, so the origin is moot and
// the API layer ignores it; inside, it decides which backend mints the
// token and therefore has to match the one the session will run on.
let resolvedOrigin: ConnectionOrigin = "local";
if (isElectron()) {
resolvedOrigin = await resolveConnectionOrigin(
{ connectionOrigin: hostConfig.connectionOrigin },
{ defaultRemote: true },
);
if (resolvedOrigin === "remote") {
if (!(await linkedServerUrl())) {
throw new Error(t("errors.remoteServerRequired"));
}
}
}
addLog({
type: "info",
stage: "guac_guacd",
message: t("remoteDesktop.checkingGuacd"),
});
const status = await getGuacdStatus(resolvedOrigin, {
hostId,
protocol: resolvedProtocolForConnect,
syncId: hostConfig.syncId,
});
if (status.enabled === false) {
throw new Error(t("remoteDesktop.disabled"));
}
if (status.guacd.status !== "connected") {
throw new Error(t("remoteDesktop.guacdUnavailable"));
}
addLog({
type: "info",
stage: "guac_token",
message: t("remoteDesktop.requestingToken", {
type: resolvedProtocolForConnect.toUpperCase(),
}),
});
// hostId 0 is a quick-connect host: nothing to look up, mint the token
// straight from what the user typed. It cannot be shared or logged as
// host activity because there is no host row.
const result =
hostId === 0
? await getGuacamoleToken(
{
protocol: resolvedProtocolForConnect,
hostname: hostConfig.ip,
port: hostConfig.port,
username: hostConfig.username,
password: hostConfig.password,
domain: hostConfig.domain,
ignoreCert: true,
guacamoleConfig: hostConfig.guacamoleConfig,
},
resolvedOrigin,
)
: await getGuacamoleTokenFromHost(
hostId,
resolvedOrigin,
protocol,
promptedCredentials ?? undefined,
hostConfig.syncId,
tabId,
);
if (result) {
setSessionLookup(
result.termixConnectId
? { id: result.termixConnectId, origin: resolvedOrigin }
: null,
);
setToken(result.token);
setGuacamoleConnectionId(result.guacamoleConnectionId ?? null);
if (hostId !== 0) {
logActivity(resolvedProtocolForConnect, hostId, hostName).catch(
() => {},
);
}
}
}, [
hostId,
hostName,
protocol,
promptedCredentials,
resolvedProtocolForConnect,
hostConfig,
tabId,
addLog,
t,
]);
const tokenRetry = useConnectionRetry({
connect: async () => {
try {
await fetchToken();
tokenRetryRef.current.markConnected();
} catch (err: unknown) {
const message = errorMessage(err, t("remoteDesktop.failedToConnect"));
setError(message || t("remoteDesktop.failedToConnect"));
addLog({ type: "error", stage: "error", message });
tokenRetryRef.current.markFailed();
}
},
enabled: !needsCredentialPrompt || !!promptedCredentials,
autoStart: false,
});
const tokenRetryRef = useRef(tokenRetry);
tokenRetryRef.current = tokenRetry;
useEffect(() => {
if (needsCredentialPrompt && !promptedCredentials) {
setPromptOpen(true);
return;
}
clearLogs();
tokenRetryRef.current.reset();
tokenRetryRef.current.retryNow();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [hostId, protocol, needsCredentialPrompt, promptedCredentials]);
const handleReconnect = useCallback(() => {
setConnectionError(null);
setError(null);
setToken(null);
setIsDisplayReady(false);
if (needsCredentialPrompt) {
setPromptedCredentials(null);
setPromptUsername("");
setPromptPassword("");
setPromptDomain(hostConfig.domain ?? "");
setPromptOpen(true);
return;
}
clearLogs();
tokenRetryRef.current.reset();
tokenRetryRef.current.retryNow();
}, [needsCredentialPrompt, hostConfig.domain, clearLogs]);
reconnectRef.current = handleReconnect;
if (promptOpen) {
return (
<Dialog
open={promptOpen}
onOpenChange={(open) => {
if (!open) setPromptOpen(false);
}}
>
<DialogContent className="sm:max-w-sm">
<DialogHeader>
<DialogTitle className="text-lg font-bold">
{t("remoteDesktop.credentialPromptTitle")}
</DialogTitle>
<DialogDescription className="text-xs text-muted-foreground">
{t("remoteDesktop.credentialPromptDescription")}
</DialogDescription>
</DialogHeader>
<form
className="flex flex-col gap-4 mt-1"
onSubmit={(e) => {
e.preventDefault();
setPromptedCredentials({
username: promptUsername,
password: promptPassword,
domain: promptDomain,
});
setPromptOpen(false);
}}
>
<div className="flex flex-col gap-1.5">
<label className="text-xs font-semibold">
{t("hosts.guac.username")}
</label>
<Input
autoFocus
placeholder="Administrator"
value={promptUsername}
onChange={(e) => setPromptUsername(e.target.value)}
/>
</div>
<div className="flex flex-col gap-1.5">
<label className="text-xs font-semibold">
{t("hosts.guac.domain")}
</label>
<Input
placeholder="WORKGROUP"
value={promptDomain}
onChange={(e) => setPromptDomain(e.target.value)}
/>
</div>
<div className="flex flex-col gap-1.5">
<label className="text-xs font-semibold">
{t("hosts.guac.password")}
</label>
<PasswordInput
className="h-8 text-xs pr-8"
placeholder="••••••••"
value={promptPassword}
onChange={(e) => setPromptPassword(e.target.value)}
/>
</div>
<div className="flex items-center justify-end gap-2 mt-2">
<Button type="submit" variant="outline">
{t("remoteDesktop.connect")}
</Button>
</div>
</form>
</DialogContent>
</Dialog>
);
}
if (error || !token) {
return (
<div className="relative w-full h-full">
<ConnectionScreen
status={error ? tokenRetry.status : "connecting"}
message={t("remoteDesktop.connecting", {
type: (
protocol ||
hostConfig.connectionType ||
"remote"
).toUpperCase(),
})}
attempt={tokenRetry.attempt}
maxAttempts={tokenRetry.maxAttempts}
nextRetryInMs={tokenRetry.nextRetryInMs}
onManualRetry={handleReconnect}
retryLabel={t("remoteDesktop.retry")}
/>
</div>
);
}
const resolvedProtocol = resolvedProtocolForConnect;
const configuredDpi = readConfiguredDimension(guacConfig.dpi);
const configuredWidth = readConfiguredDimension(guacConfig.width);
const configuredHeight = readConfiguredDimension(guacConfig.height);
return (
<div className="relative w-full h-full">
{(!isDisplayReady || connectionError) && (
<ConnectionScreen
status={connectionError ? "disconnected" : "connecting"}
message={t("remoteDesktop.connecting", {
type: resolvedProtocol.toUpperCase(),
})}
onManualRetry={handleReconnect}
retryLabel={t("remoteDesktop.reconnect")}
className="z-50"
/>
)}
<GuacamoleDisplay
key={`${token}-${touchMode}`}
ref={displayRef}
connectionConfig={{
token,
protocol: resolvedProtocol,
type: resolvedProtocol,
connectionOrigin: hostConfig.connectionOrigin,
width: configuredWidth,
height: configuredHeight,
dpi: configuredDpi,
}}
isVisible={isVisible}
touchMode={touchMode}
allowUpload={canUploadToRdpDrive(
allowUpload,
guacConfig.enableDrive === true,
filesystem !== null,
)}
onConnect={() => setIsDisplayReady(true)}
onError={(err) => {
setConnectionError(err);
addLog({ type: "error", stage: "error", message: err });
}}
onStageChange={(stage) => {
const type = resolvedProtocol.toUpperCase();
switch (stage) {
case "guac_connecting":
addLog({
type: "info",
stage,
message: t("remoteDesktop.openingSession", { type }),
});
break;
case "guac_handshake":
addLog({
type: "info",
stage,
message: t("remoteDesktop.negotiating", { type }),
});
break;
case "guac_ready":
addLog({
type: "success",
stage,
message: t("remoteDesktop.sessionReady", { type }),
});
break;
default:
break;
}
}}
onZoomChange={setDisplayZoom}
onFilesystem={setFilesystem}
onDropFiles={handleDropFiles}
onDropUnavailable={handleDropUnavailable}
/>
{filesystem && fileBrowserOpen && (
<GuacamoleFileBrowser
filesystem={filesystem}
allowUpload={allowUpload}
allowDownload={allowDownload}
pendingUploads={pendingUploads}
onPendingUploadsHandled={() => setPendingUploads([])}
onClose={() => setFileBrowserOpen(false)}
/>
)}
{hostConfig.showToolbar !== false && !toolbarHidden && (
<GuacamoleToolbar
displayRef={displayRef}
protocol={resolvedProtocol}
touchMode={touchMode}
hasFilesystem={filesystem !== null}
fileBrowserOpen={fileBrowserOpen}
onToggleFileBrowser={() => setFileBrowserOpen((open) => !open)}
onTouchModeChange={setTouchMode}
zoom={displayZoom}
onHide={() => setToolbarHidden(true)}
slotContext={
typeof hostId === "number"
? {
hostId,
sessionId: guacamoleConnectionId,
protocol: resolvedProtocol,
tabInstanceId: tabId,
origin:
sessionLookup?.origin === "remote" ? "remote" : "local",
}
: undefined
}
/>
)}
</div>
);
});
export default GuacamoleApp;
@@ -0,0 +1,924 @@
import type React from "react";
import {
useEffect,
useRef,
useState,
useImperativeHandle,
forwardRef,
useCallback,
} from "react";
import Guacamole from "guacamole-common-js";
import { Upload } from "lucide-react";
import { useTranslation } from "@termix/plugin-sdk/frontend";
import { getGuacamoleToken } from "./guacamole-api";
import { buildGuacamoleWebSocketBaseUrl } from "./guacamole-websocket-url.ts";
import {
buildOriginWsUrl,
getBasePath,
isElectron,
resolveConnectionOrigin,
type ConnectionOrigin,
type ConnectionStage,
} from "@termix/plugin-sdk/ui";
import { errorMessage } from "./host-remote";
import { guacStateToStage } from "./guacamole-state.ts";
import { isPasteShortcut, pasteTextToRemote } from "./guacamole-clipboard.ts";
import { getGuacamoleDisplaySize } from "./guacamole-display-size.ts";
import { bindPointerInput } from "./guacamole-pointer.ts";
import {
getFileDropDisposition,
hasDraggedFiles,
} from "./guacamole-file-drop.ts";
import {
uploadFileToClient,
type GuacamoleFileStreamClient,
} from "./guacamole-filesystem.ts";
import { clampGuacamoleZoom, stepGuacamoleZoom } from "./guacamole-zoom.ts";
export type GuacamoleConnectionType = "rdp" | "vnc" | "telnet";
export interface GuacamoleConnectionConfig {
token?: string;
protocol?: GuacamoleConnectionType;
type?: GuacamoleConnectionType;
hostname?: string;
port?: number;
username?: string;
password?: string;
domain?: string;
width?: number;
height?: number;
dpi?: number;
/**
* Which backend serves this session. Supplied by the owning app so the
* websocket dials the same backend that minted the token; omitted by
* shared/collab views, which follow the default for the connection type.
*/
connectionOrigin?: ConnectionOrigin | null;
[key: string]: unknown;
}
export interface GuacamoleDisplayHandle {
disconnect: () => void;
isConnected: () => boolean;
sendKey: (keysym: number, pressed: boolean) => void;
sendMouse: (x: number, y: number, buttonMask: number) => void;
setClipboard: (data: string) => void;
getFilesystem: () => Guacamole.Object | null;
uploadFile: (file: File) => Promise<void>;
zoomIn: () => number;
zoomOut: () => number;
resetZoom: () => number;
}
export type GuacamoleTouchMode = "touchscreen" | "touchpad";
interface GuacamoleDisplayProps {
connectionConfig: GuacamoleConnectionConfig;
isVisible: boolean;
touchMode?: GuacamoleTouchMode | null;
allowUpload?: boolean;
onConnect?: () => void;
onDisconnect?: () => void;
onError?: (error: string) => void;
onFilesystem?: (filesystem: Guacamole.Object | null) => void;
onDropFiles?: (files: File[]) => void;
onDropUnavailable?: () => void;
onStageChange?: (stage: ConnectionStage) => void;
onZoomChange?: (zoom: number) => void;
}
export const GuacamoleDisplay = forwardRef<
GuacamoleDisplayHandle,
GuacamoleDisplayProps
>(function GuacamoleDisplay(
{
connectionConfig,
isVisible,
touchMode,
allowUpload = false,
onConnect,
onDisconnect,
onError,
onFilesystem,
onDropFiles,
onDropUnavailable,
onStageChange,
onZoomChange,
},
ref,
) {
const { t } = useTranslation();
// The host config pins the session resolution; without it the display follows
// the container.
const hasConfiguredSize =
connectionConfig.width != null && connectionConfig.height != null;
const containerRef = useRef<HTMLDivElement>(null);
const displayRef = useRef<HTMLDivElement>(null);
const displayElementRef = useRef<HTMLElement | null>(null);
const clientRef = useRef<Guacamole.Client | null>(null);
const filesystemRef = useRef<Guacamole.Object | null>(null);
// Held in a ref so tearing down the client can report the loss without
// rebuilding the connect callback whenever the parent re-renders.
const onFilesystemRef = useRef(onFilesystem);
onFilesystemRef.current = onFilesystem;
const onStageChangeRef = useRef(onStageChange);
onStageChangeRef.current = onStageChange;
const keyboardRef = useRef<Guacamole.Keyboard | null>(null);
const unbindPointerRef = useRef<(() => void) | null>(null);
const scaleRef = useRef<number>(1);
const fitScaleRef = useRef<number>(1);
const zoomRef = useRef<number>(1);
const resizeTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const hasKeyboardFocusRef = useRef(false);
const windowFocusedRef = useRef(
typeof document === "undefined" ? true : document.hasFocus(),
);
const hasInitiatedRef = useRef(false);
const isMountedRef = useRef(false);
const isConnectingRef = useRef(false);
const [isReady, setIsReady] = useState(false);
const [hasError, setHasError] = useState(false);
// RDP's guacd backend (FreeRDP) can report the Guacamole-level CONNECTED
// state from a single early sync before the actual RDP handshake with the
// remote host has finished negotiating - a handshake that can then still
// fail up to ~30s later. Waiting for a couple of real frame syncs before
// announcing the connection avoids a black screen with no connecting UI
// during that window; VNC/telnet fail before ever reaching CONNECTED, so
// they are unaffected.
const syncCountRef = useRef(0);
const disconnectClient = useCallback(() => {
unbindPointerRef.current?.();
unbindPointerRef.current = null;
const client = clientRef.current;
clientRef.current = null;
isConnectingRef.current = false;
if (filesystemRef.current) {
filesystemRef.current = null;
onFilesystemRef.current?.(null);
}
if (!client) return;
try {
client.disconnect();
} catch (error) {
console.warn("Failed to disconnect Guacamole client", error);
}
}, []);
const applyZoom = useCallback(
(nextZoom: number): number => {
const zoom = clampGuacamoleZoom(nextZoom);
zoomRef.current = zoom;
const display = clientRef.current?.getDisplay();
if (display && displayRef.current) {
const scale = fitScaleRef.current * zoom;
scaleRef.current = scale;
display.scale(scale);
displayRef.current.style.width = `${display.getWidth() * scale}px`;
displayRef.current.style.height = `${display.getHeight() * scale}px`;
}
onZoomChange?.(zoom);
return zoom;
},
[onZoomChange],
);
useImperativeHandle(ref, () => ({
disconnect: disconnectClient,
isConnected: () => isReady && !hasError,
sendKey: (keysym: number, pressed: boolean) => {
if (clientRef.current) {
clientRef.current.sendKeyEvent(pressed ? 1 : 0, keysym);
}
},
sendMouse: (x: number, y: number, buttonMask: number) => {
if (clientRef.current) {
clientRef.current.sendMouseState(
new Guacamole.Mouse.State({
x,
y,
left: !!(buttonMask & 1),
middle: !!(buttonMask & 2),
right: !!(buttonMask & 4),
}),
);
}
},
setClipboard: (data: string) => {
if (clientRef.current) {
const stream = clientRef.current.createClipboardStream("text/plain");
const writer = new Guacamole.StringWriter(stream);
writer.sendText(data);
writer.sendEnd();
}
},
getFilesystem: () => filesystemRef.current,
uploadFile: (file: File) => {
const client = clientRef.current;
if (!client) return Promise.reject(new Error("RDP session is not ready"));
return uploadFileToClient(
client as unknown as GuacamoleFileStreamClient,
file,
);
},
zoomIn: () => applyZoom(stepGuacamoleZoom(zoomRef.current, 1)),
zoomOut: () => applyZoom(stepGuacamoleZoom(zoomRef.current, -1)),
resetZoom: () => applyZoom(1),
}));
const getWebSocketConnection = useCallback(
async (
containerWidth: number,
containerHeight: number,
): Promise<{ url: string; query: string } | null> => {
try {
let token: string;
const connectionProtocol =
connectionConfig.protocol ?? connectionConfig.type;
// Resolved before the token is minted, not just before the socket is
// opened: the token has to come from whichever backend will serve the
// session, so both steps have to agree on the origin.
const origin = await resolveConnectionOrigin(
{ connectionOrigin: connectionConfig.connectionOrigin },
{ defaultRemote: true },
);
if (connectionConfig.token) {
token = connectionConfig.token;
} else {
const data = await getGuacamoleToken(
{
protocol: connectionProtocol ?? "rdp",
hostname: String(connectionConfig.hostname ?? ""),
port: connectionConfig.port,
username: connectionConfig.username,
password: connectionConfig.password,
domain: connectionConfig.domain,
security:
typeof connectionConfig.security === "string"
? connectionConfig.security
: undefined,
ignoreCert:
typeof connectionConfig.ignoreCert === "boolean"
? connectionConfig.ignoreCert
: undefined,
guacamoleConfig: connectionConfig.guacamoleConfig as Parameters<
typeof getGuacamoleToken
>[0]["guacamoleConfig"],
},
origin,
);
token = data.token;
}
const displaySize = getGuacamoleDisplaySize(
connectionConfig.width ?? containerWidth ?? 1280,
connectionConfig.height ?? containerHeight ?? 720,
connectionProtocol,
window.devicePixelRatio,
connectionConfig.dpi,
);
let wsBase: string | null;
if (isElectron()) {
// includeJwt stays off: a display authenticates with the encrypted
// connection token in the query, not a session.
const target = await buildOriginWsUrl({
origin,
localPort: 30001,
localPath: "/plugin-ws/remote-desktop/display",
remotePath: "/plugin-ws/remote-desktop/display",
includeJwt: false,
});
if (!target) {
onError?.(t("errors.remoteServerRequired"));
return null;
}
wsBase = target.url;
} else {
wsBase = buildGuacamoleWebSocketBaseUrl({
isElectronApp: false,
isEmbeddedApp: false,
basePath: getBasePath(),
location: window.location,
});
}
const params = new URLSearchParams({
token,
width: String(displaySize.width),
height: String(displaySize.height),
});
if (displaySize.dpi) params.set("dpi", String(displaySize.dpi));
return { url: wsBase, query: params.toString() };
} catch (error) {
const message = errorMessage(error);
onError?.(message);
return null;
}
},
[connectionConfig, onError, t],
);
const refreshKeyboardHandlers = useCallback(() => {
const keyboard = keyboardRef.current;
const client = clientRef.current;
const displayElement = displayElementRef.current;
if (!keyboard) return;
const documentVisible =
typeof document === "undefined" || document.visibilityState === "visible";
const displayIsFocused =
!!displayElement &&
typeof document !== "undefined" &&
document.activeElement === displayElement;
const shouldCaptureInput =
!!client &&
!!displayElement &&
isVisible &&
documentVisible &&
windowFocusedRef.current &&
(hasKeyboardFocusRef.current || displayIsFocused);
if (!shouldCaptureInput) {
keyboard.reset();
keyboard.onkeydown = null;
keyboard.onkeyup = null;
return;
}
keyboard.onkeydown = (keysym: number) => {
if (!clientRef.current) return;
if (!isVisible || !windowFocusedRef.current) return;
const activeDisplay = displayElementRef.current;
const stillFocused =
!!activeDisplay &&
typeof document !== "undefined" &&
document.activeElement === activeDisplay;
if (!hasKeyboardFocusRef.current && !stillFocused) return;
clientRef.current.sendKeyEvent(1, keysym);
};
keyboard.onkeyup = (keysym: number) => {
// Focus loss must still release keys already sent to the remote host.
clientRef.current?.sendKeyEvent(0, keysym);
};
}, [isVisible]);
const rescaleDisplay = useCallback(
(immediate: boolean = false) => {
if (!clientRef.current || !containerRef.current) return;
const performRescale = () => {
if (!clientRef.current || !containerRef.current) return;
const display = clientRef.current.getDisplay();
const cWidth = containerRef.current.clientWidth;
const cHeight = containerRef.current.clientHeight;
const displayWidth = display.getWidth();
const displayHeight = display.getHeight();
if (
displayWidth > 0 &&
displayHeight > 0 &&
cWidth > 0 &&
cHeight > 0
) {
fitScaleRef.current = Math.min(
cWidth / displayWidth,
cHeight / displayHeight,
);
applyZoom(zoomRef.current);
}
};
if (immediate) {
performRescale();
} else {
if (resizeTimeoutRef.current) {
clearTimeout(resizeTimeoutRef.current);
}
resizeTimeoutRef.current = setTimeout(performRescale, 200);
}
},
[applyZoom],
);
const connect = useCallback(async () => {
if (isConnectingRef.current) return;
isConnectingRef.current = true;
setIsReady(false);
setHasError(false);
syncCountRef.current = 0;
// Let layout settle before measuring without depending on animation frames,
// which may be throttled while Electron windows or tabs are inactive.
await new Promise<void>((resolve) => setTimeout(resolve, 0));
if (!isMountedRef.current) {
isConnectingRef.current = false;
return;
}
// The tab's DOM node can still be display:none (and report 0x0) when this
// tab is restored in the background. Measuring then would force the
// window-size fallback, which ignores the tab bar and makes the remote
// resolution too tall (the bottom gets cut off). Poll briefly for a real
// size before connecting so we capture the actual visible viewport.
const measureContainer = () => {
const rect = containerRef.current?.getBoundingClientRect();
return { width: rect?.width || 0, height: rect?.height || 0 };
};
let { width: containerWidth, height: containerHeight } = measureContainer();
for (
let attempt = 0;
(containerWidth < 100 || containerHeight < 100) && attempt < 40;
attempt++
) {
await new Promise<void>((resolve) => setTimeout(resolve, 25));
if (!isMountedRef.current) {
isConnectingRef.current = false;
return;
}
({ width: containerWidth, height: containerHeight } = measureContainer());
}
if (containerWidth < 100 || containerHeight < 100) {
containerWidth = window.innerWidth || 1280;
containerHeight = window.innerHeight || 720;
}
const wsConnection = await getWebSocketConnection(
containerWidth,
containerHeight,
);
if (!isMountedRef.current) {
isConnectingRef.current = false;
return;
}
if (!wsConnection) {
isConnectingRef.current = false;
return;
}
const tunnel = new Guacamole.WebSocketTunnel(wsConnection.url);
const client = new Guacamole.Client(tunnel);
clientRef.current = client;
const display = client.getDisplay();
const displayElement = display.getElement();
displayElementRef.current = displayElement;
if (displayRef.current) {
displayRef.current.innerHTML = "";
displayRef.current.appendChild(displayElement);
}
displayElement.setAttribute("tabindex", "0");
displayElement.style.outline = "none";
// Reading navigator.clipboard outside a user gesture is denied by Safari
// and commonly denied by Chromium. The paste event carries the text under
// the browser's normal permission model, so use it on every browser and
// replace the original shortcut with an ordered clipboard update + Ctrl+V.
displayElement.addEventListener(
"keydown",
(event) => {
if (isPasteShortcut(event)) {
event.stopImmediatePropagation();
}
},
true,
);
displayElement.addEventListener(
"paste",
(event) => {
if (clientRef.current !== client) return;
const text = event.clipboardData?.getData("text/plain");
if (!text) return;
event.preventDefault();
event.stopImmediatePropagation();
pasteTextToRemote(client, text);
},
true,
);
display.onresize = () => {
if (!isMountedRef.current || clientRef.current !== client) return;
rescaleDisplay(true);
setIsReady(true);
};
const protocol = connectionConfig.protocol ?? connectionConfig.type;
if (protocol === "telnet" && isMountedRef.current) {
setIsReady(true);
}
const sendMouseState = (state: Guacamole.Mouse.State) => {
displayElement.focus({ preventScroll: true });
const scale = scaleRef.current;
const adjustedState = new Guacamole.Mouse.State(
Math.round(state.x / scale),
Math.round(state.y / scale),
state.left,
state.middle,
state.right,
state.up,
state.down,
) as Guacamole.Mouse.State;
client.sendMouseState(adjustedState);
};
unbindPointerRef.current?.();
unbindPointerRef.current = bindPointerInput(
displayElement,
touchMode,
sendMouseState,
);
const keyboard = new Guacamole.Keyboard(displayElement);
keyboardRef.current = keyboard;
const handleDisplayFocus = () => {
hasKeyboardFocusRef.current = true;
refreshKeyboardHandlers();
};
const handleDisplayBlur = () => {
hasKeyboardFocusRef.current = false;
refreshKeyboardHandlers();
};
displayElement.addEventListener("focus", handleDisplayFocus);
displayElement.addEventListener("blur", handleDisplayBlur);
displayElement.addEventListener("mousedown", handleDisplayFocus);
displayElement.addEventListener("touchstart", handleDisplayFocus, {
passive: true,
});
refreshKeyboardHandlers();
client.onstatechange = (state: number) => {
if (!isMountedRef.current || clientRef.current !== client) return;
onStageChangeRef.current?.(guacStateToStage(state));
switch (state) {
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
isConnectingRef.current = false;
if (protocol !== "rdp") {
setIsReady(true);
onConnect?.();
}
// A configured resolution is the size the session should render at;
// resizing it to the container would discard it. rescaleDisplay still
// fits that fixed display into whatever space is available.
if (!hasConfiguredSize && containerRef.current) {
const rect = containerRef.current.getBoundingClientRect();
const size = getGuacamoleDisplaySize(
rect.width,
rect.height,
protocol,
window.devicePixelRatio,
connectionConfig.dpi,
);
client.sendSize(size.width, size.height);
}
rescaleDisplay(false);
break;
case 4:
break;
case 5:
isConnectingRef.current = false;
setIsReady(false);
setHasError(true);
hasKeyboardFocusRef.current = false;
refreshKeyboardHandlers();
onError?.(t("remoteDesktop.connectionError"));
onDisconnect?.();
break;
}
};
client.onerror = (error: Guacamole.Status) => {
if (!isMountedRef.current || clientRef.current !== client) return;
const errorMessage = error.message || t("remoteDesktop.connectionError");
setIsReady(false);
setHasError(true);
isConnectingRef.current = false;
onError?.(errorMessage);
};
if (protocol === "rdp") {
client.onsync = () => {
if (!isMountedRef.current || clientRef.current !== client) return;
if (syncCountRef.current >= 2) return;
syncCountRef.current += 1;
if (syncCountRef.current >= 2) {
setIsReady(true);
onConnect?.();
}
};
}
client.onclipboard = (stream: Guacamole.InputStream, mimetype: string) => {
if (mimetype === "text/plain") {
const reader = new Guacamole.StringReader(stream);
let data = "";
reader.ontext = (text: string) => {
data += text;
};
reader.onend = () => {
navigator.clipboard?.writeText?.(data).catch(() => {});
};
}
};
client.onaudio = (stream: Guacamole.InputStream, mimetype: string) => {
Guacamole.AudioPlayer.getInstance(stream, mimetype);
};
// Only fires when the connection enables drive redirection; guacd exposes
// the redirected drive as a single filesystem object.
client.onfilesystem = (filesystem: Guacamole.Object) => {
if (!isMountedRef.current || clientRef.current !== client) return;
filesystemRef.current = filesystem;
onFilesystemRef.current?.(filesystem);
filesystem.onundefine = () => {
if (filesystemRef.current !== filesystem) return;
filesystemRef.current = null;
onFilesystemRef.current?.(null);
};
};
client.onfile = (
stream: Guacamole.InputStream,
mimetype: string,
filename: string,
) => {
const reader = new Guacamole.BlobReader(stream, mimetype);
reader.onend = () => {
const blob = reader.getBlob();
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
};
stream.sendAck("OK", Guacamole.Status.Code.SUCCESS);
};
try {
client.connect(wsConnection.query);
} catch (error) {
isConnectingRef.current = false;
if (!isMountedRef.current) return;
setIsReady(false);
setHasError(true);
onError?.(errorMessage(error, t("remoteDesktop.connectionError")));
}
}, [
getWebSocketConnection,
onConnect,
onDisconnect,
onError,
refreshKeyboardHandlers,
rescaleDisplay,
connectionConfig.protocol,
connectionConfig.type,
connectionConfig.dpi,
hasConfiguredSize,
touchMode,
t,
]);
useEffect(() => {
isMountedRef.current = true;
if (isVisible && !hasInitiatedRef.current) {
hasInitiatedRef.current = true;
connect();
}
}, [isVisible, connect]);
useEffect(() => {
if (!isVisible) {
hasKeyboardFocusRef.current = false;
}
refreshKeyboardHandlers();
}, [isVisible, refreshKeyboardHandlers]);
useEffect(() => {
const handleWindowFocus = () => {
windowFocusedRef.current = true;
refreshKeyboardHandlers();
};
const handleWindowBlur = () => {
windowFocusedRef.current = false;
hasKeyboardFocusRef.current = false;
refreshKeyboardHandlers();
};
const handleVisibilityChange = () => {
windowFocusedRef.current =
document.visibilityState === "visible" && document.hasFocus();
if (document.visibilityState !== "visible") {
hasKeyboardFocusRef.current = false;
}
refreshKeyboardHandlers();
};
window.addEventListener("focus", handleWindowFocus);
window.addEventListener("blur", handleWindowBlur);
document.addEventListener("visibilitychange", handleVisibilityChange);
return () => {
window.removeEventListener("focus", handleWindowFocus);
window.removeEventListener("blur", handleWindowBlur);
document.removeEventListener("visibilitychange", handleVisibilityChange);
};
}, [refreshKeyboardHandlers]);
useEffect(() => {
return () => {
isMountedRef.current = false;
hasInitiatedRef.current = false;
isConnectingRef.current = false;
if (resizeTimeoutRef.current) {
clearTimeout(resizeTimeoutRef.current);
}
disconnectClient();
displayElementRef.current = null;
};
}, [disconnectClient]);
useEffect(() => {
if (!containerRef.current) return;
const resizeObserver = new ResizeObserver(() => {
if (resizeTimeoutRef.current) clearTimeout(resizeTimeoutRef.current);
resizeTimeoutRef.current = setTimeout(() => {
if (clientRef.current && containerRef.current) {
const rect = containerRef.current.getBoundingClientRect();
if (rect.width > 0 && rect.height > 0) {
if (!hasConfiguredSize) {
const size = getGuacamoleDisplaySize(
rect.width,
rect.height,
connectionConfig.protocol ?? connectionConfig.type,
window.devicePixelRatio,
connectionConfig.dpi,
);
clientRef.current.sendSize(size.width, size.height);
}
rescaleDisplay(true);
}
}
}, 150);
});
resizeObserver.observe(containerRef.current);
return () => {
resizeObserver.disconnect();
};
}, [
connectionConfig.dpi,
connectionConfig.protocol,
connectionConfig.type,
hasConfiguredSize,
rescaleDisplay,
]);
useEffect(() => {
const container = containerRef.current;
const protocol = connectionConfig.protocol ?? connectionConfig.type;
if (!container || !isReady || protocol !== "vnc") return;
let pinchDistance = 0;
let pinchZoom = zoomRef.current;
const distance = (touches: TouchList) =>
Math.hypot(
touches[0].clientX - touches[1].clientX,
touches[0].clientY - touches[1].clientY,
);
const onTouchStart = (event: TouchEvent) => {
if (event.touches.length !== 2) return;
event.preventDefault();
event.stopPropagation();
pinchDistance = distance(event.touches);
pinchZoom = zoomRef.current;
};
const onTouchMove = (event: TouchEvent) => {
if (event.touches.length !== 2 || pinchDistance === 0) return;
event.preventDefault();
event.stopPropagation();
applyZoom(pinchZoom * (distance(event.touches) / pinchDistance));
};
const onTouchEnd = () => {
pinchDistance = 0;
};
container.addEventListener("touchstart", onTouchStart, {
passive: false,
capture: true,
});
container.addEventListener("touchmove", onTouchMove, {
passive: false,
capture: true,
});
container.addEventListener("touchend", onTouchEnd, { capture: true });
return () => {
container.removeEventListener("touchstart", onTouchStart, true);
container.removeEventListener("touchmove", onTouchMove, true);
container.removeEventListener("touchend", onTouchEnd, true);
};
}, [applyZoom, connectionConfig.protocol, connectionConfig.type, isReady]);
const canDropFiles = allowUpload && onDropFiles != null;
const [isDraggingFiles, setIsDraggingFiles] = useState(false);
// Nested elements fire dragleave as the pointer crosses them, so track depth
// rather than clearing the highlight on the first leave.
const dragDepthRef = useRef(0);
const handleDragEnter = useCallback(
(event: React.DragEvent) => {
if (!hasDraggedFiles(event.dataTransfer.types)) return;
event.preventDefault();
if (!canDropFiles) return;
dragDepthRef.current += 1;
setIsDraggingFiles(true);
},
[canDropFiles],
);
const handleDragLeave = useCallback(() => {
dragDepthRef.current = Math.max(0, dragDepthRef.current - 1);
if (dragDepthRef.current === 0) setIsDraggingFiles(false);
}, []);
const handleDrop = useCallback(
(event: React.DragEvent) => {
if (!hasDraggedFiles(event.dataTransfer.types)) return;
event.preventDefault();
dragDepthRef.current = 0;
setIsDraggingFiles(false);
const files = Array.from(event.dataTransfer.files);
const disposition = getFileDropDisposition(
event.dataTransfer.types,
files.length,
canDropFiles,
);
if (disposition === "upload") onDropFiles?.(files);
if (disposition === "reject") onDropUnavailable?.();
},
[canDropFiles, onDropFiles, onDropUnavailable],
);
return (
<div
ref={containerRef}
className="absolute inset-0 overflow-auto"
style={{ backgroundColor: "var(--bg-base)" }}
onDragEnter={handleDragEnter}
onDragOver={(event) => {
if (hasDraggedFiles(event.dataTransfer.types)) event.preventDefault();
}}
onDragLeave={canDropFiles ? handleDragLeave : undefined}
onDrop={handleDrop}
>
<div
ref={displayRef}
className="relative flex min-h-full min-w-full items-center justify-center"
style={{
cursor: isReady ? "none" : "default",
visibility: isReady ? "visible" : "hidden",
}}
/>
{isDraggingFiles && (
<div className="absolute inset-0 z-30 flex items-center justify-center pointer-events-none bg-background/70 backdrop-blur-sm">
<div className="flex items-center gap-2 rounded-sm border-2 border-dashed border-border px-4 py-3 text-sm font-semibold">
<Upload className="size-4" />
{t("remoteDesktop.files.dropToUpload")}
</div>
</div>
)}
</div>
);
});
@@ -0,0 +1,245 @@
import { errorMessage } from "./host-remote";
import { useCallback, useEffect, useRef, useState } from "react";
import Guacamole from "guacamole-common-js";
import { useTranslation } from "@termix/plugin-sdk/frontend";
import {
ArrowUp,
Download,
File as FileIcon,
Folder,
RefreshCw,
Upload,
X,
} from "lucide-react";
import { toast } from "sonner";
import { Button } from "@termix/plugin-sdk/ui";
import {
downloadFile,
listDirectory,
parentPath,
saveBlobAs,
uploadFile,
describeUploadError,
type RemoteFileEntry,
} from "./guacamole-filesystem.ts";
interface GuacamoleFileBrowserProps {
filesystem: Guacamole.Object;
allowUpload: boolean;
allowDownload: boolean;
pendingUploads: File[];
onPendingUploadsHandled: () => void;
onClose: () => void;
}
export function GuacamoleFileBrowser({
filesystem,
allowUpload,
allowDownload,
pendingUploads,
onPendingUploadsHandled,
onClose,
}: GuacamoleFileBrowserProps) {
const { t } = useTranslation();
const [path, setPath] = useState("/");
const [entries, setEntries] = useState<RemoteFileEntry[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [busyName, setBusyName] = useState<string | null>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
const refresh = useCallback(
async (target: string) => {
setLoading(true);
setError(null);
try {
setEntries(await listDirectory(filesystem, target));
} catch (err) {
setEntries([]);
setError(errorMessage(err, t("remoteDesktop.files.listFailed")));
} finally {
setLoading(false);
}
},
[filesystem, t],
);
useEffect(() => {
void refresh(path);
}, [refresh, path]);
const upload = useCallback(
async (files: File[]) => {
for (const file of files) {
setBusyName(file.name);
try {
await uploadFile(filesystem, path, file);
toast.success(t("remoteDesktop.files.uploaded", { name: file.name }));
} catch (err) {
toast.error(
describeUploadError(err, (key) =>
t(`remoteDesktop.files.${key}`, { name: file.name }),
),
);
} finally {
setBusyName(null);
}
}
void refresh(path);
},
[filesystem, path, refresh, t],
);
// Files dropped on the display land here so they share the browser's current
// directory rather than always going to the drive root.
useEffect(() => {
if (pendingUploads.length === 0) return;
onPendingUploadsHandled();
if (!allowUpload) {
toast.error(t("remoteDesktop.files.uploadDisabled"));
return;
}
void upload(pendingUploads);
}, [pendingUploads, onPendingUploadsHandled, allowUpload, upload, t]);
const download = useCallback(
async (entry: RemoteFileEntry) => {
setBusyName(entry.name);
try {
const { blob, filename } = await downloadFile(filesystem, entry.path);
saveBlobAs(blob, filename);
} catch (err) {
toast.error(
err instanceof Error
? err.message
: t("remoteDesktop.files.downloadFailed", { name: entry.name }),
);
} finally {
setBusyName(null);
}
},
[filesystem, t],
);
return (
<div className="absolute right-2 top-2 bottom-2 z-30 flex w-80 flex-col rounded-sm border border-border bg-background/95 shadow-lg backdrop-blur-sm">
<div className="flex items-center gap-1 border-b border-border px-2 py-1.5">
<span className="flex-1 truncate text-xs font-semibold">
{t("remoteDesktop.files.title")}
</span>
<Button
variant="ghost"
size="icon"
className="size-6"
onClick={onClose}
aria-label={t("common.close")}
>
<X className="size-3.5" />
</Button>
</div>
<div className="flex items-center gap-1 border-b border-border px-2 py-1.5">
<Button
variant="ghost"
size="icon"
className="size-6"
disabled={path === "/"}
onClick={() => setPath(parentPath(path))}
aria-label={t("remoteDesktop.files.parent")}
>
<ArrowUp className="size-3.5" />
</Button>
<span className="flex-1 truncate text-[11px] text-muted-foreground">
{path}
</span>
<Button
variant="ghost"
size="icon"
className="size-6"
onClick={() => void refresh(path)}
aria-label={t("remoteDesktop.files.refresh")}
>
<RefreshCw className="size-3.5" />
</Button>
</div>
<div className="flex-1 overflow-y-auto">
{loading && (
<p className="px-2 py-3 text-[11px] text-muted-foreground">
{t("common.loading")}
</p>
)}
{!loading && error && (
<p className="px-2 py-3 text-[11px] text-red-500">{error}</p>
)}
{!loading && !error && entries.length === 0 && (
<p className="px-2 py-3 text-[11px] text-muted-foreground">
{t("remoteDesktop.files.empty")}
</p>
)}
{!loading &&
!error &&
entries.map((entry) => (
<div
key={entry.path}
className="flex items-center gap-1.5 px-2 py-1 hover:bg-muted"
>
{entry.isDirectory ? (
<Folder className="size-3.5 shrink-0 text-muted-foreground" />
) : (
<FileIcon className="size-3.5 shrink-0 text-muted-foreground" />
)}
<button
type="button"
className="flex-1 truncate text-left text-[11px] disabled:opacity-60"
disabled={!entry.isDirectory}
onClick={() => entry.isDirectory && setPath(entry.path)}
>
{entry.name}
</button>
{!entry.isDirectory && allowDownload && (
<Button
variant="ghost"
size="icon"
className="size-5 shrink-0"
disabled={busyName === entry.name}
onClick={() => void download(entry)}
aria-label={t("remoteDesktop.files.download")}
>
<Download className="size-3" />
</Button>
)}
</div>
))}
</div>
{allowUpload && (
<div className="border-t border-border px-2 py-1.5">
<input
ref={fileInputRef}
type="file"
multiple
className="hidden"
onChange={(e) => {
const files = Array.from(e.target.files ?? []);
e.target.value = "";
if (files.length > 0) void upload(files);
}}
/>
<Button
variant="outline"
size="sm"
className="h-7 w-full text-[11px]"
disabled={busyName !== null}
onClick={() => fileInputRef.current?.click()}
>
<Upload className="mr-1 size-3" />
{busyName !== null
? t("remoteDesktop.files.uploading", { name: busyName })
: t("remoteDesktop.files.upload")}
</Button>
</div>
)}
</div>
);
}
@@ -0,0 +1,633 @@
import React, {
useState,
useRef,
useCallback,
useLayoutEffect,
useEffect,
} from "react";
import {
GripVertical,
Monitor,
ChevronLeft,
ChevronRight,
ChevronUp,
ChevronDown,
ChevronsLeftRight,
FolderOpen,
Touchpad,
MousePointer,
ZoomIn,
ZoomOut,
Scan,
X,
} from "lucide-react";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@termix/plugin-sdk/ui";
import type {
GuacamoleDisplayHandle,
GuacamoleTouchMode,
} from "./GuacamoleDisplay.tsx";
import { useTranslation } from "@termix/plugin-sdk/frontend";
import { ActionSlot } from "@termix/plugin-sdk/ui";
import { cn } from "@termix/plugin-sdk/ui";
import {
detectRuntimeMetaKeyFamily,
metaKeyLabels,
type MetaKeyFamily,
} from "./guacamole-meta-key.ts";
interface GuacamoleToolbarProps {
displayRef: React.RefObject<GuacamoleDisplayHandle>;
protocol: "rdp" | "vnc" | "telnet";
touchMode?: GuacamoleTouchMode | null;
hasFilesystem?: boolean;
fileBrowserOpen?: boolean;
onToggleFileBrowser?: () => void;
onTouchModeChange?: (mode: GuacamoleTouchMode) => void;
zoom?: number;
onHide?: () => void;
metaKeyFamily?: MetaKeyFamily;
/** Handed to "remote-desktop.toolbar" actions (the share button). */
slotContext?: RemoteDesktopToolbarContext;
}
export const REMOTE_DESKTOP_TOOLBAR_SLOT = "remote-desktop.toolbar";
/** What a "remote-desktop.toolbar" action is invoked with. */
export interface RemoteDesktopToolbarContext {
hostId: number;
/** guacd's connection id, once the session is up. */
sessionId: string | null;
protocol: "rdp" | "vnc" | "telnet";
tabInstanceId?: string;
/** The backend the session runs on. */
origin?: "local" | "remote";
}
const MODIFIER_KEYSYMS = {
ctrl: 0xffe3,
alt: 0xffe9,
shift: 0xffe1,
win: 0xffeb,
} as const;
const FKEY_KEYSYMS = Array.from({ length: 12 }, (_, i) => 0xffbe + i);
const BTN_BASE =
"flex items-center justify-center gap-1 h-7 px-2 text-[10px] font-medium text-muted-foreground hover:text-foreground hover:bg-muted transition-colors rounded-sm whitespace-nowrap select-none";
const BTN_ICON =
"flex items-center justify-center size-7 text-muted-foreground hover:text-foreground hover:bg-muted transition-colors rounded-sm select-none";
const SEP = "w-px h-5 bg-border mx-0.5 shrink-0";
function TipBtn({
tooltip,
onClick,
className,
children,
}: {
tooltip: string;
onClick: () => void;
className?: string;
children: React.ReactNode;
}) {
return (
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
aria-label={tooltip}
onClick={onClick}
className={cn(BTN_BASE, className)}
>
{children}
</button>
</TooltipTrigger>
<TooltipContent side="bottom" sideOffset={6}>
{tooltip}
</TooltipContent>
</Tooltip>
);
}
function TipIconBtn({
tooltip,
onClick,
className,
children,
}: {
tooltip: string;
onClick: () => void;
className?: string;
children: React.ReactNode;
}) {
return (
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
aria-label={tooltip}
onClick={onClick}
className={cn(BTN_ICON, className)}
>
{children}
</button>
</TooltipTrigger>
<TooltipContent side="bottom" sideOffset={6}>
{tooltip}
</TooltipContent>
</Tooltip>
);
}
export const GuacamoleToolbar: React.FC<GuacamoleToolbarProps> = ({
displayRef,
protocol,
touchMode,
hasFilesystem = false,
fileBrowserOpen = false,
onToggleFileBrowser,
onTouchModeChange,
zoom = 1,
onHide,
metaKeyFamily,
slotContext,
}) => {
const { t } = useTranslation();
const meta = metaKeyLabels(
metaKeyFamily ?? detectRuntimeMetaKeyFamily(protocol),
);
const [position, setPosition] = useState({ x: 0, y: 12 });
const [collapsed, setCollapsed] = useState(false);
const [showFKeys, setShowFKeys] = useState(false);
const [stickyKeys, setStickyKeys] = useState<Record<number, boolean>>({
[MODIFIER_KEYSYMS.ctrl]: false,
[MODIFIER_KEYSYMS.alt]: false,
[MODIFIER_KEYSYMS.shift]: false,
[MODIFIER_KEYSYMS.win]: false,
});
const toolbarRef = useRef<HTMLDivElement>(null);
const isDraggingRef = useRef(false);
const dragOriginRef = useRef({ mouseX: 0, mouseY: 0, posX: 0, posY: 0 });
const [isDragging, setIsDragging] = useState(false);
useLayoutEffect(() => {
const el = toolbarRef.current;
if (!el) return;
const parent = el.offsetParent as HTMLElement | null;
if (!parent) return;
const parentW = parent.clientWidth;
const toolbarW = el.offsetWidth;
setPosition((p) => ({ ...p, x: Math.max(0, (parentW - toolbarW) / 2) }));
}, [collapsed]);
useEffect(() => {
if (!isDragging) return;
const onMove = (e: MouseEvent) => {
if (!isDraggingRef.current) return;
const parent = toolbarRef.current?.offsetParent as HTMLElement | null;
const parentW = parent?.clientWidth ?? Infinity;
const parentH = parent?.clientHeight ?? Infinity;
const toolbarW = toolbarRef.current?.offsetWidth ?? 0;
const toolbarH = toolbarRef.current?.offsetHeight ?? 0;
const dx = e.clientX - dragOriginRef.current.mouseX;
const dy = e.clientY - dragOriginRef.current.mouseY;
setPosition({
x: Math.max(
0,
Math.min(dragOriginRef.current.posX + dx, parentW - toolbarW),
),
y: Math.max(
0,
Math.min(dragOriginRef.current.posY + dy, parentH - toolbarH),
),
});
};
const onUp = () => {
isDraggingRef.current = false;
setIsDragging(false);
document.body.style.userSelect = "";
};
document.addEventListener("mousemove", onMove);
document.addEventListener("mouseup", onUp);
return () => {
document.removeEventListener("mousemove", onMove);
document.removeEventListener("mouseup", onUp);
};
}, [isDragging]);
const startDrag = useCallback(
(e: React.MouseEvent) => {
e.preventDefault();
isDraggingRef.current = true;
dragOriginRef.current = {
mouseX: e.clientX,
mouseY: e.clientY,
posX: position.x,
posY: position.y,
};
document.body.style.userSelect = "none";
setIsDragging(true);
},
[position],
);
const releaseStickyKeys = useCallback(() => {
const display = displayRef.current;
if (!display) return;
setStickyKeys((prev) => {
const next = { ...prev };
for (const [ksStr, active] of Object.entries(prev)) {
if (active) {
display.sendKey(Number(ksStr), false);
next[Number(ksStr)] = false;
}
}
return next;
});
}, [displayRef]);
const sendCombo = useCallback(
(...keysyms: number[]) => {
const display = displayRef.current;
if (!display) return;
for (const k of keysyms) display.sendKey(k, true);
for (const k of [...keysyms].reverse()) display.sendKey(k, false);
releaseStickyKeys();
},
[displayRef, releaseStickyKeys],
);
const toggleStickyKey = useCallback(
(keysym: number) => {
const display = displayRef.current;
if (!display) return;
setStickyKeys((prev) => {
const isActive = prev[keysym];
display.sendKey(keysym, !isActive);
return { ...prev, [keysym]: !isActive };
});
},
[displayRef],
);
const isRdpVnc = protocol === "rdp" || protocol === "vnc";
const containerStyle: React.CSSProperties = {
position: "absolute",
left: position.x,
top: position.y,
zIndex: 20,
};
return (
<TooltipProvider delayDuration={500}>
<div
ref={toolbarRef}
style={containerStyle}
onMouseDown={(e) => e.preventDefault()}
>
{collapsed ? (
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center bg-background/85 backdrop-blur-sm border border-border shadow-lg rounded-sm overflow-hidden">
<button
type="button"
onMouseDown={startDrag}
className="flex items-center justify-center size-7 text-muted-foreground hover:text-foreground hover:bg-muted transition-colors cursor-grab active:cursor-grabbing"
>
<GripVertical className="size-3" />
</button>
<div className="w-px h-4 bg-border" />
<button
type="button"
onClick={() => setCollapsed(false)}
className="flex items-center justify-center size-7 text-muted-foreground hover:text-foreground hover:bg-muted transition-colors"
>
<Monitor className="size-3.5" />
</button>
</div>
</TooltipTrigger>
<TooltipContent side="bottom" sideOffset={6}>
{t("remoteDesktop.toolbar.expand")}
</TooltipContent>
</Tooltip>
) : (
<div className="flex items-center bg-background/85 backdrop-blur-sm border border-border shadow-lg rounded-sm px-0.5 py-0.5 gap-0">
{/* Drag handle */}
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
onMouseDown={startDrag}
className="flex items-center justify-center h-7 px-1 text-muted-foreground hover:text-foreground hover:bg-muted transition-colors rounded-sm cursor-grab active:cursor-grabbing"
>
<GripVertical className="size-3.5" />
</button>
</TooltipTrigger>
<TooltipContent side="bottom" sideOffset={6}>
{t("remoteDesktop.toolbar.dragHandle")}
</TooltipContent>
</Tooltip>
{/* Touch mode toggle — touch devices only */}
{touchMode != null && onTouchModeChange && (
<>
<div className={SEP} />
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
onClick={() =>
onTouchModeChange(
touchMode === "touchscreen"
? "touchpad"
: "touchscreen",
)
}
className={cn(BTN_ICON)}
>
{touchMode === "touchscreen" ? (
<MousePointer className="size-3.5" />
) : (
<Touchpad className="size-3.5" />
)}
</button>
</TooltipTrigger>
<TooltipContent side="bottom" sideOffset={6}>
{touchMode === "touchscreen"
? t("remoteDesktop.toolbar.switchToTrackpad")
: t("remoteDesktop.toolbar.switchToTouch")}
</TooltipContent>
</Tooltip>
</>
)}
{/* Drive files — only once guacd reports a redirected filesystem */}
{hasFilesystem && onToggleFileBrowser && (
<>
<div className={SEP} />
<TipIconBtn
tooltip={t("remoteDesktop.files.title")}
onClick={onToggleFileBrowser}
className={cn(fileBrowserOpen && "bg-muted text-foreground")}
>
<FolderOpen className="size-3.5" />
</TipIconBtn>
</>
)}
{isRdpVnc && (
<>
<div className={SEP} />
<TipIconBtn
tooltip={t("remoteDesktop.toolbar.zoomOut")}
onClick={() => displayRef.current?.zoomOut()}
>
<ZoomOut className="size-3.5" />
</TipIconBtn>
<TipBtn
tooltip={t("remoteDesktop.toolbar.resetZoom")}
onClick={() => displayRef.current?.resetZoom()}
className="min-w-12 tabular-nums"
>
{Math.round(zoom * 100)}%
</TipBtn>
<TipIconBtn
tooltip={t("remoteDesktop.toolbar.zoomIn")}
onClick={() => displayRef.current?.zoomIn()}
>
<ZoomIn className="size-3.5" />
</TipIconBtn>
<TipIconBtn
tooltip={t("remoteDesktop.toolbar.fitToScreen")}
onClick={() => displayRef.current?.resetZoom()}
>
<Scan className="size-3.5" />
</TipIconBtn>
</>
)}
{/* System combos — RDP/VNC only */}
{isRdpVnc && (
<>
<div className={SEP} />
<TipBtn
tooltip={t("remoteDesktop.toolbar.ctrlAltDel")}
onClick={() => sendCombo(0xffe3, 0xffe9, 0xffff)}
>
CAD
</TipBtn>
<TipBtn
tooltip={t(meta.lockTooltip)}
onClick={() => sendCombo(MODIFIER_KEYSYMS.win, 0x006c)}
>
{meta.lock}
</TipBtn>
<TipBtn
tooltip={t(meta.keyTooltip)}
onClick={() => sendCombo(MODIFIER_KEYSYMS.win)}
>
{meta.short}
</TipBtn>
</>
)}
{/* Sticky modifiers — RDP/VNC only */}
{isRdpVnc && (
<>
<div className={SEP} />
{(
[
[
"ctrl",
MODIFIER_KEYSYMS.ctrl,
t("remoteDesktop.toolbar.ctrl"),
],
[
"alt",
MODIFIER_KEYSYMS.alt,
t("remoteDesktop.toolbar.alt"),
],
[
"shift",
MODIFIER_KEYSYMS.shift,
t("remoteDesktop.toolbar.shift"),
],
["win", MODIFIER_KEYSYMS.win, t(meta.stickyKey)],
] as [string, number, string][]
).map(([key, ks, label]) => (
<Tooltip key={key}>
<TooltipTrigger asChild>
<button
type="button"
onClick={() => toggleStickyKey(ks)}
className={cn(
BTN_BASE,
stickyKeys[ks] &&
"bg-primary/15 text-primary border border-primary/30",
)}
>
{label}
</button>
</TooltipTrigger>
<TooltipContent side="bottom" sideOffset={6}>
{stickyKeys[ks]
? t("remoteDesktop.toolbar.stickyActive", {
key: label,
})
: t("remoteDesktop.toolbar.stickyInactive", {
key: label,
})}
</TooltipContent>
</Tooltip>
))}
</>
)}
{/* Function key toggle */}
<div className={SEP} />
<TipBtn
tooltip={t("remoteDesktop.toolbar.fnToggle")}
onClick={() => setShowFKeys((v) => !v)}
className={cn(
showFKeys &&
"bg-primary/15 text-primary border border-primary/30",
)}
>
Fn
</TipBtn>
{/* F1-F12 row */}
{showFKeys &&
FKEY_KEYSYMS.map((ks, i) => (
<TipBtn
key={ks}
tooltip={`F${i + 1}`}
onClick={() => sendCombo(ks)}
>
F{i + 1}
</TipBtn>
))}
{/* Navigation */}
<div className={SEP} />
<TipBtn
tooltip={t("remoteDesktop.toolbar.esc")}
onClick={() => sendCombo(0xff1b)}
>
Esc
</TipBtn>
<TipBtn
tooltip={t("remoteDesktop.toolbar.tab")}
onClick={() => sendCombo(0xff09)}
>
Tab
</TipBtn>
<TipBtn
tooltip={t("remoteDesktop.toolbar.home")}
onClick={() => sendCombo(0xff50)}
>
Home
</TipBtn>
<TipBtn
tooltip={t("remoteDesktop.toolbar.end")}
onClick={() => sendCombo(0xff57)}
>
End
</TipBtn>
<TipBtn
tooltip={t("remoteDesktop.toolbar.pageUp")}
onClick={() => sendCombo(0xff55)}
>
PgUp
</TipBtn>
<TipBtn
tooltip={t("remoteDesktop.toolbar.pageDown")}
onClick={() => sendCombo(0xff56)}
>
PgDn
</TipBtn>
{/* Arrow cluster */}
<div className="flex flex-col ml-0.5">
<div className="flex justify-center">
<TipIconBtn
tooltip={t("remoteDesktop.toolbar.arrowUp")}
onClick={() => sendCombo(0xff52)}
>
<ChevronUp className="size-3" />
</TipIconBtn>
</div>
<div className="flex">
<TipIconBtn
tooltip={t("remoteDesktop.toolbar.arrowLeft")}
onClick={() => sendCombo(0xff51)}
>
<ChevronLeft className="size-3" />
</TipIconBtn>
<TipIconBtn
tooltip={t("remoteDesktop.toolbar.arrowDown")}
onClick={() => sendCombo(0xff54)}
>
<ChevronDown className="size-3" />
</TipIconBtn>
<TipIconBtn
tooltip={t("remoteDesktop.toolbar.arrowRight")}
onClick={() => sendCombo(0xff53)}
>
<ChevronRight className="size-3" />
</TipIconBtn>
</div>
</div>
{slotContext && (
<ActionSlot
slotId={REMOTE_DESKTOP_TOOLBAR_SLOT}
className={cn(BTN_ICON)}
hideLabels
when={{ ...slotContext }}
context={() => [slotContext]}
/>
)}
{/* Collapse */}
<div className="w-px h-5 bg-border mx-0.5 shrink-0" />
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
onClick={() => setCollapsed(true)}
className={cn(BTN_ICON)}
>
<ChevronsLeftRight className="size-3.5" />
</button>
</TooltipTrigger>
<TooltipContent side="bottom" sideOffset={6}>
{t("remoteDesktop.toolbar.collapse")}
</TooltipContent>
</Tooltip>
{onHide && (
<TipIconBtn
tooltip={t("remoteDesktop.toolbar.hide")}
onClick={onHide}
>
<X className="size-3.5" />
</TipIconBtn>
)}
</div>
)}
</div>
</TooltipProvider>
);
};
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,309 @@
import type { PluginApiClient, TermixApp } from "@termix/plugin-sdk/frontend";
import {
isElectron,
resolveRemoteHostId,
type ConnectionOrigin,
} from "@termix/plugin-sdk/ui";
import type { GuacamoleConfig } from "./guacamole-config";
import { errorMessage } from "./host-remote";
let app: Pick<TermixApp, "apiFor"> | null = null;
/** Set in activate, cleared on deactivate. */
export function setRemoteDesktopApp(next: Pick<TermixApp, "apiFor"> | null) {
app = next;
}
/**
* Picks the backend that will actually serve this session, so the token and
* status come from the same place as the connection.
*
* Outside Electron there is only one backend. Inside it, a host left on
* Default still resolves to the connected server -- asking the embedded
* backend would report the guacd *it* can reach rather than the one serving
* the session. A host explicitly set to "This Device" (Support#1240) is
* served by the embedded backend against the user's own guacd, so its calls
* have to go there instead.
*
* Callers pass the origin explicitly rather than defaulting it: a default
* silently sent a missed call site to the remote server, which fails with a
* bare network error on a desktop that has none configured.
*/
function guacamoleApi(origin: ConnectionOrigin): PluginApiClient {
if (!app) throw new Error("Remote Desktop is not active");
return app.apiFor(origin);
}
function apiError(error: unknown, action: string): Error {
return new Error(errorMessage(error, `Failed to ${action}`));
}
export interface GuacamoleTokenRequest {
protocol: "rdp" | "vnc" | "telnet";
hostname: string;
port?: number;
username?: string;
password?: string;
domain?: string;
security?: string;
ignoreCert?: boolean;
guacamoleConfig?: GuacamoleConfig;
}
export interface GuacamoleTokenResponse {
token: string;
guacamoleConnectionId?: string | null;
termixConnectId?: string;
}
type GuacamoleConfigSource = {
guacamoleConfig?: string | Record<string, unknown> | null;
};
export function getGuacamoleDpi(
source?: GuacamoleConfigSource,
): number | undefined {
const config = source?.guacamoleConfig;
if (!config) return undefined;
let dpi: unknown;
if (typeof config === "string") {
try {
dpi = JSON.parse(config).dpi;
} catch {
return undefined;
}
} else {
dpi = config.dpi;
}
const parsedDpi = typeof dpi === "string" ? Number(dpi) : dpi;
if (
typeof parsedDpi !== "number" ||
!Number.isFinite(parsedDpi) ||
parsedDpi <= 0
) {
return undefined;
}
return Math.trunc(parsedDpi);
}
function toGuacamoleParams(
config: GuacamoleTokenRequest["guacamoleConfig"],
): Record<string, unknown> {
if (!config) return {};
const params: Record<string, unknown> = {};
const mappings: Record<string, string> = {
colorDepth: "color-depth",
resizeMethod: "resize-method",
forceLossless: "force-lossless",
disableAudio: "disable-audio",
enableAudioInput: "enable-audio-input",
enableWallpaper: "enable-wallpaper",
enableTheming: "enable-theming",
enableFontSmoothing: "enable-font-smoothing",
enableFullWindowDrag: "enable-full-window-drag",
enableDesktopComposition: "enable-desktop-composition",
enableMenuAnimations: "enable-menu-animations",
disableBitmapCaching: "disable-bitmap-caching",
disableOffscreenCaching: "disable-offscreen-caching",
disableGlyphCaching: "disable-glyph-caching",
disableGfx: "disable-gfx",
enablePrinting: "enable-printing",
printerName: "printer-name",
enableDrive: "enable-drive",
driveName: "drive-name",
drivePath: "drive-path",
createDrivePath: "create-drive-path",
disableDownload: "disable-download",
disableUpload: "disable-upload",
enableTouch: "enable-touch",
clientName: "client-name",
initialProgram: "initial-program",
serverLayout: "server-layout",
gatewayHostname: "gateway-hostname",
gatewayPort: "gateway-port",
gatewayUsername: "gateway-username",
gatewayPassword: "gateway-password",
gatewayDomain: "gateway-domain",
remoteApp: "remote-app",
remoteAppDir: "remote-app-dir",
remoteAppArgs: "remote-app-args",
normalizeClipboard: "normalize-clipboard",
disableCopy: "disable-copy",
disablePaste: "disable-paste",
swapRedBlue: "swap-red-blue",
readOnly: "read-only",
recordingPath: "recording-path",
recordingName: "recording-name",
createRecordingPath: "create-recording-path",
recordingExcludeOutput: "recording-exclude-output",
recordingExcludeMouse: "recording-exclude-mouse",
recordingIncludeKeys: "recording-include-keys",
wolSendPacket: "wol-send-packet",
wolMacAddr: "wol-mac-addr",
wolBroadcastAddr: "wol-broadcast-addr",
wolUdpPort: "wol-udp-port",
wolWaitTime: "wol-wait-time",
};
for (const [key, value] of Object.entries(config)) {
if (value !== undefined && value !== null && value !== "") {
const paramName = mappings[key] || key;
if (typeof value === "boolean") {
params[paramName] = value ? "true" : "false";
} else {
params[paramName] = value;
}
}
}
return params;
}
export async function getGuacamoleToken(
request: GuacamoleTokenRequest,
origin: ConnectionOrigin,
): Promise<GuacamoleTokenResponse> {
try {
const guacParams = toGuacamoleParams(request.guacamoleConfig);
const response = await guacamoleApi(origin).post<GuacamoleTokenResponse>(
"/token",
{
type: request.protocol,
hostname: request.hostname,
port: request.port,
username: request.username,
password: request.password,
domain: request.domain,
security: request.security,
"ignore-cert": request.ignoreCert,
...guacParams,
},
);
return response.data;
} catch (error) {
throw apiError(error, "get a connection token");
}
}
export async function getGuacamoleTokenFromHost(
hostId: number,
origin: ConnectionOrigin,
protocol?: "rdp" | "vnc" | "telnet",
promptedCredentials?: {
username?: string;
password?: string;
domain?: string;
},
syncId?: string | null,
tabInstanceId?: string,
): Promise<GuacamoleTokenResponse> {
try {
// A locally-originated session is served by the embedded backend, which
// knows the host by its local id -- remapping it onto the remote
// server's id would address the wrong row, or fail outright when no
// server is configured at all.
const useRemote = isElectron() && origin === "remote";
const remoteHostId = useRemote ? await resolveRemoteHostId(syncId) : null;
if (useRemote && syncId && remoteHostId === null) {
throw new Error("The synced host does not exist on the remote server");
}
const targetHostId = remoteHostId ?? hostId;
const response = await guacamoleApi(origin).post<GuacamoleTokenResponse>(
`/connect-host/${targetHostId}`,
{
...(protocol ? { protocol } : {}),
...(tabInstanceId ? { tabInstanceId } : {}),
...(promptedCredentials?.username
? { promptedUsername: promptedCredentials.username }
: {}),
...(promptedCredentials?.password
? { promptedPassword: promptedCredentials.password }
: {}),
...(promptedCredentials
? { promptedDomain: promptedCredentials.domain ?? "" }
: {}),
},
);
return response.data;
} catch (error) {
throw apiError(error, "get a connection token");
}
}
export interface RemoteDesktopStatus {
enabled: boolean;
guacd: { status: string };
}
/** `probe: false` skips dialing guacd, for a caller that only needs `enabled`. */
export async function getGuacdStatus(
origin: ConnectionOrigin,
options: {
probe?: boolean;
hostId?: number;
protocol?: "rdp" | "vnc" | "telnet";
syncId?: string | null;
} = {},
): Promise<RemoteDesktopStatus> {
const params = new URLSearchParams();
if (options.probe === false) params.set("probe", "0");
if (options.hostId) {
const useRemote = isElectron() && origin === "remote";
const remoteHostId = useRemote
? await resolveRemoteHostId(options.syncId)
: null;
if (useRemote && options.syncId && remoteHostId === null) {
throw new Error("The synced host does not exist on the remote server");
}
params.set("hostId", String(remoteHostId ?? options.hostId));
if (options.protocol) params.set("protocol", options.protocol);
}
const query = params.toString();
const response = await guacamoleApi(origin).get<RemoteDesktopStatus>(
query ? `/status?${query}` : "/status",
);
return response.data;
}
export async function getGuacamoleConnectionId(
connectId: string,
origin: ConnectionOrigin,
signal?: AbortSignal,
): Promise<string | null> {
const response = await guacamoleApi(origin).get<{
guacamoleConnectionId?: string | null;
}>(`/connection/${encodeURIComponent(connectId)}`, { signal });
return response.data.guacamoleConnectionId ?? null;
}
export async function nativeRdpAvailable(): Promise<boolean> {
if (!isElectron()) return false;
try {
const response = await guacamoleApi("local").get<{ available: boolean }>(
"/native-rdp",
);
return response.data.available === true;
} catch {
return false;
}
}
/** Always the embedded backend: it is the one that can reach Electron. */
export async function openNativeRdp(request: {
host: string;
port?: number;
username?: string;
domain?: string;
}): Promise<{ success: boolean; error?: string }> {
const response = await guacamoleApi("local").post<{
success: boolean;
error?: string;
}>("/native-rdp", request);
return response.data;
}
@@ -0,0 +1,39 @@
import Guacamole from "guacamole-common-js";
const CONTROL_LEFT_KEYSYM = 0xffe3;
const V_KEYSYM = 0x76;
interface ClipboardOutputStream {
sendBlob(data: string): void;
sendEnd(): void;
}
export interface GuacamoleClipboardClient {
createClipboardStream(mimetype: string): ClipboardOutputStream;
sendKeyEvent(pressed: number, keysym: number): void;
}
export function isPasteShortcut(
event: Pick<KeyboardEvent, "altKey" | "ctrlKey" | "key" | "metaKey">,
): boolean {
return (
!event.altKey &&
(event.ctrlKey || event.metaKey) &&
event.key.toLowerCase() === "v"
);
}
export function pasteTextToRemote(
client: GuacamoleClipboardClient,
text: string,
): void {
const stream = client.createClipboardStream("text/plain");
const writer = new Guacamole.StringWriter(stream);
writer.sendText(text);
writer.sendEnd();
client.sendKeyEvent(1, CONTROL_LEFT_KEYSYM);
client.sendKeyEvent(1, V_KEYSYM);
client.sendKeyEvent(0, V_KEYSYM);
client.sendKeyEvent(0, CONTROL_LEFT_KEYSYM);
}
@@ -0,0 +1,225 @@
declare module "guacamole-common-js" {
namespace Guacamole {
class Client {
constructor(tunnel: Tunnel);
connect(data?: string): void;
disconnect(): void;
getDisplay(): Display;
sendKeyEvent(pressed: number, keysym: number): void;
sendMouseState(state: Mouse.State): void;
sendSize(width: number, height: number): void;
setClipboard(stream: OutputStream, mimetype: string): void;
createClipboardStream(mimetype: string): OutputStream;
onstatechange: ((state: number) => void) | null;
onerror: ((error: Status) => void) | null;
onsync: ((timestamp: number) => void) | null;
onclipboard: ((stream: InputStream, mimetype: string) => void) | null;
onaudio: ((stream: InputStream, mimetype: string) => void) | null;
onfile:
| ((stream: InputStream, mimetype: string, filename: string) => void)
| null;
onfilesystem: ((filesystem: Object, name: string) => void) | null;
}
// Mirrors Guacamole.Object: a named collection of streams. Within this
// namespace `Object` refers to this class, not the global one.
class Object {
static readonly ROOT_STREAM: string;
static readonly STREAM_INDEX_MIMETYPE: string;
readonly index: number;
requestInputStream(
name: string,
bodyCallback?: (stream: InputStream, mimetype: string) => void,
): void;
createOutputStream(mimetype: string, name: string): OutputStream;
onbody: ((stream: InputStream, mimetype: string) => void) | null;
onundefine: (() => void) | null;
}
class AudioPlayer {
static getInstance(
stream: InputStream,
mimetype: string,
): AudioPlayer | null;
sync(): void;
}
class Display {
getElement(): HTMLElement;
getWidth(): number;
getHeight(): number;
scale(scale: number): void;
onresize: (() => void) | null;
}
class Tunnel {
onerror: ((status: Status) => void) | null;
onstatechange: ((state: number) => void) | null;
}
class WebSocketTunnel extends Tunnel {
constructor(url: string);
}
class SessionRecording {
constructor(source: Blob | Tunnel, refreshInterval?: number);
onload: (() => void) | null;
onerror: ((message: string) => void) | null;
onprogress: ((duration: number, parsedSize: number) => void) | null;
onplay: (() => void) | null;
onpause: (() => void) | null;
onseek:
((position: number, current: number, total: number) => void) | null;
getDisplay(): Display;
getPosition(): number;
getDuration(): number;
isPlaying(): boolean;
play(): void;
pause(): void;
seek(position: number, callback?: () => void): void;
abort(): void;
}
class Mouse {
constructor(element: HTMLElement);
onmousedown: ((state: Mouse.State) => void) | null;
onmouseup: ((state: Mouse.State) => void) | null;
onmousemove: ((state: Mouse.State) => void) | null;
onmouseout: ((state: Mouse.State) => void) | null;
}
namespace Mouse {
class State {
constructor(
x: number,
y: number,
left?: boolean,
middle?: boolean,
right?: boolean,
up?: boolean,
down?: boolean,
);
constructor(state: {
x: number;
y: number;
left?: boolean;
middle?: boolean;
right?: boolean;
up?: boolean;
down?: boolean;
});
x: number;
y: number;
left: boolean;
middle: boolean;
right: boolean;
up: boolean;
down: boolean;
}
interface MouseEvent {
state: Mouse.State;
preventDefault(): void;
stopPropagation(): void;
}
class Touchpad {
constructor(element: HTMLElement);
currentState: Mouse.State;
clickTimingThreshold: number;
clickMoveThreshold: number;
scrollThreshold: number;
onEach(
types: string[],
listener: (event: Mouse.MouseEvent) => void,
): void;
on(type: string, listener: (event: Mouse.MouseEvent) => void): void;
}
class Touchscreen {
constructor(element: HTMLElement);
currentState: Mouse.State;
clickTimingThreshold: number;
clickMoveThreshold: number;
scrollThreshold: number;
longPressThreshold: number;
onEach(
types: string[],
listener: (event: Mouse.MouseEvent) => void,
): void;
on(type: string, listener: (event: Mouse.MouseEvent) => void): void;
}
}
class Keyboard {
constructor(element: Document | HTMLElement);
reset(): void;
onkeydown: ((keysym: number) => void) | null;
onkeyup: ((keysym: number) => void) | null;
}
class Status {
code: number;
message: string;
isError(): boolean;
static readonly Code: {
SUCCESS: number;
UNSUPPORTED: number;
SERVER_ERROR: number;
SERVER_BUSY: number;
UPSTREAM_TIMEOUT: number;
UPSTREAM_ERROR: number;
RESOURCE_NOT_FOUND: number;
RESOURCE_CONFLICT: number;
RESOURCE_CLOSED: number;
CLIENT_BAD_REQUEST: number;
CLIENT_UNAUTHORIZED: number;
CLIENT_FORBIDDEN: number;
CLIENT_TIMEOUT: number;
};
}
class InputStream {
onblob: ((data: string) => void) | null;
onend: (() => void) | null;
sendAck(message: string, code: number): void;
}
class OutputStream {
sendBlob(data: string): void;
sendEnd(): void;
}
class StringReader {
constructor(stream: InputStream);
ontext: ((text: string) => void) | null;
onend: (() => void) | null;
}
class StringWriter {
constructor(stream: OutputStream);
sendText(text: string): void;
sendEnd(): void;
}
class BlobReader {
constructor(stream: InputStream, mimetype: string);
getBlob(): Blob;
getLength(): number;
onprogress: ((length: number) => void) | null;
onend: (() => void) | null;
}
class BlobWriter {
constructor(stream: OutputStream);
sendBlob(blob: Blob): void;
sendEnd(): void;
onack: ((status: Status) => void) | null;
onerror: ((blob: Blob, offset: number, error: Status) => void) | null;
onprogress: ((blob: Blob, offset: number) => void) | null;
oncomplete: ((blob: Blob) => void) | null;
}
}
export default Guacamole;
}
@@ -0,0 +1,64 @@
/**
* Per-host Guacamole connection settings, as edited in the host editor and
* forwarded to guacd. Field names are camelCase here and translated to the
* hyphenated protocol parameters in toGuacamoleParams().
*/
export interface GuacamoleConfig {
colorDepth?: number;
width?: number;
height?: number;
dpi?: number;
resizeMethod?: string;
forceLossless?: boolean;
disableAudio?: boolean;
enableAudioInput?: boolean;
enableWallpaper?: boolean;
enableTheming?: boolean;
enableFontSmoothing?: boolean;
enableFullWindowDrag?: boolean;
enableDesktopComposition?: boolean;
enableMenuAnimations?: boolean;
disableBitmapCaching?: boolean;
disableOffscreenCaching?: boolean;
disableGlyphCaching?: boolean;
disableGfx?: boolean;
enablePrinting?: boolean;
printerName?: string;
enableDrive?: boolean;
driveName?: string;
drivePath?: string;
createDrivePath?: boolean;
disableDownload?: boolean;
disableUpload?: boolean;
enableTouch?: boolean;
clientName?: string;
console?: boolean;
initialProgram?: string;
serverLayout?: string;
timezone?: string;
gatewayHostname?: string;
gatewayPort?: number;
gatewayUsername?: string;
gatewayPassword?: string;
gatewayDomain?: string;
remoteApp?: string;
remoteAppDir?: string;
remoteAppArgs?: string;
normalizeClipboard?: string;
disableCopy?: boolean;
disablePaste?: boolean;
cursor?: string;
swapRedBlue?: boolean;
readOnly?: boolean;
recordingPath?: string;
recordingName?: string;
createRecordingPath?: boolean;
recordingExcludeOutput?: boolean;
recordingExcludeMouse?: boolean;
recordingIncludeKeys?: boolean;
wolSendPacket?: boolean;
wolMacAddr?: string;
wolBroadcastAddr?: string;
wolUdpPort?: number;
wolWaitTime?: number;
}
@@ -0,0 +1,48 @@
const DEFAULT_RDP_DPI = 96;
const MAX_DEVICE_PIXEL_RATIO = 3;
/**
* Reads a guacamoleConfig display field. The UI stores these as strings, and
* leaves them empty when the size should follow the browser window.
*/
export function readConfiguredDimension(value: unknown): number | undefined {
const parsed = Number(value);
return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
}
export interface GuacamoleDisplaySize {
width: number;
height: number;
dpi?: number;
pixelRatio: number;
}
export function getGuacamoleDisplaySize(
cssWidth: number,
cssHeight: number,
protocol: string | undefined,
devicePixelRatio: number,
configuredDpi?: number,
): GuacamoleDisplaySize {
const isRdp = protocol === "rdp";
const pixelRatio = isRdp
? Math.min(
MAX_DEVICE_PIXEL_RATIO,
Math.max(1, Number.isFinite(devicePixelRatio) ? devicePixelRatio : 1),
)
: 1;
const size = {
width: Math.max(1, Math.round(cssWidth * pixelRatio)),
height: Math.max(1, Math.round(cssHeight * pixelRatio)),
pixelRatio,
};
if (!isRdp) return size;
const baseDpi =
configuredDpi && Number.isFinite(configuredDpi) && configuredDpi > 0
? configuredDpi
: DEFAULT_RDP_DPI;
return { ...size, dpi: Math.round(baseDpi * pixelRatio) };
}
@@ -0,0 +1,22 @@
export type FileDropDisposition = "ignore" | "reject" | "upload";
export function hasDraggedFiles(types: readonly string[]): boolean {
return types.includes("Files");
}
export function getFileDropDisposition(
types: readonly string[],
fileCount: number,
canUpload: boolean,
): FileDropDisposition {
if (!hasDraggedFiles(types) || fileCount === 0) return "ignore";
return canUpload ? "upload" : "reject";
}
export function canUploadToRdpDrive(
uploadAllowed: boolean,
driveEnabled: boolean,
hasFilesystem: boolean,
): boolean {
return uploadAllowed && (driveEnabled || hasFilesystem);
}
@@ -0,0 +1,223 @@
import Guacamole from "guacamole-common-js";
// The bundled Guacamole runtime exposes Client.createFileStream(), but the
// package's older TypeScript declaration omits it.
export interface GuacamoleFileStreamClient {
createFileStream(mimetype: string, filename: string): Guacamole.OutputStream;
}
// The root stream of a Guacamole.Object maps stream name to mimetype; a stream
// carrying that same mimetype is itself a directory.
export const STREAM_INDEX_MIMETYPE =
"application/vnd.glyptodon.guacamole.stream-index+json";
export interface RemoteFileEntry {
name: string;
path: string;
mimetype: string;
isDirectory: boolean;
}
export function isDirectoryMimetype(mimetype: string): boolean {
return mimetype === STREAM_INDEX_MIMETYPE;
}
export function joinPath(parent: string, name: string): string {
return parent === "/" ? `/${name}` : `${parent}/${name}`;
}
export function parentPath(path: string): string {
const cut = path.lastIndexOf("/");
return cut <= 0 ? "/" : path.slice(0, cut);
}
export function basename(path: string): string {
return path.slice(path.lastIndexOf("/") + 1) || path;
}
// guacd returns absolute paths as keys, but a malformed or relative key would
// otherwise produce a broken path on the next descent.
export function parseDirectoryIndex(
json: string,
path: string,
): RemoteFileEntry[] {
const index = JSON.parse(json) as Record<string, string>;
return Object.entries(index)
.map(([key, mimetype]) => {
const name = basename(key);
return {
name,
path: key.startsWith("/") ? key : joinPath(path, name),
mimetype,
isDirectory: isDirectoryMimetype(mimetype),
};
})
.sort((a, b) =>
a.isDirectory === b.isDirectory
? a.name.localeCompare(b.name)
: a.isDirectory
? -1
: 1,
);
}
// A refused stream is answered with an error ack that never reaches the body
// callback, so every request needs its own deadline to avoid hanging forever.
const REQUEST_TIMEOUT_MS = 15000;
function requestStream(
filesystem: Guacamole.Object,
path: string,
): Promise<{ stream: Guacamole.InputStream; mimetype: string }> {
return new Promise((resolve, reject) => {
const timeout = setTimeout(
() => reject(new Error(`Timed out reading ${path}`)),
REQUEST_TIMEOUT_MS,
);
filesystem.requestInputStream(path, (stream, mimetype) => {
clearTimeout(timeout);
resolve({ stream, mimetype });
});
});
}
export async function listDirectory(
filesystem: Guacamole.Object,
path: string,
): Promise<RemoteFileEntry[]> {
const { stream, mimetype } = await requestStream(filesystem, path);
if (!isDirectoryMimetype(mimetype)) {
throw new Error(`${path} is not a directory`);
}
const json = await new Promise<string>((resolve) => {
const reader = new Guacamole.StringReader(stream);
let text = "";
reader.ontext = (chunk: string) => {
text += chunk;
};
reader.onend = () => resolve(text);
});
return parseDirectoryIndex(json, path);
}
export async function downloadFile(
filesystem: Guacamole.Object,
path: string,
): Promise<{ blob: Blob; filename: string }> {
const { stream, mimetype } = await requestStream(filesystem, path);
if (isDirectoryMimetype(mimetype)) {
throw new Error(`${path} is a directory`);
}
const blob = await new Promise<Blob>((resolve) => {
const reader = new Guacamole.BlobReader(stream, mimetype);
reader.onend = () => resolve(reader.getBlob());
});
return { blob, filename: basename(path) };
}
export function uploadFile(
filesystem: Guacamole.Object,
directory: string,
file: File,
onProgress?: (sent: number, total: number) => void,
): Promise<void> {
return writeUpload(
filesystem.createOutputStream(
file.type || "application/octet-stream",
joinPath(directory, file.name),
),
file,
onProgress,
);
}
/**
* Uploads through Guacamole's connection-level file stream. RDP exposes this
* path even when guacd does not advertise a browsable filesystem object, so
* display drops must not depend on `Client.onfilesystem` having fired.
*/
export function uploadFileToClient(
client: GuacamoleFileStreamClient,
file: File,
onProgress?: (sent: number, total: number) => void,
): Promise<void> {
return writeUpload(createClientFileStream(client, file), file, onProgress);
}
export function createClientFileStream(
client: GuacamoleFileStreamClient,
file: File,
): Guacamole.OutputStream {
return client.createFileStream(
file.type || "application/octet-stream",
file.name,
);
}
function writeUpload(
stream: Guacamole.OutputStream,
file: File,
onProgress?: (sent: number, total: number) => void,
): Promise<void> {
return new Promise((resolve, reject) => {
const writer = new Guacamole.BlobWriter(stream);
// A rejected blob stops the writer without firing onerror or oncomplete —
// only the error ack reports it, so without this the upload hangs forever.
writer.onack = (status: Guacamole.Status) => {
if (status.isError()) {
reject(
new Error(
status.message || `Server refused the upload of ${file.name}`,
),
);
}
};
writer.onerror = () => reject(new Error(`Failed to upload ${file.name}`));
writer.onprogress = (_blob: Blob, offset: number) =>
onProgress?.(offset, file.size);
writer.oncomplete = () => {
writer.sendEnd();
onProgress?.(file.size, file.size);
resolve();
};
writer.sendBlob(file);
});
}
export function saveBlobAs(blob: Blob, filename: string): void {
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = filename;
link.click();
URL.revokeObjectURL(url);
}
/**
* Turns an upload failure into something a user can act on. guacd answers a
* refused stream with an ack like "FAIL (CANNOT OPEN)" - accurate, but it
* means "the drive folder isn't writable", which is what people need to hear.
*/
export function describeUploadError(
error: unknown,
t: (key: "driveNotWritable" | "driveUnavailable" | "uploadFailed") => string,
): string {
const message = error instanceof Error ? error.message : "";
if (/cannot open|can't open|permission denied/i.test(message)) {
return t("driveNotWritable");
}
if (/no fs/i.test(message)) {
return t("driveUnavailable");
}
return message || t("uploadFailed");
}
@@ -0,0 +1,82 @@
export type MetaKeyFamily = "win" | "super" | "cmd";
export interface MetaKeyLabels {
family: MetaKeyFamily;
short: string;
lock: string;
stickyKey: string;
keyTooltip: string;
lockTooltip: string;
}
/**
* RDP is a Windows desktop, so the toolbar keeps Windows labels. VNC is used
* for Linux and macOS as often as Windows; branding those sessions with a
* Windows key is wrong, and the local device is the only OS we can see.
*/
export function detectMetaKeyFamily(
protocol: "rdp" | "vnc" | "telnet",
env: {
platform?: string;
userAgent?: string;
userAgentDataPlatform?: string;
} = {},
): MetaKeyFamily {
if (protocol === "rdp") return "win";
const uaPlatform = env.userAgentDataPlatform ?? "";
const platform = env.platform ?? "";
const userAgent = env.userAgent ?? "";
const haystack = `${uaPlatform} ${platform} ${userAgent}`;
if (/iphone|ipad|ipod|mac/i.test(haystack)) return "cmd";
if (/win/i.test(haystack)) return "win";
return "super";
}
export function detectRuntimeMetaKeyFamily(
protocol: "rdp" | "vnc" | "telnet",
): MetaKeyFamily {
if (typeof navigator === "undefined") {
return detectMetaKeyFamily(protocol);
}
const uaData = navigator as Navigator & {
userAgentData?: { platform?: string };
};
return detectMetaKeyFamily(protocol, {
platform: navigator.platform,
userAgent: navigator.userAgent,
userAgentDataPlatform: uaData.userAgentData?.platform,
});
}
export function metaKeyLabels(family: MetaKeyFamily): MetaKeyLabels {
if (family === "cmd") {
return {
family,
short: "Cmd",
lock: "Cmd+L",
stickyKey: "remoteDesktop.toolbar.cmd",
keyTooltip: "remoteDesktop.toolbar.cmdKey",
lockTooltip: "remoteDesktop.toolbar.cmdL",
};
}
if (family === "super") {
return {
family,
short: "Super",
lock: "Super+L",
stickyKey: "remoteDesktop.toolbar.super",
keyTooltip: "remoteDesktop.toolbar.superKey",
lockTooltip: "remoteDesktop.toolbar.superL",
};
}
return {
family,
short: "Win",
lock: "Win+L",
stickyKey: "remoteDesktop.toolbar.win",
keyTooltip: "remoteDesktop.toolbar.winKey",
lockTooltip: "remoteDesktop.toolbar.winL",
};
}
@@ -0,0 +1,65 @@
import Guacamole from "guacamole-common-js";
import {
createScrollCoalescer,
isScrollButtonState,
} from "./guacamole-scroll.js";
export type GuacamoleTouchMode = "touchscreen" | "touchpad";
/**
* Guacamole.Mouse listens for mousedown/mousemove/mouseup; the Touchscreen and
* Touchpad emulators listen only for touch events. The two sets do not overlap,
* so binding one instead of the other leaves that input dead.
*
* A touch-capable device is not a device without a pointer: laptops with a
* touchscreen report maxTouchPoints > 0 and are still driven by a mouse. The
* physical pointer is therefore always bound, and a touch emulator is layered
* on top when one is selected.
*/
export function bindPointerInput(
element: HTMLElement,
touchMode: GuacamoleTouchMode | null | undefined,
onState: (state: Guacamole.Mouse.State) => void,
): () => void {
const coalescer = createScrollCoalescer((state) => {
onState(state as Guacamole.Mouse.State);
});
let expectingScrollRelease = false;
const handleState = (state: Guacamole.Mouse.State) => {
coalescer.notePointer(state);
if (isScrollButtonState(state)) {
coalescer.ingestClick(state.down ? 1 : -1);
expectingScrollRelease = true;
return;
}
if (
expectingScrollRelease &&
!state.left &&
!state.middle &&
!state.right
) {
expectingScrollRelease = false;
return;
}
expectingScrollRelease = false;
onState(state);
};
const mouse = new Guacamole.Mouse(element);
mouse.onmousedown = mouse.onmouseup = mouse.onmousemove = handleState;
if (touchMode === "touchscreen") {
const touchscreen = new Guacamole.Mouse.Touchscreen(element);
touchscreen.onEach(["mousedown", "mousemove", "mouseup"], (event) =>
handleState(event.state),
);
} else if (touchMode === "touchpad") {
const touchpad = new Guacamole.Mouse.Touchpad(element);
touchpad.onEach(["mousedown", "mousemove", "mouseup"], (event) =>
handleState(event.state),
);
}
return () => coalescer.dispose();
}
@@ -0,0 +1,131 @@
/**
* guacamole-common-js turns each wheel tick into one or more mouse-button
* clicks (button 4/5). High-resolution trackpads and the library's duplicate
* wheel/mousewheel listeners can emit dozens of those clicks in a single
* gesture. Each click is two Guacamole instructions, so a laggy path (a
* Cloudflare-proxied remote session especially) queues them and the remote
* desktop keeps scrolling long after the user stopped.
*
* Cap the in-flight click count and space flushes out so scrolling stays
* usable without changing the websocket proxy.
*/
export const MAX_SCROLL_CLICKS_PER_FLUSH = 2;
export const SCROLL_FLUSH_INTERVAL_MS = 32;
export type ScrollClickDirection = 1 | -1;
export interface GuacamolePointerState {
x: number;
y: number;
left: boolean;
middle: boolean;
right: boolean;
up: boolean;
down: boolean;
}
export function clampPendingScrollClicks(
pending: number,
delta: number,
max = MAX_SCROLL_CLICKS_PER_FLUSH,
): number {
return Math.max(-max, Math.min(max, pending + delta));
}
export function takeScrollClicks(pending: number): {
clicks: number;
direction: ScrollClickDirection | 0;
} {
if (pending === 0) return { clicks: 0, direction: 0 };
const direction: ScrollClickDirection = pending < 0 ? -1 : 1;
return {
clicks: Math.min(MAX_SCROLL_CLICKS_PER_FLUSH, Math.abs(pending)),
direction,
};
}
function mouseState(
x: number,
y: number,
buttons: Pick<GuacamolePointerState, "left" | "middle" | "right">,
up: boolean,
down: boolean,
): GuacamolePointerState {
return {
x,
y,
left: buttons.left,
middle: buttons.middle,
right: buttons.right,
up,
down,
};
}
export function createScrollCoalescer(
send: (state: GuacamolePointerState) => void,
now: () => number = () => Date.now(),
schedule: (fn: () => void, ms: number) => number = (fn, ms) =>
window.setTimeout(fn, ms),
cancel: (id: number) => void = (id) => window.clearTimeout(id),
): {
notePointer: (state: GuacamolePointerState) => void;
ingestClick: (direction: ScrollClickDirection) => void;
dispose: () => void;
} {
let pending = 0;
let timer: number | null = null;
let x = 0;
let y = 0;
let buttons = { left: false, middle: false, right: false };
let lastFlushAt = 0;
const flush = () => {
timer = null;
const { clicks, direction } = takeScrollClicks(pending);
pending = 0;
if (clicks === 0 || direction === 0) return;
lastFlushAt = now();
const up = direction < 0;
const down = direction > 0;
for (let i = 0; i < clicks; i++) {
send(mouseState(x, y, buttons, up, down));
send(mouseState(x, y, buttons, false, false));
}
};
return {
notePointer(state) {
x = state.x;
y = state.y;
buttons = {
left: state.left,
middle: state.middle,
right: state.right,
};
},
ingestClick(direction) {
pending = clampPendingScrollClicks(pending, direction);
const elapsed = now() - lastFlushAt;
if (elapsed >= SCROLL_FLUSH_INTERVAL_MS) {
flush();
return;
}
if (timer == null) {
timer = schedule(flush, SCROLL_FLUSH_INTERVAL_MS - elapsed);
}
},
dispose() {
if (timer == null) return;
cancel(timer);
timer = null;
},
};
}
export function isScrollButtonState(state: {
up?: boolean;
down?: boolean;
}): boolean {
return state.up === true || state.down === true;
}
@@ -0,0 +1,31 @@
import { getGuacamoleConnectionId } from "./guacamole-api";
import type { ConnectionOrigin } from "@termix/plugin-sdk/ui";
export function watchGuacamoleConnectionId(
connectId: string,
origin: ConnectionOrigin,
onReady: (id: string) => void,
): () => void {
const controller = new AbortController();
let timer: ReturnType<typeof setTimeout> | undefined;
let attempts = 0;
const poll = async () => {
try {
const id = await getGuacamoleConnectionId(
connectId,
origin,
controller.signal,
);
if (controller.signal.aborted) return;
if (id) onReady(id);
else if (++attempts < 20) timer = setTimeout(poll, 500);
} catch {
// Session sharing discovery must not interrupt the remote display.
}
};
void poll();
return () => {
controller.abort();
clearTimeout(timer);
};
}
@@ -0,0 +1,26 @@
import type { ConnectionStage } from "@termix/plugin-sdk/ui";
// Guacamole client states, per guacamole-common-js Guacamole.Client#STATE_*.
const GUAC_STATE_IDLE = 0;
const GUAC_STATE_CONNECTING = 1;
const GUAC_STATE_WAITING = 2;
const GUAC_STATE_CONNECTED = 3;
const GUAC_STATE_DISCONNECTING = 4;
const GUAC_STATE_DISCONNECTED = 5;
export function guacStateToStage(state: number): ConnectionStage {
switch (state) {
case GUAC_STATE_CONNECTING:
return "guac_connecting";
case GUAC_STATE_WAITING:
return "guac_handshake";
case GUAC_STATE_CONNECTED:
return "guac_ready";
case GUAC_STATE_DISCONNECTING:
case GUAC_STATE_DISCONNECTED:
return "guac_disconnected";
case GUAC_STATE_IDLE:
default:
return "guac_connecting";
}
}
@@ -0,0 +1,39 @@
/**
* Where the Guacamole display socket lives.
*
* One path in every deployment: the plugin serves it at
* /plugin-ws/remote-desktop/display on the main backend, so there is no
* guacamole port and no separate nginx block any more. Electron with a remote
* server is the only case that targets a different host.
*/
export function buildGuacamoleWebSocketBaseUrl({
isElectronApp,
isEmbeddedApp,
configuredServerUrl,
basePath,
location,
}: {
isDev?: boolean;
isElectronApp: boolean;
isEmbeddedApp: boolean;
configuredServerUrl?: string;
basePath: string;
location: Pick<Location, "protocol" | "host">;
}) {
const route = "/plugin-ws/remote-desktop/display";
if (isElectronApp && !isEmbeddedApp && configuredServerUrl) {
const wsProtocol = configuredServerUrl.startsWith("https://")
? "wss://"
: "ws://";
const wsHost = configuredServerUrl
.replace(/^https?:\/\//, "")
.replace(/\/$/, "");
return `${wsProtocol}${wsHost}${route}`;
}
if (isElectronApp) return `ws://127.0.0.1:30001${route}`;
const wsProtocol = location.protocol === "https:" ? "wss" : "ws";
return `${wsProtocol}://${location.host}${basePath}${route}`;
}
@@ -0,0 +1,11 @@
export const MIN_GUACAMOLE_ZOOM = 0.5;
export const MAX_GUACAMOLE_ZOOM = 4;
export const GUACAMOLE_ZOOM_STEP = 0.25;
export function clampGuacamoleZoom(zoom: number): number {
return Math.min(MAX_GUACAMOLE_ZOOM, Math.max(MIN_GUACAMOLE_ZOOM, zoom));
}
export function stepGuacamoleZoom(zoom: number, direction: -1 | 1): number {
return clampGuacamoleZoom(zoom + direction * GUACAMOLE_ZOOM_STEP);
}
@@ -0,0 +1,147 @@
import type {
HostProtocolAuthSummary,
PluginHostRecord,
QuickConnectLogin,
} from "@termix/plugin-sdk/frontend";
import type { GuacamoleConfig } from "./guacamole-config";
export const PLUGIN_ID = "remote-desktop";
export type Protocol = "rdp" | "vnc" | "telnet";
/** This plugin's host settings, as they arrive on host.pluginSettings. */
export interface RemoteHostOptions {
enableRdp: boolean;
enableVnc: boolean;
enableTelnet: boolean;
rdpPort: number;
vncPort: number;
telnetPort: number;
rdpSecurity: string;
rdpIgnoreCert: boolean;
guacamoleConfig: GuacamoleConfig;
enableToolbar: boolean;
}
export const ENABLE_KEY = {
rdp: "enableRdp",
vnc: "enableVnc",
telnet: "enableTelnet",
} as const satisfies Record<Protocol, keyof RemoteHostOptions>;
export const PORT_KEY = {
rdp: "rdpPort",
vnc: "vncPort",
telnet: "telnetPort",
} as const satisfies Record<Protocol, keyof RemoteHostOptions>;
export const DEFAULT_PORT: Record<Protocol, number> = {
rdp: 3389,
vnc: 5900,
telnet: 23,
};
/**
* The host fields a session reads. Each protocol's login is core's, under
* protocolAuth, since this plugin declares its protocols in the manifest.
*/
export interface RemoteHostLogin {
id?: number | string;
name?: string | null;
ip?: string;
syncId?: string | null;
connectionOrigin?: "local" | "remote" | null;
protocolAuth?: Record<string, HostProtocolAuthSummary>;
quickConnectLogin?: QuickConnectLogin;
authOverrides?: Record<
string,
{
credentialId?: number | string | null;
required?: boolean;
ownerAuthShared?: boolean;
}
>;
pluginSettings?: Record<string, Record<string, unknown>>;
}
/** The RDP domain a host's saved login carries. */
export function rdpDomain(host: RemoteHostLogin): string | undefined {
return host.protocolAuth?.rdp?.fields?.domain || undefined;
}
function port(value: unknown, fallback: number): number {
const parsed = typeof value === "string" ? Number(value) : value;
return typeof parsed === "number" && Number.isInteger(parsed) && parsed > 0
? parsed
: fallback;
}
export function parseGuacamoleConfig(value: unknown): GuacamoleConfig {
if (!value) return {};
if (typeof value === "string") {
try {
const parsed = JSON.parse(value);
return parsed && typeof parsed === "object" ? parsed : {};
} catch {
return {};
}
}
return typeof value === "object" ? (value as GuacamoleConfig) : {};
}
export function remoteOptions(
settings: Record<string, unknown> | undefined,
): RemoteHostOptions {
const values = settings ?? {};
return {
enableRdp: values.enableRdp === true,
enableVnc: values.enableVnc === true,
enableTelnet: values.enableTelnet === true,
rdpPort: port(values.rdpPort, DEFAULT_PORT.rdp),
vncPort: port(values.vncPort, DEFAULT_PORT.vnc),
telnetPort: port(values.telnetPort, DEFAULT_PORT.telnet),
rdpSecurity:
typeof values.rdpSecurity === "string" ? values.rdpSecurity : "",
rdpIgnoreCert: values.rdpIgnoreCert === true,
guacamoleConfig: parseGuacamoleConfig(values.guacamoleConfig),
enableToolbar: values.enableToolbar !== false,
};
}
export function hostRemoteOptions(
host:
| Pick<RemoteHostLogin, "pluginSettings">
| PluginHostRecord
| null
| undefined,
): RemoteHostOptions {
const bag = (host as RemoteHostLogin | null | undefined)?.pluginSettings;
return remoteOptions(bag?.[PLUGIN_ID]);
}
export function protocolEnabled(
host:
| Pick<RemoteHostLogin, "pluginSettings">
| PluginHostRecord
| null
| undefined,
protocol: Protocol,
): boolean {
return hostRemoteOptions(host)[ENABLE_KEY[protocol]];
}
/** Quick Connect hosts are never saved; their ids carry this prefix. */
const QUICK_CONNECT_ID_PREFIX = "quick-connect-";
export function isQuickConnectHost(host: { id?: unknown } | null | undefined) {
return String(host?.id ?? "").startsWith(QUICK_CONNECT_ID_PREFIX);
}
export function errorMessage(error: unknown, fallback = "Unknown error") {
const data = (error as { response?: { data?: { error?: unknown } } })
?.response?.data?.error;
if (typeof data === "string" && data) return data;
if (error instanceof Error && error.message) return error.message;
if (typeof error === "string" && error) return error;
return fallback;
}
@@ -0,0 +1,457 @@
import { useEffect, useState, type ComponentType, type Ref } from "react";
import {
Info,
MessagesSquare,
Monitor,
MonitorUp,
MousePointerClick,
} from "lucide-react";
import {
invokeAction,
useTranslation,
type HostEditorSectionProps,
type PluginHostRecord,
type StandaloneViewProps,
type TabProps,
type TermixApp,
} from "@termix/plugin-sdk/frontend";
import {
FakeSwitch,
HostFeatureFields,
SectionCard,
SettingRow,
isElectron,
} from "@termix/plugin-sdk/ui";
import { toast } from "sonner";
import GuacamoleApp, { type GuacamoleAppHandle } from "./GuacamoleApp";
import { GuacamoleDisplay } from "./GuacamoleDisplay";
import {
HostEditorRdpTab,
HostEditorTelnetTab,
HostEditorVncTab,
} from "./HostEditorGuacamoleTabs";
import {
getGuacamoleTokenFromHost,
getGuacdStatus,
nativeRdpAvailable,
openNativeRdp,
setRemoteDesktopApp,
} from "./guacamole-api";
import { quickConnectGuacHost } from "./quick-connect-guac-host";
import { REMOTE_DESKTOP_TOOLBAR_SLOT } from "./GuacamoleToolbar.tsx";
import {
DEFAULT_PORT,
ENABLE_KEY,
PORT_KEY,
hostRemoteOptions,
isQuickConnectHost,
protocolEnabled,
type Protocol,
rdpDomain,
type RemoteHostLogin,
} from "./host-remote";
import { remoteDesktopForm } from "./remote-form";
const PROTOCOLS: {
id: Protocol;
titleKey: string;
descriptionKey: string;
paletteKey: string;
icon: ComponentType<{ className?: string }>;
priority: number;
order: number;
quickConnect?: { showDomain?: boolean };
}[] = [
{
id: "rdp",
titleKey: "hosts.tabRdp",
descriptionKey: "hosts.remoteDesktop",
paletteKey: "palette.connectRdp",
icon: Monitor,
priority: 50,
order: 100,
quickConnect: { showDomain: true },
},
{
id: "vnc",
titleKey: "hosts.tabVnc",
descriptionKey: "hosts.virtualNetwork",
paletteKey: "palette.connectVnc",
icon: MousePointerClick,
priority: 40,
order: 110,
quickConnect: {},
},
{
id: "telnet",
titleKey: "hosts.tabTelnet",
descriptionKey: "hosts.unencryptedShell",
paletteKey: "palette.connectTelnet",
icon: MessagesSquare,
priority: 30,
order: 120,
},
];
function RemoteDesktopTab({ tab, host, isVisible, handleRef }: TabProps) {
const record: RemoteHostLogin | undefined = host;
return (
<GuacamoleApp
ref={handleRef as Ref<GuacamoleAppHandle>}
hostId={String(host?.id ?? "")}
tabId={tab.id}
protocol={tab.type as Protocol}
isVisible={isVisible}
quickConnectHost={
record && isQuickConnectHost(record)
? quickConnectGuacHost(record)
: undefined
}
/>
);
}
function RemoteDesktopStandalone({ hostId, view }: StandaloneViewProps) {
return <GuacamoleApp hostId={hostId} protocol={view as Protocol} />;
}
/** What collab rooms and shared-session links draw a stream with. */
function RemoteDisplay({
token,
connectionOrigin,
protocol,
isVisible,
onConnect,
onError,
}: {
token: string;
connectionOrigin?: "local" | "remote";
protocol: Protocol;
isVisible: boolean;
onConnect?: () => void;
onError?: (error: string) => void;
}) {
return (
<GuacamoleDisplay
connectionConfig={{ token, protocol, type: protocol, connectionOrigin }}
isVisible={isVisible}
onConnect={onConnect}
onError={onError}
/>
);
}
function SectionNotes() {
const { t } = useTranslation();
return (
<SectionCard
title={t("hosts.remoteDesktopNotes")}
icon={<Info className="size-3.5" />}
>
<div className="flex flex-col gap-2 py-3 text-xs text-muted-foreground">
<p>{t("hosts.userDefaultsNote")}</p>
{isElectron() && <p>{t("hosts.connectionOriginNote")}</p>}
</div>
</SectionCard>
);
}
function ToolbarCard({
form,
setField,
}: Pick<ReturnType<typeof remoteDesktopForm>, "form" | "setField">) {
const { t } = useTranslation();
return (
<SectionCard
title={t("hosts.guac.toolbar")}
icon={<MonitorUp className="size-3.5" />}
>
<SettingRow
label={t("settings.host.enableToolbar.label")}
description={t("settings.host.enableToolbar.description")}
defaultKey="enableToolbar"
>
<FakeSwitch
checked={form.enableToolbar}
onChange={(value) => setField("enableToolbar", value)}
/>
</SettingRow>
</SectionCard>
);
}
/**
* The Wake-on-LAN address a host keeps, the default for a session's wake
* packet. Null while that plugin is off.
*/
function useWakeOnLanMac(hostId: string | undefined): string | null {
const [mac, setMac] = useState<string | null>(null);
useEffect(() => {
let active = true;
if (!hostId) {
setMac(null);
return;
}
invokeAction("wakeOnLan.macAddress", hostId)
.then((value) => {
if (active) setMac(typeof value === "string" && value ? value : null);
})
.catch(() => {
if (active) setMac(null);
});
return () => {
active = false;
};
}, [hostId]);
return mac;
}
function RdpSection(props: HostEditorSectionProps) {
const { form, setField, setGuacField } = remoteDesktopForm(props);
const macAddress = useWakeOnLanMac(props.host?.id);
return (
<>
<HostEditorRdpTab
form={form}
setField={setField}
setGuacField={setGuacField}
host={{ macAddress }}
credentials={props.credentials as never}
/>
<ToolbarCard form={form} setField={setField} />
<SectionNotes />
</>
);
}
function VncSection(props: HostEditorSectionProps) {
const { form, setField, setGuacField } = remoteDesktopForm(props);
const macAddress = useWakeOnLanMac(props.host?.id);
return (
<>
<HostEditorVncTab
form={form}
setField={setField}
setGuacField={setGuacField}
host={{ macAddress }}
credentials={props.credentials as never}
/>
<ToolbarCard form={form} setField={setField} />
<SectionNotes />
</>
);
}
function TelnetSection(props: HostEditorSectionProps) {
const { form, setField, setGuacField } = remoteDesktopForm(props);
return (
<>
<HostEditorTelnetTab
form={form}
setField={setField}
setGuacField={setGuacField}
credentials={props.credentials as never}
/>
<ToolbarCard form={form} setField={setField} />
<SectionNotes />
</>
);
}
/**
* The defaults editor's remote desktop section: the display settings and the
* toolbar, which every host follows unless its own guacd settings say
* otherwise. A host edits these in its protocol tabs instead.
*/
function DisplayDefaultsSection(props: HostEditorSectionProps) {
const { t } = useTranslation();
const { form, setField } = remoteDesktopForm(props);
return (
<>
<SectionCard
title={t("settings.host.displayGroup")}
icon={<Monitor className="size-3.5" />}
>
<HostFeatureFields form={props.form} updateForm={props.updateForm} />
</SectionCard>
<ToolbarCard form={form} setField={setField} />
</>
);
}
const SECTIONS: Record<Protocol, ComponentType<HostEditorSectionProps>> = {
rdp: RdpSection,
vnc: VncSection,
telnet: TelnetSection,
};
function registerNativeRdp(app: TermixApp): void {
let disposed = false;
app.onDispose(() => {
disposed = true;
});
void nativeRdpAvailable().then((available) => {
if (disposed || !available) return;
app.registerHostAction({
id: "rdp-native",
titleKey: "hosts.openNativeRdp",
icon: MonitorUp,
kind: "open",
order: 105,
when: (host) => protocolEnabled(host, "rdp"),
run: (host) => {
const record: RemoteHostLogin = host;
void openNativeRdp({
host: String(record.ip ?? ""),
port: hostRemoteOptions(record).rdpPort,
username: record.protocolAuth?.rdp?.username ?? undefined,
domain: rdpDomain(record),
})
.then((result) => {
if (result.success) toast.success(app.t("hosts.nativeRdpOpened"));
else toast.error(result.error || app.t("hosts.nativeRdpFailed"));
})
.catch(() => toast.error(app.t("hosts.nativeRdpFailed")));
},
});
});
}
function registerHostSurfaces(app: TermixApp): void {
for (const protocol of PROTOCOLS) {
const when = (host: PluginHostRecord) => protocolEnabled(host, protocol.id);
app.registerHostProtocol({
id: protocol.id,
settingKey: ENABLE_KEY[protocol.id],
portKey: PORT_KEY[protocol.id],
defaultPort: DEFAULT_PORT[protocol.id],
titleKey: protocol.titleKey,
descriptionKey: protocol.descriptionKey,
connectionOriginNoteKey: "hosts.connectionOriginGuacamoleNote",
icon: protocol.icon,
order: protocol.order,
quickConnect: protocol.quickConnect,
});
app.registerHostAction({
id: protocol.id,
titleKey: protocol.titleKey,
icon: protocol.icon,
kind: "connect",
priority: protocol.priority,
order: protocol.order,
tabType: protocol.id,
copyUrlView: protocol.id,
when,
});
app.registerPaletteEntry({
id: `remote-desktop.${protocol.id}`,
titleKey: protocol.paletteKey,
icon: protocol.icon,
keywords: [protocol.id],
scope: "host",
when: (host) => !!host && when(host),
run: (shell, host) => {
if (host) shell.openTab(host, protocol.id);
},
});
app.registerHostEditorSection({
id: protocol.id,
group: "top",
titleKey: protocol.titleKey,
icon: protocol.icon,
order: protocol.order / 5,
visible: (protocols) => !!protocols[ENABLE_KEY[protocol.id]],
component: SECTIONS[protocol.id],
});
}
app.registerHostEditorSection({
id: "remote-desktop-display",
group: "top",
titleKey: "settings.host.displayDefaultsTab",
icon: Monitor,
order: 40,
defaults: "only",
component: DisplayDefaultsSection,
});
if (isElectron()) registerNativeRdp(app);
}
export function activate(app: TermixApp): void {
setRemoteDesktopApp(app);
app.onDispose(() => setRemoteDesktopApp(null));
// Guest pages only draw shared streams.
app.registerSlotContribution("session.remoteDisplay", {
actionId: "remote-desktop.display",
titleKey: "hosts.tabRdp",
kind: "component",
component: RemoteDisplay as unknown as ComponentType<
Record<string, unknown>
>,
});
if (app.guest) return;
// The share button lives here, contributed by session sharing.
app.declareActionSlot({
id: REMOTE_DESKTOP_TOOLBAR_SLOT,
accepts: ["button"],
});
// Mints a token for presenting a host in a collab room.
app.registerAction(
"session.remoteDisplay.token",
async (hostId: number, origin: unknown, protocol: Protocol) => {
const response = await getGuacamoleTokenFromHost(
hostId,
origin as Parameters<typeof getGuacamoleTokenFromHost>[1],
protocol,
);
return response.guacamoleConnectionId
? {
token: response.token,
connectionId: response.guacamoleConnectionId,
}
: null;
},
);
for (const protocol of PROTOCOLS) {
app.registerTab(protocol.id, RemoteDesktopTab, {
icon: protocol.icon,
titleKey: protocol.titleKey,
requiresHost: true,
noHostMessageKey: "remoteDesktop.noHostSelected",
persistent: true,
session: true,
restore: (host) => protocolEnabled(host, protocol.id),
activityTypes: [protocol.id],
standalone: RemoteDesktopStandalone,
preload: () => import("./GuacamoleApp"),
});
}
// An admin can turn Remote Desktop off; its ways in go with it.
let disposed = false;
app.onDispose(() => {
disposed = true;
});
void getGuacdStatus("local", { probe: false })
.then((status) => status.enabled !== false)
.catch(() => true)
.then((enabled) => {
if (!disposed && enabled) registerHostSurfaces(app);
});
app.registerSlotContribution("onboarding.features", {
actionId: "remote-desktop.feature",
titleKey: "onboarding.feature_desktop",
descriptionKey: "onboarding.feature_desktop_desc",
icon: Monitor,
});
}
@@ -0,0 +1,28 @@
import type { GuacamoleQuickHost } from "./GuacamoleApp";
import {
hostRemoteOptions,
type Protocol,
type RemoteHostLogin,
} from "./host-remote";
/** The slice of a quick-connect host that GuacamoleApp mints a token from. */
export function quickConnectGuacHost(
host: RemoteHostLogin,
): GuacamoleQuickHost {
const options = hostRemoteOptions(host);
const connectionType: Protocol = options.enableVnc ? "vnc" : "rdp";
const login =
host.quickConnectLogin?.protocol === connectionType
? host.quickConnectLogin
: undefined;
return {
name: host.name ?? undefined,
ip: host.ip ?? "",
connectionType,
domain: login?.fields?.domain,
port: connectionType === "vnc" ? options.vncPort : options.rdpPort,
rdpAuthType: "direct",
username: login?.username,
password: login?.password,
};
}
@@ -0,0 +1,17 @@
import type { RemoteHostLogin } from "./host-remote";
export function needsRdpCredentialPrompt({
protocol,
rdpAuthType,
authOverrides,
}: {
protocol: "rdp" | "vnc" | "telnet";
rdpAuthType?: string;
authOverrides?: RemoteHostLogin["authOverrides"];
}): boolean {
return (
protocol === "rdp" &&
rdpAuthType === "none" &&
!authOverrides?.rdp?.credentialId
);
}
@@ -0,0 +1,183 @@
import type {
HostEditorSectionProps,
HostProtocolAuthForm,
} from "@termix/plugin-sdk/frontend";
import { PLUGIN_ID, remoteOptions, type Protocol } from "./host-remote";
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* The host editor fields the RDP, VNC and Telnet tabs edit. The logins are
* core's, under form.protocolAuth (this plugin declares the protocols in its
* manifest); the options (ports, security, guacd settings) are this plugin's
* host settings. Both are presented here under the names the tabs use.
*/
export interface RemoteDesktopForm {
domain: string;
security: string;
ignoreCert: boolean;
rdpPort: number;
vncPort: number;
telnetPort: number;
guacamoleConfig: Record<string, any>;
enableToolbar: boolean;
rdpAuthType: "direct" | "credential" | "none";
rdpCredentialId: string;
rdpUser: string;
rdpPassword: string;
vncAuthType: "direct" | "credential";
vncCredentialId: string;
vncUser: string;
vncPassword: string;
telnetAuthType: "direct" | "credential";
telnetCredentialId: string;
telnetUser: string;
telnetPassword: string;
}
export type RemoteFormSetField = <K extends keyof RemoteDesktopForm>(
key: K,
value: RemoteDesktopForm[K],
) => void;
/** Tab field name to this plugin's host setting key. */
const SETTING_FOR_FIELD: Partial<Record<keyof RemoteDesktopForm, string>> = {
rdpPort: "rdpPort",
vncPort: "vncPort",
telnetPort: "telnetPort",
security: "rdpSecurity",
ignoreCert: "rdpIgnoreCert",
enableToolbar: "enableToolbar",
};
type LoginPart = "authType" | "credentialId" | "username" | "password";
/** Tab field name to the protocol login it edits. */
const LOGIN_FOR_FIELD: Partial<
Record<keyof RemoteDesktopForm, [Protocol, LoginPart | "domain"]>
> = {
rdpAuthType: ["rdp", "authType"],
rdpCredentialId: ["rdp", "credentialId"],
rdpUser: ["rdp", "username"],
rdpPassword: ["rdp", "password"],
domain: ["rdp", "domain"],
vncAuthType: ["vnc", "authType"],
vncCredentialId: ["vnc", "credentialId"],
vncUser: ["vnc", "username"],
vncPassword: ["vnc", "password"],
telnetAuthType: ["telnet", "authType"],
telnetCredentialId: ["telnet", "credentialId"],
telnetUser: ["telnet", "username"],
telnetPassword: ["telnet", "password"],
};
type PluginSettingsBag = Record<string, Record<string, unknown>>;
type LoginBag = Record<string, HostProtocolAuthForm>;
const EMPTY_LOGIN: HostProtocolAuthForm = {
authType: "direct",
credentialId: "",
username: "",
password: "",
fields: {},
};
export function loginOf(
bag: LoginBag | undefined,
protocol: Protocol,
): HostProtocolAuthForm {
const login = bag?.[protocol];
return login ? { ...EMPTY_LOGIN, ...login } : EMPTY_LOGIN;
}
function patchSettings(
current: Record<string, unknown>,
patch: (settings: Record<string, unknown>) => Record<string, unknown>,
): Record<string, unknown> {
const bag = (current.pluginSettings as PluginSettingsBag | undefined) ?? {};
const mine = bag[PLUGIN_ID] ?? {};
return {
pluginSettings: { ...bag, [PLUGIN_ID]: { ...mine, ...patch(mine) } },
};
}
/** The form patch that sets one part of a protocol's login. */
export function patchLogin(
current: Record<string, unknown>,
protocol: Protocol,
part: LoginPart | "domain",
value: unknown,
): Record<string, unknown> {
const bag = (current.protocolAuth as LoginBag | undefined) ?? {};
const login = loginOf(bag, protocol);
const next: HostProtocolAuthForm =
part === "domain"
? { ...login, fields: { ...login.fields, domain: String(value ?? "") } }
: ({ ...login, [part]: value } as HostProtocolAuthForm);
return { protocolAuth: { ...bag, [protocol]: next } };
}
export function remoteDesktopForm(props: HostEditorSectionProps): {
form: RemoteDesktopForm;
setField: RemoteFormSetField;
setGuacField: (key: string, value: unknown) => void;
} {
const bag = props.form?.pluginSettings as PluginSettingsBag | undefined;
const logins = props.form?.protocolAuth as LoginBag | undefined;
const options = remoteOptions(bag?.[PLUGIN_ID]);
const rdp = loginOf(logins, "rdp");
const vnc = loginOf(logins, "vnc");
const telnet = loginOf(logins, "telnet");
const form: RemoteDesktopForm = {
rdpPort: options.rdpPort,
vncPort: options.vncPort,
telnetPort: options.telnetPort,
security: options.rdpSecurity,
ignoreCert: options.rdpIgnoreCert,
guacamoleConfig: options.guacamoleConfig as Record<string, any>,
enableToolbar: options.enableToolbar,
domain: rdp.fields.domain ?? "",
rdpAuthType: rdp.authType,
rdpCredentialId: rdp.credentialId,
rdpUser: rdp.username,
rdpPassword: rdp.password,
vncAuthType: vnc.authType === "credential" ? "credential" : "direct",
vncCredentialId: vnc.credentialId,
vncUser: vnc.username,
vncPassword: vnc.password,
telnetAuthType: telnet.authType === "credential" ? "credential" : "direct",
telnetCredentialId: telnet.credentialId,
telnetUser: telnet.username,
telnetPassword: telnet.password,
};
const setField: RemoteFormSetField = (key, value) => {
const login = LOGIN_FOR_FIELD[key];
if (login) {
props.updateForm((current) =>
patchLogin(current, login[0], login[1], value),
);
return;
}
const setting = SETTING_FOR_FIELD[key];
if (!setting) {
props.setField(key, value);
return;
}
props.updateForm((current) =>
patchSettings(current, () => ({ [setting]: value })),
);
};
const setGuacField = (key: string, value: unknown) =>
props.updateForm((current) =>
patchSettings(current, (mine) => ({
guacamoleConfig: {
...remoteOptions(mine).guacamoleConfig,
[key]: value,
},
})),
);
return { form, setField, setGuacField };
}
@@ -0,0 +1,34 @@
import { describe, expect, it } from "vitest";
import { withDriveSettings } from "../../src/backend/drive-settings.js";
describe("withDriveSettings", () => {
it("gives each user a folder under GUACD_DRIVE_PATH and creates it", () => {
expect(
withDriveSettings({ "enable-drive": true }, "user-1", {
GUACD_DRIVE_PATH: "/termix-data/rdp-drive/",
}),
).toEqual({
"enable-drive": true,
"drive-path": "/termix-data/rdp-drive/user-1",
"create-drive-path": true,
});
});
it("falls back to /drive when the environment says nothing", () => {
expect(
withDriveSettings({ "enable-drive": true }, "user-1", {}),
).toMatchObject({ "drive-path": "/drive/user-1" });
});
it("leaves a host-chosen drive-path alone", () => {
const config = { "enable-drive": true, "drive-path": "/mnt/share" };
expect(
withDriveSettings(config, "user-1", { GUACD_DRIVE_PATH: "/x" }),
).toBe(config);
});
it("does nothing when the drive is not enabled", () => {
const config = { "enable-drive": false };
expect(withDriveSettings(config, "user-1")).toBe(config);
});
});
@@ -0,0 +1,118 @@
import { describe, expect, it } from "vitest";
import {
hostPayloadLegacy,
normalizeImportedHost,
userDefaultParams,
} from "../../src/backend/host-settings.js";
describe("normalizeImportedHost", () => {
it("reads a Termix export's pluginSettings", () => {
expect(
normalizeImportedHost({
pluginSettings: {
"remote-desktop": {
enableVnc: true,
vncPort: 5901,
guacamoleConfig: { cursor: "local" },
},
},
}),
).toEqual({
enableVnc: true,
vncPort: 5901,
guacamoleConfig: { cursor: "local" },
});
});
it("reads the flat fields hosts had before 2.9.0", () => {
expect(
normalizeImportedHost({
enableRdp: true,
rdpPort: "3390",
security: "nla",
ignoreCert: 1,
guacamoleConfig: JSON.stringify({ dpi: 120 }),
enableTerminalToolbar: false,
}),
).toEqual({
enableRdp: true,
rdpPort: 3390,
rdpSecurity: "nla",
rdpIgnoreCert: true,
guacamoleConfig: { dpi: 120 },
enableToolbar: false,
});
});
it("turns a connectionType-only host into its switch and port", () => {
expect(
normalizeImportedHost({ connectionType: "telnet", port: 2323 }),
).toEqual({ enableTelnet: true, telnetPort: 2323 });
});
it("writes nothing for a plain SSH row", () => {
expect(
normalizeImportedHost({ connectionType: "ssh", port: 22 }),
).toBeNull();
});
it("drops a port out of range", () => {
expect(normalizeImportedHost({ enableRdp: true, rdpPort: 70000 })).toEqual({
enableRdp: true,
});
});
});
describe("userDefaultParams", () => {
it("maps the user settings onto guacd parameters, skipping inherit", () => {
expect(
userDefaultParams({
colorDepth: "24",
resizeMethod: "reconnect",
disableCopy: "on",
enableWallpaper: "off",
enableDrive: "inherit",
}),
).toEqual({
"color-depth": 24,
"resize-method": "reconnect",
"disable-copy": true,
"enable-wallpaper": false,
});
});
});
describe("hostPayloadLegacy", () => {
it("puts each login back in its 2.8 flat shape for older clients", () => {
expect(
hostPayloadLegacy(
{},
{
protocolAuth: {
rdp: {
authType: "direct",
credentialId: null,
username: "admin",
fields: { domain: "CORP" },
hasPassword: true,
},
vnc: { authType: "credential", credentialId: 4 },
},
},
),
).toEqual({
rdpAuthType: "direct",
rdpCredentialId: null,
rdpUser: "admin",
hasRdpPassword: true,
rdpDomain: "CORP",
domain: "CORP",
vncAuthType: "credential",
vncCredentialId: 4,
});
});
it("adds nothing for a host without logins", () => {
expect(hostPayloadLegacy({}, {})).toBeNull();
});
});
@@ -0,0 +1,27 @@
import { describe, expect, it } from "vitest";
import { resolveJumpTunnelEndpoint } from "../../src/backend/jump-tunnel-endpoint.js";
describe("resolveJumpTunnelEndpoint", () => {
it("keeps an in-process guacd tunnel on loopback", () => {
expect(resolveJumpTunnelEndpoint("localhost")).toEqual({
bindHost: "127.0.0.1",
advertisedHost: "127.0.0.1",
});
});
it("exposes the tunnel to a separate guacd container", () => {
expect(resolveJumpTunnelEndpoint("guacd")).toEqual({
bindHost: "0.0.0.0",
advertisedHost: "termix",
});
});
it("supports a custom backend hostname for external guacd", () => {
expect(
resolveJumpTunnelEndpoint("guacd.example", "termix-backend"),
).toEqual({
bindHost: "0.0.0.0",
advertisedHost: "termix-backend",
});
});
});
@@ -0,0 +1,115 @@
import net from "net";
import { afterEach, describe, expect, it } from "vitest";
import { createMacosVncCompatibilityProxy } from "../../src/backend/macos-vnc-proxy.js";
const closers: Array<() => void> = [];
afterEach(() => {
for (const close of closers.splice(0)) close();
});
async function listen(server: net.Server): Promise<number> {
return new Promise((resolve, reject) => {
server.once("error", reject);
server.listen(0, "127.0.0.1", () => {
resolve((server.address() as net.AddressInfo).port);
});
});
}
async function read(socket: net.Socket, length: number): Promise<Buffer> {
return new Promise((resolve, reject) => {
let received = Buffer.alloc(0);
const onData = (chunk: Buffer) => {
received = Buffer.concat([received, chunk]);
if (received.length < length) return;
socket.off("data", onData);
resolve(received);
};
socket.on("data", onData);
socket.once("error", reject);
});
}
describe("createMacosVncCompatibilityProxy", () => {
it("normalizes Apple's private RFB banner and forces classic VNC auth", async () => {
let clientBanner = "";
let selectedSecurityType = 0;
const target = net.createServer((socket) => {
socket.write("RFB 003.");
socket.write("889\n");
socket.once("data", (data) => {
clientBanner = data.toString("ascii");
socket.write(Buffer.from([5, 30, 33]));
socket.write(Buffer.from([36, 2, 35]));
socket.once("data", (selection) => {
selectedSecurityType = Number(selection[0]);
socket.write("desktop-data");
});
});
});
const targetPort = await listen(target);
closers.push(() => target.close());
const proxy = await createMacosVncCompatibilityProxy({
targetHost: "127.0.0.1",
targetPort,
bindHost: "127.0.0.1",
});
closers.push(proxy.close);
const client = net.createConnection(proxy.port, "127.0.0.1");
closers.push(() => client.destroy());
expect((await read(client, 12)).subarray(0, 12).toString("ascii")).toBe(
"RFB 003.008\n",
);
client.write("RFB 003.008\n");
expect([...(await read(client, 2))]).toEqual([1, 2]);
client.write(Buffer.from([2]));
expect((await read(client, 12)).toString("ascii")).toBe("desktop-data");
expect(clientBanner).toBe("RFB 003.008\n");
expect(selectedSecurityType).toBe(2);
});
it("passes standard RFB negotiation through unchanged", async () => {
const target = net.createServer((socket) => {
socket.write("RFB 003.008\n");
socket.once("data", () => socket.write(Buffer.from([2, 30, 2])));
});
const targetPort = await listen(target);
closers.push(() => target.close());
const proxy = await createMacosVncCompatibilityProxy({
targetHost: "127.0.0.1",
targetPort,
bindHost: "127.0.0.1",
});
closers.push(proxy.close);
const client = net.createConnection(proxy.port, "127.0.0.1");
closers.push(() => client.destroy());
expect((await read(client, 12)).toString("ascii")).toBe("RFB 003.008\n");
client.write("RFB 003.008\n");
expect([...(await read(client, 3))]).toEqual([2, 30, 2]);
});
it("passes Apple's security types through when VNC auth is unavailable", async () => {
const target = net.createServer((socket) => {
socket.write("RFB 003.889\n");
socket.once("data", () => socket.write(Buffer.from([2, 30, 33])));
});
const targetPort = await listen(target);
closers.push(() => target.close());
const proxy = await createMacosVncCompatibilityProxy({
targetHost: "127.0.0.1",
targetPort,
bindHost: "127.0.0.1",
});
closers.push(proxy.close);
const client = net.createConnection(proxy.port, "127.0.0.1");
closers.push(() => client.destroy());
expect((await read(client, 12)).toString("ascii")).toBe("RFB 003.008\n");
client.write("RFB 003.008\n");
expect([...(await read(client, 3))]).toEqual([2, 30, 33]);
});
});
@@ -0,0 +1,54 @@
import { describe, expect, it } from "vitest";
import {
buildRdpSettings,
resolveRdpDomain,
} from "../../src/backend/rdp-settings.js";
describe("buildRdpSettings", () => {
it("keeps saved RDP settings authoritative over stale advanced config", () => {
expect(
buildRdpSettings({
port: 3390,
domain: "EXAMPLE",
security: "nla",
ignoreCert: true,
guacConfig: {
port: 3389,
domain: "OLD",
security: "rdp",
"ignore-cert": false,
"color-depth": 24,
},
guacdOverrides: { guacdHost: "guacd.internal" },
}),
).toEqual({
port: 3390,
domain: "EXAMPLE",
security: "nla",
"ignore-cert": true,
"color-depth": 24,
guacdHost: "guacd.internal",
});
});
it("preserves an advanced security value when no saved value exists", () => {
expect(
buildRdpSettings({
port: 3389,
ignoreCert: false,
guacConfig: { security: "tls" },
guacdOverrides: {},
}).security,
).toBe("tls");
});
it("uses the prompted domain for prompt-on-connect authentication", () => {
expect(resolveRdpDomain("none", "EXAMPLE", "OLD")).toBe("EXAMPLE");
expect(resolveRdpDomain("none", "", "OLD")).toBe("");
});
it("keeps the stored domain for saved authentication", () => {
expect(resolveRdpDomain("direct", "EXAMPLE", "SAVED")).toBe("SAVED");
expect(resolveRdpDomain("none", undefined, "SAVED")).toBe("SAVED");
});
});
@@ -0,0 +1,68 @@
import { describe, expect, it } from "vitest";
import { withRecordingSettings } from "../../src/backend/recording-settings.js";
const PATH = "/app/data/session_recordings/guacamole";
const NAME = "b7e6c0f2-0000-4000-8000-000000000000.guac";
describe("withRecordingSettings", () => {
it("takes ownership of the location and filename", () => {
const merged = withRecordingSettings(
{
"recording-path": "/var/lib/termix/recordings",
"recording-name": "${GUAC_USERNAME}-${GUAC_DATE}",
"create-recording-path": false,
},
PATH,
NAME,
);
expect(merged).toMatchObject({
"recording-path": PATH,
"recording-name": NAME,
"create-recording-path": true,
});
});
it("defaults the content flags when the host has no opinion", () => {
expect(withRecordingSettings({}, PATH, NAME)).toMatchObject({
"recording-exclude-output": false,
"recording-include-keys": true,
});
});
it("keeps the host's content flags, including the falsy ones", () => {
const merged = withRecordingSettings(
{
"recording-exclude-output": true,
"recording-include-keys": false,
},
PATH,
NAME,
);
expect(merged).toMatchObject({
"recording-exclude-output": true,
"recording-include-keys": false,
});
});
it("leaves unrelated settings alone", () => {
const merged = withRecordingSettings(
{ "recording-exclude-mouse": true, width: "1920" },
PATH,
NAME,
);
expect(merged).toMatchObject({
"recording-exclude-mouse": true,
width: "1920",
});
});
it("does not mutate the settings it was given", () => {
const original = { "recording-path": "/tmp/mine" };
withRecordingSettings(original, PATH, NAME);
expect(original).toEqual({ "recording-path": "/tmp/mine" });
});
});
@@ -0,0 +1,420 @@
import http from "node:http";
import net, { type AddressInfo } from "node:net";
import express, { type Router } from "express";
import { afterEach, describe, expect, it } from "vitest";
import {
createMockCtx,
type MockPluginContext,
} from "@termix/plugin-sdk/testing";
import type {
PluginContext,
PluginProtocolTarget,
} from "@termix/plugin-sdk/backend";
import type { PluginManifest } from "@termix/plugin-sdk/manifest";
import manifestJson from "../../manifest.json";
import { activate } from "../../src/backend/index.js";
import { GuacamoleTokenService } from "../../src/backend/token-service.js";
const manifest = manifestJson as unknown as PluginManifest;
// Without a fixed key each token service picks its own.
process.env.GUACAMOLE_ENCRYPTION_KEY = "ab".repeat(32);
function target(
overrides: Partial<PluginProtocolTarget["auth"]> = {},
): PluginProtocolTarget {
return {
host: {
id: 7,
name: "Win box",
ip: "10.0.0.7",
port: 22,
ownerUserId: "user-1",
jumpHosts: [],
},
shared: false,
auth: {
authType: "direct",
username: "admin",
password: "secret",
fields: { domain: "CORP" },
...overrides,
},
};
}
interface Server {
mock: MockPluginContext;
request: (
method: string,
path: string,
body?: unknown,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
) => Promise<{ status: number; body: any }>;
close: () => Promise<void>;
}
let server: Server | null = null;
afterEach(async () => {
await server?.close();
server = null;
});
async function start(
options: {
protocolTargets?: Record<string, PluginProtocolTarget>;
services?: Record<string, object>;
capabilities?: string[];
} = {},
): Promise<Server> {
let router: Router | null = null;
const mock = createMockCtx({
pluginId: manifest.id,
manifest,
capabilities: options.capabilities ?? manifest.capabilities,
router: () => (router = express.Router()),
protocolTargets: options.protocolTargets,
services: options.services,
});
await activate(mock.ctx as PluginContext);
const app = express();
app.use(express.json());
app.use((req, _res, next) => {
mock.setActor(req.header("x-test-user") ?? "user-1");
next();
});
app.use((req, res, next) => router!(req, res, next));
const httpServer = http.createServer(app);
await new Promise<void>((resolve) => httpServer.listen(0, resolve));
const { port } = httpServer.address() as AddressInfo;
return {
mock,
async request(method, path, body) {
const response = await fetch(`http://127.0.0.1:${port}${path}`, {
method,
headers: body ? { "content-type": "application/json" } : {},
body: body ? JSON.stringify(body) : undefined,
});
const text = await response.text();
return { status: response.status, body: text ? JSON.parse(text) : null };
},
async close() {
await new Promise<void>((resolve) => httpServer.close(() => resolve()));
for (const dispose of [...mock.disposals].reverse()) await dispose();
},
};
}
const tokens = new GuacamoleTokenService();
describe("POST /connect-host/:hostId", () => {
it("mints a token from the resolved login and the host's own settings", async () => {
server = await start({ protocolTargets: { "7:rdp": target() } });
await server.mock.ctx.settings.setHost(7, "enableRdp", true);
await server.mock.ctx.settings.setHost(7, "rdpPort", 3390);
await server.mock.ctx.settings.setHost(7, "rdpSecurity", "nla");
await server.mock.ctx.settings.setHost(7, "guacamoleConfig", {
"color-depth": 16,
"enable-wallpaper": "auto",
});
const response = await server.request("POST", "/connect-host/7", {
protocol: "rdp",
});
expect(response.status).toBe(200);
expect(response.body.termixConnectId).toEqual(expect.any(String));
const token = tokens.decryptToken(response.body.token);
expect(token?.connection.settings).toMatchObject({
hostname: "10.0.0.7",
port: 3390,
username: "admin",
password: "secret",
domain: "CORP",
security: "nla",
"color-depth": 16,
});
expect(token?.connection.settings).not.toHaveProperty("enable-wallpaper");
expect(token?.termixMeta).toMatchObject({
hostId: 7,
hostName: "Win box",
protocol: "rdp",
});
expect(server.mock.audits.at(-1)).toMatchObject({
action: "rdp_connect",
success: true,
});
});
it("puts the host's display settings under its own guacd settings", async () => {
server = await start({ protocolTargets: { "7:rdp": target() } });
await server.mock.ctx.settings.setHost(7, "enableRdp", true);
await server.mock.ctx.settings.setHost(7, "guacamoleConfig", {
"color-depth": 32,
});
// What the host follows from its defaults.
await server.mock.ctx.settings.setHost(7, "colorDepth", "16");
await server.mock.ctx.settings.setHost(7, "disableCopy", "on");
const response = await server.request("POST", "/connect-host/7", {
protocol: "rdp",
});
const settings = tokens.decryptToken(response.body.token)?.connection
.settings;
expect(settings?.["color-depth"]).toBe(32);
expect(settings?.["disable-copy"]).toBe(true);
});
it("uses the prompted login when the host asks at connect time", async () => {
server = await start({
protocolTargets: { "7:rdp": target({ authType: "none", username: "" }) },
});
await server.mock.ctx.settings.setHost(7, "enableRdp", true);
const response = await server.request("POST", "/connect-host/7", {
protocol: "rdp",
promptedUsername: "bob",
promptedPassword: "pw",
promptedDomain: "",
});
const settings = tokens.decryptToken(response.body.token)?.connection
.settings;
expect(settings).toMatchObject({ username: "bob", password: "pw" });
});
it("refuses a protocol the host has switched off", async () => {
server = await start({ protocolTargets: { "7:vnc": target() } });
const response = await server.request("POST", "/connect-host/7", {
protocol: "vnc",
});
expect(response.status).toBe(400);
});
it("answers 404 when the caller cannot reach the host", async () => {
server = await start();
await server.mock.ctx.settings.setHost(7, "enableRdp", true);
const response = await server.request("POST", "/connect-host/7", {
protocol: "rdp",
});
expect(response.status).toBe(404);
});
it("refuses everything while an admin has turned it off", async () => {
server = await start({ protocolTargets: { "7:rdp": target() } });
await server.mock.ctx.settings.set("enabled", false);
await server.mock.ctx.settings.setHost(7, "enableRdp", true);
expect(
(await server.request("POST", "/connect-host/7", { protocol: "rdp" }))
.status,
).toBe(403);
expect(
(
await server.request("POST", "/token", {
type: "rdp",
hostname: "10.0.0.9",
})
).status,
).toBe(403);
const status = await server.request("GET", "/status?probe=0");
expect(status.body.enabled).toBe(false);
});
it("records only when session recording is on for the host", async () => {
const enabledFor = async (hostId: number) => hostId === 7;
server = await start({
protocolTargets: { "7:rdp": target() },
services: {
"recordings.writer": {
enabledFor,
createFinished: async () => ({ id: 1 }),
},
},
});
await server.mock.ctx.settings.setHost(7, "enableRdp", true);
const response = await server.request("POST", "/connect-host/7", {
protocol: "rdp",
});
const token = tokens.decryptToken(response.body.token);
expect(token?.recording).toMatchObject({ hostId: 7, protocol: "rdp" });
expect(token?.connection.settings["recording-name"]).toEqual(
expect.any(String),
);
});
});
describe("the rest of the routes", () => {
it("mints a quick connect token with the caller's defaults", async () => {
server = await start();
await server.mock.ctx.settings.setUser("user-1", "colorDepth", "24");
const response = await server.request("POST", "/token", {
type: "rdp",
hostname: "10.0.0.9",
username: "u",
password: "p",
});
expect(response.status).toBe(200);
expect(
tokens.decryptToken(response.body.token)?.connection.settings,
).toMatchObject({ hostname: "10.0.0.9", port: 3389, "color-depth": 24 });
});
it("ignores guacd and path settings a quick connect tries to set", async () => {
server = await start();
const response = await server.request("POST", "/token", {
type: "rdp",
hostname: "10.0.0.9",
guacdHost: "attacker.example",
guacdPort: 4822,
"drive-path": "/etc",
"recording-path": "/tmp/x",
width: 800,
});
expect(response.status).toBe(200);
const token = tokens.decryptToken(response.body.token);
expect(token?.connection.guacdHost).toBeUndefined();
expect(token?.connection.guacdPort).toBeUndefined();
expect(token?.connection.settings["drive-path"]).toBeUndefined();
expect(token?.connection.settings["recording-path"]).toBeUndefined();
expect(token?.connection.settings.width).toBe(800);
});
it("offers native RDP only in the desktop app", async () => {
server = await start();
expect((await server.request("GET", "/native-rdp")).body).toEqual({
available: false,
});
expect(
(await server.request("POST", "/native-rdp", { host: "10.0.0.7" }))
.status,
).toBe(404);
});
it("does not hand out another user's session id", async () => {
server = await start();
const response = await server.request("GET", "/connection/unknown");
expect(response.body).toEqual({ guacamoleConnectionId: null });
});
});
describe("activate", () => {
it("fails closed without network:serve", async () => {
await expect(
start({
capabilities: manifest.capabilities.filter(
(capability) => capability !== "network:serve",
),
}),
).rejects.toThrow(/network:serve/);
});
it("cannot read a host's login without credentials:read", async () => {
server = await start({
protocolTargets: { "7:rdp": target() },
capabilities: manifest.capabilities.filter(
(capability) => capability !== "credentials:read",
),
});
await server.mock.ctx.settings.setHost(7, "enableRdp", true);
const response = await server.request("POST", "/connect-host/7", {
protocol: "rdp",
});
expect(response.status).toBe(500);
expect(server.mock.checked).toContain("credentials:read");
});
it("provides sessions.live for rdp, vnc and telnet", async () => {
server = await start();
expect([...server.mock.services.keys()].sort()).toEqual(
expect.arrayContaining([
"sessions.live#rdp",
"sessions.live#telnet",
"sessions.live#vnc",
]),
);
});
});
describe("saved host guacd status", () => {
it("probes the same override used by the connection token", async () => {
const guacd = net.createServer((socket) => socket.end());
await new Promise<void>((resolve) => guacd.listen(0, "127.0.0.1", resolve));
try {
const port = (guacd.address() as AddressInfo).port;
server = await start({ protocolTargets: { "7:rdp": target() } });
await server.mock.ctx.settings.setHost(7, "enableRdp", true);
await server.mock.ctx.settings.setHost(7, "guacamoleConfig", {
"guacd-hostname": "127.0.0.1",
"guacd-port": String(port),
});
const status = await server.request(
"GET",
"/status?hostId=7&protocol=rdp",
);
expect(status.status).toBe(200);
expect(status.body.guacd).toEqual({
host: "127.0.0.1",
port,
status: "connected",
});
const result = await server.request("POST", "/connect-host/7", {
protocol: "rdp",
});
expect(tokens.decryptToken(result.body.token)?.connection).toMatchObject({
guacdHost: status.body.guacd.host,
guacdPort: status.body.guacd.port,
});
} finally {
await new Promise<void>((resolve) => guacd.close(() => resolve()));
}
});
it("keeps the global port for a host-only override and global config when absent", async () => {
server = await start({ protocolTargets: { "7:rdp": target() } });
await server.mock.ctx.settings.setHost(7, "enableRdp", true);
const global = await server.request("GET", "/status?probe=0");
const inherited = await server.request(
"GET",
"/status?probe=0&hostId=7&protocol=rdp",
);
expect(inherited.body.guacd).toEqual(global.body.guacd);
await server.mock.ctx.settings.setHost(7, "guacamoleConfig", {
"guacd-hostname": "custom.example",
});
const overridden = await server.request(
"GET",
"/status?probe=0&hostId=7&protocol=rdp",
);
expect(overridden.body.guacd).toEqual({
...global.body.guacd,
host: "custom.example",
});
});
it("refuses a host the caller cannot resolve", async () => {
server = await start();
await server.mock.ctx.settings.setHost(7, "enableRdp", true);
const result = await server.request("GET", "/status?hostId=7&protocol=rdp");
expect(result.status).toBe(404);
expect(result.body.guacd).toBeUndefined();
});
it("refuses disabled protocols", async () => {
server = await start({ protocolTargets: { "7:rdp": target() } });
const result = await server.request("GET", "/status?hostId=7&protocol=rdp");
expect(result.status).toBe(400);
});
it.each([
"hostId=bad&protocol=rdp",
"hostId=0&protocol=rdp",
"hostId=7&protocol=ssh",
"hostId=7",
])("rejects invalid queries: %s", async (query) => {
server = await start();
expect((await server.request("GET", `/status?${query}`)).status).toBe(400);
});
});
@@ -0,0 +1,106 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { RemoteSessions } from "../../src/backend/sessions.js";
import { createLiveSessions } from "../../src/backend/live-sessions.js";
import { GuacamoleTokenService } from "../../src/backend/token-service.js";
const meta = {
termixConnectId: "c1",
hostId: 7,
hostName: "Win box",
ownerUserId: "user-1",
protocol: "rdp" as const,
tabInstanceId: "tab-1",
};
afterEach(() => {
vi.useRealTimers();
});
describe("RemoteSessions", () => {
it("moves parked resources onto the session and releases them on close", () => {
const sessions = new RemoteSessions();
const tunnel = vi.fn();
const tracking = vi.fn();
sessions.park("c1", [tunnel]);
sessions.opened(meta, "g1", [tracking]);
expect(sessions.byConnect("c1")?.guacamoleConnectionId).toBe("g1");
expect(sessions.byGuacamole("g1")?.hostName).toBe("Win box");
expect(tunnel).not.toHaveBeenCalled();
sessions.closed("c1");
expect(tunnel).toHaveBeenCalledOnce();
expect(tracking).toHaveBeenCalledOnce();
expect(sessions.byGuacamole("g1")).toBeNull();
});
it("releases a tunnel guacd never used", () => {
vi.useFakeTimers();
const sessions = new RemoteSessions(1000);
const tunnel = vi.fn();
sessions.park("c1", [tunnel]);
vi.advanceTimersByTime(1000);
expect(tunnel).toHaveBeenCalledOnce();
});
it("releases everything on clear, and one failing close does not stop the rest", () => {
const sessions = new RemoteSessions();
const failing = vi.fn(() => {
throw new Error("already closed");
});
const pending = vi.fn();
const open = vi.fn();
sessions.park("c2", [failing, pending]);
sessions.park("c1", [open]);
sessions.opened(meta, "g1");
sessions.clear();
expect(pending).toHaveBeenCalledOnce();
expect(open).toHaveBeenCalledOnce();
});
});
describe("sessions.live providers", () => {
const tokens = new GuacamoleTokenService();
it("describes a live session of its own protocol only", () => {
const sessions = new RemoteSessions();
sessions.opened(meta, "g1");
const deps = { sessions, tokens, endSession: vi.fn(() => true) };
const rdp = createLiveSessions("rdp", deps);
const vnc = createLiveSessions("vnc", deps);
expect(rdp.getSession("g1")).toMatchObject({
id: "g1",
userId: "user-1",
hostId: 7,
hostName: "Win box",
isConnected: true,
tabInstanceId: "tab-1",
});
expect(vnc.getSession("g1")).toBeNull();
});
it("mints a join token for a viewer and refuses an unknown session", () => {
const sessions = new RemoteSessions();
sessions.opened(meta, "g1");
const rdp = createLiveSessions("rdp", {
sessions,
tokens,
endSession: vi.fn(() => true),
});
const token = tokens.decryptToken(rdp.createViewerToken("g1", true));
expect(token?.connection).toMatchObject({ join: "g1", readOnly: true });
expect(token?.recording).toBeUndefined();
expect(() => rdp.createViewerToken("nope", false)).toThrow();
});
it("ends the session for its owner", () => {
const sessions = new RemoteSessions();
sessions.opened(meta, "g1");
const endSession = vi.fn(() => true);
const rdp = createLiveSessions("rdp", { sessions, tokens, endSession });
rdp.ownerEndSession("g1", "owner ended");
expect(endSession).toHaveBeenCalledWith("g1");
});
});
@@ -0,0 +1,97 @@
/**
* A display token is the only credential on the public /display socket, so
* it has to be one this server minted, unedited and recent.
*/
import { describe, expect, it } from "vitest";
import {
GuacamoleTokenService,
TOKEN_TTL_MS,
isServerOwnedSetting,
} from "../../src/backend/token-service.js";
process.env.GUACAMOLE_ENCRYPTION_KEY = "ab".repeat(32);
function decode(token: string) {
return JSON.parse(Buffer.from(token, "base64").toString("utf8")) as {
iv: string;
value: string;
mac: string;
};
}
function encode(data: unknown): string {
return Buffer.from(JSON.stringify(data)).toString("base64");
}
describe("verifyToken", () => {
it("accepts a fresh token it minted", () => {
const tokens = new GuacamoleTokenService();
const token = tokens.createRdpToken("10.0.0.7", "admin", "secret");
expect(tokens.verifyToken(token)?.connection.settings.hostname).toBe(
"10.0.0.7",
);
});
it("refuses an edited ciphertext or IV", () => {
const tokens = new GuacamoleTokenService();
const data = decode(tokens.createRdpToken("10.0.0.7", "admin", "secret"));
const iv = Buffer.from(data.iv, "base64");
iv[0] ^= 0xff;
expect(
tokens.verifyToken(encode({ ...data, iv: iv.toString("base64") })),
).toBeNull();
const ciphertext = Buffer.from(data.value, "base64");
ciphertext[0] ^= 0xff;
expect(
tokens.verifyToken(
encode({ ...data, value: ciphertext.toString("base64") }),
),
).toBeNull();
});
it("refuses a token with no MAC, as guacamole-lite alone would accept", () => {
const tokens = new GuacamoleTokenService();
const { iv, value } = decode(tokens.createRdpToken("h", "u", "p"));
expect(tokens.verifyToken(encode({ iv, value }))).toBeNull();
});
it("refuses a token once it is older than the TTL", () => {
let now = 1_000_000;
const tokens = new GuacamoleTokenService(undefined, () => now);
const token = tokens.createJoinToken("session-1", true);
now += TOKEN_TTL_MS - 1;
expect(tokens.verifyToken(token)).not.toBeNull();
now += 2;
expect(tokens.verifyToken(token)).toBeNull();
});
it("refuses garbage", () => {
const tokens = new GuacamoleTokenService();
expect(tokens.verifyToken("not-a-token")).toBeNull();
expect(tokens.verifyToken(encode({ iv: 1 }))).toBeNull();
});
});
describe("isServerOwnedSetting", () => {
it.each([
"guacdHost",
"guacdPort",
"drive-path",
"create-drive-path",
"recording-path",
"recording-name",
"create-recording-path",
"typescript-path",
"sftp-private-key-path",
])("keeps %s for the server", (key) => {
expect(isServerOwnedSetting(key)).toBe(true);
});
it.each(["width", "security", "ignore-cert", "color-depth", "enable-drive"])(
"lets a caller set %s",
(key) => {
expect(isServerOwnedSetting(key)).toBe(false);
},
);
});
@@ -0,0 +1,96 @@
import { describe, expect, it } from "vitest";
import { GuacamoleTokenService } from "../../src/backend/token-service.js";
describe("GuacamoleTokenService", () => {
const tokenService = new GuacamoleTokenService();
it("disables RDP pre-authentication when no credentials are configured", () => {
const token = tokenService.createRdpToken("windows.example.test", "", "");
const decrypted = tokenService.decryptToken(token);
expect(decrypted?.connection.settings).toMatchObject({
hostname: "windows.example.test",
port: 3389,
"ignore-cert": true,
"disable-auth": true,
});
expect(decrypted?.connection.settings.username).toBeUndefined();
expect(decrypted?.connection.settings.password).toBeUndefined();
});
it("keeps normal RDP credential authentication unchanged", () => {
const token = tokenService.createRdpToken(
"windows.example.test",
"Administrator",
"secret",
);
const decrypted = tokenService.decryptToken(token);
expect(decrypted?.connection.settings).toMatchObject({
hostname: "windows.example.test",
username: "Administrator",
password: "secret",
port: 3389,
});
expect(decrypted?.connection.settings["disable-auth"]).toBeUndefined();
});
it("preserves recording metadata outside guacd connection settings", () => {
const recording = {
hostId: 42,
userId: "user-1",
protocol: "vnc" as const,
path: "recording.guac",
startedAt: "2026-07-14T00:00:00.000Z",
};
const token = tokenService.createVncToken(
"vnc.example.test",
"user",
"secret",
{},
recording,
);
expect(tokenService.decryptToken(token)?.recording).toEqual(recording);
});
it("preserves termixMeta through the encrypt/decrypt round trip", () => {
const termixMeta = {
termixConnectId: "connect-1",
hostId: 7,
hostName: "windows",
ownerUserId: "user-1",
protocol: "rdp" as const,
tabInstanceId: null,
};
const token = tokenService.createRdpToken(
"windows.example.test",
"Administrator",
"secret",
{},
undefined,
termixMeta,
);
expect(tokenService.decryptToken(token)?.termixMeta).toEqual(termixMeta);
});
it("createJoinToken sets connection.join, not connection.type", () => {
const token = tokenService.createJoinToken("guacd-conn-123", true);
const decrypted = tokenService.decryptToken(token);
expect(decrypted?.connection.join).toBe("guacd-conn-123");
expect(decrypted?.connection.type).toBeUndefined();
expect(decrypted?.connection.readOnly).toBe(true);
});
it("createJoinToken round-trips a read-write join through decryptToken", () => {
const token = tokenService.createJoinToken("guacd-conn-456", false);
const decrypted = tokenService.decryptToken(token);
expect(decrypted?.connection.join).toBe("guacd-conn-456");
expect(decrypted?.connection.readOnly).toBe(false);
expect(decrypted?.recording).toBeUndefined();
});
});
@@ -0,0 +1,152 @@
import React from "react";
import { fireEvent, render } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import { GuacamoleToolbar } from "../../src/frontend/GuacamoleToolbar.js";
import type { GuacamoleDisplayHandle } from "../../src/frontend/GuacamoleDisplay.js";
vi.mock("react-i18next", async (importOriginal) => ({
...(await importOriginal<typeof import("react-i18next")>()),
useTranslation: () => ({ t: (key: string) => key }),
}));
describe("GuacamoleToolbar Windows key", () => {
it("sends Super_L for Win shortcuts and the sticky modifier", () => {
const sendKey = vi.fn();
const displayRef = {
current: {
disconnect: vi.fn(),
isConnected: () => true,
sendKey,
sendMouse: vi.fn(),
setClipboard: vi.fn(),
getFilesystem: () => null,
uploadFile: async () => {},
zoomIn: vi.fn(() => 1.25),
zoomOut: vi.fn(() => 0.75),
resetZoom: vi.fn(() => 1),
} satisfies GuacamoleDisplayHandle,
} as React.RefObject<GuacamoleDisplayHandle>;
const { getByText } = render(
<GuacamoleToolbar
displayRef={displayRef}
protocol="rdp"
metaKeyFamily="win"
/>,
);
fireEvent.click(getByText("Win+L"));
expect(sendKey.mock.calls).toEqual([
[0xffeb, true],
[0x006c, true],
[0x006c, false],
[0xffeb, false],
]);
sendKey.mockClear();
fireEvent.click(getByText("Win"));
expect(sendKey.mock.calls).toEqual([
[0xffeb, true],
[0xffeb, false],
]);
sendKey.mockClear();
fireEvent.click(getByText("remoteDesktop.toolbar.win"));
expect(sendKey).toHaveBeenCalledWith(0xffeb, true);
});
it.each(["vnc", "rdp"] as const)(
"exposes working zoom controls for %s",
(protocol) => {
const zoomIn = vi.fn(() => 1.25);
const zoomOut = vi.fn(() => 0.75);
const resetZoom = vi.fn(() => 1);
const displayRef = {
current: {
disconnect: vi.fn(),
isConnected: () => true,
sendKey: vi.fn(),
sendMouse: vi.fn(),
setClipboard: vi.fn(),
getFilesystem: () => null,
uploadFile: async () => {},
zoomIn,
zoomOut,
resetZoom,
} satisfies GuacamoleDisplayHandle,
} as React.RefObject<GuacamoleDisplayHandle>;
const { getByLabelText, getByText } = render(
<GuacamoleToolbar
displayRef={displayRef}
protocol={protocol}
zoom={1.25}
/>,
);
fireEvent.click(getByLabelText("remoteDesktop.toolbar.zoomOut"));
fireEvent.click(getByLabelText("remoteDesktop.toolbar.zoomIn"));
fireEvent.click(getByText("125%"));
expect(zoomOut).toHaveBeenCalledOnce();
expect(zoomIn).toHaveBeenCalledOnce();
expect(resetZoom).toHaveBeenCalledOnce();
},
);
it("offers an explicit session-only hide action", () => {
const onHide = vi.fn();
const displayRef = {
current: {
disconnect: vi.fn(),
isConnected: () => true,
sendKey: vi.fn(),
sendMouse: vi.fn(),
setClipboard: vi.fn(),
getFilesystem: () => null,
uploadFile: vi.fn(),
zoomIn: vi.fn(() => 1.25),
zoomOut: vi.fn(() => 0.75),
resetZoom: vi.fn(() => 1),
} satisfies GuacamoleDisplayHandle,
} as React.RefObject<GuacamoleDisplayHandle>;
const { getByLabelText } = render(
<GuacamoleToolbar
displayRef={displayRef}
protocol="rdp"
onHide={onHide}
/>,
);
fireEvent.click(getByLabelText("remoteDesktop.toolbar.hide"));
expect(onHide).toHaveBeenCalledOnce();
});
it("labels the VNC meta key Super instead of Windows", () => {
const displayRef = {
current: {
disconnect: vi.fn(),
isConnected: () => true,
sendKey: vi.fn(),
sendMouse: vi.fn(),
setClipboard: vi.fn(),
getFilesystem: () => null,
uploadFile: vi.fn(),
zoomIn: vi.fn(() => 1.25),
zoomOut: vi.fn(() => 0.75),
resetZoom: vi.fn(() => 1),
} satisfies GuacamoleDisplayHandle,
} as React.RefObject<GuacamoleDisplayHandle>;
const { getByText, queryByText } = render(
<GuacamoleToolbar
displayRef={displayRef}
protocol="vnc"
metaKeyFamily="super"
/>,
);
expect(getByText("Super+L")).toBeTruthy();
expect(getByText("Super")).toBeTruthy();
expect(queryByText("Win+L")).toBeNull();
expect(queryByText("Win")).toBeNull();
});
});
@@ -0,0 +1,83 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import {
renderWithApp,
type RenderedPluginApp,
} from "@termix/plugin-sdk/testing";
import type { PluginManifest } from "@termix/plugin-sdk/manifest";
import * as plugin from "../../src/frontend/index";
import manifestJson from "../../manifest.json";
import locales from "../../locales/en.json";
const manifest = manifestJson as unknown as PluginManifest;
const declared = (views?: Array<{ id: string }>) =>
(views ?? []).map((view) => view.id).sort();
let rendered: RenderedPluginApp | null = null;
afterEach(async () => {
await rendered?.deactivate();
rendered = null;
});
describe(`${manifest.id} activate`, () => {
it("activates and registers only views its manifest declares", async () => {
rendered = await renderWithApp(plugin, { manifest, locales });
const contributes = manifest.contributes as unknown as Record<
string,
Array<{ id: string }> | undefined
>;
const tabs = declared(contributes.tabs);
// A rail panel may reuse one of the plugin's tab ids.
const panels = [...declared(contributes.panels), ...tabs];
const cards = declared(contributes.dashboardCards);
for (const id of rendered.registered.tabs()) expect(tabs).toContain(id);
for (const id of rendered.registered.panels()) expect(panels).toContain(id);
for (const id of rendered.registered.dashboardCards()) {
expect(cards).toContain(id);
}
});
it("removes everything it registered on deactivate", async () => {
const app = await renderWithApp(plugin, { manifest, locales });
await app.deactivate();
expect(app.registered.tabs()).toEqual([]);
expect(app.registered.panels()).toEqual([]);
expect(app.registered.railItems()).toEqual([]);
expect(app.registered.hostActions()).toEqual([]);
expect(app.registered.hostProtocols()).toEqual([]);
expect(app.registered.hostEditorSections()).toEqual([]);
expect(app.registered.dashboardCards()).toEqual([]);
expect(app.registered.settingsComponents()).toEqual([]);
expect(app.registered.actions()).toEqual([]);
});
it("adds RDP, VNC and Telnet as host protocols while turned on", async () => {
rendered = await renderWithApp(plugin, {
manifest,
locales,
api: { get: async () => ({ data: { enabled: true } }) } as never,
});
await vi.waitFor(() =>
expect(rendered!.registered.hostProtocols().sort()).toEqual([
"rdp",
"telnet",
"vnc",
]),
);
expect(rendered.registered.hostActions().map((a) => a.id)).toEqual(
expect.arrayContaining(["rdp", "vnc", "telnet"]),
);
});
it("offers no way in while an admin has turned it off", async () => {
rendered = await renderWithApp(plugin, {
manifest,
locales,
api: { get: async () => ({ data: { enabled: false } }) } as never,
});
await new Promise((resolve) => setTimeout(resolve, 20));
expect(rendered.registered.hostProtocols()).toEqual([]);
expect(rendered.registered.hostActions()).toEqual([]);
});
});
@@ -0,0 +1,217 @@
import { describe, expect, it, vi, beforeEach } from "vitest";
const authApiMock = vi.hoisted(() => ({
get: vi.fn(async () => ({ data: { guacd: { status: "disconnected" } } })),
post: vi.fn(async () => ({ data: { token: "local-token" } })),
}));
const remoteApiMock = vi.hoisted(() => ({
get: vi.fn(async () => ({ data: { guacd: { status: "connected" } } })),
post: vi.fn(async () => ({ data: { token: "remote-token" } })),
}));
const isElectronMock = vi.hoisted(() => vi.fn(() => false));
const resolveRemoteHostIdMock = vi.hoisted(() => vi.fn());
vi.mock("@termix/plugin-sdk/ui", () => ({
isElectron: isElectronMock,
resolveRemoteHostId: resolveRemoteHostIdMock,
}));
import {
setRemoteDesktopApp,
getGuacdStatus,
getGuacamoleConnectionId,
getGuacamoleTokenFromHost,
} from "../../src/frontend/guacamole-api";
// Mirrors app.apiFor: the remote server only for a remote origin in the
// desktop app, the ordinary client otherwise.
setRemoteDesktopApp({
apiFor: (origin) =>
(origin === "remote" && isElectronMock()
? remoteApiMock
: authApiMock) as never,
});
beforeEach(() => {
authApiMock.get.mockClear();
authApiMock.post.mockClear();
remoteApiMock.get.mockClear();
remoteApiMock.post.mockClear();
resolveRemoteHostIdMock.mockReset();
});
describe("guacamole API origin", () => {
it("uses the shared instance in the browser", async () => {
isElectronMock.mockReturnValue(false);
await getGuacdStatus("remote");
await getGuacamoleTokenFromHost(9, "remote", "vnc");
expect(authApiMock.get).toHaveBeenCalledWith("/status");
expect(authApiMock.post).toHaveBeenCalledOnce();
expect(remoteApiMock.get).not.toHaveBeenCalled();
expect(remoteApiMock.post).not.toHaveBeenCalled();
});
it("uses the connected remote server in the desktop app", async () => {
isElectronMock.mockReturnValue(true);
// The embedded backend has no guacd, so asking it reports "disconnected"
// even when the connected server can serve the session.
const status = await getGuacdStatus("remote");
const token = await getGuacamoleTokenFromHost(9, "remote", "vnc");
expect(status.guacd.status).toBe("connected");
expect(token.token).toBe("remote-token");
expect(remoteApiMock.get).toHaveBeenCalledWith("/status");
expect(remoteApiMock.post).toHaveBeenCalledOnce();
expect(authApiMock.get).not.toHaveBeenCalled();
expect(authApiMock.post).not.toHaveBeenCalled();
});
it("sends the connect-host payload unchanged to the remote server", async () => {
isElectronMock.mockReturnValue(true);
resolveRemoteHostIdMock.mockResolvedValue(41);
await getGuacamoleTokenFromHost(
9,
"remote",
"rdp",
{
username: "admin",
password: "secret",
domain: "EXAMPLE",
},
"host-sync-id",
);
expect(remoteApiMock.post).toHaveBeenCalledWith("/connect-host/41", {
protocol: "rdp",
promptedUsername: "admin",
promptedPassword: "secret",
promptedDomain: "EXAMPLE",
});
expect(resolveRemoteHostIdMock).toHaveBeenCalledWith("host-sync-id");
});
it("sends an empty prompted domain for local RDP accounts", async () => {
isElectronMock.mockReturnValue(false);
await getGuacamoleTokenFromHost(9, "local", "rdp", {
username: "local-admin",
password: "secret",
domain: "",
});
expect(authApiMock.post).toHaveBeenCalledWith("/connect-host/9", {
protocol: "rdp",
promptedUsername: "local-admin",
promptedPassword: "secret",
promptedDomain: "",
});
});
// Support#1240: a host set to "This Device" is served by the embedded
// backend, so its token has to come from there too -- and its id must not
// be remapped onto a remote server that may not even be configured.
it("asks the embedded backend when the host originates locally", async () => {
isElectronMock.mockReturnValue(true);
const token = await getGuacamoleTokenFromHost(9, "local", "rdp");
expect(token.token).toBe("local-token");
expect(authApiMock.post).toHaveBeenCalledWith("/connect-host/9", {
protocol: "rdp",
});
expect(remoteApiMock.post).not.toHaveBeenCalled();
expect(resolveRemoteHostIdMock).not.toHaveBeenCalled();
});
it("reports the embedded backend's own guacd for a local origin", async () => {
isElectronMock.mockReturnValue(true);
const status = await getGuacdStatus("local");
expect(status.guacd.status).toBe("disconnected");
expect(authApiMock.get).toHaveBeenCalledWith("/status");
expect(remoteApiMock.get).not.toHaveBeenCalled();
});
it("uses the remote server for an explicitly remote origin", async () => {
isElectronMock.mockReturnValue(true);
await getGuacamoleTokenFromHost(9, "remote", "rdp");
expect(remoteApiMock.post).toHaveBeenCalledOnce();
expect(authApiMock.post).not.toHaveBeenCalled();
});
it("does not fall back to a colliding local id when sync resolution fails", async () => {
isElectronMock.mockReturnValue(true);
resolveRemoteHostIdMock.mockResolvedValue(null);
await expect(
getGuacamoleTokenFromHost(
9,
"remote",
"rdp",
undefined,
"missing-sync-id",
),
).rejects.toThrow("The synced host does not exist on the remote server");
expect(remoteApiMock.post).not.toHaveBeenCalled();
});
});
it("looks up the session on the backend that issued its token", async () => {
isElectronMock.mockReturnValue(true);
const signal = new AbortController().signal;
await getGuacamoleConnectionId("connect/id", "remote", signal);
expect(remoteApiMock.get).toHaveBeenCalledWith("/connection/connect%2Fid", {
signal,
});
expect(authApiMock.get).not.toHaveBeenCalled();
});
describe("saved host status routing", () => {
it("maps the local host ID before probing the remote backend", async () => {
isElectronMock.mockReturnValue(true);
resolveRemoteHostIdMock.mockResolvedValue(41);
await getGuacdStatus("remote", {
hostId: 9,
protocol: "rdp",
syncId: "sync-9",
});
expect(resolveRemoteHostIdMock).toHaveBeenCalledWith("sync-9");
expect(remoteApiMock.get).toHaveBeenCalledWith(
"/status?hostId=41&protocol=rdp",
);
expect(authApiMock.get).not.toHaveBeenCalled();
});
it("keeps local IDs for local connections", async () => {
isElectronMock.mockReturnValue(true);
await getGuacdStatus("local", {
hostId: 9,
protocol: "vnc",
syncId: "sync-9",
});
expect(authApiMock.get).toHaveBeenCalledWith(
"/status?hostId=9&protocol=vnc",
);
expect(resolveRemoteHostIdMock).not.toHaveBeenCalled();
});
it("does not probe an unrelated remote ID when a synced host is missing", async () => {
isElectronMock.mockReturnValue(true);
resolveRemoteHostIdMock.mockResolvedValue(null);
await expect(
getGuacdStatus("remote", {
hostId: 9,
protocol: "rdp",
syncId: "missing",
}),
).rejects.toThrow("does not exist");
expect(remoteApiMock.get).not.toHaveBeenCalled();
});
});
@@ -0,0 +1,63 @@
import { describe, expect, it, vi } from "vitest";
import {
isPasteShortcut,
pasteTextToRemote,
type GuacamoleClipboardClient,
} from "../../src/frontend/guacamole-clipboard.js";
describe("Guacamole clipboard paste", () => {
it("recognizes Ctrl+V and Command+V without intercepting Alt+V", () => {
expect(
isPasteShortcut({
key: "v",
ctrlKey: true,
metaKey: false,
altKey: false,
}),
).toBe(true);
expect(
isPasteShortcut({
key: "V",
ctrlKey: false,
metaKey: true,
altKey: false,
}),
).toBe(true);
expect(
isPasteShortcut({
key: "v",
ctrlKey: true,
metaKey: false,
altKey: true,
}),
).toBe(false);
});
it("updates the remote clipboard before sending Ctrl+V", () => {
const events: string[] = [];
const client: GuacamoleClipboardClient = {
createClipboardStream: vi.fn((mimetype: string) => {
events.push(`stream:${mimetype}`);
return {
sendBlob: () => events.push("blob"),
sendEnd: () => events.push("end"),
};
}),
sendKeyEvent: vi.fn((pressed: number, keysym: number) => {
events.push(`key:${pressed}:${keysym}`);
}),
};
pasteTextToRemote(client, "Firefox clipboard");
expect(events).toEqual([
"stream:text/plain",
"blob",
"end",
"key:1:65507",
"key:1:118",
"key:0:118",
"key:0:65507",
]);
});
});
@@ -0,0 +1,67 @@
import { describe, expect, it } from "vitest";
import {
getGuacamoleDisplaySize,
readConfiguredDimension,
} from "../../src/frontend/guacamole-display-size";
describe("getGuacamoleDisplaySize", () => {
it("requests native pixels and matching DPI for HiDPI RDP", () => {
expect(getGuacamoleDisplaySize(1280, 720, "rdp", 2)).toEqual({
width: 2560,
height: 1440,
dpi: 192,
pixelRatio: 2,
});
});
it("scales a configured RDP DPI with the device pixel ratio", () => {
expect(getGuacamoleDisplaySize(1000, 600, "rdp", 1.5, 120)).toEqual({
width: 1500,
height: 900,
dpi: 180,
pixelRatio: 1.5,
});
});
it("leaves non-RDP protocols at CSS-pixel dimensions", () => {
expect(getGuacamoleDisplaySize(1280, 720, "vnc", 2)).toEqual({
width: 1280,
height: 720,
pixelRatio: 1,
});
});
it("caps pathological pixel ratios to protect remote session size", () => {
expect(getGuacamoleDisplaySize(400, 800, "rdp", 4)).toEqual({
width: 1200,
height: 2400,
dpi: 288,
pixelRatio: 3,
});
});
it("uses a configured resolution in place of the container size", () => {
expect(
getGuacamoleDisplaySize(800, 600, "rdp", 1, undefined),
).toMatchObject({ width: 800, height: 600 });
expect(
getGuacamoleDisplaySize(1920, 1080, "rdp", 1, undefined),
).toMatchObject({ width: 1920, height: 1080 });
});
});
describe("readConfiguredDimension", () => {
it("accepts the strings the host editor stores", () => {
expect(readConfiguredDimension("1920")).toBe(1920);
expect(readConfiguredDimension(1080)).toBe(1080);
});
it("treats an unset or unusable value as 'follow the container'", () => {
expect(readConfiguredDimension("")).toBeUndefined();
expect(readConfiguredDimension(undefined)).toBeUndefined();
expect(readConfiguredDimension(null)).toBeUndefined();
expect(readConfiguredDimension("auto")).toBeUndefined();
expect(readConfiguredDimension("0")).toBeUndefined();
expect(readConfiguredDimension("-1080")).toBeUndefined();
});
});
@@ -0,0 +1,25 @@
import { describe, expect, it } from "vitest";
import {
canUploadToRdpDrive,
getFileDropDisposition,
hasDraggedFiles,
} from "../../src/frontend/guacamole-file-drop.ts";
describe("Guacamole file drop", () => {
it("recognizes external file drags", () => {
expect(hasDraggedFiles(["Files"])).toBe(true);
expect(hasDraggedFiles(["text/plain"])).toBe(false);
});
it("rejects files when upload is unavailable instead of ignoring them", () => {
expect(getFileDropDisposition(["Files"], 1, false)).toBe("reject");
expect(getFileDropDisposition(["Files"], 1, true)).toBe("upload");
});
it("accepts an enabled RDP drive before a filesystem object is advertised", () => {
expect(canUploadToRdpDrive(true, true, false)).toBe(true);
expect(canUploadToRdpDrive(true, false, true)).toBe(true);
expect(canUploadToRdpDrive(true, false, false)).toBe(false);
expect(canUploadToRdpDrive(false, true, true)).toBe(false);
});
});
@@ -0,0 +1,20 @@
import { describe, expect, it } from "vitest";
import { describeUploadError } from "../../src/frontend/guacamole-filesystem";
const t = (key: string) => `#${key}`;
describe("describeUploadError", () => {
it("explains guacd's refusal to open the target file as a drive-folder problem", () => {
expect(describeUploadError(new Error("FAIL (CANNOT OPEN)"), t)).toBe(
"#driveNotWritable",
);
expect(describeUploadError(new Error("FAIL (NO FS)"), t)).toBe(
"#driveUnavailable",
);
});
it("passes other messages through and falls back for unknown errors", () => {
expect(describeUploadError(new Error("disk full"), t)).toBe("disk full");
expect(describeUploadError(undefined, t)).toBe("#uploadFailed");
});
});
@@ -0,0 +1,106 @@
/// <reference types="vite/client" />
import ts from "typescript";
import Guacamole from "guacamole-common-js";
import { afterEach, describe, expect, it, vi } from "vitest";
import displaySource from "../../src/frontend/GuacamoleDisplay.tsx?raw";
// Exercise the display's actual focus callback with Guacamole's real keyboard.
const source = ts.createSourceFile(
"GuacamoleDisplay.tsx",
displaySource,
ts.ScriptTarget.Latest,
true,
ts.ScriptKind.TSX,
);
let callback: string | undefined;
function visit(node: ts.Node) {
if (
ts.isVariableDeclaration(node) &&
node.name.getText(source) === "refreshKeyboardHandlers" &&
node.initializer &&
ts.isCallExpression(node.initializer)
) {
callback = node.initializer.arguments[0].getText(source);
}
ts.forEachChild(node, visit);
}
visit(source);
if (!callback) throw new Error("Missing refreshKeyboardHandlers callback");
const runRefresh = new Function(
"state",
ts.transpileModule(
`const { keyboardRef, clientRef, displayElementRef, isVisible, windowFocusedRef, hasKeyboardFocusRef } = state;
(${callback})();`,
{ compilerOptions: { target: ts.ScriptTarget.ES2022 } },
).outputText,
) as (state: Record<string, unknown>) => void;
afterEach(() => {
vi.restoreAllMocks();
document.body.replaceChildren();
});
describe("remote keyboard focus loss", () => {
it.each(["window", "display", "hidden", "tab"])(
"releases held modifiers when losing %s focus",
(reason) => {
const display = document.createElement("div");
display.tabIndex = 0;
document.body.append(display);
display.focus();
vi.spyOn(document, "visibilityState", "get").mockReturnValue("visible");
const keyboard = new Guacamole.Keyboard(display) as Guacamole.Keyboard & {
press(keysym: number): boolean;
release(keysym: number): void;
pressed: Record<number, boolean>;
};
const sendKeyEvent = vi.fn();
const state = {
keyboardRef: { current: keyboard },
clientRef: { current: { sendKeyEvent } },
displayElementRef: { current: display },
isVisible: true,
windowFocusedRef: { current: true },
hasKeyboardFocusRef: { current: true },
};
const refresh = () => runRefresh(state);
refresh();
const keys = [0xffeb, 0xffe3, 0xffe9]; // Windows, Ctrl, Alt
keys.forEach((key) => keyboard.press(key));
expect(sendKeyEvent.mock.calls).toEqual(keys.map((key) => [1, key]));
if (reason === "window") state.windowFocusedRef.current = false;
if (reason === "tab") state.isVisible = false;
if (reason === "hidden") {
vi.spyOn(document, "visibilityState", "get").mockReturnValue("hidden");
}
if (reason === "display") {
state.hasKeyboardFocusRef.current = false;
display.blur();
}
refresh();
expect(sendKeyEvent.mock.calls.slice(3)).toEqual(
[...keys].sort((a, b) => a - b).map((key) => [0, key]),
);
expect(Object.keys(keyboard.pressed)).toEqual([]);
expect(keyboard.onkeydown).toBeNull();
expect(keyboard.onkeyup).toBeNull();
keyboard.press(0x6c);
keyboard.reset();
expect(sendKeyEvent).toHaveBeenCalledTimes(6);
state.isVisible = true;
state.windowFocusedRef.current = true;
state.hasKeyboardFocusRef.current = true;
vi.spyOn(document, "visibilityState", "get").mockReturnValue("visible");
display.focus();
refresh();
keyboard.press(0x6c);
keyboard.release(0x6c);
expect(sendKeyEvent.mock.calls.slice(6)).toEqual([
[1, 0x6c],
[0, 0x6c],
]);
},
);
});
@@ -0,0 +1,48 @@
import { describe, expect, it } from "vitest";
import {
detectMetaKeyFamily,
metaKeyLabels,
} from "../../src/frontend/guacamole-meta-key.js";
describe("guacamole toolbar meta key labels", () => {
it("keeps Windows labels for RDP regardless of the local device", () => {
expect(
detectMetaKeyFamily("rdp", {
platform: "MacIntel",
userAgent: "Macintosh; Intel Mac OS X",
userAgentDataPlatform: "macOS",
}),
).toBe("win");
});
it("labels VNC with the local device's modifier instead of Windows", () => {
expect(
detectMetaKeyFamily("vnc", {
platform: "MacIntel",
userAgent: "Macintosh; Intel Mac OS X",
userAgentDataPlatform: "macOS",
}),
).toBe("cmd");
expect(
detectMetaKeyFamily("vnc", {
platform: "Linux x86_64",
userAgent: "X11; Linux x86_64",
userAgentDataPlatform: "Linux",
}),
).toBe("super");
expect(
detectMetaKeyFamily("vnc", {
platform: "Win32",
userAgent: "Windows NT 10.0",
userAgentDataPlatform: "Windows",
}),
).toBe("win");
});
it("exposes Super/Cmd button copy for non-Windows VNC sessions", () => {
expect(metaKeyLabels("super").short).toBe("Super");
expect(metaKeyLabels("super").lock).toBe("Super+L");
expect(metaKeyLabels("cmd").short).toBe("Cmd");
expect(metaKeyLabels("cmd").lock).toBe("Cmd+L");
});
});
@@ -0,0 +1,82 @@
import { describe, expect, it, vi } from "vitest";
import { bindPointerInput } from "../../src/frontend/guacamole-pointer.js";
function listenedEvents(): {
element: HTMLElement;
events: () => string[];
} {
const element = document.createElement("div");
const seen: string[] = [];
const original = element.addEventListener.bind(element);
element.addEventListener = ((type: string, ...rest: unknown[]) => {
seen.push(type);
return (original as (t: string, ...r: unknown[]) => void)(type, ...rest);
}) as typeof element.addEventListener;
return { element, events: () => seen };
}
describe("bindPointerInput", () => {
// A touchscreen laptop reports maxTouchPoints > 0 and still has a mouse.
// Binding only the touch emulator left the pointer dead on those machines.
it.each([
["no touch mode", null],
["touchscreen", "touchscreen" as const],
["touchpad", "touchpad" as const],
])("keeps the physical pointer bound with %s", (_label, touchMode) => {
const { element, events } = listenedEvents();
bindPointerInput(element, touchMode, vi.fn());
expect(events()).toContain("mousedown");
expect(events()).toContain("mousemove");
expect(events()).toContain("mouseup");
});
it("adds touch listeners only when a touch mode is selected", () => {
const withoutTouch = listenedEvents();
bindPointerInput(withoutTouch.element, null, vi.fn());
const plainTouchCount = withoutTouch
.events()
.filter((e) => e.startsWith("touch")).length;
const withTouch = listenedEvents();
bindPointerInput(withTouch.element, "touchscreen", vi.fn());
const touchModeCount = withTouch
.events()
.filter((e) => e.startsWith("touch")).length;
expect(touchModeCount).toBeGreaterThan(plainTouchCount);
});
it("does not forward a wheel flood as individual remote scroll clicks", () => {
vi.useFakeTimers();
const element = document.createElement("div");
const onState = vi.fn();
const dispose = bindPointerInput(element, null, onState);
for (let i = 0; i < 30; i++) {
element.dispatchEvent(
new WheelEvent("wheel", {
deltaY: 120,
deltaMode: 0,
bubbles: true,
cancelable: true,
}),
);
}
const presses = onState.mock.calls.filter(
([state]) => state.up === true || state.down === true,
);
expect(presses.length).toBeLessThanOrEqual(2);
vi.advanceTimersByTime(40);
const laterPresses = onState.mock.calls.filter(
([state]) => state.up === true || state.down === true,
);
expect(laterPresses.length).toBeLessThanOrEqual(4);
dispose();
vi.useRealTimers();
});
});
@@ -0,0 +1,81 @@
import { describe, expect, it, vi } from "vitest";
import {
clampPendingScrollClicks,
createScrollCoalescer,
isScrollButtonState,
MAX_SCROLL_CLICKS_PER_FLUSH,
SCROLL_FLUSH_INTERVAL_MS,
takeScrollClicks,
} from "../../src/frontend/guacamole-scroll.js";
describe("guacamole scroll coalescing", () => {
it("caps a click flood so a laggy tunnel cannot queue a backlog", () => {
expect(clampPendingScrollClicks(0, 40)).toBe(MAX_SCROLL_CLICKS_PER_FLUSH);
expect(clampPendingScrollClicks(0, -40)).toBe(-MAX_SCROLL_CLICKS_PER_FLUSH);
expect(takeScrollClicks(20)).toEqual({
clicks: MAX_SCROLL_CLICKS_PER_FLUSH,
direction: 1,
});
});
it("sends the first notch immediately and drops later clicks in the same burst", () => {
const send = vi.fn();
let now = 1_000;
const scheduled: Array<{ fn: () => void; ms: number }> = [];
const coalescer = createScrollCoalescer(
send,
() => now,
(fn, ms) => {
scheduled.push({ fn, ms });
return scheduled.length;
},
() => {},
);
coalescer.notePointer({
x: 12,
y: 34,
left: false,
middle: false,
right: false,
up: false,
down: false,
});
for (let i = 0; i < 20; i++) coalescer.ingestClick(1);
const firstBurst = send.mock.calls.map(([state]) => state);
expect(firstBurst).toEqual([
{
x: 12,
y: 34,
left: false,
middle: false,
right: false,
up: false,
down: true,
},
{
x: 12,
y: 34,
left: false,
middle: false,
right: false,
up: false,
down: false,
},
]);
send.mockClear();
now += SCROLL_FLUSH_INTERVAL_MS;
expect(scheduled).toHaveLength(1);
scheduled[0].fn();
expect(send.mock.calls).toHaveLength(MAX_SCROLL_CLICKS_PER_FLUSH * 2);
coalescer.dispose();
});
it("recognizes only the scroll buttons as coalesced input", () => {
expect(isScrollButtonState({ up: true, down: false })).toBe(true);
expect(isScrollButtonState({ up: false, down: true })).toBe(true);
expect(isScrollButtonState({ up: false, down: false })).toBe(false);
});
});
Loaded 100 of 4405 files, more files were not shown because too many files have changed in this diff. Show more