28 Commits

Author SHA1 Message Date
veeso
92d20f33fe new site 2023-10-01 16:20:35 +02:00
veeso
15caef83e6 0.12.2 2023-10-01 16:20:04 +02:00
veeso
19992402e2 fix: panic if the terminal screen is too small 2023-10-01 16:18:11 +02:00
veeso
182bbb94a2 0.12.2 2023-10-01 16:12:47 +02:00
Christian Visintin
20c3e22572 [BUG] - termscp not respecting port in ssh config (#216) 2023-10-01 16:12:43 +02:00
veeso
d3c2c084db fix: fmt 2023-08-23 10:11:13 +02:00
veeso
cfbecc049d feat: tui-realm 1.9 2023-08-23 09:18:09 +02:00
Christian Visintin
9de171fb83 added cfg smb-windows to be able to disable SMB on windows (#210)
* added cfg smb-windows to be able to disable SMB on windows

* lint
2023-08-20 10:24:34 +02:00
veeso
2f48c765e3 termscp 0.12.0 2023-07-06 16:17:33 +02:00
Christian Visintin
ca005cbecd save local file paths in bookmark (#204)
* fix: renamed Bookmark 'directory' to 'remote_path' (keep name in file)

* feat: local_path as file transfer parameter and in bookmark
2023-07-06 16:05:22 +02:00
Christian Visintin
ee28d34f29 fix: don't update path breadcrumb if enter/scan dir failed (#203) 2023-07-06 14:55:53 +02:00
veeso
295191b9eb Merge branch 'main' into develop 2023-07-06 11:49:06 +02:00
veeso
16b2aa8596 fix: rustup target 2023-07-06 11:38:38 +02:00
Christian Visintin
522ee0355e feat: build artifacts workflow (#202) 2023-07-06 11:26:14 +02:00
veeso
e5ca956897 fix: don't run CI on site/.md change 2023-07-06 11:07:22 +02:00
Christian Visintin
a071f3d420 feat: smb is now an optional feature (#200) 2023-07-06 11:02:53 +02:00
veeso
66888c418c fix: deps 2023-07-06 09:59:25 +02:00
veeso
b5f712fa04 fix: better main runner 2023-07-06 09:49:35 +02:00
529
1509401599 Add missing row in KeybindingsPopup (#193) 2023-07-06 09:17:55 +02:00
veeso
6506f4ae59 fixed theme 2023-07-05 13:22:56 +02:00
veeso
5a5fa52b57 fixed theme 2023-07-05 12:58:21 +02:00
veeso
b7d4c68ebe new termscp site 2023-07-05 12:33:17 +02:00
Renan Dorneles Schuquel
00a6c53178 Fix: Some URL typos on 'install.sh' and 'README.md'. (#197)
Co-authored-by: rschuquel <renan.schuquel@unimedvtrp.com.br>
2023-06-23 21:54:38 +02:00
Christian Visintin
67bc1a2a08 Ignore backlog issues 2023-05-23 09:20:11 +02:00
Christian Visintin
0bb3c9a26e Create stale.yml 2023-05-22 23:22:57 +02:00
veeso
4394941c71 fix: readme site links 2023-05-20 15:01:22 +02:00
veeso
175ea23bfc fix: sh compliant macos.sh build script 2023-05-18 11:53:21 +02:00
veeso
cef100e5d9 fix: install workflow 2023-05-18 11:42:24 +02:00
73 changed files with 2059 additions and 1805 deletions

42
.github/workflows/build-artifacts.yml vendored Normal file
View 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/*

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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"

View File

@@ -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

View File

@@ -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

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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]

View File

@@ -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).
---

View File

@@ -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
View File

@@ -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

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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
View 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>

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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
View 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>

View 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>&nbsp;|&nbsp;</span>
<a class="text-xs font-thin" href="https://veeso.dev/en/privacy" target="_blank">Privacy policy</a>
<span>&nbsp;|&nbsp;</span>
<a class="text-xs font-thin" href="https://veeso.dev/en/cookie-policy" target="_blank">Cookie policy</a>
</p>
</div>
</div>

View 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" />&nbsp;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" />&nbsp;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>

View File

@@ -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>&nbsp;<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>&nbsp;<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>&nbsp;<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>&nbsp;<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>&nbsp;<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
View 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>&nbsp;
<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>

View File

@@ -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
>&nbsp;
<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>

View File

@@ -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>&nbsp;<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>&nbsp;<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>&nbsp;<span translate="updates.gui.title">GUI method</span></h2>
<h2 class="text-2xl font-thin"><i class="fa fa-desktop"></i>&nbsp;<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>&nbsp;<span translate="updates.cli.title">CLI method</span></h2>
<h2 class="text-2xl font-thin"><i class="fa fa-glasses"></i>&nbsp;<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

View File

@@ -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&nbsp;<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>&nbsp;<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>&nbsp;<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>&nbsp;|&nbsp;</span>
<a class="footer--link" href="https://veeso.dev/en/privacy" target="_blank"
>Privacy policy</a
>
<span>&nbsp;|&nbsp;</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
View 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;
}

View File

@@ -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();
});

View File

@@ -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());
});

View File

@@ -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": {

View File

@@ -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": {

View File

@@ -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": {

View File

@@ -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": {

View File

@@ -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

File diff suppressed because one or more lines are too long

2
site/robots.txt Normal file
View File

@@ -0,0 +1,2 @@
User-Agent: *
Allow: /

29
site/sitemap.xml Normal file
View 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
View 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
View 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>

View File

@@ -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

View File

@@ -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");

View File

@@ -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]

View File

@@ -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"));

View File

@@ -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(&params.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(&params.address).user {
if let Some(username) = &ssh_config.user {
debug!("found username from config: {username}");
opts = opts.username(username);
} else {

View File

@@ -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]

View File

@@ -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
}

View File

@@ -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;

View File

@@ -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(),
);

View File

@@ -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)]

View File

@@ -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,

View File

@@ -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(),
})
}

View File

@@ -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,

View File

@@ -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

View File

@@ -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],
}
}

View File

@@ -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,
};

View File

@@ -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()

View File

@@ -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(),

View File

@@ -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,
}

View File

@@ -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)
}
}
}

View File

@@ -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;

View File

@@ -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")
);
}

View File

@@ -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
View 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: [],
};