mirror of
https://github.com/schlagmichdoch/PairDrop.git
synced 2026-04-06 18:03:48 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59dca141b6 | ||
|
|
d0046e83cb | ||
|
|
2aeadb44e2 | ||
|
|
7827a47d29 | ||
|
|
4edc9c9b22 | ||
|
|
398a69d7a0 | ||
|
|
dfe69cc873 | ||
|
|
f5fde731b0 | ||
|
|
d6eee480b3 | ||
|
|
f6ad85a744 | ||
|
|
d50480b2f8 | ||
|
|
ac1e88b6a0 | ||
|
|
0fe36e132c |
7
.github/workflows/github-image.yml
vendored
7
.github/workflows/github-image.yml
vendored
@@ -28,6 +28,12 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup qemu
|
||||
uses: docker/setup-qemu-action@v2.1.0
|
||||
|
||||
- name: Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2.5.0
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
|
||||
@@ -46,6 +52,7 @@ jobs:
|
||||
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
@@ -57,9 +57,10 @@ Developed based on [Snapdrop](https://github.com/RobinLinus/snapdrop)
|
||||
* Change your display name permanently to easily differentiate your devices
|
||||
* [Paste files/text and choose the recipient afterwords ](https://github.com/RobinLinus/snapdrop/pull/534)
|
||||
* [Prevent devices from sleeping on file transfer](https://github.com/RobinLinus/snapdrop/pull/413)
|
||||
* Warn user before PairDrop is closed on file transfer
|
||||
* Warn user before PairDrop is closed on file transfer
|
||||
* Open PairDrop on multiple tabs simultaneously (Thanks [@willstott101](https://github.com/willstott101))
|
||||
* [Video and Audio preview](https://github.com/RobinLinus/snapdrop/pull/455) (Thanks [@victorwads](https://github.com/victorwads))
|
||||
* Switch theme back to auto/system after darkmode or lightmode is enabled
|
||||
* Node-only implementation (Thanks [@Bellisario](https://github.com/Bellisario))
|
||||
* Automatic restart on error (Thanks [@KaKi87](https://github.com/KaKi87))
|
||||
* Lots of stability fixes (Thanks [@MWY001](https://github.com/MWY001) [@skiby7](https://github.com/skiby7) and [@willstott101](https://github.com/willstott101))
|
||||
|
||||
@@ -33,10 +33,10 @@ https://routinehub.co/shortcut/13990/
|
||||
|
||||
|
||||
## Send directly from share menu on Android
|
||||
The [Web Share Target API](https://developer.mozilla.org/en-US/docs/Web/Manifest/share_target) is implemented but not yet tested.
|
||||
When the PWA is installed, it should register itself to the share-menu of the device automatically.
|
||||
The [Web Share Target API](https://developer.mozilla.org/en-US/docs/Web/Manifest/share_target) is implemented.
|
||||
|
||||
When the PWA is installed, it will register itself to the share-menu of the device automatically.
|
||||
|
||||
This feature is still under development. Please test this feature and create an issue if it does not work.
|
||||
|
||||
## Send directly via command-line interface
|
||||
Send files or text with PairDrop via command-line interface.
|
||||
|
||||
8
index.js
8
index.js
@@ -63,14 +63,6 @@ const rtcConfig = process.env.RTC_CONFIG
|
||||
"iceServers": [
|
||||
{
|
||||
"urls": "stun:stun.l.google.com:19302"
|
||||
},
|
||||
{
|
||||
"urls": "stun:openrelay.metered.ca:80"
|
||||
},
|
||||
{
|
||||
"urls": "turn:openrelay.metered.ca:443",
|
||||
"username": "openrelayproject",
|
||||
"credential": "openrelayproject"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
18
package-lock.json
generated
18
package-lock.json
generated
@@ -1,17 +1,17 @@
|
||||
{
|
||||
"name": "pairdrop",
|
||||
"version": "1.5.2",
|
||||
"version": "1.6.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "pairdrop",
|
||||
"version": "1.5.2",
|
||||
"version": "1.6.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"express": "^4.18.2",
|
||||
"express-rate-limit": "^6.7.0",
|
||||
"ua-parser-js": "^1.0.34",
|
||||
"ua-parser-js": "^1.0.35",
|
||||
"unique-names-generator": "^4.3.0",
|
||||
"ws": "^8.13.0"
|
||||
},
|
||||
@@ -583,9 +583,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ua-parser-js": {
|
||||
"version": "1.0.34",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.34.tgz",
|
||||
"integrity": "sha512-K9mwJm/DaB6mRLZfw6q8IMXipcrmuT6yfhYmwhAkuh+81sChuYstYA+znlgaflUPaYUa3odxKPKGw6Vw/lANew==",
|
||||
"version": "1.0.35",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.35.tgz",
|
||||
"integrity": "sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -1070,9 +1070,9 @@
|
||||
}
|
||||
},
|
||||
"ua-parser-js": {
|
||||
"version": "1.0.34",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.34.tgz",
|
||||
"integrity": "sha512-K9mwJm/DaB6mRLZfw6q8IMXipcrmuT6yfhYmwhAkuh+81sChuYstYA+znlgaflUPaYUa3odxKPKGw6Vw/lANew=="
|
||||
"version": "1.0.35",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.35.tgz",
|
||||
"integrity": "sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA=="
|
||||
},
|
||||
"unique-names-generator": {
|
||||
"version": "4.7.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pairdrop",
|
||||
"version": "1.5.2",
|
||||
"version": "1.6.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
@@ -12,7 +12,7 @@
|
||||
"dependencies": {
|
||||
"express": "^4.18.2",
|
||||
"express-rate-limit": "^6.7.0",
|
||||
"ua-parser-js": "^1.0.34",
|
||||
"ua-parser-js": "^1.0.35",
|
||||
"unique-names-generator": "^4.3.0",
|
||||
"ws": "^8.13.0"
|
||||
},
|
||||
|
||||
@@ -44,32 +44,46 @@
|
||||
<use xlink:href="#info-outline" />
|
||||
</svg>
|
||||
</a>
|
||||
<a id="theme" class="icon-button" title="Switch Darkmode/Lightmode" >
|
||||
<svg class="icon">
|
||||
<use xlink:href="#icon-theme" />
|
||||
</svg>
|
||||
</a>
|
||||
<a id="notification" class="icon-button" title="Enable Notifications" hidden>
|
||||
<div id="theme-wrapper">
|
||||
<div id="theme-auto" class="icon-button selected" title="Adapt to System" >
|
||||
<svg class="icon">
|
||||
<use xlink:href="#icon-theme-auto" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div id="theme-light" class="icon-button" title="Always Light" >
|
||||
<svg class="icon">
|
||||
<use xlink:href="#icon-theme-light" />
|
||||
</svg>
|
||||
</div>
|
||||
<div id="theme-dark" class="icon-button" title="Always Dark" >
|
||||
<svg class="icon">
|
||||
<use xlink:href="#icon-theme-dark" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="notification" class="icon-button" title="Enable Notifications" hidden>
|
||||
<svg class="icon">
|
||||
<use xlink:href="#notifications" />
|
||||
</svg>
|
||||
</a>
|
||||
<a id="install" class="icon-button" title="Install PairDrop" hidden>
|
||||
</div>
|
||||
<div id="install" class="icon-button" title="Install PairDrop" hidden>
|
||||
<svg class="icon">
|
||||
<use xlink:href="#homescreen" />
|
||||
</svg>
|
||||
</a>
|
||||
<a id="pair-device" class="icon-button" title="Pair Device" hidden>
|
||||
</div>
|
||||
<div id="pair-device" class="icon-button" title="Pair Device" hidden>
|
||||
<svg class="icon">
|
||||
<use xlink:href="#pair-device-icon" />
|
||||
</svg>
|
||||
</a>
|
||||
<a id="clear-pair-devices" class="icon-button" title="Clear All Paired Devices" hidden>
|
||||
</div>
|
||||
<div id="clear-pair-devices" class="icon-button" title="Clear All Paired Devices" hidden>
|
||||
<svg class="icon">
|
||||
<use xlink:href="#clear-pair-devices-icon" />
|
||||
</svg>
|
||||
</a>
|
||||
<a id="cancel-paste-mode" class="button" hidden>Done</a>
|
||||
</div>
|
||||
<div id="cancel-paste-mode" class="button" hidden>Done</div>
|
||||
</header>
|
||||
<!-- Center -->
|
||||
<div id="center">
|
||||
@@ -112,12 +126,12 @@
|
||||
<div id="pair-instructions" class="font-subheading center text-center">Input this key on another device<br>or scan the QR-Code.</div>
|
||||
<hr>
|
||||
<div id="key-input-container">
|
||||
<input type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" autofocus contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-1" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" autofocus contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-2" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-3" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-4" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-5" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-6" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
</div>
|
||||
<div class="font-subheading center text-center">Enter key from another device to continue.</div>
|
||||
<div class="center row-reverse">
|
||||
@@ -206,7 +220,7 @@
|
||||
<span class="display-name"></span>
|
||||
</div>
|
||||
<div class="row-separator"></div>
|
||||
<div id="text-input" class="textarea" role="textbox" autocapitalize="none" spellcheck="false" autofocus contenteditable></div>
|
||||
<div id="text-input" title="Message" class="textarea" role="textbox" autocapitalize="none" spellcheck="false" autofocus contenteditable></div>
|
||||
<div class="center row-reverse">
|
||||
<button class="button" type="submit" title="STR + ENTER" disabled close>Send</button>
|
||||
<button class="button" type="button" title="ESCAPE" close>Cancel</button>
|
||||
@@ -328,7 +342,13 @@
|
||||
<path d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1.41 16.09V20h-2.67v-1.93c-1.71-.36-3.16-1.46-3.27-3.4h1.96c.1 1.05.82 1.87 2.65 1.87 1.96 0 2.4-.98 2.4-1.59 0-.83-.44-1.61-2.67-2.14-2.48-.6-4.18-1.62-4.18-3.67 0-1.72 1.39-2.84 3.11-3.21V4h2.67v1.95c1.86.45 2.79 1.86 2.85 3.39H14.3c-.05-1.11-.64-1.87-2.22-1.87-1.5 0-2.4.68-2.4 1.64 0 .84.65 1.39 2.67 1.91s4.18 1.39 4.18 3.91c-.01 1.83-1.38 2.83-3.12 3.16z" />
|
||||
</symbol>
|
||||
<symbol id="icon-theme" viewBox="0 0 24 24">
|
||||
<symbol id="icon-theme-auto" viewBox="0 0 24 24">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-54 -54 620 620"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M448 256c0-106-86-192-192-192V448c106 0 192-86 192-192zM0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256z"/></svg>
|
||||
</symbol>
|
||||
<symbol id="icon-theme-light" viewBox="0 0 24 24">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-54 -54 620 620"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M361.5 1.2c5 2.1 8.6 6.6 9.6 11.9L391 121l107.9 19.8c5.3 1 9.8 4.6 11.9 9.6s1.5 10.7-1.6 15.2L446.9 256l62.3 90.3c3.1 4.5 3.7 10.2 1.6 15.2s-6.6 8.6-11.9 9.6L391 391 371.1 498.9c-1 5.3-4.6 9.8-9.6 11.9s-10.7 1.5-15.2-1.6L256 446.9l-90.3 62.3c-4.5 3.1-10.2 3.7-15.2 1.6s-8.6-6.6-9.6-11.9L121 391 13.1 371.1c-5.3-1-9.8-4.6-11.9-9.6s-1.5-10.7 1.6-15.2L65.1 256 2.8 165.7c-3.1-4.5-3.7-10.2-1.6-15.2s6.6-8.6 11.9-9.6L121 121 140.9 13.1c1-5.3 4.6-9.8 9.6-11.9s10.7-1.5 15.2 1.6L256 65.1 346.3 2.8c4.5-3.1 10.2-3.7 15.2-1.6zM160 256a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zm224 0a128 128 0 1 0 -256 0 128 128 0 1 0 256 0z"/></svg>
|
||||
</symbol>
|
||||
<symbol id="icon-theme-dark" viewBox="0 0 24 24">
|
||||
<rect fill="none" height="24" width="24"/><path d="M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36c-0.98,1.37-2.58,2.26-4.4,2.26 c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"/>
|
||||
</symbol>
|
||||
<symbol id="pair-device-icon" viewBox="0 0 640 512">
|
||||
@@ -345,11 +365,11 @@
|
||||
</symbol>
|
||||
</svg>
|
||||
<!-- Scripts -->
|
||||
<script src="scripts/util.js"></script>
|
||||
<script src="scripts/theme.js"></script>
|
||||
<script src="scripts/network.js"></script>
|
||||
<script src="scripts/ui.js"></script>
|
||||
<script src="scripts/theme.js" async></script>
|
||||
<script src="scripts/qrcode.js" async></script>
|
||||
<script src="scripts/util.js" async></script>
|
||||
<script src="scripts/QRCode.min.js" async></script>
|
||||
<script src="scripts/zip.min.js" async></script>
|
||||
<script src="scripts/NoSleep.min.js" async></script>
|
||||
<!-- Sounds -->
|
||||
|
||||
2
public/robots.txt
Normal file
2
public/robots.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow:
|
||||
@@ -1,39 +1,78 @@
|
||||
(function(){
|
||||
|
||||
// Select the button
|
||||
const btnTheme = document.getElementById('theme');
|
||||
// Check for dark mode preference at the OS level
|
||||
const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
|
||||
// Get the user's theme preference from local storage, if it's available
|
||||
const currentTheme = localStorage.getItem('theme');
|
||||
// If the user's preference in localStorage is dark...
|
||||
const prefersDarkTheme = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
const prefersLightTheme = window.matchMedia('(prefers-color-scheme: light)').matches;
|
||||
|
||||
const $themeAuto = document.getElementById('theme-auto');
|
||||
const $themeLight = document.getElementById('theme-light');
|
||||
const $themeDark = document.getElementById('theme-dark');
|
||||
|
||||
let currentTheme = localStorage.getItem('theme');
|
||||
|
||||
if (currentTheme === 'dark') {
|
||||
// ...let's toggle the .dark-theme class on the body
|
||||
document.body.classList.toggle('dark-theme');
|
||||
// Otherwise, if the user's preference in localStorage is light...
|
||||
setModeToDark();
|
||||
} else if (currentTheme === 'light') {
|
||||
// ...let's toggle the .light-theme class on the body
|
||||
document.body.classList.toggle('light-theme');
|
||||
setModeToLight();
|
||||
}
|
||||
|
||||
// Listen for a click on the button
|
||||
btnTheme.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
// If the user's OS setting is dark and matches our .dark-theme class...
|
||||
let theme;
|
||||
if (prefersDarkScheme.matches) {
|
||||
// ...then toggle the light mode class
|
||||
document.body.classList.toggle('light-theme');
|
||||
// ...but use .dark-theme if the .light-theme class is already on the body,
|
||||
theme = document.body.classList.contains('light-theme') ? 'light' : 'dark';
|
||||
|
||||
$themeAuto.addEventListener('click', _ => {
|
||||
if (currentTheme) {
|
||||
setModeToAuto();
|
||||
} else {
|
||||
// Otherwise, let's do the same thing, but for .dark-theme
|
||||
document.body.classList.toggle('dark-theme');
|
||||
theme = document.body.classList.contains('dark-theme') ? 'dark' : 'light';
|
||||
setModeToDark();
|
||||
}
|
||||
});
|
||||
$themeLight.addEventListener('click', _ => {
|
||||
if (currentTheme !== 'light') {
|
||||
setModeToLight();
|
||||
} else {
|
||||
setModeToAuto();
|
||||
}
|
||||
});
|
||||
$themeDark.addEventListener('click', _ => {
|
||||
if (currentTheme !== 'dark') {
|
||||
setModeToDark();
|
||||
} else {
|
||||
setModeToLight();
|
||||
}
|
||||
// Finally, let's save the current preference to localStorage to keep using it
|
||||
localStorage.setItem('theme', theme);
|
||||
});
|
||||
|
||||
function setModeToDark() {
|
||||
document.body.classList.remove('light-theme');
|
||||
document.body.classList.add('dark-theme');
|
||||
localStorage.setItem('theme', 'dark');
|
||||
currentTheme = 'dark';
|
||||
|
||||
$themeAuto.classList.remove("selected");
|
||||
$themeLight.classList.remove("selected");
|
||||
$themeDark.classList.add("selected");
|
||||
}
|
||||
|
||||
function setModeToLight() {
|
||||
document.body.classList.remove('dark-theme');
|
||||
document.body.classList.add('light-theme');
|
||||
localStorage.setItem('theme', 'light');
|
||||
currentTheme = 'light';
|
||||
|
||||
$themeAuto.classList.remove("selected");
|
||||
$themeLight.classList.add("selected");
|
||||
$themeDark.classList.remove("selected");
|
||||
}
|
||||
|
||||
function setModeToAuto() {
|
||||
document.body.classList.remove('dark-theme');
|
||||
document.body.classList.remove('light-theme');
|
||||
if (prefersDarkTheme) {
|
||||
document.body.classList.add('dark-theme');
|
||||
} else if (prefersLightTheme) {
|
||||
document.body.classList.add('light-theme');
|
||||
}
|
||||
localStorage.removeItem('theme');
|
||||
currentTheme = undefined;
|
||||
|
||||
$themeAuto.classList.add("selected");
|
||||
$themeLight.classList.remove("selected");
|
||||
$themeDark.classList.remove("selected");
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const cacheVersion = 'v1.5.2';
|
||||
const cacheVersion = 'v1.6.0';
|
||||
const cacheTitle = `pairdrop-cache-${cacheVersion}`;
|
||||
const urlsToCache = [
|
||||
'index.html',
|
||||
|
||||
@@ -75,11 +75,75 @@ html {
|
||||
}
|
||||
|
||||
header {
|
||||
position: relative;
|
||||
height: 56px;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
position: absolute;
|
||||
align-items: baseline;
|
||||
padding: 8px 16px;
|
||||
box-sizing: border-box;
|
||||
width: 100vw;
|
||||
z-index: 2;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
header > a,
|
||||
header > div {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
header > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-self: flex-start;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
header > div .icon-button {
|
||||
height: 40px;
|
||||
transition: all 300ms;
|
||||
}
|
||||
|
||||
header > div > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
header > div:not(:hover) .icon-button:not(.selected) {
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
header > div:hover::before {
|
||||
border-radius: 20px;
|
||||
background: currentColor;
|
||||
opacity: 0.1;
|
||||
transition: opacity 300ms;
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 40px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
header > div:hover .icon-button.selected::before {
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
@media (pointer: coarse) {
|
||||
header > div:hover .icon-button.selected:hover::before {
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
header > div .icon-button:not(.selected) {
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
header > div > div {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
@@ -192,15 +256,10 @@ x-noscript {
|
||||
}
|
||||
}
|
||||
|
||||
/* Main Header */
|
||||
|
||||
body>header a {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
#center {
|
||||
position: relative;
|
||||
display: flex;
|
||||
margin-top: 56px;
|
||||
flex-direction: column-reverse;
|
||||
flex-grow: 1;
|
||||
--footer-height: 132px;
|
||||
@@ -974,8 +1033,8 @@ button::-moz-focus-inner {
|
||||
|
||||
#about x-background {
|
||||
position: absolute;
|
||||
top: calc(32px - 250px);
|
||||
right: calc(32px - 250px);
|
||||
top: calc(28px - 250px);
|
||||
right: calc(36px - 250px);
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
border-radius: 50%;
|
||||
|
||||
@@ -44,32 +44,46 @@
|
||||
<use xlink:href="#info-outline" />
|
||||
</svg>
|
||||
</a>
|
||||
<a id="theme" class="icon-button" title="Switch Darkmode/Lightmode" >
|
||||
<svg class="icon">
|
||||
<use xlink:href="#icon-theme" />
|
||||
</svg>
|
||||
</a>
|
||||
<a id="notification" class="icon-button" title="Enable Notifications" hidden>
|
||||
<div id="theme-wrapper">
|
||||
<div id="theme-auto" class="icon-button selected" title="Adapt to System" >
|
||||
<svg class="icon">
|
||||
<use xlink:href="#icon-theme-auto" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div id="theme-light" class="icon-button" title="Always Light" >
|
||||
<svg class="icon">
|
||||
<use xlink:href="#icon-theme-light" />
|
||||
</svg>
|
||||
</div>
|
||||
<div id="theme-dark" class="icon-button" title="Always Dark" >
|
||||
<svg class="icon">
|
||||
<use xlink:href="#icon-theme-dark" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="notification" class="icon-button" title="Enable Notifications" hidden>
|
||||
<svg class="icon">
|
||||
<use xlink:href="#notifications" />
|
||||
</svg>
|
||||
</a>
|
||||
<a id="install" class="icon-button" title="Install PairDrop" hidden>
|
||||
</div>
|
||||
<div id="install" class="icon-button" title="Install PairDrop" hidden>
|
||||
<svg class="icon">
|
||||
<use xlink:href="#homescreen" />
|
||||
</svg>
|
||||
</a>
|
||||
<a id="pair-device" class="icon-button" title="Pair Device" hidden>
|
||||
</div>
|
||||
<div id="pair-device" class="icon-button" title="Pair Device" hidden>
|
||||
<svg class="icon">
|
||||
<use xlink:href="#pair-device-icon" />
|
||||
</svg>
|
||||
</a>
|
||||
<a id="clear-pair-devices" class="icon-button" title="Clear All Paired Devices" hidden>
|
||||
</div>
|
||||
<div id="clear-pair-devices" class="icon-button" title="Clear All Paired Devices" hidden>
|
||||
<svg class="icon">
|
||||
<use xlink:href="#clear-pair-devices-icon" />
|
||||
</svg>
|
||||
</a>
|
||||
<a id="cancel-paste-mode" class="button" hidden>Done</a>
|
||||
</div>
|
||||
<div id="cancel-paste-mode" class="button" hidden>Done</div>
|
||||
</header>
|
||||
<!-- Center -->
|
||||
<div id="center">
|
||||
@@ -115,12 +129,12 @@
|
||||
<div id="pair-instructions" class="font-subheading center text-center">Input this key on another device<br>or scan the QR-Code.</div>
|
||||
<hr>
|
||||
<div id="key-input-container">
|
||||
<input type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" autofocus contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-1" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" autofocus contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-2" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-3" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-4" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-5" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
<input type="tel" class="textarea center" aria-label="pair-key-6" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder="" disabled>
|
||||
</div>
|
||||
<div class="font-subheading center text-center">Enter key from another device to continue.</div>
|
||||
<div class="center row-reverse">
|
||||
@@ -209,7 +223,7 @@
|
||||
<span class="display-name"></span>
|
||||
</div>
|
||||
<div class="row-separator"></div>
|
||||
<div id="text-input" class="textarea" role="textbox" autocapitalize="none" spellcheck="false" autofocus contenteditable></div>
|
||||
<div id="text-input" title="Message" class="textarea" role="textbox" autocapitalize="none" spellcheck="false" autofocus contenteditable></div>
|
||||
<div class="center row-reverse">
|
||||
<button class="button" type="submit" title="STR + ENTER" disabled close>Send</button>
|
||||
<button class="button" type="button" title="ESCAPE" close>Cancel</button>
|
||||
@@ -331,7 +345,13 @@
|
||||
<path d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1.41 16.09V20h-2.67v-1.93c-1.71-.36-3.16-1.46-3.27-3.4h1.96c.1 1.05.82 1.87 2.65 1.87 1.96 0 2.4-.98 2.4-1.59 0-.83-.44-1.61-2.67-2.14-2.48-.6-4.18-1.62-4.18-3.67 0-1.72 1.39-2.84 3.11-3.21V4h2.67v1.95c1.86.45 2.79 1.86 2.85 3.39H14.3c-.05-1.11-.64-1.87-2.22-1.87-1.5 0-2.4.68-2.4 1.64 0 .84.65 1.39 2.67 1.91s4.18 1.39 4.18 3.91c-.01 1.83-1.38 2.83-3.12 3.16z" />
|
||||
</symbol>
|
||||
<symbol id="icon-theme" viewBox="0 0 24 24">
|
||||
<symbol id="icon-theme-auto" viewBox="0 0 24 24">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-54 -54 620 620"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M448 256c0-106-86-192-192-192V448c106 0 192-86 192-192zM0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256z"/></svg>
|
||||
</symbol>
|
||||
<symbol id="icon-theme-light" viewBox="0 0 24 24">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-54 -54 620 620"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M361.5 1.2c5 2.1 8.6 6.6 9.6 11.9L391 121l107.9 19.8c5.3 1 9.8 4.6 11.9 9.6s1.5 10.7-1.6 15.2L446.9 256l62.3 90.3c3.1 4.5 3.7 10.2 1.6 15.2s-6.6 8.6-11.9 9.6L391 391 371.1 498.9c-1 5.3-4.6 9.8-9.6 11.9s-10.7 1.5-15.2-1.6L256 446.9l-90.3 62.3c-4.5 3.1-10.2 3.7-15.2 1.6s-8.6-6.6-9.6-11.9L121 391 13.1 371.1c-5.3-1-9.8-4.6-11.9-9.6s-1.5-10.7 1.6-15.2L65.1 256 2.8 165.7c-3.1-4.5-3.7-10.2-1.6-15.2s6.6-8.6 11.9-9.6L121 121 140.9 13.1c1-5.3 4.6-9.8 9.6-11.9s10.7-1.5 15.2 1.6L256 65.1 346.3 2.8c4.5-3.1 10.2-3.7 15.2-1.6zM160 256a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zm224 0a128 128 0 1 0 -256 0 128 128 0 1 0 256 0z"/></svg>
|
||||
</symbol>
|
||||
<symbol id="icon-theme-dark" viewBox="0 0 24 24">
|
||||
<rect fill="none" height="24" width="24"/><path d="M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36c-0.98,1.37-2.58,2.26-4.4,2.26 c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"/>
|
||||
</symbol>
|
||||
<symbol id="pair-device-icon" viewBox="0 0 640 512">
|
||||
@@ -348,11 +368,11 @@
|
||||
</symbol>
|
||||
</svg>
|
||||
<!-- Scripts -->
|
||||
<script src="scripts/util.js"></script>
|
||||
<script src="scripts/theme.js"></script>
|
||||
<script src="scripts/network.js"></script>
|
||||
<script src="scripts/ui.js"></script>
|
||||
<script src="scripts/theme.js" async></script>
|
||||
<script src="scripts/qrcode.js" async></script>
|
||||
<script src="scripts/util.js" async></script>
|
||||
<script src="scripts/QRCode.min.js" async></script>
|
||||
<script src="scripts/zip.min.js" async></script>
|
||||
<script src="scripts/NoSleep.min.js" async></script>
|
||||
<!-- Sounds -->
|
||||
|
||||
2
public_included_ws_fallback/scripts/robots.txt
Normal file
2
public_included_ws_fallback/scripts/robots.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow:
|
||||
@@ -1,39 +1,78 @@
|
||||
(function(){
|
||||
|
||||
// Select the button
|
||||
const btnTheme = document.getElementById('theme');
|
||||
// Check for dark mode preference at the OS level
|
||||
const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
|
||||
// Get the user's theme preference from local storage, if it's available
|
||||
const currentTheme = localStorage.getItem('theme');
|
||||
// If the user's preference in localStorage is dark...
|
||||
const prefersDarkTheme = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
const prefersLightTheme = window.matchMedia('(prefers-color-scheme: light)').matches;
|
||||
|
||||
const $themeAuto = document.getElementById('theme-auto');
|
||||
const $themeLight = document.getElementById('theme-light');
|
||||
const $themeDark = document.getElementById('theme-dark');
|
||||
|
||||
let currentTheme = localStorage.getItem('theme');
|
||||
|
||||
if (currentTheme === 'dark') {
|
||||
// ...let's toggle the .dark-theme class on the body
|
||||
document.body.classList.toggle('dark-theme');
|
||||
// Otherwise, if the user's preference in localStorage is light...
|
||||
setModeToDark();
|
||||
} else if (currentTheme === 'light') {
|
||||
// ...let's toggle the .light-theme class on the body
|
||||
document.body.classList.toggle('light-theme');
|
||||
setModeToLight();
|
||||
}
|
||||
|
||||
// Listen for a click on the button
|
||||
btnTheme.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
// If the user's OS setting is dark and matches our .dark-theme class...
|
||||
let theme;
|
||||
if (prefersDarkScheme.matches) {
|
||||
// ...then toggle the light mode class
|
||||
document.body.classList.toggle('light-theme');
|
||||
// ...but use .dark-theme if the .light-theme class is already on the body,
|
||||
theme = document.body.classList.contains('light-theme') ? 'light' : 'dark';
|
||||
|
||||
$themeAuto.addEventListener('click', _ => {
|
||||
if (currentTheme) {
|
||||
setModeToAuto();
|
||||
} else {
|
||||
// Otherwise, let's do the same thing, but for .dark-theme
|
||||
document.body.classList.toggle('dark-theme');
|
||||
theme = document.body.classList.contains('dark-theme') ? 'dark' : 'light';
|
||||
setModeToDark();
|
||||
}
|
||||
});
|
||||
$themeLight.addEventListener('click', _ => {
|
||||
if (currentTheme !== 'light') {
|
||||
setModeToLight();
|
||||
} else {
|
||||
setModeToAuto();
|
||||
}
|
||||
});
|
||||
$themeDark.addEventListener('click', _ => {
|
||||
if (currentTheme !== 'dark') {
|
||||
setModeToDark();
|
||||
} else {
|
||||
setModeToLight();
|
||||
}
|
||||
// Finally, let's save the current preference to localStorage to keep using it
|
||||
localStorage.setItem('theme', theme);
|
||||
});
|
||||
|
||||
function setModeToDark() {
|
||||
document.body.classList.remove('light-theme');
|
||||
document.body.classList.add('dark-theme');
|
||||
localStorage.setItem('theme', 'dark');
|
||||
currentTheme = 'dark';
|
||||
|
||||
$themeAuto.classList.remove("selected");
|
||||
$themeLight.classList.remove("selected");
|
||||
$themeDark.classList.add("selected");
|
||||
}
|
||||
|
||||
function setModeToLight() {
|
||||
document.body.classList.remove('dark-theme');
|
||||
document.body.classList.add('light-theme');
|
||||
localStorage.setItem('theme', 'light');
|
||||
currentTheme = 'light';
|
||||
|
||||
$themeAuto.classList.remove("selected");
|
||||
$themeLight.classList.add("selected");
|
||||
$themeDark.classList.remove("selected");
|
||||
}
|
||||
|
||||
function setModeToAuto() {
|
||||
document.body.classList.remove('dark-theme');
|
||||
document.body.classList.remove('light-theme');
|
||||
if (prefersDarkTheme) {
|
||||
document.body.classList.add('dark-theme');
|
||||
} else if (prefersLightTheme) {
|
||||
document.body.classList.add('light-theme');
|
||||
}
|
||||
localStorage.removeItem('theme');
|
||||
currentTheme = undefined;
|
||||
|
||||
$themeAuto.classList.add("selected");
|
||||
$themeLight.classList.remove("selected");
|
||||
$themeDark.classList.remove("selected");
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const cacheVersion = 'v1.5.2';
|
||||
const cacheVersion = 'v1.6.0';
|
||||
const cacheTitle = `pairdrop-included-ws-fallback-cache-${cacheVersion}`;
|
||||
const urlsToCache = [
|
||||
'index.html',
|
||||
|
||||
@@ -76,11 +76,75 @@ html {
|
||||
}
|
||||
|
||||
header {
|
||||
position: relative;
|
||||
height: 56px;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
position: absolute;
|
||||
align-items: baseline;
|
||||
padding: 8px 16px;
|
||||
box-sizing: border-box;
|
||||
width: 100vw;
|
||||
z-index: 2;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
header > a,
|
||||
header > div {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
header > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-self: flex-start;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
header > div .icon-button {
|
||||
height: 40px;
|
||||
transition: all 300ms;
|
||||
}
|
||||
|
||||
header > div > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
header > div:not(:hover) .icon-button:not(.selected) {
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
header > div:hover::before {
|
||||
border-radius: 20px;
|
||||
background: currentColor;
|
||||
opacity: 0.1;
|
||||
transition: opacity 300ms;
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 40px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
header > div:hover .icon-button.selected::before {
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
@media (pointer: coarse) {
|
||||
header > div:hover .icon-button.selected:hover::before {
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
header > div .icon-button:not(.selected) {
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
header > div > div {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
@@ -193,15 +257,10 @@ x-noscript {
|
||||
}
|
||||
}
|
||||
|
||||
/* Main Header */
|
||||
|
||||
body>header a {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
#center {
|
||||
position: relative;
|
||||
display: flex;
|
||||
margin-top: 56px;
|
||||
flex-direction: column-reverse;
|
||||
flex-grow: 1;
|
||||
--footer-height: 146px;
|
||||
|
||||
@@ -3,14 +3,6 @@
|
||||
"iceServers": [
|
||||
{
|
||||
"urls": "stun:stun.l.google.com:19302"
|
||||
},
|
||||
{
|
||||
"urls": "stun:openrelay.metered.ca:80"
|
||||
},
|
||||
{
|
||||
"urls": "turn:openrelay.metered.ca:443",
|
||||
"username": "openrelayproject",
|
||||
"credential": "openrelayproject"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user