feat: Add i18n support for terminal customization and login stats

- Add comprehensive terminal customization translations (60+ keys) for appearance, behavior, and advanced settings across all 4 languages
- Add SSH login statistics translations
- Update HostManagerEditor to use i18n for all terminal customization UI elements
- Update LoginStatsWidget to use i18n for all UI text
- Add missing logger imports in backend files for improved debugging
This commit is contained in:
ZacharyZcR
2025-11-09 06:13:27 +08:00
parent 398d5c0704
commit 62a7734777
11 changed files with 373 additions and 121 deletions

60
package-lock.json generated
View File

@@ -154,7 +154,6 @@
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5",
@@ -440,7 +439,6 @@
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.19.1.tgz",
"integrity": "sha512-q6NenYkEy2fn9+JyjIxMWcNjzTL/IhwqfzOut1/G3PrIFkrbl4AL7Wkse5tLrQUUyqGoAKU5+Pi5jnnXxH5HGw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
@@ -489,7 +487,6 @@
"resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz",
"integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/language": "^6.0.0",
@@ -516,7 +513,6 @@
"resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.11.tgz",
"integrity": "sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/lang-css": "^6.0.0",
@@ -544,7 +540,6 @@
"resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.4.tgz",
"integrity": "sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/language": "^6.6.0",
@@ -745,7 +740,6 @@
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.3.tgz",
"integrity": "sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.23.0",
@@ -822,7 +816,6 @@
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz",
"integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@marijn/find-cluster-break": "^1.0.0"
}
@@ -844,7 +837,6 @@
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.6.tgz",
"integrity": "sha512-qiS0z1bKs5WOvHIAC0Cybmv4AJSkAXgX5aD6Mqd2epSLlVJsQl8NG23jCVouIgkh4All/mrbdsf2UOLFnJw0tw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@codemirror/state": "^6.5.0",
"crelt": "^1.0.6",
@@ -1172,7 +1164,6 @@
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -1559,6 +1550,7 @@
"dev": true,
"license": "BSD-2-Clause",
"optional": true,
"peer": true,
"dependencies": {
"cross-dirname": "^0.1.0",
"debug": "^4.3.4",
@@ -1580,6 +1572,7 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
@@ -2536,8 +2529,7 @@
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.3.0.tgz",
"integrity": "sha512-L9X8uHCYU310o99L3/MpJKYxPzXPOS7S0NmBaM7UO/x2Kb2WbmMLSkfvdr1KxRIFYOpbY0Jhn7CfLSUDzL8arQ==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/@lezer/cpp": {
"version": "1.1.3",
@@ -2577,7 +2569,6 @@
"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz",
"integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==",
"license": "MIT",
"peer": true,
"dependencies": {
"@lezer/common": "^1.3.0"
}
@@ -2609,7 +2600,6 @@
"resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.4.tgz",
"integrity": "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.1.3",
@@ -2632,7 +2622,6 @@
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz",
"integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@lezer/common": "^1.0.0"
}
@@ -4856,7 +4845,6 @@
"integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/node": "*"
}
@@ -5014,7 +5002,6 @@
"resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.5.tgz",
"integrity": "sha512-LuIQOcb6UmnF7C1PCFmEU1u2hmiHL43fgFQX67sN3H4Z+0Yk0Neo++mFsBjhOAuLzvlQeqAAkeDOZrJs9rzumQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^5.0.0",
@@ -5137,7 +5124,6 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.2.tgz",
"integrity": "sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==",
"license": "MIT",
"peer": true,
"dependencies": {
"undici-types": "~7.16.0"
}
@@ -5180,7 +5166,6 @@
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz",
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.0.2"
}
@@ -5191,7 +5176,6 @@
"integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==",
"devOptional": true,
"license": "MIT",
"peer": true,
"peerDependencies": {
"@types/react": "^19.2.0"
}
@@ -5359,7 +5343,6 @@
"integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.46.2",
"@typescript-eslint/types": "8.46.2",
@@ -5736,8 +5719,7 @@
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
"integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/7zip-bin": {
"version": "5.2.0",
@@ -5772,7 +5754,6 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -6195,7 +6176,6 @@
"integrity": "sha512-3yVdyZhklTiNrtg+4WqHpJpFDd+WHTg2oM7UcR80GqL05AOV0xEJzc6qNvFYoEtE+hRp1n9MpN6/+4yhlGkDXQ==",
"hasInstallScript": true,
"license": "MIT",
"peer": true,
"dependencies": {
"bindings": "^1.5.0",
"prebuild-install": "^7.1.1"
@@ -6330,7 +6310,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.8.19",
"caniuse-lite": "^1.0.30001751",
@@ -7334,7 +7313,6 @@
"integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"env-paths": "^2.2.1",
"import-fresh": "^3.3.0",
@@ -7411,7 +7389,8 @@
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
"dev": true,
"license": "MIT",
"optional": true
"optional": true,
"peer": true
},
"node_modules/cross-spawn": {
"version": "7.0.6",
@@ -7870,7 +7849,6 @@
"integrity": "sha512-59CAAjAhTaIMCN8y9kD573vDkxbs1uhDcrFLHSgutYdPcGOU35Rf95725snvzEOy4BFB7+eLJ8djCNPmGwG67w==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"app-builder-lib": "26.0.12",
"builder-util": "26.0.11",
@@ -7968,7 +7946,8 @@
"version": "3.1.7",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.7.tgz",
"integrity": "sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==",
"license": "(MPL-2.0 OR Apache-2.0)"
"license": "(MPL-2.0 OR Apache-2.0)",
"peer": true
},
"node_modules/dot-prop": {
"version": "5.3.0",
@@ -8320,6 +8299,7 @@
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@electron/asar": "^3.2.1",
"debug": "^4.1.1",
@@ -8340,6 +8320,7 @@
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"graceful-fs": "^4.1.2",
"jsonfile": "^4.0.0",
@@ -8355,6 +8336,7 @@
"integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
"dev": true,
"license": "MIT",
"peer": true,
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
@@ -8365,6 +8347,7 @@
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">= 4.0.0"
}
@@ -8627,7 +8610,6 @@
"integrity": "sha512-iy2GE3MHrYTL5lrCtMZ0X1KLEKKUjmK0kzwcnefhR66txcEmXZD2YWgR5GNdcEwkNx3a0siYkSvl0vIC+Svjmg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -10226,7 +10208,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.27.6"
},
@@ -11768,6 +11749,7 @@
"resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz",
"integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==",
"license": "MIT",
"peer": true,
"bin": {
"marked": "bin/marked.js"
},
@@ -13795,6 +13777,7 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"commander": "^9.4.0"
},
@@ -13812,6 +13795,7 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": "^12.20.0 || >=14"
}
@@ -14263,7 +14247,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -14273,7 +14256,6 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -14300,7 +14282,6 @@
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.66.0.tgz",
"integrity": "sha512-xXBqsWGKrY46ZqaHDo+ZUYiMUgi8suYu5kdrS20EG8KiL7VRQitEbNjm+UcrDYrNi1YLyfpmAeGjCZYXLT9YBw==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=18.0.0"
},
@@ -14448,7 +14429,6 @@
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/use-sync-external-store": "^0.0.6",
"use-sync-external-store": "^1.4.0"
@@ -14657,8 +14637,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/redux-thunk": {
"version": "3.1.0",
@@ -15979,6 +15958,7 @@
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"mkdirp": "^0.5.1",
"rimraf": "~2.6.2"
@@ -16019,6 +15999,7 @@
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"minimist": "^1.2.6"
},
@@ -16033,6 +16014,7 @@
"deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
"license": "ISC",
"peer": true,
"dependencies": {
"glob": "^7.1.3"
},
@@ -16137,7 +16119,6 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -16343,7 +16324,6 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"devOptional": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -16756,7 +16736,6 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz",
"integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==",
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.5.0",
@@ -16848,7 +16827,6 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},

View File

@@ -22,7 +22,7 @@ import { nanoid } from "nanoid";
import speakeasy from "speakeasy";
import QRCode from "qrcode";
import type { Request, Response } from "express";
import { authLogger } from "../../utils/logger.js";
import { authLogger, databaseLogger } from "../../utils/logger.js";
import { AuthManager } from "../../utils/auth-manager.js";
import { DataCrypto } from "../../utils/data-crypto.js";
import { LazyFieldEncryption } from "../../utils/lazy-field-encryption.js";

View File

@@ -6,7 +6,7 @@ import { Client as SSHClient } from "ssh2";
import { getDb } from "../database/db/index.js";
import { sshCredentials, sshData } from "../database/db/schema.js";
import { eq, and } from "drizzle-orm";
import { fileLogger } from "../utils/logger.js";
import { fileLogger, sshLogger } from "../utils/logger.js";
import { SimpleDBOps } from "../utils/simple-db-ops.js";
import { AuthManager } from "../utils/auth-manager.js";
import type { AuthenticatedRequest } from "../../types/index.js";

View File

@@ -15,7 +15,7 @@ import type {
ErrorType,
} from "../../types/index.js";
import { CONNECTION_STATES } from "../../types/index.js";
import { tunnelLogger } from "../utils/logger.js";
import { tunnelLogger, sshLogger } from "../utils/logger.js";
import { SystemCrypto } from "../utils/system-crypto.js";
import { SimpleDBOps } from "../utils/simple-db-ops.js";
import { DataCrypto } from "../utils/data-crypto.js";

View File

@@ -1,4 +1,5 @@
import ssh2Pkg from "ssh2";
import { sshLogger } from "./logger.js";
const ssh2Utils = ssh2Pkg.utils;
function detectKeyTypeFromContent(keyContent: string): string {

View File

@@ -706,6 +706,69 @@
"statusMonitoring": "Status",
"metricsMonitoring": "Metriken",
"terminalCustomizationNotice": "Hinweis: Terminal-Anpassungen funktionieren nur in der Desktop-Website-Version. Mobile und Electron-Apps verwenden die Standard-Terminaleinstellungen des Systems.",
"terminalCustomization": "Terminal-Anpassung",
"appearance": "Aussehen",
"behavior": "Verhalten",
"advanced": "Erweitert",
"themePreview": "Themen-Vorschau",
"theme": "Thema",
"selectTheme": "Thema auswählen",
"chooseColorTheme": "Wählen Sie ein Farbthema für das Terminal",
"fontFamily": "Schriftfamilie",
"selectFont": "Schriftart auswählen",
"selectFontDesc": "Wählen Sie die im Terminal zu verwendende Schriftart",
"fontSize": "Schriftgröße",
"fontSizeValue": "Schriftgröße: {{value}}px",
"adjustFontSize": "Terminal-Schriftgröße anpassen",
"letterSpacing": "Zeichenabstand",
"letterSpacingValue": "Zeichenabstand: {{value}}px",
"adjustLetterSpacing": "Abstand zwischen Zeichen anpassen",
"lineHeight": "Zeilenhöhe",
"lineHeightValue": "Zeilenhöhe: {{value}}",
"adjustLineHeight": "Abstand zwischen Zeilen anpassen",
"cursorStyle": "Cursor-Stil",
"selectCursorStyle": "Cursor-Stil auswählen",
"cursorStyleBlock": "Block",
"cursorStyleUnderline": "Unterstrich",
"cursorStyleBar": "Balken",
"chooseCursorAppearance": "Cursor-Erscheinungsbild wählen",
"cursorBlink": "Cursor-Blinken",
"enableCursorBlink": "Cursor-Blinkanimation aktivieren",
"scrollbackBuffer": "Rückwärts-Puffer",
"scrollbackBufferValue": "Rückwärts-Puffer: {{value}} Zeilen",
"scrollbackBufferDesc": "Anzahl der Zeilen im Rückwärtsverlauf",
"bellStyle": "Signalton-Stil",
"selectBellStyle": "Signalton-Stil auswählen",
"bellStyleNone": "Keine",
"bellStyleSound": "Ton",
"bellStyleVisual": "Visuell",
"bellStyleBoth": "Beides",
"bellStyleDesc": "Behandlung des Terminal-Signaltons (BEL-Zeichen, \\x07). Programme lösen dies aus, wenn Aufgaben abgeschlossen werden, Fehler auftreten oder für Benachrichtigungen. \"Ton\" spielt einen akustischen Signalton ab, \"Visuell\" lässt den Bildschirm kurz aufblinken, \"Beides\" macht beides, \"Keine\" deaktiviert Signalton-Benachrichtigungen.",
"rightClickSelectsWord": "Rechtsklick wählt Wort",
"rightClickSelectsWordDesc": "Rechtsklick wählt das Wort unter dem Cursor aus",
"fastScrollModifier": "Schnellscroll-Modifikator",
"selectModifier": "Modifikator auswählen",
"modifierAlt": "Alt",
"modifierCtrl": "Strg",
"modifierShift": "Umschalt",
"fastScrollModifierDesc": "Modifikatortaste für schnelles Scrollen",
"fastScrollSensitivity": "Schnellscroll-Empfindlichkeit",
"fastScrollSensitivityValue": "Schnellscroll-Empfindlichkeit: {{value}}",
"fastScrollSensitivityDesc": "Scroll-Geschwindigkeitsmultiplikator bei gedrücktem Modifikator",
"minimumContrastRatio": "Minimales Kontrastverhältnis",
"minimumContrastRatioValue": "Minimales Kontrastverhältnis: {{value}}",
"minimumContrastRatioDesc": "Farben automatisch für bessere Lesbarkeit anpassen",
"sshAgentForwarding": "SSH-Agent-Weiterleitung",
"sshAgentForwardingDesc": "SSH-Authentifizierungsagent an Remote-Host weiterleiten",
"backspaceMode": "Rücktaste-Modus",
"selectBackspaceMode": "Rücktaste-Modus auswählen",
"backspaceModeNormal": "Normal (DEL)",
"backspaceModeControlH": "Control-H (^H)",
"backspaceModeDesc": "Rücktasten-Verhalten für Kompatibilität",
"startupSnippet": "Start-Snippet",
"selectSnippet": "Snippet auswählen",
"searchSnippets": "Snippets durchsuchen...",
"snippetNone": "Keine",
"noneAuthTitle": "Keyboard-Interactive-Authentifizierung",
"noneAuthDescription": "Diese Authentifizierungsmethode verwendet beim Herstellen der Verbindung zum SSH-Server die Keyboard-Interactive-Authentifizierung.",
"noneAuthDetails": "Keyboard-Interactive-Authentifizierung ermöglicht dem Server, Sie während der Verbindung zur Eingabe von Anmeldeinformationen aufzufordern. Dies ist nützlich für Server, die eine Multi-Faktor-Authentifizierung oder eine dynamische Passworteingabe erfordern.",
@@ -1119,7 +1182,14 @@
"noInterfacesFound": "Keine Netzwerkschnittstellen gefunden",
"totalProcesses": "Gesamtprozesse",
"running": "läuft",
"noProcessesFound": "Keine Prozesse gefunden"
"noProcessesFound": "Keine Prozesse gefunden",
"loginStats": "SSH-Anmeldestatistiken",
"totalLogins": "Gesamtanmeldungen",
"uniqueIPs": "Eindeutige IPs",
"recentSuccessfulLogins": "Letzte erfolgreiche Anmeldungen",
"recentFailedAttempts": "Letzte fehlgeschlagene Versuche",
"noRecentLoginData": "Keine aktuellen Anmeldedaten",
"from": "von"
},
"auth": {
"loginTitle": "Melden Sie sich bei Termix an",

View File

@@ -778,6 +778,69 @@
"statusMonitoring": "Status",
"metricsMonitoring": "Metrics",
"terminalCustomizationNotice": "Note: Terminal customizations only work on desktop (website and Electron app). Mobile apps and mobile website use system default terminal settings.",
"terminalCustomization": "Terminal Customization",
"appearance": "Appearance",
"behavior": "Behavior",
"advanced": "Advanced",
"themePreview": "Theme Preview",
"theme": "Theme",
"selectTheme": "Select theme",
"chooseColorTheme": "Choose a color theme for the terminal",
"fontFamily": "Font Family",
"selectFont": "Select font",
"selectFontDesc": "Select the font to use in the terminal",
"fontSize": "Font Size",
"fontSizeValue": "Font Size: {{value}}px",
"adjustFontSize": "Adjust the terminal font size",
"letterSpacing": "Letter Spacing",
"letterSpacingValue": "Letter Spacing: {{value}}px",
"adjustLetterSpacing": "Adjust spacing between characters",
"lineHeight": "Line Height",
"lineHeightValue": "Line Height: {{value}}",
"adjustLineHeight": "Adjust spacing between lines",
"cursorStyle": "Cursor Style",
"selectCursorStyle": "Select cursor style",
"cursorStyleBlock": "Block",
"cursorStyleUnderline": "Underline",
"cursorStyleBar": "Bar",
"chooseCursorAppearance": "Choose the cursor appearance",
"cursorBlink": "Cursor Blink",
"enableCursorBlink": "Enable cursor blinking animation",
"scrollbackBuffer": "Scrollback Buffer",
"scrollbackBufferValue": "Scrollback Buffer: {{value}} lines",
"scrollbackBufferDesc": "Number of lines to keep in scrollback history",
"bellStyle": "Bell Style",
"selectBellStyle": "Select bell style",
"bellStyleNone": "None",
"bellStyleSound": "Sound",
"bellStyleVisual": "Visual",
"bellStyleBoth": "Both",
"bellStyleDesc": "How to handle terminal bell (BEL character, \\x07). Programs trigger this when completing tasks, encountering errors, or for notifications. \"Sound\" plays an audio beep, \"Visual\" flashes the screen briefly, \"Both\" does both, \"None\" disables bell alerts.",
"rightClickSelectsWord": "Right Click Selects Word",
"rightClickSelectsWordDesc": "Right-clicking selects the word under cursor",
"fastScrollModifier": "Fast Scroll Modifier",
"selectModifier": "Select modifier",
"modifierAlt": "Alt",
"modifierCtrl": "Ctrl",
"modifierShift": "Shift",
"fastScrollModifierDesc": "Modifier key for fast scrolling",
"fastScrollSensitivity": "Fast Scroll Sensitivity",
"fastScrollSensitivityValue": "Fast Scroll Sensitivity: {{value}}",
"fastScrollSensitivityDesc": "Scroll speed multiplier when modifier is held",
"minimumContrastRatio": "Minimum Contrast Ratio",
"minimumContrastRatioValue": "Minimum Contrast Ratio: {{value}}",
"minimumContrastRatioDesc": "Automatically adjust colors for better readability",
"sshAgentForwarding": "SSH Agent Forwarding",
"sshAgentForwardingDesc": "Forward SSH authentication agent to remote host",
"backspaceMode": "Backspace Mode",
"selectBackspaceMode": "Select backspace mode",
"backspaceModeNormal": "Normal (DEL)",
"backspaceModeControlH": "Control-H (^H)",
"backspaceModeDesc": "Backspace key behavior for compatibility",
"startupSnippet": "Startup Snippet",
"selectSnippet": "Select snippet",
"searchSnippets": "Search snippets...",
"snippetNone": "None",
"noneAuthTitle": "Keyboard-Interactive Authentication",
"noneAuthDescription": "This authentication method will use keyboard-interactive authentication when connecting to the SSH server.",
"noneAuthDetails": "Keyboard-interactive authentication allows the server to prompt you for credentials during connection. This is useful for servers that require multi-factor authentication or if you do not want to save credentials locally.",
@@ -1228,7 +1291,14 @@
"noInterfacesFound": "No network interfaces found",
"totalProcesses": "Total Processes",
"running": "Running",
"noProcessesFound": "No processes found"
"noProcessesFound": "No processes found",
"loginStats": "SSH Login Statistics",
"totalLogins": "Total Logins",
"uniqueIPs": "Unique IPs",
"recentSuccessfulLogins": "Recent Successful Logins",
"recentFailedAttempts": "Recent Failed Attempts",
"noRecentLoginData": "No recent login data",
"from": "from"
},
"auth": {
"loginTitle": "Login to Termix",

View File

@@ -766,6 +766,69 @@
"statusMonitoring": "Статус",
"metricsMonitoring": "Метрики",
"terminalCustomizationNotice": "Примечание: Настройки терминала работают только на рабочем столе (веб-сайт и Electron-приложение). Мобильные приложения и мобильный веб-сайт используют системные настройки терминала по умолчанию.",
"terminalCustomization": "Настройка терминала",
"appearance": "Внешний вид",
"behavior": "Поведение",
"advanced": "Расширенные",
"themePreview": "Предпросмотр темы",
"theme": "Тема",
"selectTheme": "Выбрать тему",
"chooseColorTheme": "Выберите цветовую тему для терминала",
"fontFamily": "Семейство шрифтов",
"selectFont": "Выбрать шрифт",
"selectFontDesc": "Выберите шрифт для использования в терминале",
"fontSize": "Размер шрифта",
"fontSizeValue": "Размер шрифта: {{value}}px",
"adjustFontSize": "Настроить размер шрифта терминала",
"letterSpacing": "Межбуквенный интервал",
"letterSpacingValue": "Межбуквенный интервал: {{value}}px",
"adjustLetterSpacing": "Настроить расстояние между символами",
"lineHeight": "Высота строки",
"lineHeightValue": "Высота строки: {{value}}",
"adjustLineHeight": "Настроить расстояние между строками",
"cursorStyle": "Стиль курсора",
"selectCursorStyle": "Выбрать стиль курсора",
"cursorStyleBlock": "Блок",
"cursorStyleUnderline": "Подчеркивание",
"cursorStyleBar": "Полоса",
"chooseCursorAppearance": "Выбрать внешний вид курсора",
"cursorBlink": "Мигание курсора",
"enableCursorBlink": "Включить анимацию мигания курсора",
"scrollbackBuffer": "Буфер прокрутки",
"scrollbackBufferValue": "Буфер прокрутки: {{value}} строк",
"scrollbackBufferDesc": "Количество строк для хранения в истории прокрутки",
"bellStyle": "Стиль звонка",
"selectBellStyle": "Выбрать стиль звонка",
"bellStyleNone": "Нет",
"bellStyleSound": "Звук",
"bellStyleVisual": "Визуальный",
"bellStyleBoth": "Оба",
"bellStyleDesc": "Как обрабатывать звонок терминала (символ BEL, \\x07). Программы вызывают его при завершении задач, возникновении ошибок или для уведомлений. \"Звук\" воспроизводит звуковой сигнал, \"Визуальный\" кратковременно мигает экран, \"Оба\" делает и то, и другое, \"Нет\" отключает звуковые оповещения.",
"rightClickSelectsWord": "Правый клик выбирает слово",
"rightClickSelectsWordDesc": "Правый клик выбирает слово под курсором",
"fastScrollModifier": "Модификатор быстрой прокрутки",
"selectModifier": "Выбрать модификатор",
"modifierAlt": "Alt",
"modifierCtrl": "Ctrl",
"modifierShift": "Shift",
"fastScrollModifierDesc": "Клавиша-модификатор для быстрой прокрутки",
"fastScrollSensitivity": "Чувствительность быстрой прокрутки",
"fastScrollSensitivityValue": "Чувствительность быстрой прокрутки: {{value}}",
"fastScrollSensitivityDesc": "Множитель скорости прокрутки при удержании модификатора",
"minimumContrastRatio": "Минимальная контрастность",
"minimumContrastRatioValue": "Минимальная контрастность: {{value}}",
"minimumContrastRatioDesc": "Автоматически настраивать цвета для лучшей читаемости",
"sshAgentForwarding": "Переадресация SSH-агента",
"sshAgentForwardingDesc": "Переадресовать агент SSH-аутентификации на удаленный хост",
"backspaceMode": "Режим Backspace",
"selectBackspaceMode": "Выбрать режим Backspace",
"backspaceModeNormal": "Обычный (DEL)",
"backspaceModeControlH": "Control-H (^H)",
"backspaceModeDesc": "Поведение клавиши Backspace для совместимости",
"startupSnippet": "Сниппет запуска",
"selectSnippet": "Выбрать сниппет",
"searchSnippets": "Поиск сниппетов...",
"snippetNone": "Нет",
"noneAuthTitle": "Интерактивная аутентификация по клавиатуре",
"noneAuthDescription": "Этот метод аутентификации будет использовать интерактивную аутентификацию по клавиатуре при подключении к SSH-серверу.",
"noneAuthDetails": "Интерактивная аутентификация по клавиатуре позволяет серверу запрашивать у вас учетные данные во время подключения. Это полезно для серверов, которые требуют многофакторную аутентификацию или динамический ввод пароля."
@@ -1215,7 +1278,14 @@
"noInterfacesFound": "Сетевые интерфейсы не найдены",
"totalProcesses": "Всего процессов",
"running": "Запущено",
"noProcessesFound": "Процессы не найдены"
"noProcessesFound": "Процессы не найдены",
"loginStats": "Статистика входов SSH",
"totalLogins": "Всего входов",
"uniqueIPs": "Уникальные IP",
"recentSuccessfulLogins": "Последние успешные входы",
"recentFailedAttempts": "Последние неудачные попытки",
"noRecentLoginData": "Нет данных о недавних входах",
"from": "с"
},
"auth": {
"loginTitle": "Вход в Termix",

View File

@@ -790,6 +790,69 @@
"statusMonitoring": "状态",
"metricsMonitoring": "指标",
"terminalCustomizationNotice": "注意:终端自定义仅在桌面网站版本中有效。移动和 Electron 应用程序使用系统默认终端设置。",
"terminalCustomization": "终端自定义",
"appearance": "外观",
"behavior": "行为",
"advanced": "高级",
"themePreview": "主题预览",
"theme": "主题",
"selectTheme": "选择主题",
"chooseColorTheme": "选择终端的颜色主题",
"fontFamily": "字体系列",
"selectFont": "选择字体",
"selectFontDesc": "选择终端使用的字体",
"fontSize": "字体大小",
"fontSizeValue": "字体大小:{{value}}px",
"adjustFontSize": "调整终端字体大小",
"letterSpacing": "字母间距",
"letterSpacingValue": "字母间距:{{value}}px",
"adjustLetterSpacing": "调整字符之间的间距",
"lineHeight": "行高",
"lineHeightValue": "行高:{{value}}",
"adjustLineHeight": "调整行之间的间距",
"cursorStyle": "光标样式",
"selectCursorStyle": "选择光标样式",
"cursorStyleBlock": "块状",
"cursorStyleUnderline": "下划线",
"cursorStyleBar": "竖线",
"chooseCursorAppearance": "选择光标外观",
"cursorBlink": "光标闪烁",
"enableCursorBlink": "启用光标闪烁动画",
"scrollbackBuffer": "回滚缓冲区",
"scrollbackBufferValue": "回滚缓冲区:{{value}} 行",
"scrollbackBufferDesc": "保留在回滚历史记录中的行数",
"bellStyle": "铃声样式",
"selectBellStyle": "选择铃声样式",
"bellStyleNone": "无",
"bellStyleSound": "声音",
"bellStyleVisual": "视觉",
"bellStyleBoth": "两者",
"bellStyleDesc": "如何处理终端铃声BEL字符\\x07。程序在完成任务、遇到错误或通知时会触发此功能。\"声音\"播放音频提示音,\"视觉\"短暂闪烁屏幕,\"两者\"同时执行,\"无\"禁用铃声提醒。",
"rightClickSelectsWord": "右键选择单词",
"rightClickSelectsWordDesc": "右键单击选择光标下的单词",
"fastScrollModifier": "快速滚动修饰键",
"selectModifier": "选择修饰键",
"modifierAlt": "Alt",
"modifierCtrl": "Ctrl",
"modifierShift": "Shift",
"fastScrollModifierDesc": "快速滚动的修饰键",
"fastScrollSensitivity": "快速滚动灵敏度",
"fastScrollSensitivityValue": "快速滚动灵敏度:{{value}}",
"fastScrollSensitivityDesc": "按住修饰键时的滚动速度倍数",
"minimumContrastRatio": "最小对比度",
"minimumContrastRatioValue": "最小对比度:{{value}}",
"minimumContrastRatioDesc": "自动调整颜色以获得更好的可读性",
"sshAgentForwarding": "SSH 代理转发",
"sshAgentForwardingDesc": "将 SSH 身份验证代理转发到远程主机",
"backspaceMode": "退格模式",
"selectBackspaceMode": "选择退格模式",
"backspaceModeNormal": "正常 (DEL)",
"backspaceModeControlH": "Control-H (^H)",
"backspaceModeDesc": "退格键行为兼容性",
"startupSnippet": "启动代码片段",
"selectSnippet": "选择代码片段",
"searchSnippets": "搜索代码片段...",
"snippetNone": "无",
"noneAuthTitle": "键盘交互式认证",
"noneAuthDescription": "此认证方法在连接到 SSH 服务器时将使用键盘交互式认证。",
"noneAuthDetails": "键盘交互式认证允许服务器在连接期间提示您输入凭据。这对于需要多因素认证或动态密码输入的服务器很有用。",
@@ -1199,7 +1262,14 @@
"noInterfacesFound": "未找到网络接口",
"totalProcesses": "总进程数",
"running": "运行中",
"noProcessesFound": "未找到进程"
"noProcessesFound": "未找到进程",
"loginStats": "SSH 登录统计",
"totalLogins": "总登录次数",
"uniqueIPs": "唯一 IP 数",
"recentSuccessfulLogins": "最近成功登录",
"recentFailedAttempts": "最近失败尝试",
"noRecentLoginData": "无最近登录数据",
"from": "来自"
},
"auth": {
"loginTitle": "登录 Termix",

View File

@@ -1363,15 +1363,15 @@ export function HostManagerEditor({
</AlertDescription>
</Alert>
<h1 className="text-xl font-semibold mt-7">
Terminal Customization
{t("hosts.terminalCustomization")}
</h1>
<Accordion type="multiple" className="w-full">
<AccordionItem value="appearance">
<AccordionTrigger>Appearance</AccordionTrigger>
<AccordionTrigger>{t("hosts.appearance")}</AccordionTrigger>
<AccordionContent className="space-y-4 pt-4">
<div className="space-y-2">
<label className="text-sm font-medium">
Theme Preview
{t("hosts.themePreview")}
</label>
<TerminalPreview
theme={form.watch("terminalConfig.theme")}
@@ -1395,14 +1395,14 @@ export function HostManagerEditor({
name="terminalConfig.theme"
render={({ field }) => (
<FormItem>
<FormLabel>Theme</FormLabel>
<FormLabel>{t("hosts.theme")}</FormLabel>
<Select
onValueChange={field.onChange}
value={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select theme" />
<SelectValue placeholder={t("hosts.selectTheme")} />
</SelectTrigger>
</FormControl>
<SelectContent>
@@ -1416,7 +1416,7 @@ export function HostManagerEditor({
</SelectContent>
</Select>
<FormDescription>
Choose a color theme for the terminal
{t("hosts.chooseColorTheme")}
</FormDescription>
</FormItem>
)}
@@ -1427,14 +1427,14 @@ export function HostManagerEditor({
name="terminalConfig.fontFamily"
render={({ field }) => (
<FormItem>
<FormLabel>Font Family</FormLabel>
<FormLabel>{t("hosts.fontFamily")}</FormLabel>
<Select
onValueChange={field.onChange}
value={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select font" />
<SelectValue placeholder={t("hosts.selectFont")} />
</SelectTrigger>
</FormControl>
<SelectContent>
@@ -1449,7 +1449,7 @@ export function HostManagerEditor({
</SelectContent>
</Select>
<FormDescription>
Select the font to use in the terminal
{t("hosts.selectFontDesc")}
</FormDescription>
</FormItem>
)}
@@ -1460,7 +1460,7 @@ export function HostManagerEditor({
name="terminalConfig.fontSize"
render={({ field }) => (
<FormItem>
<FormLabel>Font Size: {field.value}px</FormLabel>
<FormLabel>{t("hosts.fontSizeValue", { value: field.value })}</FormLabel>
<FormControl>
<Slider
min={8}
@@ -1473,7 +1473,7 @@ export function HostManagerEditor({
/>
</FormControl>
<FormDescription>
Adjust the terminal font size
{t("hosts.adjustFontSize")}
</FormDescription>
</FormItem>
)}
@@ -1485,7 +1485,7 @@ export function HostManagerEditor({
render={({ field }) => (
<FormItem>
<FormLabel>
Letter Spacing: {field.value}px
{t("hosts.letterSpacingValue", { value: field.value })}
</FormLabel>
<FormControl>
<Slider
@@ -1499,7 +1499,7 @@ export function HostManagerEditor({
/>
</FormControl>
<FormDescription>
Adjust spacing between characters
{t("hosts.adjustLetterSpacing")}
</FormDescription>
</FormItem>
)}
@@ -1510,7 +1510,7 @@ export function HostManagerEditor({
name="terminalConfig.lineHeight"
render={({ field }) => (
<FormItem>
<FormLabel>Line Height: {field.value}</FormLabel>
<FormLabel>{t("hosts.lineHeightValue", { value: field.value })}</FormLabel>
<FormControl>
<Slider
min={1}
@@ -1523,7 +1523,7 @@ export function HostManagerEditor({
/>
</FormControl>
<FormDescription>
Adjust spacing between lines
{t("hosts.adjustLineHeight")}
</FormDescription>
</FormItem>
)}
@@ -1534,26 +1534,26 @@ export function HostManagerEditor({
name="terminalConfig.cursorStyle"
render={({ field }) => (
<FormItem>
<FormLabel>Cursor Style</FormLabel>
<FormLabel>{t("hosts.cursorStyle")}</FormLabel>
<Select
onValueChange={field.onChange}
value={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select cursor style" />
<SelectValue placeholder={t("hosts.selectCursorStyle")} />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="block">Block</SelectItem>
<SelectItem value="block">{t("hosts.cursorStyleBlock")}</SelectItem>
<SelectItem value="underline">
Underline
{t("hosts.cursorStyleUnderline")}
</SelectItem>
<SelectItem value="bar">Bar</SelectItem>
<SelectItem value="bar">{t("hosts.cursorStyleBar")}</SelectItem>
</SelectContent>
</Select>
<FormDescription>
Choose the cursor appearance
{t("hosts.chooseCursorAppearance")}
</FormDescription>
</FormItem>
)}
@@ -1565,9 +1565,9 @@ export function HostManagerEditor({
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-3">
<div className="space-y-0.5">
<FormLabel>Cursor Blink</FormLabel>
<FormLabel>{t("hosts.cursorBlink")}</FormLabel>
<FormDescription>
Enable cursor blinking animation
{t("hosts.enableCursorBlink")}
</FormDescription>
</div>
<FormControl>
@@ -1583,7 +1583,7 @@ export function HostManagerEditor({
</AccordionItem>
<AccordionItem value="behavior">
<AccordionTrigger>Behavior</AccordionTrigger>
<AccordionTrigger>{t("hosts.behavior")}</AccordionTrigger>
<AccordionContent className="space-y-4 pt-4">
<FormField
control={form.control}
@@ -1591,7 +1591,7 @@ export function HostManagerEditor({
render={({ field }) => (
<FormItem>
<FormLabel>
Scrollback Buffer: {field.value} lines
{t("hosts.scrollbackBufferValue", { value: field.value })}
</FormLabel>
<FormControl>
<Slider
@@ -1605,7 +1605,7 @@ export function HostManagerEditor({
/>
</FormControl>
<FormDescription>
Number of lines to keep in scrollback history
{t("hosts.scrollbackBufferDesc")}
</FormDescription>
</FormItem>
)}
@@ -1616,30 +1616,25 @@ export function HostManagerEditor({
name="terminalConfig.bellStyle"
render={({ field }) => (
<FormItem>
<FormLabel>Bell Style</FormLabel>
<FormLabel>{t("hosts.bellStyle")}</FormLabel>
<Select
onValueChange={field.onChange}
value={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select bell style" />
<SelectValue placeholder={t("hosts.selectBellStyle")} />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="none">None</SelectItem>
<SelectItem value="sound">Sound</SelectItem>
<SelectItem value="visual">Visual</SelectItem>
<SelectItem value="both">Both</SelectItem>
<SelectItem value="none">{t("hosts.bellStyleNone")}</SelectItem>
<SelectItem value="sound">{t("hosts.bellStyleSound")}</SelectItem>
<SelectItem value="visual">{t("hosts.bellStyleVisual")}</SelectItem>
<SelectItem value="both">{t("hosts.bellStyleBoth")}</SelectItem>
</SelectContent>
</Select>
<FormDescription>
How to handle terminal bell (BEL character,
\x07). Programs trigger this when completing
tasks, encountering errors, or for
notifications. "Sound" plays an audio beep,
"Visual" flashes the screen briefly, "Both" does
both, "None" disables bell alerts.
{t("hosts.bellStyleDesc")}
</FormDescription>
</FormItem>
)}
@@ -1651,9 +1646,9 @@ export function HostManagerEditor({
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-3">
<div className="space-y-0.5">
<FormLabel>Right Click Selects Word</FormLabel>
<FormLabel>{t("hosts.rightClickSelectsWord")}</FormLabel>
<FormDescription>
Right-clicking selects the word under cursor
{t("hosts.rightClickSelectsWordDesc")}
</FormDescription>
</div>
<FormControl>
@@ -1671,24 +1666,24 @@ export function HostManagerEditor({
name="terminalConfig.fastScrollModifier"
render={({ field }) => (
<FormItem>
<FormLabel>Fast Scroll Modifier</FormLabel>
<FormLabel>{t("hosts.fastScrollModifier")}</FormLabel>
<Select
onValueChange={field.onChange}
value={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select modifier" />
<SelectValue placeholder={t("hosts.selectModifier")} />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="alt">Alt</SelectItem>
<SelectItem value="ctrl">Ctrl</SelectItem>
<SelectItem value="shift">Shift</SelectItem>
<SelectItem value="alt">{t("hosts.modifierAlt")}</SelectItem>
<SelectItem value="ctrl">{t("hosts.modifierCtrl")}</SelectItem>
<SelectItem value="shift">{t("hosts.modifierShift")}</SelectItem>
</SelectContent>
</Select>
<FormDescription>
Modifier key for fast scrolling
{t("hosts.fastScrollModifierDesc")}
</FormDescription>
</FormItem>
)}
@@ -1700,7 +1695,7 @@ export function HostManagerEditor({
render={({ field }) => (
<FormItem>
<FormLabel>
Fast Scroll Sensitivity: {field.value}
{t("hosts.fastScrollSensitivityValue", { value: field.value })}
</FormLabel>
<FormControl>
<Slider
@@ -1714,7 +1709,7 @@ export function HostManagerEditor({
/>
</FormControl>
<FormDescription>
Scroll speed multiplier when modifier is held
{t("hosts.fastScrollSensitivityDesc")}
</FormDescription>
</FormItem>
)}
@@ -1726,7 +1721,7 @@ export function HostManagerEditor({
render={({ field }) => (
<FormItem>
<FormLabel>
Minimum Contrast Ratio: {field.value}
{t("hosts.minimumContrastRatioValue", { value: field.value })}
</FormLabel>
<FormControl>
<Slider
@@ -1740,8 +1735,7 @@ export function HostManagerEditor({
/>
</FormControl>
<FormDescription>
Automatically adjust colors for better
readability
{t("hosts.minimumContrastRatioDesc")}
</FormDescription>
</FormItem>
)}
@@ -1750,7 +1744,7 @@ export function HostManagerEditor({
</AccordionItem>
<AccordionItem value="advanced">
<AccordionTrigger>Advanced</AccordionTrigger>
<AccordionTrigger>{t("hosts.advanced")}</AccordionTrigger>
<AccordionContent className="space-y-4 pt-4">
<FormField
control={form.control}
@@ -1758,10 +1752,9 @@ export function HostManagerEditor({
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-3">
<div className="space-y-0.5">
<FormLabel>SSH Agent Forwarding</FormLabel>
<FormLabel>{t("hosts.sshAgentForwarding")}</FormLabel>
<FormDescription>
Forward SSH authentication agent to remote
host
{t("hosts.sshAgentForwardingDesc")}
</FormDescription>
</div>
<FormControl>
@@ -1779,27 +1772,27 @@ export function HostManagerEditor({
name="terminalConfig.backspaceMode"
render={({ field }) => (
<FormItem>
<FormLabel>Backspace Mode</FormLabel>
<FormLabel>{t("hosts.backspaceMode")}</FormLabel>
<Select
onValueChange={field.onChange}
value={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select backspace mode" />
<SelectValue placeholder={t("hosts.selectBackspaceMode")} />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="normal">
Normal (DEL)
{t("hosts.backspaceModeNormal")}
</SelectItem>
<SelectItem value="control-h">
Control-H (^H)
{t("hosts.backspaceModeControlH")}
</SelectItem>
</SelectContent>
</Select>
<FormDescription>
Backspace key behavior for compatibility
{t("hosts.backspaceModeDesc")}
</FormDescription>
</FormItem>
)}
@@ -1810,7 +1803,7 @@ export function HostManagerEditor({
name="terminalConfig.startupSnippetId"
render={({ field }) => (
<FormItem>
<FormLabel>Startup Snippet</FormLabel>
<FormLabel>{t("hosts.startupSnippet")}</FormLabel>
<Select
onValueChange={(value) => {
field.onChange(
@@ -1822,13 +1815,13 @@ export function HostManagerEditor({
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select snippet" />
<SelectValue placeholder={t("hosts.selectSnippet")} />
</SelectTrigger>
</FormControl>
<SelectContent>
<div className="px-2 pb-2 sticky top-0 bg-popover z-10">
<Input
placeholder="Search snippets..."
placeholder={t("hosts.searchSnippets")}
value={snippetSearch}
onChange={(e) =>
setSnippetSearch(e.target.value)
@@ -1839,7 +1832,7 @@ export function HostManagerEditor({
/>
</div>
<div className="max-h-[200px] overflow-y-auto">
<SelectItem value="none">None</SelectItem>
<SelectItem value="none">{t("hosts.snippetNone")}</SelectItem>
{snippets
.filter((snippet) =>
snippet.name
@@ -2662,7 +2655,7 @@ export function HostManagerEditor({
{widget === "system" &&
t("serverStats.systemInfo")}
{widget === "login_stats" &&
"SSH Login Statistics"}
t("serverStats.loginStats")}
</label>
</div>
))}

View File

@@ -41,7 +41,7 @@ export function LoginStatsWidget({
<div className="flex items-center gap-2 flex-shrink-0 mb-3">
<UserCheck className="h-5 w-5 text-green-400" />
<h3 className="font-semibold text-lg text-white">
SSH Login Statistics
{t("serverStats.loginStats")}
</h3>
</div>
@@ -50,14 +50,14 @@ export function LoginStatsWidget({
<div className="bg-dark-bg-darker p-2 rounded border border-dark-border/30">
<div className="flex items-center gap-1 text-xs text-gray-400 mb-1">
<Activity className="h-3 w-3" />
<span>Total Logins</span>
<span>{t("serverStats.totalLogins")}</span>
</div>
<div className="text-xl font-bold text-green-400">{totalLogins}</div>
</div>
<div className="bg-dark-bg-darker p-2 rounded border border-dark-border/30">
<div className="flex items-center gap-1 text-xs text-gray-400 mb-1">
<MapPin className="h-3 w-3" />
<span>Unique IPs</span>
<span>{t("serverStats.uniqueIPs")}</span>
</div>
<div className="text-xl font-bold text-blue-400">{uniqueIPs}</div>
</div>
@@ -68,12 +68,12 @@ export function LoginStatsWidget({
<div className="flex items-center gap-2 mb-1">
<UserCheck className="h-4 w-4 text-green-400" />
<span className="text-sm font-semibold text-gray-300">
Recent Successful Logins
{t("serverStats.recentSuccessfulLogins")}
</span>
</div>
{recentLogins.length === 0 ? (
<div className="text-xs text-gray-500 italic p-2">
No recent login data
{t("serverStats.noRecentLoginData")}
</div>
) : (
<div className="space-y-1">
@@ -86,7 +86,7 @@ export function LoginStatsWidget({
<span className="text-green-400 font-mono truncate">
{login.user}
</span>
<span className="text-gray-500">from</span>
<span className="text-gray-500">{t("serverStats.from")}</span>
<span className="text-blue-400 font-mono truncate">
{login.ip}
</span>
@@ -105,7 +105,7 @@ export function LoginStatsWidget({
<div className="flex items-center gap-2 mb-1">
<UserX className="h-4 w-4 text-red-400" />
<span className="text-sm font-semibold text-gray-300">
Recent Failed Attempts
{t("serverStats.recentFailedAttempts")}
</span>
</div>
<div className="space-y-1">
@@ -118,7 +118,7 @@ export function LoginStatsWidget({
<span className="text-red-400 font-mono truncate">
{login.user}
</span>
<span className="text-gray-500">from</span>
<span className="text-gray-500">{t("serverStats.from")}</span>
<span className="text-blue-400 font-mono truncate">
{login.ip}
</span>