mirror of
https://github.com/veeso/termscp.git
synced 2026-09-12 07:06:02 -07:00
ci(site): add format/lint/test/build workflow for astro site
Add Site GitHub Actions workflow running prettier format check, astro check, tests, and build on changes under site/. Wire prettier into the site package with config, ignore, and scripts, and format existing sources.
This commit is contained in:
@@ -0,0 +1,43 @@
|
|||||||
|
name: Site
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- "site/**"
|
||||||
|
- ".github/workflows/site.yml"
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
paths:
|
||||||
|
- "site/**"
|
||||||
|
- ".github/workflows/site.yml"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: site
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-site:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||||
|
with:
|
||||||
|
node-version: 24
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: site/package-lock.json
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
- name: Format
|
||||||
|
run: npm run format:check
|
||||||
|
- name: Lint
|
||||||
|
run: npm run check
|
||||||
|
- name: Test
|
||||||
|
run: npm test --if-present
|
||||||
|
- name: Build
|
||||||
|
run: npm run build
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
dist
|
||||||
|
node_modules
|
||||||
|
.astro
|
||||||
|
package-lock.json
|
||||||
|
public/install.sh
|
||||||
|
public/install.ps1
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"plugins": ["prettier-plugin-astro"],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": "*.astro",
|
||||||
|
"options": {
|
||||||
|
"parser": "astro"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Generated
+44
@@ -14,6 +14,8 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@astrojs/check": "^0.9.9",
|
"@astrojs/check": "^0.9.9",
|
||||||
"@tailwindcss/vite": "^4.0.0",
|
"@tailwindcss/vite": "^4.0.0",
|
||||||
|
"prettier": "3.8.3",
|
||||||
|
"prettier-plugin-astro": "0.14.1",
|
||||||
"tailwindcss": "^4.0.0",
|
"tailwindcss": "^4.0.0",
|
||||||
"typescript": "^5.6.0"
|
"typescript": "^5.6.0"
|
||||||
}
|
}
|
||||||
@@ -5717,6 +5719,21 @@
|
|||||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/prettier-plugin-astro": {
|
||||||
|
"version": "0.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier-plugin-astro/-/prettier-plugin-astro-0.14.1.tgz",
|
||||||
|
"integrity": "sha512-RiBETaaP9veVstE4vUwSIcdATj6dKmXljouXc/DDNwBSPTp8FRkLGDSGFClKsAFeeg+13SB0Z1JZvbD76bigJw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@astrojs/compiler": "^2.9.1",
|
||||||
|
"prettier": "^3.0.0",
|
||||||
|
"sass-formatter": "^0.7.6"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.15.0 || >=16.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/prismjs": {
|
"node_modules/prismjs": {
|
||||||
"version": "1.30.0",
|
"version": "1.30.0",
|
||||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
|
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
|
||||||
@@ -6066,6 +6083,23 @@
|
|||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/s.color": {
|
||||||
|
"version": "0.0.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/s.color/-/s.color-0.0.15.tgz",
|
||||||
|
"integrity": "sha512-AUNrbEUHeKY8XsYr/DYpl+qk5+aM+DChopnWOPEzn8YKzOhv4l2zH6LzZms3tOZP3wwdOyc0RmTciyi46HLIuA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/sass-formatter": {
|
||||||
|
"version": "0.7.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/sass-formatter/-/sass-formatter-0.7.9.tgz",
|
||||||
|
"integrity": "sha512-CWZ8XiSim+fJVG0cFLStwDvft1VI7uvXdCNJYXhDvowiv+DsbD1nXLiQ4zrE5UBvj5DWZJ93cwN0NX5PMsr1Pw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"suf-log": "^2.5.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/sax": {
|
"node_modules/sax": {
|
||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz",
|
||||||
@@ -6256,6 +6290,16 @@
|
|||||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/suf-log": {
|
||||||
|
"version": "2.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/suf-log/-/suf-log-2.5.3.tgz",
|
||||||
|
"integrity": "sha512-KvC8OPjzdNOe+xQ4XWJV2whQA0aM1kGVczMQ8+dStAO6KfEB140JEVQ9dE76ONZ0/Ylf67ni4tILPJB41U0eow==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"s.color": "0.0.15"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/svgo": {
|
"node_modules/svgo": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.1.tgz",
|
||||||
|
|||||||
+5
-1
@@ -9,7 +9,9 @@
|
|||||||
"prebuild": "node scripts/copy-install.mjs",
|
"prebuild": "node scripts/copy-install.mjs",
|
||||||
"build": "astro build",
|
"build": "astro build",
|
||||||
"preview": "astro preview",
|
"preview": "astro preview",
|
||||||
"check": "astro check"
|
"check": "astro check",
|
||||||
|
"format": "prettier --write .",
|
||||||
|
"format:check": "prettier --check ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/sitemap": "^3.2.0",
|
"@astrojs/sitemap": "^3.2.0",
|
||||||
@@ -18,6 +20,8 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@astrojs/check": "^0.9.9",
|
"@astrojs/check": "^0.9.9",
|
||||||
"@tailwindcss/vite": "^4.0.0",
|
"@tailwindcss/vite": "^4.0.0",
|
||||||
|
"prettier": "3.8.3",
|
||||||
|
"prettier-plugin-astro": "0.14.1",
|
||||||
"tailwindcss": "^4.0.0",
|
"tailwindcss": "^4.0.0",
|
||||||
"typescript": "^5.6.0"
|
"typescript": "^5.6.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
import { copyFile, access } from 'node:fs/promises';
|
import { copyFile, access } from "node:fs/promises";
|
||||||
import { dirname, join } from 'node:path';
|
import { dirname, join } from "node:path";
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
const here = dirname(fileURLToPath(import.meta.url));
|
const here = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
// repo-root install scripts (single source of truth)
|
// repo-root install scripts (single source of truth)
|
||||||
const scripts = ['install.sh', 'install.ps1'];
|
const scripts = ["install.sh", "install.ps1"];
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
scripts.map(async (name) => {
|
scripts.map(async (name) => {
|
||||||
const src = join(here, '..', '..', name);
|
const src = join(here, "..", "..", name);
|
||||||
const dest = join(here, '..', 'public', name);
|
const dest = join(here, "..", "public", name);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await access(src);
|
await access(src);
|
||||||
} catch {
|
} catch {
|
||||||
console.error(`[copy-install] source not found: ${src}`);
|
console.error(`[copy-install] source not found: ${src}`);
|
||||||
console.error(`[copy-install] the repo-root ${name} must be available at build time.`);
|
console.error(
|
||||||
|
`[copy-install] the repo-root ${name} must be available at build time.`,
|
||||||
|
);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
const { text, class: className } = Astro.props;
|
const { text, class: className } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class:list={[
|
class:list={[
|
||||||
@@ -15,11 +16,29 @@ const { text, class: className } = Astro.props;
|
|||||||
aria-label="Copy command to clipboard"
|
aria-label="Copy command to clipboard"
|
||||||
title="Copy"
|
title="Copy"
|
||||||
>
|
>
|
||||||
<svg class="copy-icon h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
<svg
|
||||||
|
class="copy-icon h-4 w-4"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
||||||
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
||||||
</svg>
|
</svg>
|
||||||
<svg class="check-icon hidden h-4 w-4 text-green" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
<svg
|
||||||
|
class="check-icon hidden h-4 w-4 text-green"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
<path d="M20 6 9 17l-5-5"></path>
|
<path d="M20 6 9 17l-5-5"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
@@ -46,7 +65,9 @@ const { text, class: className } = Astro.props;
|
|||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener("click", (event) => {
|
document.addEventListener("click", (event) => {
|
||||||
const button = (event.target as HTMLElement)?.closest<HTMLElement>(".copy-btn");
|
const button = (event.target as HTMLElement)?.closest<HTMLElement>(
|
||||||
|
".copy-btn",
|
||||||
|
);
|
||||||
if (button) {
|
if (button) {
|
||||||
handleCopy(button);
|
handleCopy(button);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,44 +17,98 @@ const remote = [
|
|||||||
];
|
];
|
||||||
const status = "↹ switch · ↑↓ move · ↵ open · SPACE transfer";
|
const status = "↹ switch · ↑↓ move · ↵ open · SPACE transfer";
|
||||||
---
|
---
|
||||||
|
|
||||||
<section class="mx-auto max-w-5xl px-4 pt-12">
|
<section class="mx-auto max-w-5xl px-4 pt-12">
|
||||||
<p class="text-green text-sm tracking-widest uppercase">// terminal file transfer</p>
|
<p class="text-green text-sm tracking-widest uppercase">
|
||||||
<div class="mt-4 overflow-hidden rounded-lg border border-line bg-mantle shadow-2xl">
|
// terminal file transfer
|
||||||
|
</p>
|
||||||
|
<div
|
||||||
|
class="mt-4 overflow-hidden rounded-lg border border-line bg-mantle shadow-2xl"
|
||||||
|
>
|
||||||
<div class="border-b border-line bg-crust px-3 py-2 text-xs text-overlay">
|
<div class="border-b border-line bg-crust px-3 py-2 text-xs text-overlay">
|
||||||
termscp — 192.168.1.10 (sftp) connected<span class="blink ml-1"></span>
|
termscp — 192.168.1.10 (sftp) connected<span class="blink ml-1"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 bg-base">
|
<div class="grid grid-cols-2 bg-base">
|
||||||
<div>
|
<div>
|
||||||
<div class="border-b border-line bg-mantle px-3 py-1.5 text-xs text-teal">/home/veeso (local)</div>
|
<div
|
||||||
{local.map((r) => (
|
class="border-b border-line bg-mantle px-3 py-1.5 text-xs text-teal"
|
||||||
<div class={`flex justify-between gap-2 px-3 py-0.5 text-sm ${r.kind === "dir" ? "text-blue" : "text-text"}`}>
|
>
|
||||||
<span class="truncate min-w-0">{r.name}</span><span class="text-overlay shrink-0">{r.size}</span>
|
/home/veeso (local)
|
||||||
</div>
|
</div>
|
||||||
))}
|
{
|
||||||
|
local.map((r) => (
|
||||||
|
<div
|
||||||
|
class={`flex justify-between gap-2 px-3 py-0.5 text-sm ${r.kind === "dir" ? "text-blue" : "text-text"}`}
|
||||||
|
>
|
||||||
|
<>
|
||||||
|
<span class="truncate min-w-0">{r.name}</span>
|
||||||
|
<span class="text-overlay shrink-0">{r.size}</span>
|
||||||
|
</>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="border-l border-line">
|
<div class="border-l border-line">
|
||||||
<div class="border-b border-line bg-mantle px-3 py-1.5 text-xs text-mauve">/var/www (remote)</div>
|
<div
|
||||||
{remote.map((r) => (
|
class="border-b border-line bg-mantle px-3 py-1.5 text-xs text-mauve"
|
||||||
<div class={`flex justify-between gap-2 px-3 py-0.5 text-sm ${
|
>
|
||||||
r.kind === "sel" ? "bg-blue text-base font-bold" : r.kind === "dir" ? "text-blue" : "text-text"
|
/var/www (remote)
|
||||||
}`}>
|
</div>
|
||||||
|
{
|
||||||
|
remote.map((r) => (
|
||||||
|
<div
|
||||||
|
class={`flex justify-between gap-2 px-3 py-0.5 text-sm ${
|
||||||
|
r.kind === "sel"
|
||||||
|
? "bg-blue text-base font-bold"
|
||||||
|
: r.kind === "dir"
|
||||||
|
? "text-blue"
|
||||||
|
: "text-text"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
<span class="truncate min-w-0">{r.name}</span>
|
<span class="truncate min-w-0">{r.name}</span>
|
||||||
<span class={`shrink-0 ${r.kind === "sel" ? "text-base" : "text-overlay"}`}>{r.size}</span>
|
<span
|
||||||
|
class={`shrink-0 ${r.kind === "sel" ? "text-base" : "text-overlay"}`}
|
||||||
|
>
|
||||||
|
{r.size}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* set:html is safe: status is a static author-controlled string, never user input. */}
|
{
|
||||||
<div class="flex justify-between border-t border-line bg-crust px-3 py-1.5 text-xs text-overlay">
|
/* set:html is safe: status is a static author-controlled string, never user input. */
|
||||||
<span set:html={status.replace(/(↹|↑↓|↵|SPACE)/g, '<span class="text-yellow">$1</span>')}></span>
|
}
|
||||||
|
<div
|
||||||
|
class="flex justify-between border-t border-line bg-crust px-3 py-1.5 text-xs text-overlay"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
set:html={status.replace(
|
||||||
|
/(↹|↑↓|↵|SPACE)/g,
|
||||||
|
'<span class="text-yellow">$1</span>',
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<span>4 files · 1.2 GB</span>
|
<span>4 files · 1.2 GB</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-base px-6 py-6 text-center">
|
<div class="bg-base px-6 py-6 text-center">
|
||||||
<h1 class="text-2xl font-bold text-text">The dual-pane explorer for your terminal</h1>
|
<h1 class="text-2xl font-bold text-text">
|
||||||
<p class="mx-auto mt-2 max-w-xl text-subtext">Local left, remote right. Transfer, sync, edit — never leave the keyboard.</p>
|
The dual-pane explorer for your terminal
|
||||||
|
</h1>
|
||||||
|
<p class="mx-auto mt-2 max-w-xl text-subtext">
|
||||||
|
Local left, remote right. Transfer, sync, edit — never leave the
|
||||||
|
keyboard.
|
||||||
|
</p>
|
||||||
<div class="mt-5 flex flex-wrap justify-center gap-3">
|
<div class="mt-5 flex flex-wrap justify-center gap-3">
|
||||||
<a href="/install" class="rounded-lg bg-green px-5 py-2.5 font-bold text-crust">Install termscp →</a>
|
<a
|
||||||
<a href={DOCS_URL} class="rounded-lg border border-line px-5 py-2.5 text-text">User manual</a>
|
href="/install"
|
||||||
|
class="rounded-lg bg-green px-5 py-2.5 font-bold text-crust"
|
||||||
|
>Install termscp →</a
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href={DOCS_URL}
|
||||||
|
class="rounded-lg border border-line px-5 py-2.5 text-text"
|
||||||
|
>User manual</a
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
---
|
---
|
||||||
interface Props { title: string; body: string; }
|
interface Props {
|
||||||
|
title: string;
|
||||||
|
body: string;
|
||||||
|
}
|
||||||
const { title, body } = Astro.props;
|
const { title, body } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="rounded-lg border border-line bg-mantle p-5">
|
<div class="rounded-lg border border-line bg-mantle p-5">
|
||||||
<h3 class="text-green">{title}</h3>
|
<h3 class="text-green">{title}</h3>
|
||||||
<p class="mt-2 text-sm text-subtext">{body}</p>
|
<p class="mt-2 text-sm text-subtext">{body}</p>
|
||||||
|
|||||||
@@ -2,13 +2,20 @@
|
|||||||
import { DOCS_URL, GITHUB_URL, VERSION } from "../consts";
|
import { DOCS_URL, GITHUB_URL, VERSION } from "../consts";
|
||||||
const year = new Date().getFullYear();
|
const year = new Date().getFullYear();
|
||||||
---
|
---
|
||||||
|
|
||||||
<footer class="mt-auto border-t border-line bg-mantle">
|
<footer class="mt-auto border-t border-line bg-mantle">
|
||||||
<div class="mx-auto flex max-w-5xl flex-col items-center gap-2 px-4 py-8 text-sm text-overlay">
|
<div
|
||||||
|
class="mx-auto flex max-w-5xl flex-col items-center gap-2 px-4 py-8 text-sm text-overlay"
|
||||||
|
>
|
||||||
<div class="flex gap-5">
|
<div class="flex gap-5">
|
||||||
<a href={GITHUB_URL} class="hover:text-text">GitHub</a>
|
<a href={GITHUB_URL} class="hover:text-text">GitHub</a>
|
||||||
<a href="https://crates.io/crates/termscp" class="hover:text-text">crates.io</a>
|
<a href="https://crates.io/crates/termscp" class="hover:text-text"
|
||||||
|
>crates.io</a
|
||||||
|
>
|
||||||
<a href={DOCS_URL} class="hover:text-text">User manual</a>
|
<a href={DOCS_URL} class="hover:text-text">User manual</a>
|
||||||
</div>
|
</div>
|
||||||
<p>termscp v{VERSION} · © {year} Christian Visintin · Released under the MIT license.</p>
|
<p>
|
||||||
|
termscp v{VERSION} · © {year} Christian Visintin · Released under the MIT license.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -1,35 +1,52 @@
|
|||||||
---
|
---
|
||||||
import CopyButton from "./CopyButton.astro";
|
import CopyButton from "./CopyButton.astro";
|
||||||
|
|
||||||
interface Method { id: string; label: string; cmd: string; }
|
interface Method {
|
||||||
interface Props { methods: Method[]; }
|
id: string;
|
||||||
|
label: string;
|
||||||
|
cmd: string;
|
||||||
|
}
|
||||||
|
interface Props {
|
||||||
|
methods: Method[];
|
||||||
|
}
|
||||||
const { methods } = Astro.props;
|
const { methods } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="rounded-lg border border-line bg-mantle">
|
<div class="rounded-lg border border-line bg-mantle">
|
||||||
<div class="flex flex-wrap gap-1 border-b border-line p-2" role="tablist">
|
<div class="flex flex-wrap gap-1 border-b border-line p-2" role="tablist">
|
||||||
{methods.map((m, i) => (
|
{
|
||||||
|
methods.map((m, i) => (
|
||||||
<button
|
<button
|
||||||
class="install-tab rounded px-3 py-1 text-sm text-overlay data-[active]:bg-base data-[active]:text-green"
|
class="install-tab rounded px-3 py-1 text-sm text-overlay data-[active]:bg-base data-[active]:text-green"
|
||||||
data-target={m.id}
|
data-target={m.id}
|
||||||
data-active={i === 0 ? "" : undefined}
|
data-active={i === 0 ? "" : undefined}
|
||||||
>{m.label}</button>
|
>
|
||||||
))}
|
{m.label}
|
||||||
|
</button>
|
||||||
|
))
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
{methods.map((m, i) => (
|
{
|
||||||
|
methods.map((m, i) => (
|
||||||
<div class="install-panel relative" data-id={m.id} hidden={i !== 0}>
|
<div class="install-panel relative" data-id={m.id} hidden={i !== 0}>
|
||||||
<pre class="m-0 overflow-auto bg-base px-4 py-4 pr-12 text-sm text-text"><code>{m.cmd}</code></pre>
|
<pre class="m-0 overflow-auto bg-base px-4 py-4 pr-12 text-sm text-text">
|
||||||
|
<code>{m.cmd}</code>
|
||||||
|
</pre>
|
||||||
<CopyButton text={m.cmd} class="absolute right-2 top-2" />
|
<CopyButton text={m.cmd} class="absolute right-2 top-2" />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<script is:inline>
|
<script is:inline>
|
||||||
document.querySelectorAll(".install-tab").forEach((tab) => {
|
document.querySelectorAll(".install-tab").forEach((tab) => {
|
||||||
tab.addEventListener("click", () => {
|
tab.addEventListener("click", () => {
|
||||||
const id = tab.getAttribute("data-target");
|
const id = tab.getAttribute("data-target");
|
||||||
document.querySelectorAll(".install-tab").forEach((t) =>
|
document
|
||||||
t.toggleAttribute("data-active", t === tab));
|
.querySelectorAll(".install-tab")
|
||||||
document.querySelectorAll(".install-panel").forEach((p) =>
|
.forEach((t) => t.toggleAttribute("data-active", t === tab));
|
||||||
(p.hidden = p.getAttribute("data-id") !== id));
|
document
|
||||||
|
.querySelectorAll(".install-panel")
|
||||||
|
.forEach((p) => (p.hidden = p.getAttribute("data-id") !== id));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
import ThemeToggle from "./ThemeToggle.astro";
|
import ThemeToggle from "./ThemeToggle.astro";
|
||||||
import { DOCS_URL, GITHUB_URL } from "../consts";
|
import { DOCS_URL, GITHUB_URL } from "../consts";
|
||||||
---
|
---
|
||||||
<header class="sticky top-0 z-50 border-b border-line bg-mantle/90 backdrop-blur">
|
|
||||||
|
<header
|
||||||
|
class="sticky top-0 z-50 border-b border-line bg-mantle/90 backdrop-blur"
|
||||||
|
>
|
||||||
<nav class="mx-auto flex max-w-5xl items-center justify-between px-4 py-3">
|
<nav class="mx-auto flex max-w-5xl items-center justify-between px-4 py-3">
|
||||||
<a href="/" class="flex items-center gap-2 text-text">
|
<a href="/" class="flex items-center gap-2 text-text">
|
||||||
<img src="/assets/images/termscp.webp" alt="termscp" class="h-7 w-7" />
|
<img src="/assets/images/termscp.webp" alt="termscp" class="h-7 w-7" />
|
||||||
@@ -19,8 +22,13 @@ import { DOCS_URL, GITHUB_URL } from "../consts";
|
|||||||
|
|
||||||
<!-- Mobile -->
|
<!-- Mobile -->
|
||||||
<details class="relative sm:hidden">
|
<details class="relative sm:hidden">
|
||||||
<summary class="cursor-pointer list-none rounded p-2 text-text hover:bg-mantle" aria-label="Menu">☰</summary>
|
<summary
|
||||||
<div class="absolute right-0 mt-2 flex w-48 flex-col gap-3 rounded-lg border border-line bg-mantle p-4 text-sm shadow-xl">
|
class="cursor-pointer list-none rounded p-2 text-text hover:bg-mantle"
|
||||||
|
aria-label="Menu">☰</summary
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="absolute right-0 mt-2 flex w-48 flex-col gap-3 rounded-lg border border-line bg-mantle p-4 text-sm shadow-xl"
|
||||||
|
>
|
||||||
<a href="/install" class="text-overlay hover:text-text">Install</a>
|
<a href="/install" class="text-overlay hover:text-text">Install</a>
|
||||||
<a href={DOCS_URL} class="text-overlay hover:text-text">User manual</a>
|
<a href={DOCS_URL} class="text-overlay hover:text-text">User manual</a>
|
||||||
<a href={GITHUB_URL} class="text-overlay hover:text-text">GitHub</a>
|
<a href={GITHUB_URL} class="text-overlay hover:text-text">GitHub</a>
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
---
|
---
|
||||||
const protocols = ["SCP", "SFTP", "FTP/S", "S3", "Kube", "SMB", "WebDAV"];
|
const protocols = ["SCP", "SFTP", "FTP/S", "S3", "Kube", "SMB", "WebDAV"];
|
||||||
---
|
---
|
||||||
|
|
||||||
<section class="mx-auto max-w-5xl px-4 py-12 text-center">
|
<section class="mx-auto max-w-5xl px-4 py-12 text-center">
|
||||||
<h2 class="text-overlay text-sm uppercase tracking-widest">Speaks every protocol</h2>
|
<h2 class="text-overlay text-sm uppercase tracking-widest">
|
||||||
|
Speaks every protocol
|
||||||
|
</h2>
|
||||||
<div class="mt-4 flex flex-wrap justify-center gap-2">
|
<div class="mt-4 flex flex-wrap justify-center gap-2">
|
||||||
{protocols.map((proto) => (
|
{
|
||||||
<span class="rounded-full border border-line px-4 py-1 text-sm text-text">{proto}</span>
|
protocols.map((proto) => (
|
||||||
))}
|
<span class="rounded-full border border-line px-4 py-1 text-sm text-text">
|
||||||
|
{proto}
|
||||||
|
</span>
|
||||||
|
))
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
// Theme toggle. Contract: localStorage 'theme' = "frappe" | "latte". Safe to render multiple times.
|
// Theme toggle. Contract: localStorage 'theme' = "frappe" | "latte". Safe to render multiple times.
|
||||||
---
|
---
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
data-theme-toggle
|
data-theme-toggle
|
||||||
@@ -17,15 +18,22 @@
|
|||||||
window.__themeToggleInit = true;
|
window.__themeToggleInit = true;
|
||||||
const root = document.documentElement;
|
const root = document.documentElement;
|
||||||
const btns = document.querySelectorAll("[data-theme-toggle]");
|
const btns = document.querySelectorAll("[data-theme-toggle]");
|
||||||
const setPressed = () => btns.forEach((b) =>
|
const setPressed = () =>
|
||||||
b.setAttribute("aria-pressed", String(root.getAttribute("data-theme") === "latte")));
|
btns.forEach((b) =>
|
||||||
|
b.setAttribute(
|
||||||
|
"aria-pressed",
|
||||||
|
String(root.getAttribute("data-theme") === "latte"),
|
||||||
|
),
|
||||||
|
);
|
||||||
btns.forEach((b) =>
|
btns.forEach((b) =>
|
||||||
b.addEventListener("click", () => {
|
b.addEventListener("click", () => {
|
||||||
const next = root.getAttribute("data-theme") === "frappe" ? "latte" : "frappe";
|
const next =
|
||||||
|
root.getAttribute("data-theme") === "frappe" ? "latte" : "frappe";
|
||||||
root.setAttribute("data-theme", next);
|
root.setAttribute("data-theme", next);
|
||||||
localStorage.setItem("theme", next);
|
localStorage.setItem("theme", next);
|
||||||
setPressed();
|
setPressed();
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
setPressed();
|
setPressed();
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -30,7 +30,10 @@ const ogImage = new URL("/assets/images/og_preview.jpg", site).href;
|
|||||||
<meta property="og:description" content={description} />
|
<meta property="og:description" content={description} />
|
||||||
<meta property="og:url" content={canonical} />
|
<meta property="og:url" content={canonical} />
|
||||||
<meta property="og:image" content={ogImage} />
|
<meta property="og:image" content={ogImage} />
|
||||||
<meta property="og:image:alt" content="termscp — terminal file transfer and explorer" />
|
<meta
|
||||||
|
property="og:image:alt"
|
||||||
|
content="termscp — terminal file transfer and explorer"
|
||||||
|
/>
|
||||||
<meta property="og:image:width" content="1280" />
|
<meta property="og:image:width" content="1280" />
|
||||||
<meta property="og:image:height" content="640" />
|
<meta property="og:image:height" content="640" />
|
||||||
|
|
||||||
@@ -41,24 +44,51 @@ const ogImage = new URL("/assets/images/og_preview.jpg", site).href;
|
|||||||
<meta name="twitter:image" content={ogImage} />
|
<meta name="twitter:image" content={ogImage} />
|
||||||
|
|
||||||
<!-- Favicons -->
|
<!-- Favicons -->
|
||||||
<link rel="icon" type="image/png" sizes="96x96" href="/assets/images/favicon-96x96.png" />
|
<link
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/assets/images/favicon-32x32.png" />
|
rel="icon"
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/assets/images/favicon-16x16.png" />
|
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"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- Font preload -->
|
<!-- Font preload -->
|
||||||
<link rel="preload" href="/fonts/jetbrains-mono-400.woff2" as="font" type="font/woff2" crossorigin />
|
<link
|
||||||
|
rel="preload"
|
||||||
|
href="/fonts/jetbrains-mono-400.woff2"
|
||||||
|
as="font"
|
||||||
|
type="font/woff2"
|
||||||
|
crossorigin
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- No-flash theme bootstrap. Contract: localStorage 'theme' is "frappe" | "latte"; ThemeToggle writes the same. -->
|
<!-- No-flash theme bootstrap. Contract: localStorage 'theme' is "frappe" | "latte"; ThemeToggle writes the same. -->
|
||||||
<script is:inline>
|
<script is:inline>
|
||||||
(() => {
|
(() => {
|
||||||
const t = localStorage.getItem("theme") ||
|
const t =
|
||||||
(matchMedia("(prefers-color-scheme: light)").matches ? "latte" : "frappe");
|
localStorage.getItem("theme") ||
|
||||||
|
(matchMedia("(prefers-color-scheme: light)").matches
|
||||||
|
? "latte"
|
||||||
|
: "frappe");
|
||||||
document.documentElement.setAttribute("data-theme", t);
|
document.documentElement.setAttribute("data-theme", t);
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Umami analytics -->
|
<!-- Umami analytics -->
|
||||||
<script defer src="https://cloud.umami.is/script.js" data-website-id="ad2d5621-a893-488c-baa6-d39c306374cf"></script>
|
<script
|
||||||
|
defer
|
||||||
|
src="https://cloud.umami.is/script.js"
|
||||||
|
data-website-id="ad2d5621-a893-488c-baa6-d39c306374cf"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-base text-text font-mono min-h-screen flex flex-col">
|
<body class="bg-base text-text font-mono min-h-screen flex flex-col">
|
||||||
<slot />
|
<slot />
|
||||||
|
|||||||
@@ -7,14 +7,30 @@ import ProtocolStrip from "../components/ProtocolStrip.astro";
|
|||||||
import FeatureCard from "../components/FeatureCard.astro";
|
import FeatureCard from "../components/FeatureCard.astro";
|
||||||
|
|
||||||
const features = [
|
const features = [
|
||||||
{ title: "Handy UI", body: "Explore and operate on the remote and local file system with a handy UI." },
|
{
|
||||||
|
title: "Handy UI",
|
||||||
|
body: "Explore and operate on the remote and local file system with a handy UI.",
|
||||||
|
},
|
||||||
{ title: "Cross platform", body: "Runs on Windows, macOS, Linux and BSD." },
|
{ title: "Cross platform", body: "Runs on Windows, macOS, Linux and BSD." },
|
||||||
{ title: "Customizable", body: "Customize the explorer, the text editor and default options." },
|
{
|
||||||
{ title: "Bookmarks", body: "Connect to favourite hosts via bookmarks and recent connections." },
|
title: "Customizable",
|
||||||
{ title: "Security first", body: "Store passwords in your operating system key vault." },
|
body: "Customize the explorer, the text editor and default options.",
|
||||||
{ title: "Eye on performance", body: "Built with an eye on performance to keep CPU usage low." },
|
},
|
||||||
|
{
|
||||||
|
title: "Bookmarks",
|
||||||
|
body: "Connect to favourite hosts via bookmarks and recent connections.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Security first",
|
||||||
|
body: "Store passwords in your operating system key vault.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Eye on performance",
|
||||||
|
body: "Built with an eye on performance to keep CPU usage low.",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
---
|
---
|
||||||
|
|
||||||
<Base
|
<Base
|
||||||
title="termscp — terminal file transfer & explorer for SCP/SFTP/FTP/S3/Kube/SMB/WebDAV"
|
title="termscp — terminal file transfer & explorer for SCP/SFTP/FTP/S3/Kube/SMB/WebDAV"
|
||||||
description="A feature-rich terminal UI file transfer and explorer with support for SCP/SFTP/FTP/Kube/S3/WebDAV/SMB."
|
description="A feature-rich terminal UI file transfer and explorer with support for SCP/SFTP/FTP/Kube/S3/WebDAV/SMB."
|
||||||
@@ -26,9 +42,7 @@ const features = [
|
|||||||
<section class="mx-auto max-w-5xl px-4 py-12">
|
<section class="mx-auto max-w-5xl px-4 py-12">
|
||||||
<h2 class="mb-6 text-center text-xl text-text">Why termscp</h2>
|
<h2 class="mb-6 text-center text-xl text-text">Why termscp</h2>
|
||||||
<div class="grid gap-5 sm:grid-cols-2 lg:grid-cols-3">
|
<div class="grid gap-5 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
{features.map((f) => (
|
{features.map((f) => <FeatureCard title={f.title} body={f.body} />)}
|
||||||
<FeatureCard title={f.title} body={f.body} />
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<ProtocolStrip />
|
<ProtocolStrip />
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import InstallTabs from "../components/InstallTabs.astro";
|
|||||||
import CopyButton from "../components/CopyButton.astro";
|
import CopyButton from "../components/CopyButton.astro";
|
||||||
import { DOCS_URL } from "../consts";
|
import { DOCS_URL } from "../consts";
|
||||||
|
|
||||||
const script = "curl --proto '=https' --tlsv1.2 -sSLf https://termscp.rs/install.sh | sh";
|
const script =
|
||||||
|
"curl --proto '=https' --tlsv1.2 -sSLf https://termscp.rs/install.sh | sh";
|
||||||
const psScript = "irm https://termscp.rs/install.ps1 | iex";
|
const psScript = "irm https://termscp.rs/install.ps1 | iex";
|
||||||
|
|
||||||
const methods = [
|
const methods = [
|
||||||
@@ -16,6 +17,7 @@ const methods = [
|
|||||||
{ id: "choco", label: "Windows (Chocolatey)", cmd: "choco install termscp" },
|
{ id: "choco", label: "Windows (Chocolatey)", cmd: "choco install termscp" },
|
||||||
];
|
];
|
||||||
---
|
---
|
||||||
|
|
||||||
<Base
|
<Base
|
||||||
title="Install termscp"
|
title="Install termscp"
|
||||||
description="Install termscp on Linux, macOS, BSD and Windows — one shell command, or via Cargo, pacman, pkgin and Chocolatey."
|
description="Install termscp on Linux, macOS, BSD and Windows — one shell command, or via Cargo, pacman, pkgin and Chocolatey."
|
||||||
@@ -24,37 +26,58 @@ const methods = [
|
|||||||
<Nav />
|
<Nav />
|
||||||
<main class="mx-auto w-full max-w-3xl flex-1 px-4 py-12">
|
<main class="mx-auto w-full max-w-3xl flex-1 px-4 py-12">
|
||||||
<h1 class="text-2xl font-bold text-text">Install termscp</h1>
|
<h1 class="text-2xl font-bold text-text">Install termscp</h1>
|
||||||
<p class="mt-2 text-subtext">Pick your platform. One command and you're in.</p>
|
<p class="mt-2 text-subtext">
|
||||||
|
Pick your platform. One command and you're in.
|
||||||
|
</p>
|
||||||
|
|
||||||
<div class="mt-6 rounded-lg border border-line bg-mantle p-1">
|
<div class="mt-6 rounded-lg border border-line bg-mantle p-1">
|
||||||
<div class="flex items-center gap-2 border-b border-line px-3 py-2 text-xs text-overlay">
|
<div
|
||||||
|
class="flex items-center gap-2 border-b border-line px-3 py-2 text-xs text-overlay"
|
||||||
|
>
|
||||||
<span class="text-green">$</span> Linux · macOS · BSD
|
<span class="text-green">$</span> Linux · macOS · BSD
|
||||||
</div>
|
</div>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<pre class="m-0 overflow-auto bg-base px-4 py-4 pr-12 text-sm text-text"><code>{script}</code></pre>
|
<pre
|
||||||
|
class="m-0 overflow-auto bg-base px-4 py-4 pr-12 text-sm text-text"><code>{script}</code></pre>
|
||||||
<CopyButton text={script} class="absolute right-2 top-2" />
|
<CopyButton text={script} class="absolute right-2 top-2" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-2 text-xs text-overlay">Requires <span class="text-text">curl</span>. macOS uses Homebrew if available, otherwise builds from source.</p>
|
<p class="mt-2 text-xs text-overlay">
|
||||||
|
Requires <span class="text-text">curl</span>. macOS uses Homebrew if
|
||||||
|
available, otherwise builds from source.
|
||||||
|
</p>
|
||||||
|
|
||||||
<div class="mt-6 rounded-lg border border-line bg-mantle p-1">
|
<div class="mt-6 rounded-lg border border-line bg-mantle p-1">
|
||||||
<div class="flex items-center gap-2 border-b border-line px-3 py-2 text-xs text-overlay">
|
<div
|
||||||
|
class="flex items-center gap-2 border-b border-line px-3 py-2 text-xs text-overlay"
|
||||||
|
>
|
||||||
<span class="text-green">></span> Windows (PowerShell)
|
<span class="text-green">></span> Windows (PowerShell)
|
||||||
</div>
|
</div>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<pre class="m-0 overflow-auto bg-base px-4 py-4 pr-12 text-sm text-text"><code>{psScript}</code></pre>
|
<pre
|
||||||
|
class="m-0 overflow-auto bg-base px-4 py-4 pr-12 text-sm text-text"><code>{psScript}</code></pre>
|
||||||
<CopyButton text={psScript} class="absolute right-2 top-2" />
|
<CopyButton text={psScript} class="absolute right-2 top-2" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-2 text-xs text-overlay">Downloads the latest release and adds <span class="text-text">termscp</span> to your user PATH.</p>
|
<p class="mt-2 text-xs text-overlay">
|
||||||
|
Downloads the latest release and adds <span class="text-text"
|
||||||
|
>termscp</span
|
||||||
|
> to your user PATH.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h2 class="mt-10 mb-3 text-lg text-text">Package managers</h2>
|
<h2 class="mt-10 mb-3 text-lg text-text">Package managers</h2>
|
||||||
<InstallTabs methods={methods} />
|
<InstallTabs methods={methods} />
|
||||||
|
|
||||||
<p class="mt-8 text-sm text-overlay">Update anytime with <span class="text-green">termscp --update</span>.</p>
|
<p class="mt-8 text-sm text-overlay">
|
||||||
|
Update anytime with <span class="text-green">termscp --update</span>.
|
||||||
|
</p>
|
||||||
<p class="mt-2 text-sm text-overlay">
|
<p class="mt-2 text-sm text-overlay">
|
||||||
Linux build deps: <span class="text-text">libdbus-1</span>, <span class="text-text">pkg-config</span>, <span class="text-text">libsmbclient</span>.
|
Linux build deps: <span class="text-text">libdbus-1</span>, <span
|
||||||
More details in the <a href={DOCS_URL} class="text-blue hover:underline">docs</a>.
|
class="text-text">pkg-config</span
|
||||||
|
>, <span class="text-text">libsmbclient</span>. More details in the <a
|
||||||
|
href={DOCS_URL}
|
||||||
|
class="text-blue hover:underline">docs</a
|
||||||
|
>.
|
||||||
</p>
|
</p>
|
||||||
</main>
|
</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|||||||
@@ -80,9 +80,15 @@ html {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Theme toggle icons: shown based on active theme, set before paint by the head bootstrap */
|
/* Theme toggle icons: shown based on active theme, set before paint by the head bootstrap */
|
||||||
[data-icon] { display: none; }
|
[data-icon] {
|
||||||
[data-theme="frappe"] [data-icon="frappe"] { display: inline; }
|
display: none;
|
||||||
[data-theme="latte"] [data-icon="latte"] { display: inline; }
|
}
|
||||||
|
[data-theme="frappe"] [data-icon="frappe"] {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
[data-theme="latte"] [data-icon="latte"] {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
.blink {
|
.blink {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -92,4 +98,8 @@ html {
|
|||||||
vertical-align: -1px;
|
vertical-align: -1px;
|
||||||
animation: blink 1s steps(1) infinite;
|
animation: blink 1s steps(1) infinite;
|
||||||
}
|
}
|
||||||
@keyframes blink { 50% { opacity: 0; } }
|
@keyframes blink {
|
||||||
|
50% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+32
-7
@@ -2,25 +2,50 @@
|
|||||||
"$schema": "https://openapi.vercel.sh/vercel.json",
|
"$schema": "https://openapi.vercel.sh/vercel.json",
|
||||||
"redirects": [
|
"redirects": [
|
||||||
{ "source": "/index.html", "destination": "/", "permanent": true },
|
{ "source": "/index.html", "destination": "/", "permanent": true },
|
||||||
{ "source": "/get-started.html", "destination": "/install", "permanent": true },
|
{
|
||||||
|
"source": "/get-started.html",
|
||||||
|
"destination": "/install",
|
||||||
|
"permanent": true
|
||||||
|
},
|
||||||
{ "source": "/get-started", "destination": "/install", "permanent": true },
|
{ "source": "/get-started", "destination": "/install", "permanent": true },
|
||||||
{ "source": "/updates.html", "destination": "/install", "permanent": true },
|
{ "source": "/updates.html", "destination": "/install", "permanent": true },
|
||||||
{ "source": "/user-manual.html", "destination": "https://docs.termscp.rs", "permanent": true },
|
{
|
||||||
{ "source": "/user-manual", "destination": "https://docs.termscp.rs", "permanent": true },
|
"source": "/user-manual.html",
|
||||||
{ "source": "/changelog.html", "destination": "https://github.com/veeso/termscp/blob/main/CHANGELOG.md", "permanent": true }
|
"destination": "https://docs.termscp.rs",
|
||||||
|
"permanent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "/user-manual",
|
||||||
|
"destination": "https://docs.termscp.rs",
|
||||||
|
"permanent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "/changelog.html",
|
||||||
|
"destination": "https://github.com/veeso/termscp/blob/main/CHANGELOG.md",
|
||||||
|
"permanent": true
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"headers": [
|
"headers": [
|
||||||
{
|
{
|
||||||
"source": "/fonts/(.*)",
|
"source": "/fonts/(.*)",
|
||||||
"headers": [{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }]
|
"headers": [
|
||||||
|
{
|
||||||
|
"key": "Cache-Control",
|
||||||
|
"value": "public, max-age=31536000, immutable"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"source": "/install.sh",
|
"source": "/install.sh",
|
||||||
"headers": [{ "key": "Content-Type", "value": "text/x-shellscript; charset=utf-8" }]
|
"headers": [
|
||||||
|
{ "key": "Content-Type", "value": "text/x-shellscript; charset=utf-8" }
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"source": "/install.ps1",
|
"source": "/install.ps1",
|
||||||
"headers": [{ "key": "Content-Type", "value": "text/plain; charset=utf-8" }]
|
"headers": [
|
||||||
|
{ "key": "Content-Type", "value": "text/plain; charset=utf-8" }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user