mirror of
https://github.com/veeso/termscp.git
synced 2026-09-17 17:45:54 -07:00
refactor(site): english-only, remove i18n machinery
This commit is contained in:
@@ -5,11 +5,6 @@ import sitemap from "@astrojs/sitemap";
|
||||
|
||||
export default defineConfig({
|
||||
site: "https://termscp.rs",
|
||||
i18n: {
|
||||
defaultLocale: "en",
|
||||
locales: ["en", "zh-CN", "it", "fr", "es"],
|
||||
routing: { prefixDefaultLocale: false },
|
||||
},
|
||||
integrations: [sitemap()],
|
||||
vite: { plugins: [tailwindcss()] },
|
||||
});
|
||||
|
||||
Generated
+26
-377
@@ -14,8 +14,7 @@
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.0.0",
|
||||
"tailwindcss": "^4.0.0",
|
||||
"typescript": "^5.6.0",
|
||||
"vitest": "^2.1.0"
|
||||
"typescript": "^5.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@astrojs/compiler": {
|
||||
@@ -2027,119 +2026,6 @@
|
||||
"integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@vitest/expect": {
|
||||
"version": "2.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz",
|
||||
"integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/spy": "2.1.9",
|
||||
"@vitest/utils": "2.1.9",
|
||||
"chai": "^5.1.2",
|
||||
"tinyrainbow": "^1.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/mocker": {
|
||||
"version": "2.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz",
|
||||
"integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/spy": "2.1.9",
|
||||
"estree-walker": "^3.0.3",
|
||||
"magic-string": "^0.30.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"msw": "^2.4.9",
|
||||
"vite": "^5.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"msw": {
|
||||
"optional": true
|
||||
},
|
||||
"vite": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/pretty-format": {
|
||||
"version": "2.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz",
|
||||
"integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tinyrainbow": "^1.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/runner": {
|
||||
"version": "2.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz",
|
||||
"integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/utils": "2.1.9",
|
||||
"pathe": "^1.1.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/snapshot": {
|
||||
"version": "2.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz",
|
||||
"integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "2.1.9",
|
||||
"magic-string": "^0.30.12",
|
||||
"pathe": "^1.1.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/spy": {
|
||||
"version": "2.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz",
|
||||
"integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tinyspy": "^3.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/utils": {
|
||||
"version": "2.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz",
|
||||
"integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "2.1.9",
|
||||
"loupe": "^3.1.2",
|
||||
"tinyrainbow": "^1.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.16.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
|
||||
@@ -2282,16 +2168,6 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/assertion-error": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
|
||||
"integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/astro": {
|
||||
"version": "5.18.2",
|
||||
"resolved": "https://registry.npmjs.org/astro/-/astro-5.18.2.tgz",
|
||||
@@ -2980,16 +2856,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/cac": {
|
||||
"version": "6.7.14",
|
||||
"resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
|
||||
"integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/camelcase": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz",
|
||||
@@ -3012,23 +2878,6 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/chai": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz",
|
||||
"integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"assertion-error": "^2.0.1",
|
||||
"check-error": "^2.1.1",
|
||||
"deep-eql": "^5.0.1",
|
||||
"loupe": "^3.1.0",
|
||||
"pathval": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "5.6.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
|
||||
@@ -3071,16 +2920,6 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/check-error": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz",
|
||||
"integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz",
|
||||
@@ -3301,16 +3140,6 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/deep-eql": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
|
||||
"integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/defu": {
|
||||
"version": "6.1.7",
|
||||
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz",
|
||||
@@ -3570,16 +3399,6 @@
|
||||
"integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/expect-type": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
|
||||
"integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/extend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
@@ -4289,13 +4108,6 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/loupe": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz",
|
||||
"integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "11.5.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz",
|
||||
@@ -5330,23 +5142,6 @@
|
||||
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/pathe": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
|
||||
"integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pathval": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz",
|
||||
"integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 14.16"
|
||||
}
|
||||
},
|
||||
"node_modules/piccolore": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz",
|
||||
@@ -5803,13 +5598,6 @@
|
||||
"@types/hast": "^3.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/siginfo": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
|
||||
"integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/sisteransi": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
|
||||
@@ -5866,20 +5654,6 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/stackback": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
|
||||
"integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/std-env": {
|
||||
"version": "3.10.0",
|
||||
"resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz",
|
||||
"integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/stream-replace-string": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz",
|
||||
@@ -5984,13 +5758,6 @@
|
||||
"integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinybench": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
|
||||
"integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinyexec": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz",
|
||||
@@ -6016,36 +5783,6 @@
|
||||
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||
}
|
||||
},
|
||||
"node_modules/tinypool": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz",
|
||||
"integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.0.0 || >=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyrainbow": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz",
|
||||
"integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyspy": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz",
|
||||
"integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/trim-lines": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
|
||||
@@ -6439,6 +6176,7 @@
|
||||
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.21.3",
|
||||
"postcss": "^8.4.43",
|
||||
@@ -6493,29 +6231,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vite-node": {
|
||||
"version": "2.1.9",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz",
|
||||
"integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cac": "^6.7.14",
|
||||
"debug": "^4.3.7",
|
||||
"es-module-lexer": "^1.5.4",
|
||||
"pathe": "^1.1.2",
|
||||
"vite": "^5.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"vite-node": "vite-node.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.0.0 || >=20.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
|
||||
@@ -6528,6 +6243,7 @@
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -6544,6 +6260,7 @@
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -6560,6 +6277,7 @@
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -6576,6 +6294,7 @@
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -6592,6 +6311,7 @@
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -6608,6 +6328,7 @@
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -6624,6 +6345,7 @@
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -6640,6 +6362,7 @@
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -6656,6 +6379,7 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -6672,6 +6396,7 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -6688,6 +6413,7 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -6704,6 +6430,7 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -6720,6 +6447,7 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -6736,6 +6464,7 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -6752,6 +6481,7 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -6768,6 +6498,7 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -6784,6 +6515,7 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -6800,6 +6532,7 @@
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -6816,6 +6549,7 @@
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -6832,6 +6566,7 @@
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -6848,6 +6583,7 @@
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -6864,6 +6600,7 @@
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -6880,6 +6617,7 @@
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -6891,6 +6629,7 @@
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
@@ -6942,79 +6681,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vitest": {
|
||||
"version": "2.1.9",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz",
|
||||
"integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/expect": "2.1.9",
|
||||
"@vitest/mocker": "2.1.9",
|
||||
"@vitest/pretty-format": "^2.1.9",
|
||||
"@vitest/runner": "2.1.9",
|
||||
"@vitest/snapshot": "2.1.9",
|
||||
"@vitest/spy": "2.1.9",
|
||||
"@vitest/utils": "2.1.9",
|
||||
"chai": "^5.1.2",
|
||||
"debug": "^4.3.7",
|
||||
"expect-type": "^1.1.0",
|
||||
"magic-string": "^0.30.12",
|
||||
"pathe": "^1.1.2",
|
||||
"std-env": "^3.8.0",
|
||||
"tinybench": "^2.9.0",
|
||||
"tinyexec": "^0.3.1",
|
||||
"tinypool": "^1.0.1",
|
||||
"tinyrainbow": "^1.2.0",
|
||||
"vite": "^5.0.0",
|
||||
"vite-node": "2.1.9",
|
||||
"why-is-node-running": "^2.3.0"
|
||||
},
|
||||
"bin": {
|
||||
"vitest": "vitest.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.0.0 || >=20.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@edge-runtime/vm": "*",
|
||||
"@types/node": "^18.0.0 || >=20.0.0",
|
||||
"@vitest/browser": "2.1.9",
|
||||
"@vitest/ui": "2.1.9",
|
||||
"happy-dom": "*",
|
||||
"jsdom": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@edge-runtime/vm": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/browser": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/ui": {
|
||||
"optional": true
|
||||
},
|
||||
"happy-dom": {
|
||||
"optional": true
|
||||
},
|
||||
"jsdom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/tinyexec": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
|
||||
"integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/web-namespaces": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz",
|
||||
@@ -7034,23 +6700,6 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/why-is-node-running": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
|
||||
"integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"siginfo": "^2.0.0",
|
||||
"stackback": "0.0.2"
|
||||
},
|
||||
"bin": {
|
||||
"why-is-node-running": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/widest-line": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz",
|
||||
|
||||
+2
-4
@@ -9,8 +9,7 @@
|
||||
"prebuild": "node scripts/copy-install.mjs",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"check": "astro check",
|
||||
"test": "vitest run"
|
||||
"check": "astro check"
|
||||
},
|
||||
"dependencies": {
|
||||
"astro": "^5.0.0",
|
||||
@@ -19,7 +18,6 @@
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.0.0",
|
||||
"tailwindcss": "^4.0.0",
|
||||
"typescript": "^5.6.0",
|
||||
"vitest": "^2.1.0"
|
||||
"typescript": "^5.6.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
---
|
||||
import { localizePath, useTranslations, DOCS_URL, type Locale } from "../i18n/ui";
|
||||
interface Props { locale: Locale; }
|
||||
const { locale } = Astro.props;
|
||||
const t = useTranslations(locale);
|
||||
const p = (sub: string) => localizePath(locale, sub);
|
||||
import { DOCS_URL } from "../consts";
|
||||
|
||||
const local = [
|
||||
{ name: " ..", size: "DIR", kind: "dir" },
|
||||
@@ -19,16 +15,17 @@ const remote = [
|
||||
{ name: " docker-compose.yml", size: "1.7 KB", kind: "file" },
|
||||
{ name: " logs/", size: "DIR", kind: "dir" },
|
||||
];
|
||||
const status = "↹ switch · ↑↓ move · ↵ open · SPACE transfer";
|
||||
---
|
||||
<section class="mx-auto max-w-5xl px-4 pt-12">
|
||||
<p class="text-green text-sm tracking-widest uppercase">{t("hero.kicker")}</p>
|
||||
<p class="text-green text-sm tracking-widest uppercase">// terminal file transfer</p>
|
||||
<div class="mt-4 overflow-hidden rounded-lg border border-line bg-mantle shadow-2xl">
|
||||
<div class="border-b border-line bg-crust px-3 py-2 text-xs text-overlay">
|
||||
{t("hero.tabs", { host: "192.168.1.10" })}<span class="blink ml-1"></span>
|
||||
termscp — 192.168.1.10 (sftp) connected<span class="blink ml-1"></span>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 bg-base">
|
||||
<div>
|
||||
<div class="border-b border-line bg-mantle px-3 py-1.5 text-xs text-teal">{t("hero.local")}</div>
|
||||
<div class="border-b border-line bg-mantle px-3 py-1.5 text-xs text-teal">/home/veeso (local)</div>
|
||||
{local.map((r) => (
|
||||
<div class={`flex justify-between gap-2 px-3 py-0.5 text-sm ${r.kind === "dir" ? "text-blue" : "text-text"}`}>
|
||||
<span class="truncate min-w-0">{r.name}</span><span class="text-overlay shrink-0">{r.size}</span>
|
||||
@@ -36,7 +33,7 @@ const remote = [
|
||||
))}
|
||||
</div>
|
||||
<div class="border-l border-line">
|
||||
<div class="border-b border-line bg-mantle px-3 py-1.5 text-xs text-mauve">{t("hero.remote")}</div>
|
||||
<div class="border-b border-line bg-mantle px-3 py-1.5 text-xs text-mauve">/var/www (remote)</div>
|
||||
{remote.map((r) => (
|
||||
<div class={`flex justify-between gap-2 px-3 py-0.5 text-sm ${
|
||||
r.kind === "sel" ? "bg-blue text-base font-bold" : r.kind === "dir" ? "text-blue" : "text-text"
|
||||
@@ -47,17 +44,17 @@ const remote = [
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{/* set:html is safe: status is a static author-controlled string, never user input. */}
|
||||
<div class="flex justify-between border-t border-line bg-crust px-3 py-1.5 text-xs text-overlay">
|
||||
{/* set:html is safe: hero.status is author-controlled i18n content, never user input. Do not interpolate untrusted data here. */}
|
||||
<span set:html={t("hero.status").replace(/(↹|↑↓|↵|SPACE)/g, '<span class="text-yellow">$1</span>')}></span>
|
||||
<span set:html={status.replace(/(↹|↑↓|↵|SPACE)/g, '<span class="text-yellow">$1</span>')}></span>
|
||||
<span>4 files · 1.2 GB</span>
|
||||
</div>
|
||||
<div class="bg-base px-6 py-6 text-center">
|
||||
<h1 class="text-2xl font-bold text-text">{t("hero.title")}</h1>
|
||||
<p class="mx-auto mt-2 max-w-xl text-subtext">{t("hero.subtitle")}</p>
|
||||
<h1 class="text-2xl font-bold text-text">The dual-pane explorer for your terminal</h1>
|
||||
<p class="mx-auto mt-2 max-w-xl text-subtext">Local left, remote right. Transfer, sync, edit — never leave the keyboard.</p>
|
||||
<div class="mt-5 flex flex-wrap justify-center gap-3">
|
||||
<a href={p("/install")} class="rounded-lg bg-green px-5 py-2.5 font-bold text-crust">{t("cta.install")}</a>
|
||||
<a href={DOCS_URL} class="rounded-lg border border-line px-5 py-2.5 text-text">{t("cta.manual")}</a>
|
||||
<a href="/install" class="rounded-lg bg-green px-5 py-2.5 font-bold text-crust">Install termscp →</a>
|
||||
<a href={DOCS_URL} class="rounded-lg border border-line px-5 py-2.5 text-text">User manual</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
---
|
||||
import { useTranslations, DOCS_URL, type Locale } from "../i18n/ui";
|
||||
interface Props { locale: Locale; }
|
||||
const { locale } = Astro.props;
|
||||
const t = useTranslations(locale);
|
||||
import { DOCS_URL, GITHUB_URL } from "../consts";
|
||||
const year = new Date().getFullYear();
|
||||
---
|
||||
<footer class="mt-auto border-t border-line bg-mantle">
|
||||
<div class="mx-auto flex max-w-5xl flex-col items-center gap-2 px-4 py-8 text-sm text-overlay">
|
||||
<div class="flex gap-5">
|
||||
<a href="https://github.com/veeso/termscp" class="hover:text-text">GitHub</a>
|
||||
<a href={GITHUB_URL} class="hover:text-text">GitHub</a>
|
||||
<a href="https://crates.io/crates/termscp" class="hover:text-text">crates.io</a>
|
||||
<a href={DOCS_URL} class="hover:text-text">{t("nav.manual")}</a>
|
||||
<a href="https://ko-fi.com/veeso" class="hover:text-text">{t("footer.support")}</a>
|
||||
<a href={DOCS_URL} class="hover:text-text">User manual</a>
|
||||
<a href="https://ko-fi.com/veeso" class="hover:text-text">Support me</a>
|
||||
</div>
|
||||
<p>© {year} Christian Visintin · {t("footer.rights")}</p>
|
||||
<p>© {year} Christian Visintin · Released under the MIT license.</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
---
|
||||
import { locales, localizePath, type Locale } from "../i18n/ui";
|
||||
|
||||
interface Props {
|
||||
locale: Locale;
|
||||
path: string; // unprefixed path, e.g. "/install" or "/"
|
||||
}
|
||||
const { locale, path } = Astro.props;
|
||||
const labels: Record<Locale, string> = {
|
||||
en: "EN",
|
||||
"zh-CN": "中文",
|
||||
it: "IT",
|
||||
fr: "FR",
|
||||
es: "ES",
|
||||
};
|
||||
const href = (l: Locale) => localizePath(l, path);
|
||||
---
|
||||
<div class="flex items-center gap-2 text-sm">
|
||||
{locales.map((l) => (
|
||||
<a
|
||||
href={href(l)}
|
||||
class={l === locale ? "text-green" : "text-overlay hover:text-text"}
|
||||
>{labels[l]}</a>
|
||||
))}
|
||||
</div>
|
||||
@@ -1,29 +1,19 @@
|
||||
---
|
||||
import { localizePath, useTranslations, DOCS_URL, type Locale } from "../i18n/ui";
|
||||
import ThemeToggle from "./ThemeToggle.astro";
|
||||
import LangPicker from "./LangPicker.astro";
|
||||
|
||||
interface Props {
|
||||
locale: Locale;
|
||||
path: string;
|
||||
}
|
||||
const { locale, path } = Astro.props;
|
||||
const t = useTranslations(locale);
|
||||
const p = (sub: string) => localizePath(locale, sub);
|
||||
import { DOCS_URL, GITHUB_URL } from "../consts";
|
||||
---
|
||||
<header class="sticky top-0 z-50 border-b border-line bg-mantle/90 backdrop-blur">
|
||||
<nav class="mx-auto flex max-w-5xl items-center justify-between px-4 py-3">
|
||||
<a href={p("/")} class="flex items-center gap-2 text-text">
|
||||
<a href="/" class="flex items-center gap-2 text-text">
|
||||
<img src="/assets/images/termscp.webp" alt="termscp" class="h-7 w-7" />
|
||||
<span class="font-bold">termscp</span>
|
||||
</a>
|
||||
|
||||
<!-- Desktop -->
|
||||
<div class="hidden items-center gap-5 text-sm sm:flex">
|
||||
<a href={p("/install")} class="text-overlay hover:text-text">{t("nav.install")}</a>
|
||||
<a href={DOCS_URL} class="text-overlay hover:text-text">{t("nav.manual")}</a>
|
||||
<a href="https://github.com/veeso/termscp" class="text-overlay hover:text-text">{t("nav.github")}</a>
|
||||
<LangPicker locale={locale} path={path} />
|
||||
<a href="/install" class="text-overlay hover:text-text">Install</a>
|
||||
<a href={DOCS_URL} class="text-overlay hover:text-text">User manual</a>
|
||||
<a href={GITHUB_URL} class="text-overlay hover:text-text">GitHub</a>
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
|
||||
@@ -31,10 +21,9 @@ const p = (sub: string) => localizePath(locale, sub);
|
||||
<details class="relative sm:hidden">
|
||||
<summary class="cursor-pointer list-none rounded p-2 text-text hover:bg-mantle" aria-label="Menu">☰</summary>
|
||||
<div class="absolute right-0 mt-2 flex w-48 flex-col gap-3 rounded-lg border border-line bg-mantle p-4 text-sm shadow-xl">
|
||||
<a href={p("/install")} class="text-overlay hover:text-text">{t("nav.install")}</a>
|
||||
<a href={DOCS_URL} class="text-overlay hover:text-text">{t("nav.manual")}</a>
|
||||
<a href="https://github.com/veeso/termscp" class="text-overlay hover:text-text">{t("nav.github")}</a>
|
||||
<LangPicker locale={locale} path={path} />
|
||||
<a href="/install" class="text-overlay hover:text-text">Install</a>
|
||||
<a href={DOCS_URL} class="text-overlay hover:text-text">User manual</a>
|
||||
<a href={GITHUB_URL} class="text-overlay hover:text-text">GitHub</a>
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</details>
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
---
|
||||
import { useTranslations, type Locale } from "../i18n/ui";
|
||||
interface Props { locale: Locale; }
|
||||
const { locale } = Astro.props;
|
||||
const t = useTranslations(locale);
|
||||
const protocols = ["SCP", "SFTP", "FTP/S", "S3", "Kube", "SMB", "WebDAV"];
|
||||
---
|
||||
<section class="mx-auto max-w-5xl px-4 py-12 text-center">
|
||||
<h2 class="text-overlay text-sm uppercase tracking-widest">{t("protocols.heading")}</h2>
|
||||
<h2 class="text-overlay text-sm uppercase tracking-widest">Speaks every protocol</h2>
|
||||
<div class="mt-4 flex flex-wrap justify-center gap-2">
|
||||
{protocols.map((proto) => (
|
||||
<span class="rounded-full border border-line px-4 py-1 text-sm text-text">{proto}</span>
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export const DOCS_URL = "https://docs.termscp.rs";
|
||||
export const GITHUB_URL = "https://github.com/veeso/termscp";
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"nav.home": "Home",
|
||||
"nav.install": "Install",
|
||||
"nav.manual": "User manual",
|
||||
"nav.github": "GitHub",
|
||||
"hero.tabs": "termscp — {host} (sftp) connected",
|
||||
"hero.local": "/home/veeso (local)",
|
||||
"hero.remote": "/var/www (remote)",
|
||||
"hero.status": "↹ switch · ↑↓ move · ↵ open · SPACE transfer",
|
||||
"hero.title": "The dual-pane explorer for your terminal",
|
||||
"hero.subtitle": "Local left, remote right. Transfer, sync, edit — never leave the keyboard.",
|
||||
"hero.kicker": "// terminal file transfer",
|
||||
"cta.install": "Install termscp →",
|
||||
"cta.manual": "User manual",
|
||||
"features.heading": "Why termscp",
|
||||
"features.handy.title": "Handy UI",
|
||||
"features.handy.body": "Explore and operate on the remote and local file system with a handy UI.",
|
||||
"features.cross.title": "Cross platform",
|
||||
"features.cross.body": "Runs on Windows, macOS, Linux and BSD.",
|
||||
"features.custom.title": "Customizable",
|
||||
"features.custom.body": "Customize the explorer, the text editor and default options.",
|
||||
"features.bookmarks.title": "Bookmarks",
|
||||
"features.bookmarks.body": "Connect to favourite hosts via bookmarks and recent connections.",
|
||||
"features.security.title": "Security first",
|
||||
"features.security.body": "Store passwords in your operating system key vault.",
|
||||
"features.performance.title": "Eye on performance",
|
||||
"features.performance.body": "Built with an eye on performance to keep CPU usage low.",
|
||||
"protocols.heading": "Speaks every protocol",
|
||||
"install.heading": "Install termscp",
|
||||
"install.subtitle": "Pick your platform. One command and you're in.",
|
||||
"footer.support": "Support me",
|
||||
"footer.rights": "Released under the MIT license.",
|
||||
"lang.label": "Language"
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"nav.home": "Inicio",
|
||||
"nav.install": "Instalar",
|
||||
"nav.manual": "Manual de usuario",
|
||||
"nav.github": "GitHub",
|
||||
"hero.tabs": "termscp — {host} (sftp) conectado",
|
||||
"hero.local": "/home/veeso (local)",
|
||||
"hero.remote": "/var/www (remoto)",
|
||||
"hero.status": "↹ cambiar · ↑↓ mover · ↵ abrir · ESPACIO transferir",
|
||||
"hero.title": "El explorador de doble panel para tu terminal",
|
||||
"hero.subtitle": "Local a la izquierda, remoto a la derecha. Transfiere, sincroniza, edita — sin soltar el teclado.",
|
||||
"hero.kicker": "// transferencia de archivos en terminal",
|
||||
"cta.install": "Instalar termscp →",
|
||||
"cta.manual": "Manual de usuario",
|
||||
"features.heading": "Por qué termscp",
|
||||
"features.handy.title": "Interfaz práctica",
|
||||
"features.handy.body": "Explora y opera en el sistema de archivos remoto y local con una interfaz práctica.",
|
||||
"features.cross.title": "Multiplataforma",
|
||||
"features.cross.body": "Funciona en Windows, macOS, Linux y BSD.",
|
||||
"features.custom.title": "Personalizable",
|
||||
"features.custom.body": "Personaliza el explorador, el editor de texto y las opciones predeterminadas.",
|
||||
"features.bookmarks.title": "Marcadores",
|
||||
"features.bookmarks.body": "Conéctate a tus hosts favoritos mediante marcadores y conexiones recientes.",
|
||||
"features.security.title": "Seguridad primero",
|
||||
"features.security.body": "Guarda tus contraseñas en la bóveda de claves de tu sistema operativo.",
|
||||
"features.performance.title": "Ojo en el rendimiento",
|
||||
"features.performance.body": "Desarrollado teniendo en cuenta el rendimiento para mantener bajo el uso de CPU.",
|
||||
"protocols.heading": "Habla todos los protocolos",
|
||||
"install.heading": "Instalar termscp",
|
||||
"install.subtitle": "Elige tu plataforma. Un comando y listo.",
|
||||
"footer.support": "Apóyame",
|
||||
"footer.rights": "Publicado bajo la licencia MIT.",
|
||||
"lang.label": "Idioma"
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"nav.home": "Accueil",
|
||||
"nav.install": "Installer",
|
||||
"nav.github": "GitHub",
|
||||
"hero.tabs": "termscp — {host} (sftp) connecté",
|
||||
"hero.local": "/home/veeso (local)",
|
||||
"hero.remote": "/var/www (distant)",
|
||||
"hero.status": "↹ basculer · ↑↓ déplacer · ↵ ouvrir · ESPACE transférer",
|
||||
"hero.title": "L'explorateur à deux panneaux pour votre terminal",
|
||||
"hero.subtitle": "Local à gauche, distant à droite. Transférez, synchronisez, éditez — sans quitter le clavier.",
|
||||
"hero.kicker": "// transfert de fichiers en terminal",
|
||||
"cta.install": "Installer termscp →",
|
||||
"cta.manual": "Manuel d'utilisateur",
|
||||
"features.heading": "Pourquoi termscp",
|
||||
"features.handy.title": "Interface pratique",
|
||||
"features.handy.body": "Explorez et utilisez le système de fichiers distant et local avec une interface pratique.",
|
||||
"features.cross.title": "Multi-plateforme",
|
||||
"features.cross.body": "Fonctionne sur Windows, macOS, Linux et BSD.",
|
||||
"features.custom.title": "Personnalisable",
|
||||
"features.custom.body": "Personnalisez l'explorateur, l'éditeur de texte et les options par défaut.",
|
||||
"features.bookmarks.title": "Signets",
|
||||
"features.bookmarks.body": "Connectez-vous à vos hôtes préférés via des signets et des connexions récentes.",
|
||||
"features.security.title": "Sécurité avant tout",
|
||||
"features.security.body": "Enregistrez vos mots de passe dans le coffre-fort de votre système.",
|
||||
"features.performance.title": "Regard sur les performances",
|
||||
"features.performance.body": "Développé en gardant un œil sur les performances pour éviter une utilisation élevée du processeur.",
|
||||
"protocols.heading": "Parle tous les protocoles",
|
||||
"install.heading": "Installer termscp",
|
||||
"install.subtitle": "Choisissez votre plateforme. Une commande et c'est parti.",
|
||||
"footer.support": "Me soutenir",
|
||||
"footer.rights": "Publié sous licence MIT.",
|
||||
"lang.label": "Langue"
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"nav.home": "Home",
|
||||
"nav.install": "Installa",
|
||||
"nav.manual": "Manuale utente",
|
||||
"nav.github": "GitHub",
|
||||
"hero.tabs": "termscp — {host} (sftp) connesso",
|
||||
"hero.local": "/home/veeso (locale)",
|
||||
"hero.remote": "/var/www (remoto)",
|
||||
"hero.status": "↹ cambia · ↑↓ muovi · ↵ apri · SPAZIO trasferisci",
|
||||
"hero.title": "L'explorer a doppio pannello per il tuo terminale",
|
||||
"hero.subtitle": "Locale a sinistra, remoto a destra. Trasferisci, sincronizza, modifica — senza mai lasciare la tastiera.",
|
||||
"hero.kicker": "// file transfer da terminale",
|
||||
"cta.install": "Installa termscp →",
|
||||
"cta.manual": "Manuale utente",
|
||||
"features.heading": "Perché termscp",
|
||||
"features.handy.title": "UI ergonomica",
|
||||
"features.handy.body": "Naviga e lavora sul file system remoto e locale attraverso una UI di facile utilizzo.",
|
||||
"features.cross.title": "Multi piattaforma",
|
||||
"features.cross.body": "Gira su Windows, macOS, Linux e BSD.",
|
||||
"features.custom.title": "Personalizzabile",
|
||||
"features.custom.body": "Personalizza l'explorer, l'editor di testo e le opzioni predefinite.",
|
||||
"features.bookmarks.title": "Preferiti",
|
||||
"features.bookmarks.body": "Connettiti agli host preferiti tramite preferiti e connessioni recenti.",
|
||||
"features.security.title": "Sicurezza in prima classe",
|
||||
"features.security.body": "Salva le password nel vault del tuo sistema operativo.",
|
||||
"features.performance.title": "Focus sulle performance",
|
||||
"features.performance.body": "Sviluppato con un occhio alle performance per mantenere basso l'uso della CPU.",
|
||||
"protocols.heading": "Parla ogni protocollo",
|
||||
"install.heading": "Installa termscp",
|
||||
"install.subtitle": "Scegli la tua piattaforma. Un comando e sei dentro.",
|
||||
"footer.support": "Supportami",
|
||||
"footer.rights": "Rilasciato sotto licenza MIT.",
|
||||
"lang.label": "Lingua"
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { useTranslations, locales, defaultLocale } from "./ui";
|
||||
|
||||
describe("i18n", () => {
|
||||
it("exposes the five supported locales with en default", () => {
|
||||
expect(locales).toEqual(["en", "zh-CN", "it", "fr", "es"]);
|
||||
expect(defaultLocale).toBe("en");
|
||||
});
|
||||
|
||||
it("resolves a key in the requested locale", () => {
|
||||
const t = useTranslations("it");
|
||||
expect(t("nav.install")).toBe("Installa");
|
||||
});
|
||||
|
||||
it("falls back to en when a key is missing in the locale", () => {
|
||||
const t = useTranslations("fr");
|
||||
expect(t("nav.manual")).toBe("User manual");
|
||||
});
|
||||
|
||||
it("returns the key itself when missing everywhere", () => {
|
||||
const t = useTranslations("en");
|
||||
expect(t("does.not.exist")).toBe("does.not.exist");
|
||||
});
|
||||
|
||||
it("interpolates {vars}", () => {
|
||||
const t = useTranslations("en");
|
||||
expect(t("hero.tabs", { host: "1.2.3.4" })).toContain("1.2.3.4");
|
||||
});
|
||||
|
||||
it("interpolates the full string exactly", () => {
|
||||
const t = useTranslations("en");
|
||||
expect(t("hero.tabs", { host: "10.0.0.1" })).toBe(
|
||||
"termscp — 10.0.0.1 (sftp) connected",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,57 +0,0 @@
|
||||
import en from "./en.json";
|
||||
import zhCN from "./zh-CN.json";
|
||||
import it from "./it.json";
|
||||
import fr from "./fr.json";
|
||||
import es from "./es.json";
|
||||
|
||||
/** External documentation site (mdbook), deployed separately. */
|
||||
export const DOCS_URL = "https://docs.termscp.rs";
|
||||
|
||||
export const locales = ["en", "zh-CN", "it", "fr", "es"] as const;
|
||||
export type Locale = (typeof locales)[number];
|
||||
export const defaultLocale: Locale = "en";
|
||||
|
||||
/** Authoritative set of translation keys, derived from the en dictionary. */
|
||||
export type TranslationKey = keyof typeof en;
|
||||
|
||||
// `en` is left uncast so it remains the source of truth for `TranslationKey`.
|
||||
// Other locales may omit keys, so they are typed as partial dictionaries.
|
||||
const dictionaries = {
|
||||
en,
|
||||
"zh-CN": zhCN as Partial<Record<TranslationKey, string>>,
|
||||
it: it as Partial<Record<TranslationKey, string>>,
|
||||
fr: fr as Partial<Record<TranslationKey, string>>,
|
||||
es: es as Partial<Record<TranslationKey, string>>,
|
||||
} satisfies Record<Locale, Partial<Record<TranslationKey, string>>>;
|
||||
|
||||
export function isLocale(value: string): value is Locale {
|
||||
return (locales as readonly string[]).includes(value);
|
||||
}
|
||||
|
||||
/** Prefix a path with the locale, except for the default locale. */
|
||||
export function localizePath(locale: Locale, path: string): string {
|
||||
return locale === defaultLocale ? path : `/${locale}${path}`;
|
||||
}
|
||||
|
||||
/** Resolve a translator for `locale`, falling back to en, then to the key. */
|
||||
export function useTranslations(locale: Locale) {
|
||||
const dict = dictionaries[locale] ?? dictionaries[defaultLocale];
|
||||
// `TranslationKey | (string & {})` keeps autocomplete for known keys while
|
||||
// still accepting arbitrary strings, preserving the "return key if missing"
|
||||
// contract at runtime.
|
||||
return (
|
||||
key: TranslationKey | (string & {}),
|
||||
vars?: Record<string, string>,
|
||||
): string => {
|
||||
let value =
|
||||
dict[key as TranslationKey] ??
|
||||
dictionaries[defaultLocale][key as TranslationKey] ??
|
||||
key;
|
||||
if (vars) {
|
||||
for (const [k, v] of Object.entries(vars)) {
|
||||
value = value.replaceAll(`{${k}}`, v);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
};
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"nav.home": "主页",
|
||||
"nav.install": "安装",
|
||||
"nav.manual": "用户手册",
|
||||
"nav.github": "GitHub",
|
||||
"hero.tabs": "termscp — {host} (sftp) 已连接",
|
||||
"hero.local": "/home/veeso (本地)",
|
||||
"hero.remote": "/var/www (远程)",
|
||||
"hero.status": "↹ 切换 · ↑↓ 移动 · ↵ 打开 · 空格 传输",
|
||||
"hero.title": "为你的终端打造的双栏文件浏览器",
|
||||
"hero.subtitle": "左边本地,右边远程。传输、同步、编辑 — 始终不离键盘。",
|
||||
"hero.kicker": "// 终端文件传输",
|
||||
"cta.install": "安装 termscp →",
|
||||
"cta.manual": "用户手册",
|
||||
"features.heading": "为什么选择 termscp",
|
||||
"features.handy.title": "方便的用户界面",
|
||||
"features.handy.body": "使用方便的 UI 在远程和本地文件系统上探索和操作。",
|
||||
"features.cross.title": "跨平台",
|
||||
"features.cross.body": "在 Windows、macOS、Linux 和 BSD 上运行。",
|
||||
"features.custom.title": "可定制",
|
||||
"features.custom.body": "自定义文件浏览器、文本编辑器和默认选项。",
|
||||
"features.bookmarks.title": "书签",
|
||||
"features.bookmarks.body": "通过书签和最近的连接连接到你最喜欢的主机。",
|
||||
"features.security.title": "安全第一",
|
||||
"features.security.body": "将你的密码保存到操作系统的密钥保管库中。",
|
||||
"features.performance.title": "关注性能",
|
||||
"features.performance.body": "在开发时关注性能,以保持较低的 CPU 使用率。",
|
||||
"protocols.heading": "支持各种协议",
|
||||
"install.heading": "安装 termscp",
|
||||
"install.subtitle": "选择你的平台。一条命令即可上手。",
|
||||
"footer.support": "支持我",
|
||||
"footer.rights": "基于 MIT 许可证发布。",
|
||||
"lang.label": "语言"
|
||||
}
|
||||
@@ -1,24 +1,21 @@
|
||||
---
|
||||
import "../styles/theme.css";
|
||||
import { defaultLocale, locales, type Locale } from "../i18n/ui";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
locale?: Locale;
|
||||
/** Path WITHOUT locale prefix, e.g. "/install". "/" for home. */
|
||||
/** Path for canonical/og, e.g. "/install" or "/". */
|
||||
path: string;
|
||||
}
|
||||
|
||||
const { title, description, locale = defaultLocale, path } = Astro.props;
|
||||
const { title, description, path } = Astro.props;
|
||||
const site = Astro.site?.href ?? "https://termscp.rs/";
|
||||
const localizedPath = locale === defaultLocale ? path : `/${locale}${path}`;
|
||||
const canonical = new URL(localizedPath, site).href;
|
||||
const canonical = new URL(path, site).href;
|
||||
const ogImage = new URL("/assets/images/og_preview.jpg", site).href;
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang={locale} data-theme="frappe">
|
||||
<html lang="en" data-theme="frappe">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
@@ -26,16 +23,6 @@ const ogImage = new URL("/assets/images/og_preview.jpg", site).href;
|
||||
<meta name="description" content={description} />
|
||||
<link rel="canonical" href={canonical} />
|
||||
|
||||
<!-- hreflang alternates -->
|
||||
{locales.map((l) => (
|
||||
<link
|
||||
rel="alternate"
|
||||
hreflang={l}
|
||||
href={new URL(l === defaultLocale ? path : `/${l}${path}`, site).href}
|
||||
/>
|
||||
))}
|
||||
<link rel="alternate" hreflang="x-default" href={new URL(path, site).href} />
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="termscp" />
|
||||
@@ -43,11 +30,11 @@ const ogImage = new URL("/assets/images/og_preview.jpg", site).href;
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:url" content={canonical} />
|
||||
<meta property="og:image" content={ogImage} />
|
||||
<meta property="og:image:alt" content="termscp — terminal file transfer and explorer" />
|
||||
<meta property="og:image:width" content="1280" />
|
||||
<meta property="og:image:height" content="640" />
|
||||
<meta property="og:image:alt" content="termscp — terminal file transfer and explorer" />
|
||||
|
||||
<!-- Twitter Card -->
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content={title} />
|
||||
<meta name="twitter:description" content={description} />
|
||||
@@ -61,7 +48,7 @@ const ogImage = new URL("/assets/images/og_preview.jpg", site).href;
|
||||
<!-- Font preload -->
|
||||
<link rel="preload" href="/fonts/jetbrains-mono-400.woff2" as="font" type="font/woff2" crossorigin />
|
||||
|
||||
<!-- No-flash theme bootstrap. Contract: localStorage 'theme' is "frappe" | "latte"; the ThemeToggle component writes the same values. -->
|
||||
<!-- No-flash theme bootstrap. Contract: localStorage 'theme' is "frappe" | "latte"; ThemeToggle writes the same. -->
|
||||
<script is:inline>
|
||||
(() => {
|
||||
const t = localStorage.getItem("theme") ||
|
||||
|
||||
+14
-11
@@ -5,30 +5,33 @@ import Footer from "../components/Footer.astro";
|
||||
import ExplorerHero from "../components/ExplorerHero.astro";
|
||||
import ProtocolStrip from "../components/ProtocolStrip.astro";
|
||||
import FeatureCard from "../components/FeatureCard.astro";
|
||||
import { useTranslations } from "../i18n/ui";
|
||||
|
||||
const locale = "en" as const;
|
||||
const t = useTranslations(locale);
|
||||
const features = ["handy", "cross", "custom", "bookmarks", "security", "performance"];
|
||||
const features = [
|
||||
{ title: "Handy UI", body: "Explore and operate on the remote and local file system with a handy UI." },
|
||||
{ title: "Cross platform", body: "Runs on Windows, macOS, Linux and BSD." },
|
||||
{ title: "Customizable", body: "Customize the explorer, the text editor and default options." },
|
||||
{ title: "Bookmarks", body: "Connect to favourite hosts via bookmarks and recent connections." },
|
||||
{ title: "Security first", body: "Store passwords in your operating system key vault." },
|
||||
{ title: "Eye on performance", body: "Built with an eye on performance to keep CPU usage low." },
|
||||
];
|
||||
---
|
||||
<Base
|
||||
title="termscp — terminal file transfer & explorer for SCP/SFTP/FTP/S3/Kube/SMB/WebDAV"
|
||||
description="A feature-rich terminal UI file transfer and explorer with support for SCP/SFTP/FTP/Kube/S3/WebDAV/SMB."
|
||||
locale={locale}
|
||||
path="/"
|
||||
>
|
||||
<Nav locale={locale} path="/" />
|
||||
<Nav />
|
||||
<main class="flex-1">
|
||||
<ExplorerHero locale={locale} />
|
||||
<ExplorerHero />
|
||||
<section class="mx-auto max-w-5xl px-4 py-12">
|
||||
<h2 class="mb-6 text-center text-xl text-text">{t("features.heading")}</h2>
|
||||
<h2 class="mb-6 text-center text-xl text-text">Why termscp</h2>
|
||||
<div class="grid gap-5 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{features.map((f) => (
|
||||
<FeatureCard title={t(`features.${f}.title`)} body={t(`features.${f}.body`)} />
|
||||
<FeatureCard title={f.title} body={f.body} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
<ProtocolStrip locale={locale} />
|
||||
<ProtocolStrip />
|
||||
</main>
|
||||
<Footer locale={locale} />
|
||||
<Footer />
|
||||
</Base>
|
||||
|
||||
@@ -3,14 +3,10 @@ import Base from "../layouts/Base.astro";
|
||||
import Nav from "../components/Nav.astro";
|
||||
import Footer from "../components/Footer.astro";
|
||||
import InstallTabs from "../components/InstallTabs.astro";
|
||||
import { useTranslations, DOCS_URL } from "../i18n/ui";
|
||||
|
||||
const locale = "en" as const;
|
||||
const t = useTranslations(locale);
|
||||
import { DOCS_URL } from "../consts";
|
||||
|
||||
const script = "curl --proto '=https' --tlsv1.2 -sSLf https://termscp.rs/install.sh | sh";
|
||||
|
||||
// Verified package managers (see repo README). Cargo is universal.
|
||||
const methods = [
|
||||
{ id: "cargo", label: "Cargo", cmd: "cargo install termscp --locked" },
|
||||
{ id: "arch", label: "Arch Linux", cmd: "pacman -S termscp" },
|
||||
@@ -21,15 +17,13 @@ const methods = [
|
||||
<Base
|
||||
title="Install termscp"
|
||||
description="Install termscp on Linux, macOS, BSD and Windows — one shell command, or via Cargo, pacman, pkgin and Chocolatey."
|
||||
locale={locale}
|
||||
path="/install"
|
||||
>
|
||||
<Nav locale={locale} path="/install" />
|
||||
<Nav />
|
||||
<main class="mx-auto w-full max-w-3xl flex-1 px-4 py-12">
|
||||
<h1 class="text-2xl font-bold text-text">{t("install.heading")}</h1>
|
||||
<p class="mt-2 text-subtext">{t("install.subtitle")}</p>
|
||||
<h1 class="text-2xl font-bold text-text">Install termscp</h1>
|
||||
<p class="mt-2 text-subtext">Pick your platform. One command and you're in.</p>
|
||||
|
||||
<!-- Primary: install script -->
|
||||
<div class="mt-6 rounded-lg border border-line bg-mantle p-1">
|
||||
<div class="flex items-center gap-2 border-b border-line px-3 py-2 text-xs text-overlay">
|
||||
<span class="text-green">$</span> Linux · macOS · BSD
|
||||
@@ -38,17 +32,14 @@ const methods = [
|
||||
</div>
|
||||
<p class="mt-2 text-xs text-overlay">Requires <span class="text-text">curl</span>. macOS uses Homebrew if available, otherwise builds from source.</p>
|
||||
|
||||
<!-- Secondary: package managers -->
|
||||
<h2 class="mt-10 mb-3 text-lg text-text">Package managers</h2>
|
||||
<InstallTabs methods={methods} />
|
||||
|
||||
<p class="mt-8 text-sm text-overlay">
|
||||
Update anytime with <span class="text-green">termscp --update</span>.
|
||||
</p>
|
||||
<p class="mt-8 text-sm text-overlay">Update anytime with <span class="text-green">termscp --update</span>.</p>
|
||||
<p class="mt-2 text-sm text-overlay">
|
||||
Linux build deps: <span class="text-text">libdbus-1</span>, <span class="text-text">pkg-config</span>, <span class="text-text">libsmbclient</span>.
|
||||
More details in the <a href={DOCS_URL} class="text-blue hover:underline">docs</a>.
|
||||
</p>
|
||||
</main>
|
||||
<Footer locale={locale} />
|
||||
<Footer />
|
||||
</Base>
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import { defineConfig } from "vitest/config";
|
||||
|
||||
export default defineConfig({
|
||||
test: { include: ["src/**/*.test.ts", "scripts/**/*.test.ts"] },
|
||||
});
|
||||
Reference in New Issue
Block a user