mirror of
https://github.com/veeso/termscp.git
synced 2025-12-07 09:36:00 -08:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92d20f33fe | ||
|
|
15caef83e6 | ||
|
|
19992402e2 | ||
|
|
182bbb94a2 | ||
|
|
20c3e22572 | ||
|
|
d3c2c084db | ||
|
|
cfbecc049d | ||
|
|
9de171fb83 | ||
|
|
2f48c765e3 | ||
|
|
ca005cbecd | ||
|
|
ee28d34f29 | ||
|
|
295191b9eb | ||
|
|
16b2aa8596 | ||
|
|
522ee0355e | ||
|
|
e5ca956897 | ||
|
|
a071f3d420 | ||
|
|
66888c418c | ||
|
|
b5f712fa04 | ||
|
|
1509401599 | ||
|
|
6506f4ae59 | ||
|
|
5a5fa52b57 | ||
|
|
b7d4c68ebe | ||
|
|
00a6c53178 | ||
|
|
67bc1a2a08 | ||
|
|
0bb3c9a26e | ||
|
|
4394941c71 | ||
|
|
175ea23bfc | ||
|
|
cef100e5d9 |
42
.github/workflows/build-artifacts.yml
vendored
Normal file
42
.github/workflows/build-artifacts.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: "Build artifacts"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-binaries:
|
||||
name: Build - ${{ matrix.platform.release_for }}
|
||||
strategy:
|
||||
matrix:
|
||||
platform:
|
||||
- release_for: MacOS-x86_64
|
||||
os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
script: macos.sh
|
||||
|
||||
- release_for: MacOS-M1
|
||||
os: macos-latest
|
||||
target: aarch64-apple-darwin
|
||||
script: macos.sh
|
||||
|
||||
runs-on: ${{ matrix.platform.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
override: true
|
||||
target: ${{ matrix.platform.target }}
|
||||
- name: Build release
|
||||
run: cargo build --release --target ${{ matrix.platform.target }}
|
||||
- name: Prepare artifact files
|
||||
run: |
|
||||
mkdir -p .artifact
|
||||
mv target/${{ matrix.platform.target }}/release/termscp .artifact/termscp
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
name: ${{ matrix.platform.release_for }}
|
||||
path: .artifact/*
|
||||
10
.github/workflows/coverage.yml
vendored
10
.github/workflows/coverage.yml
vendored
@@ -1,6 +1,14 @@
|
||||
name: Coverage
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "*.md"
|
||||
- "./site/**/*"
|
||||
push:
|
||||
paths-ignore:
|
||||
- "*.md"
|
||||
- "./site/**/*"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
4
.github/workflows/install.yml
vendored
4
.github/workflows/install.yml
vendored
@@ -16,8 +16,8 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install dependencies
|
||||
run: sudo apt update && sudo apt install -y curl wget
|
||||
run: sudo apt update && sudo apt install -y curl wget libsmbclient
|
||||
- name: Install termscp from script
|
||||
run: |
|
||||
./install.sh -v=0.12.0 -f
|
||||
./install.sh -v=0.12.2 -f
|
||||
which termscp || exit 1
|
||||
|
||||
10
.github/workflows/linux.yml
vendored
10
.github/workflows/linux.yml
vendored
@@ -1,6 +1,14 @@
|
||||
name: Linux
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "*.md"
|
||||
- "./site/**/*"
|
||||
push:
|
||||
paths-ignore:
|
||||
- "*.md"
|
||||
- "./site/**/*"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
10
.github/workflows/macos.yml
vendored
10
.github/workflows/macos.yml
vendored
@@ -1,6 +1,14 @@
|
||||
name: MacOS
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "*.md"
|
||||
- "./site/**/*"
|
||||
push:
|
||||
paths-ignore:
|
||||
- "*.md"
|
||||
- "./site/**/*"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
23
.github/workflows/stale.yml
vendored
Normal file
23
.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: Close inactive issues
|
||||
on:
|
||||
schedule:
|
||||
- cron: "30 1 * * *"
|
||||
|
||||
jobs:
|
||||
close-issues:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v4.1.1
|
||||
with:
|
||||
days-before-issue-stale: 30
|
||||
days-before-issue-close: 7
|
||||
stale-issue-label: "stale"
|
||||
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
|
||||
close-issue-message: "This issue was closed because it has been inactive for 7 days since being marked as stale."
|
||||
days-before-pr-stale: -1
|
||||
days-before-pr-close: -1
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
exempt-issue-labels: "backlog"
|
||||
4
.github/workflows/website.yml
vendored
4
.github/workflows/website.yml
vendored
@@ -5,6 +5,8 @@ on:
|
||||
# Runs on pushes targeting the default branch
|
||||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- "./site/**/*"
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
@@ -35,7 +37,7 @@ jobs:
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v1
|
||||
with:
|
||||
path: './site/'
|
||||
path: "./site/"
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v1
|
||||
|
||||
10
.github/workflows/windows.yml
vendored
10
.github/workflows/windows.yml
vendored
@@ -1,6 +1,14 @@
|
||||
name: Windows
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "*.md"
|
||||
- "./site/**/*"
|
||||
push:
|
||||
paths-ignore:
|
||||
- "*.md"
|
||||
- "./site/**/*"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
20
CHANGELOG.md
20
CHANGELOG.md
@@ -1,6 +1,8 @@
|
||||
# Changelog
|
||||
|
||||
- [Changelog](#changelog)
|
||||
- [0.12.2](#0122)
|
||||
- [0.12.1](#0121)
|
||||
- [0.12.0](#0120)
|
||||
- [0.11.3](#0113)
|
||||
- [0.11.2](#0112)
|
||||
@@ -31,6 +33,24 @@
|
||||
|
||||
---
|
||||
|
||||
## 0.12.2
|
||||
|
||||
Released on 01/10/2023
|
||||
|
||||
- [Issue 205](https://github.com/veeso/termscp/issues/205): Allow windows build without SMB support
|
||||
- [Issue 215](https://github.com/veeso/termscp/issues/215): termscp not respecting port in SSH config. The port specified for the host in the SSH configuration wasn't evaluated.
|
||||
- [Issue 213](https://github.com/veeso/termscp/issues/215): termscp panicks if the terminal window is too small
|
||||
|
||||
## 0.12.1
|
||||
|
||||
Released on 06/07/2023
|
||||
|
||||
- [Issue 169](https://github.com/veeso/termscp/issues/169): Local working directory can now be specified in connection form and be saved into bookmarks.
|
||||
- [Issue 188](https://github.com/veeso/termscp/issues/188): The breadcrumbs path is not fallbacked after a failed enter into the directory
|
||||
|
||||
- SMB support is now a feature (you can build rust without default features to disable smb).
|
||||
- If SSH connection timeout is 0, the connection won't timeout.
|
||||
|
||||
## 0.12.0
|
||||
|
||||
Released on 16/05/2023
|
||||
|
||||
782
Cargo.lock
generated
782
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
55
Cargo.toml
55
Cargo.toml
@@ -1,16 +1,22 @@
|
||||
[package]
|
||||
authors = ["Christian Visintin <christian.visintin@veeso.dev>"]
|
||||
categories = ["command-line-utilities"]
|
||||
description = "termscp is a feature rich terminal file transfer and explorer with support for SCP/SFTP/FTP/S3"
|
||||
description = "termscp is a feature rich terminal file transfer and explorer with support for SCP/SFTP/FTP/S3/SMB"
|
||||
edition = "2021"
|
||||
homepage = "https://termscp.veeso.dev"
|
||||
include = ["src/**/*", "LICENSE", "README.md", "CHANGELOG.md"]
|
||||
keywords = ["scp-client", "sftp-client", "ftp-client", "winscp", "command-line-utility"]
|
||||
keywords = [
|
||||
"scp-client",
|
||||
"sftp-client",
|
||||
"ftp-client",
|
||||
"winscp",
|
||||
"command-line-utility",
|
||||
]
|
||||
license = "MIT"
|
||||
name = "termscp"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/veeso/termscp"
|
||||
version = "0.12.0"
|
||||
version = "0.12.2"
|
||||
|
||||
[package.metadata.rpm]
|
||||
package = "termscp"
|
||||
@@ -41,26 +47,35 @@ edit = "^0.1"
|
||||
filetime = "^0.2"
|
||||
hostname = "^0.3"
|
||||
keyring = { version = "^2.0", optional = true }
|
||||
lazy-regex = "^2.5"
|
||||
lazy-regex = "^3"
|
||||
lazy_static = "^1.4"
|
||||
log = "^0.4"
|
||||
magic-crypt = "^3.1"
|
||||
notify = "^4.0"
|
||||
notify-rust = { version = "^4.5", default-features = false, features = [ "d" ] }
|
||||
open = "^4.0"
|
||||
notify = "=4.0.17"
|
||||
notify-rust = { version = "^4.5", default-features = false, features = ["d"] }
|
||||
open = "^5.0"
|
||||
rand = "^0.8.5"
|
||||
remotefs = "^0.2.0"
|
||||
remotefs-aws-s3 = { version = "^0.2.1", default-features = false, features = [ "find", "rustls" ] }
|
||||
remotefs-aws-s3 = { version = "^0.2.1", default-features = false, features = [
|
||||
"find",
|
||||
"rustls",
|
||||
] }
|
||||
rpassword = "^7.0"
|
||||
self_update = { version = "^0.36", default-features = false, features = [ "rustls", "archive-tar", "archive-zip", "compression-flate2", "compression-zip-deflate" ] }
|
||||
serde = { version = "^1", features = [ "derive" ] }
|
||||
self_update = { version = "^0.37", default-features = false, features = [
|
||||
"rustls",
|
||||
"archive-tar",
|
||||
"archive-zip",
|
||||
"compression-flate2",
|
||||
"compression-zip-deflate",
|
||||
] }
|
||||
serde = { version = "^1", features = ["derive"] }
|
||||
simplelog = "^0.12"
|
||||
ssh2-config = "^0.2"
|
||||
tempfile = "^3.4"
|
||||
thiserror = "^1"
|
||||
toml = "^0.7"
|
||||
tui-realm-stdlib = "^1.2"
|
||||
tuirealm = "^1.8.0"
|
||||
tui-realm-stdlib = "^1.3"
|
||||
tuirealm = "^1.9"
|
||||
unicode-width = "^0.1"
|
||||
version-compare = "^0.1"
|
||||
whoami = "^1.4"
|
||||
@@ -74,23 +89,23 @@ serial_test = "^2.0"
|
||||
cfg_aliases = "0.1"
|
||||
|
||||
[features]
|
||||
default = [ "with-keyring" ]
|
||||
github-actions = [ ]
|
||||
with-keyring = [ "keyring" ]
|
||||
default = ["smb", "with-keyring"]
|
||||
github-actions = []
|
||||
with-keyring = ["keyring"]
|
||||
smb = ["remotefs-smb"]
|
||||
|
||||
[target."cfg(not(target_os = \"macos\"))"]
|
||||
[target."cfg(not(target_os = \"macos\"))".dependencies]
|
||||
remotefs-smb = "^0.2"
|
||||
remotefs-smb = { version = "^0.2", optional = true }
|
||||
|
||||
[target."cfg(target_family = \"windows\")"]
|
||||
[target."cfg(target_family = \"windows\")".dependencies]
|
||||
remotefs-ftp = { version = "^0.1.2", features = [ "native-tls" ] }
|
||||
remotefs-ftp = { version = "^0.1.2", features = ["native-tls"] }
|
||||
remotefs-ssh = "^0.2"
|
||||
|
||||
[target."cfg(target_family = \"unix\")"]
|
||||
[target."cfg(target_family = \"unix\")".dependencies]
|
||||
remotefs-ftp = { version = "^0.1.2", features = [ "vendored", "native-tls" ] }
|
||||
remotefs-ssh = { version = "^0.2", features = [ "ssh2-vendored" ] }
|
||||
remotefs-ftp = { version = "^0.1.2", features = ["vendored", "native-tls"] }
|
||||
remotefs-ssh = { version = "^0.2", features = ["ssh2-vendored"] }
|
||||
users = "0.11.0"
|
||||
|
||||
[profile.dev]
|
||||
|
||||
14
README.md
14
README.md
@@ -6,11 +6,11 @@
|
||||
|
||||
<p align="center">~ A feature rich terminal file transfer ~</p>
|
||||
<p align="center">
|
||||
<a href="https://termscp.veeso.dev/termscp/" target="_blank">Website</a>
|
||||
<a href="https://termscp.veeso.dev" target="_blank">Website</a>
|
||||
·
|
||||
<a href="https://termscp.veeso.dev/termscp/#get-started" target="_blank">Installation</a>
|
||||
<a href="https://termscp.veeso.dev/#get-started" target="_blank">Installation</a>
|
||||
·
|
||||
<a href="https://termscp.veeso.dev/termscp/#user-manual" target="_blank">User manual</a>
|
||||
<a href="https://termscp.veeso.dev/#user-manual" target="_blank">User manual</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
@@ -62,8 +62,8 @@
|
||||
/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">Developed by <a href="https://termscp.veeso.dev/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Current version: 0.12.0 (16/05/2023)</p>
|
||||
<p align="center">Developed by <a href="https://veeso.dev/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Current version: 0.12.2 (01/10/2023)</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://opensource.org/licenses/MIT"
|
||||
@@ -179,7 +179,7 @@ NetBSD users can install termscp from the official repositories.
|
||||
pkgin install termscp
|
||||
```
|
||||
|
||||
For more information or other platforms, please visit [termscp.veeso.dev](https://termscp.veeso.dev/termscp/#get-started) to view all installation methods.
|
||||
For more information or other platforms, please visit [termscp.veeso.dev](https://termscp.veeso.dev/#get-started) to view all installation methods.
|
||||
|
||||
⚠️ If you're looking on how to update termscp just run termscp from CLI with: `(sudo) termscp --update` ⚠️
|
||||
|
||||
@@ -226,7 +226,7 @@ You can make a donation with one of these platforms:
|
||||
|
||||
## User manual 📚
|
||||
|
||||
The user manual can be found on the [termscp's website](https://termscp.veeso.dev/termscp/#user-manual) or on [Github](docs/man.md).
|
||||
The user manual can be found on the [termscp's website](https://termscp.veeso.dev/#user-manual) or on [Github](docs/man.md).
|
||||
|
||||
---
|
||||
|
||||
|
||||
5
build.rs
5
build.rs
@@ -9,7 +9,8 @@ fn main() {
|
||||
unix: { target_family = "unix" },
|
||||
windows: { target_family = "windows" },
|
||||
// exclusive features
|
||||
smb: { not( macos ) },
|
||||
smb_unix: { all(unix, not(macos)) }
|
||||
smb: { all(feature = "smb", not( macos )) },
|
||||
smb_unix: { all(unix, feature = "smb", not(macos)) },
|
||||
smb_windows: { all(windows, feature = "smb") }
|
||||
}
|
||||
}
|
||||
|
||||
4
dist/build/macos.sh
vendored
4
dist/build/macos.sh
vendored
@@ -81,7 +81,7 @@ fi
|
||||
# Build release (x86_64)
|
||||
X86_TARGET=""
|
||||
X86_TARGET_DIR=""
|
||||
if [ "$ARCH" == "aarch64" ]; then
|
||||
if [ "$ARCH" = "aarch64" ]; then
|
||||
X86_TARGET="--target x86_64-apple-darwin"
|
||||
X86_TARGET_DIR="target/x86_64-apple-darwin/release/"
|
||||
fi
|
||||
@@ -92,7 +92,7 @@ RET_X86_64=$?
|
||||
|
||||
ARM64_TARGET=""
|
||||
ARM64_TARGET_DIR=""
|
||||
if [ "$ARCH" == "aarch64" ]; then
|
||||
if [ "$ARCH" = "aarch64" ]; then
|
||||
ARM64_TARGET="--target aarch64-apple-darwin"
|
||||
ARM64_TARGET_DIR="target/aarch64-apple-darwin/release/"
|
||||
fi
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
|
||||
<p align="center">~ Eine funktionsreiche Terminal-Dateiübertragung ~</p>
|
||||
<p align="center">
|
||||
<a href="https://termscp.veeso.dev/termscp/" target="_blank">Webseite</a>
|
||||
<a href="https://termscp.veeso.dev" target="_blank">Webseite</a>
|
||||
·
|
||||
<a href="https://termscp.veeso.dev/termscp/#get-started" target="_blank">Installation</a>
|
||||
<a href="https://termscp.veeso.dev/#get-started" target="_blank">Installation</a>
|
||||
·
|
||||
<a href="https://termscp.veeso.dev/termscp/#user-manual" target="_blank">Benutzerhandbuch</a>
|
||||
<a href="https://termscp.veeso.dev/#user-manual" target="_blank">Benutzerhandbuch</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
@@ -62,8 +62,8 @@
|
||||
/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">Entwickelt von <a href="https://termscp.veeso.dev/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Aktuelle Version: 0.12.0 (16/05/2023)</p>
|
||||
<p align="center">Entwickelt von <a href="https://veeso.dev/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Aktuelle Version: 0.12.2 (01/10/2023)</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://opensource.org/licenses/MIT"
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
|
||||
<p align="center">~ Una transferencia de archivos de terminal rica en funciones ~</p>
|
||||
<p align="center">
|
||||
<a href="https://termscp.veeso.dev/termscp/" target="_blank">Sitio Web</a>
|
||||
<a href="https://termscp.veeso.dev" target="_blank">Sitio Web</a>
|
||||
·
|
||||
<a href="https://termscp.veeso.dev/termscp/#get-started" target="_blank">Instalación</a>
|
||||
<a href="https://termscp.veeso.dev/#get-started" target="_blank">Instalación</a>
|
||||
·
|
||||
<a href="https://termscp.veeso.dev/termscp/#user-manual" target="_blank">Manual de usuario</a>
|
||||
<a href="https://termscp.veeso.dev/#user-manual" target="_blank">Manual de usuario</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
@@ -62,8 +62,8 @@
|
||||
/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">Desarrollado por <a href="https://termscp.veeso.dev/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Versión actual: 0.12.0 (16/05/2023)</p>
|
||||
<p align="center">Desarrollado por <a href="https://veeso.dev/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Versión actual: 0.12.2 (01/10/2023)</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://opensource.org/licenses/MIT"
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
|
||||
<p align="center">~ Un file transfer de terminal riche en fonctionnalités ~</p>
|
||||
<p align="center">
|
||||
<a href="https://termscp.veeso.dev/termscp/" target="_blank">Site internet</a>
|
||||
<a href="https://termscp.veeso.dev" target="_blank">Site internet</a>
|
||||
·
|
||||
<a href="https://termscp.veeso.dev/termscp/#get-started" target="_blank">Installation</a>
|
||||
<a href="https://termscp.veeso.dev/#get-started" target="_blank">Installation</a>
|
||||
·
|
||||
<a href="https://termscp.veeso.dev/termscp/#user-manual" target="_blank">Manuel de l'Utilisateur</a>
|
||||
<a href="https://termscp.veeso.dev/#user-manual" target="_blank">Manuel de l'Utilisateur</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
@@ -62,8 +62,8 @@
|
||||
/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">Développé par <a href="https://termscp.veeso.dev/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Version actuelle: 0.12.0 (16/05/2023)</p>
|
||||
<p align="center">Développé par <a href="https://veeso.dev/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Version actuelle: 0.12.2 (01/10/2023)</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://opensource.org/licenses/MIT"
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
|
||||
<p align="center">~ Un file transfer ricco di funzionalità ~</p>
|
||||
<p align="center">
|
||||
<a href="https://termscp.veeso.dev/termscp/" target="_blank">Sito</a>
|
||||
<a href="https://termscp.veeso.dev" target="_blank">Sito</a>
|
||||
·
|
||||
<a href="https://termscp.veeso.dev/termscp/#get-started" target="_blank">Installazione</a>
|
||||
<a href="https://termscp.veeso.dev/#get-started" target="_blank">Installazione</a>
|
||||
·
|
||||
<a href="https://termscp.veeso.dev/termscp/#user-manual" target="_blank">Manuale utente</a>
|
||||
<a href="https://termscp.veeso.dev/#user-manual" target="_blank">Manuale utente</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
@@ -62,8 +62,8 @@
|
||||
/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">Sviluppato da <a href="https://termscp.veeso.dev/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Versione corrente: 0.12.0 (16/05/2023)</p>
|
||||
<p align="center">Sviluppato da <a href="https://veeso.dev/" target="_blank">@veeso</a></p>
|
||||
<p align="center">Versione corrente: 0.12.2 (01/10/2023)</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://opensource.org/licenses/MIT"
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
|
||||
<p align="center">~ 功能丰富的终端文件传输工具 ~</p>
|
||||
<p align="center">
|
||||
<a href="https://termscp.veeso.dev/termscp/" target="_blank">网站</a>
|
||||
<a href="https://termscp.veeso.dev" target="_blank">网站</a>
|
||||
·
|
||||
<a href="https://termscp.veeso.dev/termscp/#get-started" target="_blank">安装</a>
|
||||
<a href="https://termscp.veeso.dev/#get-started" target="_blank">安装</a>
|
||||
·
|
||||
<a href="https://termscp.veeso.dev/termscp/#user-manual" target="_blank">用户手册</a>
|
||||
<a href="https://termscp.veeso.dev/#user-manual" target="_blank">用户手册</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
@@ -62,8 +62,8 @@
|
||||
/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">由 <a href="https://termscp.veeso.dev/" target="_blank">@veeso</a> 开发</p>
|
||||
<p align="center">当前版本: 0.12.0 (16/05/2023)</p>
|
||||
<p align="center">由 <a href="https://veeso.dev/" target="_blank">@veeso</a> 开发</p>
|
||||
<p align="center">当前版本: 0.12.2 (01/10/2023)</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://opensource.org/licenses/MIT"
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# -f, -y, --force, --yes
|
||||
# Skip the confirmation prompt during installation
|
||||
|
||||
TERMSCP_VERSION="0.12.0"
|
||||
TERMSCP_VERSION="0.12.2"
|
||||
GITHUB_URL="https://github.com/veeso/termscp/releases/download/v${TERMSCP_VERSION}"
|
||||
DEB_URL_AMD64="${GITHUB_URL}/termscp_${TERMSCP_VERSION}_amd64.deb"
|
||||
DEB_URL_AARCH64="${GITHUB_URL}/termscp_${TERMSCP_VERSION}_arm64.deb"
|
||||
@@ -476,8 +476,8 @@ case $PLATFORM in
|
||||
esac
|
||||
|
||||
completed "Congratulations! Termscp has successfully been installed on your system!"
|
||||
info "If you're a new user, you might be interested in reading the user manual <https://termscp.veeso.dev/termscp/#user-manual>"
|
||||
info "While if you've just updated your termscp version, you can find the changelog at this link <https://termscp.veeso.dev/termscp/#changelog>"
|
||||
info "If you're a new user, you might be interested in reading the user manual <https://termscp.veeso.dev/#user-manual>"
|
||||
info "While if you've just updated your termscp version, you can find the changelog at this link <https://termscp.veeso.dev/#changelog>"
|
||||
info "Remember that if you encounter any issue, you can report them on Github <https://github.com/veeso/termscp/issues/new>"
|
||||
info "Feel free to open an issue also if you have an idea which could improve the project"
|
||||
info "If you want to support the project, please, consider a little donation <https://ko-fi.com/veeso>"
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 77 KiB |
74
site/changelog.html
Normal file
74
site/changelog.html
Normal file
@@ -0,0 +1,74 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en_US" class="dark">
|
||||
|
||||
<head>
|
||||
<title>
|
||||
termscp is a terminal file transfer and explorer for SCP/SFTP/FTP/S3/SMB | termscp
|
||||
</title>
|
||||
<meta property="og:description"
|
||||
content="termscp is a feature rich terminal file transfer and explorer, with support for SCP/SFTP/FTP/S3. It is Linux, MacOS, FreeBSD, NetBSD and Windows compatible" />
|
||||
<meta name="description"
|
||||
content="termscp is a feature rich terminal file transfer and explorer, with support for SCP/SFTP/FTP/S3. It is Linux, MacOS, FreeBSD, NetBSD and Windows compatible" />
|
||||
<meta property="og:title" content="termscp is a terminal file transfer and explorer for SCP/SFTP/FTP/S3 | termscp" />
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<meta property="og:image" content="https://termscp.veeso.dev/assets/images/og_preview.jpg" />
|
||||
<meta property="og:image:type" content="image/jpg" />
|
||||
<meta property="og:image:width" content="1280" />
|
||||
<meta property="og:image:height" content="640" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="termscp" />
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta property="og:url" content="https://termscp.veeso.dev" />
|
||||
<!-- Icons -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.11.0/devicon.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"
|
||||
integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w=="
|
||||
crossorigin="anonymous" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/fontawesome.min.css"
|
||||
integrity="sha512-OdEXQYCOldjqUEsuMKsZRj93Ht23QRlhIb8E/X0sbwZhme8eUw6g8q7AdxGJKakcBbv7+/PX0Gc2btf7Ru8cZA=="
|
||||
crossorigin="anonymous" />
|
||||
<!-- tailwind -->
|
||||
<link href="output.css" rel="stylesheet" />
|
||||
<!-- Favicons -->
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="assets/images/favicon-96x96.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/images/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/images/favicon-16x16.png" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="layout" class="dark:bg-brand dark:text-gray-100">
|
||||
<!-- Menu -->
|
||||
<header id="menu"></header>
|
||||
<main>
|
||||
<div id="main" class="w-8/12 sm:w-full mx-auto pt-4 pb-8"></div>
|
||||
</main>
|
||||
<footer id="footer"></footer>
|
||||
</div>
|
||||
<!-- Scripts -->
|
||||
<!-- jQuery -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||
<!-- Showdown JS-->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.9.1/showdown.min.js"
|
||||
integrity="sha512-L03kznCrNOfVxOUovR6ESfCz9Gfny7gihUX/huVbQB9zjODtYpxaVtIaAkpetoiyV2eqWbvxMH9fiSv5enX7bw=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<!-- Local -->
|
||||
<script src="js/lang.min.js"></script>
|
||||
<script src="js/core.js"></script>
|
||||
<script src="js/events.js"></script>
|
||||
<script src="js/resolvers.js"></script>
|
||||
<!-- ko-fi -->
|
||||
<script src="https://storage.ko-fi.com/cdn/scripts/overlay-widget.js"></script>
|
||||
<script>
|
||||
kofiWidgetOverlay.draw("veeso", {
|
||||
type: "floating-chat",
|
||||
"floating-chat.donateButton.text": "Support me",
|
||||
"floating-chat.donateButton.background-color": "#31363b",
|
||||
"floating-chat.donateButton.text-color": "#fff",
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,45 +0,0 @@
|
||||
.start h1 {
|
||||
font-size: 2.5em;
|
||||
}
|
||||
|
||||
.start h2 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.start h3 {
|
||||
font-size: 1.6em;
|
||||
}
|
||||
|
||||
.start h2 i,
|
||||
h3 i {
|
||||
color: #606060;
|
||||
}
|
||||
|
||||
.start .sub-system {
|
||||
padding: 0px 2em;
|
||||
}
|
||||
|
||||
.start .sub-system .installation {
|
||||
padding: 0px 2em;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.start .installation {
|
||||
padding: 2px 4em;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.start .installation p {
|
||||
font-size: 1.3em;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.start .installation li {
|
||||
font-size: 1.1em;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.start .installation a {
|
||||
text-decoration: none;
|
||||
color: dodgerblue;
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
/** Intro */
|
||||
|
||||
.intro {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.intro .title {
|
||||
font-size: 5em;
|
||||
}
|
||||
|
||||
.intro .logo {
|
||||
border-radius: 25%;
|
||||
height: auto;
|
||||
width: 256px;
|
||||
}
|
||||
|
||||
.intro .caption {
|
||||
font-size: 2em;
|
||||
font-weight: 300;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.intro .get-started {
|
||||
background-color: #404040;
|
||||
border-radius: 0.5em;
|
||||
color: snow;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.intro .get-started a {
|
||||
color: snow;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.intro .features {
|
||||
align-content: stretch;
|
||||
align-items: flex-start;
|
||||
border-top: 1px solid #eee;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
margin-top: 2.5rem;
|
||||
padding: 1.2rem 0;
|
||||
}
|
||||
|
||||
.intro .feature {
|
||||
flex: auto;
|
||||
}
|
||||
|
||||
@media (min-width: 600px) {
|
||||
.intro .feature {
|
||||
flex-grow: 1;
|
||||
flex-basis: 30%;
|
||||
max-width: 30%;
|
||||
}
|
||||
}
|
||||
|
||||
.intro .feature h3 {
|
||||
color: #101010;
|
||||
font-size: 1.5em;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.intro .feature p {
|
||||
color: #303030;
|
||||
}
|
||||
|
||||
.intro .preview {
|
||||
border-radius: 5px;
|
||||
padding: 5% 10%;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.intro .preview video {
|
||||
height: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.intro .discover {
|
||||
align-content: stretch;
|
||||
align-items: flex-start;
|
||||
border-top: 1px solid #eee;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
margin-left: 0;
|
||||
margin-top: 2.5rem;
|
||||
padding: 1.2rem 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (min-width: 600px) {
|
||||
.intro .discover {
|
||||
margin-left: 20%;
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
|
||||
.intro .discover .hook {
|
||||
flex-grow: 1;
|
||||
flex-basis: 30%;
|
||||
font-size: 1.5em;
|
||||
max-width: 30%;
|
||||
}
|
||||
|
||||
.intro .discover .hook a {
|
||||
text-decoration: none;
|
||||
color: #404040;
|
||||
}
|
||||
|
||||
.intro .discover .hook a i {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.intro .discover .hook a {
|
||||
transition: all 0.4s ease-in;
|
||||
}
|
||||
|
||||
.intro .discover .hook a:hover {
|
||||
color: dodgerblue;
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
|
||||
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
padding: 16px;
|
||||
overflow: auto;
|
||||
font-size: 85%;
|
||||
line-height: 1.45;
|
||||
color: #d0d0d0;
|
||||
background-color: #222629;
|
||||
border-radius: 3px;
|
||||
word-wrap: normal;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
|
||||
pre .function {
|
||||
color: #f08d49;
|
||||
}
|
||||
|
||||
pre .string {
|
||||
color: #7ec699;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5 {
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
header {
|
||||
margin: 0 2em 0 0;
|
||||
}
|
||||
|
||||
.img-circle {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.pull-left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.pull-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
section.page {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-top: 1px solid #aaa;
|
||||
display: inline-block;
|
||||
padding-bottom: 2em;
|
||||
}
|
||||
|
||||
section.page hr {
|
||||
margin: 1em 0 1em 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 90%;
|
||||
height: auto;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.container header a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
border-bottom: dotted 1px #80808080;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.container header a i {
|
||||
color: #606060;
|
||||
}
|
||||
|
||||
/** Footer */
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
background-color: #eee;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
footer .contacts {
|
||||
list-style: none;
|
||||
cursor: default;
|
||||
margin-block-start: 0;
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
|
||||
footer .contacts li {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
footer .contacts a {
|
||||
border: 0;
|
||||
font-size: 3em;
|
||||
color: #606060;
|
||||
display: inline-block;
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
line-height: 2em;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
footer .contacts a i {
|
||||
transition: all 0.4s ease-in;
|
||||
}
|
||||
|
||||
footer .contacts li a:hover i {
|
||||
color: #202020 !important;
|
||||
}
|
||||
|
||||
.footer--vat {
|
||||
color: #404040;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer--address {
|
||||
color: #505050;
|
||||
font-size: 0.8em;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer--copyright {
|
||||
color: #606060;
|
||||
font-size: 0.7em;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer--link {
|
||||
color: #606060;
|
||||
}
|
||||
|
||||
.alert {
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0.25rem;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
padding: 0.5rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.alert-center {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
background-color: #fff3cd;
|
||||
border-color: #ffeeba;
|
||||
color: #856404;
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
background-color: #f8d7da;
|
||||
border-color: #f5c6cb;
|
||||
color: #721c24;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background-color: #d4edda;
|
||||
border-color: #c3e6cb;
|
||||
color: #155724;
|
||||
}
|
||||
|
||||
.alert-primary {
|
||||
background-color: #cce5ff;
|
||||
border-color: #b8daff;
|
||||
color: #004085;
|
||||
}
|
||||
|
||||
/* ko-fi */
|
||||
.floatingchat-container-wrap {
|
||||
left: auto !important;
|
||||
right: 16px !important;
|
||||
}
|
||||
|
||||
.floating-chat-kofi-popup-iframe {
|
||||
left: auto !important;
|
||||
right: 16px !important;
|
||||
}
|
||||
@@ -12,7 +12,6 @@
|
||||
}
|
||||
|
||||
.markdown p {
|
||||
color: #202020;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
@@ -46,16 +45,11 @@
|
||||
|
||||
.markdown blockquote {
|
||||
border-left: 0.25em solid #ccc;
|
||||
color: #606060;
|
||||
font-size: 90%;
|
||||
padding: 0.1em;
|
||||
padding-left: 0.5em;
|
||||
}
|
||||
|
||||
.markdown blockquote p {
|
||||
color: #606060;
|
||||
}
|
||||
|
||||
.markdown pre code {
|
||||
background-color: inherit;
|
||||
font-size: 100%;
|
||||
@@ -68,6 +62,10 @@
|
||||
padding: 0.2em 0.4em;
|
||||
}
|
||||
|
||||
:is(.dark) .markdown code {
|
||||
background-color: #404040;
|
||||
}
|
||||
|
||||
.markdown table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
@@ -82,14 +80,6 @@
|
||||
border-top: 1px solid #c6cbd1;
|
||||
}
|
||||
|
||||
.markdown table tr {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.markdown table tr:nth-child(even) {
|
||||
background-color: #dfe2e5;
|
||||
}
|
||||
|
||||
.markdown table td,
|
||||
.markdown table th {
|
||||
border: 1px solid #c6cbd1;
|
||||
|
||||
@@ -1,268 +0,0 @@
|
||||
/** Menu */
|
||||
|
||||
#menu {
|
||||
margin-left: -20vw; /* "#menu" width */
|
||||
width: 20vw;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
z-index: 1000; /* so the menu or its navicon stays above all content */
|
||||
background: #f0f0f0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
/*
|
||||
All anchors inside the menu should be styled like this.
|
||||
*/
|
||||
#menu a {
|
||||
color: #606060;
|
||||
border: none;
|
||||
padding: 0.6em 0 0.6em 0.6em;
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
||||
/*
|
||||
Remove all background/borders, since we are applying them to #menu.
|
||||
*/
|
||||
#menu .pure-menu,
|
||||
#menu .pure-menu ul {
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/*
|
||||
Add that light border to separate items into groups.
|
||||
*/
|
||||
#menu .pure-menu ul,
|
||||
#menu .pure-menu .menu-item-divided {
|
||||
border-top: 1px solid #808080;
|
||||
}
|
||||
|
||||
#menu .pure-menu i {
|
||||
margin-right: 1ch;
|
||||
}
|
||||
|
||||
#menu .pure-menu-item i {
|
||||
font-size: 0.6em;
|
||||
}
|
||||
|
||||
/*
|
||||
Change color of the anchor links on hover/focus.
|
||||
*/
|
||||
#menu .pure-menu li a:hover,
|
||||
#menu .pure-menu li a:focus {
|
||||
background: #ccc;
|
||||
}
|
||||
|
||||
/*
|
||||
This styles the selected menu item `<li>`.
|
||||
*/
|
||||
#menu .pure-menu-selected {
|
||||
background: #ddd;
|
||||
}
|
||||
|
||||
#menu .pure-menu-selected i {
|
||||
color: dodgerblue;
|
||||
}
|
||||
|
||||
/*
|
||||
This styles a link within a selected menu item `<li>`.
|
||||
*/
|
||||
#menu .pure-menu-selected a {
|
||||
color: dodgerblue;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/*
|
||||
This styles the menu heading.
|
||||
*/
|
||||
#menu .pure-menu-heading {
|
||||
color: #202020;
|
||||
margin: 0;
|
||||
margin: 10% 5% 10% 5%;
|
||||
position: relative;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
#menu .pure-menu-heading .avatar {
|
||||
width: 30%;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
|
||||
#menu .pure-menu-heading h1 {
|
||||
font-size: 1.4em;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
#menu .pure-menu-heading p {
|
||||
color: #404040;
|
||||
font-size: 1.1em;
|
||||
font-weight: 300;
|
||||
text-transform: none;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
#menu .pure-menu-bottom {
|
||||
bottom: 0;
|
||||
display: none;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
#menu .pure-menu-bottom {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
#menu .pure-menu-bottom a {
|
||||
font-size: 1.5em;
|
||||
color: #606060;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
border: 0;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
#menu .pure-menu-bottom a:hover {
|
||||
color: #404040;
|
||||
}
|
||||
|
||||
#menu .pure-menu-bottom ul {
|
||||
list-style: none;
|
||||
cursor: default;
|
||||
margin-block-start: 0;
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
|
||||
#menu .pure-menu-bottom ul li {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* -- Dynamic Button For Responsive Menu -------------------------------------*/
|
||||
|
||||
/*
|
||||
The button to open/close the Menu is custom-made and not part of Pure. Here's
|
||||
how it works:
|
||||
*/
|
||||
|
||||
/*
|
||||
`.menu-link` represents the responsive menu toggle that shows/hides on
|
||||
small screens.
|
||||
*/
|
||||
.menu-link {
|
||||
position: fixed;
|
||||
display: block; /* show this only on small screens */
|
||||
top: 0;
|
||||
left: 0; /* "#menu width" */
|
||||
background: #eee;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
font-size: 10px; /* change this value to increase/decrease button size */
|
||||
z-index: 10;
|
||||
width: 2em;
|
||||
height: auto;
|
||||
padding: 2.1em 1.6em;
|
||||
}
|
||||
|
||||
.menu-link:hover,
|
||||
.menu-link:focus {
|
||||
background: #202020;
|
||||
}
|
||||
|
||||
.menu-link span {
|
||||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.menu-link span,
|
||||
.menu-link span:before,
|
||||
.menu-link span:after {
|
||||
background-color: #fff;
|
||||
pointer-events: none;
|
||||
width: 100%;
|
||||
height: 0.2em;
|
||||
}
|
||||
|
||||
.menu-link span:before,
|
||||
.menu-link span:after {
|
||||
position: absolute;
|
||||
margin-top: -0.6em;
|
||||
content: " ";
|
||||
}
|
||||
|
||||
.menu-link span:after {
|
||||
margin-top: 0.6em;
|
||||
}
|
||||
|
||||
/* -- Responsive Styles (Media Queries) ------------------------------------- */
|
||||
|
||||
#layout,
|
||||
#menu,
|
||||
.menu-link {
|
||||
-webkit-transition: all 0.2s ease-out;
|
||||
-moz-transition: all 0.2s ease-out;
|
||||
-ms-transition: all 0.2s ease-out;
|
||||
-o-transition: all 0.2s ease-out;
|
||||
transition: all 0.2s ease-out;
|
||||
}
|
||||
|
||||
#layout {
|
||||
position: relative;
|
||||
left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
#layout.active #menu {
|
||||
left: 20vw;
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
#layout.active .menu-link {
|
||||
left: 240px;
|
||||
}
|
||||
|
||||
/*
|
||||
Hides the menu at `640px`, but modify this based on your app's needs.
|
||||
*/
|
||||
@media (min-width: 640px) {
|
||||
.header {
|
||||
padding-left: 2em;
|
||||
padding-right: 2em;
|
||||
}
|
||||
|
||||
#layout {
|
||||
padding-left: 20vw; /* left col width "#menu" */
|
||||
left: 0;
|
||||
}
|
||||
#menu {
|
||||
left: 20vw;
|
||||
}
|
||||
|
||||
.menu-link {
|
||||
position: fixed;
|
||||
left: 20vw;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#layout.active .menu-link {
|
||||
left: 20vw;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
/* Only apply this when the window is small. Otherwise, the following
|
||||
case results in extra padding on the left:
|
||||
* Make the window small.
|
||||
* Tap the menu to trigger the active state.
|
||||
* Make the window large again.
|
||||
*/
|
||||
#main.active {
|
||||
position: relative;
|
||||
left: 20vw;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
.updates h1 {
|
||||
font-size: 2.5em;
|
||||
}
|
||||
|
||||
.updates h2 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.updates h3 {
|
||||
font-size: 1.6em;
|
||||
}
|
||||
|
||||
.updates h2 i,
|
||||
h3 i {
|
||||
color: #606060;
|
||||
}
|
||||
|
||||
.updates .desc {
|
||||
font-size: 1.1em;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.updates ol {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.wall-of-text {
|
||||
color: #444;
|
||||
line-height: 1.8em;
|
||||
margin: 0 auto;
|
||||
text-align: justify;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.tl-dr {
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
}
|
||||
75
site/get-started.html
Normal file
75
site/get-started.html
Normal file
@@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en_US" class="dark">
|
||||
|
||||
<head>
|
||||
<title>
|
||||
get started with termscp | termscp
|
||||
</title>
|
||||
<meta property="og:description"
|
||||
content="Install termscp within a couple of minutes with the shell script" />
|
||||
<meta name="description"
|
||||
content="Install termscp within a couple of minutes with the shell script" />
|
||||
<meta property="og:title" content="get started with termscp | termscp" />
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<meta property="og:image" content="https://termscp.veeso.dev/assets/images/og_preview.jpg" />
|
||||
<meta property="og:image:type" content="image/jpg" />
|
||||
<meta property="og:image:width" content="1280" />
|
||||
<meta property="og:image:height" content="640" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="termscp" />
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta property="og:url" content="https://termscp.veeso.dev" />
|
||||
<!-- Icons -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.11.0/devicon.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"
|
||||
integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w=="
|
||||
crossorigin="anonymous" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/fontawesome.min.css"
|
||||
integrity="sha512-OdEXQYCOldjqUEsuMKsZRj93Ht23QRlhIb8E/X0sbwZhme8eUw6g8q7AdxGJKakcBbv7+/PX0Gc2btf7Ru8cZA=="
|
||||
crossorigin="anonymous" />
|
||||
<!-- tailwind -->
|
||||
<link href="output.css" rel="stylesheet" />
|
||||
<!-- Favicons -->
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="assets/images/favicon-96x96.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/images/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/images/favicon-16x16.png" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="layout" class="dark:bg-brand dark:text-gray-100">
|
||||
|
||||
<!-- Menu -->
|
||||
<header id="menu"></header>
|
||||
<main>
|
||||
<div id="main" class="w-8/12 sm:w-full mx-auto pt-4 pb-8"></div>
|
||||
</main>
|
||||
<footer id="footer"></footer>
|
||||
</div>
|
||||
<!-- Scripts -->
|
||||
<!-- jQuery -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||
<!-- Showdown JS-->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.9.1/showdown.min.js"
|
||||
integrity="sha512-L03kznCrNOfVxOUovR6ESfCz9Gfny7gihUX/huVbQB9zjODtYpxaVtIaAkpetoiyV2eqWbvxMH9fiSv5enX7bw=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<!-- Local -->
|
||||
<script src="js/lang.min.js"></script>
|
||||
<script src="js/core.js"></script>
|
||||
<script src="js/events.js"></script>
|
||||
<script src="js/resolvers.js"></script>
|
||||
<!-- ko-fi -->
|
||||
<script src="https://storage.ko-fi.com/cdn/scripts/overlay-widget.js"></script>
|
||||
<script>
|
||||
kofiWidgetOverlay.draw("veeso", {
|
||||
type: "floating-chat",
|
||||
"floating-chat.donateButton.text": "Support me",
|
||||
"floating-chat.donateButton.background-color": "#31363b",
|
||||
"floating-chat.donateButton.text-color": "#fff",
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
38
site/html/components/footer.html
Normal file
38
site/html/components/footer.html
Normal file
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<div class="flex flex-col bg-brand text-gray-200 justify-around items-center gap-12 p-8 sm:pb-24">
|
||||
<div class="flex flex-row justify-around items-center gap-16 w-3/6 sm:w-full">
|
||||
<div>
|
||||
<a href="https://github.com/veeso/termscp" class="text-3xl sm:text-xl no-underline" target="_blank"><i
|
||||
class="devicon-github-original"></i></a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://crates.io/crates/termscp" class="text-3xl sm:text-xl no-underline" target="_blank"><i
|
||||
class="devicon-rust-plain"></i></a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://community.chocolatey.org/packages/termscp" class="text-3xl sm:text-xl no-underline"
|
||||
target="_blank"><i class="devicon-windows8-plain"></i></a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://ko-fi.com/veeso" target="_blank" class="text-3xl sm:text-xl no-underline"><i
|
||||
class="fa fa-coffee"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col justify-around gap-1 items-center w-full">
|
||||
<!-- vat number -->
|
||||
<p class="text-sm font-thin">
|
||||
<span>P.IVA IT03104140300 </span>
|
||||
</p>
|
||||
<p class="text-xs font-thin">
|
||||
<span>Via Antonio Marangoni 33, 33100, Udine (UD)</span>
|
||||
</p>
|
||||
<!-- Copyright -->
|
||||
<p class="text-xs font-thin">
|
||||
<span>Christian Visintin © </span><span resolve-copyright></span>
|
||||
<span> | </span>
|
||||
<a class="text-xs font-thin" href="https://veeso.dev/en/privacy" target="_blank">Privacy policy</a>
|
||||
<span> | </span>
|
||||
<a class="text-xs font-thin" href="https://veeso.dev/en/cookie-policy" target="_blank">Cookie policy</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
97
site/html/components/menu.html
Normal file
97
site/html/components/menu.html
Normal file
@@ -0,0 +1,97 @@
|
||||
<!DOCTYPE html>
|
||||
<div
|
||||
class="flex flex-row fixed bg-gray-100 dark:bg-brand text-brand dark:text-gray-100 border-b border-gray-400 dark:border-gray-800 items-center justify-between w-screen h-auto py-4 px-8">
|
||||
<!-- Desktop menu-->
|
||||
<div class="sm:hidden flex flex-row justify-start items-center gap-12 w-full">
|
||||
<h2 class="lg:text-xl md:text-lg dark:text-gray-100">
|
||||
<a href="/">
|
||||
<img class="w-[32px] h-auto m-auto inline-block mr-2 hover:underline" alt="logo"
|
||||
src="assets/images/termscp.webp" /> termscp
|
||||
</a>
|
||||
</h2>
|
||||
<h2 class="lg:text-xl md:text-lg dark:text-gray-100 hover:underline">
|
||||
<a translate="menu.getStarted" href="/get-started.html"></a>
|
||||
</h2>
|
||||
<h2 class="lg:text-xl md:text-lg dark:text-gray-100 hover:underline">
|
||||
<a translate="menu.updates" href="/updates.html"></a>
|
||||
</h2>
|
||||
<h2 class="lg:text-xl md:text-lg dark:text-gray-100 hover:underline">
|
||||
<a translate="menu.manual" href="/user-manual.html"></a>
|
||||
</h2>
|
||||
<h2 class="lg:text-xl md:text-lg dark:text-gray-100 hover:underline">
|
||||
<a translate="menu.changelog" href="/changelog.html"></a>
|
||||
</h2>
|
||||
<!-- End region -->
|
||||
<div class="flex flex-row self-end justify-end items-center gap-8 flex-1">
|
||||
<!-- Dark/light theme toggle -->
|
||||
<button id="theme-toggle" type="button" onclick="toggleTheme()"
|
||||
class="text-brand dark:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg lg:text-2xl md:text-lg p-2.5">
|
||||
<svg id="theme-toggle-dark-icon" class="w-5 h-5 hidden" fill="currentColor" viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path>
|
||||
</svg>
|
||||
<svg id="theme-toggle-light-icon" class="w-5 h-5 hidden" fill="currentColor" viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
|
||||
fill-rule="evenodd" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<!-- Socials -->
|
||||
<div>
|
||||
<a href="https://github.com/veeso/termscp" class="lg:text-2xl md:text-lg dark:text-gray-100" target="_blank"><i
|
||||
class="devicon-github-original"></i></a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://crates.io/crates/termscp" class="lg:text-2xl md:text-lg dark:text-gray-100" target="_blank"><i
|
||||
class="devicon-rust-plain"></i></a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://www.linkedin.com/in/christian-visintin/" class="lg:text-2xl md:text-lg dark:text-gray-100"
|
||||
target="_blank"><i class="devicon-linkedin-plain"></i></a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://twitter.com/veeso_dev" target="_blank" class="lg:text-2xl md:text-lg dark:text-gray-100"><i
|
||||
class="devicon-twitter-plain"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Mobile topbar -->
|
||||
<div class="sm:flex hidden flex-row justify-start items-center gap-8 w-full">
|
||||
<h2 class="text-2xl dark:text-gray-100">
|
||||
<a href="/">
|
||||
<img class="w-[32px] h-auto m-auto inline-block mr-2" alt="logo"
|
||||
src="assets/images/termscp.webp" /> termscp
|
||||
</a>
|
||||
</h2>
|
||||
<div id="menu-icon" class="justify-self-end flex-1 flex justify-end">
|
||||
<i onclick="onToggleMenu()" class="fa fa-bars text-2xl cursor-pointer"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Mobile menu -->
|
||||
<div id="mobile-menu"
|
||||
class="hidden transition-all fixed top-0 left-0 h-screen bg-brand text-white w-screen flex-col justify-start items-center p-8">
|
||||
<div class="flex flex-row justify-end items-end w-full">
|
||||
<div id="menu-icon" class="justify-self-end flex-1 flex justify-end">
|
||||
<i onclick="onToggleMenu()" class="fa fa-bars text-2xl cursor-pointer"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col justify-start items-center w-full gap-4 hover:underline">
|
||||
<h2 class="text-2xl text-white">
|
||||
<a translate="menu.intro" href="/"></a>
|
||||
</h2>
|
||||
<h2 class="text-2xl text-white hover:underline">
|
||||
<a translate="menu.getStarted" href="/get-started.html"></a>
|
||||
</h2>
|
||||
<h2 class="text-2xl text-white hover:underline">
|
||||
<a translate="menu.updates" href="/updates.html"></a>
|
||||
</h2>
|
||||
<h2 class="text-2xl text-white hover:underline">
|
||||
<a translate="menu.manual" href="/user-manual.html"></a>
|
||||
</h2>
|
||||
<h2 class="text-2xl text-white hover:underline">
|
||||
<a translate="menu.changelog" href="/changelog.html"></a>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,17 +1,15 @@
|
||||
<head>
|
||||
<link rel="stylesheet" href="css/get-started.css" />
|
||||
</head>
|
||||
<!DOCTYPE html>
|
||||
|
||||
<body>
|
||||
<section id="start" class="container start">
|
||||
<h1 translate="getStarted.title">Get started</h1>
|
||||
<section>
|
||||
<h2>
|
||||
<section id="start" class="flex flex-col mx-auto items-center justify-center w-full px-12 gap-8 dark:text-gray-100">
|
||||
<h1 translate="getStarted.title" class="text-3xl font-thin">Get started</h1>
|
||||
<section class="w-full">
|
||||
<h2 class="text-2xl font-thin">
|
||||
<i class="fa fa-rocket"></i> <span translate="getStarted.quickSetup">Quick setup</span>
|
||||
</h2>
|
||||
<div class="installation">
|
||||
<div class="alert alert-primary">
|
||||
<p>
|
||||
<div class="p-4 my-4 text-sm text-blue-800 rounded-lg bg-blue-50">
|
||||
<p class="text-lg">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
<span translate="getStarted.suggested">We strongly suggest this method to install termscp</span>
|
||||
</p>
|
||||
@@ -23,8 +21,8 @@
|
||||
<pre><span class="function">curl</span> --proto <span class="string">'=https'</span> --tlsv1.2 -sSLf <span class="string">"https://git.io/JBhDb"</span> | sh</pre>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h2>
|
||||
<section class="w-full">
|
||||
<h2 class="text-2xl font-thin">
|
||||
<i class="devicon-windows8-plain"></i> <span translate="getStarted.windows.title">Windows users</span>
|
||||
</h2>
|
||||
<div class="installation">
|
||||
@@ -37,20 +35,20 @@
|
||||
<span translate="getStarted.windows.moderation">Consider that Chocolatey moderation can take up to a few weeks
|
||||
since last release, so if the latest version is not available yet,
|
||||
you can install it downloading the ZIP file from</span>
|
||||
<a href="https://github.com/veeso/termscp/releases/latest/download/termscp.0.12.0.nupkg"
|
||||
<a href="https://github.com/veeso/termscp/releases/latest/download/termscp.0.12.2.nupkg"
|
||||
target="_blank">Github</a>
|
||||
<span translate="getStarted.windows.then">and then, from the ZIP directory, install it via</span>
|
||||
</p>
|
||||
<pre><span class="function">choco</span> install <span class="string">termscp</span> -s .</pre>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h2>
|
||||
<section class="w-full">
|
||||
<h2 class="text-2xl font-thin">
|
||||
<i class="devicon-linux-plain"></i> <span translate="getStarted.linuxUsers">Linux users</span>
|
||||
</h2>
|
||||
<div class="sub-system">
|
||||
<div class="alert alert-warning">
|
||||
<p>
|
||||
<div class="p-4 my-4 text-sm text-yellow-800 rounded-lg bg-yellow-50">
|
||||
<p class="text-lg">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<span translate="getStarted.notConfident">Opt for these methods instead if you don't feel confident using
|
||||
the shell script</span>
|
||||
@@ -77,7 +75,7 @@
|
||||
On Debian based distros, you can install termscp using the Deb
|
||||
package via:
|
||||
</p>
|
||||
<pre><span class="function">wget</span> -O termscp.deb <span class="string">https://github.com/veeso/termscp/releases/latest/download/termscp_0.12.0_amd64.deb</span>
|
||||
<pre><span class="function">wget</span> -O termscp.deb <span class="string">https://github.com/veeso/termscp/releases/latest/download/termscp_0.12.2_amd64.deb</span>
|
||||
sudo <span class="function">dpkg</span> -i <span class="string">termscp.deb</span></pre>
|
||||
</div>
|
||||
<h3>
|
||||
@@ -89,7 +87,7 @@ sudo <span class="function">dpkg</span> -i <span class="string">termscp.deb</spa
|
||||
On RedHat based distros, you can install termscp using the RPM
|
||||
package via:
|
||||
</p>
|
||||
<pre><span class="function">wget</span> -O termscp.rpm <span class="string">https://github.com/veeso/termscp/releases/latest/download/termscp-0.12.0-1.x86_64.rpm</span>
|
||||
<pre><span class="function">wget</span> -O termscp.rpm <span class="string">https://github.com/veeso/termscp/releases/latest/download/termscp-0.12.2-1.x86_64.rpm</span>
|
||||
sudo <span class="function">rpm</span> -U <span class="string">termscp.rpm</span></pre>
|
||||
</div>
|
||||
<h3>
|
||||
@@ -104,16 +102,16 @@ sudo <span class="function">rpm</span> -U <span class="string">termscp.rpm</span
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h2>
|
||||
<section class="w-full">
|
||||
<h2 class="text-2xl font-thin">
|
||||
<i class="devicon-apple-plain"></i> <span translate="getStarted.macos.title">MacOS users</span>
|
||||
</h2>
|
||||
<div class="installation">
|
||||
<div class="alert alert-warning">
|
||||
<p>
|
||||
<div class="p-4 my-4 text-sm text-yellow-800 rounded-lg bg-yellow-50">
|
||||
<p class="text-lg">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<span translate="getStarted.notConfident">Opt for this method instead if you don't feel confident using the
|
||||
shell script</span>
|
||||
<span translate="getStarted.notConfident">Opt for these methods instead if you don't feel confident using
|
||||
the shell script</span>
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
@@ -123,13 +121,13 @@ sudo <span class="function">rpm</span> -U <span class="string">termscp.rpm</span
|
||||
<pre><span class="function">brew</span> install <span class="string">veeso/termscp/termscp</span></pre>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h2>
|
||||
<section class="w-full">
|
||||
<h2 class="text-2xl font-thin">
|
||||
<i class="devicon-rust-plain"></i> <span translate="getStarted.cargo.title">Install with Cargo</span>
|
||||
</h2>
|
||||
<div class="installation">
|
||||
<div class="alert alert-warning">
|
||||
<p>
|
||||
<div class="p-4 my-4 text-sm text-yellow-800 rounded-lg bg-yellow-50">
|
||||
<p class="text-lg">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<span translate="getStarted.noBinary">Opt for this method instead if binaries for your platform are not
|
||||
available</span>
|
||||
@@ -142,19 +140,18 @@ sudo <span class="function">rpm</span> -U <span class="string">termscp.rpm</span
|
||||
<span translate="getStarted.cargo.requirements">To install termscp via Cargo, these requirements must be
|
||||
satisfied:</span>
|
||||
</p>
|
||||
<ul>
|
||||
<ul class="list-disc px-8">
|
||||
<li>
|
||||
Linux:
|
||||
<ul>
|
||||
<ul class="list-disc px-12">
|
||||
<li>pkg-config</li>
|
||||
<li>libssh2</li>
|
||||
<li>openssl-dev</li>
|
||||
<li>libdbus-1</li>
|
||||
<li>libsmbclient</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
FreeBSD:
|
||||
<ul>
|
||||
<li>libssh</li>
|
||||
<ul class="list-disc px-12">
|
||||
<li>dbus</li>
|
||||
<li>pkg-conf</li>
|
||||
<li>gcc</li>
|
||||
@@ -163,11 +160,13 @@ sudo <span class="function">rpm</span> -U <span class="string">termscp.rpm</span
|
||||
</ul>
|
||||
<p translate="getStarted.cargo.install">Then you can install it via</p>
|
||||
<pre><span class="function">cargo</span> install --locked <span class="string">termscp</span></pre>
|
||||
<p translate="getStarted.cargo.noKeyring">
|
||||
<p translate="getStarted.cargo.noKeyring" class="pt-4 pb-2">
|
||||
Or if you don't want to have support for keyring or you're building on
|
||||
*BSD:
|
||||
</p>
|
||||
<pre><span class="function">cargo</span> install --locked --no-default-features <span class="string">termscp</span></pre>
|
||||
<pre><span class="function">cargo</span> install --locked --no-default-features --features smb <span class="string">termscp</span></pre>
|
||||
<p translate="getStarted.cargo.noSMB" class="pt-4 pb-2"></p>
|
||||
<pre><span class="function">cargo</span> install --locked --no-default-features --features with-keyring <span class="string">termscp</span></pre>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
83
site/html/home.html
Normal file
83
site/html/home.html
Normal file
@@ -0,0 +1,83 @@
|
||||
<!DOCTYPE html>
|
||||
<section id="intro" class="flex flex-col mx-auto items-center justify-center w-full px-4 dark:bg-brand dark:text-gray-100">
|
||||
<h1 class="text-3xl text-center font-thin">termscp</h1>
|
||||
<img class="w-[256px] h-auto m-auto" alt="logo" src="assets/images/termscp.webp" />
|
||||
<h2 class="text-xl font-thin text-center py-6" translate="intro.caption">
|
||||
A feature rich terminal UI file transfer and explorer with support for
|
||||
SCP/SFTP/FTP/S3/SMB
|
||||
</h2>
|
||||
<button class="bg-brand hover:bg-gray-800 text-white font-thin text-xl py-2 px-4 rounded-xl max-w-fit">
|
||||
<a href="/get-started.html" class="no-underline" translate="intro.getStarted">Get started →</a>
|
||||
</button>
|
||||
<div class="p-4 my-4 text-sm text-green-800 rounded-lg bg-green-50">
|
||||
<p class="text-lg">
|
||||
<span translate="intro.versionAlert">termscp 0.12.2 is NOW out! Download it from</span>
|
||||
<a href="/get-started.html" translate="intro.here">here!</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 sm:grid-cols-1 gap-8 py-8 w-4/6 sm:w-full">
|
||||
<div class="feature">
|
||||
<h3 translate="intro.features.handy.title" class="text-center text-lg">Handy UI</h3>
|
||||
<p translate="intro.features.handy.body" class="text-center">
|
||||
Explore and operate on the remote and on the local machine file system
|
||||
with a handy UI.
|
||||
</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h3 translate="intro.features.crossPlatform.title" class="text-center text-lg">Cross platform</h3>
|
||||
<p translate="intro.features.crossPlatform.body" class="text-center">
|
||||
Runs on Windows, MacOS, Linux and BSD
|
||||
</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h3 translate="intro.features.customizable.title" class="text-center text-lg">Customizable</h3>
|
||||
<p translate="intro.features.customizable.body" class="text-center">
|
||||
Customize the file explorer, the text editor to use and default
|
||||
options
|
||||
</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h3 translate="intro.features.bookmarks.title" class="text-center text-lg">Bookmarks</h3>
|
||||
<p translate="intro.features.bookmarks.body" class="text-center">
|
||||
Connect to your favourite hosts through built-in bookmarks and recent
|
||||
connections support
|
||||
</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h3 translate="intro.features.security.title" class="text-center text-lg">Security first</h3>
|
||||
<p translate="intro.features.security.body" class="text-center">
|
||||
Save your password into your operating system key vault
|
||||
</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h3 translate="intro.features.performance.title" class="text-center text-lg">Eye on performance</h3>
|
||||
<p translate="intro.features.performance.body" class="text-center">
|
||||
termscp has been developed keeping an eye on performance to prevent
|
||||
cpu usage
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-4/6 sm:w-5/6">
|
||||
<video autoplay muted loop>
|
||||
<source src="assets/videos/explorer.mp4" type="video/mp4" resolve-video-fallback="assets/images/explorer.gif" />
|
||||
</video>
|
||||
</div>
|
||||
<hr class="h-px my-8 bg-gray-800 border-0" />
|
||||
<div class="flex flex-row sm:flex-col justify-around gap-12">
|
||||
<div class="hook">
|
||||
<h3 class="text-center text-gray-500 dark:text-gray-100 font-light text-xl">
|
||||
<a href="/get-started.html" class="no-underline hover:underline" translate="intro.footer.getStarted">Get started</a>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="hook">
|
||||
<h3 class="text-center text-gray-500 dark:text-gray-100 font-light text-xl">
|
||||
<a href="/user-manual.html" class="no-underline hover:underline" translate="intro.footer.manual">User manual</a>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="hook">
|
||||
<h3 class="text-center text-gray-500 dark:text-gray-100 font-light text-xl">
|
||||
<a href="/updates.html" class="no-underline hover:underline" translate="intro.footer.updates">Install updates</a>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -1,96 +0,0 @@
|
||||
<head>
|
||||
<link rel="stylesheet" href="css/intro.css" />
|
||||
</head>
|
||||
<body>
|
||||
<section id="intro" class="container intro">
|
||||
<h1 class="title">termscp</h1>
|
||||
<img class="logo" alt="logo" src="assets/images/termscp.webp" />
|
||||
<h2 class="caption" translate="intro.caption">
|
||||
A feature rich terminal UI file transfer and explorer with support for
|
||||
SCP/SFTP/FTP/S3
|
||||
</h2>
|
||||
<button class="pure-button get-started">
|
||||
<a href="#get-started" translate="intro.getStarted">Get started →</a>
|
||||
</button>
|
||||
<div class="alert alert-center alert-success">
|
||||
<p>
|
||||
<span translate="intro.versionAlert"
|
||||
>termscp 0.12.0 is NOW out! Download it from</span
|
||||
>
|
||||
<a href="#get-started" translate="intro.here">here!</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="features">
|
||||
<div class="feature">
|
||||
<h3 translate="intro.features.handy.title">Handy UI</h3>
|
||||
<p translate="intro.features.handy.body">
|
||||
Explore and operate on the remote and on the local machine file system
|
||||
with a handy UI.
|
||||
</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h3 translate="intro.features.crossPlatform.title">Cross platform</h3>
|
||||
<p translate="intro.features.crossPlatform.body">
|
||||
Runs on Windows, MacOS, Linux and BSD
|
||||
</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h3 translate="intro.features.customizable.title">Customizable</h3>
|
||||
<p translate="intro.features.customizable.body">
|
||||
Customize the file explorer, the text editor to use and default
|
||||
options
|
||||
</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h3 translate="intro.features.bookmarks.title">Bookmarks</h3>
|
||||
<p translate="intro.features.bookmarks.body">
|
||||
Connect to your favourite hosts through built-in bookmarks and recent
|
||||
connections support
|
||||
</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h3 translate="intro.features.security.title">Security first</h3>
|
||||
<p translate="intro.features.security.body">
|
||||
Save your password into your operating system key vault
|
||||
</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h3 translate="intro.features.performance.title">Eye on performance</h3>
|
||||
<p translate="intro.features.performance.body">
|
||||
termscp has been developed keeping an eye on performance to prevent
|
||||
cpu usage
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="preview">
|
||||
<video autoplay muted loop>
|
||||
<source
|
||||
src="assets/videos/explorer.mp4"
|
||||
type="video/mp4"
|
||||
resolve-video-fallback="assets/images/explorer.gif"
|
||||
/>
|
||||
</video>
|
||||
</div>
|
||||
<div class="discover">
|
||||
<div class="hook">
|
||||
<h3>
|
||||
<a href="#get-started" translate="intro.footer.getStarted"
|
||||
>Get started</a
|
||||
>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="hook">
|
||||
<h3>
|
||||
<a href="#user-manual" translate="intro.footer.manual">User manual</a>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="hook">
|
||||
<h3>
|
||||
<a href="#updates" translate="intro.footer.updates"
|
||||
>Install updates</a
|
||||
>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
@@ -2,25 +2,16 @@
|
||||
<link rel="stylesheet" href="css/updates.css" />
|
||||
</head>
|
||||
<body>
|
||||
<section id="updates" class="container updates">
|
||||
<h1 translate="updates.title">Keeping termscp up to date</h1>
|
||||
<div class="alert alert-warning">
|
||||
<p>
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<span translate="updates.disclaimer">
|
||||
Updating termscp with this method is only available for 0.7.x versions
|
||||
or higher. If you have an older version, you have to install updates
|
||||
using the</span> <a href="#get-started">install.sh script</a>
|
||||
</p>
|
||||
</div>
|
||||
<section id="updates" class="flex flex-col mx-auto items-center justify-center w-full px-12 gap-8 dark:text-gray-100">
|
||||
<h1 translate="updates.title" class="text-3xl font-thin">Keeping termscp up to date</h1>
|
||||
<!-- Reasons -->
|
||||
<section>
|
||||
<h2>
|
||||
<h2 class="text-2xl font-thin">
|
||||
<i class="fa fa-question-circle"></i> <span translate="updates.reasons.title">Why should you install
|
||||
updates</span>
|
||||
</h2>
|
||||
<div class="wall-of-text">
|
||||
<p translate="updates.reasons.wallOfText">
|
||||
<p translate="updates.reasons.wallOfText" class="text-gray-700 dark:text-gray-300">
|
||||
Termscp is an application that is still in its early stage of
|
||||
development, the first version has been released in december in 2020
|
||||
and practically there's only one
|
||||
@@ -42,7 +33,7 @@
|
||||
awesome features 🦄 you can't miss and the application is getting more
|
||||
reliable and stable after each update 😄
|
||||
</p>
|
||||
<p class="tl-dr">
|
||||
<p class="italic text-xl text-gray-700 dark:text-gray-300">
|
||||
<span>TL;DR</span>
|
||||
<span translate="updates.reasons.tldr"></span>
|
||||
</p>
|
||||
@@ -50,26 +41,26 @@
|
||||
</section>
|
||||
<!-- Gui method -->
|
||||
<section>
|
||||
<h2><i class="fa fa-desktop"></i> <span translate="updates.gui.title">GUI method</span></h2>
|
||||
<h2 class="text-2xl font-thin"><i class="fa fa-desktop"></i> <span translate="updates.gui.title">GUI method</span></h2>
|
||||
<div class="installation">
|
||||
<p translate="updates.gui.body" class="description">
|
||||
<p translate="updates.gui.body" class="text-gray-700 dark:text-gray-300">
|
||||
The GUI method just consists in starting termscp with no options, you
|
||||
then should be in front of the authentication form. If there's an
|
||||
update available a message like "termscp x.y.z is OUT! Update and read
|
||||
release notes with CTRL+R". All you have to do at this point to update
|
||||
termscp, is:
|
||||
</p>
|
||||
<ol>
|
||||
<ol class="list-decimal px-8 text-gray-700 dark:text-gray-300">
|
||||
<li translate="updates.gui.steps.st">press CTRL+R. The release notes should now be displayed.</li>
|
||||
<li translate="updates.gui.steps.nd">Select "YES" in the "Install update?" radio input</li>
|
||||
<li translate="updates.gui.steps.rd">Press "ENTER"</li>
|
||||
</ol>
|
||||
<p translate="updates.gui.then" class="description">
|
||||
<p translate="updates.gui.then" class="text-gray-700 dark:text-gray-300">
|
||||
If everything worked correctly a green message "termscp x.y.z has been
|
||||
installed!" will be displayed. Just restart termscp and enjoy the
|
||||
update 😄
|
||||
</p>
|
||||
<div class="alert alert-warning">
|
||||
<div class="p-4 my-4 text-sm text-yellow-800 rounded-lg bg-yellow-50">
|
||||
<p>
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<span translate="updates.gui.pex">
|
||||
@@ -82,20 +73,20 @@
|
||||
</section>
|
||||
<!-- CLI method -->
|
||||
<section>
|
||||
<h2><i class="fa fa-glasses"></i> <span translate="updates.cli.title">CLI method</span></h2>
|
||||
<h2 class="text-2xl font-thin"><i class="fa fa-glasses"></i> <span translate="updates.cli.title">CLI method</span></h2>
|
||||
<div class="installation">
|
||||
<p translate="updates.cli.body" class="description">
|
||||
<p translate="updates.cli.body" class="text-gray-700 dark:text-gray-300">
|
||||
If you prefer, you can install a new update just using the dedicated
|
||||
CLI option:
|
||||
</p>
|
||||
<pre><span class="function">termscp</span> --update</pre>
|
||||
<div class="alert alert-warning">
|
||||
<div class="p-4 my-4 text-sm text-yellow-800 rounded-lg bg-yellow-50">
|
||||
<p>
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<span translate="updates.cli.pex">Run with sudo if necessary (Debian/FreeBSD/RedHat users)</span>
|
||||
</p>
|
||||
</div>
|
||||
<p translate="updates.cli.then" class="description">
|
||||
<p translate="updates.cli.then" class="text-gray-700 dark:text-gray-300">
|
||||
Once started, you will be prompted whether to install or not the
|
||||
update. Confirm the installation and ta-dah, the new version of
|
||||
termscp should now be available on your machine
|
||||
|
||||
345
site/index.html
345
site/index.html
@@ -1,272 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en_US">
|
||||
<head>
|
||||
<title>
|
||||
termscp is a terminal file transfer and explorer for SCP/SFTP/FTP/S3
|
||||
</title>
|
||||
<meta
|
||||
property="og:description"
|
||||
content="termscp is a feature rich terminal file transfer and explorer, with support for SCP/SFTP/FTP/S3. It is Linux, MacOS, FreeBSD, NetBSD and Windows compatible"
|
||||
/>
|
||||
<meta
|
||||
name="description"
|
||||
content="termscp is a feature rich terminal file transfer and explorer, with support for SCP/SFTP/FTP/S3. It is Linux, MacOS, FreeBSD, NetBSD and Windows compatible"
|
||||
/>
|
||||
<meta
|
||||
property="og:title"
|
||||
content="termscp is a terminal file transfer and explorer for SCP/SFTP/FTP/S3"
|
||||
/>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<meta
|
||||
property="og:image"
|
||||
content="https://termscp.veeso.dev/assets/images/og_preview.jpg"
|
||||
/>
|
||||
<meta property="og:image:type" content="image/jpg" />
|
||||
<meta property="og:image:width" content="1024" />
|
||||
<meta property="og:image:height" content="820" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="termscp" />
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta property="og:url" content="https://termscp.veeso.dev" />
|
||||
<!-- Pure.css -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://unpkg.com/purecss@2.0.5/build/pure-min.css"
|
||||
/>
|
||||
<!-- Icons -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.11.0/devicon.min.css"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"
|
||||
integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w=="
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/fontawesome.min.css"
|
||||
integrity="sha512-OdEXQYCOldjqUEsuMKsZRj93Ht23QRlhIb8E/X0sbwZhme8eUw6g8q7AdxGJKakcBbv7+/PX0Gc2btf7Ru8cZA=="
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<!-- Main -->
|
||||
<link rel="stylesheet" href="css/markdown.css" />
|
||||
<link rel="stylesheet" href="css/menu.css" />
|
||||
<link rel="stylesheet" href="css/main.css" />
|
||||
<!-- Favicons -->
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="96x96"
|
||||
href="assets/images/favicon-96x96.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="assets/images/favicon-32x32.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="assets/images/favicon-16x16.png"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="layout">
|
||||
<!-- Menu toggle -->
|
||||
<a href="#menu" id="menu-burger" class="menu-link">
|
||||
<!-- Hamburger icon -->
|
||||
<span></span>
|
||||
</a>
|
||||
<!-- Menu -->
|
||||
<header id="menu">
|
||||
<div class="pure-menu">
|
||||
<div class="pure-menu-heading">
|
||||
<div class="pic-box">
|
||||
<img class="avatar" alt="logo" src="assets/images/termscp.webp" />
|
||||
</div>
|
||||
<h1>termscp</h1>
|
||||
<p translate="menu.desc">
|
||||
A feature rich terminal UI file transfer
|
||||
</p>
|
||||
<a href="https://github.com/veeso/termscp/stargazers/">
|
||||
<img
|
||||
src="https://img.shields.io/github/stars/veeso/termscp.svg?style=social&label=Star&maxAge=2592000"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<ul class="pure-menu-list">
|
||||
<li class="pure-menu-item pure-menu-selected">
|
||||
<a href="#intro" class="pure-menu-link" translate="menu.intro"
|
||||
>Intro</a
|
||||
>
|
||||
</li>
|
||||
<li class="pure-menu-item">
|
||||
<a
|
||||
href="#get-started"
|
||||
class="pure-menu-link"
|
||||
translate="menu.getStarted"
|
||||
>Get started</a
|
||||
>
|
||||
</li>
|
||||
<li class="pure-menu-item">
|
||||
<a href="#updates" class="pure-menu-link" translate="menu.updates"
|
||||
>Install updates</a
|
||||
>
|
||||
</li>
|
||||
<li class="pure-menu-item">
|
||||
<a
|
||||
href="#user-manual"
|
||||
class="pure-menu-link"
|
||||
translate="menu.manual"
|
||||
>User manual</a
|
||||
>
|
||||
</li>
|
||||
<li class="pure-menu-item">
|
||||
<a
|
||||
href="#changelog"
|
||||
class="pure-menu-link"
|
||||
translate="menu.changelog"
|
||||
>Release history</a
|
||||
>
|
||||
</li>
|
||||
<li class="pure-menu-item">
|
||||
<a
|
||||
href="https://github.com/veeso/termscp"
|
||||
class="pure-menu-link"
|
||||
target="_blank"
|
||||
>Github <i class="fas fa-external-link-alt"></i
|
||||
></a>
|
||||
</li>
|
||||
<li class="pure-menu-item">
|
||||
<a
|
||||
href="https://veeso.dev/"
|
||||
class="pure-menu-link"
|
||||
target="_blank"
|
||||
><span translate="menu.author">About the author</span> <i
|
||||
class="fas fa-external-link-alt"
|
||||
></i
|
||||
></a>
|
||||
</li>
|
||||
<li class="pure-menu-item">
|
||||
<a
|
||||
href="https://ko-fi.com/veeso"
|
||||
class="pure-menu-link"
|
||||
target="_blank"
|
||||
><span translate="menu.support">Support me</span> <i
|
||||
class="fas fa-external-link-alt"
|
||||
></i
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="pure-menu-bottom">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://github.com/veeso/termscp" target="_blank"
|
||||
><i class="devicon-github-original"></i
|
||||
></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://crates.io/crates/termscp" target="_blank"
|
||||
><i class="devicon-rust-plain"></i
|
||||
></a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://community.chocolatey.org/packages/termscp"
|
||||
target="_blank"
|
||||
><i class="devicon-windows8-plain"></i
|
||||
></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://ko-fi.com/veeso" target="_blank"
|
||||
><i class="fa fa-coffee"></i
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<div id="main"></div>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
<ul class="contacts">
|
||||
<li>
|
||||
<a href="https://github.com/veeso/termscp" target="_blank"
|
||||
><i class="devicon-github-original"></i
|
||||
></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://crates.io/crates/termscp" target="_blank"
|
||||
><i class="devicon-rust-plain"></i
|
||||
></a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://community.chocolatey.org/packages/termscp"
|
||||
target="_blank"
|
||||
><i class="devicon-windows8-plain"></i
|
||||
></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://ko-fi.com/veeso" target="_blank"
|
||||
><i class="fa fa-coffee"></i
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- vat number -->
|
||||
<p class="footer--vat">
|
||||
<span>P.IVA IT03104140300 </span>
|
||||
</p>
|
||||
<p class="footer--address">
|
||||
<span>Via Antonio Marangoni 33, 33100, Udine (UD)</span>
|
||||
</p>
|
||||
<!-- Copyright -->
|
||||
<p class="footer--copyright">
|
||||
<span>Christian Visintin © </span><span resolve-copyright></span>
|
||||
<span> | </span>
|
||||
<a class="footer--link" href="https://veeso.dev/en/privacy" target="_blank"
|
||||
>Privacy policy</a
|
||||
>
|
||||
<span> | </span>
|
||||
<a class="footer--link" href="https://veeso.dev/en/cookie-policy" target="_blank"
|
||||
>Cookie policy</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
<!-- Scripts -->
|
||||
<!-- jQuery -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||
<!-- Showdown JS-->
|
||||
<script
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.9.1/showdown.min.js"
|
||||
integrity="sha512-L03kznCrNOfVxOUovR6ESfCz9Gfny7gihUX/huVbQB9zjODtYpxaVtIaAkpetoiyV2eqWbvxMH9fiSv5enX7bw=="
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
></script>
|
||||
<!-- Local -->
|
||||
<script src="js/lang.min.js"></script>
|
||||
<script src="js/core.js"></script>
|
||||
<script src="js/events.js"></script>
|
||||
<script src="js/resolvers.js"></script>
|
||||
<!-- ko-fi -->
|
||||
<script src="https://storage.ko-fi.com/cdn/scripts/overlay-widget.js"></script>
|
||||
<script>
|
||||
kofiWidgetOverlay.draw("veeso", {
|
||||
type: "floating-chat",
|
||||
"floating-chat.donateButton.text": "Support me",
|
||||
"floating-chat.donateButton.background-color": "#323842",
|
||||
"floating-chat.donateButton.text-color": "#fff",
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
<html lang="en_US" class="dark">
|
||||
|
||||
<head>
|
||||
<title>
|
||||
termscp is a terminal file transfer and explorer for SCP/SFTP/FTP/S3/SMB | termscp
|
||||
</title>
|
||||
<meta property="og:description"
|
||||
content="a WinSCP alternative for Linux and MacOS with support for SCP/SFTP/FTP/S3/SMB. Command line file transfer with user interface compatible with all the operating systems." />
|
||||
<meta name="description"
|
||||
content="a WinSCP alternative for Linux and MacOS with support for SCP/SFTP/FTP/S3/SMB. Command line file transfer with user interface compatible with all the operating systems." />
|
||||
<meta property="og:title" content="termscp is a terminal file transfer and explorer for SCP/SFTP/FTP/S3/SMB | termscp" />
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<meta property="og:image" content="https://termscp.veeso.dev/assets/images/og_preview.jpg" />
|
||||
<meta property="og:image:type" content="image/jpg" />
|
||||
<meta property="og:image:width" content="1280" />
|
||||
<meta property="og:image:height" content="640" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="termscp" />
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta property="og:url" content="https://termscp.veeso.dev" />
|
||||
<!-- Icons -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.11.0/devicon.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"
|
||||
integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w=="
|
||||
crossorigin="anonymous" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/fontawesome.min.css"
|
||||
integrity="sha512-OdEXQYCOldjqUEsuMKsZRj93Ht23QRlhIb8E/X0sbwZhme8eUw6g8q7AdxGJKakcBbv7+/PX0Gc2btf7Ru8cZA=="
|
||||
crossorigin="anonymous" />
|
||||
<!-- tailwind -->
|
||||
<link href="output.css" rel="stylesheet" />
|
||||
<!-- Favicons -->
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="assets/images/favicon-96x96.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/images/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/images/favicon-16x16.png" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="layout" class="dark:bg-brand dark:text-gray-100">
|
||||
|
||||
<!-- Menu -->
|
||||
<header id="menu"></header>
|
||||
<main>
|
||||
<div id="main" class="w-8/12 sm:w-full mx-auto pt-4 pb-8"></div>
|
||||
</main>
|
||||
<footer id="footer"></footer>
|
||||
</div>
|
||||
<!-- Scripts -->
|
||||
<!-- jQuery -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||
<!-- Showdown JS-->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.9.1/showdown.min.js"
|
||||
integrity="sha512-L03kznCrNOfVxOUovR6ESfCz9Gfny7gihUX/huVbQB9zjODtYpxaVtIaAkpetoiyV2eqWbvxMH9fiSv5enX7bw=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<!-- Local -->
|
||||
<script src="js/lang.min.js"></script>
|
||||
<script src="js/core.js"></script>
|
||||
<script src="js/events.js"></script>
|
||||
<script src="js/resolvers.js"></script>
|
||||
<!-- ko-fi -->
|
||||
<script src="https://storage.ko-fi.com/cdn/scripts/overlay-widget.js"></script>
|
||||
<script>
|
||||
kofiWidgetOverlay.draw("veeso", {
|
||||
type: "floating-chat",
|
||||
"floating-chat.donateButton.text": "Support me",
|
||||
"floating-chat.donateButton.background-color": "#31363b",
|
||||
"floating-chat.donateButton.text-color": "#fff",
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
132
site/input.css
Normal file
132
site/input.css
Normal file
@@ -0,0 +1,132 @@
|
||||
@import "css/markdown.css";
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@font-face {
|
||||
font-family: "Sora";
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/sora/v11/xMQ9uFFYT72X5wkB_18qmnndmSdSnx2BAfO5mnuyOo1l_iMwWa-xsaQ.woff2)
|
||||
format("woff2");
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB,
|
||||
U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: "Sora";
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/sora/v11/xMQ9uFFYT72X5wkB_18qmnndmSdSnx2BAfO5mnuyOo1l_iMwV6-x.woff2)
|
||||
format("woff2");
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
||||
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
|
||||
U+FEFF, U+FFFD;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: "Sora";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/sora/v11/xMQ9uFFYT72X5wkB_18qmnndmSdSnx2BAfO5mnuyOo1l_iMwWa-xsaQ.woff2)
|
||||
format("woff2");
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB,
|
||||
U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: "Sora";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/sora/v11/xMQ9uFFYT72X5wkB_18qmnndmSdSnx2BAfO5mnuyOo1l_iMwV6-x.woff2)
|
||||
format("woff2");
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
||||
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
|
||||
U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Sora", sans-serif;
|
||||
margin: 0;
|
||||
min-width: 100vw;
|
||||
overflow-x: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
main {
|
||||
padding-top: 5rem;
|
||||
}
|
||||
|
||||
p {
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
input {
|
||||
font-family: "Sora", sans-serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
textarea {
|
||||
font-family: "Sora", sans-serif;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
button {
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button,
|
||||
input {
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
padding: 16px;
|
||||
overflow: auto;
|
||||
font-size: 85%;
|
||||
line-height: 1.45;
|
||||
color: #d0d0d0;
|
||||
background-color: #222629;
|
||||
border-radius: 3px;
|
||||
word-wrap: normal;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
|
||||
pre .function {
|
||||
color: #f08d49;
|
||||
}
|
||||
|
||||
pre .string {
|
||||
color: #7ec699;
|
||||
}
|
||||
@@ -33,3 +33,99 @@ function languageSupported(lang) {
|
||||
function setSiteLanguage(lang) {
|
||||
setLanguage(lang);
|
||||
}
|
||||
|
||||
const converter = new showdown.Converter({ tables: true });
|
||||
|
||||
/**
|
||||
* @description load page associated to hash
|
||||
* @param {string} hash
|
||||
*/
|
||||
function loadPage(path) {
|
||||
switch (path) {
|
||||
case "/":
|
||||
case "/index.html":
|
||||
loadHtml("home.html");
|
||||
break;
|
||||
case "/get-started.html":
|
||||
loadHtml("get-started.html");
|
||||
break;
|
||||
case "/user-manual.html":
|
||||
loadUserManual();
|
||||
break;
|
||||
case "/updates.html":
|
||||
loadHtml("updates.html");
|
||||
break;
|
||||
case "/changelog.html":
|
||||
loadMarkdown(
|
||||
"https://raw.githubusercontent.com/veeso/termscp/main/CHANGELOG.md"
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function loadHtml(page) {
|
||||
const url = "html/" + page;
|
||||
$("#main").load(url, function () {
|
||||
onPageLoaded();
|
||||
});
|
||||
}
|
||||
|
||||
function loadMenu() {
|
||||
$("#menu").load("html/components/menu.html", function () {
|
||||
onPageLoaded();
|
||||
});
|
||||
}
|
||||
|
||||
function loadFooter() {
|
||||
$("#footer").load("html/components/footer.html", function () {
|
||||
onPageLoaded();
|
||||
});
|
||||
}
|
||||
|
||||
function loadMarkdown(page) {
|
||||
getMarkdown(page, function (md) {
|
||||
const div = jQuery("<div/>", {
|
||||
id: page,
|
||||
class: "container markdown",
|
||||
});
|
||||
div.html(converter.makeHtml(md));
|
||||
$("#main").empty();
|
||||
$("#main").append(div);
|
||||
onPageLoaded();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description get markdown and pass result to onLoaded
|
||||
* @param {string} url
|
||||
* @param {function} onLoaded
|
||||
*/
|
||||
function getMarkdown(url, onLoaded) {
|
||||
$.ajax({
|
||||
url,
|
||||
type: "GET",
|
||||
dataType: "text",
|
||||
success: onLoaded,
|
||||
});
|
||||
}
|
||||
|
||||
function loadUserManual() {
|
||||
// Load language
|
||||
const lang = getNavigatorLanguage();
|
||||
if (lang === "en") {
|
||||
loadMarkdown(
|
||||
`https://raw.githubusercontent.com/veeso/termscp/main/docs/man.md`
|
||||
);
|
||||
} else {
|
||||
loadMarkdown(
|
||||
`https://raw.githubusercontent.com/veeso/termscp/main/docs/${lang}/man.md`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// startup
|
||||
$(function () {
|
||||
loadPage(window.location.pathname);
|
||||
loadMenu();
|
||||
loadFooter();
|
||||
});
|
||||
|
||||
@@ -1,133 +1,81 @@
|
||||
const hashBlacklist = ["#menu"];
|
||||
const converter = new showdown.Converter({ tables: true });
|
||||
|
||||
/**
|
||||
* @description handle hash change
|
||||
*/
|
||||
function onHashChange() {
|
||||
const hash = location.hash;
|
||||
if (!hashBlacklist.includes(hash) && hash.length > 0) {
|
||||
selectMenuEntry(location.hash);
|
||||
loadPage(hash);
|
||||
} else if (hash.length === 0 || hash === "#") {
|
||||
loadPage("#intro");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description select menu entry
|
||||
* @param {*} hash
|
||||
*/
|
||||
function selectMenuEntry(hash) {
|
||||
// Remove current entry
|
||||
$(".pure-menu-selected").removeClass("pure-menu-selected");
|
||||
$('a[href$="' + hash + '"]')
|
||||
.parent()
|
||||
.addClass("pure-menu-selected");
|
||||
}
|
||||
|
||||
/**
|
||||
* @description load page associated to hash
|
||||
* @param {string} hash
|
||||
*/
|
||||
function loadPage(hash) {
|
||||
switch (hash) {
|
||||
case "#intro":
|
||||
loadHtml("intro.html");
|
||||
break;
|
||||
case "#get-started":
|
||||
loadHtml("get-started.html");
|
||||
break;
|
||||
case "#user-manual":
|
||||
loadUserManual();
|
||||
break;
|
||||
case "#updates":
|
||||
loadHtml("updates.html");
|
||||
break;
|
||||
case "#changelog":
|
||||
loadMarkdown(
|
||||
"https://raw.githubusercontent.com/veeso/termscp/main/CHANGELOG.md"
|
||||
);
|
||||
break;
|
||||
}
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
|
||||
function loadHtml(page) {
|
||||
const url = "html/" + page;
|
||||
$("#main").load(url, function () {
|
||||
onPageLoaded();
|
||||
});
|
||||
}
|
||||
|
||||
function loadMarkdown(page) {
|
||||
getMarkdown(page, function (md) {
|
||||
const div = jQuery("<div/>", {
|
||||
id: page,
|
||||
class: "container markdown",
|
||||
});
|
||||
div.html(converter.makeHtml(md));
|
||||
$("#main").empty();
|
||||
$("#main").append(div);
|
||||
onPageLoaded();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description get markdown and pass result to onLoaded
|
||||
* @param {string} url
|
||||
* @param {function} onLoaded
|
||||
*/
|
||||
function getMarkdown(url, onLoaded) {
|
||||
$.ajax({
|
||||
url,
|
||||
type: "GET",
|
||||
dataType: "text",
|
||||
success: onLoaded,
|
||||
});
|
||||
}
|
||||
|
||||
function onMenuBurgerClick() {
|
||||
const active = $("#menu").hasClass("active");
|
||||
if (active) {
|
||||
$("#layout").removeClass("active");
|
||||
$("#menu").removeClass("active");
|
||||
} else {
|
||||
$("#layout").addClass("active");
|
||||
$("#menu").addClass("active");
|
||||
}
|
||||
}
|
||||
|
||||
function loadUserManual() {
|
||||
// Load language
|
||||
const lang = getNavigatorLanguage();
|
||||
if (lang === "en") {
|
||||
loadMarkdown(
|
||||
`https://raw.githubusercontent.com/veeso/termscp/main/docs/man.md`
|
||||
);
|
||||
} else {
|
||||
loadMarkdown(
|
||||
`https://raw.githubusercontent.com/veeso/termscp/main/docs/${lang}/man.md`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function onPageLoaded() {
|
||||
reloadTranslations();
|
||||
setThemeToggle();
|
||||
setTheme(getTheme());
|
||||
}
|
||||
|
||||
// Register
|
||||
window.onhashchange = onHashChange;
|
||||
function onToggleMenu() {
|
||||
const mobileMenu = $("#mobile-menu");
|
||||
let wasVisible = false;
|
||||
// if not visible set flex and slide in, otherwise slide out
|
||||
if (!mobileMenu.is(":visible")) {
|
||||
mobileMenu.css("display", "flex");
|
||||
mobileMenu.addClass("animate__animated animate__slideInLeft");
|
||||
} else {
|
||||
mobileMenu.addClass("animate__animated animate__slideOutLeft");
|
||||
wasVisible = true;
|
||||
}
|
||||
|
||||
// on animation end remove animation, if visible set hidden
|
||||
mobileMenu.on("animationend", () => {
|
||||
mobileMenu.removeClass(
|
||||
"animate__animated animate__slideOutLeft animate__slideInLeft"
|
||||
);
|
||||
if (wasVisible) {
|
||||
mobileMenu.css("display", "none");
|
||||
}
|
||||
mobileMenu.off("animationend");
|
||||
});
|
||||
}
|
||||
|
||||
function getTheme() {
|
||||
const theme = localStorage.getItem("theme");
|
||||
|
||||
if (!theme) {
|
||||
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? "theme-dark"
|
||||
: "theme-light";
|
||||
}
|
||||
|
||||
return theme;
|
||||
}
|
||||
|
||||
function setThemeToggle() {
|
||||
if (getTheme() === "theme-dark") {
|
||||
$("#theme-toggle-dark-icon").css("display", "block");
|
||||
$("#theme-toggle-light-icon").css("display", "none");
|
||||
} else {
|
||||
$("#theme-toggle-dark-icon").css("display", "none");
|
||||
$("#theme-toggle-light-icon").css("display", "block");
|
||||
}
|
||||
}
|
||||
|
||||
// function to set a given theme/color-scheme
|
||||
function setTheme(themeName) {
|
||||
localStorage.setItem("theme", themeName);
|
||||
if (themeName === "theme-dark") {
|
||||
document.documentElement.classList.add("dark");
|
||||
} else {
|
||||
document.documentElement.classList.remove("dark");
|
||||
}
|
||||
setThemeToggle();
|
||||
}
|
||||
|
||||
// function to toggle between light and dark theme
|
||||
function toggleTheme() {
|
||||
console.log("theme", getTheme());
|
||||
if (getTheme() === "theme-dark") {
|
||||
setTheme("theme-light");
|
||||
} else {
|
||||
setTheme("theme-dark");
|
||||
}
|
||||
}
|
||||
|
||||
// Startup
|
||||
$(function () {
|
||||
onHashChange();
|
||||
// Init language
|
||||
setSiteLanguage(getNavigatorLanguage());
|
||||
// Burger event listener
|
||||
$("#menu-burger").on("click", onMenuBurgerClick);
|
||||
$(".pure-menu-heading").on("click", function () {
|
||||
location.hash = "#";
|
||||
onHashChange();
|
||||
});
|
||||
|
||||
// init theme
|
||||
setTheme(getTheme());
|
||||
});
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"intro": {
|
||||
"caption": "A feature rich terminal UI file transfer and explorer with support for SCP/SFTP/FTP/S3",
|
||||
"getStarted": "Get started →",
|
||||
"versionAlert": "termscp 0.12.0 is NOW out! Download it from",
|
||||
"versionAlert": "termscp 0.12.2 is NOW out! Download it from",
|
||||
"here": "here",
|
||||
"features": {
|
||||
"handy": {
|
||||
@@ -59,7 +59,7 @@
|
||||
},
|
||||
"linuxUsers": "Linux users",
|
||||
"notConfident": "Opt for these methods instead if you don't feel confident using the shell script",
|
||||
"noBinary": "Opt for this method instead if binaries for your platform are not available",
|
||||
"noBinary": "Opt for this method instead if binaries for your platform are not available or you want to select features",
|
||||
"arch": {
|
||||
"title": "Arch derived users",
|
||||
"intro": "On Arch Linux based distros, you can install termscp using an AUR package manager such as",
|
||||
@@ -82,7 +82,8 @@
|
||||
"body": "If a package is not available for your system, you can opt to install termscp via",
|
||||
"requirements": "To install termscp via Cargo, these requirements must be satisfied",
|
||||
"install": "Then you can install termscp via",
|
||||
"noKeyring": "Or if you don't want to have support for keyring or you're building on *BSD:"
|
||||
"noKeyring": "Or if you don't want to have support for keyring or you're building on *BSD:",
|
||||
"noSMB": "Or if you want to disable SMB:"
|
||||
}
|
||||
},
|
||||
"updates": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"intro": {
|
||||
"caption": "Un explorador y transferencia de archivos de terminal rico en funciones, con apoyo para SCP/SFTP/FTP/S3",
|
||||
"getStarted": "Para iniciar →",
|
||||
"versionAlert": "termscp 0.12.0 ya está disponible! Descárgalo desde",
|
||||
"versionAlert": "termscp 0.12.2 ya está disponible! Descárgalo desde",
|
||||
"here": "aquì",
|
||||
"features": {
|
||||
"handy": {
|
||||
@@ -82,7 +82,8 @@
|
||||
"body": "Si un paquete no está disponible para su sistema, puede optar por instalar termscp a través de",
|
||||
"requirements": "Para instalar termscp a través de Cargo, se deben cumplir estos requisitos",
|
||||
"install": "Entonces puedes instalar termscp a través de",
|
||||
"noKeyring": "O si no desea tener soporte para llavero o está construyendo sobre *BSD:"
|
||||
"noKeyring": "O si no desea tener soporte para llavero o está construyendo sobre *BSD:",
|
||||
"noSMB": "O si no desea tener soporte para SMB:"
|
||||
}
|
||||
},
|
||||
"updates": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"intro": {
|
||||
"caption": "Un file transfer et navigateur de terminal riche en fonctionnalités avec support pour SCP/SFTP/FTP/S3",
|
||||
"getStarted": "Pour commencer →",
|
||||
"versionAlert": "termscp 0.12.0 est maintenant sorti! Télécharge-le depuis",
|
||||
"versionAlert": "termscp 0.12.2 est maintenant sorti! Télécharge-le depuis",
|
||||
"here": "ici",
|
||||
"features": {
|
||||
"handy": {
|
||||
@@ -82,7 +82,8 @@
|
||||
"body": "Si un package n'est pas disponible pour votre système, vous pouvez choisir d'installer Termscp via",
|
||||
"requirements": "Pour installer termscp via Cargo, ces conditions doivent être remplies",
|
||||
"install": "Ensuite, vous pouvez installer termscp via",
|
||||
"noKeyring": "Ou si vous ne voulez pas avoir de support pour le trousseau de clés ou si vous construisez sur *BSD :"
|
||||
"noKeyring": "Ou si vous ne voulez pas avoir de support pour le trousseau de clés ou si vous construisez sur *BSD :",
|
||||
"noSMB": "Ou si vous ne voulez pas avoir de support pour SMB:"
|
||||
}
|
||||
},
|
||||
"updates": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"intro": {
|
||||
"caption": "Un file transfer ed explorer ricco di funzionalità con supporto per SFTP/SCP/FTP/S3",
|
||||
"getStarted": "Installa termscp →",
|
||||
"versionAlert": "termscp 0.12.0 è ORA disponbile! Scaricalo da",
|
||||
"versionAlert": "termscp 0.12.2 è ORA disponbile! Scaricalo da",
|
||||
"here": "qui",
|
||||
"features": {
|
||||
"handy": {
|
||||
@@ -82,7 +82,8 @@
|
||||
"body": "Se un pacchetto non è disponibile per il tuo sistema, puoi installare termscp con cargo con",
|
||||
"requirements": "Per installare con Cargo, questi requisiti di sistema devono essere soddisfatti",
|
||||
"install": "Infine puoi installare termscp con",
|
||||
"noKeyring": "O se non hai il supporto a DBUS o sei un utente *BSD:"
|
||||
"noKeyring": "O se non hai il supporto a DBUS o sei un utente *BSD:",
|
||||
"noSMB": "O se vuoi disabilitare il supporto per SMB:"
|
||||
}
|
||||
},
|
||||
"updates": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"intro": {
|
||||
"caption": "功能丰富的终端 UI 文件传输和浏览器,支持 SCP/SFTP/FTP/S3",
|
||||
"getStarted": "开始 →",
|
||||
"versionAlert": "termscp 0.12.0 现已发布! 从下载",
|
||||
"versionAlert": "termscp 0.12.2 现已发布! 从下载",
|
||||
"here": "这里",
|
||||
"features": {
|
||||
"handy": {
|
||||
@@ -82,7 +82,8 @@
|
||||
"body": "如果您的系统没有可用的软件包,您可以选择通过以下方式安装 termscp:",
|
||||
"requirements": "要通过 Cargo 安装termscp,必须满足这些要求",
|
||||
"install": "然后你可以通过安装termscp",
|
||||
"noKeyring": "或者,如果您不想支持密钥环,或者您正在构建 *BSD:"
|
||||
"noKeyring": "或者,如果您不想支持密钥环,或者您正在构建 *BSD:",
|
||||
"noSMB": "或者如果你想禁用 SMB :"
|
||||
}
|
||||
},
|
||||
"updates": {
|
||||
|
||||
3
site/output.css
Normal file
3
site/output.css
Normal file
File diff suppressed because one or more lines are too long
2
site/robots.txt
Normal file
2
site/robots.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
User-Agent: *
|
||||
Allow: /
|
||||
29
site/sitemap.xml
Normal file
29
site/sitemap.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
||||
xmlns:xhtml="http://www.w3.org/1999/xhtml">
|
||||
<url>
|
||||
<loc>https://termscp.veeso.dev/</loc>
|
||||
<lastmod>2023-07-05</lastmod>
|
||||
<priority>1.00</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://termscp.veeso.dev/get-started.html</loc>
|
||||
<lastmod>2023-07-05</lastmod>
|
||||
<priority>0.95</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://termscp.veeso.dev/updates.html</loc>
|
||||
<lastmod>2023-07-05</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://termscp.veeso.dev/user-manual.html</loc>
|
||||
<lastmod>2023-07-05</lastmod>
|
||||
<priority>0.90</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://termscp.veeso.dev/changelog.html</loc>
|
||||
<lastmod>2023-07-05</lastmod>
|
||||
<priority>0.50</priority>
|
||||
</url>
|
||||
</urlset>
|
||||
75
site/updates.html
Normal file
75
site/updates.html
Normal file
@@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en_US" class="dark">
|
||||
|
||||
<head>
|
||||
<title>
|
||||
Install termscp updates | termscp
|
||||
</title>
|
||||
<meta property="og:description"
|
||||
content="How to update termscp. Keep termscp up to date with the built-in command." />
|
||||
<meta name="description"
|
||||
content="How to update termscp. Keep termscp up to date with the built-in command." />
|
||||
<meta property="og:title" content="Install termscp updates | termscp" />
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<meta property="og:image" content="https://termscp.veeso.dev/assets/images/og_preview.jpg" />
|
||||
<meta property="og:image:type" content="image/jpg" />
|
||||
<meta property="og:image:width" content="1280" />
|
||||
<meta property="og:image:height" content="640" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="termscp" />
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta property="og:url" content="https://termscp.veeso.dev" />
|
||||
<!-- Icons -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.11.0/devicon.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"
|
||||
integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w=="
|
||||
crossorigin="anonymous" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/fontawesome.min.css"
|
||||
integrity="sha512-OdEXQYCOldjqUEsuMKsZRj93Ht23QRlhIb8E/X0sbwZhme8eUw6g8q7AdxGJKakcBbv7+/PX0Gc2btf7Ru8cZA=="
|
||||
crossorigin="anonymous" />
|
||||
<!-- tailwind -->
|
||||
<link href="output.css" rel="stylesheet" />
|
||||
<!-- Favicons -->
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="assets/images/favicon-96x96.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/images/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/images/favicon-16x16.png" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="layout" class="dark:bg-brand dark:text-gray-100">
|
||||
|
||||
<!-- Menu -->
|
||||
<header id="menu"></header>
|
||||
<main>
|
||||
<div id="main" class="w-8/12 sm:w-full mx-auto pt-4 pb-8"></div>
|
||||
</main>
|
||||
<footer id="footer"></footer>
|
||||
</div>
|
||||
<!-- Scripts -->
|
||||
<!-- jQuery -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||
<!-- Showdown JS-->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.9.1/showdown.min.js"
|
||||
integrity="sha512-L03kznCrNOfVxOUovR6ESfCz9Gfny7gihUX/huVbQB9zjODtYpxaVtIaAkpetoiyV2eqWbvxMH9fiSv5enX7bw=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<!-- Local -->
|
||||
<script src="js/lang.min.js"></script>
|
||||
<script src="js/core.js"></script>
|
||||
<script src="js/events.js"></script>
|
||||
<script src="js/resolvers.js"></script>
|
||||
<!-- ko-fi -->
|
||||
<script src="https://storage.ko-fi.com/cdn/scripts/overlay-widget.js"></script>
|
||||
<script>
|
||||
kofiWidgetOverlay.draw("veeso", {
|
||||
type: "floating-chat",
|
||||
"floating-chat.donateButton.text": "Support me",
|
||||
"floating-chat.donateButton.background-color": "#31363b",
|
||||
"floating-chat.donateButton.text-color": "#fff",
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
75
site/user-manual.html
Normal file
75
site/user-manual.html
Normal file
@@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en_US" class="dark">
|
||||
|
||||
<head>
|
||||
<title>
|
||||
termscp user manual | termscp
|
||||
</title>
|
||||
<meta property="og:description"
|
||||
content="How to use termscp. User manual for termscp. How to setup ssh keys in termscp? How to set color themes in termscp?" />
|
||||
<meta name="description"
|
||||
content="How to use termscp. User manual for termscp. How to setup ssh keys in termscp? How to set color themes in termscp?" />
|
||||
<meta property="og:title" content="termscp user manual | termscp" />
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<meta property="og:image" content="https://termscp.veeso.dev/assets/images/og_preview.jpg" />
|
||||
<meta property="og:image:type" content="image/jpg" />
|
||||
<meta property="og:image:width" content="1280" />
|
||||
<meta property="og:image:height" content="640" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="termscp" />
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta property="og:url" content="https://termscp.veeso.dev" />
|
||||
<!-- Icons -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.11.0/devicon.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"
|
||||
integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w=="
|
||||
crossorigin="anonymous" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/fontawesome.min.css"
|
||||
integrity="sha512-OdEXQYCOldjqUEsuMKsZRj93Ht23QRlhIb8E/X0sbwZhme8eUw6g8q7AdxGJKakcBbv7+/PX0Gc2btf7Ru8cZA=="
|
||||
crossorigin="anonymous" />
|
||||
<!-- tailwind -->
|
||||
<link href="output.css" rel="stylesheet" />
|
||||
<!-- Favicons -->
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="assets/images/favicon-96x96.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/images/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/images/favicon-16x16.png" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="layout" class="dark:bg-brand dark:text-gray-100">
|
||||
|
||||
<!-- Menu -->
|
||||
<header id="menu"></header>
|
||||
<main>
|
||||
<div id="main" class="w-8/12 sm:w-full mx-auto pt-4 pb-8"></div>
|
||||
</main>
|
||||
<footer id="footer"></footer>
|
||||
</div>
|
||||
<!-- Scripts -->
|
||||
<!-- jQuery -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||
<!-- Showdown JS-->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.9.1/showdown.min.js"
|
||||
integrity="sha512-L03kznCrNOfVxOUovR6ESfCz9Gfny7gihUX/huVbQB9zjODtYpxaVtIaAkpetoiyV2eqWbvxMH9fiSv5enX7bw=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<!-- Local -->
|
||||
<script src="js/lang.min.js"></script>
|
||||
<script src="js/core.js"></script>
|
||||
<script src="js/events.js"></script>
|
||||
<script src="js/resolvers.js"></script>
|
||||
<!-- ko-fi -->
|
||||
<script src="https://storage.ko-fi.com/cdn/scripts/overlay-widget.js"></script>
|
||||
<script>
|
||||
kofiWidgetOverlay.draw("veeso", {
|
||||
type: "floating-chat",
|
||||
"floating-chat.donateButton.text": "Support me",
|
||||
"floating-chat.donateButton.background-color": "#31363b",
|
||||
"floating-chat.donateButton.text-color": "#fff",
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
// Deps
|
||||
// Namespaces
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
use remotefs_ssh::SshKeyStorage as SshKeyStorageTrait;
|
||||
@@ -34,12 +34,11 @@ pub enum NextActivity {
|
||||
pub struct ActivityManager {
|
||||
context: Option<Context>,
|
||||
ticks: Duration,
|
||||
local_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl ActivityManager {
|
||||
/// Initializes a new Activity Manager
|
||||
pub fn new(local_dir: &Path, ticks: Duration) -> Result<ActivityManager, HostError> {
|
||||
pub fn new(ticks: Duration) -> Result<ActivityManager, HostError> {
|
||||
// Prepare Context
|
||||
// Initialize configuration client
|
||||
let (config_client, error_config): (ConfigClient, Option<String>) =
|
||||
@@ -59,7 +58,6 @@ impl ActivityManager {
|
||||
let ctx: Context = Context::new(bookmarks_client, config_client, theme_provider, error);
|
||||
Ok(ActivityManager {
|
||||
context: Some(ctx),
|
||||
local_dir: local_dir.to_path_buf(),
|
||||
ticks,
|
||||
})
|
||||
}
|
||||
@@ -243,8 +241,19 @@ impl ActivityManager {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// get local path:
|
||||
// - if set in file transfer params, get it from there
|
||||
// - otherwise is env current dir
|
||||
// - otherwise is /
|
||||
let local_wrkdir = ft_params
|
||||
.local_path
|
||||
.clone()
|
||||
.or(std::env::current_dir().ok())
|
||||
.unwrap_or(PathBuf::from("/"));
|
||||
|
||||
// Prepare activity
|
||||
let host: Localhost = match Localhost::new(self.local_dir.clone()) {
|
||||
let host: Localhost = match Localhost::new(local_wrkdir) {
|
||||
Ok(host) => host,
|
||||
Err(err) => {
|
||||
// Set error in context
|
||||
|
||||
@@ -38,8 +38,11 @@ pub struct Bookmark {
|
||||
pub username: Option<String>,
|
||||
/// Password is optional; base64, aes-128 encrypted password
|
||||
pub password: Option<String>,
|
||||
/// Remote folder to connect to
|
||||
pub directory: Option<PathBuf>,
|
||||
/// Remote folder to connect to (serde rename for legacy reasons)
|
||||
#[serde(rename = "directory")]
|
||||
pub remote_path: Option<PathBuf>,
|
||||
/// local folder to open at startup
|
||||
pub local_path: Option<PathBuf>,
|
||||
/// S3 params; optional. When used other fields are empty for sure
|
||||
pub s3: Option<S3Params>,
|
||||
/// SMB params; optional. Extra params required for SMB protocol
|
||||
@@ -71,7 +74,8 @@ pub struct SmbParams {
|
||||
impl From<FileTransferParams> for Bookmark {
|
||||
fn from(params: FileTransferParams) -> Self {
|
||||
let protocol = params.protocol;
|
||||
let directory = params.entry_directory;
|
||||
let remote_path = params.remote_path;
|
||||
let local_path = params.local_path;
|
||||
// Create generic or others
|
||||
match params.params {
|
||||
ProtocolParams::Generic(params) => Self {
|
||||
@@ -80,7 +84,8 @@ impl From<FileTransferParams> for Bookmark {
|
||||
port: Some(params.port),
|
||||
username: params.username,
|
||||
password: params.password,
|
||||
directory,
|
||||
remote_path,
|
||||
local_path,
|
||||
s3: None,
|
||||
smb: None,
|
||||
},
|
||||
@@ -90,7 +95,8 @@ impl From<FileTransferParams> for Bookmark {
|
||||
port: None,
|
||||
username: None,
|
||||
password: None,
|
||||
directory,
|
||||
remote_path,
|
||||
local_path,
|
||||
s3: Some(S3Params::from(params)),
|
||||
smb: None,
|
||||
},
|
||||
@@ -104,7 +110,8 @@ impl From<FileTransferParams> for Bookmark {
|
||||
port: None,
|
||||
username: params.username,
|
||||
password: params.password,
|
||||
directory,
|
||||
remote_path,
|
||||
local_path,
|
||||
s3: None,
|
||||
},
|
||||
}
|
||||
@@ -155,7 +162,8 @@ impl From<Bookmark> for FileTransferParams {
|
||||
Self::new(bookmark.protocol, ProtocolParams::Smb(params))
|
||||
}
|
||||
}
|
||||
.entry_directory(bookmark.directory) // Set entry directory
|
||||
.remote_path(bookmark.remote_path) // Set entry remote_path
|
||||
.local_path(bookmark.local_path) // Set entry local path
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,7 +254,8 @@ mod tests {
|
||||
protocol: FileTransferProtocol::Sftp,
|
||||
username: Some(String::from("root")),
|
||||
password: Some(String::from("password")),
|
||||
directory: Some(PathBuf::from("/tmp")),
|
||||
remote_path: Some(PathBuf::from("/tmp")),
|
||||
local_path: Some(PathBuf::from("/usr")),
|
||||
s3: None,
|
||||
smb: None,
|
||||
};
|
||||
@@ -256,7 +265,8 @@ mod tests {
|
||||
protocol: FileTransferProtocol::Scp,
|
||||
username: Some(String::from("admin")),
|
||||
password: Some(String::from("password")),
|
||||
directory: Some(PathBuf::from("/home")),
|
||||
remote_path: Some(PathBuf::from("/home")),
|
||||
local_path: Some(PathBuf::from("/usr")),
|
||||
s3: None,
|
||||
smb: None,
|
||||
};
|
||||
@@ -273,9 +283,13 @@ mod tests {
|
||||
assert_eq!(bookmark.username.as_deref().unwrap(), "root");
|
||||
assert_eq!(bookmark.password.as_deref().unwrap(), "password");
|
||||
assert_eq!(
|
||||
bookmark.directory.as_deref().unwrap(),
|
||||
bookmark.remote_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/tmp")
|
||||
);
|
||||
assert_eq!(
|
||||
bookmark.local_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/usr")
|
||||
);
|
||||
let bookmark: &Bookmark = hosts
|
||||
.recents
|
||||
.get(&String::from("ISO20201218T181432"))
|
||||
@@ -286,9 +300,13 @@ mod tests {
|
||||
assert_eq!(bookmark.username.as_deref().unwrap(), "admin");
|
||||
assert_eq!(bookmark.password.as_deref().unwrap(), "password");
|
||||
assert_eq!(
|
||||
bookmark.directory.as_deref().unwrap(),
|
||||
bookmark.remote_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/home")
|
||||
);
|
||||
assert_eq!(
|
||||
bookmark.local_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/usr")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -300,7 +318,8 @@ mod tests {
|
||||
password: Some(String::from("omar")),
|
||||
});
|
||||
let params: FileTransferParams = FileTransferParams::new(FileTransferProtocol::Scp, params)
|
||||
.entry_directory(Some(PathBuf::from("/home")));
|
||||
.remote_path(Some(PathBuf::from("/home")))
|
||||
.local_path(Some(PathBuf::from("/tmp")));
|
||||
let bookmark = Bookmark::from(params);
|
||||
assert_eq!(bookmark.protocol, FileTransferProtocol::Scp);
|
||||
assert_eq!(bookmark.address.as_deref().unwrap(), "127.0.0.1");
|
||||
@@ -308,9 +327,13 @@ mod tests {
|
||||
assert_eq!(bookmark.username.as_deref().unwrap(), "root");
|
||||
assert_eq!(bookmark.password.as_deref().unwrap(), "omar");
|
||||
assert_eq!(
|
||||
bookmark.directory.as_deref().unwrap(),
|
||||
bookmark.remote_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/home")
|
||||
);
|
||||
assert_eq!(
|
||||
bookmark.local_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/tmp")
|
||||
);
|
||||
assert!(bookmark.s3.is_none());
|
||||
}
|
||||
|
||||
@@ -345,16 +368,21 @@ mod tests {
|
||||
protocol: FileTransferProtocol::Sftp,
|
||||
username: Some(String::from("root")),
|
||||
password: Some(String::from("password")),
|
||||
directory: Some(PathBuf::from("/tmp")),
|
||||
remote_path: Some(PathBuf::from("/tmp")),
|
||||
local_path: Some(PathBuf::from("/usr")),
|
||||
s3: None,
|
||||
smb: None,
|
||||
};
|
||||
let params = FileTransferParams::from(bookmark);
|
||||
assert_eq!(params.protocol, FileTransferProtocol::Sftp);
|
||||
assert_eq!(
|
||||
params.entry_directory.as_deref().unwrap(),
|
||||
params.remote_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/tmp")
|
||||
);
|
||||
assert_eq!(
|
||||
params.local_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/usr")
|
||||
);
|
||||
let gparams = params.params.generic_params().unwrap();
|
||||
assert_eq!(gparams.address.as_str(), "192.168.1.1");
|
||||
assert_eq!(gparams.port, 22);
|
||||
@@ -370,7 +398,8 @@ mod tests {
|
||||
port: None,
|
||||
username: None,
|
||||
password: None,
|
||||
directory: Some(PathBuf::from("/tmp")),
|
||||
remote_path: Some(PathBuf::from("/tmp")),
|
||||
local_path: Some(PathBuf::from("/usr")),
|
||||
s3: Some(S3Params {
|
||||
bucket: String::from("veeso"),
|
||||
region: Some(String::from("eu-west-1")),
|
||||
@@ -385,9 +414,13 @@ mod tests {
|
||||
let params = FileTransferParams::from(bookmark);
|
||||
assert_eq!(params.protocol, FileTransferProtocol::AwsS3);
|
||||
assert_eq!(
|
||||
params.entry_directory.as_deref().unwrap(),
|
||||
params.remote_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/tmp")
|
||||
);
|
||||
assert_eq!(
|
||||
params.local_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/usr")
|
||||
);
|
||||
let gparams = params.params.s3_params().unwrap();
|
||||
assert_eq!(gparams.bucket_name.as_str(), "veeso");
|
||||
assert_eq!(gparams.region.as_deref().unwrap(), "eu-west-1");
|
||||
@@ -407,7 +440,8 @@ mod tests {
|
||||
port: Some(445),
|
||||
username: Some("foo".to_string()),
|
||||
password: Some("bar".to_string()),
|
||||
directory: Some(PathBuf::from("/tmp")),
|
||||
remote_path: Some(PathBuf::from("/tmp")),
|
||||
local_path: Some(PathBuf::from("/usr")),
|
||||
s3: None,
|
||||
smb: Some(SmbParams {
|
||||
share: "test".to_string(),
|
||||
@@ -418,9 +452,13 @@ mod tests {
|
||||
let params = FileTransferParams::from(bookmark);
|
||||
assert_eq!(params.protocol, FileTransferProtocol::Smb);
|
||||
assert_eq!(
|
||||
params.entry_directory.as_deref().unwrap(),
|
||||
params.remote_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/tmp")
|
||||
);
|
||||
assert_eq!(
|
||||
params.local_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/usr")
|
||||
);
|
||||
let smb_params = params.params.smb_params().unwrap();
|
||||
assert_eq!(smb_params.address.as_str(), "localhost");
|
||||
assert_eq!(smb_params.port, 445);
|
||||
@@ -439,7 +477,8 @@ mod tests {
|
||||
port: Some(445),
|
||||
username: None,
|
||||
password: None,
|
||||
directory: Some(PathBuf::from("/tmp")),
|
||||
remote_path: Some(PathBuf::from("/tmp")),
|
||||
local_path: Some(PathBuf::from("/usr")),
|
||||
s3: None,
|
||||
smb: Some(SmbParams {
|
||||
share: "test".to_string(),
|
||||
@@ -450,9 +489,13 @@ mod tests {
|
||||
let params = FileTransferParams::from(bookmark);
|
||||
assert_eq!(params.protocol, FileTransferProtocol::Smb);
|
||||
assert_eq!(
|
||||
params.entry_directory.as_deref().unwrap(),
|
||||
params.remote_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/tmp")
|
||||
);
|
||||
assert_eq!(
|
||||
params.local_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/usr")
|
||||
);
|
||||
let smb_params = params.params.smb_params().unwrap();
|
||||
assert_eq!(smb_params.address.as_str(), "localhost");
|
||||
assert_eq!(smb_params.share.as_str(), "test");
|
||||
|
||||
@@ -380,7 +380,7 @@ mod tests {
|
||||
assert_eq!(host.username.as_deref().unwrap(), "cvisintin");
|
||||
assert_eq!(host.password.as_deref().unwrap(), "mysecret");
|
||||
assert_eq!(
|
||||
host.directory.as_deref().unwrap(),
|
||||
host.remote_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/tmp")
|
||||
);
|
||||
let host: &Bookmark = hosts.bookmarks.get("aws-server-prod1").unwrap();
|
||||
@@ -441,7 +441,8 @@ mod tests {
|
||||
protocol: FileTransferProtocol::Sftp,
|
||||
username: Some(String::from("root")),
|
||||
password: None,
|
||||
directory: None,
|
||||
remote_path: None,
|
||||
local_path: None,
|
||||
s3: None,
|
||||
smb: None,
|
||||
},
|
||||
@@ -454,7 +455,8 @@ mod tests {
|
||||
protocol: FileTransferProtocol::Sftp,
|
||||
username: Some(String::from("cvisintin")),
|
||||
password: Some(String::from("password")),
|
||||
directory: Some(PathBuf::from("/tmp")),
|
||||
remote_path: Some(PathBuf::from("/tmp")),
|
||||
local_path: Some(PathBuf::from("/usr")),
|
||||
s3: None,
|
||||
smb: None,
|
||||
},
|
||||
@@ -467,7 +469,8 @@ mod tests {
|
||||
protocol: FileTransferProtocol::AwsS3,
|
||||
username: None,
|
||||
password: None,
|
||||
directory: None,
|
||||
remote_path: None,
|
||||
local_path: None,
|
||||
s3: Some(S3Params {
|
||||
bucket: "veeso".to_string(),
|
||||
region: Some("eu-west-1".to_string()),
|
||||
@@ -492,7 +495,8 @@ mod tests {
|
||||
protocol: FileTransferProtocol::Smb,
|
||||
username: None,
|
||||
password: None,
|
||||
directory: None,
|
||||
remote_path: None,
|
||||
local_path: None,
|
||||
s3: None,
|
||||
smb: smb_params,
|
||||
},
|
||||
@@ -506,7 +510,8 @@ mod tests {
|
||||
protocol: FileTransferProtocol::Scp,
|
||||
username: Some(String::from("omar")),
|
||||
password: Some(String::from("aaa")),
|
||||
directory: Some(PathBuf::from("/tmp")),
|
||||
remote_path: Some(PathBuf::from("/tmp")),
|
||||
local_path: Some(PathBuf::from("/usr")),
|
||||
s3: None,
|
||||
smb: None,
|
||||
},
|
||||
@@ -519,8 +524,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_config_serialization_theme_serialize() {
|
||||
let mut theme: Theme = Theme::default();
|
||||
theme.auth_address = Color::Rgb(240, 240, 240);
|
||||
let theme: Theme = Theme {
|
||||
auth_address: Color::Rgb(240, 240, 240),
|
||||
..Default::default()
|
||||
};
|
||||
let tmpfile: tempfile::NamedTempFile = tempfile::NamedTempFile::new().unwrap();
|
||||
let (reader, writer) = create_file_ioers(tmpfile.path());
|
||||
assert!(serialize(&theme, Box::new(writer)).is_ok());
|
||||
@@ -547,7 +554,7 @@ mod tests {
|
||||
let file_content: &str = r#"
|
||||
[bookmarks]
|
||||
raspberrypi2 = { address = "192.168.1.31", port = 22, protocol = "SFTP", username = "root", password = "mypassword" }
|
||||
msi-estrem = { address = "192.168.1.30", port = 22, protocol = "SFTP", username = "cvisintin", password = "mysecret", directory = "/tmp" }
|
||||
msi-estrem = { address = "192.168.1.30", port = 22, protocol = "SFTP", username = "cvisintin", password = "mysecret", directory = "/tmp", local_path = "/usr" }
|
||||
aws-server-prod1 = { address = "51.23.67.12", port = 21, protocol = "FTPS", username = "aws001" }
|
||||
|
||||
[bookmarks.my-bucket]
|
||||
|
||||
@@ -315,8 +315,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_fs_explorer_stack() {
|
||||
let mut explorer: FileExplorer = FileExplorer::default();
|
||||
explorer.stack_size = 2;
|
||||
let mut explorer = FileExplorer {
|
||||
stack_size: 2,
|
||||
dirstack: VecDeque::with_capacity(2),
|
||||
..Default::default()
|
||||
};
|
||||
explorer.dirstack = VecDeque::with_capacity(2);
|
||||
// Push dir
|
||||
explorer.pushd(Path::new("/tmp"));
|
||||
|
||||
@@ -140,7 +140,7 @@ impl Builder {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[cfg(smb_windows)]
|
||||
fn smb_client(params: SmbParams) -> SmbFs {
|
||||
let mut credentials = SmbCredentials::new(params.address, params.share);
|
||||
|
||||
@@ -159,16 +159,26 @@ impl Builder {
|
||||
let mut opts = SshOpts::new(params.address.clone())
|
||||
.key_storage(Box::new(Self::make_ssh_storage(config_client)))
|
||||
.port(params.port);
|
||||
// get ssh config
|
||||
let ssh_config = config_client
|
||||
.get_ssh_config()
|
||||
.and_then(|path| {
|
||||
debug!("reading ssh config at {}", path);
|
||||
ssh_utils::parse_ssh2_config(path).ok()
|
||||
})
|
||||
.map(|config| config.query(¶ms.address));
|
||||
|
||||
//* override port
|
||||
if let Some(port) = ssh_config.as_ref().and_then(|config| config.port) {
|
||||
opts = opts.port(port);
|
||||
}
|
||||
|
||||
//* get username. Case 1 provided in params
|
||||
if let Some(username) = params.username {
|
||||
opts = opts.username(username);
|
||||
} else if let Some(ssh_config) = config_client.get_ssh_config().and_then(|x| {
|
||||
//* case 2: found in ssh2 config
|
||||
debug!("reading ssh config at {}", x);
|
||||
ssh_utils::parse_ssh2_config(x).ok()
|
||||
}) {
|
||||
} else if let Some(ssh_config) = &ssh_config {
|
||||
debug!("no username was provided, checking whether a user is set for this host");
|
||||
if let Some(username) = ssh_config.query(¶ms.address).user {
|
||||
if let Some(username) = &ssh_config.user {
|
||||
debug!("found username from config: {username}");
|
||||
opts = opts.username(username);
|
||||
} else {
|
||||
|
||||
@@ -11,7 +11,8 @@ use super::FileTransferProtocol;
|
||||
pub struct FileTransferParams {
|
||||
pub protocol: FileTransferProtocol,
|
||||
pub params: ProtocolParams,
|
||||
pub entry_directory: Option<PathBuf>,
|
||||
pub remote_path: Option<PathBuf>,
|
||||
pub local_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
/// Container for protocol params
|
||||
@@ -64,13 +65,20 @@ impl FileTransferParams {
|
||||
Self {
|
||||
protocol,
|
||||
params,
|
||||
entry_directory: None,
|
||||
remote_path: None,
|
||||
local_path: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set entry directory
|
||||
pub fn entry_directory<P: AsRef<Path>>(mut self, dir: Option<P>) -> Self {
|
||||
self.entry_directory = dir.map(|x| x.as_ref().to_path_buf());
|
||||
/// Set remote directory
|
||||
pub fn remote_path<P: AsRef<Path>>(mut self, dir: Option<P>) -> Self {
|
||||
self.remote_path = dir.map(|x| x.as_ref().to_path_buf());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set local directory
|
||||
pub fn local_path<P: AsRef<Path>>(mut self, dir: Option<P>) -> Self {
|
||||
self.local_path = dir.map(|x| x.as_ref().to_path_buf());
|
||||
self
|
||||
}
|
||||
|
||||
@@ -325,16 +333,15 @@ mod test {
|
||||
fn test_filetransfer_params() {
|
||||
let params: FileTransferParams =
|
||||
FileTransferParams::new(FileTransferProtocol::Scp, ProtocolParams::default())
|
||||
.entry_directory(Some(&Path::new("/tmp")));
|
||||
.remote_path(Some(&Path::new("/tmp")))
|
||||
.local_path(Some(&Path::new("/usr")));
|
||||
assert_eq!(
|
||||
params.params.generic_params().unwrap().address.as_str(),
|
||||
"localhost"
|
||||
);
|
||||
assert_eq!(params.protocol, FileTransferProtocol::Scp);
|
||||
assert_eq!(
|
||||
params.entry_directory.as_deref().unwrap(),
|
||||
Path::new("/tmp")
|
||||
);
|
||||
assert_eq!(params.remote_path.as_deref().unwrap(), Path::new("/tmp"));
|
||||
assert_eq!(params.local_path.as_deref().unwrap(), Path::new("/usr"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
110
src/main.rs
110
src/main.rs
@@ -15,7 +15,7 @@ extern crate magic_crypt;
|
||||
|
||||
// External libs
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
|
||||
// Include
|
||||
@@ -139,59 +139,63 @@ fn parse_remote_address(remote: &str) -> Result<FileTransferParams, String> {
|
||||
/// Run task and return rc
|
||||
fn run(run_opts: RunOpts) -> i32 {
|
||||
match run_opts.task {
|
||||
Task::ImportTheme(theme) => match support::import_theme(theme.as_path()) {
|
||||
Ok(_) => {
|
||||
println!("Theme has been successfully imported!");
|
||||
0
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("{err}");
|
||||
1
|
||||
}
|
||||
},
|
||||
Task::InstallUpdate => match support::install_update() {
|
||||
Ok(msg) => {
|
||||
println!("{msg}");
|
||||
0
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("Could not install update: {err}");
|
||||
1
|
||||
}
|
||||
},
|
||||
Task::Activity(activity) => {
|
||||
// Get working directory
|
||||
let wrkdir: PathBuf = match env::current_dir() {
|
||||
Ok(dir) => dir,
|
||||
Err(_) => PathBuf::from("/"),
|
||||
};
|
||||
// Create activity manager (and context too)
|
||||
let mut manager: ActivityManager =
|
||||
match ActivityManager::new(wrkdir.as_path(), run_opts.ticks) {
|
||||
Ok(m) => m,
|
||||
Err(err) => {
|
||||
eprintln!("Could not start activity manager: {err}");
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
// Set file transfer params if set
|
||||
match run_opts.remote {
|
||||
Remote::Bookmark(BookmarkParams { name, password }) => {
|
||||
if let Err(err) = manager.resolve_bookmark_name(&name, password.as_deref()) {
|
||||
eprintln!("{err}");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
Remote::Host(HostParams { params, password }) => {
|
||||
if let Err(err) = manager.set_filetransfer_params(params, password.as_deref()) {
|
||||
eprintln!("{err}");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
Remote::None => {}
|
||||
}
|
||||
manager.run(activity);
|
||||
Task::ImportTheme(theme) => run_import_theme(&theme),
|
||||
Task::InstallUpdate => run_install_update(),
|
||||
Task::Activity(activity) => run_activity(activity, run_opts.ticks, run_opts.remote),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_import_theme(theme: &Path) -> i32 {
|
||||
match support::import_theme(theme) {
|
||||
Ok(_) => {
|
||||
println!("Theme has been successfully imported!");
|
||||
0
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("{err}");
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_install_update() -> i32 {
|
||||
match support::install_update() {
|
||||
Ok(msg) => {
|
||||
println!("{msg}");
|
||||
0
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("Could not install update: {err}");
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_activity(activity: NextActivity, ticks: Duration, remote: Remote) -> i32 {
|
||||
// Create activity manager (and context too)
|
||||
let mut manager: ActivityManager = match ActivityManager::new(ticks) {
|
||||
Ok(m) => m,
|
||||
Err(err) => {
|
||||
eprintln!("Could not start activity manager: {err}");
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
// Set file transfer params if set
|
||||
match remote {
|
||||
Remote::Bookmark(BookmarkParams { name, password }) => {
|
||||
if let Err(err) = manager.resolve_bookmark_name(&name, password.as_deref()) {
|
||||
eprintln!("{err}");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
Remote::Host(HostParams { params, password }) => {
|
||||
if let Err(err) = manager.set_filetransfer_params(params, password.as_deref()) {
|
||||
eprintln!("{err}");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
Remote::None => {}
|
||||
}
|
||||
manager.run(activity);
|
||||
0
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ pub mod auto_update;
|
||||
pub mod bookmarks_client;
|
||||
pub mod config_client;
|
||||
pub mod environment;
|
||||
pub(self) mod keys;
|
||||
mod keys;
|
||||
pub mod logging;
|
||||
pub mod notifications;
|
||||
pub mod sshkey_storage;
|
||||
|
||||
@@ -150,7 +150,13 @@ impl AuthActivity {
|
||||
self.mount_protocol(bookmark.protocol);
|
||||
self.mount_remote_directory(
|
||||
bookmark
|
||||
.entry_directory
|
||||
.remote_path
|
||||
.map(|x| x.to_string_lossy().to_string())
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
self.mount_local_directory(
|
||||
bookmark
|
||||
.local_path
|
||||
.map(|x| x.to_string_lossy().to_string())
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
|
||||
@@ -8,13 +8,12 @@ use tuirealm::event::{Key, KeyEvent, KeyModifiers};
|
||||
use tuirealm::props::{Alignment, BorderType, Borders, Color, InputType, Style};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent, State, StateValue};
|
||||
|
||||
use super::{FileTransferProtocol, FormMsg, Msg, UiMsg};
|
||||
use crate::ui::activities::auth::{
|
||||
RADIO_PROTOCOL_FTP, RADIO_PROTOCOL_FTPS, RADIO_PROTOCOL_S3, RADIO_PROTOCOL_SCP,
|
||||
RADIO_PROTOCOL_SFTP, RADIO_PROTOCOL_SMB,
|
||||
};
|
||||
|
||||
use super::{FileTransferProtocol, FormMsg, Msg, UiMsg};
|
||||
|
||||
// -- protocol
|
||||
|
||||
#[derive(MockComponent)]
|
||||
@@ -118,7 +117,7 @@ impl InputRemoteDirectory {
|
||||
)
|
||||
.foreground(color)
|
||||
.placeholder("/home/foo", Style::default().fg(Color::Rgb(128, 128, 128)))
|
||||
.title("Default remote directory", Alignment::Left)
|
||||
.title("Default remote working directory", Alignment::Left)
|
||||
.input_type(InputType::Text)
|
||||
.value(remote_dir),
|
||||
}
|
||||
@@ -136,6 +135,42 @@ impl Component<Msg, NoUserEvent> for InputRemoteDirectory {
|
||||
}
|
||||
}
|
||||
|
||||
// -- remote directory
|
||||
|
||||
#[derive(MockComponent)]
|
||||
pub struct InputLocalDirectory {
|
||||
component: Input,
|
||||
}
|
||||
|
||||
impl InputLocalDirectory {
|
||||
pub fn new(local_dir: &str, color: Color) -> Self {
|
||||
Self {
|
||||
component: Input::default()
|
||||
.borders(
|
||||
Borders::default()
|
||||
.color(color)
|
||||
.modifiers(BorderType::Rounded),
|
||||
)
|
||||
.foreground(color)
|
||||
.placeholder("/home/foo", Style::default().fg(Color::Rgb(128, 128, 128)))
|
||||
.title("Default local working directory", Alignment::Left)
|
||||
.input_type(InputType::Text)
|
||||
.value(local_dir),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for InputLocalDirectory {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
handle_input_ev(
|
||||
self,
|
||||
ev,
|
||||
Msg::Ui(UiMsg::LocalDirectoryBlurDown),
|
||||
Msg::Ui(UiMsg::LocalDirectoryBlurUp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// -- address
|
||||
|
||||
#[derive(MockComponent)]
|
||||
|
||||
@@ -16,9 +16,10 @@ pub use bookmarks::{
|
||||
#[cfg(unix)]
|
||||
pub use form::InputSmbWorkgroup;
|
||||
pub use form::{
|
||||
InputAddress, InputPassword, InputPort, InputRemoteDirectory, InputS3AccessKey, InputS3Bucket,
|
||||
InputS3Endpoint, InputS3Profile, InputS3Region, InputS3SecretAccessKey, InputS3SecurityToken,
|
||||
InputS3SessionToken, InputSmbShare, InputUsername, ProtocolRadio, RadioS3NewPathStyle,
|
||||
InputAddress, InputLocalDirectory, InputPassword, InputPort, InputRemoteDirectory,
|
||||
InputS3AccessKey, InputS3Bucket, InputS3Endpoint, InputS3Profile, InputS3Region,
|
||||
InputS3SecretAccessKey, InputS3SecurityToken, InputS3SessionToken, InputSmbShare,
|
||||
InputUsername, ProtocolRadio, RadioS3NewPathStyle,
|
||||
};
|
||||
pub use popup::{
|
||||
ErrorPopup, InfoPopup, InstallUpdatePopup, Keybindings, QuitPopup, ReleaseNotes, WaitPopup,
|
||||
|
||||
@@ -59,7 +59,8 @@ impl AuthActivity {
|
||||
Ok(FileTransferParams {
|
||||
protocol,
|
||||
params: ProtocolParams::Generic(params),
|
||||
entry_directory: self.get_input_remote_directory(),
|
||||
local_path: self.get_input_local_directory(),
|
||||
remote_path: self.get_input_remote_directory(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -72,7 +73,8 @@ impl AuthActivity {
|
||||
Ok(FileTransferParams {
|
||||
protocol: FileTransferProtocol::AwsS3,
|
||||
params: ProtocolParams::AwsS3(params),
|
||||
entry_directory: self.get_input_remote_directory(),
|
||||
local_path: self.get_input_local_directory(),
|
||||
remote_path: self.get_input_remote_directory(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -91,7 +93,8 @@ impl AuthActivity {
|
||||
Ok(FileTransferParams {
|
||||
protocol: FileTransferProtocol::Smb,
|
||||
params: ProtocolParams::Smb(params),
|
||||
entry_directory: self.get_input_remote_directory(),
|
||||
local_path: self.get_input_local_directory(),
|
||||
remote_path: self.get_input_remote_directory(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ pub enum Id {
|
||||
InfoPopup,
|
||||
InstallUpdatePopup,
|
||||
Keybindings,
|
||||
LocalDirectory,
|
||||
NewVersionChangelog,
|
||||
NewVersionDisclaimer,
|
||||
Password,
|
||||
@@ -108,6 +109,8 @@ pub enum UiMsg {
|
||||
CloseKeybindingsPopup,
|
||||
CloseQuitPopup,
|
||||
CloseSaveBookmark,
|
||||
LocalDirectoryBlurDown,
|
||||
LocalDirectoryBlurUp,
|
||||
ParamsFormBlur,
|
||||
PasswordBlurDown,
|
||||
PasswordBlurUp,
|
||||
|
||||
@@ -158,6 +158,12 @@ impl AuthActivity {
|
||||
assert!(self.app.umount(&Id::BookmarkName).is_ok());
|
||||
assert!(self.app.umount(&Id::BookmarkSavePassword).is_ok());
|
||||
}
|
||||
UiMsg::LocalDirectoryBlurDown => {
|
||||
assert!(self.app.active(&Id::Protocol).is_ok());
|
||||
}
|
||||
UiMsg::LocalDirectoryBlurUp => {
|
||||
assert!(self.app.active(&Id::RemoteDirectory).is_ok());
|
||||
}
|
||||
UiMsg::ParamsFormBlur => {
|
||||
assert!(self.app.active(&Id::BookmarksList).is_ok());
|
||||
}
|
||||
@@ -201,13 +207,13 @@ impl AuthActivity {
|
||||
.is_ok());
|
||||
}
|
||||
UiMsg::ProtocolBlurUp => {
|
||||
assert!(self.app.active(&Id::RemoteDirectory).is_ok());
|
||||
assert!(self.app.active(&Id::LocalDirectory).is_ok());
|
||||
}
|
||||
UiMsg::RececentsListBlur => {
|
||||
assert!(self.app.active(&Id::BookmarksList).is_ok());
|
||||
}
|
||||
UiMsg::RemoteDirectoryBlurDown => {
|
||||
assert!(self.app.active(&Id::Protocol).is_ok());
|
||||
assert!(self.app.active(&Id::LocalDirectory).is_ok());
|
||||
}
|
||||
UiMsg::RemoteDirectoryBlurUp => {
|
||||
assert!(self
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use tuirealm::props::Color;
|
||||
use tuirealm::tui::layout::{Constraint, Direction, Layout};
|
||||
use tuirealm::tui::widgets::Clear;
|
||||
use tuirealm::{State, StateValue, Sub, SubClause, SubEventClause};
|
||||
@@ -43,6 +44,7 @@ impl AuthActivity {
|
||||
// Auth form
|
||||
self.mount_protocol(default_protocol);
|
||||
self.mount_remote_directory("");
|
||||
self.mount_local_directory("");
|
||||
self.mount_address("");
|
||||
self.mount_port(Self::get_default_port_for_protocol(default_protocol));
|
||||
self.mount_username("");
|
||||
@@ -412,12 +414,11 @@ impl AuthActivity {
|
||||
/// Mount size error
|
||||
pub(super) fn mount_size_err(&mut self) {
|
||||
// Mount
|
||||
let err_color = self.theme().misc_error_dialog;
|
||||
assert!(self
|
||||
.app
|
||||
.remount(
|
||||
Id::WindowSizeError,
|
||||
Box::new(components::WindowSizeError::new(err_color)),
|
||||
Box::new(components::WindowSizeError::new(Color::Red)),
|
||||
vec![]
|
||||
)
|
||||
.is_ok());
|
||||
@@ -582,14 +583,14 @@ impl AuthActivity {
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
pub(super) fn mount_remote_directory<S: AsRef<str>>(&mut self, entry_directory: S) {
|
||||
pub(super) fn mount_remote_directory<S: AsRef<str>>(&mut self, remote_path: S) {
|
||||
let protocol_color = self.theme().auth_protocol;
|
||||
assert!(self
|
||||
.app
|
||||
.remount(
|
||||
Id::RemoteDirectory,
|
||||
Box::new(components::InputRemoteDirectory::new(
|
||||
entry_directory.as_ref(),
|
||||
remote_path.as_ref(),
|
||||
protocol_color
|
||||
)),
|
||||
vec![]
|
||||
@@ -597,6 +598,21 @@ impl AuthActivity {
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
pub(super) fn mount_local_directory<S: AsRef<str>>(&mut self, local_path: S) {
|
||||
let color = self.theme().auth_username;
|
||||
assert!(self
|
||||
.app
|
||||
.remount(
|
||||
Id::LocalDirectory,
|
||||
Box::new(components::InputLocalDirectory::new(
|
||||
local_path.as_ref(),
|
||||
color
|
||||
)),
|
||||
vec![]
|
||||
)
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
pub(super) fn mount_address(&mut self, address: &str) {
|
||||
let addr_color = self.theme().auth_address;
|
||||
assert!(self
|
||||
@@ -853,6 +869,15 @@ impl AuthActivity {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn get_input_local_directory(&self) -> Option<PathBuf> {
|
||||
match self.app.state(&Id::LocalDirectory) {
|
||||
Ok(State::One(StateValue::String(x))) if !x.is_empty() => {
|
||||
Some(PathBuf::from(x.as_str()))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn get_input_addr(&self) -> String {
|
||||
match self.app.state(&Id::Address) {
|
||||
Ok(State::One(StateValue::String(x))) => x,
|
||||
@@ -1053,6 +1078,12 @@ impl AuthActivity {
|
||||
Some(&Id::RemoteDirectory) => {
|
||||
[Id::Port, Id::Username, Id::Password, Id::RemoteDirectory]
|
||||
}
|
||||
Some(&Id::LocalDirectory) => [
|
||||
Id::Username,
|
||||
Id::Password,
|
||||
Id::RemoteDirectory,
|
||||
Id::LocalDirectory,
|
||||
],
|
||||
_ => [Id::Address, Id::Port, Id::Username, Id::Password],
|
||||
}
|
||||
}
|
||||
@@ -1093,6 +1124,12 @@ impl AuthActivity {
|
||||
Id::S3NewPathStyle,
|
||||
Id::RemoteDirectory,
|
||||
],
|
||||
Some(&Id::LocalDirectory) => [
|
||||
Id::S3SessionToken,
|
||||
Id::S3NewPathStyle,
|
||||
Id::RemoteDirectory,
|
||||
Id::LocalDirectory,
|
||||
],
|
||||
_ => [Id::S3Bucket, Id::S3Region, Id::S3Endpoint, Id::S3Profile],
|
||||
}
|
||||
}
|
||||
@@ -1111,6 +1148,12 @@ impl AuthActivity {
|
||||
Id::SmbWorkgroup,
|
||||
Id::RemoteDirectory,
|
||||
],
|
||||
Some(&Id::LocalDirectory) => [
|
||||
Id::Password,
|
||||
Id::SmbWorkgroup,
|
||||
Id::RemoteDirectory,
|
||||
Id::LocalDirectory,
|
||||
],
|
||||
_ => [Id::Address, Id::Port, Id::SmbShare, Id::Username],
|
||||
}
|
||||
}
|
||||
@@ -1127,6 +1170,12 @@ impl AuthActivity {
|
||||
Id::Password,
|
||||
Id::RemoteDirectory,
|
||||
],
|
||||
Some(&Id::LocalDirectory) => [
|
||||
Id::Username,
|
||||
Id::Password,
|
||||
Id::RemoteDirectory,
|
||||
Id::LocalDirectory,
|
||||
],
|
||||
_ => [Id::Address, Id::SmbShare, Id::Username, Id::Password],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
//! `filetransfer_activiy` is the module which implements the Filetransfer activity, which is the main activity afterall
|
||||
|
||||
use remotefs::fs::UnixPex;
|
||||
pub(self) use remotefs::File;
|
||||
use remotefs::File;
|
||||
use tuirealm::{State, StateValue};
|
||||
|
||||
pub(self) use super::browser::FileExplorerTab;
|
||||
pub(self) use super::{
|
||||
use super::browser::FileExplorerTab;
|
||||
use super::{
|
||||
FileTransferActivity, Id, LogLevel, Msg, PendingActionMsg, TransferMsg, TransferOpts,
|
||||
TransferPayload, UiMsg,
|
||||
};
|
||||
|
||||
@@ -750,6 +750,7 @@ impl KeybindingsPopup {
|
||||
.add_col(TextSpan::from(
|
||||
" Open text file with preferred editor",
|
||||
))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<P>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Toggle log panel"))
|
||||
.add_row()
|
||||
@@ -785,6 +786,7 @@ impl KeybindingsPopup {
|
||||
.add_col(TextSpan::from(
|
||||
" Toggle synchronized browsing",
|
||||
))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<Z>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Change file permissions"))
|
||||
.add_row()
|
||||
|
||||
@@ -7,7 +7,7 @@ use tuirealm::props::{
|
||||
Alignment, AttrValue, Attribute, Borders, Color, Style, Table, TextModifiers,
|
||||
};
|
||||
use tuirealm::tui::layout::Corner;
|
||||
use tuirealm::tui::text::{Span, Spans};
|
||||
use tuirealm::tui::text::{Line, Span};
|
||||
use tuirealm::tui::widgets::{List as TuiList, ListItem, ListState};
|
||||
use tuirealm::{MockComponent, Props, State, StateValue};
|
||||
|
||||
@@ -211,7 +211,7 @@ impl MockComponent for FileList {
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
ListItem::new(Spans::from(columns))
|
||||
ListItem::new(Line::from(columns))
|
||||
})
|
||||
.collect(), // Make List item from TextSpan
|
||||
_ => Vec::new(),
|
||||
|
||||
@@ -18,11 +18,11 @@ use std::time::Duration;
|
||||
|
||||
// Includes
|
||||
use chrono::{DateTime, Local};
|
||||
pub(self) use lib::browser;
|
||||
use lib::browser;
|
||||
use lib::browser::Browser;
|
||||
use lib::transfer::{TransferOpts, TransferStates};
|
||||
use remotefs::RemoteFs;
|
||||
pub(self) use session::TransferPayload;
|
||||
use session::TransferPayload;
|
||||
use tempfile::TempDir;
|
||||
use tuirealm::{Application, EventListenerCfg, NoUserEvent};
|
||||
|
||||
@@ -220,7 +220,7 @@ pub struct FileTransferActivity {
|
||||
cache: Option<TempDir>,
|
||||
/// Fs watcher
|
||||
fswatcher: Option<FsWatcher>,
|
||||
/// conncted once
|
||||
/// connected once
|
||||
connected: bool,
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ use std::time::Instant;
|
||||
// Ext
|
||||
use bytesize::ByteSize;
|
||||
use remotefs::fs::{File, Metadata, ReadStream, UnixPex, Welcome, WriteStream};
|
||||
use remotefs::{RemoteError, RemoteErrorType};
|
||||
use remotefs::{RemoteError, RemoteErrorType, RemoteResult};
|
||||
use thiserror::Error;
|
||||
|
||||
use super::{FileTransferActivity, LogLevel};
|
||||
@@ -53,7 +53,7 @@ impl FileTransferActivity {
|
||||
/// Connect to remote
|
||||
pub(super) fn connect(&mut self) {
|
||||
let ft_params = self.context().ft_params().unwrap().clone();
|
||||
let entry_dir: Option<PathBuf> = ft_params.entry_directory;
|
||||
let entry_dir: Option<PathBuf> = ft_params.remote_path;
|
||||
// Connect to remote
|
||||
match self.client.connect() {
|
||||
Ok(Welcome { banner, .. }) => {
|
||||
@@ -71,11 +71,11 @@ impl FileTransferActivity {
|
||||
}
|
||||
// Try to change directory to entry directory
|
||||
let mut remote_chdir: Option<PathBuf> = None;
|
||||
if let Some(entry_directory) = &entry_dir {
|
||||
remote_chdir = Some(entry_directory.clone());
|
||||
if let Some(remote_path) = &entry_dir {
|
||||
remote_chdir = Some(remote_path.clone());
|
||||
}
|
||||
if let Some(entry_directory) = remote_chdir {
|
||||
self.remote_changedir(entry_directory.as_path(), false);
|
||||
if let Some(remote_path) = remote_chdir {
|
||||
self.remote_changedir(remote_path.as_path(), false);
|
||||
}
|
||||
// Set state to explorer
|
||||
self.umount_wait();
|
||||
@@ -115,9 +115,10 @@ impl FileTransferActivity {
|
||||
if let Ok(wrkdir) = self.client.pwd() {
|
||||
self.mount_blocking_wait("Loading remote directory...");
|
||||
|
||||
self.remote_scan(wrkdir.as_path());
|
||||
// Set wrkdir
|
||||
self.remote_mut().wrkdir = wrkdir;
|
||||
if self.remote_scan(wrkdir.as_path()).is_ok() {
|
||||
// Set wrkdir
|
||||
self.remote_mut().wrkdir = wrkdir;
|
||||
}
|
||||
|
||||
self.umount_wait();
|
||||
}
|
||||
@@ -128,40 +129,47 @@ impl FileTransferActivity {
|
||||
self.mount_blocking_wait("Loading local directory...");
|
||||
|
||||
let wrkdir: PathBuf = self.host.pwd();
|
||||
self.local_scan(wrkdir.as_path());
|
||||
self.local_mut().wrkdir = wrkdir;
|
||||
|
||||
if self.local_scan(wrkdir.as_path()).is_ok() {
|
||||
self.local_mut().wrkdir = wrkdir;
|
||||
}
|
||||
|
||||
self.umount_wait();
|
||||
}
|
||||
|
||||
/// Scan current local directory
|
||||
fn local_scan(&mut self, path: &Path) {
|
||||
fn local_scan(&mut self, path: &Path) -> Result<(), HostError> {
|
||||
match self.host.scan_dir(path) {
|
||||
Ok(files) => {
|
||||
// Set files and sort (sorting is implicit)
|
||||
self.local_mut().set_files(files);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => {
|
||||
self.log_and_alert(
|
||||
LogLevel::Error,
|
||||
format!("Could not scan current directory: {err}"),
|
||||
);
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Scan current remote directory
|
||||
fn remote_scan(&mut self, path: &Path) {
|
||||
fn remote_scan(&mut self, path: &Path) -> RemoteResult<()> {
|
||||
match self.client.list_dir(path) {
|
||||
Ok(files) => {
|
||||
// Set files and sort (sorting is implicit)
|
||||
self.remote_mut().set_files(files);
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => {
|
||||
self.log_and_alert(
|
||||
LogLevel::Error,
|
||||
format!("Could not scan current directory: {err}"),
|
||||
);
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,7 +366,7 @@ impl SetupActivity {
|
||||
.get_color(&Id::Theme(IdTheme::StatusSync))
|
||||
.map_err(|_| Id::Theme(IdTheme::StatusSync))?;
|
||||
// Update theme
|
||||
let mut theme: &mut Theme = self.theme_mut();
|
||||
let theme = self.theme_mut();
|
||||
theme.auth_address = auth_address;
|
||||
theme.auth_bookmarks = auth_bookmarks;
|
||||
theme.auth_password = auth_password;
|
||||
|
||||
@@ -73,7 +73,7 @@ static REMOTE_SMB_OPT_REGEX: Lazy<Regex> = lazy_regex!(
|
||||
* - group 3: share
|
||||
* - group 4: remote-dir?
|
||||
*/
|
||||
#[cfg(windows)]
|
||||
#[cfg(smb_windows)]
|
||||
static REMOTE_SMB_OPT_REGEX: Lazy<Regex> =
|
||||
lazy_regex!(r"(?:([^@]+)@)?(?:([^:\\]+))(?:\\([^\\]+))?(?:(\\.+))?");
|
||||
|
||||
@@ -171,7 +171,7 @@ fn parse_remote_opt_protocol(
|
||||
let protocol = match protocol {
|
||||
Some(Ok(protocol)) => protocol,
|
||||
Some(Err(err)) => return Err(err),
|
||||
#[cfg(windows)]
|
||||
#[cfg(smb_windows)]
|
||||
None if groups.get(2).is_some() => FileTransferProtocol::Smb,
|
||||
None => default,
|
||||
};
|
||||
@@ -218,7 +218,7 @@ fn parse_generic_remote_opt(
|
||||
},
|
||||
};
|
||||
// Get workdir
|
||||
let entry_directory: Option<PathBuf> =
|
||||
let remote_path: Option<PathBuf> =
|
||||
groups.get(4).map(|group| PathBuf::from(group.as_str()));
|
||||
let params: ProtocolParams = ProtocolParams::Generic(
|
||||
GenericProtocolParams::default()
|
||||
@@ -226,7 +226,7 @@ fn parse_generic_remote_opt(
|
||||
.port(port)
|
||||
.username(username),
|
||||
);
|
||||
Ok(FileTransferParams::new(protocol, params).entry_directory(entry_directory))
|
||||
Ok(FileTransferParams::new(protocol, params).remote_path(remote_path))
|
||||
}
|
||||
None => Err(String::from("Bad remote host syntax!")),
|
||||
}
|
||||
@@ -245,13 +245,13 @@ fn parse_s3_remote_opt(s: &str) -> Result<FileTransferParams, String> {
|
||||
.map(|x| x.as_str().to_string())
|
||||
.unwrap_or_default();
|
||||
let profile: Option<String> = groups.get(3).map(|x| x.as_str().to_string());
|
||||
let entry_directory: Option<PathBuf> =
|
||||
let remote_path: Option<PathBuf> =
|
||||
groups.get(4).map(|group| PathBuf::from(group.as_str()));
|
||||
Ok(FileTransferParams::new(
|
||||
FileTransferProtocol::AwsS3,
|
||||
ProtocolParams::AwsS3(AwsS3Params::new(bucket, Some(region), profile)),
|
||||
)
|
||||
.entry_directory(entry_directory))
|
||||
.remote_path(remote_path))
|
||||
}
|
||||
None => Err(String::from("Bad remote host syntax!")),
|
||||
}
|
||||
@@ -282,20 +282,20 @@ fn parse_smb_remote_opts(s: &str) -> Result<FileTransferParams, String> {
|
||||
Some(group) => group.as_str().to_string(),
|
||||
None => return Err(String::from("Missing address")),
|
||||
};
|
||||
let entry_directory: Option<PathBuf> =
|
||||
let remote_path: Option<PathBuf> =
|
||||
groups.get(5).map(|group| PathBuf::from(group.as_str()));
|
||||
|
||||
Ok(FileTransferParams::new(
|
||||
FileTransferProtocol::Smb,
|
||||
ProtocolParams::Smb(SmbParams::new(address, share).port(port).username(username)),
|
||||
)
|
||||
.entry_directory(entry_directory))
|
||||
.remote_path(remote_path))
|
||||
}
|
||||
None => Err(String::from("Bad remote host syntax!")),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[cfg(smb_windows)]
|
||||
fn parse_smb_remote_opts(s: &str) -> Result<FileTransferParams, String> {
|
||||
match REMOTE_SMB_OPT_REGEX.captures(s) {
|
||||
Some(groups) => {
|
||||
@@ -308,14 +308,14 @@ fn parse_smb_remote_opts(s: &str) -> Result<FileTransferParams, String> {
|
||||
Some(group) => group.as_str().to_string(),
|
||||
None => return Err(String::from("Missing address")),
|
||||
};
|
||||
let entry_directory: Option<PathBuf> =
|
||||
let remote_path: Option<PathBuf> =
|
||||
groups.get(4).map(|group| PathBuf::from(group.as_str()));
|
||||
|
||||
Ok(FileTransferParams::new(
|
||||
FileTransferProtocol::Smb,
|
||||
ProtocolParams::Smb(SmbParams::new(address, share).username(username)),
|
||||
)
|
||||
.entry_directory(entry_directory))
|
||||
.remote_path(remote_path))
|
||||
}
|
||||
None => Err(String::from("Bad remote host syntax!")),
|
||||
}
|
||||
@@ -441,7 +441,7 @@ mod tests {
|
||||
params.username.as_deref().unwrap().to_string(),
|
||||
String::from("root")
|
||||
);
|
||||
assert!(result.entry_directory.is_none());
|
||||
assert!(result.remote_path.is_none());
|
||||
// User + port
|
||||
let result: FileTransferParams = parse_remote_opt(&String::from("root@172.26.104.1:8022"))
|
||||
.ok()
|
||||
@@ -454,7 +454,7 @@ mod tests {
|
||||
String::from("root")
|
||||
);
|
||||
assert_eq!(result.protocol, FileTransferProtocol::Sftp);
|
||||
assert!(result.entry_directory.is_none());
|
||||
assert!(result.remote_path.is_none());
|
||||
// Port only
|
||||
let result: FileTransferParams = parse_remote_opt(&String::from("172.26.104.1:4022"))
|
||||
.ok()
|
||||
@@ -464,7 +464,7 @@ mod tests {
|
||||
assert_eq!(params.address, String::from("172.26.104.1"));
|
||||
assert_eq!(params.port, 4022);
|
||||
assert!(params.username.is_none());
|
||||
assert!(result.entry_directory.is_none());
|
||||
assert!(result.remote_path.is_none());
|
||||
// Protocol
|
||||
let result: FileTransferParams = parse_remote_opt(&String::from("ftp://172.26.104.1"))
|
||||
.ok()
|
||||
@@ -474,7 +474,7 @@ mod tests {
|
||||
assert_eq!(params.address, String::from("172.26.104.1"));
|
||||
assert_eq!(params.port, 21); // Fallback to ftp default
|
||||
assert!(params.username.is_none()); // Doesn't fall back
|
||||
assert!(result.entry_directory.is_none());
|
||||
assert!(result.remote_path.is_none());
|
||||
// Protocol
|
||||
let result: FileTransferParams = parse_remote_opt(&String::from("sftp://172.26.104.1"))
|
||||
.ok()
|
||||
@@ -484,7 +484,7 @@ mod tests {
|
||||
assert_eq!(params.address, String::from("172.26.104.1"));
|
||||
assert_eq!(params.port, 22); // Fallback to sftp default
|
||||
assert!(params.username.is_none()); // Doesn't fall back
|
||||
assert!(result.entry_directory.is_none());
|
||||
assert!(result.remote_path.is_none());
|
||||
let result: FileTransferParams = parse_remote_opt(&String::from("scp://172.26.104.1"))
|
||||
.ok()
|
||||
.unwrap();
|
||||
@@ -493,7 +493,7 @@ mod tests {
|
||||
assert_eq!(params.address, String::from("172.26.104.1"));
|
||||
assert_eq!(params.port, 22); // Fallback to scp default
|
||||
assert!(params.username.is_none()); // Doesn't fall back
|
||||
assert!(result.entry_directory.is_none());
|
||||
assert!(result.remote_path.is_none());
|
||||
// Protocol + user
|
||||
let result: FileTransferParams =
|
||||
parse_remote_opt(&String::from("ftps://anon@172.26.104.1"))
|
||||
@@ -507,7 +507,7 @@ mod tests {
|
||||
params.username.as_deref().unwrap().to_string(),
|
||||
String::from("anon")
|
||||
);
|
||||
assert!(result.entry_directory.is_none());
|
||||
assert!(result.remote_path.is_none());
|
||||
// Path
|
||||
let result: FileTransferParams =
|
||||
parse_remote_opt(&String::from("root@172.26.104.1:8022:/var"))
|
||||
@@ -521,7 +521,7 @@ mod tests {
|
||||
params.username.as_deref().unwrap().to_string(),
|
||||
String::from("root")
|
||||
);
|
||||
assert_eq!(result.entry_directory.unwrap(), PathBuf::from("/var"));
|
||||
assert_eq!(result.remote_path.unwrap(), PathBuf::from("/var"));
|
||||
// Port only
|
||||
let result: FileTransferParams = parse_remote_opt(&String::from("172.26.104.1:home"))
|
||||
.ok()
|
||||
@@ -531,7 +531,7 @@ mod tests {
|
||||
assert_eq!(params.address, String::from("172.26.104.1"));
|
||||
assert_eq!(params.port, 22);
|
||||
assert!(params.username.is_none());
|
||||
assert_eq!(result.entry_directory.unwrap(), PathBuf::from("home"));
|
||||
assert_eq!(result.remote_path.unwrap(), PathBuf::from("home"));
|
||||
// All together now
|
||||
let result: FileTransferParams =
|
||||
parse_remote_opt(&String::from("ftp://anon@172.26.104.1:8021:/tmp"))
|
||||
@@ -545,7 +545,7 @@ mod tests {
|
||||
params.username.as_deref().unwrap().to_string(),
|
||||
String::from("anon")
|
||||
);
|
||||
assert_eq!(result.entry_directory.unwrap(), PathBuf::from("/tmp"));
|
||||
assert_eq!(result.remote_path.unwrap(), PathBuf::from("/tmp"));
|
||||
// bad syntax
|
||||
// Bad protocol
|
||||
assert!(parse_remote_opt(&String::from("omar://172.26.104.1")).is_err());
|
||||
@@ -562,7 +562,7 @@ mod tests {
|
||||
.unwrap();
|
||||
let params = result.params.s3_params().unwrap();
|
||||
assert_eq!(result.protocol, FileTransferProtocol::AwsS3);
|
||||
assert_eq!(result.entry_directory, None);
|
||||
assert_eq!(result.remote_path, None);
|
||||
assert_eq!(params.bucket_name.as_str(), "mybucket");
|
||||
assert_eq!(params.region.as_deref().unwrap(), "eu-central-1");
|
||||
assert_eq!(params.profile, None);
|
||||
@@ -573,7 +573,7 @@ mod tests {
|
||||
.unwrap();
|
||||
let params = result.params.s3_params().unwrap();
|
||||
assert_eq!(result.protocol, FileTransferProtocol::AwsS3);
|
||||
assert_eq!(result.entry_directory, None);
|
||||
assert_eq!(result.remote_path, None);
|
||||
assert_eq!(params.bucket_name.as_str(), "mybucket");
|
||||
assert_eq!(params.region.as_deref().unwrap(), "eu-central-1");
|
||||
assert_eq!(params.profile.as_deref(), Some("default"));
|
||||
@@ -584,7 +584,7 @@ mod tests {
|
||||
.unwrap();
|
||||
let params = result.params.s3_params().unwrap();
|
||||
assert_eq!(result.protocol, FileTransferProtocol::AwsS3);
|
||||
assert_eq!(result.entry_directory, Some(PathBuf::from("/foobar")));
|
||||
assert_eq!(result.remote_path, Some(PathBuf::from("/foobar")));
|
||||
assert_eq!(params.bucket_name.as_str(), "mybucket");
|
||||
assert_eq!(params.region.as_deref().unwrap(), "eu-central-1");
|
||||
assert_eq!(params.profile, None);
|
||||
@@ -595,7 +595,7 @@ mod tests {
|
||||
.unwrap();
|
||||
let params = result.params.s3_params().unwrap();
|
||||
assert_eq!(result.protocol, FileTransferProtocol::AwsS3);
|
||||
assert_eq!(result.entry_directory, Some(PathBuf::from("/foobar")));
|
||||
assert_eq!(result.remote_path, Some(PathBuf::from("/foobar")));
|
||||
assert_eq!(params.bucket_name.as_str(), "mybucket");
|
||||
assert_eq!(params.region.as_deref().unwrap(), "eu-central-1");
|
||||
assert_eq!(params.profile.as_deref(), Some("default"));
|
||||
@@ -615,7 +615,7 @@ mod tests {
|
||||
assert!(params.username.is_some());
|
||||
assert!(params.password.is_none());
|
||||
assert!(params.workgroup.is_none());
|
||||
assert!(result.entry_directory.is_none());
|
||||
assert!(result.remote_path.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -633,13 +633,13 @@ mod tests {
|
||||
assert!(params.workgroup.is_none());
|
||||
assert_eq!(params.share.as_str(), "myshare");
|
||||
assert_eq!(
|
||||
result.entry_directory.as_deref().unwrap(),
|
||||
result.remote_path.as_deref().unwrap(),
|
||||
std::path::Path::new("/dir/subdir")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
#[cfg(smb_windows)]
|
||||
fn should_parse_smb_address() {
|
||||
let result = parse_remote_opt(&String::from("\\\\myserver\\myshare"))
|
||||
.ok()
|
||||
@@ -648,11 +648,11 @@ mod tests {
|
||||
|
||||
assert_eq!(params.address.as_str(), "myserver");
|
||||
assert_eq!(params.share.as_str(), "myshare");
|
||||
assert!(result.entry_directory.is_none());
|
||||
assert!(result.remote_path.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
#[cfg(smb_windows)]
|
||||
fn should_parse_smb_address_with_opts() {
|
||||
let result = parse_remote_opt(&String::from("\\\\omar@myserver\\myshare\\path"))
|
||||
.ok()
|
||||
@@ -663,7 +663,7 @@ mod tests {
|
||||
assert_eq!(params.share.as_str(), "myshare");
|
||||
assert_eq!(params.username.as_deref().unwrap(), "omar");
|
||||
assert_eq!(
|
||||
result.entry_directory.as_deref().unwrap(),
|
||||
result.remote_path.as_deref().unwrap(),
|
||||
std::path::Path::new("\\path")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ pub fn parse_ssh2_config(path: &str) -> Result<SshConfig, String> {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use crate::utils::{ssh::parse_ssh2_config, test_helpers};
|
||||
use crate::utils::ssh::parse_ssh2_config;
|
||||
use crate::utils::test_helpers;
|
||||
|
||||
#[test]
|
||||
fn should_parse_ssh2_config() {
|
||||
|
||||
26
tailwind.config.js
Normal file
26
tailwind.config.js
Normal file
@@ -0,0 +1,26 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["./site/**/*.{html,js}"],
|
||||
darkMode: "class",
|
||||
theme: {
|
||||
screens: {
|
||||
sm: { max: "640px" },
|
||||
md: "768px",
|
||||
lg: "1024px",
|
||||
xl: "1280px",
|
||||
"2xl": "1536px",
|
||||
},
|
||||
extend: {
|
||||
colors: {
|
||||
brand: "#31363b",
|
||||
},
|
||||
fontSize: {
|
||||
xl: "1.5rem",
|
||||
"2xl": "2rem",
|
||||
"3xl": "3.5rem",
|
||||
"4xl": "7rem",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
Reference in New Issue
Block a user