Move many things over from Knossos (and other rearrangements) (#102)

This commit is contained in:
Emma Alexia 2023-10-16 21:18:23 -04:00 committed by GitHub
parent 46a6fee81d
commit 8369330053
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
68 changed files with 852 additions and 342 deletions

View File

@ -1,6 +1,7 @@
# Analytics
<DemoContainer>
<client-only>
<LineChart
:data="{
labels: [
@ -77,6 +78,7 @@
],
}"
/>
</client-only>
</DemoContainer>
```vue

View File

@ -0,0 +1,74 @@
<svg width="100%" height="100%" viewBox="0 0 3247 1234" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.5;">
<g transform="matrix(1,0,0,1,-149.406,-1192.33)">
<g transform="matrix(1,0,0,1,0,-0.0649902)">
<g transform="matrix(1,0,0,1,-190.112,0.0649902)">
<g transform="matrix(1549.76,0,0,1549.76,285.034,2355.46)">
<path d="M0.386,-0L0.526,-0L0.526,-0.129L0.619,-0.129L0.619,-0.247L0.526,-0.247L0.526,-0.705L0.318,-0.705L0.035,-0.252L0.035,-0.129L0.386,-0.129L0.386,-0ZM0.164,-0.243L0.164,-0.247L0.385,-0.598L0.389,-0.598L0.389,-0.243L0.164,-0.243Z" style="fill-rule:nonzero;" />
</g>
</g>
</g>
<g transform="matrix(1,0,0,1,0,-0.0649902)">
<g transform="matrix(1,0,0,1,2151.95,0.0649902)">
<g transform="matrix(1549.76,0,0,1549.76,285.034,2355.46)">
<path d="M0.386,-0L0.526,-0L0.526,-0.129L0.619,-0.129L0.619,-0.247L0.526,-0.247L0.526,-0.705L0.318,-0.705L0.035,-0.252L0.035,-0.129L0.386,-0.129L0.386,-0ZM0.164,-0.243L0.164,-0.247L0.385,-0.598L0.389,-0.598L0.389,-0.243L0.164,-0.243Z" style="fill-rule:nonzero;" />
</g>
</g>
</g>
<g transform="matrix(1,0,0,1,0,-0.0649902)">
<g transform="matrix(1.04827,0,0,1.04827,1374.32,1499.56)">
<path d="M309.082,335.571L283.216,266.381L336.966,211.169L404.9,196.531L424.57,220.74L393.254,252.46L365.941,261.052L346.425,281.11L355.987,307.719L375.387,328.306L402.745,321.031L422.216,299.648L464.729,286.185L477.395,314.677L433.529,368.46L360.02,391.735L309.082,335.571Z" />
</g>
<g transform="matrix(1.6241,0,0,1.6241,3325.29,75.5995)">
<path d="M-799.272,1141.68C-827.119,1200.8 -887.255,1241.76 -956.882,1241.76C-1048.32,1241.76 -1123.39,1171.12 -1130.46,1081.49" style="fill:none;stroke:currentcolor;stroke-width:33.35px;" />
</g>
<g transform="matrix(1.6241,0,0,1.6241,3325.29,75.5995)">
<path d="M-1129.95,1048.4C-1120.36,961.34 -1046.48,893.522 -956.882,893.522C-860.784,893.522 -782.765,971.541 -782.765,1067.64C-782.765,1082.32 -784.586,1096.58 -788.016,1110.21" style="fill:none;stroke:currentcolor;stroke-width:33.35px;" />
</g>
<g transform="matrix(-0.578174,2.15777,-2.15777,-0.578174,3522.29,4490.98)">
<path d="M-799.272,1141.68C-827.119,1200.8 -887.255,1241.76 -956.882,1241.76C-1048.32,1241.76 -1123.39,1171.12 -1130.46,1081.49" style="fill:none;stroke:currentcolor;stroke-width:24.25px;" />
</g>
<g transform="matrix(-0.578174,2.15777,-2.15777,-0.578174,3522.29,4490.98)">
<path d="M-1129.95,1048.4C-1120.36,961.34 -1046.48,893.522 -956.882,893.522C-860.784,893.522 -782.765,971.541 -782.765,1067.64C-782.765,1082.32 -784.586,1096.58 -788.016,1110.21" style="fill:none;stroke:currentcolor;stroke-width:24.25px;" />
</g>
<g transform="matrix(2.06597,1.89638,-1.89638,2.06597,5772.64,1417.93)">
<path d="M-799.272,1141.68C-827.119,1200.8 -887.255,1241.76 -956.882,1241.76C-1048.32,1241.76 -1123.39,1171.12 -1130.46,1081.49" style="fill:none;stroke:currentcolor;stroke-width:19.32px;" />
</g>
<g transform="matrix(2.06597,1.89638,-1.89638,2.06597,5772.64,1417.93)">
<path d="M-1129.95,1048.4C-1120.36,961.34 -1046.48,893.522 -956.882,893.522C-860.784,893.522 -782.765,971.541 -782.765,1067.64C-782.765,1082.32 -784.586,1096.58 -788.016,1110.21" style="fill:none;stroke:currentcolor;stroke-width:19.32px;" />
</g>
<g transform="matrix(-3.26901,0.875928,-0.875928,-3.26901,-420.338,6137.6)">
<path d="M-799.272,1141.68C-827.119,1200.8 -887.255,1241.76 -956.882,1241.76C-1048.32,1241.76 -1123.39,1171.12 -1130.46,1081.49" style="fill:none;stroke:currentcolor;stroke-width:16.01px;" />
</g>
<g transform="matrix(-3.26901,0.875928,-0.875928,-3.26901,-420.338,6137.6)">
<path d="M-1129.95,1048.4C-1120.36,961.34 -1046.48,893.522 -956.882,893.522C-860.784,893.522 -782.765,971.541 -782.765,1067.64C-782.765,1082.32 -784.586,1096.58 -788.016,1110.21" style="fill:none;stroke:currentcolor;stroke-width:16.01px;" />
</g>
<g transform="matrix(1,0,0,1,2728.09,741.915)">
<path d="M-904.543,901.539C-833.986,923.78 -782.765,989.775 -782.765,1067.64C-782.765,1137.58 -824.09,1197.94 -883.633,1225.62" style="fill:none;stroke:currentcolor;stroke-width:54.17px;" />
</g>
<g transform="matrix(1,0,0,1,2728.09,741.915)">
<path d="M-937.34,1240.67C-943.756,1241.39 -950.276,1241.76 -956.882,1241.76C-1052.98,1241.76 -1131,1163.74 -1131,1067.64C-1131,971.541 -1052.98,893.522 -956.882,893.522" style="fill:none;stroke:currentcolor;stroke-width:54.17px;" />
</g>
<g transform="matrix(1,0,0,1,2728.09,741.915)">
<path d="M-904.543,901.539C-833.986,923.78 -782.765,989.775 -782.765,1067.64C-782.765,1137.58 -824.09,1197.94 -883.633,1225.62" style="fill:none;stroke:currentcolor;stroke-width:54.17px;" />
</g>
<g transform="matrix(1,0,0,1,2728.09,741.915)">
<path d="M-937.34,1240.67C-943.756,1241.39 -950.276,1241.76 -956.882,1241.76C-1052.98,1241.76 -1131,1163.74 -1131,1067.64C-1131,971.541 -1052.98,893.522 -956.882,893.522" style="fill:none;stroke:currentcolor;stroke-width:54.17px;" />
</g>
<g transform="matrix(1,0,0,1,2730.59,729.536)">
<path d="M-791.26,1034.59L-686.335,1006.23" style="fill:none;stroke:currentcolor;stroke-width:54.17px;" />
</g>
<g transform="matrix(0.730214,-0.361316,0.535157,0.830095,1995.93,464.536)">
<path d="M-791.26,1034.59L-686.335,1006.23" style="fill:none;stroke:currentcolor;stroke-width:56.77px;" />
</g>
<g transform="matrix(0.588836,0.702962,-0.731171,0.673626,3318.16,1883.42)">
<path d="M-791.26,1034.59L-686.335,1006.23" style="fill:none;stroke:currentcolor;stroke-width:55.35px;" />
</g>
<g transform="matrix(0.260851,-0.884338,0.965379,0.238953,979.167,372.085)">
<path d="M-791.26,1034.59L-686.335,1006.23" style="fill:none;stroke:currentcolor;stroke-width:55.28px;" />
</g>
<g transform="matrix(1.94443,-0.569179,-0.569179,1.34303,4301.69,-217.31)">
<path d="M-1202.21,1224.92L-999.087,1102.51" style="fill:none;stroke:currentcolor;stroke-width:48.54px;" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

9
lib/assets/external/apple.svg vendored Executable file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="56px" height="56px" viewBox="0 0 56 56" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 61 (89581) - https://sketch.com -->
<title>Black Logo Square</title>
<desc>Created with Sketch.</desc>
<g id="Black-Logo-Square" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M28.2226562,20.3846154 C29.0546875,20.3846154 30.0976562,19.8048315 30.71875,19.0317864 C31.28125,18.3312142 31.6914062,17.352829 31.6914062,16.3744437 C31.6914062,16.2415766 31.6796875,16.1087095 31.65625,16 C30.7304687,16.0362365 29.6171875,16.640178 28.9492187,17.4494596 C28.421875,18.06548 27.9414062,19.0317864 27.9414062,20.0222505 C27.9414062,20.1671964 27.9648438,20.3121424 27.9765625,20.3604577 C28.0351562,20.3725366 28.1289062,20.3846154 28.2226562,20.3846154 Z M25.2929688,35 C26.4296875,35 26.9335938,34.214876 28.3515625,34.214876 C29.7929688,34.214876 30.109375,34.9758423 31.375,34.9758423 C32.6171875,34.9758423 33.4492188,33.792117 34.234375,32.6325493 C35.1132812,31.3038779 35.4765625,29.9993643 35.5,29.9389701 C35.4179688,29.9148125 33.0390625,28.9122695 33.0390625,26.0979021 C33.0390625,23.6579784 34.9140625,22.5588048 35.0195312,22.474253 C33.7773438,20.6382708 31.890625,20.5899555 31.375,20.5899555 C29.9804688,20.5899555 28.84375,21.4596313 28.1289062,21.4596313 C27.3554688,21.4596313 26.3359375,20.6382708 25.1289062,20.6382708 C22.8320312,20.6382708 20.5,22.5950413 20.5,26.2911634 C20.5,28.5861411 21.3671875,31.013986 22.4335938,32.5842339 C23.3476562,33.9129053 24.1445312,35 25.2929688,35 Z" id="" fill="currentColor" fill-rule="nonzero"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

16
lib/assets/external/bmac.svg vendored Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.1 KiB

10
lib/assets/external/discord.svg vendored Normal file
View File

@ -0,0 +1,10 @@
<svg width="71" height="55" viewBox="0 0 71 55" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<path d="M60.1045 4.8978C55.5792 2.8214 50.7265 1.2916 45.6527 0.41542C45.5603 0.39851 45.468 0.440769 45.4204 0.525289C44.7963 1.6353 44.105 3.0834 43.6209 4.2216C38.1637 3.4046 32.7345 3.4046 27.3892 4.2216C26.905 3.0581 26.1886 1.6353 25.5617 0.525289C25.5141 0.443589 25.4218 0.40133 25.3294 0.41542C20.2584 1.2888 15.4057 2.8186 10.8776 4.8978C10.8384 4.9147 10.8048 4.9429 10.7825 4.9795C1.57795 18.7309 -0.943561 32.1443 0.293408 45.3914C0.299005 45.4562 0.335386 45.5182 0.385761 45.5576C6.45866 50.0174 12.3413 52.7249 18.1147 54.5195C18.2071 54.5477 18.305 54.5139 18.3638 54.4378C19.7295 52.5728 20.9469 50.6063 21.9907 48.5383C22.0523 48.4172 21.9935 48.2735 21.8676 48.2256C19.9366 47.4931 18.0979 46.6 16.3292 45.5858C16.1893 45.5041 16.1781 45.304 16.3068 45.2082C16.679 44.9293 17.0513 44.6391 17.4067 44.3461C17.471 44.2926 17.5606 44.2813 17.6362 44.3151C29.2558 49.6202 41.8354 49.6202 53.3179 44.3151C53.3935 44.2785 53.4831 44.2898 53.5502 44.3433C53.9057 44.6363 54.2779 44.9293 54.6529 45.2082C54.7816 45.304 54.7732 45.5041 54.6333 45.5858C52.8646 46.6197 51.0259 47.4931 49.0921 48.2228C48.9662 48.2707 48.9102 48.4172 48.9718 48.5383C50.038 50.6034 51.2554 52.5699 52.5959 54.435C52.6519 54.5139 52.7526 54.5477 52.845 54.5195C58.6464 52.7249 64.529 50.0174 70.6019 45.5576C70.6551 45.5182 70.6887 45.459 70.6943 45.3942C72.1747 30.0791 68.2147 16.7757 60.1968 4.9823C60.1772 4.9429 60.1437 4.9147 60.1045 4.8978ZM23.7259 37.3253C20.2276 37.3253 17.3451 34.1136 17.3451 30.1693C17.3451 26.225 20.1717 23.0133 23.7259 23.0133C27.308 23.0133 30.1626 26.2532 30.1066 30.1693C30.1066 34.1136 27.28 37.3253 23.7259 37.3253ZM47.3178 37.3253C43.8196 37.3253 40.9371 34.1136 40.9371 30.1693C40.9371 26.225 43.7636 23.0133 47.3178 23.0133C50.9 23.0133 53.7545 26.2532 53.6986 30.1693C53.6986 34.1136 50.9 37.3253 47.3178 37.3253Z" fill="currentColor"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="71" height="55" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

1
lib/assets/external/kofi.svg vendored Normal file
View File

@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Ko-fi</title><path d="M23.881 8.948c-.773-4.085-4.859-4.593-4.859-4.593H.723c-.604 0-.679.798-.679.798s-.082 7.324-.022 11.822c.164 2.424 2.586 2.672 2.586 2.672s8.267-.023 11.966-.049c2.438-.426 2.683-2.566 2.658-3.734 4.352.24 7.422-2.831 6.649-6.916zm-11.062 3.511c-1.246 1.453-4.011 3.976-4.011 3.976s-.121.119-.31.023c-.076-.057-.108-.09-.108-.09-.443-.441-3.368-3.049-4.034-3.954-.709-.965-1.041-2.7-.091-3.71.951-1.01 3.005-1.086 4.363.407 0 0 1.565-1.782 3.468-.963 1.904.82 1.832 3.011.723 4.311zm6.173.478c-.928.116-1.682.028-1.682.028V7.284h1.77s1.971.551 1.971 2.638c0 1.913-.985 2.667-2.059 3.015z" fill="currentColor" /></svg>

After

Width:  |  Height:  |  Size: 718 B

View File

@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Open Collective</title><path d="M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12c2.54 0 4.894-.79 6.834-2.135l-3.107-3.109a7.715 7.715 0 1 1 0-13.512l3.107-3.109A11.943 11.943 0 0 0 12 0zm9.865 5.166l-3.109 3.107A7.67 7.67 0 0 1 19.715 12a7.682 7.682 0 0 1-.959 3.727l3.109 3.107A11.943 11.943 0 0 0 24 12c0-2.54-.79-4.894-2.135-6.834z"/></svg>

After

Width:  |  Height:  |  Size: 415 B

1
lib/assets/external/patreon.svg vendored Normal file
View File

@ -0,0 +1 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve"><path fill="currentColor" d="M23,7.2c0-3.1-2.4-5.6-5.2-6.5c-3.5-1.1-8.1-1-11.4,0.6C2.4,3.2,1.1,7.4,1,11.5C1,15,1.3,23.9,6.4,24c3.8,0,4.3-4.8,6.1-7.1 c1.2-1.7,2.8-2.1,4.8-2.6C20.7,13.4,23,10.7,23,7.2z"/></svg>

After

Width:  |  Height:  |  Size: 418 B

1
lib/assets/external/paypal.svg vendored Normal file
View File

@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>PayPal</title><path d="M7.076 21.337H2.47a.641.641 0 0 1-.633-.74L4.944.901C5.026.382 5.474 0 5.998 0h7.46c2.57 0 4.578.543 5.69 1.81 1.01 1.15 1.304 2.42 1.012 4.287-.023.143-.047.288-.077.437-.983 5.05-4.349 6.797-8.647 6.797h-2.19c-.524 0-.968.382-1.05.9l-1.12 7.106zm14.146-14.42a3.35 3.35 0 0 0-.607-.541c-.013.076-.026.175-.041.254-.93 4.778-4.005 7.201-9.138 7.201h-2.19a.563.563 0 0 0-.556.479l-1.187 7.527h-.506l-.24 1.516a.56.56 0 0 0 .554.647h3.882c.46 0 .85-.334.922-.788.06-.26.76-4.852.816-5.09a.932.932 0 0 1 .923-.788h.58c3.76 0 6.705-1.528 7.565-5.946.36-1.847.174-3.388-.777-4.471z" fill="currentColor"/></svg>

After

Width:  |  Height:  |  Size: 706 B

1
lib/assets/external/sso/discord.svg vendored Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 127.14 96.36"><defs><style>.cls-1{fill:#5865f2;}</style></defs><g id="图层_2" data-name="图层 2"><g id="Discord_Logos" data-name="Discord Logos"><g id="Discord_Logo_-_Large_-_White" data-name="Discord Logo - Large - White"><path class="cls-1" d="M107.7,8.07A105.15,105.15,0,0,0,81.47,0a72.06,72.06,0,0,0-3.36,6.83A97.68,97.68,0,0,0,49,6.83,72.37,72.37,0,0,0,45.64,0,105.89,105.89,0,0,0,19.39,8.09C2.79,32.65-1.71,56.6.54,80.21h0A105.73,105.73,0,0,0,32.71,96.36,77.7,77.7,0,0,0,39.6,85.25a68.42,68.42,0,0,1-10.85-5.18c.91-.66,1.8-1.34,2.66-2a75.57,75.57,0,0,0,64.32,0c.87.71,1.76,1.39,2.66,2a68.68,68.68,0,0,1-10.87,5.19,77,77,0,0,0,6.89,11.1A105.25,105.25,0,0,0,126.6,80.22h0C129.24,52.84,122.09,29.11,107.7,8.07ZM42.45,65.69C36.18,65.69,31,60,31,53s5-12.74,11.43-12.74S54,46,53.89,53,48.84,65.69,42.45,65.69Zm42.24,0C78.41,65.69,73.25,60,73.25,53s5-12.74,11.44-12.74S96.23,46,96.12,53,91.08,65.69,84.69,65.69Z"/></g></g></g></svg>

After

Width:  |  Height:  |  Size: 989 B

1
lib/assets/external/sso/github.svg vendored Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 97.63 96.03" fill="currentColor"> <path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"></path> </svg>

After

Width:  |  Height:  |  Size: 982 B

6
lib/assets/external/sso/gitlab.svg vendored Normal file
View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="93.97 97.52 192.07 185">
<path fill="#e24329" d="m282.83 170.73-.27-.69-26.14-68.22a6.81 6.81 0 0 0-2.69-3.24 7 7 0 0 0-8 .43 7 7 0 0 0-2.32 3.52l-17.65 54h-71.47l-17.65-54a6.86 6.86 0 0 0-2.32-3.53 7 7 0 0 0-8-.43 6.87 6.87 0 0 0-2.69 3.24L97.44 170l-.26.69a48.54 48.54 0 0 0 16.1 56.1l.09.07.24.17 39.82 29.82 19.7 14.91 12 9.06a8.07 8.07 0 0 0 9.76 0l12-9.06 19.7-14.91 40.06-30 .1-.08a48.56 48.56 0 0 0 16.08-56.04Z" />
<path d="m282.83 170.73-.27-.69a88.3 88.3 0 0 0-35.15 15.8L190 229.25c19.55 14.79 36.57 27.64 36.57 27.64l40.06-30 .1-.08a48.56 48.56 0 0 0 16.1-56.08Z" fill="#fc6d26" />
<path fill="#fca326" d="m153.43 256.89 19.7 14.91 12 9.06a8.07 8.07 0 0 0 9.76 0l12-9.06 19.7-14.91S209.55 244 190 229.25c-19.55 14.75-36.57 27.64-36.57 27.64Z" />
<path d="M132.58 185.84A88.19 88.19 0 0 0 97.44 170l-.26.69a48.54 48.54 0 0 0 16.1 56.1l.09.07.24.17 39.82 29.82L190 229.21Z" fill="#fc6d26" />
</svg>

After

Width:  |  Height:  |  Size: 967 B

18
lib/assets/external/sso/google.svg vendored Normal file
View File

@ -0,0 +1,18 @@
<svg width="100%" height="100%" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<circle cx="50" cy="50" r="50" style="fill:white;"/>
<g id="Google__G__Logo.svg" transform="matrix(0.0991612,0,0,0.0991612,49.3739,50)">
<g transform="matrix(1,0,0,1,-352.8,-360)">
<clipPath id="_clip1">
<rect x="0" y="0" width="705.6" height="720"/>
</clipPath>
<g clip-path="url(#_clip1)">
<g transform="matrix(1,0,0,1,4477.16,2891.98)">
<path d="M-4117.16,-2597.44L-4117.16,-2458.02L-3923.42,-2458.02C-3931.93,-2413.18 -3957.46,-2375.22 -3995.75,-2349.69L-3878.91,-2259.03C-3810.84,-2321.87 -3771.56,-2414.16 -3771.56,-2523.8C-3771.56,-2549.33 -3773.85,-2573.87 -3778.11,-2597.43L-4117.16,-2597.44Z" style="fill:rgb(66,133,244);fill-rule:nonzero;"/>
<path d="M-4318.92,-2463.46L-4345.27,-2443.29L-4438.55,-2370.64C-4379.31,-2253.15 -4257.9,-2171.98 -4117.17,-2171.98C-4019.97,-2171.98 -3938.48,-2204.05 -3878.92,-2259.03L-3995.75,-2349.69C-4027.83,-2328.09 -4068.74,-2315 -4117.17,-2315C-4210.77,-2315 -4290.3,-2378.16 -4318.77,-2463.25L-4318.92,-2463.46Z" style="fill:rgb(52,168,83);fill-rule:nonzero;"/>
<path d="M-4438.55,-2693.33C-4463.09,-2644.89 -4477.16,-2590.24 -4477.16,-2531.99C-4477.16,-2473.73 -4463.09,-2419.08 -4438.55,-2370.64C-4438.55,-2370.32 -4318.76,-2463.59 -4318.76,-2463.59C-4325.96,-2485.19 -4330.22,-2508.09 -4330.22,-2531.99C-4330.22,-2555.88 -4325.96,-2578.79 -4318.76,-2600.39L-4438.55,-2693.33Z" style="fill:rgb(251,188,5);fill-rule:nonzero;"/>
<path d="M-4117.16,-2748.64C-4064.14,-2748.64 -4017.02,-2730.31 -3979.38,-2694.97L-3876.29,-2798.06C-3938.8,-2856.31 -4019.96,-2891.99 -4117.16,-2891.99C-4257.89,-2891.99 -4379.31,-2811.15 -4438.55,-2693.33L-4318.76,-2600.38C-4290.29,-2685.47 -4210.76,-2748.64 -4117.16,-2748.64Z" style="fill:rgb(234,67,53);fill-rule:nonzero;"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

1
lib/assets/external/sso/microsoft.svg vendored Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21 21"><title>MS-SymbolLockup</title><rect x="1" y="1" width="9" height="9" fill="#f25022"/><rect x="1" y="11" width="9" height="9" fill="#00a4ef"/><rect x="11" y="1" width="9" height="9" fill="#7fba00"/><rect x="11" y="11" width="9" height="9" fill="#ffb900"/></svg>

After

Width:  |  Height:  |  Size: 321 B

4
lib/assets/external/sso/steam.svg vendored Normal file
View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-steam" viewBox="0 0 16 16">
<path d="M.329 10.333A8.01 8.01 0 0 0 7.99 16C12.414 16 16 12.418 16 8s-3.586-8-8.009-8A8.006 8.006 0 0 0 0 7.468l.003.006 4.304 1.769A2.198 2.198 0 0 1 5.62 8.88l1.96-2.844-.001-.04a3.046 3.046 0 0 1 3.042-3.043 3.046 3.046 0 0 1 3.042 3.043 3.047 3.047 0 0 1-3.111 3.044l-2.804 2a2.223 2.223 0 0 1-3.075 2.11 2.217 2.217 0 0 1-1.312-1.568L.33 10.333Z"/>
<path d="M4.868 12.683a1.715 1.715 0 0 0 1.318-3.165 1.705 1.705 0 0 0-1.263-.02l1.023.424a1.261 1.261 0 1 1-.97 2.33l-.99-.41a1.7 1.7 0 0 0 .882.84Zm3.726-6.687a2.03 2.03 0 0 0 2.027 2.029 2.03 2.03 0 0 0 2.027-2.029 2.03 2.03 0 0 0-2.027-2.027 2.03 2.03 0 0 0-2.027 2.027Zm2.03-1.527a1.524 1.524 0 1 1-.002 3.048 1.524 1.524 0 0 1 .002-3.048Z"/>
</svg>

After

Width:  |  Height:  |  Size: 839 B

3
lib/assets/external/windows.svg vendored Normal file
View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 4875 4875">
<path d="M0 0h2311v2310H0zm2564 0h2311v2310H2564zM0 2564h2311v2311H0zm2564 0h2311v2311H2564" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 189 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check-check"><path d="M18 6 7 17l-5-5"/><path d="m22 10-7.5 7.5L13 16"/></svg>

After

Width:  |  Height:  |  Size: 280 B

5
lib/assets/icons/key.svg Normal file
View File

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-key" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M16.555 3.843l3.602 3.602a2.877 2.877 0 0 1 0 4.069l-2.643 2.643a2.877 2.877 0 0 1 -4.069 0l-.301 -.301l-6.558 6.558a2 2 0 0 1 -1.239 .578l-.175 .008h-1.172a1 1 0 0 1 -.993 -.883l-.007 -.117v-1.172a2 2 0 0 1 .467 -1.284l.119 -.13l.414 -.414h2v-2h2v-2l2.144 -2.144l-.301 -.301a2.877 2.877 0 0 1 0 -4.069l2.643 -2.643a2.877 2.877 0 0 1 4.069 0z"></path>
<path d="M15 9h.01"></path>
</svg>

After

Width:  |  Height:  |  Size: 683 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-languages"><path d="m5 8 6 6"/><path d="m4 14 6-6 2-3"/><path d="M2 5h12"/><path d="M7 2h1"/><path d="m22 22-5-10-5 10"/><path d="M14 18h6"/></svg>

After

Width:  |  Height:  |  Size: 349 B

View File

@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M16 12H3"></path>
<path d="M16 6H3"></path>
<path d="M10 18H3"></path>
<path d="M21 6v10a2 2 0 0 1-2 2h-4"></path>
<path d="m16 16-2 2 2 2"></path>
</svg>

After

Width:  |  Height:  |  Size: 356 B

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
</svg>

After

Width:  |  Height:  |  Size: 271 B

View File

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="m12 8-9.04 9.06a2.82 2.82 0 1 0 3.98 3.98L16 12"></path>
<circle cx="17" cy="7" r="5"></circle>
</svg>

After

Width:  |  Height:  |  Size: 298 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10s10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8s8 3.58 8 8s-3.58 8-8 8z"/><circle cx="12" cy="12" r="5" fill="currentColor"/></svg>

After

Width:  |  Height:  |  Size: 298 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10s10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8s8 3.58 8 8s-3.58 8-8 8z"/></svg>

After

Width:  |  Height:  |  Size: 247 B

View File

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="9 17 4 12 9 7"></polyline>
<path d="M20 18v-2a4 4 0 0 0-4-4H4"></path>
</svg>

After

Width:  |  Height:  |  Size: 282 B

View File

@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M11 11h4"></path>
<path d="M11 15h7"></path>
<path d="M11 19h10"></path>
<path d="M9 7 6 4 3 7"></path>
<path d="M6 6v14"></path>
</svg>

After

Width:  |  Height:  |  Size: 338 B

View File

@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M11 5h10"></path>
<path d="M11 9h7"></path>
<path d="M11 13h4"></path>
<path d="m3 17 3 3 3-3"></path>
<path d="M6 18V4"></path>
</svg>

After

Width:  |  Height:  |  Size: 337 B

View File

@ -4,7 +4,7 @@
ref="img"
:class="`avatar size-${size} ${circle ? 'circle' : ''} ${noShadow ? 'no-shadow' : ''} ${
pixelated ? 'pixelated' : ''
}`"
} ${raised ? 'raised' : ''}`"
:src="src"
:alt="alt"
:loading="loading"
@ -12,7 +12,9 @@
/>
<svg
v-else
:class="`avatar size-${size} ${circle ? 'circle' : ''} ${noShadow ? 'no-shadow' : ''}`"
:class="`avatar size-${size} ${circle ? 'circle' : ''} ${noShadow ? 'no-shadow' : ''} ${
raised ? 'raised' : ''
}`"
xml:space="preserve"
fill-rule="evenodd"
stroke-linecap="round"
@ -32,51 +34,48 @@
</svg>
</template>
<script>
export default {
props: {
src: {
type: String,
default: null,
},
alt: {
type: String,
default: '',
},
size: {
type: String,
default: 'sm',
validator(value) {
return ['xs', 'sm', 'md', 'lg', 'none'].includes(value)
},
},
circle: {
type: Boolean,
default: false,
},
noShadow: {
type: Boolean,
default: false,
},
loading: {
type: String,
default: 'lazy',
<script setup>
import { ref } from 'vue'
const pixelated = ref(false)
const img = ref(null)
defineProps({
src: {
type: String,
default: null,
},
alt: {
type: String,
default: '',
},
size: {
type: String,
default: 'sm',
validator(value) {
return ['xxs', 'xs', 'sm', 'md', 'lg', 'none'].includes(value)
},
},
data() {
return {
pixelated: false,
}
circle: {
type: Boolean,
default: false,
},
methods: {
updatePixelated() {
if (this.$refs.img && this.$refs.img.naturalWidth && this.$refs.img.naturalWidth <= 96) {
this.pixelated = true
} else {
this.pixelated = false
}
},
noShadow: {
type: Boolean,
default: false,
},
loading: {
type: String,
default: 'lazy',
},
raised: {
type: Boolean,
default: false,
},
})
function updatePixelated() {
pixelated.value = !!(img.value && img.value.naturalWidth && img.value.naturalWidth <= 96)
}
</script>
@ -91,6 +90,12 @@ export default {
max-width: var(--size) !important;
max-height: var(--size) !important;
&.size-xxs {
--size: 1.25rem;
box-shadow: var(--shadow-inset), var(--shadow-card);
border-radius: var(--radius-sm);
}
&.size-xs {
--size: 2.5rem;
box-shadow: var(--shadow-inset), var(--shadow-card);
@ -128,5 +133,9 @@ export default {
&.pixelated {
image-rendering: pixelated;
}
&.raised {
background-color: var(--color-raised-bg);
}
}
</style>

View File

@ -1,27 +1,38 @@
<template>
<span :class="'version-badge ' + color + ' type--' + type">
<template v-if="color"> <span class="circle" /> {{ type }} </template>
<template v-if="color"> <span class="circle" /> {{ capitalizeString(type) }}</template>
<!-- User roles -->
<template v-else-if="type === 'admin'"> <ModrinthIcon /> Modrinth Team </template>
<template v-else-if="type === 'moderator'"> <ScaleIcon /> Moderator </template>
<template v-else-if="type === 'admin'"> <ModrinthIcon /> Modrinth Team</template>
<template v-else-if="type === 'moderator'"> <ScaleIcon /> Moderator</template>
<template v-else-if="type === 'creator'"><BoxIcon /> Creator</template>
<!-- Project statuses -->
<template v-else-if="type === 'approved'"><ListIcon /> Listed</template>
<template v-else-if="type === 'approved-general'"><CheckIcon /> Approved</template>
<template v-else-if="type === 'unlisted'"><EyeOffIcon /> Unlisted</template>
<template v-else-if="type === 'withheld'"><EyeOffIcon /> Withheld</template>
<template v-else-if="type === 'private'"><LockIcon /> Private</template>
<template v-else-if="type === 'scheduled'"> <CalendarIcon /> Scheduled </template>
<template v-else-if="type === 'scheduled'"> <CalendarIcon /> Scheduled</template>
<template v-else-if="type === 'draft'"><FileTextIcon /> Draft</template>
<template v-else-if="type === 'archived'"> <ArchiveIcon /> Archived </template>
<template v-else-if="type === 'archived'"> <ArchiveIcon /> Archived</template>
<template v-else-if="type === 'rejected'"><XIcon /> Rejected</template>
<template v-else-if="type === 'processing'"> <UpdatedIcon /> Under review </template>
<template v-else-if="type === 'processing'"> <UpdatedIcon /> Under review</template>
<!-- Team members -->
<template v-else-if="type === 'accepted'"><CheckIcon /> Accepted</template>
<template v-else-if="type === 'pending'"> <UpdatedIcon /> Pending </template>
<template v-else> <span class="circle" /> {{ type }} </template>
<template v-else-if="type === 'pending'"> <UpdatedIcon /> Pending</template>
<!-- Transaction statuses (pending, processing reused) -->
<template v-else-if="type === 'processed'"><CheckIcon /> Processed</template>
<template v-else-if="type === 'failed'"><XIcon /> Failed</template>
<template v-else-if="type === 'returned'"><XIcon /> Returned</template>
<!-- Report status -->
<template v-else-if="type === 'closed'"> <XIcon /> Closed</template>
<!-- Other -->
<template v-else> <span class="circle" /> {{ capitalizeString(type) }} </template>
</span>
</template>
@ -39,7 +50,8 @@ import {
CheckIcon,
LockIcon,
CalendarIcon,
} from '@/components'
capitalizeString,
} from '@'
defineProps({
type: {
@ -52,7 +64,6 @@ defineProps({
},
})
</script>
<style lang="scss" scoped>
.version-badge {
display: flex;
@ -75,8 +86,11 @@ defineProps({
margin-right: 0.25rem;
}
&.type--closed,
&.type--withheld,
&.type--rejected,
&.type--returned,
&.type--failed,
&.red {
--badge-color: var(--color-red);
}
@ -91,7 +105,8 @@ defineProps({
&.type--accepted,
&.type--admin,
&.type--success,
&.type--processed,
&.type--approved-general,
&.green {
--badge-color: var(--color-green);
}

View File

@ -1,5 +1,5 @@
<script setup>
import { ExternalIcon, UnknownIcon } from '@/components'
import { ExternalIcon, UnknownIcon } from '@'
import { computed } from 'vue'

View File

@ -1,5 +1,5 @@
<script setup>
import { Button, DropdownIcon } from '@/components'
import { Button, DropdownIcon } from '@'
import { reactive } from 'vue'

View File

@ -24,7 +24,7 @@
</div>
</template>
<script setup>
import { CheckIcon, DropdownIcon } from '@/components'
import { CheckIcon, DropdownIcon } from '@'
</script>
<script>
import { defineComponent } from 'vue'

View File

@ -4,7 +4,7 @@
v-for="item in items"
:key="item"
class="btn"
:class="{ selected: selected === item }"
:class="{ selected: selected === item, capitalize: capitalize }"
@click="toggleItem(item)"
>
<CheckIcon v-if="selected === item" />
@ -13,7 +13,7 @@
</div>
</template>
<script setup>
import { CheckIcon, Button } from '@/components'
import { CheckIcon, Button } from '@'
</script>
<script>
import { defineComponent } from 'vue'
@ -36,6 +36,10 @@ export default defineComponent({
default: (x) => x,
type: Function,
},
capitalize: {
type: Boolean,
default: true,
},
},
emits: ['update:modelValue'],
computed: {
@ -72,7 +76,9 @@ export default defineComponent({
flex-wrap: wrap;
.btn {
text-transform: capitalize;
&.capitalize {
text-transform: capitalize;
}
svg {
width: 1em;

View File

@ -0,0 +1,21 @@
<template>
<router-link v-if="isLink" :to="to">
<slot />
</router-link>
<span v-else>
<slot />
</span>
</template>
<script setup>
defineProps({
to: {
type: String,
required: true,
},
isLink: {
type: Boolean,
required: true,
},
})
</script>

View File

@ -1,13 +1,13 @@
<template>
<button class="code" :class="{ copied }" title="Copy code to clipboard" @click="copyText">
{{ text }}
<span>{{ text }}</span>
<CheckIcon v-if="copied" />
<ClipboardCopyIcon v-else />
</button>
</template>
<script setup>
import { CheckIcon, ClipboardCopyIcon } from '@/components'
import { CheckIcon, ClipboardCopyIcon } from '@'
</script>
<script>
@ -34,7 +34,7 @@ export default {
<style lang="scss" scoped>
.code {
display: flex;
display: inline-flex;
grid-gap: 0.5rem;
font-family: var(--mono-font);
font-size: var(--font-size-sm);
@ -47,6 +47,12 @@ export default {
transition: opacity 0.5s ease-in-out, filter 0.2s ease-in-out, transform 0.05s ease-in-out,
outline 0.2s ease-in-out;
span {
max-width: 10rem;
overflow: hidden;
text-overflow: ellipsis;
}
svg {
width: 1em;
height: 1em;

View File

@ -0,0 +1,34 @@
<template>
<div class="double-icon">
<slot name="primary" />
<div class="secondary">
<slot name="secondary" />
</div>
</div>
</template>
<style lang="scss" scoped>
.double-icon {
position: relative;
height: fit-content;
line-height: 0;
.secondary {
position: absolute;
bottom: -4px;
right: -4px;
background-color: var(--color-bg);
padding: var(--spacing-card-xs);
border-radius: 50%;
aspect-ratio: 1 / 1;
width: fit-content;
height: fit-content;
line-height: 0;
svg {
width: 1rem;
height: 1rem;
}
}
}
</style>

View File

@ -61,7 +61,7 @@
</template>
<script setup>
import { DropdownIcon } from '@/components'
import { DropdownIcon } from '@'
import { computed, ref, watch } from 'vue'
const props = defineProps({

View File

@ -4,7 +4,11 @@
A {{ type }}
</span>
<span
v-else-if="!['resourcepack', 'shader'].includes(type) && !(type === 'plugin' && search)"
v-else-if="
!['resourcepack', 'shader'].includes(type) &&
!(type === 'plugin' && search) &&
!categories.includes('datapack')
"
class="environment"
>
<template v-if="clientSide === 'optional' && serverSide === 'optional'">
@ -44,7 +48,7 @@
</span>
</template>
<script setup>
import { GlobeIcon, ClientIcon, ServerIcon, InfoIcon } from '@/components'
import { GlobeIcon, ClientIcon, ServerIcon, InfoIcon } from '@'
</script>
<script>
import { defineComponent } from 'vue'
@ -80,6 +84,13 @@ export default defineComponent({
required: false,
default: false,
},
categories: {
type: Array,
required: false,
default() {
return []
},
},
},
})
</script>

View File

@ -13,7 +13,7 @@
</template>
<script>
import { fileIsValid } from '@/helpers/utils.js'
import { fileIsValid } from '@'
import { defineComponent } from 'vue'
export default defineComponent({
props: {

View File

@ -1,140 +0,0 @@
<template>
<Modal ref="modal" :header="`Report ${props.itemType}`" :noblur="noblur">
<div class="modal-report">
<div class="markdown-body">
<p>
Modding should be safe for everyone, so we take abuse and malicious intent seriously at
Modrinth. We want to hear about harmful content on the site that violates our
<router-link to="/legal/terms">ToS</router-link>
and
<router-link to="/legal/rules">Rules</router-link>
. Rest assured, well keep your identifying information private.
</p>
<p v-if="props.itemType === 'project' || props.itemType === 'version'">
Please <strong>do not</strong> use this to report bugs with the project itself. This form
is only for submitting a report to Modrinth staff. If the project has an Issues link or a
Discord invite, consider reporting it there.
</p>
</div>
<div>
<label class="report-label" for="report-type">
<span>
<strong>Reason</strong>
</span>
</label>
<DropdownSelect
id="report-type"
v-model="reportType"
:options="props.reportTypes"
default-value="Choose report type"
class="multiselect"
/>
</div>
<label class="report-label" for="additional-information">
<strong>Additional information</strong>
<span> Include links and images if possible. Markdown formatting is supported. </span>
</label>
<div>
<div v-if="bodyViewType === 'source'" class="text-input textarea-wrapper">
<Chips v-model="bodyViewType" class="separator" :items="['source', 'preview']" />
<textarea id="body" v-model="body" spellcheck="true" />
</div>
<div v-else class="preview" v-html="renderString(body)"></div>
</div>
<div class="input-group push-right">
<Button @click="cancel">
<XIcon />
Cancel
</Button>
<Button color="primary" @click="submitReport">
<CheckIcon />
Report
</Button>
</div>
</div>
</Modal>
</template>
<script setup>
import { Modal, Chips, XIcon, CheckIcon, DropdownSelect } from '@/components'
import { renderString } from '@/helpers/parse.js'
import { ref } from 'vue'
const props = defineProps({
itemType: {
type: String,
default: '',
},
itemId: {
type: String,
default: '',
},
reportTypes: {
type: Array,
default: () => [],
},
submitReport: {
type: Function,
default: () => {},
},
noblur: {
type: Boolean,
default: false,
},
})
const reportType = ref('')
const body = ref('')
const bodyViewType = ref('source')
const modal = ref(null)
function cancel() {
reportType.value = ''
body.value = ''
bodyViewType.value = 'source'
modal.value.hide()
}
function show() {
modal.value.show()
}
defineExpose({
show,
})
</script>
<style scoped lang="scss">
.modal-report {
padding: var(--gap-lg);
display: flex;
flex-direction: column;
gap: 1rem;
}
.markdown-body {
margin-bottom: 1rem;
}
.report-label {
display: flex;
flex-direction: column;
align-items: flex-start;
margin-bottom: 0.5rem;
}
.text-input {
height: 12rem;
gap: 1rem;
textarea {
// here due to a bug in safari
max-height: 9rem;
}
.preview {
overflow-y: auto;
}
}
</style>

View File

@ -115,6 +115,14 @@ function stopTimer(notif) {
margin: 0;
}
}
@media screen and (max-width: 750px) {
bottom: calc(var(--size-mobile-navbar-height, 15px) + 10px) !important;
&.browse-menu-open {
bottom: calc(var(--size-mobile-navbar-height-expanded, 15px) + 10px) !important;
}
}
}
.notifs-enter-active,

View File

@ -36,8 +36,7 @@
<script setup>
import { ref } from 'vue'
import PopoutMenu from '@/components/base/PopoutMenu.vue'
import Button from '@/components/base/Button.vue'
import { Button, PopoutMenu } from '@'
defineProps({
options: {

View File

@ -50,7 +50,7 @@
</div>
</template>
<script setup>
import { GapIcon, LeftArrowIcon, RightArrowIcon } from '@/components'
import { GapIcon, LeftArrowIcon, RightArrowIcon } from '@'
</script>
<script>
import { defineComponent } from 'vue'
@ -77,7 +77,7 @@ export default defineComponent({
pages() {
let pages = []
if (this.count > 4) {
if (this.count > 7) {
if (this.page + 3 >= this.count) {
pages = [
1,
@ -88,7 +88,7 @@ export default defineComponent({
this.count - 1,
this.count,
]
} else if (this.page > 4) {
} else if (this.page > 5) {
pages = [1, '-', this.page - 1, this.page, this.page + 1, '-', this.count]
} else {
pages = [1, 2, 3, 4, 5, '-', this.count]
@ -103,6 +103,9 @@ export default defineComponent({
methods: {
switchPage(newPage) {
this.$emit('switch-page', newPage)
if (newPage !== null && newPage !== '' && !isNaN(newPage)) {
this.$emit('switch-page', Math.min(Math.max(newPage, 1), this.count))
}
},
},
})

View File

@ -34,6 +34,7 @@
:server-side="serverSide"
:type="projectTypeDisplay"
:search="search"
:categories="categories"
/>
</Categories>
<div class="stats">
@ -65,7 +66,9 @@
</div>
</article>
</template>
<script setup>
import { formatNumber } from '@/helpers'
import {
Badge,
HeartIcon,
@ -75,12 +78,13 @@ import {
Avatar,
Categories,
EnvironmentIndicator,
} from '@/components'
import { formatNumber } from '@/helpers/utils.js'
} from '@'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
dayjs.extend(relativeTime)
</script>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
@ -152,10 +156,6 @@ export default defineComponent({
type: String,
default: null,
},
hasModMessage: {
type: Boolean,
default: false,
},
serverSide: {
type: String,
required: false,
@ -306,8 +306,7 @@ export default defineComponent({
img,
svg {
border-radius: var(--radius-lg);
border: 4px solid var(--color-raised-bg);
border-bottom: none;
box-shadow: -2px -2px 0 2px var(--color-raised-bg), 2px -2px 0 2px var(--color-raised-bg);
}
}

View File

@ -25,9 +25,11 @@
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
import BisectIcon from '@/assets/external/bh.svg'
import { BisectIcon } from '@'
const props = defineProps({
external: {
type: Boolean,
@ -38,6 +40,7 @@ const props = defineProps({
default: '',
},
})
const target = computed(() => (props.external ? '_blank' : '_self'))
</script>

View File

@ -33,5 +33,3 @@ export default {
},
}
</script>
<style scoped></style>

View File

@ -18,8 +18,9 @@
/>
</g>
</g>
</g></svg
><svg
</g>
</svg>
<svg
class="rotate inner"
width="100%"
height="100%"
@ -45,9 +46,7 @@
viewBox="0 0 590 591"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xml:space="preserve"
xmlns:serif="http://www.serif.com/"
style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 2"
>
<g transform="matrix(1,0,0,1,652.392,-0.400578)">
@ -72,12 +71,6 @@
</div>
</template>
<script>
export default {
name: 'AnimatedLogo',
}
</script>
<style lang="scss" scoped>
div {
height: 5rem;

View File

@ -7,7 +7,7 @@
stroke-miterlimit="2"
clip-rule="evenodd"
viewBox="0 0 3307 593"
:class="{ animate: animate }"
:class="{ animate }"
>
<path
fill-rule="nonzero"
@ -30,16 +30,13 @@
</svg>
</template>
<script>
export default {
name: 'TextLogo',
props: {
animate: {
type: Boolean,
default: false,
},
<script setup>
defineProps({
animate: {
type: Boolean,
default: false,
},
}
})
</script>
<style lang="scss" scoped>

View File

@ -1,5 +1,5 @@
<script setup>
import { defineProps, ref } from 'vue'
import { ref } from 'vue'
import { Bar } from 'vue-chartjs'
import {
Chart as ChartJS,

View File

@ -1,43 +1,76 @@
// Base content
export { default as Avatar } from './base/Avatar.vue'
export { default as Badge } from './base/Badge.vue'
export { default as Button } from './base/Button.vue'
export { default as Card } from './base/Card.vue'
export { default as Checkbox } from './base/Checkbox.vue'
export { default as Chips } from './base/Chips.vue'
export { default as ConditionalNuxtLink } from './base/ConditionalNuxtLink.vue'
export { default as CopyCode } from './base/CopyCode.vue'
export { default as DoubleIcon } from './base/DoubleIcon.vue'
export { default as DropArea } from './base/DropArea.vue'
export { default as DropdownSelect } from './base/DropdownSelect.vue'
export { default as EnvironmentIndicator } from './base/EnvironmentIndicator.vue'
export { default as FileInput } from './base/FileInput.vue'
export { default as Notifications } from './base/Notifications.vue'
export { default as OverflowMenu } from './base/OverflowMenu.vue'
export { default as Page } from './base/Page.vue'
export { default as Slider } from './base/Slider.vue'
export { default as AnimatedLogo } from './brand/AnimatedLogo.vue'
export { default as TextLogo } from './brand/TextLogo.vue'
export { default as Pagination } from './base/Pagination.vue'
export { default as Modal } from './base/Modal.vue'
export { default as ModalReport } from './base/ModalReport.vue'
export { default as PopoutMenu } from './base/PopoutMenu.vue'
export { default as ProjectCard } from './base/ProjectCard.vue'
export { default as Promotion } from './base/Promotion.vue'
export { default as EnvironmentIndicator } from './base/EnvironmentIndicator.vue'
export { default as DropdownSelect } from './base/DropdownSelect.vue'
export { default as FileInput } from './base/FileInput.vue'
export { default as DropArea } from './base/DropArea.vue'
export { default as Slider } from './base/Slider.vue'
export { default as Toggle } from './base/Toggle.vue'
export { default as CopyCode } from './base/CopyCode.vue'
export { default as Notifications } from './base/Notifications.vue'
export { default as ModalConfirm } from './base/ModalConfirm.vue'
export { default as Breadcrumbs } from './base/Breadcrumbs.vue'
export { default as ShareModal } from './base/ShareModal.vue'
export { default as LineChart } from './base/LineChart.vue'
export { default as PieChart } from './base/PieChart.vue'
export { default as BarChart } from './base/BarChart.vue'
export { default as SearchDropdown } from './search/SearchDropdown.vue'
export { default as Categories } from './search/Categories.vue'
export { default as SearchFilter } from './search/SearchFilter.vue'
// Branding
export { default as ModrinthIcon } from '@/assets/branding/logo.svg?component'
export { default as AnimatedLogo } from './brand/AnimatedLogo.vue'
export { default as TextLogo } from './brand/TextLogo.vue'
export { default as FourOhFourNotFound } from '@/assets/branding/404.svg?component'
// Charts
export { default as BarChart } from './chart/BarChart.vue'
export { default as LineChart } from './chart/LineChart.vue'
export { default as PieChart } from './chart/PieChart.vue'
// Modals
export { default as Modal } from './modal/Modal.vue'
export { default as ConfirmModal } from './modal/ConfirmModal.vue'
export { default as ReportModal } from './modal/ReportModal.vue'
export { default as ShareModal } from './modal/ShareModal.vue'
// Navigation
export { default as Breadcrumbs } from './nav/Breadcrumbs.vue'
export { default as NavItem } from './nav/NavItem.vue'
export { default as NavRow } from './nav/NavRow.vue'
export { default as NavStack } from './nav/NavStack.vue'
export { default as PopoutMenu } from './base/PopoutMenu.vue'
export { default as OverflowMenu } from './base/OverflowMenu.vue'
// Search
export { default as Categories } from './search/Categories.vue'
export { default as SearchDropdown } from './search/SearchDropdown.vue'
export { default as SearchFilter } from './search/SearchFilter.vue'
// External Icons
export { default as SSODiscordIcon } from '@/assets/external/sso/discord.svg?component'
export { default as SSOGitHubIcon } from '@/assets/external/sso/github.svg?component'
export { default as SSOGitLabIcon } from '@/assets/external/sso/gitlab.svg?component'
export { default as SSOGoogleIcon } from '@/assets/external/sso/google.svg?component'
export { default as SSOMicrosoftIcon } from '@/assets/external/sso/microsoft.svg?component'
export { default as SSOSteamIcon } from '@/assets/external/sso/steam.svg?component'
export { default as AppleIcon } from '@/assets/external/apple.svg?component'
export { default as BisectIcon } from '@/assets/external/bh.svg?component'
export { default as BuyMeACoffeeIcon } from '@/assets/external/bmac.svg?component'
export { default as DiscordIcon } from '@/assets/external/discord.svg?component'
export { default as KoFiIcon } from '@/assets/external/kofi.svg?component'
export { default as MastodonIcon } from '@/assets/external/mastodon.svg?component'
export { default as OpenCollectiveIcon } from '@/assets/external/opencollective.svg?component'
export { default as PatreonIcon } from '@/assets/external/patreon.svg?component'
export { default as PayPalIcon } from '@/assets/external/paypal.svg?component'
export { default as RedditIcon } from '@/assets/external/reddit.svg?component'
export { default as TwitterIcon } from '@/assets/external/twitter.svg?component'
export { default as WindowsIcon } from '@/assets/external/windows.svg?component'
// Icons
export { default as AlignLeftIcon } from '@/assets/icons/align-left.svg?component'
export { default as ArchiveIcon } from '@/assets/icons/archive.svg?component'
export { default as AsteriskIcon } from '@/assets/icons/asterisk.svg?component'
@ -49,6 +82,7 @@ export { default as BoxIcon } from '@/assets/icons/box.svg?component'
export { default as CalendarIcon } from '@/assets/icons/calendar.svg?component'
export { default as ChartIcon } from '@/assets/icons/chart.svg?component'
export { default as CheckIcon } from '@/assets/icons/check.svg?component'
export { default as CheckCheckIcon } from '@/assets/icons/check-check.svg?component'
export { default as CheckCircleIcon } from '@/assets/icons/check-circle.svg?component'
export { default as ChevronLeftIcon } from '@/assets/icons/chevron-left.svg?component'
export { default as ChevronRightIcon } from '@/assets/icons/chevron-right.svg?component'
@ -89,14 +123,20 @@ export { default as HomeIcon } from '@/assets/icons/home.svg?component'
export { default as ImageIcon } from '@/assets/icons/image.svg?component'
export { default as InfoIcon } from '@/assets/icons/info.svg?component'
export { default as IssuesIcon } from '@/assets/icons/issues.svg?component'
export { default as KeyIcon } from '@/assets/icons/key.svg?component'
export { default as LanguagesIcon } from '@/assets/icons/languages.svg?component'
export { default as LeftArrowIcon } from '@/assets/icons/left-arrow.svg?component'
export { default as LibraryIcon } from '@/assets/icons/library.svg?component'
export { default as LightBulbIcon } from '@/assets/icons/light-bulb.svg?component'
export { default as LinkIcon } from '@/assets/icons/link.svg?component'
export { default as ListIcon } from '@/assets/icons/list.svg?component'
export { default as ListEndIcon } from '@/assets/icons/list-end.svg?component'
export { default as LockIcon } from '@/assets/icons/lock.svg?component'
export { default as LogInIcon } from '@/assets/icons/log-in.svg?component'
export { default as LogOutIcon } from '@/assets/icons/log-out.svg?component'
export { default as MailIcon } from '@/assets/icons/mail.svg?component'
export { default as MessageIcon } from '@/assets/icons/message.svg?component'
export { default as MicrophoneIcon } from '@/assets/icons/microphone.svg?component'
export { default as MoonIcon } from '@/assets/icons/moon.svg?component'
export { default as MoreHorizontalIcon } from '@/assets/icons/more-horizontal.svg?component'
export { default as MoreVerticalIcon } from '@/assets/icons/more-vertical.svg?component'
@ -104,6 +144,9 @@ export { default as OmorphiaIcon } from '@/assets/icons/omorphia.svg?component'
export { default as PaintBrushIcon } from '@/assets/icons/paintbrush.svg?component'
export { default as PlayIcon } from '@/assets/icons/play.svg?component'
export { default as PlusIcon } from '@/assets/icons/plus.svg?component'
export { default as RadioButtonIcon } from '@/assets/icons/radio-button.svg?component'
export { default as RadioButtonChecked } from '@/assets/icons/radio-button-checked.svg?component'
export { default as ReplyIcon } from '@/assets/icons/reply.svg?component'
export { default as ReportIcon } from '@/assets/icons/report.svg?component'
export { default as RightArrowIcon } from '@/assets/icons/right-arrow.svg?component'
export { default as SaveIcon } from '@/assets/icons/save.svg?component'
@ -112,8 +155,11 @@ export { default as SearchIcon } from '@/assets/icons/search.svg?component'
export { default as SendIcon } from '@/assets/icons/send.svg?component'
export { default as ServerIcon } from '@/assets/icons/server.svg?component'
export { default as SettingsIcon } from '@/assets/icons/settings.svg?component'
export { default as ShareIcon } from '@/assets/icons/share.svg?component'
export { default as ShieldIcon } from '@/assets/icons/shield.svg?component'
export { default as SlashIcon } from '@/assets/icons/slash.svg?component'
export { default as SortAscendingIcon } from '@/assets/icons/sort-asc.svg?component'
export { default as SortDescendingIcon } from '@/assets/icons/sort-desc.svg?component'
export { default as StarIcon } from '@/assets/icons/star.svg?component'
export { default as StopCircleIcon } from '@/assets/icons/stop-circle.svg?component'
export { default as SunIcon } from '@/assets/icons/sun.svg?component'
@ -136,11 +182,3 @@ export { default as VersionIcon } from '@/assets/icons/version.svg?component'
export { default as WikiIcon } from '@/assets/icons/wiki.svg?component'
export { default as XIcon } from '@/assets/icons/x.svg?component'
export { default as XCircleIcon } from '@/assets/icons/x-circle.svg?component'
export { default as MailIcon } from '@/assets/icons/mail.svg?component'
export { default as ShareIcon } from '@/assets/icons/share.svg?component'
export { default as MastodonIcon } from '@/assets/external/mastodon.svg?component'
export { default as RedditIcon } from '@/assets/external/reddit.svg?component'
export { default as TwitterIcon } from '@/assets/external/twitter.svg?component'
export { default as ModrinthIcon } from '@/assets/branding/logo.svg?component'

View File

@ -1,17 +1,17 @@
<template>
<Modal ref="modal" :header="props.title" :noblur="noblur">
<Modal ref="modal" :header="title" :noblur="noblur">
<div class="modal-delete">
<div class="markdown-body" v-html="renderString(props.description)" />
<label v-if="props.hasToType" for="confirmation" class="confirmation-label">
<div class="markdown-body" v-html="renderString(description)" />
<label v-if="hasToType" for="confirmation" class="confirmation-label">
<span>
<strong>To verify, type</strong>
<em class="confirmation-text">{{ props.confirmationText }}</em>
<em class="confirmation-text">{{ confirmationText }}</em>
<strong>below:</strong>
</span>
</label>
<div class="confirmation-input">
<input
v-if="props.hasToType"
v-if="hasToType"
id="confirmation"
v-model="confirmation_typed"
type="text"
@ -26,7 +26,7 @@
</button>
<button class="btn btn-danger" :disabled="action_disabled" @click="proceed">
<TrashIcon />
{{ props.proceedLabel }}
{{ proceedLabel }}
</button>
</div>
</div>
@ -34,8 +34,7 @@
</template>
<script setup>
import { renderString } from '@/helpers/parse'
import { XIcon, TrashIcon, Modal } from '@/components'
import { Modal, TrashIcon, XIcon, renderString } from '@'
import { ref } from 'vue'
const props = defineProps({

View File

@ -32,7 +32,7 @@
</template>
<script setup>
import { XIcon } from '@/components'
import { XIcon } from '@'
import { ref } from 'vue'
const props = defineProps({

View File

@ -0,0 +1,123 @@
<template>
<Modal ref="modal" :header="`Report ${itemType}`" :noblur="noblur">
<div class="modal-report universal-labels">
<div class="markdown-body">
<p>
Modding should be safe for everyone, so we take abuse and malicious intent seriously at
Modrinth. We want to hear about harmful content on the site that violates our
<router-link to="/legal/terms">ToS</router-link> and
<router-link to="/legal/rules">Rules</router-link>. Rest assured, we'll keep your
identifying information private.
</p>
<p v-if="itemType === 'project' || itemType === 'version'">
Please <strong>do not</strong> use this to report bugs with the project itself. This form
is only for submitting a report to Modrinth staff. If the project has an Issues link or a
Discord invite, consider reporting it there.
</p>
</div>
<label for="report-type">
<span class="label__title">Reason</span>
</label>
<DropdownSelect
id="report-type"
v-model="reportType"
name="report-type"
:options="reportTypes"
:display-name="capitalizeString"
default-value="Choose report type"
class="multiselect"
/>
<label for="report-body">
<span class="label__title">Additional information</span>
<span class="label__description markdown-body">
Please provide additional context about your report. Include links and images if possible.
<strong>Empty reports will be closed.</strong> This editor supports
<a href="https://docs.modrinth.com/markdown" target="_blank">Markdown formatting</a>.
</span>
</label>
<Chips v-model="bodyViewType" class="separator" :items="['source', 'preview']" />
<div class="text-input textarea-wrapper">
<textarea v-if="bodyViewType === 'source'" id="body" v-model="body" spellcheck="true" />
<div v-else class="preview" v-html="renderString(body)" />
</div>
<div class="input-group push-right">
<Button @click="cancel">
<XIcon />
Cancel
</Button>
<Button color="primary" @click="submitReport">
<CheckIcon />
Report
</Button>
</div>
</div>
</Modal>
</template>
<script setup>
import { Chips, DropdownSelect, Modal, CheckIcon, XIcon, capitalizeString, renderString } from '@'
import { ref } from 'vue'
defineProps({
itemType: {
type: String,
default: '',
},
itemId: {
type: String,
default: '',
},
reportTypes: {
type: Array,
default: () => [],
},
submitReport: {
type: Function,
default: () => {},
},
noblur: {
type: Boolean,
default: false,
},
})
const reportType = ref('')
const body = ref('')
const bodyViewType = ref('source')
const modal = ref(null)
function cancel() {
reportType.value = ''
body.value = ''
bodyViewType.value = 'source'
modal.value.hide()
}
function show() {
modal.value.show()
}
defineExpose({
show,
})
</script>
<style scoped lang="scss">
.modal-report {
padding: var(--gap-lg);
.textarea-wrapper {
height: 10rem;
:first-child {
max-height: 8rem;
transform: translateY(1rem);
}
}
.preview {
overflow-y: auto;
}
}
</style>

View File

@ -10,7 +10,7 @@ import {
TwitterIcon,
MastodonIcon,
RedditIcon,
} from '@/components'
} from '@'
import { computed, ref, nextTick } from 'vue'
import QrcodeVue from 'qrcode.vue'

View File

@ -15,7 +15,7 @@
</template>
<script setup>
import { ChevronRightIcon } from '@/components'
import { ChevronRightIcon } from '@'
defineProps({
linkStack: {
type: Array,

View File

@ -1,5 +1,5 @@
<script setup>
import { Button } from '@/components'
import { Button } from '@'
defineProps({
link: {

View File

@ -1,7 +1,3 @@
<script setup>
defineProps({})
</script>
<template>
<div class="omorphia__navstack">
<slot />

View File

@ -9,23 +9,16 @@
</div>
</template>
<script setup>
import { formatCategory } from '@/helpers/utils.js'
</script>
<script>
export default {
name: 'Categories',
props: {
categories: {
type: Array,
default() {
return []
},
import { formatCategory } from '@'
defineProps({
categories: {
type: Array,
default() {
return []
},
},
methods: {
formatCategory,
},
}
})
</script>
<style lang="scss" scoped>

View File

@ -67,7 +67,7 @@
<script setup>
import { ref } from 'vue'
import { Avatar, Button, XIcon, SearchIcon } from '@/components'
import { Avatar, Button, XIcon, SearchIcon } from '@'
const props = defineProps({
options: {

View File

@ -15,50 +15,48 @@
</Checkbox>
</template>
<script>
import { defineComponent } from 'vue'
import Checkbox from '@/components/base/Checkbox.vue'
export default defineComponent({
components: {
Checkbox,
<script setup>
import { Checkbox } from '@'
defineProps({
facetName: {
type: String,
default: '',
},
props: {
facetName: {
type: String,
default: '',
},
displayName: {
type: String,
default: '',
},
icon: {
type: String,
default: '',
},
activeFilters: {
type: Array,
default() {
return []
},
},
displayName: {
type: String,
default: '',
},
emits: ['toggle'],
methods: {
toggle() {
this.$emit('toggle', this.facetName)
icon: {
type: String,
default: '',
},
activeFilters: {
type: Array,
default() {
return []
},
},
})
const emit = defineEmits(['toggle'])
function toggle() {
emit('toggle', this.facetName)
}
</script>
<style lang="scss" scoped>
.filter {
margin-bottom: 0.5rem;
:deep(.filter-text) {
display: flex;
align-items: center;
.icon {
height: 1rem;
svg {
margin-right: 0.25rem;
width: 1rem;
@ -66,6 +64,7 @@ export default defineComponent({
}
}
}
span {
user-select: none;
}

View File

@ -1,3 +1,5 @@
export * from './highlight.js'
export * from './parse.js'
export * from './projects.js'
export * from './users.js'
export * from './utils.js'

View File

@ -95,7 +95,9 @@ export const configuredXss = new xss.FilterXSS({
return xss.safeAttrValue(
tag,
name,
`https://wsrv.nl/?url=${encodeURIComponent(url.toString())}&n=-1`,
`https://wsrv.nl/?url=${encodeURIComponent(
url.toString().replaceAll('&amp;', '&')
)}&n=-1`,
cssFilter
)
} else {

109
lib/helpers/projects.js Normal file
View File

@ -0,0 +1,109 @@
// noinspection JSUnusedGlobalSymbols
export const getProjectTypeForDisplay = (type, categories, tags) => {
if (type === 'mod') {
const isPlugin = categories.some((category) => {
return tags.loaderData.allPluginLoaders.includes(category)
})
const isMod = categories.some((category) => {
return tags.loaderData.modLoaders.includes(category)
})
const isDataPack = categories.some((category) => {
return tags.loaderData.dataPackLoaders.includes(category)
})
if (isMod && isPlugin && isDataPack) {
return 'mod, plugin, and data pack'
} else if (isMod && isPlugin) {
return 'mod and plugin'
} else if (isMod && isDataPack) {
return 'mod and data pack'
} else if (isPlugin && isDataPack) {
return 'plugin and data pack'
} else if (isDataPack) {
return 'data pack'
} else if (isPlugin) {
return 'plugin'
}
}
return type
}
export const getProjectTypeForUrl = (type, loaders, tags) => {
if (type === 'mod') {
const isMod = loaders.some((category) => {
return tags.loaderData.modLoaders.includes(category)
})
const isPlugin = loaders.some((category) => {
return tags.loaderData.allPluginLoaders.includes(category)
})
const isDataPack = loaders.some((category) => {
return tags.loaderData.dataPackLoaders.includes(category)
})
if (isDataPack) {
return 'datapack'
} else if (isPlugin) {
return 'plugin'
} else if (isMod) {
return 'mod'
} else {
return 'mod'
}
} else {
return type
}
}
export const getProjectLink = (project) => {
return `/${getProjectTypeForUrl(project.project_type, project.loaders)}/${
project.slug ? project.slug : project.id
}`
}
export const getVersionLink = (project, version) => {
if (version) {
return getProjectLink(project) + '/version/' + version.id
} else {
return getProjectLink(project)
}
}
export const isApproved = (project) => {
return project && APPROVED_PROJECT_STATUSES.includes(project.status)
}
export const isListed = (project) => {
return project && LISTED_PROJECT_STATUSES.includes(project.status)
}
export const isUnlisted = (project) => {
return project && UNLISTED_PROJECT_STATUSES.includes(project.status)
}
export const isPrivate = (project) => {
return project && PRIVATE_PROJECT_STATUSES.includes(project.status)
}
export const isRejected = (project) => {
return project && REJECTED_PROJECT_STATUSES.includes(project.status)
}
export const isUnderReview = (project) => {
return project && UNDER_REVIEW_PROJECT_STATUSES.includes(project.status)
}
export const isDraft = (project) => {
return project && DRAFT_PROJECT_STATUSES.includes(project.status)
}
export const APPROVED_PROJECT_STATUSES = ['approved', 'archived', 'unlisted', 'private']
export const LISTED_PROJECT_STATUSES = ['approved', 'archived']
export const UNLISTED_PROJECT_STATUSES = ['unlisted', 'withheld']
export const PRIVATE_PROJECT_STATUSES = ['private', 'rejected', 'processing']
export const REJECTED_PROJECT_STATUSES = ['rejected', 'withheld']
export const UNDER_REVIEW_PROJECT_STATUSES = ['processing']
export const DRAFT_PROJECT_STATUSES = ['draft']

11
lib/helpers/users.js Normal file
View File

@ -0,0 +1,11 @@
// noinspection JSUnusedGlobalSymbols
export const getUserLink = (user) => {
return `/user/${user.username}`
}
export const isStaff = (user) => {
return user && STAFF_ROLES.includes(user.role)
}
export const STAFF_ROLES = ['moderator', 'admin']

View File

@ -1,3 +1,81 @@
// noinspection JSUnusedGlobalSymbols
import dayjs from 'dayjs'
export const external = (cosmetics) => (cosmetics.externalLinksNewTab ? '_blank' : '')
// Only use on the complete list of versions for a project,
// partial lists will generate the wrong version slugs
export const computeVersions = (versions, members) => {
const visitedVersions = []
const returnVersions = []
const authorMembers = {}
for (const version of versions.sort(
(a, b) => dayjs(a.date_published) - dayjs(b.date_published)
)) {
if (visitedVersions.includes(version.version_number)) {
visitedVersions.push(version.version_number)
version.displayUrlEnding = version.id
} else {
visitedVersions.push(version.version_number)
version.displayUrlEnding = version.version_number
}
version.primaryFile = version.files.find((file) => file.primary) ?? version.files[0]
if (!version.primaryFile) {
version.primaryFile = {
hashes: {
sha1: '',
sha512: '',
},
url: '#',
filename: 'unknown',
primary: false,
size: 0,
file_type: null,
}
}
version.author = authorMembers[version.author_id]
if (!version.author) {
version.author = members.find((x) => x.user.id === version.author_id)
authorMembers[version.author_id] = version.author
}
returnVersions.push(version)
}
return returnVersions
.reverse()
.map((version, index) => {
const nextVersion = returnVersions[index + 1]
if (nextVersion && version.changelog && nextVersion.changelog === version.changelog) {
return { duplicate: true, ...version }
} else {
return { duplicate: false, ...version }
}
})
.sort((a, b) => dayjs(b.date_published) - dayjs(a.date_published))
}
export const sortedCategories = (tags) => {
return tags.categories.slice().sort((a, b) => {
const headerCompare = a.header.localeCompare(b.header)
if (headerCompare !== 0) {
return headerCompare
}
if (a.header === 'resolutions' && b.header === 'resolutions') {
return a.name.replace(/\D/g, '') - b.name.replace(/\D/g, '')
} else if (a.header === 'performance impact' && b.header === 'performance impact') {
const x = ['potato', 'low', 'medium', 'high', 'screenshot']
return x.indexOf(a.name) - x.indexOf(b.name)
}
return 0
})
}
export const formatNumber = (number, abbreviate = true) => {
const x = +number
if (x >= 1000000 && abbreviate) {
@ -66,6 +144,8 @@ export const formatCategory = (name) => {
return 'BungeeCord'
} else if (name === 'liteloader') {
return 'LiteLoader'
} else if (name === 'neoforge') {
return 'NeoForge'
} else if (name === 'game-mechanics') {
return 'Game Mechanics'
} else if (name === 'worldgen') {
@ -101,7 +181,7 @@ export const formatCategoryHeader = (name) => {
export const formatProjectStatus = (name) => {
if (name === 'approved') {
return 'Listed'
return 'Public'
} else if (name === 'processing') {
return 'Under review'
}
@ -109,8 +189,8 @@ export const formatProjectStatus = (name) => {
return capitalizeString(name)
}
export const formatVersions = (versionArray, store) => {
const allVersions = store.state.tag.gameVersions.slice().reverse()
export const formatVersions = (versionArray, gameVersions) => {
const allVersions = gameVersions.slice().reverse()
const allReleases = allVersions.filter((x) => x.version_type === 'release')
const intervals = []
@ -176,13 +256,13 @@ export const formatVersions = (versionArray, store) => {
for (const interval of newIntervals) {
if (interval.length === 2) {
output.push(`${interval[0][0]}${interval[1][0]}`)
output.push(`${interval[0][0]}${interval[1][0]}`)
} else {
output.push(interval[0][0])
}
}
return output.join(', ')
return (output.length === 0 ? versionArray : output).join(', ')
}
export function cycleValue(value, values) {

View File

@ -1,7 +1,7 @@
{
"name": "omorphia",
"type": "module",
"version": "0.5.1",
"version": "0.6.0",
"files": [
"dist"
],