Files
file-whizz/index.html
T
2025-03-20 14:20:18 +01:00

121 lines
6.4 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="stylesheet" href="style.css" />
<link rel="stylesheet" href="material-colors.css" />
<script src="https://cdn.jsdelivr.net/npm/peerjs@1.5.4/dist/peerjs.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lucide@0/dist/umd/lucide.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/qrcode-js-package@1/qrcode.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/daisyui@5" rel="stylesheet" type="text/css" />
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<link href="https://cdn.jsdelivr.net/npm/daisyui@5/themes.css" rel="stylesheet" type="text/css" />
<script type="importmap">
{
"imports": {
"vue": "https://cdn.jsdelivr.net/npm/vue@3/dist/vue.esm-browser.js"
}
}
</script>
<script type="module" src="main.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<title>🐝 File Whizz • No need for cloud when you have wings</title>
<meta name="description" content="Peer-to-peer file transfers in your web browser. No intermediary servers." />
<meta property="og:title" content="🐝 File Whizz • No need for cloud when you have wings">
<meta property="og:description" content="Peer-to-peer file transfers in your web browser. No intermediary servers.">
<meta property="og:url" content="https://file.klemek.fr">
<meta property="og:image" content="https://file.klemek.fr/preview.jpg">
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="🐝 File Whizz • No need for cloud when you have wings">
<meta name="twitter:description" content="Peer-to-peer file transfers in your web browser. No intermediary servers.">
<meta name="twitter:image" content="https://file.klemek.fr/preview.jpg" />
</head>
<body data-theme="bumblebee" class="min-h-screen flex flex-col w-screen overflow-y-hidden">
<div class="hero flex-grow bg-base-200">
<main id="app" class="hero-content text-center" style="display: none">
<div class="card w-full max-w-sm bg-base-100/95 shadow-sm">
<div class="card-body">
<h2 class="card-title text-4xl">🐝 File Whizz</h2>
<div v-if="isServer" class="mt-2 w-full italic">
Send files easily through <a class="underline" target="_blank" href="https://webrtc.org/">WebRTC</a>
directly to another device, without an intermediary server.
</div>
<div v-else class="mt-2 w-full">
Receive files easily through <a class="underline" target="_blank" href="https://webrtc.org/">WebRTC</a>
directly from another device, without an intermediary server.
</div>
<div class="mt-2 w-full">
<div class="inline-grid *:[grid-area:1/1] align-middle mr-1">
<div v-if="statusBlinking(status)" :class="`status status-${statusColor(status)} status-lg animate-ping`">
</div>
<div :class="`status status-${statusColor(status)} status-lg`"></div>
</div> {{ status }}
</div>
<div v-if="error" class="mt-2 text-error" v-html="error">
</div>
<div v-if="fileName" class="mt-2 w-full truncate">
<code>{{ fileName }}<br>({{ prettyFileSize }})</code>
</div>
<template v-if="isServer">
<div v-if="serverCanUpload" class="my-t w-full">
<label for="file-input" class="btn btn-primary w-full"><i icon="file-up"></i> Select file</label>
<input @change="onFileChange" id="file-input" type="file" class="hidden" />
</div>
<div ref="qrcode" id="qrcode" :class="serverIsReady ? '' : 'hidden'" class="w-full max-w-64 mx-auto mt-2">
</div>
<div v-if="serverIsReady" @click="onShare" class="mt-2 w-full btn btn-primary">
<i icon="share-2"></i> {{ serverShareText }}
</div>
<ul v-if="serverIsReady && server.clients.length" class="mt-2 list rounded-box text-left">
<li class="list-row px-0 py-2 first:pt-0 last:pb-0" v-for="client in server.clients"
v-bind:key="client.id">
<div class="list-col-grow">
<div>{{ client.userAgent ?? client.id }}</div>
<div>
<div class="inline-grid *:[grid-area:1/1] align-middle mr-1">
<div v-if="statusBlinking(client.status)"
:class="`status status-${statusColor(client.status)} animate-ping`">
</div>
<div :class="`status status-${statusColor(client.status)}`"></div>
</div> {{ client.status }}
</div>
<progress :value="client.sent" :max="fileSize" aria-busy="!client.connected" class="progress"
:class="client.sent >= fileSize ? 'progress-success' : 'progress-primary'"></progress>
</div>
</li>
</ul>
</template>
<template v-else>
<div @click="onDownload" :class="clientIsReady ? 'btn-primary' : 'btn-disabled'" class="mt-2 w-full btn">
<i icon="file-down"></i> Download
</div>
<div v-if="clientDownloading || clientFinished" class="w-full">
<progress :class="clientFinished ? 'progress-success' : 'progress-primary'"
:value="clientDownloadProgress" :max="fileSize" class="progress w-full"></progress>
<label>{{ prettyDownloadSpeed }}<span v-if="clientDownloading"> - {{ prettyRemainingTime
}}</span></label>
</div>
</template>
</div>
</div>
</main>
</div>
<footer class="footer sm:footer-horizontal footer-center bg-base-300 text-base-content p-4">
<aside>
<p>
2025 - Made by
<i icon="at-sign"></i><a href="https://github.com/klemek" target="_blank" class="underline">klemek</a>
(<i icon="github"></i> <a href="https://github.com/klemek/file-whizz" target="_blank"
class="underline">Repository</a>) -
Photo by
<a href="https://unsplash.com/photos/shallow-focus-photography-of-purple-flowers-7mBwD5kKUrs"
class="underline">Lachlan Gowen</a>
on <a href="https://unsplash.com/" target="_blank" class="underline">Unsplash</a>
</p>
</aside>
</footer>
</body>
</html>