mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-04-01 09:36:10 +00:00
* website: fix fade effect not covering the full description * website: correct text in xftp-protocol-dark.svg
317 lines
14 KiB
HTML
317 lines
14 KiB
HTML
---
|
|
layout: layouts/main.html
|
|
title: "SimpleX File Transfer"
|
|
description: "Send files securely with end-to-end encryption"
|
|
templateEngineOverride: njk
|
|
active_file: true
|
|
---
|
|
{% set lang = page.url | getlang %}
|
|
{% from "components/macro.njk" import overlay %}
|
|
|
|
<link rel="stylesheet" href="/file-assets/index.css">
|
|
<style>
|
|
/* Reset website .card styles that conflict with xftp bundle */
|
|
#app .card { cursor: default; }
|
|
#app .card,
|
|
#app .card > div { transition: unset; height: auto; }
|
|
#app .card > div > * { opacity: unset; max-height: unset; transform: unset; transition: unset; }
|
|
|
|
/* Align xftp bundle with website design */
|
|
#app { font-family: inherit; color: #3F484B; max-width: 600px; width: 100%; margin: 0 auto; padding: 0; }
|
|
#app .card { background: transparent; box-shadow: none; padding: 0; border-radius: 0; }
|
|
#app .card > h1 { display: none; }
|
|
.dark #app h1 { color: #fff; }
|
|
#app .drop-zone { border: 2px dashed #c5d3de; border-radius: 12px; padding: 40px 24px; }
|
|
#app .drop-zone.drag-over { border-color: #0053D0; background: #DBEEFF; }
|
|
#app .btn { background: #0053D0; border-radius: 34px; padding: 12px 32px; font-size: .9rem; font-weight: 600; }
|
|
#app .btn:hover { background: #1661D1; }
|
|
#app .btn-secondary { background: #6b7280; border-radius: 34px; }
|
|
#app .hint { color: #6b7280; }
|
|
#app .link-row input { border-radius: 8px; border-color: #c5d3de; }
|
|
#app .security-note { background: #ffffff; border-radius: 12px; color: #3F484B; }
|
|
#app .security-note a { color: #0197FF; }
|
|
#app .success { color: #0053D0; }
|
|
|
|
.dark #app { color: #e5e7eb; }
|
|
.dark #app .card { background: transparent; box-shadow: none; }
|
|
.dark #app .drop-zone { border-color: #374151; }
|
|
.dark #app .drop-zone.drag-over { border-color: #70F0F9; background: #1B325C; }
|
|
.dark #app .btn { background: #70F0F9; color: #000832; }
|
|
.dark #app .btn:hover { background: #66D9E2; }
|
|
.dark #app .link-row input { border-color: #374151; background: #071C46; color: #e5e7eb; }
|
|
.dark #app .security-note { background: #0B2A59; color: #d1d5db; }
|
|
.dark #app .security-note a { color: #70F0F9; }
|
|
.dark #app .success { color: #70F0F9; }
|
|
|
|
#app #upload-error { border: 2px dashed #c5d3de; border-radius: 12px; padding: 40px 24px; text-align: center; }
|
|
.dark #app #upload-error { border-color: #374151; }
|
|
|
|
#app .stage > .btn { margin-top: 20px; }
|
|
#app .security-note { margin-top: 20px; }
|
|
#app .drop-zone .btn { margin: 10px 0 16px; }
|
|
|
|
/* Protocol overlay: image fixed, text scrolls natively */
|
|
/* Push popup below navbar at all widths */
|
|
#xftp-protocol { padding-top: 70px; }
|
|
#xftp-protocol .overlay-card {
|
|
display: flex;
|
|
flex-direction: column;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
@media (min-width: 768px) {
|
|
#xftp-protocol .overlay-card { max-height: min(660px, 100%); }
|
|
}
|
|
#xftp-protocol .overlay-card > h1 {
|
|
flex-shrink: 0;
|
|
padding-bottom: 16px;
|
|
margin-bottom: 0;
|
|
z-index: 1;
|
|
}
|
|
@media (min-width: 1024px) and (max-height: 750px) {
|
|
#xftp-protocol .overlay-card > h1 {
|
|
font-size: 22px;
|
|
}
|
|
}
|
|
/* .flex is the full-width scroll container */
|
|
#xftp-protocol .overlay-card > .flex {
|
|
flex: 1;
|
|
min-height: 0;
|
|
overflow-y: auto;
|
|
scrollbar-width: thin;
|
|
scrollbar-color: #c5d3de transparent;
|
|
}
|
|
.dark #xftp-protocol .overlay-card > .flex {
|
|
scrollbar-color: #374151 transparent;
|
|
}
|
|
#xftp-protocol .overlay-card > .flex::-webkit-scrollbar { width: 6px; }
|
|
#xftp-protocol .overlay-card > .flex::-webkit-scrollbar-track { background: transparent; }
|
|
#xftp-protocol .overlay-card > .flex::-webkit-scrollbar-thumb { background: #c5d3de; border-radius: 3px; }
|
|
.dark #xftp-protocol .overlay-card > .flex::-webkit-scrollbar-thumb { background: #374151; }
|
|
/* Desktop: text stays left, image fixed on right */
|
|
@media (min-width: 1024px) and (min-height: 600px) {
|
|
#xftp-protocol .overlay-card > .flex > div:first-child {
|
|
max-width: 50%;
|
|
}
|
|
#xftp-protocol .overlay-card > .flex > div:last-child {
|
|
position: absolute;
|
|
right: 56px;
|
|
top: 0;
|
|
bottom: 0;
|
|
left: 55%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
pointer-events: none;
|
|
}
|
|
#xftp-protocol .overlay-card > .flex > div:last-child > div {
|
|
height: 100%;
|
|
padding: 40px 0;
|
|
box-sizing: border-box;
|
|
}
|
|
#xftp-protocol .overlay-card > .flex > div:last-child img {
|
|
height: 100%;
|
|
}
|
|
}
|
|
#xftp-protocol .close-overlay-btn {
|
|
position: absolute;
|
|
top: 24px;
|
|
right: 24px;
|
|
cursor: pointer;
|
|
z-index: 3;
|
|
fill: #606C71;
|
|
}
|
|
.dark #xftp-protocol .close-overlay-btn { fill: #e5e7eb; }
|
|
/* Scroll fade: gradient over text area only, hidden at bottom */
|
|
#xftp-protocol .overlay-card::after {
|
|
content: '';
|
|
position: absolute;
|
|
bottom: 40px;
|
|
left: 24px;
|
|
right: 24px;
|
|
height: 48px;
|
|
background: linear-gradient(rgba(255,255,255,0), #fff);
|
|
pointer-events: none;
|
|
z-index: 2;
|
|
transition: opacity 0.2s;
|
|
}
|
|
.dark #xftp-protocol .overlay-card::after {
|
|
background: linear-gradient(rgba(7,28,70,0), #071C46);
|
|
}
|
|
@media (min-width: 640px) {
|
|
#xftp-protocol .overlay-card::after { bottom: 56px; left: 56px; right: 56px; }
|
|
}
|
|
@media (min-width: 1024px) and (min-height: 600px) {
|
|
#xftp-protocol .overlay-card::after { right: 50%; }
|
|
}
|
|
#xftp-protocol.scrolled-bottom .overlay-card::after { opacity: 0; }
|
|
#xftp-protocol .overlay-card.flex,
|
|
#xftp-protocol.flex .overlay-card { animation: overlaySlideIn 0.25s ease-out; }
|
|
@keyframes overlaySlideIn {
|
|
from { opacity: 0; transform: scale(0.95) translateY(10px); }
|
|
to { opacity: 1; transform: scale(1) translateY(0); }
|
|
}
|
|
#xftp-protocol.overlay-closing { animation: overlayFadeOut 0.2s ease-in forwards; }
|
|
#xftp-protocol.overlay-closing .overlay-card { animation: overlaySlideOut 0.2s ease-in forwards; }
|
|
@keyframes overlayFadeOut {
|
|
from { opacity: 1; }
|
|
to { opacity: 0; }
|
|
}
|
|
@keyframes overlaySlideOut {
|
|
from { opacity: 1; transform: scale(1) translateY(0); }
|
|
to { opacity: 0; transform: scale(0.95) translateY(10px); }
|
|
}
|
|
|
|
.file-proto-link {
|
|
font-size: 1.25rem;
|
|
font-weight: 600;
|
|
background: linear-gradient(90deg, #001aa7 0%, #0095e7 100%);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
text-decoration: none;
|
|
}
|
|
.dark .file-proto-link {
|
|
background: linear-gradient(90deg, #019bfe 0%, #64fdff 58%, #c8feff 100%);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
}
|
|
|
|
</style>
|
|
|
|
<section class="bg-secondary-bg-light dark:bg-primary-bg-dark py-10 mt-[66px] px-5">
|
|
<div class="container flex flex-col items-center mx-auto">
|
|
<noscript>
|
|
<p class="text-center text-grey-black dark:text-white py-10">
|
|
{{ "file-noscript" | i18n({}, lang) | safe }}
|
|
</p>
|
|
</noscript>
|
|
<h1 class="text-[38px] text-center font-bold text-active-blue mb-8">{{ 'file-title' | i18n({}, lang) | safe }}</h1>
|
|
<div id="app" data-xftp-app data-no-hashchange class="w-full">
|
|
<div class="card">
|
|
<div class="drop-zone">
|
|
<p>{{ 'file-drop-text' | i18n({}, lang) | safe }}</p>
|
|
<p class="hint">{{ 'file-drop-hint' | i18n({}, lang) | safe }}</p>
|
|
<span class="btn">{{ 'file-choose' | i18n({}, lang) | safe }}</span>
|
|
<p class="hint">{{ 'file-max-size' | i18n({}, lang) | safe }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<template id="dl-placeholder">
|
|
<div class="card">
|
|
<h1>{{ "file-title" | i18n({}, lang) | safe }}</h1>
|
|
<div class="stage">
|
|
<p>File available</p>
|
|
<button class="btn">{{ "file-download-btn" | i18n({}, lang) | safe }}</button>
|
|
<div class="security-note">
|
|
<p>{{ "file-dl-sec-1" | i18n({}, lang) | safe }}</p>
|
|
<p>{{ "file-sec-2" | i18n({}, lang) | safe }}</p>
|
|
<p>{{ "file-sec-3" | i18n({}, lang) | safe }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>if(location.hash.length>50){var a=document.getElementById("app"),t=document.getElementById("dl-placeholder");if(a&&t)a.replaceChildren(t.content.cloneNode(true))}</script>
|
|
<script type="module" src="/file-assets/index.js"></script>
|
|
|
|
<p class="text-base text-center text-grey-black dark:text-white mt-10 max-w-[600px]">{{ 'file-desc' | i18n({}, lang) | safe }}</p>
|
|
<a href="javascript:void(0)" data-show-overlay="xftp-protocol" class="open-overlay-btn file-proto-link mt-3 block text-center">{{ "file-learn-more" | i18n({}, lang) | safe }}</a>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="bg-primary-bg-light dark:bg-secondary-bg-dark py-[90px] px-5">
|
|
<div class="container flex flex-col items-center mx-auto text-center">
|
|
<h2 class="text-[35px] leading-[45px] md:text-[45px] md:leading-[55px] text-grey-black dark:text-white font-bold mb-5">
|
|
{{ "file-cta-heading" | i18n({}, lang) | safe }}
|
|
</h2>
|
|
<p class="text-base text-grey-black dark:text-white mb-14 max-w-[600px]">
|
|
{{ "file-cta-subheading" | i18n({}, lang) | safe }}
|
|
</p>
|
|
<div class="flex items-center justify-center gap-4 flex-wrap">
|
|
<a href="https://apps.apple.com/us/app/simplex-chat/id1605771084" target="_blank"><img class="h-[40px] w-auto" src="/img/new/apple_store.svg" /></a>
|
|
<a href="https://play.google.com/store/apps/details?id=chat.simplex.app" target="_blank"><img class="h-[40px] w-auto" src="/img/new/google_play.svg" /></a>
|
|
<a href="{{ '' if lang == 'en' else '/' ~ lang }}/fdroid"><img class="h-[40px] w-auto" src="/img/new/f_droid.svg" /></a>
|
|
<a href="https://testflight.apple.com/join/DWuT2LQu" target="_blank"><img class="h-[40px] w-auto" src="/img/new/testflight.png" /></a>
|
|
<a href="https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex-aarch64.apk" target="_blank"><img class="h-[40px] w-auto" src="/img/new/apk_icon.png" /></a>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{% for section in file_overlays.sections %}
|
|
{{ overlay(section, lang) }}
|
|
{% endfor %}
|
|
|
|
<script>
|
|
window.__XFTP_I18N__ = {
|
|
"title": {{ 'file-title' | i18n({}, lang) | dump | safe }},
|
|
"dropZone": {{ 'file-drop-text' | i18n({}, lang) | dump | safe }},
|
|
"dropZoneHint": {{ 'file-drop-hint' | i18n({}, lang) | dump | safe }},
|
|
"chooseFile": {{ 'file-choose' | i18n({}, lang) | dump | safe }},
|
|
"maxSizeHint": {{ 'file-max-size' | i18n({}, lang) | dump | safe }},
|
|
"encrypting": {{ 'file-encrypting' | i18n({}, lang) | dump | safe }},
|
|
"uploading": {{ 'file-uploading' | i18n({}, lang) | dump | safe }},
|
|
"cancel": {{ 'file-cancel' | i18n({}, lang) | dump | safe }},
|
|
"fileUploaded": {{ 'file-uploaded' | i18n({}, lang) | dump | safe }},
|
|
"copy": {{ 'file-copy' | i18n({}, lang) | dump | safe }},
|
|
"copied": {{ 'file-copied' | i18n({}, lang) | dump | safe }},
|
|
"share": {{ 'file-share' | i18n({}, lang) | dump | safe }},
|
|
"expiryHint": {{ 'file-expiry' | i18n({}, lang) | dump | safe }},
|
|
"securityNote1": {{ 'file-sec-1' | i18n({}, lang) | dump | safe }},
|
|
"securityNote2": {{ 'file-sec-2' | i18n({}, lang) | dump | safe }},
|
|
"securityNote3": {{ 'file-sec-3' | i18n({}, lang) | dump | safe }},
|
|
"retry": {{ 'file-retry' | i18n({}, lang) | dump | safe }},
|
|
"downloading": {{ 'file-downloading' | i18n({}, lang) | dump | safe }},
|
|
"decrypting": {{ 'file-decrypting' | i18n({}, lang) | dump | safe }},
|
|
"downloadComplete": {{ 'file-download-complete' | i18n({}, lang) | dump | safe }},
|
|
"download": {{ 'file-download-btn' | i18n({}, lang) | dump | safe }},
|
|
"fileTooLarge": {{ 'file-too-large' | i18n({}, lang) | dump | safe }},
|
|
"fileEmpty": {{ 'file-empty' | i18n({}, lang) | dump | safe }},
|
|
"invalidLink": {{ 'file-invalid-link' | i18n({}, lang) | dump | safe }},
|
|
"initError": {{ 'file-init-error' | i18n({}, lang) | dump | safe }},
|
|
"fileAvailable": {{ 'file-available' | i18n({}, lang) | dump | safe }},
|
|
"dlSecurityNote1": {{ 'file-dl-sec-1' | i18n({}, lang) | dump | safe }},
|
|
"dlSecurityNote2": {{ 'file-sec-2' | i18n({}, lang) | dump | safe }},
|
|
"dlSecurityNote3": {{ 'file-sec-3' | i18n({}, lang) | dump | safe }},
|
|
"workersRequired": {{ 'file-workers-required' | i18n({}, lang) | dump | safe }}
|
|
}
|
|
</script>
|
|
|
|
<script>
|
|
// Intercept close for #xftp-protocol to add fade-out animation
|
|
document.getElementById('xftp-protocol')?.addEventListener('click', function(e) {
|
|
if (e.target.closest('.close-overlay-btn') || (e.target.closest('.overlay') && !e.target.closest('.overlay-card'))) {
|
|
e.stopPropagation();
|
|
var el = document.getElementById('xftp-protocol');
|
|
el.classList.add('overlay-closing');
|
|
el.addEventListener('animationend', function handler() {
|
|
el.removeEventListener('animationend', handler);
|
|
el.classList.remove('flex', 'overlay-closing');
|
|
el.classList.add('hidden');
|
|
document.body.classList.remove('lock-scroll');
|
|
history.replaceState(null, null, ' ');
|
|
});
|
|
}
|
|
}, true);
|
|
|
|
// Hide scroll fade gradient when text bottom is visible (works with column-reverse)
|
|
var scrollArea = document.querySelector('#xftp-protocol .overlay-card > .flex');
|
|
if (scrollArea) {
|
|
var textCol = scrollArea.querySelector(':scope > div:first-child > div');
|
|
function checkScrollBottom() {
|
|
if (!textCol) return;
|
|
var scrollRect = scrollArea.getBoundingClientRect();
|
|
if (scrollRect.height === 0) return; // not laid out yet
|
|
var textBottom = textCol.getBoundingClientRect().bottom;
|
|
document.getElementById('xftp-protocol').classList.toggle('scrolled-bottom', textBottom <= scrollRect.bottom + 10);
|
|
}
|
|
scrollArea.addEventListener('scroll', checkScrollBottom);
|
|
new MutationObserver(function() {
|
|
if (!document.getElementById('xftp-protocol').classList.contains('hidden')) {
|
|
requestAnimationFrame(checkScrollBottom);
|
|
}
|
|
}).observe(document.getElementById('xftp-protocol'), { attributes: true, attributeFilter: ['class'] });
|
|
}
|
|
</script>
|
|
|