A photo manager for VRChat.

change styles, fix filters

+3 -1
changelog
··· 99 - Fixed some icons not fading out when moving to the settings menu 100 - Removed the auto updater 101 - Removed account login stuff 102 - - Fixed app name on windows
··· 99 - Fixed some icons not fading out when moving to the settings menu 100 - Removed the auto updater 101 - Removed account login stuff 102 + - Fixed app name on windows 103 + - Update styles 104 + - Fixed filters removing photos without metadata
+1 -1
package.json
··· 18 "@tauri-apps/plugin-process": "2.0.0-rc.0", 19 "@tauri-apps/plugin-shell": "2.0.0-rc.0", 20 "@types/animejs": "^3.1.13", 21 - "animejs": "^3.2.2", 22 "solid-js": "^1.9.9" 23 }, 24 "devDependencies": {
··· 18 "@tauri-apps/plugin-process": "2.0.0-rc.0", 19 "@tauri-apps/plugin-shell": "2.0.0-rc.0", 20 "@types/animejs": "^3.1.13", 21 + "animejs": "^4.1.3", 22 "solid-js": "^1.9.9" 23 }, 24 "devDependencies": {
+105 -105
pnpm-lock.yaml
··· 27 specifier: ^3.1.13 28 version: 3.1.13 29 animejs: 30 - specifier: ^3.2.2 31 - version: 3.2.2 32 solid-js: 33 specifier: ^1.9.9 34 version: 1.9.9 ··· 284 '@jridgewell/trace-mapping@0.3.30': 285 resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} 286 287 - '@rollup/rollup-android-arm-eabi@4.46.2': 288 - resolution: {integrity: sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==} 289 cpu: [arm] 290 os: [android] 291 292 - '@rollup/rollup-android-arm64@4.46.2': 293 - resolution: {integrity: sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==} 294 cpu: [arm64] 295 os: [android] 296 297 - '@rollup/rollup-darwin-arm64@4.46.2': 298 - resolution: {integrity: sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==} 299 cpu: [arm64] 300 os: [darwin] 301 302 - '@rollup/rollup-darwin-x64@4.46.2': 303 - resolution: {integrity: sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==} 304 cpu: [x64] 305 os: [darwin] 306 307 - '@rollup/rollup-freebsd-arm64@4.46.2': 308 - resolution: {integrity: sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==} 309 cpu: [arm64] 310 os: [freebsd] 311 312 - '@rollup/rollup-freebsd-x64@4.46.2': 313 - resolution: {integrity: sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==} 314 cpu: [x64] 315 os: [freebsd] 316 317 - '@rollup/rollup-linux-arm-gnueabihf@4.46.2': 318 - resolution: {integrity: sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==} 319 cpu: [arm] 320 os: [linux] 321 322 - '@rollup/rollup-linux-arm-musleabihf@4.46.2': 323 - resolution: {integrity: sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==} 324 cpu: [arm] 325 os: [linux] 326 327 - '@rollup/rollup-linux-arm64-gnu@4.46.2': 328 - resolution: {integrity: sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==} 329 cpu: [arm64] 330 os: [linux] 331 332 - '@rollup/rollup-linux-arm64-musl@4.46.2': 333 - resolution: {integrity: sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==} 334 cpu: [arm64] 335 os: [linux] 336 337 - '@rollup/rollup-linux-loongarch64-gnu@4.46.2': 338 - resolution: {integrity: sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==} 339 cpu: [loong64] 340 os: [linux] 341 342 - '@rollup/rollup-linux-ppc64-gnu@4.46.2': 343 - resolution: {integrity: sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==} 344 cpu: [ppc64] 345 os: [linux] 346 347 - '@rollup/rollup-linux-riscv64-gnu@4.46.2': 348 - resolution: {integrity: sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==} 349 cpu: [riscv64] 350 os: [linux] 351 352 - '@rollup/rollup-linux-riscv64-musl@4.46.2': 353 - resolution: {integrity: sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==} 354 cpu: [riscv64] 355 os: [linux] 356 357 - '@rollup/rollup-linux-s390x-gnu@4.46.2': 358 - resolution: {integrity: sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==} 359 cpu: [s390x] 360 os: [linux] 361 362 - '@rollup/rollup-linux-x64-gnu@4.46.2': 363 - resolution: {integrity: sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==} 364 cpu: [x64] 365 os: [linux] 366 367 - '@rollup/rollup-linux-x64-musl@4.46.2': 368 - resolution: {integrity: sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==} 369 cpu: [x64] 370 os: [linux] 371 372 - '@rollup/rollup-win32-arm64-msvc@4.46.2': 373 - resolution: {integrity: sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==} 374 cpu: [arm64] 375 os: [win32] 376 377 - '@rollup/rollup-win32-ia32-msvc@4.46.2': 378 - resolution: {integrity: sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==} 379 cpu: [ia32] 380 os: [win32] 381 382 - '@rollup/rollup-win32-x64-msvc@4.46.2': 383 - resolution: {integrity: sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==} 384 cpu: [x64] 385 os: [win32] 386 ··· 388 resolution: {integrity: sha512-v454Qs3REHc3Za59U+/eSmBsdmF+3NE5+76+lFDaitVqN4ZglDHENDaMARYKGJVZuxiSkzyqG0SeG7lLQjVkPA==} 389 engines: {node: '>= 18.18', npm: '>= 6.6.0', yarn: '>= 1.19.1'} 390 391 - '@tauri-apps/api@2.7.0': 392 - resolution: {integrity: sha512-v7fVE8jqBl8xJFOcBafDzXFc8FnicoH3j8o8DNNs0tHuEBmXUDqrCOAzMRX0UkfpwqZLqvrvK0GNQ45DfnoVDg==} 393 394 '@tauri-apps/cli-darwin-arm64@2.0.0-rc.5': 395 resolution: {integrity: sha512-EoduJ5SeMfBKCe7I291JBH+lkrf2E0+mQF1rP+Jq4CjWPer11OeEcUSFtHURB3Z3ItzObQ7ALPulMGhMe6E9rg==} ··· 486 '@types/estree@1.0.8': 487 resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} 488 489 - animejs@3.2.2: 490 - resolution: {integrity: sha512-Ao95qWLpDPXXM+WrmwcKbl6uNlC5tjnowlaRYtuVDHHoygjtIPfDUoK9NthrlZsQSKjZXlmji2TrBUAVbiH0LQ==} 491 492 babel-plugin-jsx-dom-expressions@0.40.1: 493 resolution: {integrity: sha512-b4iHuirqK7RgaMzB2Lsl7MqrlDgQtVRSSazyrmx7wB3T759ggGjod5Rkok5MfHjQXhR7tRPmdwoeGPqBnW2KfA==} ··· 503 solid-js: 504 optional: true 505 506 - browserslist@4.25.2: 507 - resolution: {integrity: sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==} 508 engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} 509 hasBin: true 510 ··· 526 supports-color: 527 optional: true 528 529 - electron-to-chromium@1.5.202: 530 - resolution: {integrity: sha512-NxbYjRmiHcHXV1Ws3fWUW+SLb62isauajk45LUJ/HgIOkUA7jLZu/X2Iif+X9FBNK8QkF9Zb4Q2mcwXCcY30mg==} 531 532 entities@6.0.1: 533 resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} ··· 599 resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} 600 engines: {node: ^10 || ^12 || >=14} 601 602 - rollup@4.46.2: 603 - resolution: {integrity: sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==} 604 engines: {node: '>=18.0.0', npm: '>=8.0.0'} 605 hasBin: true 606 ··· 743 dependencies: 744 '@babel/compat-data': 7.28.0 745 '@babel/helper-validator-option': 7.27.1 746 - browserslist: 4.25.2 747 lru-cache: 5.1.1 748 semver: 6.3.1 749 ··· 897 '@jridgewell/resolve-uri': 3.1.2 898 '@jridgewell/sourcemap-codec': 1.5.5 899 900 - '@rollup/rollup-android-arm-eabi@4.46.2': 901 optional: true 902 903 - '@rollup/rollup-android-arm64@4.46.2': 904 optional: true 905 906 - '@rollup/rollup-darwin-arm64@4.46.2': 907 optional: true 908 909 - '@rollup/rollup-darwin-x64@4.46.2': 910 optional: true 911 912 - '@rollup/rollup-freebsd-arm64@4.46.2': 913 optional: true 914 915 - '@rollup/rollup-freebsd-x64@4.46.2': 916 optional: true 917 918 - '@rollup/rollup-linux-arm-gnueabihf@4.46.2': 919 optional: true 920 921 - '@rollup/rollup-linux-arm-musleabihf@4.46.2': 922 optional: true 923 924 - '@rollup/rollup-linux-arm64-gnu@4.46.2': 925 optional: true 926 927 - '@rollup/rollup-linux-arm64-musl@4.46.2': 928 optional: true 929 930 - '@rollup/rollup-linux-loongarch64-gnu@4.46.2': 931 optional: true 932 933 - '@rollup/rollup-linux-ppc64-gnu@4.46.2': 934 optional: true 935 936 - '@rollup/rollup-linux-riscv64-gnu@4.46.2': 937 optional: true 938 939 - '@rollup/rollup-linux-riscv64-musl@4.46.2': 940 optional: true 941 942 - '@rollup/rollup-linux-s390x-gnu@4.46.2': 943 optional: true 944 945 - '@rollup/rollup-linux-x64-gnu@4.46.2': 946 optional: true 947 948 - '@rollup/rollup-linux-x64-musl@4.46.2': 949 optional: true 950 951 - '@rollup/rollup-win32-arm64-msvc@4.46.2': 952 optional: true 953 954 - '@rollup/rollup-win32-ia32-msvc@4.46.2': 955 optional: true 956 957 - '@rollup/rollup-win32-x64-msvc@4.46.2': 958 optional: true 959 960 '@tauri-apps/api@2.0.0-rc.0': {} 961 962 - '@tauri-apps/api@2.7.0': {} 963 964 '@tauri-apps/cli-darwin-arm64@2.0.0-rc.5': 965 optional: true ··· 1006 1007 '@tauri-apps/plugin-deep-link@2.4.1': 1008 dependencies: 1009 - '@tauri-apps/api': 2.7.0 1010 1011 '@tauri-apps/plugin-http@2.0.0-rc.1': 1012 dependencies: 1013 - '@tauri-apps/api': 2.7.0 1014 1015 '@tauri-apps/plugin-process@2.0.0-rc.0': 1016 dependencies: ··· 1045 1046 '@types/estree@1.0.8': {} 1047 1048 - animejs@3.2.2: {} 1049 1050 babel-plugin-jsx-dom-expressions@0.40.1(@babel/core@7.28.3): 1051 dependencies: ··· 1064 optionalDependencies: 1065 solid-js: 1.9.9 1066 1067 - browserslist@4.25.2: 1068 dependencies: 1069 caniuse-lite: 1.0.30001735 1070 - electron-to-chromium: 1.5.202 1071 node-releases: 2.0.19 1072 - update-browserslist-db: 1.1.3(browserslist@4.25.2) 1073 1074 caniuse-lite@1.0.30001735: {} 1075 ··· 1081 dependencies: 1082 ms: 2.1.3 1083 1084 - electron-to-chromium@1.5.202: {} 1085 1086 entities@6.0.1: {} 1087 ··· 1154 picocolors: 1.1.1 1155 source-map-js: 1.2.1 1156 1157 - rollup@4.46.2: 1158 dependencies: 1159 '@types/estree': 1.0.8 1160 optionalDependencies: 1161 - '@rollup/rollup-android-arm-eabi': 4.46.2 1162 - '@rollup/rollup-android-arm64': 4.46.2 1163 - '@rollup/rollup-darwin-arm64': 4.46.2 1164 - '@rollup/rollup-darwin-x64': 4.46.2 1165 - '@rollup/rollup-freebsd-arm64': 4.46.2 1166 - '@rollup/rollup-freebsd-x64': 4.46.2 1167 - '@rollup/rollup-linux-arm-gnueabihf': 4.46.2 1168 - '@rollup/rollup-linux-arm-musleabihf': 4.46.2 1169 - '@rollup/rollup-linux-arm64-gnu': 4.46.2 1170 - '@rollup/rollup-linux-arm64-musl': 4.46.2 1171 - '@rollup/rollup-linux-loongarch64-gnu': 4.46.2 1172 - '@rollup/rollup-linux-ppc64-gnu': 4.46.2 1173 - '@rollup/rollup-linux-riscv64-gnu': 4.46.2 1174 - '@rollup/rollup-linux-riscv64-musl': 4.46.2 1175 - '@rollup/rollup-linux-s390x-gnu': 4.46.2 1176 - '@rollup/rollup-linux-x64-gnu': 4.46.2 1177 - '@rollup/rollup-linux-x64-musl': 4.46.2 1178 - '@rollup/rollup-win32-arm64-msvc': 4.46.2 1179 - '@rollup/rollup-win32-ia32-msvc': 4.46.2 1180 - '@rollup/rollup-win32-x64-msvc': 4.46.2 1181 fsevents: 2.3.3 1182 1183 semver@6.3.1: {} ··· 1207 1208 typescript@5.9.2: {} 1209 1210 - update-browserslist-db@1.1.3(browserslist@4.25.2): 1211 dependencies: 1212 - browserslist: 4.25.2 1213 escalade: 3.2.0 1214 picocolors: 1.1.1 1215 ··· 1232 dependencies: 1233 esbuild: 0.21.5 1234 postcss: 8.5.6 1235 - rollup: 4.46.2 1236 optionalDependencies: 1237 fsevents: 2.3.3 1238
··· 27 specifier: ^3.1.13 28 version: 3.1.13 29 animejs: 30 + specifier: ^4.1.3 31 + version: 4.1.3 32 solid-js: 33 specifier: ^1.9.9 34 version: 1.9.9 ··· 284 '@jridgewell/trace-mapping@0.3.30': 285 resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} 286 287 + '@rollup/rollup-android-arm-eabi@4.46.3': 288 + resolution: {integrity: sha512-UmTdvXnLlqQNOCJnyksjPs1G4GqXNGW1LrzCe8+8QoaLhhDeTXYBgJ3k6x61WIhlHX2U+VzEJ55TtIjR/HTySA==} 289 cpu: [arm] 290 os: [android] 291 292 + '@rollup/rollup-android-arm64@4.46.3': 293 + resolution: {integrity: sha512-8NoxqLpXm7VyeI0ocidh335D6OKT0UJ6fHdnIxf3+6oOerZZc+O7r+UhvROji6OspyPm+rrIdb1gTXtVIqn+Sg==} 294 cpu: [arm64] 295 os: [android] 296 297 + '@rollup/rollup-darwin-arm64@4.46.3': 298 + resolution: {integrity: sha512-csnNavqZVs1+7/hUKtgjMECsNG2cdB8F7XBHP6FfQjqhjF8rzMzb3SLyy/1BG7YSfQ+bG75Ph7DyedbUqwq1rA==} 299 cpu: [arm64] 300 os: [darwin] 301 302 + '@rollup/rollup-darwin-x64@4.46.3': 303 + resolution: {integrity: sha512-r2MXNjbuYabSIX5yQqnT8SGSQ26XQc8fmp6UhlYJd95PZJkQD1u82fWP7HqvGUf33IsOC6qsiV+vcuD4SDP6iw==} 304 cpu: [x64] 305 os: [darwin] 306 307 + '@rollup/rollup-freebsd-arm64@4.46.3': 308 + resolution: {integrity: sha512-uluObTmgPJDuJh9xqxyr7MV61Imq+0IvVsAlWyvxAaBSNzCcmZlhfYcRhCdMaCsy46ccZa7vtDDripgs9Jkqsw==} 309 cpu: [arm64] 310 os: [freebsd] 311 312 + '@rollup/rollup-freebsd-x64@4.46.3': 313 + resolution: {integrity: sha512-AVJXEq9RVHQnejdbFvh1eWEoobohUYN3nqJIPI4mNTMpsyYN01VvcAClxflyk2HIxvLpRcRggpX1m9hkXkpC/A==} 314 cpu: [x64] 315 os: [freebsd] 316 317 + '@rollup/rollup-linux-arm-gnueabihf@4.46.3': 318 + resolution: {integrity: sha512-byyflM+huiwHlKi7VHLAYTKr67X199+V+mt1iRgJenAI594vcmGGddWlu6eHujmcdl6TqSNnvqaXJqZdnEWRGA==} 319 cpu: [arm] 320 os: [linux] 321 322 + '@rollup/rollup-linux-arm-musleabihf@4.46.3': 323 + resolution: {integrity: sha512-aLm3NMIjr4Y9LklrH5cu7yybBqoVCdr4Nvnm8WB7PKCn34fMCGypVNpGK0JQWdPAzR/FnoEoFtlRqZbBBLhVoQ==} 324 cpu: [arm] 325 os: [linux] 326 327 + '@rollup/rollup-linux-arm64-gnu@4.46.3': 328 + resolution: {integrity: sha512-VtilE6eznJRDIoFOzaagQodUksTEfLIsvXymS+UdJiSXrPW7Ai+WG4uapAc3F7Hgs791TwdGh4xyOzbuzIZrnw==} 329 cpu: [arm64] 330 os: [linux] 331 332 + '@rollup/rollup-linux-arm64-musl@4.46.3': 333 + resolution: {integrity: sha512-dG3JuS6+cRAL0GQ925Vppafi0qwZnkHdPeuZIxIPXqkCLP02l7ka+OCyBoDEv8S+nKHxfjvjW4OZ7hTdHkx8/w==} 334 cpu: [arm64] 335 os: [linux] 336 337 + '@rollup/rollup-linux-loongarch64-gnu@4.46.3': 338 + resolution: {integrity: sha512-iU8DxnxEKJptf8Vcx4XvAUdpkZfaz0KWfRrnIRrOndL0SvzEte+MTM7nDH4A2Now4FvTZ01yFAgj6TX/mZl8hQ==} 339 cpu: [loong64] 340 os: [linux] 341 342 + '@rollup/rollup-linux-ppc64-gnu@4.46.3': 343 + resolution: {integrity: sha512-VrQZp9tkk0yozJoQvQcqlWiqaPnLM6uY1qPYXvukKePb0fqaiQtOdMJSxNFUZFsGw5oA5vvVokjHrx8a9Qsz2A==} 344 cpu: [ppc64] 345 os: [linux] 346 347 + '@rollup/rollup-linux-riscv64-gnu@4.46.3': 348 + resolution: {integrity: sha512-uf2eucWSUb+M7b0poZ/08LsbcRgaDYL8NCGjUeFMwCWFwOuFcZ8D9ayPl25P3pl+D2FH45EbHdfyUesQ2Lt9wA==} 349 cpu: [riscv64] 350 os: [linux] 351 352 + '@rollup/rollup-linux-riscv64-musl@4.46.3': 353 + resolution: {integrity: sha512-7tnUcDvN8DHm/9ra+/nF7lLzYHDeODKKKrh6JmZejbh1FnCNZS8zMkZY5J4sEipy2OW1d1Ncc4gNHUd0DLqkSg==} 354 cpu: [riscv64] 355 os: [linux] 356 357 + '@rollup/rollup-linux-s390x-gnu@4.46.3': 358 + resolution: {integrity: sha512-MUpAOallJim8CsJK+4Lc9tQzlfPbHxWDrGXZm2z6biaadNpvh3a5ewcdat478W+tXDoUiHwErX/dOql7ETcLqg==} 359 cpu: [s390x] 360 os: [linux] 361 362 + '@rollup/rollup-linux-x64-gnu@4.46.3': 363 + resolution: {integrity: sha512-F42IgZI4JicE2vM2PWCe0N5mR5vR0gIdORPqhGQ32/u1S1v3kLtbZ0C/mi9FFk7C5T0PgdeyWEPajPjaUpyoKg==} 364 cpu: [x64] 365 os: [linux] 366 367 + '@rollup/rollup-linux-x64-musl@4.46.3': 368 + resolution: {integrity: sha512-oLc+JrwwvbimJUInzx56Q3ujL3Kkhxehg7O1gWAYzm8hImCd5ld1F2Gry5YDjR21MNb5WCKhC9hXgU7rRlyegQ==} 369 cpu: [x64] 370 os: [linux] 371 372 + '@rollup/rollup-win32-arm64-msvc@4.46.3': 373 + resolution: {integrity: sha512-lOrQ+BVRstruD1fkWg9yjmumhowR0oLAAzavB7yFSaGltY8klttmZtCLvOXCmGE9mLIn8IBV/IFrQOWz5xbFPg==} 374 cpu: [arm64] 375 os: [win32] 376 377 + '@rollup/rollup-win32-ia32-msvc@4.46.3': 378 + resolution: {integrity: sha512-vvrVKPRS4GduGR7VMH8EylCBqsDcw6U+/0nPDuIjXQRbHJc6xOBj+frx8ksfZAh6+Fptw5wHrN7etlMmQnPQVg==} 379 cpu: [ia32] 380 os: [win32] 381 382 + '@rollup/rollup-win32-x64-msvc@4.46.3': 383 + resolution: {integrity: sha512-fi3cPxCnu3ZeM3EwKZPgXbWoGzm2XHgB/WShKI81uj8wG0+laobmqy5wbgEwzstlbLu4MyO8C19FyhhWseYKNQ==} 384 cpu: [x64] 385 os: [win32] 386 ··· 388 resolution: {integrity: sha512-v454Qs3REHc3Za59U+/eSmBsdmF+3NE5+76+lFDaitVqN4ZglDHENDaMARYKGJVZuxiSkzyqG0SeG7lLQjVkPA==} 389 engines: {node: '>= 18.18', npm: '>= 6.6.0', yarn: '>= 1.19.1'} 390 391 + '@tauri-apps/api@2.8.0': 392 + resolution: {integrity: sha512-ga7zdhbS2GXOMTIZRT0mYjKJtR9fivsXzsyq5U3vjDL0s6DTMwYRm0UHNjzTY5dh4+LSC68Sm/7WEiimbQNYlw==} 393 394 '@tauri-apps/cli-darwin-arm64@2.0.0-rc.5': 395 resolution: {integrity: sha512-EoduJ5SeMfBKCe7I291JBH+lkrf2E0+mQF1rP+Jq4CjWPer11OeEcUSFtHURB3Z3ItzObQ7ALPulMGhMe6E9rg==} ··· 486 '@types/estree@1.0.8': 487 resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} 488 489 + animejs@4.1.3: 490 + resolution: {integrity: sha512-4XzlIsQsku1ycSPzchxxT0N+ohEMZObG71nOSBBkZoV4sgQvtXa/qAANkFpTE6pegdV8JnIBZiB0LfdxNoRNMw==} 491 492 babel-plugin-jsx-dom-expressions@0.40.1: 493 resolution: {integrity: sha512-b4iHuirqK7RgaMzB2Lsl7MqrlDgQtVRSSazyrmx7wB3T759ggGjod5Rkok5MfHjQXhR7tRPmdwoeGPqBnW2KfA==} ··· 503 solid-js: 504 optional: true 505 506 + browserslist@4.25.3: 507 + resolution: {integrity: sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==} 508 engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} 509 hasBin: true 510 ··· 526 supports-color: 527 optional: true 528 529 + electron-to-chromium@1.5.207: 530 + resolution: {integrity: sha512-mryFrrL/GXDTmAtIVMVf+eIXM09BBPlO5IQ7lUyKmK8d+A4VpRGG+M3ofoVef6qyF8s60rJei8ymlJxjUA8Faw==} 531 532 entities@6.0.1: 533 resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} ··· 599 resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} 600 engines: {node: ^10 || ^12 || >=14} 601 602 + rollup@4.46.3: 603 + resolution: {integrity: sha512-RZn2XTjXb8t5g13f5YclGoilU/kwT696DIkY3sywjdZidNSi3+vseaQov7D7BZXVJCPv3pDWUN69C78GGbXsKw==} 604 engines: {node: '>=18.0.0', npm: '>=8.0.0'} 605 hasBin: true 606 ··· 743 dependencies: 744 '@babel/compat-data': 7.28.0 745 '@babel/helper-validator-option': 7.27.1 746 + browserslist: 4.25.3 747 lru-cache: 5.1.1 748 semver: 6.3.1 749 ··· 897 '@jridgewell/resolve-uri': 3.1.2 898 '@jridgewell/sourcemap-codec': 1.5.5 899 900 + '@rollup/rollup-android-arm-eabi@4.46.3': 901 optional: true 902 903 + '@rollup/rollup-android-arm64@4.46.3': 904 optional: true 905 906 + '@rollup/rollup-darwin-arm64@4.46.3': 907 optional: true 908 909 + '@rollup/rollup-darwin-x64@4.46.3': 910 optional: true 911 912 + '@rollup/rollup-freebsd-arm64@4.46.3': 913 optional: true 914 915 + '@rollup/rollup-freebsd-x64@4.46.3': 916 optional: true 917 918 + '@rollup/rollup-linux-arm-gnueabihf@4.46.3': 919 optional: true 920 921 + '@rollup/rollup-linux-arm-musleabihf@4.46.3': 922 optional: true 923 924 + '@rollup/rollup-linux-arm64-gnu@4.46.3': 925 optional: true 926 927 + '@rollup/rollup-linux-arm64-musl@4.46.3': 928 optional: true 929 930 + '@rollup/rollup-linux-loongarch64-gnu@4.46.3': 931 optional: true 932 933 + '@rollup/rollup-linux-ppc64-gnu@4.46.3': 934 optional: true 935 936 + '@rollup/rollup-linux-riscv64-gnu@4.46.3': 937 optional: true 938 939 + '@rollup/rollup-linux-riscv64-musl@4.46.3': 940 optional: true 941 942 + '@rollup/rollup-linux-s390x-gnu@4.46.3': 943 optional: true 944 945 + '@rollup/rollup-linux-x64-gnu@4.46.3': 946 optional: true 947 948 + '@rollup/rollup-linux-x64-musl@4.46.3': 949 optional: true 950 951 + '@rollup/rollup-win32-arm64-msvc@4.46.3': 952 optional: true 953 954 + '@rollup/rollup-win32-ia32-msvc@4.46.3': 955 optional: true 956 957 + '@rollup/rollup-win32-x64-msvc@4.46.3': 958 optional: true 959 960 '@tauri-apps/api@2.0.0-rc.0': {} 961 962 + '@tauri-apps/api@2.8.0': {} 963 964 '@tauri-apps/cli-darwin-arm64@2.0.0-rc.5': 965 optional: true ··· 1006 1007 '@tauri-apps/plugin-deep-link@2.4.1': 1008 dependencies: 1009 + '@tauri-apps/api': 2.8.0 1010 1011 '@tauri-apps/plugin-http@2.0.0-rc.1': 1012 dependencies: 1013 + '@tauri-apps/api': 2.8.0 1014 1015 '@tauri-apps/plugin-process@2.0.0-rc.0': 1016 dependencies: ··· 1045 1046 '@types/estree@1.0.8': {} 1047 1048 + animejs@4.1.3: {} 1049 1050 babel-plugin-jsx-dom-expressions@0.40.1(@babel/core@7.28.3): 1051 dependencies: ··· 1064 optionalDependencies: 1065 solid-js: 1.9.9 1066 1067 + browserslist@4.25.3: 1068 dependencies: 1069 caniuse-lite: 1.0.30001735 1070 + electron-to-chromium: 1.5.207 1071 node-releases: 2.0.19 1072 + update-browserslist-db: 1.1.3(browserslist@4.25.3) 1073 1074 caniuse-lite@1.0.30001735: {} 1075 ··· 1081 dependencies: 1082 ms: 2.1.3 1083 1084 + electron-to-chromium@1.5.207: {} 1085 1086 entities@6.0.1: {} 1087 ··· 1154 picocolors: 1.1.1 1155 source-map-js: 1.2.1 1156 1157 + rollup@4.46.3: 1158 dependencies: 1159 '@types/estree': 1.0.8 1160 optionalDependencies: 1161 + '@rollup/rollup-android-arm-eabi': 4.46.3 1162 + '@rollup/rollup-android-arm64': 4.46.3 1163 + '@rollup/rollup-darwin-arm64': 4.46.3 1164 + '@rollup/rollup-darwin-x64': 4.46.3 1165 + '@rollup/rollup-freebsd-arm64': 4.46.3 1166 + '@rollup/rollup-freebsd-x64': 4.46.3 1167 + '@rollup/rollup-linux-arm-gnueabihf': 4.46.3 1168 + '@rollup/rollup-linux-arm-musleabihf': 4.46.3 1169 + '@rollup/rollup-linux-arm64-gnu': 4.46.3 1170 + '@rollup/rollup-linux-arm64-musl': 4.46.3 1171 + '@rollup/rollup-linux-loongarch64-gnu': 4.46.3 1172 + '@rollup/rollup-linux-ppc64-gnu': 4.46.3 1173 + '@rollup/rollup-linux-riscv64-gnu': 4.46.3 1174 + '@rollup/rollup-linux-riscv64-musl': 4.46.3 1175 + '@rollup/rollup-linux-s390x-gnu': 4.46.3 1176 + '@rollup/rollup-linux-x64-gnu': 4.46.3 1177 + '@rollup/rollup-linux-x64-musl': 4.46.3 1178 + '@rollup/rollup-win32-arm64-msvc': 4.46.3 1179 + '@rollup/rollup-win32-ia32-msvc': 4.46.3 1180 + '@rollup/rollup-win32-x64-msvc': 4.46.3 1181 fsevents: 2.3.3 1182 1183 semver@6.3.1: {} ··· 1207 1208 typescript@5.9.2: {} 1209 1210 + update-browserslist-db@1.1.3(browserslist@4.25.3): 1211 dependencies: 1212 + browserslist: 4.25.3 1213 escalade: 3.2.0 1214 picocolors: 1.1.1 1215 ··· 1232 dependencies: 1233 esbuild: 0.21.5 1234 postcss: 8.5.6 1235 + rollup: 4.46.3 1236 optionalDependencies: 1237 fsevents: 2.3.3 1238
+1
public/icon/gear-solid-full.svg
···
··· 1 + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M259.1 73.5C262.1 58.7 275.2 48 290.4 48L350.2 48C365.4 48 378.5 58.7 381.5 73.5L396 143.5C410.1 149.5 423.3 157.2 435.3 166.3L503.1 143.8C517.5 139 533.3 145 540.9 158.2L570.8 210C578.4 223.2 575.7 239.8 564.3 249.9L511 297.3C511.9 304.7 512.3 312.3 512.3 320C512.3 327.7 511.8 335.3 511 342.7L564.4 390.2C575.8 400.3 578.4 417 570.9 430.1L541 481.9C533.4 495 517.6 501.1 503.2 496.3L435.4 473.8C423.3 482.9 410.1 490.5 396.1 496.6L381.7 566.5C378.6 581.4 365.5 592 350.4 592L290.6 592C275.4 592 262.3 581.3 259.3 566.5L244.9 496.6C230.8 490.6 217.7 482.9 205.6 473.8L137.5 496.3C123.1 501.1 107.3 495.1 99.7 481.9L69.8 430.1C62.2 416.9 64.9 400.3 76.3 390.2L129.7 342.7C128.8 335.3 128.4 327.7 128.4 320C128.4 312.3 128.9 304.7 129.7 297.3L76.3 249.8C64.9 239.7 62.3 223 69.8 209.9L99.7 158.1C107.3 144.9 123.1 138.9 137.5 143.7L205.3 166.2C217.4 157.1 230.6 149.5 244.6 143.4L259.1 73.5zM320.3 400C364.5 399.8 400.2 363.9 400 319.7C399.8 275.5 363.9 239.8 319.7 240C275.5 240.2 239.8 276.1 240 320.3C240.2 364.5 276.1 400.2 320.3 400z"/></svg>
+1 -1
src-tauri/tauri.conf.json
··· 35 "minWidth": 600, 36 "minHeight": 400, 37 "visible": false, 38 - "decorations": false, 39 "transparent": true 40 } 41 ]
··· 35 "minWidth": 600, 36 "minHeight": 400, 37 "visible": false, 38 + "decorations": true, 39 "transparent": true 40 } 41 ]
+2 -5
src/Components/App.tsx
··· 1 import { onMount } from "solid-js"; 2 - import anime from "animejs"; 3 4 - import NavBar from "./NavBar"; 5 import PhotoList from "./PhotoList"; 6 import PhotoViewer from "./PhotoViewer"; 7 import SettingsMenu from "./SettingsMenu"; 8 9 let App = () => { 10 onMount(() => { 11 - anime.set('.settings', 12 { 13 display: 'none', 14 opacity: 0, ··· 18 19 return ( 20 <div class="container"> 21 - <NavBar /> 22 - 23 <PhotoList /> 24 <PhotoViewer /> 25
··· 1 import { onMount } from "solid-js"; 2 3 import PhotoList from "./PhotoList"; 4 import PhotoViewer from "./PhotoViewer"; 5 import SettingsMenu from "./SettingsMenu"; 6 + import { utils } from "animejs"; 7 8 let App = () => { 9 onMount(() => { 10 + utils.set('.settings', 11 { 12 display: 'none', 13 opacity: 0, ··· 17 18 return ( 19 <div class="container"> 20 <PhotoList /> 21 <PhotoViewer /> 22
+1 -1
src/Components/Managers/PhotoListRenderingManager.tsx
··· 22 23 let lastDateString = null; 24 let row = new PhotoListRow(); 25 - row.Height = 100; 26 27 for (let i = 0; i < window.PhotoManager.FilteredPhotos.length; i++) { 28 let photo = window.PhotoManager.FilteredPhotos[i];
··· 22 23 let lastDateString = null; 24 let row = new PhotoListRow(); 25 + row.Height = 0; 26 27 for (let i = 0; i < window.PhotoManager.FilteredPhotos.length; i++) { 28 let photo = window.PhotoManager.FilteredPhotos[i];
+12 -3
src/Components/Managers/PhotoManager.tsx
··· 170 171 switch(this._filterType){ 172 case FilterType.USER: 173 this.Photos.map(p => { 174 if(p.metadata){ 175 try{ 176 let meta = JSON.parse(p.metadata); 177 - let photo = meta.players.find(( y: any ) => y.displayName.toLowerCase().includes(this._filter) || y.id === this._filter); 178 179 if(photo)this.FilteredPhotos.push(p); 180 } catch(e){} ··· 182 }) 183 break; 184 case FilterType.WORLD: 185 this.Photos.map(p => { 186 if(p.metadata){ 187 try{ 188 let meta = JSON.parse(p.metadata); 189 - let photo = meta.world.name.toLowerCase().includes(this._filter) || meta.world.id === this._filter; 190 - 191 if(photo)this.FilteredPhotos.push(p); 192 } catch(e){} 193 }
··· 170 171 switch(this._filterType){ 172 case FilterType.USER: 173 + if(this._filter === '')return this.FilteredPhotos = this.Photos; 174 + 175 this.Photos.map(p => { 176 if(p.metadata){ 177 try{ 178 let meta = JSON.parse(p.metadata); 179 + let photo = meta.players.find(( y: any ) => 180 + y.displayName.toLowerCase().includes(this._filter) || 181 + y.id === this._filter 182 + ); 183 184 if(photo)this.FilteredPhotos.push(p); 185 } catch(e){} ··· 187 }) 188 break; 189 case FilterType.WORLD: 190 + if(this._filter === '')return this.FilteredPhotos = this.Photos; 191 + 192 this.Photos.map(p => { 193 if(p.metadata){ 194 try{ 195 let meta = JSON.parse(p.metadata); 196 + let photo = 197 + meta.world.name.toLowerCase().includes(this._filter) || 198 + meta.world.id === this._filter; 199 + 200 if(photo)this.FilteredPhotos.push(p); 201 } catch(e){} 202 }
-125
src/Components/NavBar.tsx
··· 1 - import { emit } from '@tauri-apps/api/event'; 2 - import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'; 3 - import anime from 'animejs'; 4 - import { Show, onMount } from 'solid-js'; 5 - import { ViewState } from './Managers/ViewManager'; 6 - 7 - const appWindow = getCurrentWebviewWindow(); 8 - 9 - let NavBar = () => { 10 - let dropdownVisible = false; 11 - let inAnimation = false; 12 - let dropdown: HTMLElement; 13 - 14 - onMount(() => { 15 - anime.set(dropdown, { opacity: 0, translateX: -10 }); 16 - dropdown.style.display = 'none'; 17 - }) 18 - 19 - let setDropdownVisibility = ( visible: boolean ) => { 20 - if(inAnimation)return; 21 - 22 - if(dropdownVisible !== visible){ 23 - dropdownVisible = visible; 24 - inAnimation = true; 25 - 26 - if(visible){ 27 - dropdown.style.display = 'block'; 28 - 29 - anime({ 30 - targets: dropdown, 31 - opacity: 1, 32 - translateX: 0, 33 - easing: 'easeInOutQuad', 34 - duration: 250, 35 - complete: () => { 36 - inAnimation = false; 37 - } 38 - }) 39 - } else{ 40 - anime({ 41 - targets: dropdown, 42 - opacity: 0, 43 - translateX: -10, 44 - easing: 'easeInOutQuad', 45 - duration: 250, 46 - complete: () => { 47 - inAnimation = false; 48 - dropdown.style.display = 'none'; 49 - } 50 - }) 51 - } 52 - } 53 - } 54 - 55 - window.CloseAllPopups.push(() => setDropdownVisibility(false)); 56 - 57 - return ( 58 - <> 59 - <div class="navbar" data-tauri-drag-region> 60 - <div class="tabs" data-tauri-drag-region> 61 - <div class="nav-tab" onClick={() => { 62 - window.ViewManager.ChangeState(ViewState.PHOTO_LIST); 63 - anime( 64 - { 65 - targets: '.settings', 66 - opacity: 0, 67 - translateX: '500px', 68 - easing: 'easeInOutQuad', 69 - duration: 250, 70 - complete: () => { 71 - anime.set('.settings', { display: 'none' }); 72 - } 73 - }) 74 - }}>Photos</div> 75 - </div> 76 - <div class="nav-tab" style={{ width: '200px', "text-align": 'center', background: 'transparent' }} data-tauri-drag-region> 77 - <Show when={window.SyncManager.IsSyncing()}> 78 - <Show when={ window.SyncManager.SyncError() == "" } fallback={ "Error: " + window.SyncManager.SyncError() }> 79 - <div style={{ width: '100%', "text-align": 'center', 'font-size': '14px' }}> 80 - { window.SyncManager.SyncType() }ing: { window.SyncManager.SyncPhotoTransfers () } / { window.SyncManager.SyncPhotoTotal() }<br /> 81 - <div style={{ width: '80%', height: '2px', margin: 'auto', "margin-top": '5px', background: '#111' }}> 82 - <div style={{ height: '2px', width: (window.SyncManager.SyncPhotoTransfers() / window.SyncManager.SyncPhotoTotal()) * 100 + '%', background: '#00ccff' }}></div> 83 - </div> 84 - </div> 85 - </Show> 86 - </Show> 87 - </div> 88 - <div class="account" onClick={() => setDropdownVisibility(!dropdownVisible)}> 89 - <div class="icon"> 90 - <img draggable="false" width="24" height="24" src="/icon/caret-down-solid.svg"></img> 91 - </div> 92 - </div> 93 - <div class="control-lights"> 94 - <div class="light" onClick={() => appWindow.minimize()}> 95 - <img draggable="false" width="24" height="24" src="/icon/minus-solid.svg"></img> 96 - </div> 97 - <div class="light" onClick={() => appWindow.toggleMaximize()}> 98 - <img draggable="false" width="24" height="24" src="/icon/square-regular.svg"></img> 99 - </div> 100 - <div class="light" onClick={() => { appWindow.hide(); emit('hide-window'); } }> 101 - <img draggable="false" width="24" height="24" src="/icon/x-solid.svg"></img> 102 - </div> 103 - </div> 104 - </div> 105 - 106 - <div class="dropdown" ref={( el ) => dropdown = el}> 107 - <div class="dropdown-button" onClick={async () => { 108 - anime.set('.settings', { display: 'block' }); 109 - anime({ 110 - targets: '.settings', 111 - opacity: 1, 112 - translateX: '0px', 113 - easing: 'easeInOutQuad', 114 - duration: 250 115 - }) 116 - 117 - window.ViewManager.ChangeState(ViewState.SETTINGS); 118 - setDropdownVisibility(false); 119 - }}>Settings</div> 120 - </div> 121 - </> 122 - ) 123 - } 124 - 125 - export default NavBar;
···
+51 -72
src/Components/PhotoList.tsx
··· 2 import { listen } from '@tauri-apps/api/event'; 3 import { Window } from "@tauri-apps/api/window"; 4 5 - import anime from "animejs"; 6 import FilterMenu from "./FilterMenu"; 7 import { ViewState } from "./Managers/ViewManager"; 8 import { invoke } from "@tauri-apps/api/core"; 9 10 enum ListPopup{ 11 FILTERS, ··· 13 } 14 15 let PhotoList = () => { 16 - let photoTreeLoadingContainer: HTMLElement; 17 - 18 let scrollToTop: HTMLElement; 19 let scrollToTopActive = false; 20 21 let photoContainer: HTMLCanvasElement; 22 - let photoContainerBG: HTMLCanvasElement; 23 24 let filterContainer: HTMLDivElement; 25 26 let ctx: CanvasRenderingContext2D; 27 - let ctxBG: CanvasRenderingContext2D; 28 29 let scroll: number = 0; 30 let targetScroll: number = 0; ··· 39 40 41 window.ViewManager.OnStateTransition(ViewState.PHOTO_LIST, ViewState.SETTINGS, () => { 42 - anime({ targets: photoContainer, opacity: 0, easing: 'easeInOutQuad', duration: 100 }); 43 - anime({ targets: '.filter-options', opacity: 0, easing: 'easeInOutQuad', duration: 100 }); 44 - anime({ targets: '.reload-photos', opacity: 0, easing: 'easeInOutQuad', duration: 100 }); 45 }); 46 47 window.ViewManager.OnStateTransition(ViewState.SETTINGS, ViewState.PHOTO_LIST, () => { 48 - anime({ targets: photoContainer, opacity: 1, easing: 'easeInOutQuad', duration: 100 }); 49 - anime({ targets: '.filter-options', opacity: 1, easing: 'easeInOutQuad', duration: 100 }); 50 - anime({ targets: '.reload-photos', opacity: 1, easing: 'easeInOutQuad', duration: 100 }); 51 }); 52 53 54 window.ViewManager.OnStateTransition(ViewState.PHOTO_LIST, ViewState.PHOTO_VIEWER, () => { 55 - anime({ targets: photoContainer, opacity: 0, easing: 'easeInOutQuad', duration: 100 }); 56 - anime({ targets: '.filter-options', opacity: 0, easing: 'easeInOutQuad', duration: 100 }); 57 - anime({ targets: '.reload-photos', opacity: 0, easing: 'easeInOutQuad', duration: 100 }); 58 }); 59 60 window.ViewManager.OnStateTransition(ViewState.PHOTO_VIEWER, ViewState.PHOTO_LIST, () => { 61 - anime({ targets: photoContainer, opacity: 1, easing: 'easeInOutQuad', duration: 100 }); 62 - anime({ targets: '.filter-options', opacity: 1, easing: 'easeInOutQuad', duration: 100 }); 63 - anime({ targets: '.reload-photos', opacity: 1, easing: 'easeInOutQuad', duration: 100 }); 64 }); 65 66 ··· 74 photoContainer.width = window.innerWidth; 75 photoContainer.height = window.innerHeight; 76 77 - photoContainerBG.width = window.innerWidth; 78 - photoContainerBG.height = window.innerHeight; 79 - 80 window.PhotoListRenderingManager.ComputeLayout(); 81 } 82 83 let closeCurrentPopup = () => { 84 switch(currentPopup){ 85 case ListPopup.FILTERS: 86 - anime({ 87 - targets: filterContainer!, 88 opacity: 0, 89 easing: 'easeInOutQuad', 90 duration: 100, 91 - complete: () => { 92 filterContainer!.style.display = 'none'; 93 currentPopup = ListPopup.NONE; 94 } ··· 98 } 99 } 100 101 - let fps = 0; 102 - setInterval(() => { 103 - console.log('FPS: ' + fps); 104 - fps = 0; 105 - }, 1000); 106 - 107 let render = () => { 108 if(!quitRender) 109 requestAnimationFrame(render); ··· 112 113 if(!scrollToTopActive && scroll > photoContainer.height){ 114 scrollToTop.style.display = 'flex'; 115 - anime({ targets: scrollToTop, opacity: 1, translateY: '0px', easing: 'easeInOutQuad', duration: 100 }); 116 117 scrollToTopActive = true; 118 } else if(scrollToTopActive && scroll < photoContainer.height){ 119 - anime({ targets: scrollToTop, opacity: 0, translateY: '-10px', complete: () => scrollToTop.style.display = 'none', easing: 'easeInOutQuad', duration: 100 }); 120 scrollToTopActive = false; 121 } 122 123 - if(!ctx || !ctxBG)return; 124 ctx.clearRect(0, 0, photoContainer.width, photoContainer.height); 125 - ctxBG.clearRect(0, 0, photoContainerBG.width, photoContainerBG.height); 126 127 scroll = scroll + (targetScroll - scroll) * 0.1; 128 ··· 137 138 ctx.fillText("It's looking empty in here! You have no photos :O", photoContainer.width / 2, photoContainer.height / 2); 139 } 140 - 141 - ctxBG.drawImage(photoContainer, 0, 0); 142 - fps += 1; 143 } 144 145 listen('hide-window', () => { ··· 154 photoContainer.width = window.innerWidth; 155 photoContainer.height = window.innerHeight; 156 157 - photoContainerBG.width = window.innerWidth; 158 - photoContainerBG.height = window.innerHeight; 159 - 160 if(window.PhotoManager.HasFirstLoaded){ 161 requestAnimationFrame(render); 162 window.PhotoManager.HasFirstLoaded = false; ··· 166 window.PhotoManager.OnLoadingFinished(() => { 167 invoke('close_splashscreen'); 168 169 - anime({ 170 - targets: photoTreeLoadingContainer, 171 - height: 0, 172 - easing: 'easeInOutQuad', 173 - duration: 500, 174 - opacity: 0, 175 - complete: () => { 176 - photoTreeLoadingContainer.style.display = 'none'; 177 - } 178 - }) 179 - 180 - anime({ 181 - targets: '.reload-photos', 182 opacity: 1, 183 duration: 150, 184 easing: 'easeInOutQuad' ··· 192 193 onMount(() => { 194 ctx = photoContainer.getContext('2d')!; 195 - ctxBG = photoContainerBG.getContext('2d')!; 196 197 window.PhotoManager.Load(); 198 199 - anime.set(scrollToTop, { opacity: 0, translateY: '-10px', display: 'none' }); 200 201 photoContainer.onwheel = ( e: WheelEvent ) => { 202 targetScroll += e.deltaY * 2; ··· 210 211 photoContainer.width = window.innerWidth; 212 photoContainer.height = window.innerHeight; 213 - 214 - photoContainerBG.width = window.innerWidth; 215 - photoContainerBG.height = window.innerHeight; 216 217 photoContainer.onclick = ( e: MouseEvent ) => { 218 let photo = window.PhotoManager.FilteredPhotos.find(x => ··· 240 241 return ( 242 <div class="photo-list"> 243 - <div ref={filterContainer!} class="filter-container" style={{ 244 - height: window.PhotoManager.HasBeenIndexed() ? '83px' : '110px', 245 - width: window.PhotoManager.HasBeenIndexed() ? '600px' : '650px' 246 - }}> 247 <FilterMenu /> 248 </div> 249 - 250 - <div class="photo-tree-loading" ref={( el ) => photoTreeLoadingContainer = el}>Scanning Photo Tree...</div> 251 252 <div class="scroll-to-top" ref={( el ) => scrollToTop = el} onClick={() => targetScroll = 0}> 253 <div class="icon"> 254 <img draggable="false" src="/icon/angle-up-solid.svg"></img> 255 </div> 256 </div> 257 - <div class="reload-photos" onClick={() => window.ConfirmationBoxManager.SetConfirmationBox("Are you sure you want to reload all photos? This can cause the application to slow down while it is loading...", () => window.location.reload())}> 258 - <div class="icon" style={{ width: '17px' }}> 259 - <img draggable="false" width="17" height="17" src="/icon/arrows-rotate-solid.svg"></img> 260 - </div> 261 - </div> 262 263 <div class="filter-options"> 264 <div> ··· 268 269 filterContainer!.style.display = 'block'; 270 271 - anime({ 272 - targets: filterContainer!, 273 opacity: 1, 274 easing: 'easeInOutQuad', 275 duration: 100 276 }); 277 - }} class="icon" style={{ width: '20px', height: '20px', padding: '20px' }}> 278 <img draggable="false" style={{ width: "20px", height: "20px" }} src="/icon/sliders-solid.svg"></img> 279 </div> 280 <div class="icon-label">Filters</div> 281 </div> 282 </div> 283 284 <canvas class="photo-container" ref={( el ) => photoContainer = el}></canvas> 285 - <canvas class="photo-container-bg" ref={( el ) => photoContainerBG = el}></canvas> 286 </div> 287 ) 288 }
··· 2 import { listen } from '@tauri-apps/api/event'; 3 import { Window } from "@tauri-apps/api/window"; 4 5 import FilterMenu from "./FilterMenu"; 6 import { ViewState } from "./Managers/ViewManager"; 7 import { invoke } from "@tauri-apps/api/core"; 8 + import { animate, utils } from "animejs"; 9 10 enum ListPopup{ 11 FILTERS, ··· 13 } 14 15 let PhotoList = () => { 16 let scrollToTop: HTMLElement; 17 let scrollToTopActive = false; 18 19 let photoContainer: HTMLCanvasElement; 20 21 let filterContainer: HTMLDivElement; 22 23 let ctx: CanvasRenderingContext2D; 24 25 let scroll: number = 0; 26 let targetScroll: number = 0; ··· 35 36 37 window.ViewManager.OnStateTransition(ViewState.PHOTO_LIST, ViewState.SETTINGS, () => { 38 + animate(photoContainer, { opacity: 0.5, filter: 'blur(10px)', easing: 'easeInOutQuad', duration: 100 }); 39 + animate('.filter-options', { opacity: 0, easing: 'easeInOutQuad', duration: 100 }); 40 + animate('.scroll-to-top', { opacity: 0, easing: 'easeInOutQuad', duration: 100 }); 41 }); 42 43 window.ViewManager.OnStateTransition(ViewState.SETTINGS, ViewState.PHOTO_LIST, () => { 44 + animate(photoContainer, { opacity: 1, filter: 'blur(0px)', easing: 'easeInOutQuad', duration: 100, onComplete: () => photoContainer.style.filter = '' }); 45 + animate('.filter-options', { opacity: 1, easing: 'easeInOutQuad', duration: 100 }); 46 + animate('.scroll-to-top', { opacity: 1, easing: 'easeInOutQuad', duration: 100 }); 47 }); 48 49 50 window.ViewManager.OnStateTransition(ViewState.PHOTO_LIST, ViewState.PHOTO_VIEWER, () => { 51 + animate(photoContainer, { opacity: 0.5, filter: 'blur(10px)', easing: 'easeInOutQuad', duration: 100 }); 52 + animate('.filter-options', { opacity: 0, easing: 'easeInOutQuad', duration: 100 }); 53 + animate('.scroll-to-top', { opacity: 0, easing: 'easeInOutQuad', duration: 100 }); 54 }); 55 56 window.ViewManager.OnStateTransition(ViewState.PHOTO_VIEWER, ViewState.PHOTO_LIST, () => { 57 + animate(photoContainer, { opacity: 1, filter: 'blur(0px)', easing: 'easeInOutQuad', duration: 100, onComplete: () => photoContainer.style.filter = '' }); 58 + animate('.filter-options', { opacity: 1, easing: 'easeInOutQuad', duration: 100 }); 59 + animate('.scroll-to-top', { opacity: 1, easing: 'easeInOutQuad', duration: 100 }); 60 }); 61 62 ··· 70 photoContainer.width = window.innerWidth; 71 photoContainer.height = window.innerHeight; 72 73 window.PhotoListRenderingManager.ComputeLayout(); 74 } 75 76 let closeCurrentPopup = () => { 77 switch(currentPopup){ 78 case ListPopup.FILTERS: 79 + animate(filterContainer!, { 80 opacity: 0, 81 + translateY: '10px', 82 easing: 'easeInOutQuad', 83 duration: 100, 84 + onComplete: () => { 85 filterContainer!.style.display = 'none'; 86 currentPopup = ListPopup.NONE; 87 } ··· 91 } 92 } 93 94 let render = () => { 95 if(!quitRender) 96 requestAnimationFrame(render); ··· 99 100 if(!scrollToTopActive && scroll > photoContainer.height){ 101 scrollToTop.style.display = 'flex'; 102 + animate(scrollToTop, { opacity: 1, translateY: '0px', easing: 'easeInOutQuad', duration: 100 }); 103 104 scrollToTopActive = true; 105 } else if(scrollToTopActive && scroll < photoContainer.height){ 106 + animate(scrollToTop, { opacity: 0, translateY: '-10px', complete: () => scrollToTop.style.display = 'none', easing: 'easeInOutQuad', duration: 100 }); 107 scrollToTopActive = false; 108 } 109 110 + if(!ctx)return; 111 ctx.clearRect(0, 0, photoContainer.width, photoContainer.height); 112 113 scroll = scroll + (targetScroll - scroll) * 0.1; 114 ··· 123 124 ctx.fillText("It's looking empty in here! You have no photos :O", photoContainer.width / 2, photoContainer.height / 2); 125 } 126 } 127 128 listen('hide-window', () => { ··· 137 photoContainer.width = window.innerWidth; 138 photoContainer.height = window.innerHeight; 139 140 if(window.PhotoManager.HasFirstLoaded){ 141 requestAnimationFrame(render); 142 window.PhotoManager.HasFirstLoaded = false; ··· 146 window.PhotoManager.OnLoadingFinished(() => { 147 invoke('close_splashscreen'); 148 149 + animate('.reload-photos', { 150 opacity: 1, 151 duration: 150, 152 easing: 'easeInOutQuad' ··· 160 161 onMount(() => { 162 ctx = photoContainer.getContext('2d')!; 163 164 window.PhotoManager.Load(); 165 166 + utils.set(scrollToTop, { opacity: 0, translateY: '-10px', display: 'none' }); 167 168 photoContainer.onwheel = ( e: WheelEvent ) => { 169 targetScroll += e.deltaY * 2; ··· 177 178 photoContainer.width = window.innerWidth; 179 photoContainer.height = window.innerHeight; 180 181 photoContainer.onclick = ( e: MouseEvent ) => { 182 let photo = window.PhotoManager.FilteredPhotos.find(x => ··· 204 205 return ( 206 <div class="photo-list"> 207 + <div ref={filterContainer!} class="filter-container"> 208 <FilterMenu /> 209 </div> 210 211 <div class="scroll-to-top" ref={( el ) => scrollToTop = el} onClick={() => targetScroll = 0}> 212 <div class="icon"> 213 <img draggable="false" src="/icon/angle-up-solid.svg"></img> 214 </div> 215 </div> 216 217 <div class="filter-options"> 218 <div> ··· 222 223 filterContainer!.style.display = 'block'; 224 225 + animate(filterContainer!, { 226 opacity: 1, 227 + translateY: 0, 228 easing: 'easeInOutQuad', 229 duration: 100 230 }); 231 + }} class="icon"> 232 <img draggable="false" style={{ width: "20px", height: "20px" }} src="/icon/sliders-solid.svg"></img> 233 </div> 234 <div class="icon-label">Filters</div> 235 </div> 236 + 237 + <div> 238 + <div onClick={() => { 239 + window.location.reload(); 240 + }} class="icon"> 241 + <img draggable="false" style={{ width: "20px", height: "20px" }} src="/icon/arrows-rotate-solid.svg"></img> 242 + </div> 243 + <div class="icon-label">Reload Photos</div> 244 + </div> 245 + 246 + <div> 247 + <div onClick={() => { 248 + utils.set('.settings', { display: 'block' }); 249 + animate('.settings', { 250 + opacity: 1, 251 + translateX: '0px', 252 + easing: 'easeInOutQuad', 253 + duration: 250 254 + }) 255 + 256 + window.ViewManager.ChangeState(ViewState.SETTINGS); 257 + }} class="icon"> 258 + <img draggable="false" style={{ width: "20px", height: "20px" }} src="/icon/gear-solid-full.svg"></img> 259 + </div> 260 + <div class="icon-label">Settings</div> 261 + </div> 262 </div> 263 264 <canvas class="photo-container" ref={( el ) => photoContainer = el}></canvas> 265 </div> 266 ) 267 }
+64 -81
src/Components/PhotoViewer.tsx
··· 1 import { For, Show, createEffect, onCleanup, onMount } from "solid-js"; 2 import { invoke } from '@tauri-apps/api/core'; 3 - import anime from 'animejs'; 4 import { WorldCache } from "./Structs/WorldCache"; 5 6 let PhotoViewer = () => { 7 let viewer: HTMLElement; ··· 21 let viewerContextMenuButtons: HTMLElement[] = []; 22 23 let allowedToOpenTray = false; 24 - let trayInAnimation = false; 25 26 let authorProfileButton: HTMLDivElement; 27 ··· 52 } 53 } 54 55 let openTray = () => { 56 - if(trayOpen || trayInAnimation)return; 57 58 - trayOpen = true; 59 - trayInAnimation = true; 60 61 window.CloseAllPopups.forEach(p => p()); 62 - anime({ targets: photoTray, bottom: '0px', duration: 500 }); 63 64 - anime({ 65 - targets: photoControls, 66 bottom: '160px', 67 scale: '0.75', 68 opacity: 0, 69 duration: 500, 70 - complete: () => { 71 photoControls.style.display = 'none'; 72 - trayInAnimation = false; 73 } 74 }); 75 76 photoTrayCloseBtn.style.display = 'flex'; 77 - anime({ 78 - targets: photoTrayCloseBtn, 79 bottom: '160px', 80 opacity: 1, 81 scale: 1, 82 duration: 500 ··· 86 let copyImage = () => { 87 invoke('copy_image', { path: window.PhotoViewerManager.CurrentPhoto()!.path }) 88 .then(() => { 89 - anime.set('.copy-notif', { translateX: '-50%', translateY: '-100px' }); 90 - anime({ 91 - targets: '.copy-notif', 92 opacity: 1, 93 translateY: '0px' 94 }); 95 96 setTimeout(() => { 97 - anime({ 98 - targets: '.copy-notif', 99 opacity: 0, 100 translateY: '-100px' 101 }); ··· 104 } 105 106 let closeTray = () => { 107 - if(!trayOpen || trayInAnimation)return; 108 - trayInAnimation = true; 109 110 window.CloseAllPopups.forEach(p => p()); 111 - anime({ targets: photoTray, bottom: '-150px', duration: 500 }); 112 113 - anime({ 114 - targets: photoTrayCloseBtn, 115 bottom: '10px', 116 scale: '0.75', 117 opacity: 0, 118 duration: 500, 119 - complete: () => { 120 photoTrayCloseBtn.style.display = 'none'; 121 - trayOpen = false; 122 - trayInAnimation = false; 123 } 124 }); 125 126 photoControls.style.display = 'flex'; 127 - anime({ 128 - targets: photoControls, 129 bottom: '10px', 130 opacity: 1, 131 scale: 1, 132 duration: 500, ··· 134 } 135 136 onMount(() => { 137 - anime.set(photoControls, { translateX: '-50%' }); 138 - anime.set(photoTrayCloseBtn, { translateX: '-50%', opacity: 0, scale: '0.75', bottom: '10px' }); 139 140 window.addEventListener('keyup', switchPhotoWithKey); 141 142 let contextMenuOpen = false; 143 window.CloseAllPopups.push(() => { 144 contextMenuOpen = false; 145 - anime.set(viewerContextMenu, { opacity: 1, rotate: '0deg' }); 146 147 - anime({ 148 - targets: viewerContextMenu, 149 opacity: 0, 150 easing: 'easeInOutQuad', 151 rotate: '30deg', 152 duration: 100, 153 - complete: () => { 154 viewerContextMenu.style.display = 'none'; 155 } 156 }) ··· 174 if(contextMenuOpen){ 175 contextMenuOpen = false; 176 177 - anime.set(viewerContextMenu, { opacity: 1, rotate: '0deg' }); 178 179 - anime({ 180 - targets: viewerContextMenu, 181 opacity: 0, 182 rotate: '30deg', 183 easing: 'easeInOutQuad', 184 duration: 100, 185 - complete: () => { 186 viewerContextMenu.style.display = 'none'; 187 } 188 }) ··· 193 viewerContextMenu.style.left = e.clientX + 'px'; 194 viewerContextMenu.style.display = 'block'; 195 196 - anime.set(viewerContextMenu, { opacity: 0, rotate: '-30deg' }); 197 198 - anime({ 199 - targets: viewerContextMenu, 200 opacity: 1, 201 rotate: '0deg', 202 easing: 'easeInOutQuad', ··· 215 imageViewer.src = (window.OS === "windows" ? "http://photo.localhost/" : 'photo://localhost/') + window.PhotoViewerManager.CurrentPhoto()?.path.split('\\').join('/') + "?full"; 216 imageViewer.crossOrigin = 'anonymous'; 217 218 - anime({ 219 - targets: imageViewer, 220 opacity: 1, 221 delay: 50, 222 duration: 150, ··· 300 if(photo && !isOpen){ 301 viewer.style.display = 'flex'; 302 303 - anime({ 304 - targets: viewer, 305 opacity: 1, 306 easing: 'easeInOutQuad', 307 duration: 150 308 }); 309 - 310 - anime({ 311 - targets: '.navbar', 312 - top: '-50px' 313 - }) 314 315 - anime.set('.prev-button', { left: '-50px', top: '50%' }); 316 - anime.set('.next-button', { right: '-50px', top: '50%' }); 317 318 - anime({ targets: '.prev-button', left: '0', easing: 'easeInOutQuad', duration: 100 }); 319 - anime({ targets: '.next-button', right: '0', easing: 'easeInOutQuad', duration: 100 }); 320 321 window.CloseAllPopups.forEach(p => p()); 322 } else if(!photo && isOpen){ 323 - anime({ 324 - targets: viewer, 325 opacity: 0, 326 easing: 'easeInOutQuad', 327 duration: 150, 328 - complete: () => { 329 viewer.style.display = 'none'; 330 } 331 }); 332 - 333 - anime({ 334 - targets: '.navbar', 335 - top: '0px' 336 - }) 337 338 window.CloseAllPopups.forEach(p => p()); 339 340 - anime({ targets: '.prev-button', top: '75%', easing: 'easeInOutQuad', duration: 100 }); 341 - anime({ targets: '.next-button', top: '75%', easing: 'easeInOutQuad', duration: 100 }); 342 } 343 344 isOpen = photo != null; ··· 387 </div> 388 389 <div class="viewer-close viewer-button" onClick={() => window.PhotoViewerManager.Close()}> 390 - <div class="icon" style={{ width: '10px', margin: '0' }}> 391 <img draggable="false" src="/icon/x-solid.svg"></img> 392 </div> 393 </div> ··· 397 window.CloseAllPopups.forEach(p => p()); 398 window.PhotoViewerManager.PreviousPhoto(); 399 }}> 400 - <div class="icon" style={{ width: '15px', margin: '0' }}> 401 <img draggable="false" src="/icon/arrow-left-solid.svg"></img> 402 </div> 403 </div> ··· 406 window.CloseAllPopups.forEach(p => p()); 407 window.PhotoViewerManager.NextPhoto(); 408 }}> 409 - <div class="icon" style={{ width: '15px', margin: '0' }}> 410 <img draggable="false" src="/icon/arrow-right-solid.svg"></img> 411 </div> 412 </div> ··· 417 onClick={() => closeTray()} 418 ref={( el ) => photoTrayCloseBtn = el} 419 > 420 - <div class="icon" style={{ width: '12px', margin: '0' }}> 421 <img draggable="false" src="/icon/angle-down-solid.svg"></img> 422 </div> 423 </div> 424 425 <div class="control-buttons" ref={( el ) => photoControls = el}> 426 <div class="viewer-button" 427 - onMouseOver={( el ) => anime({ targets: el.currentTarget, width: '40px', height: '40px', 'margin-left': '15px', 'margin-right': '15px', 'margin-top': '-10px' })} 428 - onMouseLeave={( el ) => anime({ targets: el.currentTarget, width: '30px', height: '30px', 'margin-left': '20px', 'margin-right': '20px', 'margin-top': '0px' })} 429 onClick={() => { copyImage(); }}> 430 - <div class="icon" style={{ width: '12px', margin: '0' }}> 431 <img draggable="false" src="/icon/copy-solid.svg"></img> 432 </div> 433 </div> 434 <div class="viewer-button" style={{ width: '50px' }} 435 - onMouseOver={( el ) => anime({ targets: el.currentTarget, width: '70px', height: '30px', 'margin-left': '10px', 'margin-right': '10px' })} 436 - onMouseLeave={( el ) => anime({ targets: el.currentTarget, width: '50px', height: '30px', 'margin-left': '20px', 'margin-right': '20px' })} 437 ref={( el ) => trayButton = el} 438 onClick={() => openTray()} 439 > 440 - <div class="icon" style={{ width: '12px', margin: '0' }}> 441 <img draggable="false" src="/icon/angle-up-solid.svg"></img> 442 </div> 443 </div> 444 445 <div class="viewer-button" 446 ref={authorProfileButton!} 447 - onMouseOver={( el ) => anime({ targets: el.currentTarget, width: '40px', height: '40px', 'margin-left': '15px', 'margin-right': '15px', 'margin-top': '-10px' })} 448 - onMouseLeave={( el ) => anime({ targets: el.currentTarget, width: '30px', height: '30px', 'margin-left': '20px', 'margin-right': '20px', 'margin-top': '0px' })} 449 > 450 - <div class="icon" style={{ width: '12px', margin: '0' }}> 451 <img draggable="false" src="/icon/user-solid.svg"></img> 452 </div> 453 </div> 454 455 <div class="viewer-button" 456 - onMouseOver={( el ) => anime({ targets: el.currentTarget, width: '40px', height: '40px', 'margin-left': '15px', 'margin-right': '15px', 'margin-top': '-10px' })} 457 - onMouseLeave={( el ) => anime({ targets: el.currentTarget, width: '30px', height: '30px', 'margin-left': '20px', 'margin-right': '20px', 'margin-top': '0px' })} 458 onClick={() => window.ConfirmationBoxManager.SetConfirmationBox("Are you sure you want to delete this photo?", async () => { invoke("delete_photo", { 459 - path: window.PhotoViewerManager.CurrentPhoto()?.path, 460 - token: (await invoke('get_config_value_string', { key: 'token' })) || "none", 461 }); 462 })}> 463 - <div class="icon" style={{ width: '12px', margin: '0' }}> 464 <img draggable="false" src="/icon/trash-solid.svg"></img> 465 </div> 466 </div>
··· 1 import { For, Show, createEffect, onCleanup, onMount } from "solid-js"; 2 import { invoke } from '@tauri-apps/api/core'; 3 import { WorldCache } from "./Structs/WorldCache"; 4 + import { animate, JSAnimation, utils } from "animejs"; 5 6 let PhotoViewer = () => { 7 let viewer: HTMLElement; ··· 21 let viewerContextMenuButtons: HTMLElement[] = []; 22 23 let allowedToOpenTray = false; 24 25 let authorProfileButton: HTMLDivElement; 26 ··· 51 } 52 } 53 54 + let trayAnimation: JSAnimation[] = []; 55 + 56 let openTray = () => { 57 + if(trayOpen)return; 58 + trayOpen = true; 59 60 + trayAnimation.forEach(anim => anim.cancel()); 61 62 window.CloseAllPopups.forEach(p => p()); 63 + trayAnimation[0] = animate(photoTray, { bottom: '-150px', duration: 500, ease: 'outElastic' }); 64 65 + trayAnimation[1] = animate(photoControls, { 66 bottom: '160px', 67 + ease: 'outElastic', 68 scale: '0.75', 69 opacity: 0, 70 duration: 500, 71 + onComplete: () => { 72 photoControls.style.display = 'none'; 73 } 74 }); 75 76 photoTrayCloseBtn.style.display = 'flex'; 77 + trayAnimation[2] = animate(photoTrayCloseBtn, { 78 bottom: '160px', 79 + ease: 'outElastic', 80 opacity: 1, 81 scale: 1, 82 duration: 500 ··· 86 let copyImage = () => { 87 invoke('copy_image', { path: window.PhotoViewerManager.CurrentPhoto()!.path }) 88 .then(() => { 89 + utils.set('.copy-notif', { translateX: '-50%', translateY: '-100px' }); 90 + animate('.copy-notif', { 91 + ease: 'outElastic', 92 opacity: 1, 93 translateY: '0px' 94 }); 95 96 setTimeout(() => { 97 + animate('.copy-notif', { 98 + ease: 'outElastic', 99 opacity: 0, 100 translateY: '-100px' 101 }); ··· 104 } 105 106 let closeTray = () => { 107 + if(!trayOpen)return; 108 + trayOpen = false; 109 + 110 + trayAnimation.forEach(anim => anim.cancel()); 111 112 window.CloseAllPopups.forEach(p => p()); 113 + trayAnimation[0] = animate(photoTray, { bottom: '-300px', duration: 500, ease: 'outElastic' }); 114 115 + trayAnimation[2] = animate(photoTrayCloseBtn, { 116 bottom: '10px', 117 scale: '0.75', 118 + ease: 'outElastic', 119 opacity: 0, 120 duration: 500, 121 + onComplete: () => { 122 photoTrayCloseBtn.style.display = 'none'; 123 } 124 }); 125 126 photoControls.style.display = 'flex'; 127 + trayAnimation[1] = animate(photoControls, { 128 bottom: '10px', 129 + ease: 'outElastic', 130 opacity: 1, 131 scale: 1, 132 duration: 500, ··· 134 } 135 136 onMount(() => { 137 + utils.set(photoControls, { translateX: '-50%' }); 138 + utils.set(photoTrayCloseBtn, { translateX: '-50%', opacity: 0, scale: '0.75', bottom: '10px' }); 139 140 window.addEventListener('keyup', switchPhotoWithKey); 141 142 let contextMenuOpen = false; 143 window.CloseAllPopups.push(() => { 144 contextMenuOpen = false; 145 + utils.set(viewerContextMenu, { opacity: 1, rotate: '0deg' }); 146 147 + animate(viewerContextMenu, { 148 opacity: 0, 149 easing: 'easeInOutQuad', 150 rotate: '30deg', 151 duration: 100, 152 + onComplete: () => { 153 viewerContextMenu.style.display = 'none'; 154 } 155 }) ··· 173 if(contextMenuOpen){ 174 contextMenuOpen = false; 175 176 + utils.set(viewerContextMenu, { opacity: 1, rotate: '0deg' }); 177 178 + animate(viewerContextMenu, { 179 opacity: 0, 180 rotate: '30deg', 181 easing: 'easeInOutQuad', 182 duration: 100, 183 + onComplete: () => { 184 viewerContextMenu.style.display = 'none'; 185 } 186 }) ··· 191 viewerContextMenu.style.left = e.clientX + 'px'; 192 viewerContextMenu.style.display = 'block'; 193 194 + utils.set(viewerContextMenu, { opacity: 0, rotate: '-30deg' }); 195 196 + animate(viewerContextMenu, { 197 opacity: 1, 198 rotate: '0deg', 199 easing: 'easeInOutQuad', ··· 212 imageViewer.src = (window.OS === "windows" ? "http://photo.localhost/" : 'photo://localhost/') + window.PhotoViewerManager.CurrentPhoto()?.path.split('\\').join('/') + "?full"; 213 imageViewer.crossOrigin = 'anonymous'; 214 215 + animate(imageViewer, { 216 opacity: 1, 217 delay: 50, 218 duration: 150, ··· 296 if(photo && !isOpen){ 297 viewer.style.display = 'flex'; 298 299 + animate(viewer, { 300 opacity: 1, 301 easing: 'easeInOutQuad', 302 duration: 150 303 }); 304 305 + utils.set('.prev-button', { left: '-50px', top: '50%' }); 306 + utils.set('.next-button', { right: '-50px', top: '50%' }); 307 308 + animate('.prev-button', { left: '0', easing: 'easeInOutQuad', duration: 100 }); 309 + animate('.next-button', { right: '0', easing: 'easeInOutQuad', duration: 100 }); 310 311 window.CloseAllPopups.forEach(p => p()); 312 } else if(!photo && isOpen){ 313 + animate(viewer, { 314 opacity: 0, 315 easing: 'easeInOutQuad', 316 duration: 150, 317 + onComplete: () => { 318 viewer.style.display = 'none'; 319 } 320 }); 321 322 window.CloseAllPopups.forEach(p => p()); 323 324 + animate('.prev-button', { top: '75%', easing: 'easeInOutQuad', duration: 100 }); 325 + animate('.next-button', { top: '75%', easing: 'easeInOutQuad', duration: 100 }); 326 } 327 328 isOpen = photo != null; ··· 371 </div> 372 373 <div class="viewer-close viewer-button" onClick={() => window.PhotoViewerManager.Close()}> 374 + <div class="icon-small" style={{ width: '10px', margin: '0' }}> 375 <img draggable="false" src="/icon/x-solid.svg"></img> 376 </div> 377 </div> ··· 381 window.CloseAllPopups.forEach(p => p()); 382 window.PhotoViewerManager.PreviousPhoto(); 383 }}> 384 + <div class="icon-small" style={{ width: '15px', margin: '0' }}> 385 <img draggable="false" src="/icon/arrow-left-solid.svg"></img> 386 </div> 387 </div> ··· 390 window.CloseAllPopups.forEach(p => p()); 391 window.PhotoViewerManager.NextPhoto(); 392 }}> 393 + <div class="icon-small" style={{ width: '15px', margin: '0' }}> 394 <img draggable="false" src="/icon/arrow-right-solid.svg"></img> 395 </div> 396 </div> ··· 401 onClick={() => closeTray()} 402 ref={( el ) => photoTrayCloseBtn = el} 403 > 404 + <div class="icon-small" style={{ width: '12px', margin: '0' }}> 405 <img draggable="false" src="/icon/angle-down-solid.svg"></img> 406 </div> 407 </div> 408 409 <div class="control-buttons" ref={( el ) => photoControls = el}> 410 <div class="viewer-button" 411 + onMouseOver={( el ) => animate(el.currentTarget, { width: '40px', height: '40px', 'margin-left': '15px', 'margin-right': '15px', 'margin-top': '-10px' })} 412 + onMouseLeave={( el ) => animate(el.currentTarget, { width: '30px', height: '30px', 'margin-left': '20px', 'margin-right': '20px', 'margin-top': '0px' })} 413 onClick={() => { copyImage(); }}> 414 + <div class="icon-small" style={{ width: '12px', margin: '0' }}> 415 <img draggable="false" src="/icon/copy-solid.svg"></img> 416 </div> 417 </div> 418 <div class="viewer-button" style={{ width: '50px' }} 419 + onMouseOver={( el ) => animate(el.currentTarget, { width: '70px', height: '30px', 'margin-left': '10px', 'margin-right': '10px' })} 420 + onMouseLeave={( el ) => animate(el.currentTarget, { width: '50px', height: '30px', 'margin-left': '20px', 'margin-right': '20px' })} 421 ref={( el ) => trayButton = el} 422 onClick={() => openTray()} 423 > 424 + <div class="icon-small" style={{ width: '12px', margin: '0' }}> 425 <img draggable="false" src="/icon/angle-up-solid.svg"></img> 426 </div> 427 </div> 428 429 <div class="viewer-button" 430 ref={authorProfileButton!} 431 + onMouseOver={( el ) => animate(el.currentTarget, { width: '40px', height: '40px', 'margin-left': '15px', 'margin-right': '15px', 'margin-top': '-10px' })} 432 + onMouseLeave={( el ) => animate(el.currentTarget, { width: '30px', height: '30px', 'margin-left': '20px', 'margin-right': '20px', 'margin-top': '0px' })} 433 > 434 + <div class="icon-small" style={{ width: '12px', margin: '0' }}> 435 <img draggable="false" src="/icon/user-solid.svg"></img> 436 </div> 437 </div> 438 439 <div class="viewer-button" 440 + onMouseOver={( el ) => animate(el.currentTarget, { width: '40px', height: '40px', 'margin-left': '15px', 'margin-right': '15px', 'margin-top': '-10px' })} 441 + onMouseLeave={( el ) => animate(el.currentTarget, { width: '30px', height: '30px', 'margin-left': '20px', 'margin-right': '20px', 'margin-top': '0px' })} 442 onClick={() => window.ConfirmationBoxManager.SetConfirmationBox("Are you sure you want to delete this photo?", async () => { invoke("delete_photo", { 443 + path: window.PhotoViewerManager.CurrentPhoto()?.path 444 }); 445 })}> 446 + <div class="icon-small" style={{ width: '12px', margin: '0' }}> 447 <img draggable="false" src="/icon/trash-solid.svg"></img> 448 </div> 449 </div>
+40 -27
src/Components/SettingsMenu.tsx
··· 1 import { onCleanup, onMount, Show } from "solid-js"; 2 import { bytesToFormatted } from "../utils"; 3 import { invoke } from '@tauri-apps/api/core'; 4 - import anime from "animejs"; 5 import { ViewState } from "./Managers/ViewManager"; 6 7 let SettingsMenu = () => { 8 let sliderBar: HTMLElement; ··· 17 let closeWithKey = ( e: KeyboardEvent ) => { 18 if(e.key === 'Escape'){ 19 window.ViewManager.ChangeState(ViewState.PHOTO_LIST); 20 - anime({ 21 - targets: '.settings', 22 opacity: 0, 23 translateX: '500px', 24 easing: 'easeInOutQuad', 25 duration: 250, 26 - complete: () => { 27 - anime.set('.settings', { display: 'none' }); 28 } 29 }) 30 } ··· 34 if(await invoke('get_config_value_string', { key: 'transparent' }) === "true"){ 35 invoke('set_config_value_string', { key: 'transparent', value: 'true' }); 36 37 - anime({ targets: document.body, background: 'rgba(0, 0, 0, 0.5)', easing: 'linear', duration: 100 }); 38 - anime({ targets: '.settings', background: 'rgba(0, 0, 0, 0.5)', easing: 'linear', duration: 100 }); 39 } else{ 40 invoke('set_config_value_string', { key: 'transparent', value: 'false' }); 41 42 - anime({ targets: document.body, background: 'rgba(0, 0, 0, 1)', easing: 'linear', duration: 100 }); 43 - anime({ targets: '.settings', background: 'rgba(0, 0, 0, 0)', easing: 'linear', duration: 100 }); 44 } 45 46 let sliderMouseDown = false; ··· 57 58 if(!sliderMouseDown){ 59 sliderPos = sliderPos + (width / 2 - buttons[currentButton] - sliderPos) * 0.25; 60 - anime.set(sliderBar, { translateX: sliderPos }); 61 62 settingsContainer.style.left = (sliderPos - (width / 2 - buttons[0])) * sliderScale + 'px'; 63 } 64 } 65 66 render(); 67 - anime.set(sliderBar, { translateX: sliderPos }); 68 69 sliderBar.addEventListener('touchstart', ( e: TouchEvent ) => { 70 sliderMouseDown = true; ··· 73 74 window.addEventListener('touchmove', ( e: TouchEvent ) => { 75 if(sliderMouseDown){ 76 - anime.set(sliderBar, { translateX: sliderPos - (mouseStartX - e.touches[0].clientX) }); 77 settingsContainer.style.left = (sliderPos - (mouseStartX - e.touches[0].clientX) - (width / 2 - buttons[0])) * sliderScale + 'px'; 78 } 79 }) ··· 84 if(sliderMouseDown){ 85 sliderPos = sliderPos - (mouseStartX - e.touches[0].clientX); 86 87 - anime.set(sliderBar, { translateX: sliderPos - (mouseStartX - e.touches[0].clientX) }); 88 sliderMouseDown = false; 89 90 if(Math.abs(mouseStartX - e.touches[0].clientX) > 50){ ··· 118 119 window.addEventListener('mousemove', ( e: MouseEvent ) => { 120 if(sliderMouseDown){ 121 - anime.set(sliderBar, { translateX: sliderPos - (mouseStartX - e.clientX) }); 122 settingsContainer.style.left = sliderPos - (mouseStartX - e.clientX) + 'px'; 123 settingsContainer.style.left = (sliderPos - (mouseStartX - e.clientX) - (width / 2 - buttons[0])) * sliderScale + 'px'; 124 } ··· 128 if(sliderMouseDown){ 129 sliderPos = sliderPos - (mouseStartX - e.clientX); 130 131 - anime.set(sliderBar, { translateX: sliderPos - (mouseStartX - e.clientX) }); 132 sliderMouseDown = false; 133 134 if(Math.abs(mouseStartX - e.clientX) > 50){ ··· 160 sliderPos = width / 2 - buttons[currentButton]; 161 sliderScale = width / (buttons[1] - buttons[0]); 162 163 - anime.set(sliderBar, { translateX: sliderPos }); 164 }) 165 166 sliderBar.addEventListener('wheel', ( e: WheelEvent ) => { ··· 180 181 return ( 182 <div class="settings"> 183 <div class="settings-container" ref={( el ) => settingsContainer = el}> 184 <div class="settings-block"> 185 <h1>Storage Settings</h1> ··· 199 200 <label for="start-in-bg-check"> 201 <div class="selection-box"> 202 - <div class="icon" style={{ width: '10px', margin: '0', display: 'inline-flex' }}> 203 <img draggable="false" width="10" height="10" src="/icon/check-solid.svg"></img> 204 </div> 205 </div> ··· 223 224 <label for="start-with-win-check"> 225 <div class="selection-box"> 226 - <div class="icon" style={{ width: '10px', margin: '0', display: 'inline-flex' }}> 227 <img draggable="false" width="10" height="10" src="/icon/check-solid.svg"></img> 228 </div> 229 </div> ··· 238 if(el.target.checked){ 239 invoke('set_config_value_string', { key: 'transparent', value: 'true' }); 240 241 - anime({ targets: document.body, background: 'rgba(0, 0, 0, 0.5)', easing: 'linear', duration: 100 }); 242 - anime({ targets: '.settings', background: 'rgba(0, 0, 0, 0.5)', easing: 'linear', duration: 100 }); 243 } else{ 244 invoke('set_config_value_string', { key: 'transparent', value: 'false' }); 245 246 - anime({ targets: document.body, background: 'rgba(0, 0, 0, 1)', easing: 'linear', duration: 100 }); 247 - anime({ targets: '.settings', background: 'rgba(0, 0, 0, 0)', easing: 'linear', duration: 100 }); 248 } 249 }} /> 250 Window Transparency 251 252 <label for="transparent-check"> 253 <div class="selection-box"> 254 - <div class="icon" style={{ width: '10px', margin: '0', display: 'inline-flex' }}> 255 <img draggable="false" width="10" height="10" src="/icon/check-solid.svg"></img> 256 </div> 257 </div> ··· 282 await invoke('change_final_path', { newPath: finalPathData }); 283 window.location.reload(); 284 285 - anime({ 286 - targets: '.settings', 287 opacity: 0, 288 translateX: '500px', 289 easing: 'easeInOutQuad', 290 duration: 250, 291 - complete: () => { 292 - anime.set('.settings', { display: 'none' }); 293 } 294 }) 295
··· 1 import { onCleanup, onMount, Show } from "solid-js"; 2 import { bytesToFormatted } from "../utils"; 3 import { invoke } from '@tauri-apps/api/core'; 4 import { ViewState } from "./Managers/ViewManager"; 5 + import { animate, utils } from "animejs"; 6 7 let SettingsMenu = () => { 8 let sliderBar: HTMLElement; ··· 17 let closeWithKey = ( e: KeyboardEvent ) => { 18 if(e.key === 'Escape'){ 19 window.ViewManager.ChangeState(ViewState.PHOTO_LIST); 20 + animate('.settings', { 21 opacity: 0, 22 translateX: '500px', 23 easing: 'easeInOutQuad', 24 duration: 250, 25 + onComplete: () => { 26 + utils.set('.settings', { display: 'none' }); 27 } 28 }) 29 } ··· 33 if(await invoke('get_config_value_string', { key: 'transparent' }) === "true"){ 34 invoke('set_config_value_string', { key: 'transparent', value: 'true' }); 35 36 + animate(document.body, { background: 'rgba(0, 0, 0, 0.5)', easing: 'linear', duration: 100 }); 37 + animate('.settings', { background: 'rgba(0, 0, 0, 0.5)', easing: 'linear', duration: 100 }); 38 } else{ 39 invoke('set_config_value_string', { key: 'transparent', value: 'false' }); 40 41 + animate(document.body, { background: 'rgba(0, 0, 0, 1)', easing: 'linear', duration: 100 }); 42 + animate('.settings', { background: 'rgba(0, 0, 0, 0)', easing: 'linear', duration: 100 }); 43 } 44 45 let sliderMouseDown = false; ··· 56 57 if(!sliderMouseDown){ 58 sliderPos = sliderPos + (width / 2 - buttons[currentButton] - sliderPos) * 0.25; 59 + utils.set(sliderBar, { translateX: sliderPos }); 60 61 settingsContainer.style.left = (sliderPos - (width / 2 - buttons[0])) * sliderScale + 'px'; 62 } 63 } 64 65 render(); 66 + utils.set(sliderBar, { translateX: sliderPos }); 67 68 sliderBar.addEventListener('touchstart', ( e: TouchEvent ) => { 69 sliderMouseDown = true; ··· 72 73 window.addEventListener('touchmove', ( e: TouchEvent ) => { 74 if(sliderMouseDown){ 75 + utils.set(sliderBar, { translateX: sliderPos - (mouseStartX - e.touches[0].clientX) }); 76 settingsContainer.style.left = (sliderPos - (mouseStartX - e.touches[0].clientX) - (width / 2 - buttons[0])) * sliderScale + 'px'; 77 } 78 }) ··· 83 if(sliderMouseDown){ 84 sliderPos = sliderPos - (mouseStartX - e.touches[0].clientX); 85 86 + utils.set(sliderBar, { translateX: sliderPos - (mouseStartX - e.touches[0].clientX) }); 87 sliderMouseDown = false; 88 89 if(Math.abs(mouseStartX - e.touches[0].clientX) > 50){ ··· 117 118 window.addEventListener('mousemove', ( e: MouseEvent ) => { 119 if(sliderMouseDown){ 120 + utils.set(sliderBar, { translateX: sliderPos - (mouseStartX - e.clientX) }); 121 settingsContainer.style.left = sliderPos - (mouseStartX - e.clientX) + 'px'; 122 settingsContainer.style.left = (sliderPos - (mouseStartX - e.clientX) - (width / 2 - buttons[0])) * sliderScale + 'px'; 123 } ··· 127 if(sliderMouseDown){ 128 sliderPos = sliderPos - (mouseStartX - e.clientX); 129 130 + utils.set(sliderBar, { translateX: sliderPos - (mouseStartX - e.clientX) }); 131 sliderMouseDown = false; 132 133 if(Math.abs(mouseStartX - e.clientX) > 50){ ··· 159 sliderPos = width / 2 - buttons[currentButton]; 160 sliderScale = width / (buttons[1] - buttons[0]); 161 162 + utils.set(sliderBar, { translateX: sliderPos }); 163 }) 164 165 sliderBar.addEventListener('wheel', ( e: WheelEvent ) => { ··· 179 180 return ( 181 <div class="settings"> 182 + <div class="settings-close" onClick={() => { 183 + window.ViewManager.ChangeState(ViewState.PHOTO_LIST); 184 + animate('.settings', 185 + { 186 + opacity: 0, 187 + translateX: '500px', 188 + easing: 'easeInOutQuad', 189 + duration: 250, 190 + onComplete: () => { 191 + utils.set('.settings', { display: 'none' }); 192 + } 193 + }) 194 + }}> 195 + <div class="icon"><img draggable="false" src="/icon/x-solid.svg"></img></div> 196 + </div> 197 <div class="settings-container" ref={( el ) => settingsContainer = el}> 198 <div class="settings-block"> 199 <h1>Storage Settings</h1> ··· 213 214 <label for="start-in-bg-check"> 215 <div class="selection-box"> 216 + <div class="icon-small" style={{ margin: '0', display: 'inline-flex' }}> 217 <img draggable="false" width="10" height="10" src="/icon/check-solid.svg"></img> 218 </div> 219 </div> ··· 237 238 <label for="start-with-win-check"> 239 <div class="selection-box"> 240 + <div class="icon-small" style={{ margin: '0', display: 'inline-flex' }}> 241 <img draggable="false" width="10" height="10" src="/icon/check-solid.svg"></img> 242 </div> 243 </div> ··· 252 if(el.target.checked){ 253 invoke('set_config_value_string', { key: 'transparent', value: 'true' }); 254 255 + animate(document.body, { background: 'rgba(0, 0, 0, 0.5)', easing: 'linear', duration: 100 }); 256 + animate('.settings', { background: 'rgba(0, 0, 0, 0.5)', easing: 'linear', duration: 100 }); 257 } else{ 258 invoke('set_config_value_string', { key: 'transparent', value: 'false' }); 259 260 + animate(document.body, { background: 'rgba(0, 0, 0, 1)', easing: 'linear', duration: 100 }); 261 + animate('.settings', { background: 'rgba(0, 0, 0, 0)', easing: 'linear', duration: 100 }); 262 } 263 }} /> 264 Window Transparency 265 266 <label for="transparent-check"> 267 <div class="selection-box"> 268 + <div class="icon-small" style={{ margin: '0', display: 'inline-flex' }}> 269 <img draggable="false" width="10" height="10" src="/icon/check-solid.svg"></img> 270 </div> 271 </div> ··· 296 await invoke('change_final_path', { newPath: finalPathData }); 297 window.location.reload(); 298 299 + animate('.settings', { 300 opacity: 0, 301 translateX: '500px', 302 easing: 'easeInOutQuad', 303 duration: 250, 304 + onComplete: () => { 305 + utils.set('.settings', { display: 'none' }); 306 } 307 }) 308
+71
src/css/filters.css
···
··· 1 + 2 + .filter-options{ 3 + position: fixed; 4 + top: 10px; 5 + left: 10px; 6 + } 7 + 8 + .filter-container{ 9 + display: none; 10 + position: fixed; 11 + bottom: 0; 12 + left: 0; 13 + width: 100vw; 14 + padding: 10px 200px; 15 + background: rgba(85, 85, 85, 0.904); 16 + transform: translateY(10px); 17 + color: #fff; 18 + text-align: center; 19 + box-shadow: #0005 0 0 10px; 20 + opacity: 0; 21 + } 22 + 23 + .filter-container > .filter-title{ 24 + font-size: 30px; 25 + } 26 + 27 + .filter-type-select{ 28 + display: flex; 29 + justify-content: center; 30 + align-items: center; 31 + width: 75%; 32 + margin: auto; 33 + } 34 + 35 + .filter-type-select > div{ 36 + width: 100%; 37 + border: #fff 4px solid; 38 + border-left: #fff 2px solid; 39 + border-right: #fff 2px solid; 40 + padding: 5px 0; 41 + cursor: pointer; 42 + user-select: none; 43 + -webkit-user-select: none; 44 + } 45 + 46 + .filter-type-select > div:first-child{ 47 + border-left: #fff 4px solid; 48 + border-radius: 10px 0 0 10px; 49 + } 50 + 51 + .filter-type-select > div:last-child{ 52 + border-right: #fff 4px solid; 53 + border-radius: 0 10px 10px 0; 54 + } 55 + 56 + .filter-type-select > .selected-filter{ 57 + background: #00ccff55; 58 + } 59 + 60 + .filter-search{ 61 + margin-top: 10px; 62 + padding: 5px; 63 + border: #fff 4px solid; 64 + border-radius: 10px; 65 + background: #0008; 66 + outline: none; 67 + color: white; 68 + font-size: 15px; 69 + font-family: 'Rubik'; 70 + width: calc(75% - 18px); 71 + }
+40
src/css/icons.css
···
··· 1 + .icon{ 2 + width: 40px; 3 + height: 40px; 4 + padding: 10px; 5 + filter: invert(100%); 6 + display: flex; 7 + align-items: center; 8 + justify-content: center; 9 + height: 100%; 10 + cursor: pointer; 11 + user-select: none; 12 + -webkit-user-select: none; 13 + } 14 + 15 + .icon-small{ 16 + filter: invert(100%); 17 + display: flex; 18 + align-items: center; 19 + justify-content: center; 20 + height: 100%; 21 + } 22 + 23 + .icon-label{ 24 + margin-top: -20px; 25 + margin-right: -200px; 26 + width: 200px; 27 + color: white; 28 + pointer-events: none; 29 + transform: translate(20px, -9px); 30 + opacity: 0; 31 + transition: 0.25s; 32 + user-select: none; 33 + -webkit-user-select: none; 34 + } 35 + 36 + .icon:hover ~ .icon-label{ 37 + opacity: 1; 38 + transform: translate(40px, -9px); 39 + } 40 +
+23
src/css/list.css
···
··· 1 + .photo-list{ 2 + width: 100%; 3 + height: 100%; 4 + position: fixed; 5 + top: 0; 6 + left: 0; 7 + overflow: hidden; 8 + } 9 + 10 + .scroll-to-top{ 11 + position: fixed; 12 + bottom: 10px; 13 + right: 10px; 14 + color: white; 15 + width: 40px; 16 + height: 40px; 17 + cursor: pointer; 18 + border-radius: 50%; 19 + border: 2px solid white; 20 + display: flex; 21 + justify-content: center; 22 + align-items: center; 23 + }
+84
src/css/settings.css
···
··· 1 + .settings{ 2 + position: fixed; 3 + top: 0; 4 + left: 0; 5 + width: 100%; 6 + height: 100%; 7 + background: rgba(0, 0, 0, 0.4); 8 + } 9 + 10 + .settings-container{ 11 + position: fixed; 12 + top: 50px; 13 + left: 0px; 14 + width: 200%; 15 + height: calc(100% - 100px); 16 + display: flex; 17 + } 18 + 19 + .settings-close{ 20 + position: absolute; 21 + top: 10px; 22 + left: 10px; 23 + z-index: 100; 24 + cursor: pointer; 25 + user-select: none; 26 + width: 40px; 27 + height: 40px; 28 + } 29 + 30 + .settings-block{ 31 + width: 50%; 32 + height: 100%; 33 + color: white; 34 + text-align: center; 35 + } 36 + 37 + .selector{ 38 + padding: 10px 20px; 39 + border-radius: 10px; 40 + background: #000a; 41 + display: inline-block; 42 + margin: 10px; 43 + } 44 + 45 + .selector .selection-box{ 46 + height: 20px; 47 + background: #777a; 48 + margin: 5px -10px 0 -10px; 49 + border-radius: 8px; 50 + user-select: none; 51 + -webkit-user-select: none; 52 + cursor: pointer; 53 + transition: 0.25s; 54 + color: #fff1; 55 + } 56 + 57 + .selector .selection-box:hover{ 58 + height: 20px; 59 + background: #777a; 60 + margin: 5px -10px 0 -10px; 61 + border-radius: 8px; 62 + user-select: none; 63 + -webkit-user-select: none; 64 + cursor: pointer; 65 + transition: 0.25s; 66 + color: #fff5; 67 + } 68 + 69 + .selector input{ 70 + display: none; 71 + } 72 + 73 + .selector input:checked ~ label .selection-box{ 74 + background: rgba(0, 146, 204, 0.705); 75 + color: #fff; 76 + } 77 + 78 + .path{ 79 + padding: 5px 10px; 80 + background: #000a; 81 + border-radius: 5px; 82 + margin-left: 5px; 83 + cursor: pointer; 84 + }
+55
src/css/slide-bar.css
···
··· 1 + .slide-bar{ 2 + position: fixed; 3 + bottom: 0; 4 + left: 0; 5 + width: 100%; 6 + height: 50px; 7 + border-top: #aaa 1px solid; 8 + overflow-x: hidden; 9 + mask-image: linear-gradient(to left, #0000 0%, #000 20%, #000 80%, #0000 100%); 10 + background: #aaa2; 11 + box-shadow: #000 0 0 10px; 12 + } 13 + 14 + .inner-slide-bar{ 15 + display: flex; 16 + height: 50px; 17 + width: 200%; 18 + color: white; 19 + align-items: center; 20 + cursor: pointer; 21 + user-select: none; 22 + -webkit-user-select: none; 23 + } 24 + 25 + .slider-dot{ 26 + width: 5px; 27 + height: 5px; 28 + border-radius: 5px; 29 + background: #aaa; 30 + margin: auto 25px; 31 + } 32 + 33 + .slider-text{ 34 + width: 200px; 35 + text-align: center; 36 + height: 50px; 37 + display: flex; 38 + justify-content: center; 39 + align-items: center; 40 + color: #aaa; 41 + transition: 0.25s; 42 + } 43 + 44 + .slider-text:hover{ 45 + color: #fff; 46 + } 47 + 48 + .slide-bar-tri{ 49 + position: fixed; 50 + bottom: 40px; 51 + left: 50%; 52 + transform: translateX(-50%); 53 + border: transparent solid 5px; 54 + border-top: #fff solid 5px; 55 + }
+82
src/css/tray.css
···
··· 1 + .photo-tray{ 2 + position: fixed; 3 + bottom: -300px; 4 + left: 0; 5 + width: 100%; 6 + height: 300px; 7 + background: rgba(43, 43, 43, 0.76); 8 + backdrop-filter: blur(10px); 9 + -webkit-backdrop-filter: blur(10px); 10 + box-shadow: #0008 0 0 10px; 11 + padding-bottom: 150px; 12 + } 13 + 14 + .photo-tray-close{ 15 + position: fixed; 16 + bottom: 160px; 17 + left: 50%; 18 + transform: translate(-50%); 19 + color: white; 20 + background: #8885; 21 + backdrop-filter: blur(10px); 22 + -webkit-backdrop-filter: blur(10px); 23 + box-shadow: #0008 0 0 10px; 24 + display: flex; 25 + justify-content: center; 26 + align-items: center; 27 + height: 30px; 28 + width: 50px; 29 + border-radius: 50px; 30 + cursor: pointer; 31 + font-size: 12px; 32 + user-select: none; 33 + -webkit-user-select: none; 34 + transition: 0.25s width; 35 + } 36 + 37 + .photo-tray-close:hover{ 38 + width: 70px; 39 + } 40 + 41 + .photo-tray-columns{ 42 + width: 100%; 43 + height: 100%; 44 + display: flex; 45 + color: white; 46 + text-align: center; 47 + } 48 + 49 + .photo-tray-column{ 50 + height: 100%; 51 + width: 100%; 52 + scrollbar-width: thin; 53 + overflow-y: auto; 54 + overflow-x: hidden; 55 + mask-image: linear-gradient(to bottom, #0000 0%, #000 10%, #000 90%, #0000 100%); 56 + } 57 + 58 + .tray-heading{ 59 + font-weight: bold; 60 + font-size: 20px; 61 + } 62 + 63 + .world-tags{ 64 + display: flex; 65 + width: 100%; 66 + justify-content: center; 67 + align-items: center; 68 + } 69 + 70 + .world-tags div{ 71 + padding: 0 10px; 72 + color: #bbb; 73 + transition: 0.25s; 74 + } 75 + 76 + .world-tags div:hover{ 77 + color: #ddd; 78 + } 79 + 80 + .world-name{ 81 + font-size: 17px; 82 + }
+169
src/css/viewer.css
···
··· 1 + 2 + .photo-container{ 3 + width: 100%; 4 + height: 100%; 5 + } 6 + 7 + .photo-container-bg{ 8 + width: 100%; 9 + height: 100%; 10 + position: fixed; 11 + top: 0; 12 + left: 0; 13 + z-index: -1; 14 + /* filter: blur(100px); */ 15 + } 16 + 17 + .single-photo-container{ 18 + margin: 10px; 19 + display: inline-block; 20 + } 21 + 22 + .photo-viewer{ 23 + justify-content: center; 24 + width: 100%; 25 + height: 100%; 26 + position: fixed; 27 + top: 0; 28 + left: 0; 29 + z-index: 5; 30 + background: #0009; 31 + opacity: 0; 32 + display: none; 33 + } 34 + 35 + .photo-context-menu{ 36 + position: fixed; 37 + top: 0; 38 + left: 0; 39 + padding: 10px; 40 + border-radius: 5px; 41 + background: #555a; 42 + color: #aaa; 43 + box-shadow: #0005 0 0 10px; 44 + opacity: 0; 45 + } 46 + 47 + .photo-context-menu > div{ 48 + padding: 2px 10px; 49 + width: 100; 50 + text-align: center; 51 + transition: 0.1s; 52 + } 53 + 54 + .photo-context-menu > div:hover{ 55 + color: #fff; 56 + cursor: pointer; 57 + user-select: none; 58 + -webkit-user-select: none; 59 + } 60 + 61 + .image-container{ 62 + height: 100%; 63 + background-size: contain !important; 64 + background-repeat: no-repeat !important; 65 + background-position: center !important; 66 + opacity: 0; 67 + } 68 + 69 + .viewer-button{ 70 + color: white; 71 + width: 30px; 72 + height: 30px; 73 + display: flex; 74 + justify-content: center; 75 + align-items: center; 76 + border-radius: 50px; 77 + font-size: 12px; 78 + background: #8885; 79 + user-select: none; 80 + -webkit-user-select: none; 81 + cursor: pointer; 82 + z-index: 7; 83 + box-shadow: #0008 0 0 10px; 84 + } 85 + 86 + .viewer-close{ 87 + position: fixed; 88 + top: 10px; 89 + right: 10px; 90 + width: 35px; 91 + height: 35px; 92 + } 93 + 94 + .prev-button{ 95 + transition: 0.25s; 96 + position: fixed; 97 + top: 50%; 98 + left: 0; 99 + color: white; 100 + width: 50px; 101 + height: 150px; 102 + display: flex; 103 + justify-content: center; 104 + align-items: center; 105 + transform: translateY(-50%); 106 + background: rgba(255, 255, 255, 0.144); 107 + border-radius: 0 15px 15px 0; 108 + cursor: pointer; 109 + user-select: none; 110 + -webkit-user-select: none; 111 + box-shadow: #000 0 0 10px; 112 + } 113 + 114 + .prev-button:hover{ 115 + background: rgba(255, 255, 255, 0.349); 116 + } 117 + 118 + .next-button{ 119 + transition: 0.25s; 120 + position: fixed; 121 + top: 50%; 122 + right: 0; 123 + color: white; 124 + width: 50px; 125 + height: 150px; 126 + display: flex; 127 + justify-content: center; 128 + align-items: center; 129 + transform: translateY(-50%); 130 + background: rgba(255, 255, 255, 0.144); 131 + border-radius: 15px 0 0 15px; 132 + cursor: pointer; 133 + user-select: none; 134 + -webkit-user-select: none; 135 + box-shadow: #000 0 0 10px; 136 + } 137 + 138 + .next-button:hover{ 139 + background: rgba(255, 255, 255, 0.349); 140 + } 141 + 142 + .control-buttons{ 143 + position: fixed; 144 + bottom: 10px; 145 + left: 50%; 146 + transform: translateX(-50%); 147 + display: flex; 148 + } 149 + 150 + .control-buttons div{ 151 + margin: 0 20px; 152 + } 153 + 154 + .copy-notif{ 155 + position: fixed; 156 + top: 40px; 157 + left: 50%; 158 + color: white; 159 + transform: translateX(-50%) translateY(-100px); 160 + background: #8885; 161 + padding: 10px 40px; 162 + backdrop-filter: blur(10px); 163 + -webkit-backdrop-filter: blur(10px); 164 + border-radius: 50px; 165 + box-shadow: #000 0 0 10px; 166 + z-index: 12; 167 + opacity: 0; 168 + pointer-events: none; 169 + }
+9
src/index.tsx
··· 22 23 window.oncontextmenu = ( e ) => e.preventDefault(); 24 25 import "./styles.css"; 26 import App from "./Components/App"; 27 import { invoke } from "@tauri-apps/api/core"; 28
··· 22 23 window.oncontextmenu = ( e ) => e.preventDefault(); 24 25 + import './css/icons.css'; 26 + import './css/tray.css'; 27 + import './css/settings.css'; 28 + import './css/slide-bar.css'; 29 + import './css/viewer.css'; 30 + import './css/filters.css'; 31 + import './css/list.css'; 32 + 33 import "./styles.css"; 34 + 35 import App from "./Components/App"; 36 import { invoke } from "@tauri-apps/api/core"; 37
+6 -779
src/styles.css
··· 7 background: #000; 8 margin: 0; 9 font-family: Rubik, 'Courier New'; 10 } 11 12 .loading{ ··· 24 align-items: center; 25 } 26 27 - .navbar{ 28 - background: #555a; 29 - position: fixed; 30 - top: 0; 31 - left: 0; 32 - width: 100%; 33 - margin-top: -50px; 34 - padding-top: 50px; 35 - height: 50px; 36 - display: flex; 37 - backdrop-filter: blur(10px); 38 - -webkit-backdrop-filter: blur(10px); 39 - z-index: 10; 40 - box-shadow: #000 0 0 10px; 41 - } 42 - 43 - .navbar .tabs{ 44 - width: calc(100% - 450px); 45 - height: 100%; 46 - display: flex; 47 - } 48 - 49 - .navbar .account{ 50 - width: 100px; 51 - height: 100%; 52 - display: flex; 53 - justify-content: center; 54 - align-items: center; 55 - transition: 0.1s; 56 - cursor: pointer; 57 - user-select: none; 58 - -webkit-user-select: none; 59 - } 60 - 61 - .navbar .account:hover{ 62 - background: #0005; 63 - } 64 - 65 - .navbar .control-lights{ 66 - width: 150px; 67 - height: 50px; 68 - display: flex; 69 - justify-content: center; 70 - align-items: center; 71 - } 72 - 73 - .control-lights .light{ 74 - user-select: none; 75 - -webkit-user-select: none; 76 - font-size: 20px; 77 - text-align: center; 78 - color: white; 79 - width: 100%; 80 - cursor: pointer; 81 - display: flex; 82 - justify-content: center; 83 - align-items: center; 84 - height: 50px; 85 - filter: invert(100%); 86 - } 87 - 88 - .control-lights .light:hover{ 89 - background: #fff5; 90 - } 91 - 92 - .control-lights .light img{ 93 - width: 25%; 94 - } 95 - 96 - .icon{ 97 - width: 15px; 98 - filter: invert(100%); 99 - display: flex; 100 - align-items: center; 101 - justify-content: center; 102 - height: 100%; 103 - } 104 - 105 - .icon-label{ 106 - margin-top: -20px; 107 - margin-right: -200px; 108 - width: 200px; 109 - color: white; 110 - pointer-events: none; 111 - transform: translate(40px, -19px); 112 - opacity: 0; 113 - transition: 0.25s; 114 - user-select: none; 115 - -webkit-user-select: none; 116 - } 117 - 118 - .icon:hover ~ .icon-label{ 119 - opacity: 1; 120 - transform: translate(60px, -19px); 121 - } 122 - 123 - .user-pfp{ 124 - width: 35px; 125 - height: 35px; 126 - background-size: cover !important; 127 - background-position: center !important; 128 - border-radius: 50%; 129 - margin-right: 10px; 130 - } 131 - 132 - .account-dropdown{ 133 - font-size: 20px; 134 - color: white; 135 - } 136 - 137 - .nav-tab{ 138 - color: white; 139 - width: 150px; 140 - height: 100%; 141 - transition: 0.1s; 142 - cursor: pointer; 143 - user-select: none; 144 - -webkit-user-select: none; 145 - justify-content: center; 146 - align-items: center; 147 - display: flex; 148 - } 149 - 150 - .nav-tab:hover{ 151 - background: #0005; 152 - } 153 - 154 - .dropdown{ 155 - position: fixed; 156 - right: 125px; 157 - top: 60px; 158 - background: #555a; 159 - height: 60px; 160 - width: 150px; 161 - border-radius: 5px; 162 - backdrop-filter: blur(5px); 163 - z-index: 10; 164 - } 165 - 166 - .dropdown-button{ 167 - width: 100%; 168 - text-align: center; 169 - padding: 5.5px 0; 170 - color: #aaa; 171 - cursor: pointer; 172 - user-select: none; 173 - -webkit-user-select: none; 174 - transition: 0.1s; 175 - } 176 - 177 - .dropdown-button:hover{ 178 - color: #fff; 179 - } 180 - 181 - .photo-list{ 182 - width: 100%; 183 - height: 100%; 184 - position: fixed; 185 - top: 0; 186 - left: 0; 187 - overflow: hidden; 188 - } 189 - 190 - .filter-options{ 191 - position: fixed; 192 - top: 55px; 193 - left: 5px; 194 - width: 40px; 195 - height: 50px; 196 - } 197 - 198 - .filter-options img{ 199 - cursor: pointer; 200 - user-select: none; 201 - -webkit-user-select: none; 202 - } 203 - 204 - .filter-container{ 205 - display: none; 206 - position: fixed; 207 - bottom: 0; 208 - left: 50%; 209 - width: 600px; 210 - height: 83px; 211 - transform: translate(-50%); 212 - padding: 10px; 213 - border-radius: 5px 5px 0 0; 214 - backdrop-filter: blur(5px); 215 - -webkit-backdrop-filter: blur(5px); 216 - background: #555a; 217 - color: #fff; 218 - text-align: center; 219 - box-shadow: #0005 0 0 10px; 220 - opacity: 0; 221 - } 222 - 223 - .filter-container > .filter-title{ 224 - font-size: 30px; 225 - } 226 - 227 - .filter-type-select{ 228 - display: flex; 229 - justify-content: center; 230 - align-items: center; 231 - width: 75%; 232 - margin: auto; 233 - } 234 - 235 - .filter-type-select > div{ 236 - width: 100%; 237 - border: #fff 4px solid; 238 - border-left: #fff 2px solid; 239 - border-right: #fff 2px solid; 240 - padding: 5px 0; 241 - cursor: pointer; 242 - user-select: none; 243 - -webkit-user-select: none; 244 - } 245 - 246 - .filter-type-select > div:first-child{ 247 - border-left: #fff 4px solid; 248 - border-radius: 10px 0 0 10px; 249 - } 250 - 251 - .filter-type-select > div:last-child{ 252 - border-right: #fff 4px solid; 253 - border-radius: 0 10px 10px 0; 254 - } 255 - 256 - .filter-type-select > .selected-filter{ 257 - background: #00ccff55; 258 - } 259 - 260 - .filter-search{ 261 - margin-top: 10px; 262 - padding: 5px; 263 - border: #fff 4px solid; 264 - border-radius: 10px; 265 - background: #0008; 266 - outline: none; 267 - color: white; 268 - font-size: 15px; 269 - font-family: 'Rubik'; 270 - width: calc(75% - 18px); 271 - } 272 - 273 - .date-list{ 274 - mask-image: linear-gradient(to bottom, #0000, #000, #0000); 275 - overflow: auto; 276 - scrollbar-width: thin; 277 - height: calc(100% - 100px); 278 - padding: 50px 0; 279 - } 280 - 281 - .date-list-date{ 282 - padding: 10px; 283 - user-select: none; 284 - -webkit-user-select: none; 285 - cursor: pointer; 286 - transition: 0.1s; 287 - border-radius: 10px; 288 - } 289 - 290 - .date-list-date:hover{ 291 - background: #0005; 292 - box-shadow: inset #0005 0 0 10px; 293 - } 294 - 295 - .photo-tree-loading{ 296 - position: fixed; 297 - top: 0; 298 - left: 0; 299 - width: 100%; 300 - height: 100%; 301 - display: flex; 302 - justify-content: center; 303 - align-items: center; 304 - color: white; 305 - font-size: 20px; 306 - } 307 - 308 - .loading-bar{ 309 - width: 500px; 310 - height: 8px; 311 - border-radius: 12px; 312 - background: #333; 313 - margin-top: 10px; 314 - padding: 2px; 315 - } 316 - 317 - .loading-bar-inner{ 318 - width: 0%; 319 - height: 8px; 320 - border-radius: 18px; 321 - background: #00ccff; 322 - } 323 - 324 - .photo-container{ 325 - width: 100%; 326 - height: 100%; 327 - } 328 - 329 - .photo-container-bg{ 330 - width: 100%; 331 - height: 100%; 332 - position: fixed; 333 - top: 0; 334 - left: 0; 335 - z-index: -1; 336 - /* filter: blur(100px); */ 337 - } 338 - 339 - .single-photo-container{ 340 - margin: 10px; 341 - display: inline-block; 342 - } 343 - 344 - .photo-viewer{ 345 - justify-content: center; 346 - width: 100%; 347 - height: 100%; 348 - position: fixed; 349 - top: 0; 350 - left: 0; 351 - z-index: 5; 352 - background: #0009; 353 - backdrop-filter: blur(75px); 354 - -webkit-backdrop-filter: blur(75px); 355 - opacity: 0; 356 - display: none; 357 - } 358 - 359 - .photo-context-menu{ 360 - position: fixed; 361 - top: 0; 362 - left: 0; 363 - padding: 10px; 364 - border-radius: 5px; 365 - backdrop-filter: blur(5px); 366 - -webkit-backdrop-filter: blur(5px); 367 - background: #555a; 368 - color: #aaa; 369 - box-shadow: #0005 0 0 10px; 370 - opacity: 0; 371 - } 372 - 373 - .photo-context-menu > div{ 374 - padding: 2px 10px; 375 - width: calc(100% - 10px); 376 - text-align: center; 377 - transition: 0.1s; 378 - } 379 - 380 - .photo-context-menu > div:hover{ 381 - color: #fff; 382 - cursor: pointer; 383 - user-select: none; 384 - -webkit-user-select: none; 385 - } 386 - 387 - .image-container{ 388 - height: 100%; 389 - background-size: contain !important; 390 - background-repeat: no-repeat !important; 391 - background-position: center !important; 392 - opacity: 0; 393 - } 394 - 395 - .viewer-button{ 396 - color: white; 397 - width: 30px; 398 - height: 30px; 399 - display: flex; 400 - justify-content: center; 401 - align-items: center; 402 - border-radius: 50px; 403 - font-size: 12px; 404 - background: #8885; 405 - backdrop-filter: blur(10px); 406 - -webkit-backdrop-filter: blur(10px); 407 - user-select: none; 408 - -webkit-user-select: none; 409 - cursor: pointer; 410 - z-index: 7; 411 - box-shadow: #0008 0 0 10px; 412 - } 413 - 414 - .viewer-close{ 415 - position: fixed; 416 - top: 10px; 417 - right: 10px; 418 - width: 35px; 419 - height: 35px; 420 - } 421 - 422 - .prev-button{ 423 - transition: 0.25s; 424 - position: fixed; 425 - top: 50%; 426 - left: 0; 427 - color: white; 428 - width: 50px; 429 - height: 150px; 430 - display: flex; 431 - justify-content: center; 432 - align-items: center; 433 - transform: translateY(-50%); 434 - background: rgba(255, 255, 255, 0.144); 435 - backdrop-filter: blur(50px); 436 - -webkit-backdrop-filter: blur(50px); 437 - border-radius: 0 15px 15px 0; 438 - cursor: pointer; 439 - user-select: none; 440 - -webkit-user-select: none; 441 - box-shadow: #000 0 0 10px; 442 - } 443 - 444 - .prev-button:hover{ 445 - background: rgba(255, 255, 255, 0.349); 446 - } 447 - 448 - .next-button{ 449 - transition: 0.25s; 450 - position: fixed; 451 - top: 50%; 452 - right: 0; 453 - color: white; 454 - width: 50px; 455 - height: 150px; 456 - display: flex; 457 - justify-content: center; 458 - align-items: center; 459 - transform: translateY(-50%); 460 - background: rgba(255, 255, 255, 0.144); 461 - backdrop-filter: blur(50px); 462 - -webkit-backdrop-filter: blur(50px); 463 - border-radius: 15px 0 0 15px; 464 - cursor: pointer; 465 - user-select: none; 466 - -webkit-user-select: none; 467 - box-shadow: #000 0 0 10px; 468 - } 469 - 470 - .next-button:hover{ 471 - background: rgba(255, 255, 255, 0.349); 472 - } 473 - 474 - .reload-photos{ 475 - position: fixed; 476 - top: 70px; 477 - right: 20px; 478 - color: white; 479 - user-select: none; 480 - -webkit-user-select: none; 481 - cursor: pointer; 482 - opacity: 0; 483 - } 484 - 485 .confirmation-box{ 486 position: fixed; 487 top: 0; ··· 489 width: 100%; 490 height: 100%; 491 z-index: 15; 492 - background: #0005; 493 transition: 0.25s; 494 - backdrop-filter: blur(10px); 495 - -webkit-backdrop-filter: blur(10px); 496 } 497 498 .confirmation-box-container{ ··· 550 551 .button-danger:hover{ 552 box-shadow: #000a inset 0 0 10px; 553 - } 554 - 555 - .control-buttons{ 556 - position: fixed; 557 - bottom: 10px; 558 - left: 50%; 559 - transform: translateX(-50%); 560 - display: flex; 561 - } 562 - 563 - .control-buttons div{ 564 - margin: 0 20px; 565 - } 566 - 567 - .copy-notif{ 568 - position: fixed; 569 - top: 40px; 570 - left: 50%; 571 - color: white; 572 - transform: translateX(-50%) translateY(-100px); 573 - background: #8885; 574 - padding: 10px 40px; 575 - backdrop-filter: blur(10px); 576 - -webkit-backdrop-filter: blur(10px); 577 - border-radius: 50px; 578 - box-shadow: #000 0 0 10px; 579 - z-index: 12; 580 - opacity: 0; 581 - pointer-events: none; 582 - } 583 - 584 - .photo-tray{ 585 - position: fixed; 586 - bottom: -150px; 587 - left: 0; 588 - width: 100%; 589 - height: 150px; 590 - background: #7778; 591 - backdrop-filter: blur(10px); 592 - -webkit-backdrop-filter: blur(10px); 593 - box-shadow: #0008 0 0 10px; 594 - padding-bottom: 150px; 595 - margin-bottom: -150px; 596 - } 597 - 598 - .photo-tray-close{ 599 - position: fixed; 600 - bottom: 160px; 601 - left: 50%; 602 - transform: translate(-50%); 603 - color: white; 604 - background: #8885; 605 - backdrop-filter: blur(10px); 606 - -webkit-backdrop-filter: blur(10px); 607 - box-shadow: #0008 0 0 10px; 608 - display: flex; 609 - justify-content: center; 610 - align-items: center; 611 - height: 30px; 612 - width: 50px; 613 - border-radius: 50px; 614 - cursor: pointer; 615 - font-size: 12px; 616 - user-select: none; 617 - -webkit-user-select: none; 618 - transition: 0.25s width; 619 - } 620 - 621 - .photo-tray-close:hover{ 622 - width: 70px; 623 - } 624 - 625 - .photo-tray-columns{ 626 - width: 100%; 627 - height: 100%; 628 - display: flex; 629 - color: white; 630 - text-align: center; 631 - } 632 - 633 - .photo-tray-column{ 634 - height: 100%; 635 - width: 100%; 636 - scrollbar-width: thin; 637 - overflow-y: auto; 638 - overflow-x: hidden; 639 - mask-image: linear-gradient(to bottom, #0000 0%, #000 10%, #000 90%, #0000 100%); 640 - } 641 - 642 - .tray-heading{ 643 - font-weight: bold; 644 - font-size: 20px; 645 - } 646 - 647 - .world-tags{ 648 - display: flex; 649 - width: 100%; 650 - justify-content: center; 651 - align-items: center; 652 - } 653 - 654 - .world-tags div{ 655 - padding: 0 10px; 656 - color: #bbb; 657 - transition: 0.25s; 658 - } 659 - 660 - .world-tags div:hover{ 661 - color: #ddd; 662 - } 663 - 664 - .world-name{ 665 - font-size: 17px; 666 - } 667 - 668 - .settings{ 669 - position: fixed; 670 - top: 0; 671 - left: 0; 672 - width: 100%; 673 - height: 100%; 674 - background: rgba(0, 0, 0, 0.4); 675 - backdrop-filter: blur(100px); 676 - -webkit-backdrop-filter: blur(100px); 677 - } 678 - 679 - .slide-bar{ 680 - position: fixed; 681 - bottom: 0; 682 - left: 0; 683 - width: 100%; 684 - height: 50px; 685 - border-top: #aaa 1px solid; 686 - overflow-x: hidden; 687 - mask-image: linear-gradient(to left, #0000 0%, #000 20%, #000 80%, #0000 100%); 688 - background: #aaa2; 689 - box-shadow: #000 0 0 10px; 690 - } 691 - 692 - .inner-slide-bar{ 693 - display: flex; 694 - height: 50px; 695 - width: 200%; 696 - color: white; 697 - align-items: center; 698 - cursor: pointer; 699 - user-select: none; 700 - -webkit-user-select: none; 701 - } 702 - 703 - .slider-dot{ 704 - width: 5px; 705 - height: 5px; 706 - border-radius: 5px; 707 - background: #aaa; 708 - margin: auto 25px; 709 - } 710 - 711 - .slider-text{ 712 - width: 200px; 713 - text-align: center; 714 - height: 50px; 715 - display: flex; 716 - justify-content: center; 717 - align-items: center; 718 - color: #aaa; 719 - transition: 0.25s; 720 - } 721 - 722 - .slider-text:hover{ 723 - color: #fff; 724 - } 725 - 726 - .slide-bar-tri{ 727 - position: fixed; 728 - bottom: 40px; 729 - left: 50%; 730 - transform: translateX(-50%); 731 - border: transparent solid 5px; 732 - border-top: #fff solid 5px; 733 - } 734 - 735 - .settings-container{ 736 - position: fixed; 737 - top: 50px; 738 - left: 0px; 739 - width: 200%; 740 - height: calc(100% - 100px); 741 - display: flex; 742 - } 743 - 744 - .settings-block{ 745 - width: 50%; 746 - height: 100%; 747 - color: white; 748 - text-align: center; 749 - } 750 - 751 - .selector{ 752 - padding: 10px 20px; 753 - border-radius: 10px; 754 - background: #000a; 755 - display: inline-block; 756 - margin: 10px; 757 - } 758 - 759 - .selector .selection-box{ 760 - height: 20px; 761 - background: #777a; 762 - margin: 5px -10px 0 -10px; 763 - border-radius: 8px; 764 - user-select: none; 765 - -webkit-user-select: none; 766 - cursor: pointer; 767 - transition: 0.25s; 768 - color: #fff1; 769 - } 770 - 771 - .selector .selection-box:hover{ 772 - height: 20px; 773 - background: #777a; 774 - margin: 5px -10px 0 -10px; 775 - border-radius: 8px; 776 - user-select: none; 777 - -webkit-user-select: none; 778 - cursor: pointer; 779 - transition: 0.25s; 780 - color: #fff5; 781 - } 782 - 783 - .selector input{ 784 - display: none; 785 - } 786 - 787 - .selector input:checked ~ label .selection-box{ 788 - background: rgba(0, 146, 204, 0.705); 789 - color: #fff; 790 - } 791 - 792 - .path{ 793 - padding: 5px 10px; 794 - background: #000a; 795 - border-radius: 5px; 796 - margin-left: 5px; 797 - cursor: pointer; 798 - } 799 - 800 - .scroll-to-top{ 801 - position: fixed; 802 - bottom: 10px; 803 - right: 10px; 804 - color: white; 805 - width: 40px; 806 - height: 40px; 807 - cursor: pointer; 808 - border-radius: 50%; 809 - border: 2px solid white; 810 - display: flex; 811 - justify-content: center; 812 - align-items: center; 813 - } 814 - 815 - .account-profile{ 816 - margin: auto; 817 - width: 50%; 818 - height: 200px; 819 - display: flex; 820 - } 821 - 822 - .account-pfp{ 823 - width: 200px; 824 - height: 200px; 825 - background-position: center !important; 826 - background-size: cover !important; 827 - border-radius: 50%; 828 - box-shadow: #0005 0 0 10px; 829 - position: relative; 830 - z-index: 10; 831 - } 832 - 833 - .account-desc{ 834 - width: calc(100% - 200px); 835 - padding-left: 100px; 836 - height: 150px; 837 - margin: 25px 0; 838 - margin-left: -100px; 839 - background: #0009; 840 - border-radius: 10px; 841 - box-shadow: #0005 0 0 10px; 842 - } 843 - 844 - .storage-bar{ 845 - width: calc(100% - 20px); 846 - height: 10px; 847 - margin-left: 10px; 848 - background: #555; 849 - border-radius: 10px; 850 - display: flex; 851 - justify-content: left; 852 - align-items: center; 853 - margin-bottom: 2px; 854 - } 855 - 856 - .storage-bar-inner{ 857 - margin: 2px; 858 - height: 6px; 859 - background: #00ccff; 860 - border-radius: 10px; 861 - } 862 - 863 - .account-notice{ 864 - background: #0007; 865 - border-radius: 5px; 866 - box-shadow: #0005 0 0 10px; 867 - padding: 10px; 868 - margin: auto; 869 - width: calc(50% - 20px); 870 - margin-top: 25px; 871 } 872 873 img{
··· 7 background: #000; 8 margin: 0; 9 font-family: Rubik, 'Courier New'; 10 + overflow: hidden; 11 + } 12 + 13 + * { 14 + box-sizing: border-box; 15 } 16 17 .loading{ ··· 29 align-items: center; 30 } 31 32 .confirmation-box{ 33 position: fixed; 34 top: 0; ··· 36 width: 100%; 37 height: 100%; 38 z-index: 15; 39 + background: rgba(0, 0, 0, 0.76); 40 transition: 0.25s; 41 } 42 43 .confirmation-box-container{ ··· 95 96 .button-danger:hover{ 97 box-shadow: #000a inset 0 0 10px; 98 } 99 100 img{