mirror of
https://github.com/mollersuite/monofile.git
synced 2024-11-21 13:36:25 -08:00
Abort abort abort
This commit is contained in:
parent
2112c75a7d
commit
ab673ea46e
193
package-lock.json
generated
193
package-lock.json
generated
|
@ -37,7 +37,7 @@
|
|||
"@types/range-parser": "^1.2.6",
|
||||
"discord-api-types": "^0.37.61",
|
||||
"sass": "^1.57.1",
|
||||
"svelte": "^3.55.1",
|
||||
"svelte": "^4.2.15",
|
||||
"svelte-preprocess": "^5.1.3",
|
||||
"tslib": "^2.6.2",
|
||||
"vite": "^4.5.0"
|
||||
|
@ -46,6 +46,19 @@
|
|||
"node": ">=v21"
|
||||
}
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
|
||||
"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.24"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.18.20",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
|
||||
|
@ -70,12 +83,54 @@
|
|||
"node": ">=18.14.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
|
||||
"integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.2.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.24"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/set-array": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
|
||||
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
|
||||
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.25",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
|
||||
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@sveltejs/vite-plugin-svelte": {
|
||||
"version": "2.4.6",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.4.6.tgz",
|
||||
|
@ -199,6 +254,12 @@
|
|||
"@types/express": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/express": {
|
||||
"version": "4.17.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz",
|
||||
|
@ -292,6 +353,18 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/anymatch": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
|
@ -310,6 +383,15 @@
|
|||
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
|
||||
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
|
||||
},
|
||||
"node_modules/aria-query": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
|
||||
"integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"dequal": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/array-flatten": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
|
@ -334,6 +416,15 @@
|
|||
"form-data": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/axobject-query": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz",
|
||||
"integrity": "sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"dequal": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
|
@ -466,6 +557,19 @@
|
|||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/code-red": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz",
|
||||
"integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.4.15",
|
||||
"@types/estree": "^1.0.1",
|
||||
"acorn": "^8.10.0",
|
||||
"estree-walker": "^3.0.3",
|
||||
"periscopic": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
|
@ -562,6 +666,19 @@
|
|||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
|
||||
},
|
||||
"node_modules/css-tree": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
|
||||
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"mdn-data": "2.0.30",
|
||||
"source-map-js": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/data-uri-to-buffer": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
|
||||
|
@ -603,6 +720,15 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/dequal": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
||||
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/destroy": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
||||
|
@ -705,6 +831,15 @@
|
|||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
|
||||
},
|
||||
"node_modules/estree-walker": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
|
||||
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/etag": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||
|
@ -1077,6 +1212,15 @@
|
|||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-reference": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz",
|
||||
"integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
|
@ -1091,6 +1235,12 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/locate-character": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
|
||||
"integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.5",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz",
|
||||
|
@ -1103,6 +1253,12 @@
|
|||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/mdn-data": {
|
||||
"version": "2.0.30",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
|
||||
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
|
@ -1348,6 +1504,17 @@
|
|||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
|
||||
},
|
||||
"node_modules/periscopic": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz",
|
||||
"integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.0",
|
||||
"estree-walker": "^3.0.0",
|
||||
"is-reference": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
|
@ -1685,12 +1852,28 @@
|
|||
}
|
||||
},
|
||||
"node_modules/svelte": {
|
||||
"version": "3.55.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.55.1.tgz",
|
||||
"integrity": "sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ==",
|
||||
"version": "4.2.15",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.15.tgz",
|
||||
"integrity": "sha512-j9KJSccHgLeRERPlhMKrCXpk2TqL2m5Z+k+OBTQhZOhIdCCd3WfqV+ylPWeipEwq17P/ekiSFWwrVQv93i3bsg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.15",
|
||||
"@jridgewell/trace-mapping": "^0.3.18",
|
||||
"@types/estree": "^1.0.1",
|
||||
"acorn": "^8.9.0",
|
||||
"aria-query": "^5.3.0",
|
||||
"axobject-query": "^4.0.0",
|
||||
"code-red": "^1.0.3",
|
||||
"css-tree": "^2.3.1",
|
||||
"estree-walker": "^3.0.3",
|
||||
"is-reference": "^3.0.1",
|
||||
"locate-character": "^3.0.0",
|
||||
"magic-string": "^0.30.4",
|
||||
"periscopic": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-hmr": {
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
"@types/range-parser": "^1.2.6",
|
||||
"discord-api-types": "^0.37.61",
|
||||
"sass": "^1.57.1",
|
||||
"svelte": "^3.55.1",
|
||||
"svelte": "^4.2.15",
|
||||
"svelte-preprocess": "^5.1.3",
|
||||
"tslib": "^2.6.2",
|
||||
"vite": "^4.5.0"
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>$FileId</title>
|
||||
|
||||
<!--metaTags-->
|
||||
|
||||
<meta name="og:site_name" content="$Uploader">
|
||||
<meta name="title" content="$FileName">
|
||||
<meta name="description" content="$FileSize file on monofile $Version, the Discord-based file sharing service">
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="./style/downloads.scss"
|
||||
>
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="/api/v1/account/me/css"
|
||||
>
|
||||
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/svg"
|
||||
href="/static/assets/icons/file_icon.svg"
|
||||
>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="appContent">
|
||||
<div id="uploadWindow">
|
||||
<h1>
|
||||
$FileName
|
||||
</h1>
|
||||
<p style="color:#999999">
|
||||
<span class="number">$FileSize</span> — uploaded by <span class="number">$Uploader</span>
|
||||
</p>
|
||||
|
||||
<!--preview-->
|
||||
|
||||
<button style="position:relative;width:100%;top:10px;">
|
||||
<a id="dlbtn" href="/file/$FileId" download="$FileName" style="position:absolute;left:0px;top:0px;height:100%;width:100%;"></a>
|
||||
download
|
||||
</button>
|
||||
|
||||
<div style="min-height:15px" />
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
could probably replace this with fonts served directly
|
||||
from the server but it's fine for now
|
||||
*/
|
||||
|
||||
@import url("/static/assets/fonts/inconsolata.css");
|
||||
@import url("/static/assets/fonts/source_sans.css");
|
||||
@import url("/static/assets/fonts/fira_code.css");
|
||||
|
||||
$FallbackFonts:
|
||||
-apple-system,
|
||||
system-ui,
|
||||
BlinkMacSystemFont,
|
||||
"Segoe UI",
|
||||
Roboto,
|
||||
sans-serif;
|
||||
|
||||
%normal {
|
||||
font-family: "Source Sans Pro", $FallbackFonts
|
||||
}
|
||||
|
||||
/*
|
||||
everything that's not a span
|
||||
and/or has the normal class
|
||||
(it's just in case)
|
||||
*/
|
||||
|
||||
*:not(span), .normal { @extend %normal; }
|
||||
|
||||
/*
|
||||
for code blocks / terminal
|
||||
*/
|
||||
|
||||
.monospace {
|
||||
font-family: "Fira Code", monospace
|
||||
}
|
||||
|
||||
/*
|
||||
colors
|
||||
*/
|
||||
|
||||
$Background: #252525;
|
||||
/* hsl(210,12.9,24.3) */
|
||||
$darkish: rgb(54, 62, 70);
|
||||
|
||||
/*
|
||||
then other stuff
|
||||
*/
|
||||
|
||||
body {
|
||||
background-color: rgb(30, 33, 36); // this is here so that
|
||||
// pulling down to refresh
|
||||
// on mobile looks good
|
||||
}
|
||||
|
||||
#appContent {
|
||||
background-color: $Background
|
||||
}
|
||||
|
||||
/*
|
||||
scrollbars
|
||||
*/
|
||||
|
||||
* {
|
||||
/* nice scrollbars aren't needed on mobile so */
|
||||
@media screen and (min-width:500px) {
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width:5px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color:#222222;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color:#333;
|
||||
|
||||
&:hover {
|
||||
background-color:#373737;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
@use "base";
|
||||
@use "app/topbar";
|
||||
@use "app/pulldown";
|
||||
@use "app/uploads";
|
||||
|
||||
.menuBtn {
|
||||
text-decoration:none;
|
||||
font-size:16px;
|
||||
transition-duration: 100ms;
|
||||
|
||||
color:#555555;
|
||||
background-color: #00000000;
|
||||
border:none;
|
||||
margin:0 0 0 0;
|
||||
cursor:pointer;
|
||||
|
||||
position:relative;
|
||||
top:-1px;
|
||||
|
||||
&:hover {
|
||||
color:slategray;
|
||||
transition-duration: 100ms;
|
||||
}
|
||||
}
|
||||
|
||||
#appContent {
|
||||
position:absolute;
|
||||
left:0px;
|
||||
top:40px;
|
||||
width:100%;
|
||||
height: calc( 100% - 40px );
|
||||
background-image: linear-gradient(#333,base.$Background);
|
||||
|
||||
@media screen and (max-width:500px) {
|
||||
background-image: linear-gradient(#303030,base.$Background);
|
||||
}
|
||||
}
|
||||
|
||||
.number {
|
||||
font-family: "Inconsolata", monospace;
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
@use "../base";
|
||||
@use "pulldown/help";
|
||||
@use "pulldown/accounts";
|
||||
@use "pulldown/files";
|
||||
@use "pulldown/modals";
|
||||
|
||||
#overlay, .modalContainer {
|
||||
position:absolute;
|
||||
left:0px;
|
||||
height: 100%;
|
||||
width:100%;
|
||||
top:0px;
|
||||
border:none;
|
||||
outline:none;
|
||||
background-color:rgba(170, 170, 170, 0.25);
|
||||
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.pulldown {
|
||||
position: absolute;
|
||||
width: 300px;
|
||||
height: 400px;
|
||||
background-color: #222222;
|
||||
color: #dddddd;
|
||||
|
||||
top:0px;
|
||||
left:50%;
|
||||
transform:translateX(-50%);
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
p, h1, h2 {
|
||||
margin:0px;
|
||||
}
|
||||
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
.pulldown_display {
|
||||
position:absolute;
|
||||
left:0px;
|
||||
top:0px;
|
||||
width:100%;
|
||||
height:100%;
|
||||
}
|
|
@ -1,225 +0,0 @@
|
|||
.pulldown_display[data-name=accounts] {
|
||||
.notLoggedIn {
|
||||
.container_div {
|
||||
position:absolute;
|
||||
top:50%;
|
||||
transform:translateY(-50%);
|
||||
width:100%;
|
||||
text-align:center;
|
||||
|
||||
h1 {
|
||||
font-weight:600;
|
||||
font-size:24px;
|
||||
|
||||
@media screen and (max-width:500px) {
|
||||
font-size:30px;
|
||||
}
|
||||
}
|
||||
|
||||
.flavor {
|
||||
font-size:14px;
|
||||
|
||||
/* good enoough */
|
||||
|
||||
@media screen and (max-width:500px) {
|
||||
font-size:16px;
|
||||
}
|
||||
|
||||
color:#999999;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
button {
|
||||
cursor:pointer;
|
||||
background-color:#393939;
|
||||
color:#DDDDDD;
|
||||
border:none;
|
||||
outline:none;
|
||||
padding:5px;
|
||||
transition-duration: 250ms;
|
||||
/*overflow:clip;*/
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
font-size:16px;
|
||||
padding:10px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transition-duration: 250ms;
|
||||
background-color:#434343;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
flex-basis:50%;
|
||||
flex-grow:1;
|
||||
}
|
||||
|
||||
button.flavor {
|
||||
|
||||
padding: 0;
|
||||
background: none;
|
||||
|
||||
}
|
||||
|
||||
input[type=text],input[type=password] {
|
||||
border:none;
|
||||
border-radius:0;
|
||||
width:100%;
|
||||
padding:5px;
|
||||
background-color:#333333;
|
||||
color:#dddddd;
|
||||
outline:none;
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
font-size:16px;
|
||||
padding:10px;
|
||||
}
|
||||
}
|
||||
|
||||
.pwError {
|
||||
div {
|
||||
border:none;
|
||||
border-radius:0;
|
||||
width:100%;
|
||||
padding:5px;
|
||||
background-color:#663333;
|
||||
color:#dddddd;
|
||||
outline:none;
|
||||
font-size:14px;
|
||||
text-align:left;
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
font-size:16px;
|
||||
padding:10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lgBtnContainer {
|
||||
display:flex;
|
||||
position:relative;
|
||||
left:20px;
|
||||
width:calc( 100% - 40px );
|
||||
gap:10px;
|
||||
overflow:clip;
|
||||
}
|
||||
|
||||
.fields {
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
position:relative;
|
||||
left:20px;
|
||||
width:calc( 100% - 40px );
|
||||
gap:5px;
|
||||
overflow:clip;
|
||||
}
|
||||
|
||||
/*
|
||||
a {
|
||||
text-decoration: none;
|
||||
color:#999999;
|
||||
font-size:14px;
|
||||
|
||||
@media screen and (max-width:500px) {
|
||||
font-size:16px;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content:" ➜";
|
||||
font-size:0px;
|
||||
opacity: 0;
|
||||
transition-duration:250ms;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&::after {
|
||||
font-size:13px;
|
||||
opacity: 1;
|
||||
transition-duration:250ms;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.loggedIn {
|
||||
position:absolute;
|
||||
|
||||
/*
|
||||
left:10px;
|
||||
top:10px;
|
||||
*/
|
||||
|
||||
left:0px;
|
||||
top:0px;
|
||||
width:calc( 100% - 20px );
|
||||
height:calc( 100% - 20px );
|
||||
padding:10px;
|
||||
|
||||
overflow-y:auto;
|
||||
|
||||
h1 {
|
||||
font-weight:600;
|
||||
font-size:20px;
|
||||
color: #AAAAAA;
|
||||
|
||||
@media screen and (max-width:500px) {
|
||||
font-size:24px;
|
||||
}
|
||||
|
||||
.monospace {
|
||||
font-size:18px;
|
||||
@media screen and (max-width:500px) {
|
||||
font-size:22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.category {
|
||||
|
||||
p {
|
||||
text-align:left;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
0% {
|
||||
top: 0.25em;
|
||||
}/*
|
||||
25% {
|
||||
top: 0.25em;
|
||||
}
|
||||
75% {
|
||||
top: -0.25em;
|
||||
}*/
|
||||
100% {
|
||||
top: -0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
.loader {
|
||||
i {
|
||||
font-style: normal;
|
||||
position: relative;
|
||||
|
||||
animation-name: bounce;
|
||||
animation-duration: 500ms;
|
||||
animation-iteration-count: infinite;
|
||||
animation-direction: alternate;
|
||||
top:0.25em;
|
||||
|
||||
&:nth-of-type(1) {
|
||||
animation-delay: 0ms;
|
||||
}
|
||||
&:nth-of-type(2) {
|
||||
animation-delay: 125ms;
|
||||
}
|
||||
&:nth-of-type(3) {
|
||||
animation-delay: 250ms;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,160 +0,0 @@
|
|||
.pulldown_display[data-name=files] {
|
||||
.notLoggedIn {
|
||||
position:absolute;
|
||||
top:50%;
|
||||
left:0px;
|
||||
transform:translateY(-50%);
|
||||
width:100%;
|
||||
text-align:center;
|
||||
|
||||
.flavor {
|
||||
font-size:16px;
|
||||
color:#999999;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
button {
|
||||
--col: #999999;
|
||||
|
||||
background-color: #232323;
|
||||
color:var(--col);
|
||||
font-size:14px;
|
||||
border:1px solid var(--col);
|
||||
padding:2px 20px 2px 20px;
|
||||
cursor:pointer;
|
||||
transition-duration:250ms;
|
||||
|
||||
&:hover {
|
||||
background-color:#333333;
|
||||
transition-duration:250ms;
|
||||
--col:#BBBBBB;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.loggedIn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height:100%;
|
||||
overflow:hidden;
|
||||
|
||||
.searchBar {
|
||||
transition-duration:150ms;
|
||||
background-color:#212121;
|
||||
width:100%;
|
||||
padding:8px;
|
||||
color:#dddddd;
|
||||
border:none;
|
||||
border-bottom: 1px solid #aaaaaa;
|
||||
outline: none;
|
||||
border-radius:0px;
|
||||
font-size:14px;
|
||||
|
||||
&:focus {
|
||||
transition-duration:150ms;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
padding:12px;
|
||||
font-size:16px;
|
||||
}
|
||||
}
|
||||
|
||||
.fileList {
|
||||
overflow-y:auto;
|
||||
overflow-x:hidden;
|
||||
padding:5px 0;
|
||||
|
||||
.flFile {
|
||||
padding: 3px 8px;
|
||||
position:relative;
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
padding:7px 12px;
|
||||
}
|
||||
|
||||
.detail {
|
||||
color:#777777;
|
||||
font-size:14px;
|
||||
position:relative;
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
font-size:16px;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
|
||||
/* this is shit but it's the best way i can think of to do this */
|
||||
/* other than flexbox but i don't feel like doing that rn */
|
||||
|
||||
position:relative;
|
||||
top:2px;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size:18px;
|
||||
text-overflow:ellipsis;
|
||||
overflow:hidden;
|
||||
font-weight:600;
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
font-size:20px;
|
||||
}
|
||||
}
|
||||
|
||||
p, h2 {
|
||||
margin:0 0 0 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color:#00000000;
|
||||
border:none;
|
||||
outline:none;
|
||||
cursor:pointer;
|
||||
|
||||
&.hitbox {
|
||||
position:absolute;
|
||||
left:0px;
|
||||
top:0px;
|
||||
height:100%;
|
||||
width:100%;
|
||||
z-index:10;
|
||||
}
|
||||
|
||||
&.more {
|
||||
min-height:100%;
|
||||
width:auto;
|
||||
aspect-ratio: 1 / 1;
|
||||
z-index:11;
|
||||
position:relative;
|
||||
|
||||
img {
|
||||
margin:auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.flexCont {
|
||||
display: flex;
|
||||
|
||||
.fileInfo {
|
||||
width:100%;
|
||||
min-width:0;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width:500px) {
|
||||
&:hover {
|
||||
background-color: #252525;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
.pulldown_display[data-name=help] {
|
||||
|
||||
overflow-y:auto;
|
||||
|
||||
.faqGroup {
|
||||
padding:6px 10px 4px 10px;
|
||||
|
||||
h2 {
|
||||
font-weight: 400;
|
||||
color:#DDDDDD;
|
||||
font-size:16px;
|
||||
margin:0 0 0 0;
|
||||
}
|
||||
|
||||
p {
|
||||
color:#999999;
|
||||
font-size:16px;
|
||||
margin:0 0 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
.optPicker {
|
||||
|
||||
button, .inp {
|
||||
position:relative;
|
||||
width:100%;
|
||||
height:50px;
|
||||
background-color: #222222;
|
||||
border:none;
|
||||
border-bottom:1px solid #AAAAAA;
|
||||
transition-duration:150ms;
|
||||
|
||||
img {
|
||||
position:absolute;
|
||||
left:13px;
|
||||
top:50%;
|
||||
transform:translateY(-50%);
|
||||
}
|
||||
|
||||
p,input {
|
||||
text-align:left;
|
||||
position:absolute;
|
||||
top:50%;
|
||||
left:50px;
|
||||
color:#DDDDDD;
|
||||
transform:translateY(-50%);
|
||||
font-size:14px;
|
||||
background-color:#00000000;
|
||||
border:none;
|
||||
|
||||
span {
|
||||
color:#777777;
|
||||
font-size:12px;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
height:100%;
|
||||
width:calc(100% - 50px);
|
||||
outline:none; /* bad idea but i don't even care anymore */
|
||||
margin:0px;
|
||||
padding:0px;
|
||||
}
|
||||
|
||||
@media screen and (max-width:500px) {
|
||||
height:70px;
|
||||
p,input {
|
||||
font-size:16px;
|
||||
left:70px;
|
||||
|
||||
span {
|
||||
font-size:14px;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
width:calc( 100% - 70px );
|
||||
}
|
||||
|
||||
img {
|
||||
width:30px;
|
||||
height:30px;
|
||||
left:20px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
button {
|
||||
cursor:pointer;
|
||||
|
||||
&:hover {
|
||||
transition-duration:150ms;
|
||||
background-color: #252525;
|
||||
}
|
||||
}
|
||||
|
||||
.category {
|
||||
border-bottom: 1px solid #AAAAAA;
|
||||
|
||||
p {
|
||||
color: #AAAAAA;
|
||||
font-size: 14px;
|
||||
margin: 10px 0px 3px 0px;
|
||||
text-align:center;
|
||||
|
||||
@media screen and (max-width:500px) {
|
||||
font-size:16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.mdHitbox {
|
||||
position:absolute;
|
||||
width:100%;
|
||||
height:100%;
|
||||
top:0%;
|
||||
left:0%;
|
||||
cursor:pointer;
|
||||
z-index: 0;
|
||||
border:none;
|
||||
background-color: #00000000;
|
||||
outline:none;
|
||||
}
|
||||
|
||||
.modal {
|
||||
position:absolute;
|
||||
background-color:#222222;
|
||||
width:100%;
|
||||
transform:translateY(-100%);
|
||||
top:100%;
|
||||
left:0%;
|
||||
z-index: 1;
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
@use "../base";
|
||||
|
||||
#topbar {
|
||||
position:absolute;
|
||||
left:0px;
|
||||
top:0px;
|
||||
|
||||
width:100%;
|
||||
height:40px;
|
||||
|
||||
/* hsl(210,9.1,12.9) */
|
||||
background-color: rgb(30, 33, 36);
|
||||
|
||||
display:flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
column-gap:5px;
|
||||
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
#uploadWindow {
|
||||
#add_new_files {
|
||||
background-color:#191919;
|
||||
border: 1px solid gray;
|
||||
padding: 0px 0px 10px 0px;
|
||||
|
||||
p {
|
||||
font-family: "Fira Code", monospace;
|
||||
text-align: left;
|
||||
margin: 0px 0px 0px 10px;
|
||||
font-size: 30px;
|
||||
|
||||
span {
|
||||
position:relative;
|
||||
|
||||
&.add_files_txt {
|
||||
font-size:16px;
|
||||
top:-4px;
|
||||
left:10px;
|
||||
|
||||
@media screen and (max-width:500px) {
|
||||
font-size:20px;
|
||||
top:-6px;
|
||||
left:10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width:500px) {
|
||||
font-size: 40px;
|
||||
|
||||
span.add_files_txt {
|
||||
font-size:20px;
|
||||
top:-6px;
|
||||
left:10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#file_add_btns {
|
||||
width:calc( 100% - 20px );
|
||||
margin:auto;
|
||||
position:relative;
|
||||
display:flex;
|
||||
flex-direction:row;
|
||||
column-gap:10px;
|
||||
|
||||
button, input[type=text], input[type=submit] {
|
||||
background-color:#333333;
|
||||
color:#DDDDDD;
|
||||
border:none;
|
||||
border-radius: 0px;
|
||||
outline:none;
|
||||
padding:5px;
|
||||
|
||||
flex-basis:50%;
|
||||
flex-grow:1;
|
||||
transition-duration:250ms;
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
font-size:16px;
|
||||
padding:10px;
|
||||
}
|
||||
}
|
||||
|
||||
button, input[type=submit] {
|
||||
cursor:pointer;
|
||||
|
||||
&:hover {
|
||||
@media screen and (min-width: 500px) {
|
||||
transition-duration:250ms;
|
||||
flex-basis: 60%;
|
||||
}
|
||||
background-color:#393939;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.fileUpload {
|
||||
width:100%;
|
||||
height:100px;
|
||||
position:relative;
|
||||
|
||||
background-color:#262626;
|
||||
transition-duration:250ms;
|
||||
|
||||
input[type=file] {
|
||||
opacity: 0;
|
||||
position:absolute;
|
||||
left:0px;
|
||||
top:0px;
|
||||
width:100%;
|
||||
height:100%;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
p {
|
||||
position:absolute;
|
||||
top:50%;
|
||||
transform:translateY(-50%);
|
||||
font-size:12px;
|
||||
width:100%;
|
||||
text-align:center;
|
||||
padding:0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transition-duration:250ms;
|
||||
background-color:#292929;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
// should probably start using mixins for thingss like this
|
||||
|
||||
#uploadWindow {
|
||||
.file {
|
||||
background-color:#191919;
|
||||
border: 1px solid gray;
|
||||
padding: 10px;
|
||||
overflow:clip;
|
||||
position:relative;
|
||||
|
||||
h2 {
|
||||
font-size: 16px;
|
||||
margin: 0px;
|
||||
font-weight:600;
|
||||
width:calc( 100% - 20px );
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
background-color:#333333;
|
||||
color:#DDDDDD;
|
||||
border:none;
|
||||
outline:none;
|
||||
padding:5px;
|
||||
position:relative;
|
||||
|
||||
width:100%;
|
||||
transition-duration:250ms;
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
font-size:16px;
|
||||
padding:10px;
|
||||
}
|
||||
}
|
||||
|
||||
.buttonContainer {
|
||||
display:flex;
|
||||
column-gap:10px;
|
||||
|
||||
button {
|
||||
flex-basis: 50%;
|
||||
flex-grow: 1;
|
||||
padding:5px;
|
||||
}
|
||||
}
|
||||
|
||||
.uploadingContainer {
|
||||
color: #AAAAAA;
|
||||
}
|
||||
|
||||
.hitbox {
|
||||
opacity:0;
|
||||
position:absolute;
|
||||
left:0px;
|
||||
top:0px;
|
||||
height:100%;
|
||||
width:100%;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
@use "uploader/add_new_files";
|
||||
@use "uploader/file";
|
||||
|
||||
#uploadWindow {
|
||||
position:absolute;
|
||||
left:50%;
|
||||
top:50%;
|
||||
transform:translate(-50%,-50%);
|
||||
padding:10px 15px 10px 15px;
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
|
||||
width:350px;
|
||||
@media screen and (min-width:500px) {
|
||||
max-height: calc( 100% - 80px );
|
||||
}
|
||||
|
||||
background-color:#222222;
|
||||
color:#ddd;
|
||||
|
||||
h1, p, a {
|
||||
margin: 0px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
h1 > button {
|
||||
background-color: #0000 !important;
|
||||
padding: 0;
|
||||
margin-top: 5px;
|
||||
|
||||
img {
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
img { color: #DDD; }
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color:#999;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight:600;
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
.uploadContainer {
|
||||
overflow:auto;
|
||||
}
|
||||
|
||||
button, input[type=submit] {
|
||||
cursor:pointer;
|
||||
background-color:#393939;
|
||||
color:#DDDDDD;
|
||||
border:none;
|
||||
outline:none;
|
||||
padding:5px;
|
||||
transition-duration: 250ms;
|
||||
/*overflow:clip;*/
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
font-size:16px;
|
||||
padding:10px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transition-duration: 250ms;
|
||||
background-color:#434343;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
width: calc( 100% - 20px );
|
||||
height: calc( 100% - 20px );
|
||||
border-radius:0px;
|
||||
background-color:#00000000;
|
||||
|
||||
transform:none;
|
||||
left:10px;
|
||||
top:10px;
|
||||
padding:0px;
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
// probably dont need to import the entire
|
||||
// uploads css file
|
||||
// so i might just make a separate file with mixins
|
||||
// and import them
|
||||
|
||||
@use "app/uploads";
|
||||
@use "base";
|
||||
|
||||
#appContent {
|
||||
position:absolute;
|
||||
left:0px;
|
||||
top:0px;
|
||||
width:100%;
|
||||
height:100%;
|
||||
background-image: linear-gradient(#333,base.$Background);
|
||||
|
||||
@media screen and (max-width:500px) {
|
||||
background-image: linear-gradient(#303030,base.$Background);
|
||||
}
|
||||
}
|
||||
|
||||
#uploadWindow {
|
||||
img, video, audio {
|
||||
width:100%;
|
||||
}
|
||||
}
|
|
@ -1,168 +0,0 @@
|
|||
#uploadWindow {
|
||||
color: #FFFFFF
|
||||
}
|
||||
|
||||
body {
|
||||
background-color:#DDDDDD;
|
||||
}
|
||||
|
||||
#appContent {
|
||||
background: darkgray;
|
||||
@media screen and (max-width:500px) {
|
||||
background:white;
|
||||
}
|
||||
}
|
||||
|
||||
#uploadWindow {
|
||||
background: white;
|
||||
|
||||
color:black;
|
||||
|
||||
h1, p, a {
|
||||
margin: 0px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
a {
|
||||
color:rgb(153, 153, 153);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight:600;
|
||||
font-size: 25px;
|
||||
text-align:center;
|
||||
|
||||
@media screen and (max-width:500px) {
|
||||
font-size: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
& > p:nth-of-type(1) {
|
||||
text-align:center;
|
||||
font-style: italic;
|
||||
font-weight:600;
|
||||
font-size: 16px;
|
||||
color:black !important;
|
||||
@media screen and (max-width:500px) {
|
||||
font-size: 21px;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
cursor:pointer;
|
||||
color:black;
|
||||
border:none;
|
||||
outline:none;
|
||||
padding:5px;
|
||||
background: #AAAAAA;
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
font-size:16px;
|
||||
padding:10px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
outline: 1px solid #333333;
|
||||
color: black;
|
||||
background-color:#AAAAAA;
|
||||
}
|
||||
}
|
||||
|
||||
& > button:nth-last-of-type(1) {
|
||||
background-color:#66AAFF;
|
||||
&:hover {
|
||||
background-color:#66AAFF;
|
||||
}
|
||||
}
|
||||
|
||||
#add_new_files {
|
||||
background-color: #AAAAAA66;
|
||||
border:1px solid #AAAAAA;
|
||||
|
||||
#file_add_btns {
|
||||
button, input[type=text] {
|
||||
transition-duration:0s;
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
font-size:16px;
|
||||
padding:10px;
|
||||
}
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
font-family: "Fira Code", monospace;
|
||||
background-color:#AAAAAA;
|
||||
color:black;
|
||||
}
|
||||
|
||||
button {
|
||||
cursor:pointer;
|
||||
background-color:#AAAAAA;
|
||||
color: black;
|
||||
|
||||
&:hover {
|
||||
flex-basis: 50%;
|
||||
transition-duration:0s;
|
||||
background-color:#AAAAAA;
|
||||
color: black;
|
||||
outline: 1px solid #333333;
|
||||
}
|
||||
}
|
||||
|
||||
.fileUpload {
|
||||
background-color:#AAAAAA;
|
||||
transition-duration:250ms;
|
||||
|
||||
&:hover {
|
||||
transition-duration:0s;
|
||||
background-color:#AAAAAA;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.file {
|
||||
background-color: #AAAAAA66;
|
||||
border: 1px solid #AAAAAA;
|
||||
|
||||
input[type=text] {
|
||||
font-family: "Fira Code", monospace;
|
||||
background-color:#AAAAAA;
|
||||
color:black;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
* {
|
||||
/* nice scrollbars aren't needed on mobile so */
|
||||
@media screen and (min-width:500px) {
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width:5px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color:#AAAAAA;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color:#DDDDDD;
|
||||
|
||||
&:hover {
|
||||
background-color:#FFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#topbar {
|
||||
background-color: #DDDDDD;
|
||||
}
|
||||
|
||||
.error {
|
||||
.code {
|
||||
color: black;
|
||||
}
|
||||
}
|
|
@ -1,22 +1,4 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import Topbar from "./elem/Topbar.svelte";
|
||||
import PulldownManager from "./elem/PulldownManager.svelte";
|
||||
import UploadWindow from "./elem/UploadWindow.svelte";
|
||||
import { pulldownManager } from "./elem/stores.js";
|
||||
|
||||
let topbar: Topbar;
|
||||
|
||||
let pulldown: PulldownManager;
|
||||
|
||||
onMount(() => {
|
||||
pulldownManager.set(pulldown)
|
||||
})
|
||||
</script>
|
||||
|
||||
<Topbar bind:this={topbar} pulldown={pulldown} />
|
||||
<div id="appContent">
|
||||
<PulldownManager bind:this={pulldown} />
|
||||
|
||||
<UploadWindow/>
|
||||
</div>
|
||||
<div id="appContent"></div>
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
<script context="module" lang="ts">
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
// can't find a better way to do this
|
||||
import Files from "./pulldowns/Files.svelte";
|
||||
import Accounts from "./pulldowns/Accounts.svelte";
|
||||
import Help from "./pulldowns/Help.svelte";
|
||||
|
||||
export let allPulldowns = new Map()
|
||||
|
||||
allPulldowns
|
||||
.set("account",Accounts)
|
||||
.set("help",Help)
|
||||
.set("files",Files)
|
||||
|
||||
export const pulldownOpen = writable<string|false>(false);
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import { fade, scale } from "svelte/transition";
|
||||
|
||||
export function isOpen() {
|
||||
return $pulldownOpen
|
||||
}
|
||||
|
||||
export function openPulldown(name: string) {
|
||||
pulldownOpen.set(name)
|
||||
}
|
||||
|
||||
export function closePulldown() {
|
||||
pulldownOpen.set(false)
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
|
||||
})
|
||||
</script>
|
||||
{#if $pulldownOpen}
|
||||
<div class="pulldown" transition:fade={{duration:200}}>
|
||||
<svelte:component this={allPulldowns.get($pulldownOpen)} />
|
||||
</div>
|
||||
|
||||
<button
|
||||
id="overlay"
|
||||
on:click={closePulldown}
|
||||
transition:fade={{duration:200}}
|
||||
/>
|
||||
{/if}
|
|
@ -1,28 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { circOut } from "svelte/easing";
|
||||
import { scale } from "svelte/transition";
|
||||
import PulldownManager, {pulldownOpen} from "./PulldownManager.svelte";
|
||||
import { account } from "./stores.js";
|
||||
import { _void } from "./transition/_void.js";
|
||||
|
||||
export let pulldown: PulldownManager;
|
||||
</script>
|
||||
|
||||
<div id="topbar">
|
||||
{#if $pulldownOpen}
|
||||
<button
|
||||
class="menuBtn"
|
||||
on:click={pulldown.closePulldown}
|
||||
transition:_void={{duration:200,prop:"width",easingFunc:circOut}}
|
||||
>close</button>
|
||||
{/if}
|
||||
|
||||
<!-- too lazy to make this better -->
|
||||
|
||||
<button class="menuBtn" on:click={() => pulldown.openPulldown("files")}>files</button>
|
||||
<button class="menuBtn" on:click={() => pulldown.openPulldown("account")}>{$account?.username ? `@${$account.username}` : "account"}</button>
|
||||
<button class="menuBtn" on:click={() => pulldown.openPulldown("help")}>help</button>
|
||||
|
||||
<div /> <!-- not sure what's offcenter but something is
|
||||
so this div is here to ""fix"" that -->
|
||||
</div>
|
|
@ -1,378 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { _void } from "./transition/_void.js"
|
||||
import { padding_scaleY } from "./transition/padding_scaleY.js"
|
||||
import { fade } from "svelte/transition"
|
||||
import { circIn, circOut } from "svelte/easing"
|
||||
import { serverStats, refresh_stats, account } from "./stores.js"
|
||||
import bytes from "bytes"
|
||||
|
||||
import AttachmentZone from "./uploader/AttachmentZone.svelte"
|
||||
|
||||
// stats
|
||||
|
||||
refresh_stats()
|
||||
|
||||
// uploads
|
||||
|
||||
interface Upload {
|
||||
file: string | File
|
||||
|
||||
params: {
|
||||
uploadId?: string
|
||||
}
|
||||
|
||||
uploadStatus: {
|
||||
fileId?: string,
|
||||
error?: string,
|
||||
}
|
||||
|
||||
maximized?: boolean,
|
||||
viewingUrl?: boolean
|
||||
}
|
||||
|
||||
let attachmentZone
|
||||
let uploads: Record<string, Upload> = {}
|
||||
let uploadInProgress = false
|
||||
let notificationPermission =
|
||||
globalThis?.Notification?.permission ?? "denied"
|
||||
let handle_file_upload = (file: Event & { detail: File|string }) => {
|
||||
|
||||
uploads[Math.random().toString().slice(2)] = {
|
||||
file: file.detail,
|
||||
|
||||
params: {
|
||||
uploadId: "",
|
||||
},
|
||||
|
||||
uploadStatus: {}
|
||||
}
|
||||
|
||||
uploads = uploads
|
||||
|
||||
}
|
||||
|
||||
let handle_fetch_promise = (x: string, prom: Promise<Response>) => {
|
||||
return prom
|
||||
.then(async (res) => {
|
||||
let txt = await res.text()
|
||||
if (!res.ok) uploads[x].uploadStatus.error = txt
|
||||
else {
|
||||
uploads[x].uploadStatus.fileId = txt
|
||||
try {
|
||||
new Notification("Upload complete", {
|
||||
body: `View at ${location.origin}/${uploads[x].uploadStatus.fileId}`,
|
||||
actions: [
|
||||
{
|
||||
action: "open",
|
||||
title: "Open",
|
||||
},
|
||||
{
|
||||
action: "copy",
|
||||
title: "Copy",
|
||||
},
|
||||
],
|
||||
}).addEventListener(
|
||||
"notificationclick",
|
||||
(event) => {
|
||||
if ("action" in event && event.action === "open") {
|
||||
open(
|
||||
"/download/" +
|
||||
uploads[x].uploadStatus.fileId
|
||||
)
|
||||
} else {
|
||||
navigator.clipboard.writeText(
|
||||
`${location.origin}/${uploads[x].uploadStatus.fileId}`
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
} catch (_) {}
|
||||
refresh_stats()
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
uploads[x].uploadStatus.error = err.toString()
|
||||
})
|
||||
}
|
||||
|
||||
let upload_files = async () => {
|
||||
uploadInProgress = true
|
||||
|
||||
let sequential = localStorage.getItem("sequentialMode") == "true"
|
||||
|
||||
// go through all files
|
||||
for (let [x, v] of Object.entries(uploads)) {
|
||||
// quick patch-in to allow for a switch to have everything upload sequentially
|
||||
// switch will have a proper menu option later, for now i'm lazy so it's just gonna be a Secret
|
||||
let hdl = () => {
|
||||
let fd = new FormData()
|
||||
if (v.params.uploadId) fd.append("uploadId", v.params.uploadId)
|
||||
fd.append("file", v.file)
|
||||
|
||||
return handle_fetch_promise(x,fetch("/api/v1/file",{
|
||||
method: "PUT",
|
||||
body: fd
|
||||
}))
|
||||
}
|
||||
|
||||
if (sequential) await hdl()
|
||||
else hdl()
|
||||
}
|
||||
}
|
||||
|
||||
// animation
|
||||
|
||||
function fileTransition(node: HTMLElement) {
|
||||
return {
|
||||
duration: 300,
|
||||
css: (t: number) => {
|
||||
let eased = circOut(t)
|
||||
|
||||
return `
|
||||
height: ${eased * (node.offsetHeight - 22)}px;
|
||||
padding: ${eased * 10}px 10px;
|
||||
`
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="uploadWindow">
|
||||
<h1>
|
||||
monofile
|
||||
{#if notificationPermission === "default"}
|
||||
<button
|
||||
on:click={() => {
|
||||
Notification.requestPermission().then(
|
||||
(permission) => (notificationPermission = permission)
|
||||
)
|
||||
}}
|
||||
style="float:right"
|
||||
title="Notify me when the upload finishes"
|
||||
>
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-label="Notify me when the upload finishes"
|
||||
><path
|
||||
d="M9.042 19.003h5.916a3 3 0 0 1-5.916 0Zm2.958-17a7.5 7.5 0 0 1 7.5 7.5v4l1.418 3.16A.95.95 0 0 1 20.052 18h-16.1a.95.95 0 0 1-.867-1.338l1.415-3.16V9.49l.005-.25A7.5 7.5 0 0 1 12 2.004Z"
|
||||
fill="currentColor"
|
||||
/></svg
|
||||
>
|
||||
</button>
|
||||
{/if}
|
||||
</h1>
|
||||
<p style:color="#999999">
|
||||
<span class="number"
|
||||
>{$serverStats?.version ? `v${$serverStats?.version}` : "•••"}</span
|
||||
> — Discord based file sharing
|
||||
</p>
|
||||
|
||||
<div style:min-height="10px" />
|
||||
|
||||
<!-- consider splitting the file thing into a separate element maybe -->
|
||||
|
||||
<div class="uploadContainer">
|
||||
{#each Object.entries(uploads) as upload (upload[0])}
|
||||
<!-- container to allow for animate directive -->
|
||||
<div>
|
||||
<div
|
||||
class="file"
|
||||
transition:fileTransition
|
||||
style:border={upload[1].uploadStatus.error
|
||||
? "1px solid #BB7070"
|
||||
: ""}
|
||||
>
|
||||
<h2>
|
||||
{typeof upload[1].file == "string" ? upload[1].file : upload[1].file.name}
|
||||
<span style:color="#999999" style:font-weight="400"
|
||||
>{@html typeof upload[1].file == "string" ? "clone" : `upload (${bytes(upload[1].file.size)})`}</span>
|
||||
</h2>
|
||||
|
||||
{#if upload[1].maximized && !uploadInProgress}
|
||||
<div transition:padding_scaleY|local>
|
||||
<div style:height="10px" />
|
||||
<input
|
||||
placeholder="custom id"
|
||||
type="text"
|
||||
bind:value={uploads[upload[0]].params.uploadId}
|
||||
/>
|
||||
<div style:height="10px" />
|
||||
<div class="buttonContainer">
|
||||
<button
|
||||
on:click={() => {
|
||||
delete uploads[upload[0]]
|
||||
uploads = uploads
|
||||
}}
|
||||
>
|
||||
delete
|
||||
</button>
|
||||
<button
|
||||
on:click={() =>
|
||||
(uploads[upload[0]].maximized = false)}
|
||||
>
|
||||
minimize
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{:else if !uploadInProgress}
|
||||
<button
|
||||
on:click={() =>
|
||||
(uploads[upload[0]].maximized = true)}
|
||||
class="hitbox"
|
||||
/>
|
||||
{:else}
|
||||
<div
|
||||
transition:padding_scaleY|local
|
||||
class="uploadingContainer"
|
||||
>
|
||||
{#if !upload[1].uploadStatus.fileId}
|
||||
<p
|
||||
in:fade={{
|
||||
duration: 300,
|
||||
delay: 400,
|
||||
easingFunc: circOut,
|
||||
}}
|
||||
out:padding_scaleY={{
|
||||
easingFunc: circIn,
|
||||
op: true,
|
||||
}}
|
||||
>
|
||||
{upload[1].uploadStatus.error ??
|
||||
"Uploading..."}
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
{#if upload[1].uploadStatus.fileId}
|
||||
<div
|
||||
style:height="10px"
|
||||
transition:padding_scaleY
|
||||
/>
|
||||
{#if !upload[1].viewingUrl}
|
||||
<div
|
||||
class="buttonContainer"
|
||||
out:_void
|
||||
in:_void={{ easingFunc: circOut }}
|
||||
>
|
||||
<button
|
||||
on:click={() =>
|
||||
(uploads[
|
||||
upload[0]
|
||||
].viewingUrl = true)}
|
||||
>
|
||||
view url
|
||||
</button>
|
||||
<button
|
||||
on:click={() =>
|
||||
navigator.clipboard.writeText(
|
||||
`https://${window.location.host}/download/${upload[1].uploadStatus.fileId}`
|
||||
)}
|
||||
>
|
||||
copy url
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div
|
||||
class="buttonContainer"
|
||||
out:_void
|
||||
in:_void={{ easingFunc: circOut }}
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
value={`https://${window.location.host}/download/${upload[1].uploadStatus.fileId}`}
|
||||
style:flex-basis="80%"
|
||||
/>
|
||||
<button
|
||||
on:click={() =>
|
||||
(uploads[
|
||||
upload[0]
|
||||
].viewingUrl = false)}
|
||||
style:flex-basis="20%"
|
||||
>
|
||||
ok
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div style:height="10px" transition:padding_scaleY />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
{#if uploadInProgress == false}
|
||||
<!-- if required for upload, check if logged in -->
|
||||
{#if $serverStats?.accounts?.requiredForUpload ? !!$account?.username : true}
|
||||
<AttachmentZone
|
||||
bind:this={attachmentZone}
|
||||
on:addFiles={handle_file_upload}
|
||||
/>
|
||||
<div
|
||||
style:min-height="10px"
|
||||
transition:_void={{ rTarg: "height", prop: "min-height" }}
|
||||
/>
|
||||
{#if Object.keys(uploads).length > 0}
|
||||
<button
|
||||
in:padding_scaleY={{ easingFunc: circOut }}
|
||||
out:_void
|
||||
on:click={upload_files}>upload</button
|
||||
>
|
||||
<div
|
||||
transition:_void={{ rTarg: "height", prop: "min-height" }}
|
||||
style:min-height="10px"
|
||||
/>
|
||||
{/if}
|
||||
{:else}
|
||||
<p transition:_void style:color="#999999" style:text-align="center">
|
||||
Please log in to upload files.
|
||||
</p>
|
||||
<div
|
||||
transition:_void={{ rTarg: "height", prop: "min-height" }}
|
||||
style:min-height="10px"
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<p style:color="#999999" style:text-align="center">
|
||||
Hosting <span class="number" style:font-weight="600"
|
||||
>{$serverStats?.files ?? "•••"}</span
|
||||
>
|
||||
files — Maximum filesize is
|
||||
<span class="number" style:font-weight="600">
|
||||
{
|
||||
$serverStats?.maxDiscordFiles
|
||||
? bytes($serverStats.maxDiscordFileSize * $serverStats.maxDiscordFiles)
|
||||
: "•••"
|
||||
}</span>
|
||||
<br />
|
||||
</p>
|
||||
<p style:color="#999999" style:text-align="center" style:font-size="12px">
|
||||
Made with {Math.floor(Math.random() * 10) == 0 ? "🐟" : "❤"} by
|
||||
<a href="https://cetera.uk" style:font-size="12px"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 173.8 275.72"
|
||||
height="16"
|
||||
style="vertical-align:middle"
|
||||
fill="currentColor"
|
||||
>
|
||||
<circle cx="37.13" cy="26.43" r="21.6" />
|
||||
<circle cx="34.62" cy="117.87" r="34.62" class="middle" />
|
||||
<circle cx="119.78" cy="130.68" r="21.6" class="middle" />
|
||||
<circle cx="127.16" cy="46.64" r="46.65" />
|
||||
<circle cx="102.68" cy="219.58" r="56.14" class="bottom" />
|
||||
</svg> Etcetera</a
|
||||
>
|
||||
—
|
||||
<a href="https://github.com/mollersuite/monofile" style:font-size="12px"
|
||||
>source</a
|
||||
>
|
||||
</p>
|
||||
<div style:height="10px" />
|
||||
</div>
|
|
@ -1,80 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { fade, slide } from "svelte/transition";
|
||||
|
||||
interface BaseModalOption {
|
||||
name:string,
|
||||
icon:string,
|
||||
id: string | number | symbol | boolean
|
||||
}
|
||||
|
||||
type ModalOption = BaseModalOption & {inputSettings: {password?: boolean}, id: any} | BaseModalOption & { description: string }
|
||||
|
||||
type ModalOptions = ModalOption[]
|
||||
type OptionPickerReturns = {selected: any} & Record<any,any> | null
|
||||
let activeModal: {resolve: (val: OptionPickerReturns) => void, title: string, modal: ModalOptions } | undefined;
|
||||
let modalResults: Record<string | number | symbol, string> = {};
|
||||
|
||||
export function picker(title: string,mdl: ModalOptions): Promise<OptionPickerReturns> {
|
||||
if (activeModal) forceCancel()
|
||||
|
||||
return new Promise<OptionPickerReturns>((resolve,reject) => {
|
||||
activeModal = {
|
||||
resolve,
|
||||
title,
|
||||
modal:mdl
|
||||
}
|
||||
|
||||
modalResults = {}
|
||||
})
|
||||
}
|
||||
|
||||
export function forceCancel() {
|
||||
if (activeModal && activeModal.resolve) {
|
||||
activeModal.resolve(null)
|
||||
}
|
||||
activeModal = undefined
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if activeModal}
|
||||
<div class="modalContainer" transition:fade={{duration:200}}>
|
||||
<button class="mdHitbox" on:click|self={forceCancel}></button>
|
||||
<div class="modal" transition:slide={{duration:200}}>
|
||||
|
||||
<div class="optPicker">
|
||||
|
||||
<div class="category">
|
||||
<p style:margin-bottom="10px">{activeModal.title}</p>
|
||||
</div>
|
||||
|
||||
{#each activeModal.modal as option (option.id)}
|
||||
{#if "inputSettings" in option}
|
||||
<div class="inp">
|
||||
<img src={option.icon} alt={option.id.toString()}>
|
||||
|
||||
<!-- i have to do this stupidness because of svelte but -->
|
||||
<!-- its reason for blocking this is pretty good sooooo -->
|
||||
|
||||
{#if option.inputSettings.password}
|
||||
<input placeholder={option.name} type="password" bind:value={modalResults[option.id]}>
|
||||
{:else}
|
||||
<input placeholder={option.name} bind:value={modalResults[option.id]}>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<button on:click={() => {activeModal?.resolve({...modalResults,selected:option.id});activeModal=undefined;modalResults={};}}>
|
||||
<img src={option.icon} alt={option.id.toString()}>
|
||||
<p>{option.name}<span><br />{option.description}</span></p>
|
||||
</button>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
<button on:click={forceCancel}>
|
||||
<img src="/static/assets/icons/delete.svg" alt="cancel">
|
||||
<p>Cancel</p>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
|
@ -1,331 +0,0 @@
|
|||
import { fetchAccountData, account, refreshNeeded } from "../stores"
|
||||
import { get } from "svelte/store";
|
||||
import type OptionPicker from "./OptionPicker.svelte";
|
||||
|
||||
export function deleteAccount(optPicker: OptionPicker) {
|
||||
optPicker.picker("What should we do with your files?",[
|
||||
{
|
||||
name: "Delete my files",
|
||||
icon: "/static/assets/icons/admin/delete_file.svg",
|
||||
description: "Your files will be permanently deleted",
|
||||
id: true
|
||||
},
|
||||
{
|
||||
name: "Do nothing",
|
||||
icon: "/static/assets/icons/file.svg",
|
||||
description: "Your files will not be affected",
|
||||
id: false
|
||||
}
|
||||
]).then((exp) => {
|
||||
if (exp) {
|
||||
let deleteFiles = exp.selected
|
||||
|
||||
optPicker.picker(`Enter your username to continue.`,[
|
||||
{
|
||||
name: "Enter your username",
|
||||
icon: "/static/assets/icons/person.svg",
|
||||
inputSettings: {},
|
||||
id:"username"
|
||||
},
|
||||
{
|
||||
name: `Delete account ${deleteFiles ? "& files" : ""}`,
|
||||
icon: "/static/assets/icons/delete_account.svg",
|
||||
description: `This cannot be undone.`,
|
||||
id: true
|
||||
}
|
||||
]).then((fin) => {
|
||||
if (fin && fin.selected) {
|
||||
if (fin.username != (get(account)||{}).username) {
|
||||
optPicker.picker("Incorrect username. Please try again.",[])
|
||||
return
|
||||
}
|
||||
|
||||
fetch(`/auth/delete_account`,{method:"POST", body:JSON.stringify({
|
||||
deleteFiles
|
||||
})}).then((response) => {
|
||||
|
||||
if (response.status != 200) {
|
||||
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
|
||||
}
|
||||
|
||||
fetchAccountData()
|
||||
})
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function userChange(optPicker: OptionPicker) {
|
||||
optPicker.picker("Change username",[
|
||||
{
|
||||
name: "New username",
|
||||
icon: "/static/assets/icons/person.svg",
|
||||
id: "username",
|
||||
inputSettings: {}
|
||||
},
|
||||
{
|
||||
name: "Update username",
|
||||
icon: "/static/assets/icons/update.svg",
|
||||
description: "",
|
||||
id: true
|
||||
}
|
||||
]).then((exp) => {
|
||||
if (exp && exp.selected) {
|
||||
fetch(`/auth/change_username`,{method:"POST", body:JSON.stringify({
|
||||
username:exp.username
|
||||
})}).then((response) => {
|
||||
|
||||
if (response.status != 200) {
|
||||
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
|
||||
}
|
||||
|
||||
fetchAccountData()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function forgotPassword(optPicker: OptionPicker) {
|
||||
optPicker.picker("Forgot your password?",[
|
||||
{
|
||||
name: "Username",
|
||||
icon: "/static/assets/icons/person.svg",
|
||||
id: "user",
|
||||
inputSettings: {}
|
||||
},
|
||||
{
|
||||
name: "OK",
|
||||
icon: "/static/assets/icons/update.svg",
|
||||
description: "",
|
||||
id: true
|
||||
}
|
||||
]).then((exp) => {
|
||||
if (exp && exp.selected) {
|
||||
fetch(`/auth/request_emergency_login`,{method:"POST", body:JSON.stringify({
|
||||
account:exp.user
|
||||
})}).then((response) => {
|
||||
if (response.status != 200) {
|
||||
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
|
||||
} else {
|
||||
optPicker.picker(`Please follow the instructions sent to your inbox.`,[])
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function emailPotentialRemove(optPicker: OptionPicker) {
|
||||
optPicker.picker("What would you like to do?",[
|
||||
{
|
||||
name: "Set a new email",
|
||||
icon: "/static/assets/icons/change_email.svg",
|
||||
description: "",
|
||||
id: "set"
|
||||
},
|
||||
{
|
||||
name: "Disconnect email",
|
||||
icon: "/static/assets/icons/disconnect_email.svg",
|
||||
description: "",
|
||||
id: "disconnect"
|
||||
}
|
||||
]).then((exp) => {
|
||||
if (exp && exp.selected) {
|
||||
switch (exp.selected) {
|
||||
case "set":
|
||||
emailChange(optPicker);
|
||||
break
|
||||
case "disconnect":
|
||||
fetch("/auth/remove_email", {method: "POST"}).then((response) => {
|
||||
if (response.status != 200) {
|
||||
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
|
||||
}
|
||||
|
||||
fetchAccountData()
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function emailChange(optPicker: OptionPicker) {
|
||||
optPicker.picker("Change email",[
|
||||
{
|
||||
name: "New email",
|
||||
icon: "/static/assets/icons/mail.svg",
|
||||
id: "email",
|
||||
inputSettings: {}
|
||||
},
|
||||
{
|
||||
name: "Request email change",
|
||||
icon: "/static/assets/icons/update.svg",
|
||||
description: "",
|
||||
id: true
|
||||
}
|
||||
]).then((exp) => {
|
||||
if (exp && exp.selected) {
|
||||
fetch(`/auth/request_email_change`,{method:"POST", body:JSON.stringify({
|
||||
email:exp.email
|
||||
})}).then((response) => {
|
||||
if (response.status != 200) {
|
||||
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
|
||||
} else {
|
||||
optPicker.picker(`Please continue to your inbox at ${exp.email.split("@")[1]} and click on the attached link.`,[])
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function pwdChng(optPicker: OptionPicker) {
|
||||
optPicker.picker("Change password",[
|
||||
{
|
||||
name: "New password",
|
||||
icon: "/static/assets/icons/change_password.svg",
|
||||
id: "password",
|
||||
inputSettings: {
|
||||
password: true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Update password",
|
||||
icon: "/static/assets/icons/update.svg",
|
||||
description: "This will log you out of all sessions",
|
||||
id: true
|
||||
}
|
||||
]).then((exp) => {
|
||||
if (exp && exp.selected) {
|
||||
fetch(`/auth/change_password`,{method:"POST", body:JSON.stringify({
|
||||
password:exp.password
|
||||
})}).then((response) => {
|
||||
|
||||
if (response.status != 200) {
|
||||
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
|
||||
}
|
||||
|
||||
fetchAccountData()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function customcss(optPicker: OptionPicker) {
|
||||
optPicker.picker("Set custom CSS",[
|
||||
{
|
||||
name: "Enter a file ID",
|
||||
icon: "/static/assets/icons/file.svg",
|
||||
id: "fileid",
|
||||
inputSettings: {}
|
||||
},
|
||||
{
|
||||
name: "OK",
|
||||
icon: "/static/assets/icons/update.svg",
|
||||
description: "Refresh to apply changes",
|
||||
id: true
|
||||
}
|
||||
]).then((exp) => {
|
||||
if (exp && exp.selected) {
|
||||
fetch(`/api/v1/account/customization/css`, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify({
|
||||
fileId: exp.fileid,
|
||||
}),
|
||||
}).then((response) => {
|
||||
if (response.status != 200) {
|
||||
optPicker.picker(
|
||||
`${response.status} ${
|
||||
response.headers.get("x-backup-status-message") ||
|
||||
response.statusText ||
|
||||
""
|
||||
}`,
|
||||
[]
|
||||
)
|
||||
}
|
||||
|
||||
fetchAccountData()
|
||||
refreshNeeded.set(true)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function embedColor(optPicker: OptionPicker) {
|
||||
optPicker.picker("Set embed color",[
|
||||
{
|
||||
name: "FFFFFF",
|
||||
icon: "/static/assets/icons/pound.svg",
|
||||
id: "color",
|
||||
inputSettings: {}
|
||||
},
|
||||
{
|
||||
name: "OK",
|
||||
icon: "/static/assets/icons/update.svg",
|
||||
description: "",
|
||||
id: true
|
||||
}
|
||||
]).then((exp) => {
|
||||
if (exp && exp.selected) {
|
||||
fetch(`/api/v1/account/customization/embed/color`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
color: exp.color,
|
||||
}),
|
||||
}).then((response) => {
|
||||
if (response.status != 200) {
|
||||
optPicker.picker(
|
||||
`${response.status} ${
|
||||
response.headers.get("x-backup-status-message") ||
|
||||
response.statusText ||
|
||||
""
|
||||
}`,
|
||||
[]
|
||||
)
|
||||
}
|
||||
|
||||
fetchAccountData()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function embedSize(optPicker: OptionPicker) {
|
||||
optPicker.picker("Set embed image size",[
|
||||
{
|
||||
name: "Large",
|
||||
icon: "/static/assets/icons/image.svg",
|
||||
description: "",
|
||||
id: true
|
||||
},
|
||||
{
|
||||
name: "Small",
|
||||
icon: "/static/assets/icons/small_image.svg",
|
||||
description: "",
|
||||
id: false
|
||||
}
|
||||
]).then((exp) => {
|
||||
if (exp && exp.selected !== null) {
|
||||
fetch(`/api/v1/account/customization/embed/size`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
largeImage: exp.selected,
|
||||
}),
|
||||
}).then((response) => {
|
||||
if (response.status != 200) {
|
||||
optPicker.picker(
|
||||
`${response.status} ${
|
||||
response.headers.get("x-backup-status-message") ||
|
||||
response.statusText ||
|
||||
""
|
||||
}`,
|
||||
[]
|
||||
)
|
||||
}
|
||||
|
||||
fetchAccountData()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
|
@ -1,231 +0,0 @@
|
|||
import { fetchAccountData, fetchFilePointers, account } from "../stores"
|
||||
import { get } from "svelte/store";
|
||||
import type OptionPicker from "./OptionPicker.svelte";
|
||||
|
||||
export function pwdReset(optPicker: OptionPicker) {
|
||||
optPicker.picker("Reset password",[
|
||||
{
|
||||
name: "Target user",
|
||||
icon: "/static/assets/icons/person.svg",
|
||||
id: "target",
|
||||
inputSettings: {}
|
||||
},
|
||||
{
|
||||
name: "New password",
|
||||
icon: "/static/assets/icons/change_password.svg",
|
||||
id: "password",
|
||||
inputSettings: {
|
||||
password: true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Update password",
|
||||
icon: "/static/assets/icons/update.svg",
|
||||
description: "This will log the target user out of all sessions",
|
||||
id: true
|
||||
}
|
||||
]).then((exp) => {
|
||||
if (exp && exp.selected) {
|
||||
fetch(`/admin/reset`,{method:"POST", body:JSON.stringify({
|
||||
target: exp.target,
|
||||
password:exp.password
|
||||
})}).then((response) => {
|
||||
|
||||
if (response.status != 200) {
|
||||
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function chgOwner(optPicker: OptionPicker) {
|
||||
optPicker.picker("Transfer file ownership",[
|
||||
{
|
||||
name: "File ID",
|
||||
icon: "/static/assets/icons/file.svg",
|
||||
id: "file",
|
||||
inputSettings: {}
|
||||
},
|
||||
{
|
||||
name: "New owner",
|
||||
icon: "/static/assets/icons/person.svg",
|
||||
id: "owner",
|
||||
inputSettings: {}
|
||||
},
|
||||
{
|
||||
name: "Transfer file ownership",
|
||||
icon: "/static/assets/icons/update.svg",
|
||||
description: "This will transfer the file to this user",
|
||||
id: true
|
||||
}
|
||||
]).then((exp) => {
|
||||
if (exp && exp.selected) {
|
||||
fetch(`/admin/transfer`,{method:"POST", body:JSON.stringify({
|
||||
owner: exp.owner,
|
||||
target: exp.file
|
||||
})}).then((response) => {
|
||||
|
||||
if (response.status != 200) {
|
||||
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function chgId(optPicker: OptionPicker) {
|
||||
optPicker.picker("Change file ID",[
|
||||
{
|
||||
name: "Target file",
|
||||
icon: "/static/assets/icons/file.svg",
|
||||
id: "file",
|
||||
inputSettings: {}
|
||||
},
|
||||
{
|
||||
name: "New ID",
|
||||
icon: "/static/assets/icons/admin/change_file_id.svg",
|
||||
id: "new",
|
||||
inputSettings: {}
|
||||
},
|
||||
{
|
||||
name: "Update",
|
||||
icon: "/static/assets/icons/update.svg",
|
||||
description: "File will not be available at its old ID",
|
||||
id: true
|
||||
}
|
||||
]).then((exp) => {
|
||||
if (exp && exp.selected) {
|
||||
fetch(`/admin/idchange`,{method:"POST", body:JSON.stringify({
|
||||
target: exp.file,
|
||||
new: exp.new
|
||||
})}).then((response) => {
|
||||
|
||||
if (response.status != 200) {
|
||||
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function delFile(optPicker: OptionPicker) {
|
||||
optPicker.picker("Delete file",[
|
||||
{
|
||||
name: "File ID",
|
||||
icon: "/static/assets/icons/file.svg",
|
||||
id: "file",
|
||||
inputSettings: {}
|
||||
},
|
||||
{
|
||||
name: "Delete",
|
||||
icon: "/static/assets/icons/admin/delete_file.svg",
|
||||
description: "This can't be undone",
|
||||
id: true
|
||||
}
|
||||
]).then((exp) => {
|
||||
if (exp && exp.selected) {
|
||||
fetch(`/admin/delete`,{method:"POST", body:JSON.stringify({
|
||||
target: exp.file
|
||||
})}).then((response) => {
|
||||
|
||||
if (response.status != 200) {
|
||||
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function elevateUser(optPicker: OptionPicker) {
|
||||
optPicker.picker("Elevate user",[
|
||||
{
|
||||
name: "Username",
|
||||
icon: "/static/assets/icons/person.svg",
|
||||
id: "user",
|
||||
inputSettings: {}
|
||||
},
|
||||
{
|
||||
name: "Elevate to admin",
|
||||
icon: "/static/assets/icons/update.svg",
|
||||
description: "",
|
||||
id: true
|
||||
}
|
||||
]).then((exp) => {
|
||||
if (exp && exp.selected) {
|
||||
fetch(`/admin/elevate`,{method:"POST", body:JSON.stringify({
|
||||
target: exp.user
|
||||
})}).then((response) => {
|
||||
|
||||
if (response.status != 200) {
|
||||
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// im really lazy so i just stole this from account.js
|
||||
|
||||
export function deleteAccount(optPicker: OptionPicker) {
|
||||
optPicker.picker("What should we do with the target account's files?",[
|
||||
{
|
||||
name: "Delete files",
|
||||
icon: "/static/assets/icons/admin/delete_file.svg",
|
||||
description: "Files will be permanently deleted",
|
||||
id: true
|
||||
},
|
||||
{
|
||||
name: "Do nothing",
|
||||
icon: "/static/assets/icons/file.svg",
|
||||
description: "Files will not be affected",
|
||||
id: false
|
||||
}
|
||||
]).then((exp) => {
|
||||
if (exp) {
|
||||
let deleteFiles = exp.selected
|
||||
|
||||
optPicker.picker(`Enter the target account's username to continue.`,[
|
||||
{
|
||||
name: "Enter account username",
|
||||
icon: "/static/assets/icons/person.svg",
|
||||
inputSettings: {},
|
||||
id:"username"
|
||||
},
|
||||
{
|
||||
name: "Optional reason",
|
||||
icon: "/static/assets/icons/more.svg",
|
||||
inputSettings: {},
|
||||
id:"reason"
|
||||
},
|
||||
{
|
||||
name: `Delete account ${deleteFiles ? "& its files" : ""}`,
|
||||
icon: "/static/assets/icons/delete_account.svg",
|
||||
description: `This cannot be undone.`,
|
||||
id: true
|
||||
}
|
||||
]).then((fin) => {
|
||||
if (fin && fin.selected) {
|
||||
fetch(`/admin/delete_account`,{method:"POST", body:JSON.stringify({
|
||||
target: fin.username,
|
||||
reason: fin.reason,
|
||||
deleteFiles
|
||||
})}).then((response) => {
|
||||
|
||||
if (response.status != 200) {
|
||||
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
|
||||
}
|
||||
|
||||
fetchAccountData()
|
||||
})
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
|
@ -1,220 +0,0 @@
|
|||
import { fetchAccountData, fetchFilePointers, account } from "../stores"
|
||||
import { get } from "svelte/store";
|
||||
import type OptionPicker from "./OptionPicker.svelte"
|
||||
import type { FilePointer } from "../../../server/lib/files";
|
||||
|
||||
export let options = {
|
||||
FV: [
|
||||
{
|
||||
name: "Public",
|
||||
icon: "/static/assets/icons/public.svg",
|
||||
description: "Everyone can view your uploads",
|
||||
id: "public"
|
||||
},
|
||||
{
|
||||
name: "Anonymous",
|
||||
icon: "/static/assets/icons/anonymous.svg",
|
||||
description: "Your username will be hidden",
|
||||
id: "anonymous"
|
||||
},
|
||||
{
|
||||
name: "Private",
|
||||
icon: "/static/assets/icons/private.svg",
|
||||
description: "Nobody but you can view your uploads",
|
||||
id: "private"
|
||||
}
|
||||
],
|
||||
FV2: [
|
||||
{
|
||||
name: "Public",
|
||||
icon: "/static/assets/icons/public.svg",
|
||||
description: "Everyone can view this file",
|
||||
id: "public"
|
||||
},
|
||||
{
|
||||
name: "Anonymous",
|
||||
icon: "/static/assets/icons/anonymous.svg",
|
||||
description: "Your username will be hidden",
|
||||
id: "anonymous"
|
||||
},
|
||||
{
|
||||
name: "Private",
|
||||
icon: "/static/assets/icons/private.svg",
|
||||
description: "Nobody but you can view this file",
|
||||
id: "private"
|
||||
}
|
||||
],
|
||||
AYS: [
|
||||
{
|
||||
name: "Yes",
|
||||
icon: "/static/assets/icons/update.svg",
|
||||
id: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export function dfv(optPicker: OptionPicker) {
|
||||
optPicker.picker("Default file visibility",options.FV).then((exp) => {
|
||||
if (exp && exp.selected) {
|
||||
fetch(`/auth/dfv`,{method:"POST", body:JSON.stringify({
|
||||
defaultFileVisibility: exp.selected
|
||||
})}).then((response) => {
|
||||
|
||||
if (response.status != 200) {
|
||||
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
|
||||
}
|
||||
|
||||
fetchAccountData()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function update_all_files(optPicker: OptionPicker) {
|
||||
optPicker.picker("You sure?",[
|
||||
{
|
||||
name: "Yeah",
|
||||
icon: "/static/assets/icons/update.svg",
|
||||
description: `This will make all of your files ${get(account)?.defaultFileVisibility || "public"}`,
|
||||
id: true
|
||||
}
|
||||
]).then((exp) => {
|
||||
if (exp && exp.selected) {
|
||||
fetch(`/files/manage`,{method:"POST", body:JSON.stringify({
|
||||
target:get(account)?.files,
|
||||
action: "changeFileVisibility",
|
||||
|
||||
value: get(account)?.defaultFileVisibility
|
||||
})}).then((response) => {
|
||||
|
||||
if (response.status != 200) {
|
||||
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
|
||||
}
|
||||
|
||||
fetchAccountData()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function fileOptions(optPicker: OptionPicker, file: FilePointer & {id:string}) {
|
||||
optPicker.picker(file.filename,[
|
||||
{
|
||||
name: file.tag ? "Remove tag" : "Tag file",
|
||||
icon: `/static/assets/icons/${file.tag ? "tag_remove" : "tag"}.svg`,
|
||||
description: file.tag || `File has no tag`,
|
||||
id: "tag"
|
||||
},
|
||||
{
|
||||
name: "Change file visibility",
|
||||
icon: `/static/assets/icons/${file.visibility||"public"}.svg`,
|
||||
description: `File is currently ${file.visibility||"public"}`,
|
||||
id: "changeFileVisibility"
|
||||
},
|
||||
{
|
||||
name: "Delete file",
|
||||
icon: `/static/assets/icons/admin/delete_file.svg`,
|
||||
description: ``,
|
||||
id: "delete"
|
||||
}
|
||||
]).then((exp) => {
|
||||
|
||||
if (exp && exp.selected) {
|
||||
|
||||
switch( exp.selected ) {
|
||||
|
||||
case "delete":
|
||||
|
||||
fetch(`/files/manage`,{method:"POST", body:JSON.stringify({
|
||||
target: [ file.id ],
|
||||
action: "delete",
|
||||
})}).then((response) => {
|
||||
|
||||
if (response.status != 200) {
|
||||
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
|
||||
}
|
||||
|
||||
fetchFilePointers();
|
||||
})
|
||||
|
||||
break;
|
||||
|
||||
case "changeFileVisibility":
|
||||
|
||||
optPicker.picker("Set file visibility", options.FV2).then((exp) => {
|
||||
|
||||
if (exp && exp.selected) {
|
||||
|
||||
fetch(`/files/manage`, {method: "POST", body: JSON.stringify({
|
||||
target: [ file.id ],
|
||||
action: "changeFileVisibility",
|
||||
|
||||
value: exp.selected
|
||||
})}).then((response) => {
|
||||
|
||||
if (response.status != 200) {
|
||||
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
|
||||
}
|
||||
|
||||
fetchFilePointers();
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
break;
|
||||
|
||||
case "tag":
|
||||
|
||||
if (file.tag) {
|
||||
fetch(`/files/manage`, {method: "POST", body: JSON.stringify({
|
||||
target: [ file.id ],
|
||||
action: "setTag"
|
||||
})}).then(fetchFilePointers)
|
||||
return
|
||||
}
|
||||
|
||||
optPicker.picker("Enter a tag (max 30char)",[
|
||||
{
|
||||
name: "Tag name",
|
||||
icon: "/static/assets/icons/tag.svg",
|
||||
id: "tag",
|
||||
inputSettings: {}
|
||||
},
|
||||
{
|
||||
name: "OK",
|
||||
icon: "/static/assets/icons/update.svg",
|
||||
description: "",
|
||||
id: true
|
||||
}
|
||||
]).then((exp) => {
|
||||
|
||||
if (exp && exp.selected) {
|
||||
|
||||
fetch(`/files/manage`, {method: "POST", body: JSON.stringify({
|
||||
target: [ file.id ],
|
||||
action: "setTag",
|
||||
|
||||
value: exp.tag || null
|
||||
})}).then((response) => {
|
||||
|
||||
if (response.status != 200) {
|
||||
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
|
||||
}
|
||||
|
||||
fetchFilePointers();
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
break
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
}
|
|
@ -1,243 +0,0 @@
|
|||
<script lang="ts">
|
||||
import Pulldown from "./Pulldown.svelte"
|
||||
import { padding_scaleY } from "../transition/padding_scaleY"
|
||||
import { circIn,circOut } from "svelte/easing"
|
||||
import { account, fetchAccountData, serverStats, refreshNeeded } from "../stores";
|
||||
import { fade } from "svelte/transition";
|
||||
import OptionPicker from "../prompts/OptionPicker.svelte";
|
||||
import * as accOpts from "../prompts/account";
|
||||
import * as uplOpts from "../prompts/uploads";
|
||||
import * as admOpts from "../prompts/admin";
|
||||
|
||||
let targetAction: "login"|"create"
|
||||
let inProgress: boolean
|
||||
let authError:{status:number,message:string}|undefined
|
||||
|
||||
let pwErr: HTMLDivElement
|
||||
|
||||
let optPicker: OptionPicker;
|
||||
|
||||
// lazy
|
||||
|
||||
let username: string
|
||||
let password: string
|
||||
|
||||
let execute = () => {
|
||||
if (inProgress) return
|
||||
|
||||
inProgress = true
|
||||
|
||||
fetch(`/auth/${targetAction}`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
username, password
|
||||
})
|
||||
}).then(async (res) => {
|
||||
inProgress = false
|
||||
|
||||
if (res.status != 200) {
|
||||
authError = await res.json().catch(() => {
|
||||
return {
|
||||
status: res.status,
|
||||
message: res.headers.get("x-backup-status-message") || res.statusText || ""
|
||||
}
|
||||
})
|
||||
} else {
|
||||
authError = undefined, username = "", password = "";
|
||||
fetchAccountData();
|
||||
}
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
$: {
|
||||
if (pwErr && authError) {
|
||||
pwErr.animate({
|
||||
backgroundColor: ["#885555","#663333"],
|
||||
easing: "ease-out"
|
||||
},650)
|
||||
}
|
||||
}
|
||||
|
||||
// actual account menu
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<Pulldown name="accounts">
|
||||
<OptionPicker bind:this={optPicker} />
|
||||
{#if $account}
|
||||
<div class="loggedIn" transition:fade={{duration:200}}>
|
||||
<h1>
|
||||
Hey there, <span class="monospace">@{$account.username}</span>
|
||||
</h1>
|
||||
|
||||
<div class="optPicker">
|
||||
|
||||
<div class="category">
|
||||
<p>Account</p>
|
||||
</div>
|
||||
|
||||
<button on:click={() => accOpts.userChange(optPicker)}>
|
||||
<img src="/static/assets/icons/change_username.svg" alt="change username">
|
||||
<p>Change username</p>
|
||||
</button>
|
||||
|
||||
<button on:click={() => ($account?.email ? accOpts.emailPotentialRemove : accOpts.emailChange)(optPicker)}>
|
||||
<img src="/static/assets/icons/mail.svg" alt="change email">
|
||||
<p>Change email{#if $account.email}<span class="monospaceText"><br />{$account.email}</span>{/if}</p>
|
||||
</button>
|
||||
|
||||
<button on:click={() => accOpts.pwdChng(optPicker)}>
|
||||
<img src="/static/assets/icons/change_password.svg" alt="change password">
|
||||
<p>Change password<span><br />You will be logged out of all sessions</span></p>
|
||||
</button>
|
||||
|
||||
{#if !$account.admin}
|
||||
<button on:click={() => accOpts.deleteAccount(optPicker)}>
|
||||
<img src="/static/assets/icons/delete_account.svg" alt="delete account">
|
||||
<p>Delete account</p>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<div class="category">
|
||||
<p>Uploads</p>
|
||||
</div>
|
||||
|
||||
<button on:click={() => uplOpts.dfv(optPicker)}>
|
||||
<img src={`/static/assets/icons/${$account.defaultFileVisibility || "public"}.svg`} alt={$account.defaultFileVisibility || "public"}>
|
||||
<p>Default file visibility<span><br />Uploads will be <strong>{$account.defaultFileVisibility || "public"}</strong> by default</span></p>
|
||||
</button>
|
||||
|
||||
<button on:click={() => uplOpts.update_all_files(optPicker)}>
|
||||
<img src="/static/assets/icons/update.svg" alt="update">
|
||||
<p>Make all of my files {$account.defaultFileVisibility || "public"}<span><br />Matches your default file visibility</span></p>
|
||||
</button>
|
||||
|
||||
<div class="category">
|
||||
<p>Customization</p>
|
||||
</div>
|
||||
|
||||
<button on:click={() => accOpts.customcss(optPicker)}>
|
||||
<img src="/static/assets/icons/paint.svg" alt="customcss">
|
||||
<p>Set custom CSS<span><br />{@html $account.customCSS ? `Using file ID <span class="number">${$account.customCSS}</span>` : "No custom CSS set"}</span></p>
|
||||
</button>
|
||||
|
||||
<button on:click={() => accOpts.embedColor(optPicker)}>
|
||||
<img src="/static/assets/icons/pound.svg" alt="embedColor">
|
||||
<p>Set custom embed color<span><br />{@html $account?.embed?.color ? `Using custom color <span class="number">${$account?.embed?.color}</span>` : ""}</span></p>
|
||||
</button>
|
||||
|
||||
<button on:click={() => accOpts.embedSize(optPicker)}>
|
||||
<img src="/static/assets/icons/image.svg" alt="embedSize">
|
||||
<p>Set embed image size <span><br />Images currently appear {$account?.embed?.largeImage ? `large` : "small"} in embeds</span></p>
|
||||
</button>
|
||||
|
||||
{#if $refreshNeeded}
|
||||
<button on:click={() => window.location.reload()} transition:fade={{duration: 200}}>
|
||||
<img src="/static/assets/icons/refresh.svg" alt="refresh">
|
||||
<p>Refresh<span><br />Changes were made which require a refresh</span></p>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<div class="category">
|
||||
<p>Sessions</p>
|
||||
</div>
|
||||
|
||||
<button on:click={() => fetch(`/auth/logout_sessions`,{method:"POST"}).then(() => fetchAccountData())}>
|
||||
<img src="/static/assets/icons/logout_all.svg" alt="logout_all">
|
||||
<p>Log out all sessions<span><br />{$account?.sessionCount} session(s) active</span></p>
|
||||
</button>
|
||||
|
||||
<button on:click={() => fetch(`/auth/logout`,{method:"POST"}).then(() => fetchAccountData())}>
|
||||
<img src="/static/assets/icons/logout.svg" alt="logout">
|
||||
<p>Log out<span><br />Session expires {new Date($account?.sessionExpires).toLocaleDateString()}</span></p>
|
||||
</button>
|
||||
|
||||
{#if $account.admin}
|
||||
|
||||
<div class="category">
|
||||
<p>Admin</p>
|
||||
</div>
|
||||
|
||||
<button on:click={() => admOpts.deleteAccount(optPicker)}>
|
||||
<img src="/static/assets/icons/delete_account.svg" alt="delete account">
|
||||
<p>Delete user account</p>
|
||||
</button>
|
||||
|
||||
<button on:click={() => admOpts.pwdReset(optPicker)}>
|
||||
<img src="/static/assets/icons/change_password.svg" alt="change password">
|
||||
<p>Change user password</p>
|
||||
</button>
|
||||
|
||||
<button on:click={() => admOpts.elevateUser(optPicker)}>
|
||||
<img src="/static/assets/icons/admin/elevate_user.svg" alt="elevate account">
|
||||
<p>Elevate account to admin</p>
|
||||
</button>
|
||||
|
||||
<button on:click={() => admOpts.chgOwner(optPicker)}>
|
||||
<img src="/static/assets/icons/link.svg" alt="change file owner">
|
||||
<p>Change file owner</p>
|
||||
</button>
|
||||
|
||||
<button on:click={() => admOpts.chgId(optPicker)}>
|
||||
<img src="/static/assets/icons/admin/change_file_id.svg" alt="change file id">
|
||||
<p>Change file ID<span><br />Potentially buggy, usage not recommended</span></p>
|
||||
</button>
|
||||
|
||||
<button on:click={() => admOpts.delFile(optPicker)}>
|
||||
<img src="/static/assets/icons/admin/delete_file.svg" alt="delete file">
|
||||
<p>Delete file</p>
|
||||
</button>
|
||||
|
||||
{/if}
|
||||
<p style="font-size:12px;color:#AAAAAA;text-align:center;" class="monospace"><br />{$account.id}</p>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="notLoggedIn" transition:fade={{duration:200}}>
|
||||
<div class="container_div">
|
||||
<h1>monofile <span style:color="#999999">accounts</span></h1>
|
||||
<p class="flavor">Gain control of your uploads.</p>
|
||||
|
||||
{#if targetAction}
|
||||
|
||||
<div class="fields" out:padding_scaleY|local={{easingFunc:circIn}} in:padding_scaleY|local>
|
||||
{#if !$serverStats?.accounts.registrationEnabled && targetAction == "create"}
|
||||
<div class="pwError">
|
||||
<div style:background-color="#554C33">
|
||||
<p>Account registration has been disabled by this instance's owner</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if authError}
|
||||
<div class="pwError" out:padding_scaleY|local={{easingFunc:circIn}} in:padding_scaleY|local>
|
||||
<div bind:this={pwErr}>
|
||||
<p><strong>{authError.status}</strong> {authError.message}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<input placeholder="username" type="text" bind:value={username}>
|
||||
<input placeholder="password" type="password" bind:value={password}>
|
||||
<button on:click={execute}>{@html inProgress ? "<span class=loader><i>•</i> <i>•</i> <i>•</i></span>" : (targetAction=="login" ? "Log in" : "Create account") }</button>
|
||||
|
||||
{#if targetAction == "login"}
|
||||
<button class="flavor" on:click={() => accOpts.forgotPassword(optPicker)}>I forgot my password</button>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
|
||||
{:else}
|
||||
|
||||
<div class="lgBtnContainer" out:padding_scaleY|local={{easingFunc:circIn}} in:padding_scaleY|local>
|
||||
<button on:click={() => targetAction="login"}>Log in</button>
|
||||
<button on:click={() => targetAction="create"}>Sign up</button>
|
||||
</div>
|
||||
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</Pulldown>
|
|
@ -1,63 +0,0 @@
|
|||
<script lang="ts">
|
||||
import Pulldown from "./Pulldown.svelte";
|
||||
import { account, fetchFilePointers, files, pulldownManager } from "../stores.js";
|
||||
|
||||
import { fade } from "svelte/transition";
|
||||
import { flip } from "svelte/animate";
|
||||
import { fileOptions } from "../prompts/uploads";
|
||||
import OptionPicker from "../prompts/OptionPicker.svelte";
|
||||
|
||||
let picker: OptionPicker;
|
||||
let query = "";
|
||||
|
||||
fetchFilePointers();
|
||||
</script>
|
||||
|
||||
<Pulldown name="files">
|
||||
|
||||
<OptionPicker bind:this={picker} />
|
||||
|
||||
{#if $account?.username}<div class="loggedIn">
|
||||
<input type="text" placeholder={`Search ${$files.length} file(s)`} class="searchBar" bind:value={query}>
|
||||
|
||||
<div class="fileList">
|
||||
<!-- Probably wildly inefficient but who cares, I just wanna get this over with -->
|
||||
{#each $files.filter(f => f&&(f.filename.toLowerCase().includes(query.toLowerCase()) || f.id.toLowerCase().includes(query.toLowerCase()) || f.tag?.includes(query.toLowerCase()))) as file (file.id)}
|
||||
<div class="flFile" transition:fade={{duration:200}} animate:flip={{duration:200}}>
|
||||
<button class="hitbox" on:click={() => window.open(`/download/${file.id}`)}></button> <!-- this is bad, but I'm lazy -->
|
||||
<div class="flexCont">
|
||||
<div class="fileInfo">
|
||||
<h2>{file.filename}</h2>
|
||||
<p class="detail">
|
||||
<img src="/static/assets/icons/{file.visibility || "public"}.svg" alt={file.visibility||"public"} />
|
||||
<span class="number">{file.id}</span> — <span class="cd">{file.mime.split(";")[0]}</span>
|
||||
{#if file.reserved}
|
||||
<br />
|
||||
<img src="/static/assets/icons/update.svg" alt="uploading"/>
|
||||
Uploading...
|
||||
{/if}
|
||||
{#if file.tag}
|
||||
<br />
|
||||
<img src="/static/assets/icons/tag.svg" alt="tag"/>
|
||||
<span class="cd">{file.tag}</span>
|
||||
{/if}
|
||||
</p>
|
||||
</div>
|
||||
<button class="more" on:click={() => fileOptions(picker, file)}>
|
||||
<img src="/static/assets/icons/more.svg" alt="more" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="notLoggedIn">
|
||||
<div style:height="10px" />
|
||||
<p class="flavor">Log in to view uploads</p>
|
||||
<button on:click={$pulldownManager.openPulldown("account")}>OK</button>
|
||||
<div style:height="14px" />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
</Pulldown>
|
|
@ -1,25 +0,0 @@
|
|||
<!-- i'm lazy, could probably just use plain html here but hwatever, mgiht make this grab from the server idk -->
|
||||
|
||||
<script>
|
||||
import Pulldown from "./Pulldown.svelte"
|
||||
|
||||
let faq = [
|
||||
{
|
||||
question : "Are my files compressed on upload?",
|
||||
answer : "No. Files should stay completely unchanged on download."
|
||||
},
|
||||
{
|
||||
question : "How do I replace a file that I have previously uploaded?",
|
||||
answer : "You can modify the content of a file that is linked to a file ID by reuploading the file using the same custom ID."
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<Pulldown name="help">
|
||||
{#each faq as question}
|
||||
<div class="faqGroup">
|
||||
<h2>{question.question}</h2>
|
||||
<p>{question.answer}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</Pulldown>
|
|
@ -1,14 +0,0 @@
|
|||
<script lang=ts>
|
||||
|
||||
import { fade } from "svelte/transition";
|
||||
|
||||
export let name: string;
|
||||
|
||||
</script>
|
||||
<div
|
||||
class="pulldown_display"
|
||||
data-name={name}
|
||||
transition:fade={{duration:150}}
|
||||
>
|
||||
<slot />
|
||||
</div>
|
|
@ -1,54 +0,0 @@
|
|||
import { writable } from "svelte/store"
|
||||
//import type Pulldown from "./pulldowns/Pulldown.svelte"
|
||||
import type { SvelteComponent } from "svelte"
|
||||
import type { Account } from "../../server/lib/accounts"
|
||||
import type { ClientConfiguration } from "../../server/lib/config"
|
||||
import type { FilePointer } from "../../server/lib/files"
|
||||
|
||||
export let refreshNeeded = writable(false)
|
||||
export let pulldownManager = writable<SvelteComponent>()
|
||||
export let account = writable<
|
||||
(Account & { sessionCount: number; sessionExpires: number }) | undefined
|
||||
>()
|
||||
export let serverStats = writable<ClientConfiguration | undefined>()
|
||||
export let files = writable<(FilePointer & { id: string })[]>([])
|
||||
|
||||
export let fetchAccountData = function () {
|
||||
fetch("/auth/me")
|
||||
.then(async (response) => {
|
||||
if (response.status == 200) {
|
||||
account.set(await response.json())
|
||||
} else {
|
||||
account.set(undefined)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
export let fetchFilePointers = function () {
|
||||
fetch("/files/list", { cache: "no-cache" })
|
||||
.then(async (response) => {
|
||||
if (response.status == 200) {
|
||||
files.set(await response.json())
|
||||
} else {
|
||||
files.set([])
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
export let refresh_stats = () => {
|
||||
fetch("/server")
|
||||
.then(async (data) => {
|
||||
serverStats.set(await data.json())
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
fetchAccountData()
|
|
@ -1,23 +0,0 @@
|
|||
import { circIn, circOut } from "svelte/easing"
|
||||
|
||||
export function _void(
|
||||
node: HTMLElement,
|
||||
options?: { duration?:number, easingFunc?: (a:number)=>number, prop?:string, rTarg?: "height"|"width"}
|
||||
) {
|
||||
const { duration = 300, easingFunc = circIn, prop, rTarg } = options ?? {}
|
||||
let rect = node.getBoundingClientRect()
|
||||
|
||||
return {
|
||||
duration,
|
||||
css: (t: number) => {
|
||||
let eased = easingFunc(t)
|
||||
return `
|
||||
white-space: nowrap;
|
||||
${prop||"height"}: ${(eased)*(rect[rTarg || (prop && prop in rect) ? prop as keyof Omit<DOMRect, "toJSON"> : "height"])}px;
|
||||
padding: 0px;
|
||||
opacity:${eased};
|
||||
overflow: clip;
|
||||
`
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
import { circIn, circOut } from "svelte/easing"
|
||||
|
||||
function padding_scaleY(node: HTMLElement, options?: { duration?: number, easingFunc?: (a: number) => number, padY?: number, padX?: number, op?: boolean }) {
|
||||
const { duration = 300, easingFunc = circOut, padY, padX, op } = options ?? {}
|
||||
let rect = node.getBoundingClientRect()
|
||||
|
||||
return {
|
||||
duration,
|
||||
css: (t:number) => {
|
||||
let eased = easingFunc(t)
|
||||
|
||||
return `
|
||||
height: ${eased*(rect.height-(padY||0))}px;
|
||||
${padX&&padY ? `padding: ${(eased)*(padY)}px ${(padX)}px;` : ""}
|
||||
${op ? `opacity: ${eased};` : ""}
|
||||
`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {padding_scaleY}
|
|
@ -1,72 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { circOut } from "svelte/easing"
|
||||
import { _void } from "../transition/_void"
|
||||
|
||||
enum UploadTypes {
|
||||
None,
|
||||
Files,
|
||||
Clone
|
||||
}
|
||||
|
||||
let uploadType: UploadTypes = UploadTypes.None
|
||||
let dispatch = createEventDispatcher();
|
||||
|
||||
// file upload
|
||||
let files: FileList | undefined
|
||||
$: if (files) {
|
||||
[...files].forEach(file=>dispatch("addFiles", file))
|
||||
uploadType = UploadTypes.None
|
||||
}
|
||||
|
||||
// file clone
|
||||
let cloneUrlTextbox: HTMLInputElement;
|
||||
let cloneForm: HTMLFormElement;
|
||||
|
||||
$: {
|
||||
if (cloneForm && cloneUrlTextbox) {
|
||||
cloneForm.addEventListener("submit",(e) => {
|
||||
e.preventDefault()
|
||||
if (cloneUrlTextbox.value) {
|
||||
dispatch("addFiles",cloneUrlTextbox.value)
|
||||
uploadType = UploadTypes.None;
|
||||
} else {
|
||||
cloneUrlTextbox.animate([
|
||||
{"transform":"translateX(0px)"},
|
||||
{"transform":"translateX(-3px)"},
|
||||
{"transform":"translateX(3px)"},
|
||||
{"transform":"translateX(0px)"}
|
||||
],100)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- there are 100% better ways to do this but idgaf, it's still easier to manage than <1.3 lmao -->
|
||||
|
||||
<div id="add_new_files" transition:_void={{duration:200}}>
|
||||
<p>
|
||||
+<span class="add_files_txt">add files</span>
|
||||
</p>
|
||||
{#if uploadType == UploadTypes.None}
|
||||
<div id="file_add_btns" out:_void in:_void={{easingFunc:circOut}}>
|
||||
<button on:click={() => uploadType = UploadTypes.Files} >upload files...</button>
|
||||
<button on:click={() => uploadType = UploadTypes.Clone} >clone url...</button>
|
||||
</div>
|
||||
{:else}
|
||||
{#if uploadType == UploadTypes.Files}
|
||||
<div id="file_add_btns" out:_void in:_void={{easingFunc:circOut}}>
|
||||
<div class="fileUpload">
|
||||
<p>click/tap to browse<br/>or drag files into this box</p>
|
||||
<input type="file" multiple bind:files={files}>
|
||||
</div>
|
||||
</div>
|
||||
{:else if uploadType == UploadTypes.Clone}
|
||||
<form id="file_add_btns" out:_void in:_void={{easingFunc:circOut}} bind:this={cloneForm}>
|
||||
<input placeholder="url" type="text" bind:this={cloneUrlTextbox}>
|
||||
<input type="submit" value="add file" style:flex-basis="30%">
|
||||
</form>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
Loading…
Reference in a new issue