early mail config UI scaffolding; testing stuff

This commit is contained in:
ari melody 2026-02-23 16:29:08 +00:00
parent bfb687bab8
commit 14535219d9
Signed by: ari
GPG key ID: CF99829C92678188
26 changed files with 1391 additions and 0 deletions

3
src/web/backend/go.mod Normal file
View file

@ -0,0 +1,3 @@
module jupiter-mail.org/web
go 1.25.4

0
src/web/backend/main.go Normal file
View file

23
src/web/frontend/.gitignore vendored Normal file
View file

@ -0,0 +1,23 @@
node_modules
# Output
.output
.vercel
.netlify
.wrangler
/.svelte-kit
/build
# OS
.DS_Store
Thumbs.db
# Env
.env
.env.*
!.env.example
!.env.test
# Vite
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

1
src/web/frontend/.npmrc Normal file
View file

@ -0,0 +1 @@
engine-strict=true

652
src/web/frontend/deno.lock generated Normal file
View file

@ -0,0 +1,652 @@
{
"version": "5",
"specifiers": {
"npm:@lucide/svelte@0.575": "0.575.0_svelte@5.53.0__acorn@8.16.0",
"npm:@sveltejs/adapter-static@^3.0.10": "3.0.10_@sveltejs+kit@2.53.0__@sveltejs+vite-plugin-svelte@6.2.4___svelte@5.53.0____acorn@8.16.0___vite@7.3.1____picomatch@4.0.3__svelte@5.53.0___acorn@8.16.0__typescript@5.9.3__vite@7.3.1___picomatch@4.0.3__acorn@8.16.0_@sveltejs+vite-plugin-svelte@6.2.4__svelte@5.53.0___acorn@8.16.0__vite@7.3.1___picomatch@4.0.3_svelte@5.53.0__acorn@8.16.0_typescript@5.9.3_vite@7.3.1__picomatch@4.0.3",
"npm:@sveltejs/kit@^2.50.2": "2.53.0_@sveltejs+vite-plugin-svelte@6.2.4__svelte@5.53.0___acorn@8.16.0__vite@7.3.1___picomatch@4.0.3_svelte@5.53.0__acorn@8.16.0_typescript@5.9.3_vite@7.3.1__picomatch@4.0.3_acorn@8.16.0",
"npm:@sveltejs/vite-plugin-svelte@^6.2.4": "6.2.4_svelte@5.53.0__acorn@8.16.0_vite@7.3.1__picomatch@4.0.3",
"npm:svelte-check@^4.3.6": "4.4.3_svelte@5.53.0__acorn@8.16.0_typescript@5.9.3",
"npm:svelte@^5.51.0": "5.53.0_acorn@8.16.0",
"npm:typescript@^5.9.3": "5.9.3",
"npm:vite@^7.3.1": "7.3.1_picomatch@4.0.3"
},
"npm": {
"@esbuild/aix-ppc64@0.27.3": {
"integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==",
"os": ["aix"],
"cpu": ["ppc64"]
},
"@esbuild/android-arm64@0.27.3": {
"integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==",
"os": ["android"],
"cpu": ["arm64"]
},
"@esbuild/android-arm@0.27.3": {
"integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==",
"os": ["android"],
"cpu": ["arm"]
},
"@esbuild/android-x64@0.27.3": {
"integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==",
"os": ["android"],
"cpu": ["x64"]
},
"@esbuild/darwin-arm64@0.27.3": {
"integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==",
"os": ["darwin"],
"cpu": ["arm64"]
},
"@esbuild/darwin-x64@0.27.3": {
"integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==",
"os": ["darwin"],
"cpu": ["x64"]
},
"@esbuild/freebsd-arm64@0.27.3": {
"integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==",
"os": ["freebsd"],
"cpu": ["arm64"]
},
"@esbuild/freebsd-x64@0.27.3": {
"integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==",
"os": ["freebsd"],
"cpu": ["x64"]
},
"@esbuild/linux-arm64@0.27.3": {
"integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==",
"os": ["linux"],
"cpu": ["arm64"]
},
"@esbuild/linux-arm@0.27.3": {
"integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==",
"os": ["linux"],
"cpu": ["arm"]
},
"@esbuild/linux-ia32@0.27.3": {
"integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==",
"os": ["linux"],
"cpu": ["ia32"]
},
"@esbuild/linux-loong64@0.27.3": {
"integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==",
"os": ["linux"],
"cpu": ["loong64"]
},
"@esbuild/linux-mips64el@0.27.3": {
"integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==",
"os": ["linux"],
"cpu": ["mips64el"]
},
"@esbuild/linux-ppc64@0.27.3": {
"integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==",
"os": ["linux"],
"cpu": ["ppc64"]
},
"@esbuild/linux-riscv64@0.27.3": {
"integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==",
"os": ["linux"],
"cpu": ["riscv64"]
},
"@esbuild/linux-s390x@0.27.3": {
"integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==",
"os": ["linux"],
"cpu": ["s390x"]
},
"@esbuild/linux-x64@0.27.3": {
"integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
"os": ["linux"],
"cpu": ["x64"]
},
"@esbuild/netbsd-arm64@0.27.3": {
"integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==",
"os": ["netbsd"],
"cpu": ["arm64"]
},
"@esbuild/netbsd-x64@0.27.3": {
"integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==",
"os": ["netbsd"],
"cpu": ["x64"]
},
"@esbuild/openbsd-arm64@0.27.3": {
"integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==",
"os": ["openbsd"],
"cpu": ["arm64"]
},
"@esbuild/openbsd-x64@0.27.3": {
"integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==",
"os": ["openbsd"],
"cpu": ["x64"]
},
"@esbuild/openharmony-arm64@0.27.3": {
"integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==",
"os": ["openharmony"],
"cpu": ["arm64"]
},
"@esbuild/sunos-x64@0.27.3": {
"integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==",
"os": ["sunos"],
"cpu": ["x64"]
},
"@esbuild/win32-arm64@0.27.3": {
"integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==",
"os": ["win32"],
"cpu": ["arm64"]
},
"@esbuild/win32-ia32@0.27.3": {
"integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==",
"os": ["win32"],
"cpu": ["ia32"]
},
"@esbuild/win32-x64@0.27.3": {
"integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==",
"os": ["win32"],
"cpu": ["x64"]
},
"@jridgewell/gen-mapping@0.3.13": {
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"dependencies": [
"@jridgewell/sourcemap-codec",
"@jridgewell/trace-mapping"
]
},
"@jridgewell/remapping@2.3.5": {
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
"dependencies": [
"@jridgewell/gen-mapping",
"@jridgewell/trace-mapping"
]
},
"@jridgewell/resolve-uri@3.1.2": {
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="
},
"@jridgewell/sourcemap-codec@1.5.5": {
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="
},
"@jridgewell/trace-mapping@0.3.31": {
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
"dependencies": [
"@jridgewell/resolve-uri",
"@jridgewell/sourcemap-codec"
]
},
"@lucide/svelte@0.575.0_svelte@5.53.0__acorn@8.16.0": {
"integrity": "sha512-FEFp/0McZwsjBqh1Dn8H+UBm1yHFQYk+utuVMFDw57155+wz2XMoc1pw027ylCPzs+bi14UEXYKbekFhuJKtnw==",
"dependencies": [
"svelte"
]
},
"@polka/url@1.0.0-next.29": {
"integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="
},
"@rollup/rollup-android-arm-eabi@4.58.0": {
"integrity": "sha512-mr0tmS/4FoVk1cnaeN244A/wjvGDNItZKR8hRhnmCzygyRXYtKF5jVDSIILR1U97CTzAYmbgIj/Dukg62ggG5w==",
"os": ["android"],
"cpu": ["arm"]
},
"@rollup/rollup-android-arm64@4.58.0": {
"integrity": "sha512-+s++dbp+/RTte62mQD9wLSbiMTV+xr/PeRJEc/sFZFSBRlHPNPVaf5FXlzAL77Mr8FtSfQqCN+I598M8U41ccQ==",
"os": ["android"],
"cpu": ["arm64"]
},
"@rollup/rollup-darwin-arm64@4.58.0": {
"integrity": "sha512-MFWBwTcYs0jZbINQBXHfSrpSQJq3IUOakcKPzfeSznONop14Pxuqa0Kg19GD0rNBMPQI2tFtu3UzapZpH0Uc1Q==",
"os": ["darwin"],
"cpu": ["arm64"]
},
"@rollup/rollup-darwin-x64@4.58.0": {
"integrity": "sha512-yiKJY7pj9c9JwzuKYLFaDZw5gma3fI9bkPEIyofvVfsPqjCWPglSHdpdwXpKGvDeYDms3Qal8qGMEHZ1M/4Udg==",
"os": ["darwin"],
"cpu": ["x64"]
},
"@rollup/rollup-freebsd-arm64@4.58.0": {
"integrity": "sha512-x97kCoBh5MOevpn/CNK9W1x8BEzO238541BGWBc315uOlN0AD/ifZ1msg+ZQB05Ux+VF6EcYqpiagfLJ8U3LvQ==",
"os": ["freebsd"],
"cpu": ["arm64"]
},
"@rollup/rollup-freebsd-x64@4.58.0": {
"integrity": "sha512-Aa8jPoZ6IQAG2eIrcXPpjRcMjROMFxCt1UYPZZtCxRV68WkuSigYtQ/7Zwrcr2IvtNJo7T2JfDXyMLxq5L4Jlg==",
"os": ["freebsd"],
"cpu": ["x64"]
},
"@rollup/rollup-linux-arm-gnueabihf@4.58.0": {
"integrity": "sha512-Ob8YgT5kD/lSIYW2Rcngs5kNB/44Q2RzBSPz9brf2WEtcGR7/f/E9HeHn1wYaAwKBni+bdXEwgHvUd0x12lQSA==",
"os": ["linux"],
"cpu": ["arm"]
},
"@rollup/rollup-linux-arm-musleabihf@4.58.0": {
"integrity": "sha512-K+RI5oP1ceqoadvNt1FecL17Qtw/n9BgRSzxif3rTL2QlIu88ccvY+Y9nnHe/cmT5zbH9+bpiJuG1mGHRVwF4Q==",
"os": ["linux"],
"cpu": ["arm"]
},
"@rollup/rollup-linux-arm64-gnu@4.58.0": {
"integrity": "sha512-T+17JAsCKUjmbopcKepJjHWHXSjeW7O5PL7lEFaeQmiVyw4kkc5/lyYKzrv6ElWRX/MrEWfPiJWqbTvfIvjM1Q==",
"os": ["linux"],
"cpu": ["arm64"]
},
"@rollup/rollup-linux-arm64-musl@4.58.0": {
"integrity": "sha512-cCePktb9+6R9itIJdeCFF9txPU7pQeEHB5AbHu/MKsfH/k70ZtOeq1k4YAtBv9Z7mmKI5/wOLYjQ+B9QdxR6LA==",
"os": ["linux"],
"cpu": ["arm64"]
},
"@rollup/rollup-linux-loong64-gnu@4.58.0": {
"integrity": "sha512-iekUaLkfliAsDl4/xSdoCJ1gnnIXvoNz85C8U8+ZxknM5pBStfZjeXgB8lXobDQvvPRCN8FPmmuTtH+z95HTmg==",
"os": ["linux"],
"cpu": ["loong64"]
},
"@rollup/rollup-linux-loong64-musl@4.58.0": {
"integrity": "sha512-68ofRgJNl/jYJbxFjCKE7IwhbfxOl1muPN4KbIqAIe32lm22KmU7E8OPvyy68HTNkI2iV/c8y2kSPSm2mW/Q9Q==",
"os": ["linux"],
"cpu": ["loong64"]
},
"@rollup/rollup-linux-ppc64-gnu@4.58.0": {
"integrity": "sha512-dpz8vT0i+JqUKuSNPCP5SYyIV2Lh0sNL1+FhM7eLC457d5B9/BC3kDPp5BBftMmTNsBarcPcoz5UGSsnCiw4XQ==",
"os": ["linux"],
"cpu": ["ppc64"]
},
"@rollup/rollup-linux-ppc64-musl@4.58.0": {
"integrity": "sha512-4gdkkf9UJ7tafnweBCR/mk4jf3Jfl0cKX9Np80t5i78kjIH0ZdezUv/JDI2VtruE5lunfACqftJ8dIMGN4oHew==",
"os": ["linux"],
"cpu": ["ppc64"]
},
"@rollup/rollup-linux-riscv64-gnu@4.58.0": {
"integrity": "sha512-YFS4vPnOkDTD/JriUeeZurFYoJhPf9GQQEF/v4lltp3mVcBmnsAdjEWhr2cjUCZzZNzxCG0HZOvJU44UGHSdzw==",
"os": ["linux"],
"cpu": ["riscv64"]
},
"@rollup/rollup-linux-riscv64-musl@4.58.0": {
"integrity": "sha512-x2xgZlFne+QVNKV8b4wwaCS8pwq3y14zedZ5DqLzjdRITvreBk//4Knbcvm7+lWmms9V9qFp60MtUd0/t/PXPw==",
"os": ["linux"],
"cpu": ["riscv64"]
},
"@rollup/rollup-linux-s390x-gnu@4.58.0": {
"integrity": "sha512-jIhrujyn4UnWF8S+DHSkAkDEO3hLX0cjzxJZPLF80xFyzyUIYgSMRcYQ3+uqEoyDD2beGq7Dj7edi8OnJcS/hg==",
"os": ["linux"],
"cpu": ["s390x"]
},
"@rollup/rollup-linux-x64-gnu@4.58.0": {
"integrity": "sha512-+410Srdoh78MKSJxTQ+hZ/Mx+ajd6RjjPwBPNd0R3J9FtL6ZA0GqiiyNjCO9In0IzZkCNrpGymSfn+kgyPQocg==",
"os": ["linux"],
"cpu": ["x64"]
},
"@rollup/rollup-linux-x64-musl@4.58.0": {
"integrity": "sha512-ZjMyby5SICi227y1MTR3VYBpFTdZs823Rs/hpakufleBoufoOIB6jtm9FEoxn/cgO7l6PM2rCEl5Kre5vX0QrQ==",
"os": ["linux"],
"cpu": ["x64"]
},
"@rollup/rollup-openbsd-x64@4.58.0": {
"integrity": "sha512-ds4iwfYkSQ0k1nb8LTcyXw//ToHOnNTJtceySpL3fa7tc/AsE+UpUFphW126A6fKBGJD5dhRvg8zw1rvoGFxmw==",
"os": ["openbsd"],
"cpu": ["x64"]
},
"@rollup/rollup-openharmony-arm64@4.58.0": {
"integrity": "sha512-fd/zpJniln4ICdPkjWFhZYeY/bpnaN9pGa6ko+5WD38I0tTqk9lXMgXZg09MNdhpARngmxiCg0B0XUamNw/5BQ==",
"os": ["openharmony"],
"cpu": ["arm64"]
},
"@rollup/rollup-win32-arm64-msvc@4.58.0": {
"integrity": "sha512-YpG8dUOip7DCz3nr/JUfPbIUo+2d/dy++5bFzgi4ugOGBIox+qMbbqt/JoORwvI/C9Kn2tz6+Bieoqd5+B1CjA==",
"os": ["win32"],
"cpu": ["arm64"]
},
"@rollup/rollup-win32-ia32-msvc@4.58.0": {
"integrity": "sha512-b9DI8jpFQVh4hIXFr0/+N/TzLdpBIoPzjt0Rt4xJbW3mzguV3mduR9cNgiuFcuL/TeORejJhCWiAXe3E/6PxWA==",
"os": ["win32"],
"cpu": ["ia32"]
},
"@rollup/rollup-win32-x64-gnu@4.58.0": {
"integrity": "sha512-CSrVpmoRJFN06LL9xhkitkwUcTZtIotYAF5p6XOR2zW0Zz5mzb3IPpcoPhB02frzMHFNo1reQ9xSF5fFm3hUsQ==",
"os": ["win32"],
"cpu": ["x64"]
},
"@rollup/rollup-win32-x64-msvc@4.58.0": {
"integrity": "sha512-QFsBgQNTnh5K0t/sBsjJLq24YVqEIVkGpfN2VHsnN90soZyhaiA9UUHufcctVNL4ypJY0wrwad0wslx2KJQ1/w==",
"os": ["win32"],
"cpu": ["x64"]
},
"@standard-schema/spec@1.1.0": {
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="
},
"@sveltejs/acorn-typescript@1.0.9_acorn@8.16.0": {
"integrity": "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA==",
"dependencies": [
"acorn"
]
},
"@sveltejs/adapter-static@3.0.10_@sveltejs+kit@2.53.0__@sveltejs+vite-plugin-svelte@6.2.4___svelte@5.53.0____acorn@8.16.0___vite@7.3.1____picomatch@4.0.3__svelte@5.53.0___acorn@8.16.0__typescript@5.9.3__vite@7.3.1___picomatch@4.0.3__acorn@8.16.0_@sveltejs+vite-plugin-svelte@6.2.4__svelte@5.53.0___acorn@8.16.0__vite@7.3.1___picomatch@4.0.3_svelte@5.53.0__acorn@8.16.0_typescript@5.9.3_vite@7.3.1__picomatch@4.0.3": {
"integrity": "sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew==",
"dependencies": [
"@sveltejs/kit"
]
},
"@sveltejs/kit@2.53.0_@sveltejs+vite-plugin-svelte@6.2.4__svelte@5.53.0___acorn@8.16.0__vite@7.3.1___picomatch@4.0.3_svelte@5.53.0__acorn@8.16.0_typescript@5.9.3_vite@7.3.1__picomatch@4.0.3_acorn@8.16.0": {
"integrity": "sha512-Brh/9h8QEg7rWIj+Nnz/2sC49NUeS8g3Qd9H5dTO3EbWG8vCEUl06jE+r5jQVDMHdr1swmCkwZkONFsWelGTpQ==",
"dependencies": [
"@standard-schema/spec",
"@sveltejs/acorn-typescript",
"@sveltejs/vite-plugin-svelte",
"@types/cookie",
"acorn",
"cookie",
"devalue",
"esm-env",
"kleur",
"magic-string",
"mrmime",
"set-cookie-parser",
"sirv",
"svelte",
"typescript",
"vite"
],
"optionalPeers": [
"typescript"
],
"bin": true
},
"@sveltejs/vite-plugin-svelte-inspector@5.0.2_@sveltejs+vite-plugin-svelte@6.2.4__svelte@5.53.0___acorn@8.16.0__vite@7.3.1___picomatch@4.0.3_svelte@5.53.0__acorn@8.16.0_vite@7.3.1__picomatch@4.0.3": {
"integrity": "sha512-TZzRTcEtZffICSAoZGkPSl6Etsj2torOVrx6Uw0KpXxrec9Gg6jFWQ60Q3+LmNGfZSxHRCZL7vXVZIWmuV50Ig==",
"dependencies": [
"@sveltejs/vite-plugin-svelte",
"obug",
"svelte",
"vite"
]
},
"@sveltejs/vite-plugin-svelte@6.2.4_svelte@5.53.0__acorn@8.16.0_vite@7.3.1__picomatch@4.0.3": {
"integrity": "sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA==",
"dependencies": [
"@sveltejs/vite-plugin-svelte-inspector",
"deepmerge",
"magic-string",
"obug",
"svelte",
"vite",
"vitefu"
]
},
"@types/cookie@0.6.0": {
"integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="
},
"@types/estree@1.0.8": {
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="
},
"@types/trusted-types@2.0.7": {
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="
},
"acorn@8.16.0": {
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"bin": true
},
"aria-query@5.3.2": {
"integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="
},
"axobject-query@4.1.0": {
"integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="
},
"chokidar@4.0.3": {
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dependencies": [
"readdirp"
]
},
"clsx@2.1.1": {
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="
},
"cookie@0.6.0": {
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="
},
"deepmerge@4.3.1": {
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="
},
"devalue@5.6.3": {
"integrity": "sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg=="
},
"esbuild@0.27.3": {
"integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
"optionalDependencies": [
"@esbuild/aix-ppc64",
"@esbuild/android-arm",
"@esbuild/android-arm64",
"@esbuild/android-x64",
"@esbuild/darwin-arm64",
"@esbuild/darwin-x64",
"@esbuild/freebsd-arm64",
"@esbuild/freebsd-x64",
"@esbuild/linux-arm",
"@esbuild/linux-arm64",
"@esbuild/linux-ia32",
"@esbuild/linux-loong64",
"@esbuild/linux-mips64el",
"@esbuild/linux-ppc64",
"@esbuild/linux-riscv64",
"@esbuild/linux-s390x",
"@esbuild/linux-x64",
"@esbuild/netbsd-arm64",
"@esbuild/netbsd-x64",
"@esbuild/openbsd-arm64",
"@esbuild/openbsd-x64",
"@esbuild/openharmony-arm64",
"@esbuild/sunos-x64",
"@esbuild/win32-arm64",
"@esbuild/win32-ia32",
"@esbuild/win32-x64"
],
"scripts": true,
"bin": true
},
"esm-env@1.2.2": {
"integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="
},
"esrap@2.2.3": {
"integrity": "sha512-8fOS+GIGCQZl/ZIlhl59htOlms6U8NvX6ZYgYHpRU/b6tVSh3uHkOHZikl3D4cMbYM0JlpBe+p/BkZEi8J9XIQ==",
"dependencies": [
"@jridgewell/sourcemap-codec"
]
},
"fdir@6.5.0_picomatch@4.0.3": {
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dependencies": [
"picomatch"
],
"optionalPeers": [
"picomatch"
]
},
"fsevents@2.3.3": {
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"os": ["darwin"],
"scripts": true
},
"is-reference@3.0.3": {
"integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
"dependencies": [
"@types/estree"
]
},
"kleur@4.1.5": {
"integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="
},
"locate-character@3.0.0": {
"integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="
},
"magic-string@0.30.21": {
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
"dependencies": [
"@jridgewell/sourcemap-codec"
]
},
"mri@1.2.0": {
"integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="
},
"mrmime@2.0.1": {
"integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="
},
"nanoid@3.3.11": {
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"bin": true
},
"obug@2.1.1": {
"integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="
},
"picocolors@1.1.1": {
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
},
"picomatch@4.0.3": {
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="
},
"postcss@8.5.6": {
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"dependencies": [
"nanoid",
"picocolors",
"source-map-js"
]
},
"readdirp@4.1.2": {
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="
},
"rollup@4.58.0": {
"integrity": "sha512-wbT0mBmWbIvvq8NeEYWWvevvxnOyhKChir47S66WCxw1SXqhw7ssIYejnQEVt7XYQpsj2y8F9PM+Cr3SNEa0gw==",
"dependencies": [
"@types/estree"
],
"optionalDependencies": [
"@rollup/rollup-android-arm-eabi",
"@rollup/rollup-android-arm64",
"@rollup/rollup-darwin-arm64",
"@rollup/rollup-darwin-x64",
"@rollup/rollup-freebsd-arm64",
"@rollup/rollup-freebsd-x64",
"@rollup/rollup-linux-arm-gnueabihf",
"@rollup/rollup-linux-arm-musleabihf",
"@rollup/rollup-linux-arm64-gnu",
"@rollup/rollup-linux-arm64-musl",
"@rollup/rollup-linux-loong64-gnu",
"@rollup/rollup-linux-loong64-musl",
"@rollup/rollup-linux-ppc64-gnu",
"@rollup/rollup-linux-ppc64-musl",
"@rollup/rollup-linux-riscv64-gnu",
"@rollup/rollup-linux-riscv64-musl",
"@rollup/rollup-linux-s390x-gnu",
"@rollup/rollup-linux-x64-gnu",
"@rollup/rollup-linux-x64-musl",
"@rollup/rollup-openbsd-x64",
"@rollup/rollup-openharmony-arm64",
"@rollup/rollup-win32-arm64-msvc",
"@rollup/rollup-win32-ia32-msvc",
"@rollup/rollup-win32-x64-gnu",
"@rollup/rollup-win32-x64-msvc",
"fsevents"
],
"bin": true
},
"sade@1.8.1": {
"integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==",
"dependencies": [
"mri"
]
},
"set-cookie-parser@3.0.1": {
"integrity": "sha512-n7Z7dXZhJbwuAHhNzkTti6Aw9QDDjZtm3JTpTGATIdNzdQz5GuFs22w90BcvF4INfnrL5xrX3oGsuqO5Dx3A1Q=="
},
"sirv@3.0.2": {
"integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==",
"dependencies": [
"@polka/url",
"mrmime",
"totalist"
]
},
"source-map-js@1.2.1": {
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
},
"svelte-check@4.4.3_svelte@5.53.0__acorn@8.16.0_typescript@5.9.3": {
"integrity": "sha512-4HtdEv2hOoLCEsSXI+RDELk9okP/4sImWa7X02OjMFFOWeSdFF3NFy3vqpw0z+eH9C88J9vxZfUXz/Uv2A1ANw==",
"dependencies": [
"@jridgewell/trace-mapping",
"chokidar",
"fdir",
"picocolors",
"sade",
"svelte",
"typescript"
],
"bin": true
},
"svelte@5.53.0_acorn@8.16.0": {
"integrity": "sha512-7dhHkSamGS2vtoBmIW2hRab+gl5Z60alEHZB4910ePqqJNxAWnDAxsofVmlZ2tREmWyHNE+A1nCKwICAquoD2A==",
"dependencies": [
"@jridgewell/remapping",
"@jridgewell/sourcemap-codec",
"@sveltejs/acorn-typescript",
"@types/estree",
"@types/trusted-types",
"acorn",
"aria-query",
"axobject-query",
"clsx",
"devalue",
"esm-env",
"esrap",
"is-reference",
"locate-character",
"magic-string",
"zimmerframe"
]
},
"tinyglobby@0.2.15_picomatch@4.0.3": {
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"dependencies": [
"fdir",
"picomatch"
]
},
"totalist@3.0.1": {
"integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="
},
"typescript@5.9.3": {
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"bin": true
},
"vite@7.3.1_picomatch@4.0.3": {
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
"dependencies": [
"esbuild",
"fdir",
"picomatch",
"postcss",
"rollup",
"tinyglobby"
],
"optionalDependencies": [
"fsevents"
],
"bin": true
},
"vitefu@1.1.1_vite@7.3.1__picomatch@4.0.3": {
"integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==",
"dependencies": [
"vite"
],
"optionalPeers": [
"vite"
]
},
"zimmerframe@1.1.4": {
"integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="
}
},
"workspace": {
"packageJson": {
"dependencies": [
"npm:@lucide/svelte@0.575",
"npm:@sveltejs/adapter-static@^3.0.10",
"npm:@sveltejs/kit@^2.50.2",
"npm:@sveltejs/vite-plugin-svelte@^6.2.4",
"npm:svelte-check@^4.3.6",
"npm:svelte@^5.51.0",
"npm:typescript@^5.9.3",
"npm:vite@^7.3.1"
]
}
}
}

View file

@ -0,0 +1,24 @@
{
"name": "frontend",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"prepare": "svelte-kit sync || echo ''",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
},
"devDependencies": {
"@lucide/svelte": "^0.575.0",
"@sveltejs/adapter-static": "^3.0.10",
"@sveltejs/kit": "^2.50.2",
"@sveltejs/vite-plugin-svelte": "^6.2.4",
"svelte": "^5.51.0",
"svelte-check": "^4.3.6",
"typescript": "^5.9.3",
"vite": "^7.3.1"
}
}

View file

@ -0,0 +1,62 @@
:root {
--sp-2: 2px;
--sp-4: 4px;
--sp-8: 8px;
--sp-16: 16px;
--sp-32: 32px;
--sp-48: 48px;
--sp-64: 64px;
--sp-ssm: var(--sp-2);
--sp-sm: var(--sp-4);
--sp-md: var(--sp-8);
--sp-lg: var(--sp-16);
--sp-xl: var(--sp-32);
--sp-xxl: var(--sp-48);
--sp-xxxl: var(--sp-64);
--sz-member-list: 270px;
--radius-0: var(--sp-sm);
--radius-1: calc(var(--radius-0) * 2);
--radius-2: calc(var(--radius-0) * 3);
--radius-4: calc(var(--radius-0) * 4);
--bg-0: #f8f8f8;
--bg-1: #f0f0f0;
--bg-2: #d0d0d0;
--bg-3: #b0b0b0;
--bg-4: #a0a0a0;
--on-bg-0: #101010;
--on-bg-1: #202020;
--on-bg-2: #303030;
--on-bg-3: #606060;
--on-bg-4: #808080;
--brand: #fffcf7;
--on-brand: #322e1f;
--primary: #f7eddb;
--on-primary: #92a40a;
--info: #1a94ff;
--success: #92a40a;
--warning: #ffc107;
--error: #d42c2c;
}
body {
width: 100vw;
min-height: 100vh;
margin: 0;
padding: 0;
font-family: "Inter", "Arial", sans-serif;
color: var(--on-bg-1);
background-color: var(--bg-1);
}
hr {
border: none;
border-bottom: 1px solid var(--bg-2);
}

13
src/web/frontend/src/app.d.ts vendored Normal file
View file

@ -0,0 +1,13 @@
// See https://svelte.dev/docs/kit/types#app.d.ts
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface PageState {}
// interface Platform {}
}
}
export {};

View file

@ -0,0 +1,11 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo</title><path d="M94.157 22.819c-10.4-14.885-30.94-19.297-45.792-9.835L22.282 29.608A29.92 29.92 0 0 0 8.764 49.65a31.5 31.5 0 0 0 3.108 20.231 30 30 0 0 0-4.477 11.183 31.9 31.9 0 0 0 5.448 24.116c10.402 14.887 30.942 19.297 45.791 9.835l26.083-16.624A29.92 29.92 0 0 0 98.235 78.35a31.53 31.53 0 0 0-3.105-20.232 30 30 0 0 0 4.474-11.182 31.88 31.88 0 0 0-5.447-24.116" style="fill:#ff3e00"/><path d="M45.817 106.582a20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.503 18 18 0 0 1 .624-2.435l.49-1.498 1.337.981a33.6 33.6 0 0 0 10.203 5.098l.97.294-.09.968a5.85 5.85 0 0 0 1.052 3.878 6.24 6.24 0 0 0 6.695 2.485 5.8 5.8 0 0 0 1.603-.704L69.27 76.28a5.43 5.43 0 0 0 2.45-3.631 5.8 5.8 0 0 0-.987-4.371 6.24 6.24 0 0 0-6.698-2.487 5.7 5.7 0 0 0-1.6.704l-9.953 6.345a19 19 0 0 1-5.296 2.326 20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.502 17.99 17.99 0 0 1 8.13-12.052l26.081-16.623a19 19 0 0 1 5.3-2.329 20.72 20.72 0 0 1 22.237 8.243 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-.624 2.435l-.49 1.498-1.337-.98a33.6 33.6 0 0 0-10.203-5.1l-.97-.294.09-.968a5.86 5.86 0 0 0-1.052-3.878 6.24 6.24 0 0 0-6.696-2.485 5.8 5.8 0 0 0-1.602.704L37.73 51.72a5.42 5.42 0 0 0-2.449 3.63 5.79 5.79 0 0 0 .986 4.372 6.24 6.24 0 0 0 6.698 2.486 5.8 5.8 0 0 0 1.602-.704l9.952-6.342a19 19 0 0 1 5.295-2.328 20.72 20.72 0 0 1 22.237 8.242 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-8.13 12.053l-26.081 16.622a19 19 0 0 1-5.3 2.328" style="fill:#fff"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,117 @@
<script lang="ts">
import { Account } from '@jupiter/lib/account';
import { User } from '@lucide/svelte';
let {
account,
} = $props<{
account: Account,
}>();
</script>
<div class="account">
<div class="icon-container"><User /></div>
<hr>
<div class="info-container">
<h2 class="name">{account.username}</h2>
<p class="email">{account.username}@{account.domain}</p>
</div>
</div>
<style>
.account {
height: var(--sp-xxxl);
padding: .5em;
display: flex;
flex-direction: row;
background-color: var(--bg-0);
border: 1px solid var(--bg-2);
border-radius: var(--radius-0);
transition: background-color .1s, border-color .1s;
cursor: pointer;
user-select: none;
}
.account:hover {
background-color: var(--bg-1);
border-color: var(--bg-3);
}
.account:active:not(:has(.email:active)) {
background-color: var(--bg-2);
}
.icon-container {
width: var(--sp-xxxl);
min-width: var(--sp-xxxl);
height: var(--sp-xxxl);
display: flex;
justify-content: center;
align-items: center;
}
.icon-container :global(svg) {
--size: 60%;
width: var(--size);
height: var(--size);
}
.account hr {
margin: 0 .5em;
border: none;
border-left: 1px solid var(--bg-2);
}
.info-container {
width: 100%;
display: grid;
grid-template-rows: 1fr 1fr;
}
.info-container .name {
margin: 0;
font-size: 1.2em;
line-height: 1.6em;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.info-container .email {
width: fit-content;
margin: 0;
padding: .2em;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
font-family: monospace;
font-size: 1.2em;
line-height: 1.6em;
color: var(--on-bg-3);
background-color: var(--bg-1);
border: 1px solid var(--bg-2);
border-radius: var(--radius-0);
transition: background-color .1s, border-color .1s;
cursor: pointer;
}
.email:hover {
background-color: var(--bg-2);
border-color: var(--bg-3);
}
.email:active {
background-color: var(--bg-3);
}
</style>

View file

@ -0,0 +1,27 @@
<script lang="ts">
import { type Icon } from '@lucide/svelte';
let { children } = $props();
</script>
<header>{@render children()}</header>
<style>
header {
padding: 1em 1.5em;
display: flex;
flex-direction: row;
justify-content: start;
align-items: center;
gap: .5em;
border-bottom: 1px solid var(--bg-2);
user-select: none;
}
header :global(h1) {
margin: 0;
}
</style>

View file

@ -0,0 +1,85 @@
<script lang="ts">
import { X } from '@lucide/svelte';
import { createEventDispatcher } from "svelte";
import { fade, fly } from "svelte/transition";
import { ToastTypeToString } from '@jupiter/lib/toasts';
let {
text,
type,
sticky = false,
onclose,
} = $props<{
text: string,
type: "info" | "success" | "warning" | "error",
sticky: boolean,
onclose?: Function,
}>();
const dispatch = createEventDispatcher();
</script>
<div class={["notification", ToastTypeToString(type)]} in:fly={{ x: 100 }} out:fade>
<span class="content">{text}</span>
{#if sticky}
<div class="close"><X onclick={() => {dispatch("dismiss")}} /></div>
{/if}
</div>
<style>
.notification {
padding: 1em 1.5em;
display: flex;
gap: .2em;
background-color: var(--bg-0);
border: 1px solid var(--bg-2);
border-left: var(--sp-sm) solid var(--bg-2);
border-radius: var(--radius-0);
box-shadow: 0 var(--sp-sm) var(--sp-md) #0001;
/*animation: slide-in 1s cubic-bezier(0,1.2,.5,1) forwards;*/
}
.close {
height: 1em;
transform: translate(5px, 2px);
}
.close :global(svg) {
width: 1em;
height: 1em;
margin: -.2em;
padding: .2em;
border-radius: 1em;
}
.close:hover :global(svg) {
background: var(--bg-1);
}
.close:active :global(svg) {
background: var(--bg-2);
}
.notification:global(.info) {
border-color: var(--info);
}
.notification:global(.success) {
border-color: var(--success);
}
.notification:global(.warning) {
border-color: var(--warning);
}
.notification:global(.error) {
border-color: var(--error);
}
@keyframes slide-in {
from { transform: translateX(calc(100% + 2em)) }
}
</style>

View file

@ -0,0 +1,43 @@
<script lang="ts">
import Toast from '@jupiter/components/ui/Toast.svelte';
import { type Toast as IToast, dismissToast, toasts } from '@jupiter/lib/toasts';
</script>
<div class="toast-overlay">
{#if $toasts}
{#each $toasts as toast, i}
<Toast
text={toast.text}
type={toast.type}
sticky={toast.sticky}
on:dismiss={() => {dismissToast(toast.id)}}
/>
{/each}
{/if}
</div>
<style>
.toast-overlay {
position: fixed;
top: 0;
left: 0;
--padding: 2em;
width: calc(100vw - var(--padding) * 2);
height: calc(100vh - var(--padding) * 2);
padding: var(--padding);
display: flex;
flex-direction: column;
justify-content: end;
align-items: end;
gap: .5em;
pointer-events: none;
z-index: 999;
}
.toast-overlay :global(*) {
pointer-events: initial;
}
</style>

View file

@ -0,0 +1,5 @@
export interface Account {
username: string,
domain: string,
mail_directory: string,
}

View file

@ -0,0 +1,21 @@
import type { Account } from "./accounts.ts";
let token = "";
export function fetchAccounts(): Promise<Account[]> {
return new Promise((res) => {
setTimeout(() => {
res([
{
username: "ari",
domain: "example.org",
mail_directory: `/var/mail/example.org/ari/home`,
},
{
username: "testbanger",
domain: "gmail.com",
mail_directory: `/var/mail/example.org/ari/home`,
}
]);
}, 300)
})
}

View file

@ -0,0 +1 @@
// place files you want to import through the `$lib` alias in this folder.

View file

@ -0,0 +1,72 @@
export enum ToastType {
INFO,
SUCCESS,
WARNING,
ERROR,
}
export interface Toast {
id: number,
text: string,
type: ToastType,
sticky: boolean,
timeout: number,
}
import { writable } from "svelte/store";
export const DEFAULT_NOTIFICATION_TIMEOUT = 5000;
export const toasts = writable<Toast[]>([]);
export function pushToast(toast: Record<string, any>) {
const id = Math.floor(Math.random() * 1_000_000);
const defaults: Toast = {
id,
text: "",
type: ToastType.INFO,
sticky: false,
timeout: DEFAULT_NOTIFICATION_TIMEOUT,
};
toast = { ...defaults, ...toast };
console.log(`adding toast ${toast.id} (timeout ${toast.timeout})...`);
toasts.update((toasts) => [...toasts, { ...defaults, ...toast }]);
if (!toast.sticky) setTimeout(() => { dismissToast(id) }, toast.timeout);
}
export function dismissToast(id: number) {
console.log(`dismissing toast ${id}...`);
toasts.update((toasts) => toasts.filter((toast) => toast.id !== id));
}
export function ToastTypeToString(type: ToastType) {
switch(type) {
case ToastType.INFO:
return "info";
case ToastType.SUCCESS:
return "success";
case ToastType.WARNING:
return "warning";
case ToastType.ERROR:
return "error";
default:
return "info";
}
}
export function ToastTypeToEnum(type: string) {
switch(type) {
case "info":
return ToastType.INFO;
case "success":
return ToastType.SUCCESS;
case "warning":
return ToastType.WARNING;
case "error":
return ToastType.ERROR;
default:
return ToastType.INFO;
}
}

View file

@ -0,0 +1,95 @@
<script lang="ts">
import '@jupiter/app.css';
import favicon from '@jupiter/assets/favicon.svg';
import { LayoutDashboard, Mail, Settings, User } from '@lucide/svelte';
import { onMount } from 'svelte';
import ToastOverlay from '@jupiter/components/ui/ToastOverlay.svelte';
let { children } = $props();
</script>
<svelte:head>
<link rel="icon" href={favicon} />
</svelte:head>
<div class="app">
<header class="sidebar">
<h1><Mail /> Jupiter Mail</h1>
<a href="/"><LayoutDashboard /> Dashboard</a>
<a href="/accounts"><User /> Accounts</a>
<a href="/settings"><Settings /> Settings</a>
</header>
<main>
{@render children()}
</main>
<ToastOverlay />
</div>
<style>
.app {
width: 100vw;
min-height: 100vh;
display: grid;
grid-template-columns: 15em auto;
}
.sidebar {
height: calc(100% - 2em);
padding: 1em;
display: flex;
flex-direction: column;
gap: .5em;
overflow-y: scroll;
background-color: var(--bg-1);
border-right: 1px solid var(--bg-2);
z-index: 100;
}
.sidebar h1 {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: .5em;
font-size: 1.5em;
user-select: none;
}
.sidebar a {
margin-left: var(--sp-sm);
padding: .5em;
display: flex;
flex-direction: row;
justify-content: start;
align-items: center;
gap: .5em;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
color: var(--on-primary);
background-color: var(--bg-0);
text-decoration: none;
border: 1px solid var(--bg-2);
border-radius: var(--radius-0);
box-shadow: calc(0px - var(--sp-sm)) 0 0 var(--on-primary);
user-select: none;
}
.sidebar a:hover {
background-color: var(--bg-1);
}
.sidebar a:active {
background-color: var(--bg-2);
}
</style>

View file

@ -0,0 +1,19 @@
<script lang="ts">
import { LayoutDashboard } from '@lucide/svelte';
import Header from '@jupiter/components/ui/Header.svelte';
</script>
<div class="page-container">
<Header>
<LayoutDashboard />
<h1>Dashboard</h1>
</Header>
</div>
<style>
.page-container {
min-height: 100%;
background-color: var(--bg-0);
}
</style>

View file

@ -0,0 +1,55 @@
<script lang="ts">
import { User } from '@lucide/svelte';
import { onMount } from 'svelte';
import Header from '@jupiter/components/ui/Header.svelte';
import AccountListItem from '@jupiter/components/ui/AccountListItem.svelte';
import { type Account } from '@jupiter/lib/account';
import * as api from '@jupiter/lib/api';
import { pushToast, ToastType } from '@jupiter/lib/toasts';
let accounts: Account[] = $state([]);
onMount(async () => {
pushToast({ text: "Fetching accounts...", type: ToastType.INFO });
accounts = await api.fetchAccounts();
pushToast({ text: "Accounts loaded successfully.", type: ToastType.SUCCESS });
});
</script>
<div class="page-container">
<Header>
<User />
<h1>Accounts</h1>
</Header>
<main>
<p>Click an account below to configure:</p>
<hr>
<div class="account-list">
{#each accounts as account}
<AccountListItem account={account} />
{/each}
</div>
</main>
</div>
<style>
.page-container {
min-height: 100%;
background-color: var(--bg-0);
}
main {
padding: 2em;
}
.account-list {
margin: 0;
padding: 0;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1em;
}
</style>

View file

@ -0,0 +1,19 @@
<script lang="ts">
import { Settings } from '@lucide/svelte';
import Header from '@jupiter/components/ui/Header.svelte';
</script>
<div class="page-container">
<Header>
<Settings />
<h1>Settings</h1>
</Header>
</div>
<style>
.page-container {
min-height: 100%;
background-color: var(--bg-0);
}
</style>

View file

@ -0,0 +1,3 @@
# allow crawling everything by default
User-agent: *
Disallow:

View file

@ -0,0 +1,13 @@
import adapter from '@sveltejs/adapter-static';
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
adapter: adapter(),
alias: {
'@jupiter/*': './src',
}
},
};
export default config;

View file

@ -0,0 +1,20 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"rewriteRelativeImportExtensions": true,
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "nodenext"
}
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
//
// To make changes to top-level options such as include and exclude, we recommend extending
// the generated config; see https://svelte.dev/docs/kit/configuration#typescript
}

View file

@ -0,0 +1,6 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit()]
});