feat: new hero page (v1.1.0)
BIN
public/branding/dark-banner.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
public/branding/full-desktop-light.png
Normal file
|
After Width: | Height: | Size: 159 KiB |
BIN
public/branding/full-desktop.png
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
public/branding/light-banner.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 213 B After Width: | Height: | Size: 218 KiB |
BIN
public/imgs/icon-cf.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
public/imgs/icon-gd.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
public/imgs/icon-gl.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
public/imgs/icon-p.png
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
public/imgs/icon-scf.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
public/imgs/icon-sgd.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
public/imgs/icon-sgl.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
public/imgs/icon-sp.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
15
public/svg/icon-cf.svg
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<svg width="266" height="265" viewBox="0 0 266 265" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="0.524048" width="264.939" height="264.939" rx="66" fill="url(#paint0_linear_1_19)"/>
|
||||||
|
<path d="M104.513 123.27H94.8717C92.3148 123.27 89.8626 122.254 88.0546 120.446C86.2466 118.638 85.2309 116.186 85.2309 113.629V94.3476C85.2309 91.7907 86.2466 89.3385 88.0546 87.5305C89.8626 85.7225 92.3148 84.7068 94.8717 84.7068H171.998C174.555 84.7068 177.007 85.7225 178.815 87.5305C180.623 89.3385 181.639 91.7907 181.639 94.3476V113.629C181.639 116.186 180.623 118.638 178.815 120.446C177.007 122.254 174.555 123.27 171.998 123.27H162.357M104.513 142.552H94.8717C92.3148 142.552 89.8626 143.567 88.0546 145.376C86.2466 147.184 85.2309 149.636 85.2309 152.193V171.474C85.2309 174.031 86.2466 176.483 88.0546 178.291C89.8626 180.099 92.3148 181.115 94.8717 181.115H171.998C174.555 181.115 177.007 180.099 178.815 178.291C180.623 176.483 181.639 174.031 181.639 171.474V152.193C181.639 149.636 180.623 147.184 178.815 145.376C177.007 143.567 174.555 142.552 171.998 142.552H162.357M104.513 103.988H104.561M104.513 161.833H104.561M138.255 103.988L118.974 132.911H147.896L128.615 161.833" stroke="white" stroke-width="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<circle cx="132.993" cy="132.469" r="91.3779" stroke="url(#paint1_linear_1_19)" stroke-width="8"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_1_19" x1="107.824" y1="54.754" x2="230.579" y2="225.198" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#007BFF"/>
|
||||||
|
<stop offset="1" stop-color="#BF00FF" stop-opacity="0.5"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_1_19" x1="132.993" y1="37.0914" x2="132.993" y2="227.847" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#EFEC32"/>
|
||||||
|
<stop offset="1" stop-color="#98FF60"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
14
public/svg/icon-gd.svg
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<svg width="265" height="266" viewBox="0 0 265 266" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="0.0612793" y="0.86145" width="264.939" height="264.939" rx="66" fill="url(#paint0_linear_1_20)"/>
|
||||||
|
<path d="M104.05 124.132H94.4089C91.852 124.132 89.3998 123.116 87.5918 121.308C85.7838 119.5 84.7681 117.048 84.7681 114.491V95.2091C84.7681 92.6522 85.7838 90.2 87.5918 88.392C89.3998 86.584 91.852 85.5683 94.4089 85.5683H171.536C174.092 85.5683 176.545 86.584 178.353 88.392C180.161 90.2 181.176 92.6522 181.176 95.2091V114.491C181.176 117.048 180.161 119.5 178.353 121.308C176.545 123.116 174.092 124.132 171.536 124.132H161.895M104.05 143.413H94.4089C91.852 143.413 89.3998 144.429 87.5918 146.237C85.7838 148.045 84.7681 150.497 84.7681 153.054V172.336C84.7681 174.893 85.7838 177.345 87.5918 179.153C89.3998 180.961 91.852 181.977 94.4089 181.977H171.536C174.092 181.977 176.545 180.961 178.353 179.153C180.161 177.345 181.176 174.893 181.176 172.336V153.054C181.176 150.497 180.161 148.045 178.353 146.237C176.545 144.429 174.092 143.413 171.536 143.413H161.895M104.05 104.85H104.098M104.05 162.695H104.098M137.793 104.85L118.511 133.772H147.433L128.152 162.695" stroke="white" stroke-width="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<circle cx="132.531" cy="133.331" r="91.3779" stroke="url(#paint1_linear_1_20)" stroke-width="8"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_1_20" x1="107.361" y1="55.6155" x2="230.116" y2="226.059" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_1_20" x1="132.531" y1="37.9529" x2="132.531" y2="228.709" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#EFEC32"/>
|
||||||
|
<stop offset="1" stop-color="#98FF60"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.7 KiB |
14
public/svg/icon-gl.svg
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<svg width="265" height="265" viewBox="0 0 265 265" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="0.0612793" width="264.939" height="264.939" rx="66" fill="url(#paint0_linear_1_25)"/>
|
||||||
|
<path d="M104.05 123.27H94.4089C91.852 123.27 89.3998 122.254 87.5918 120.446C85.7838 118.638 84.7681 116.186 84.7681 113.629V94.3476C84.7681 91.7907 85.7838 89.3385 87.5918 87.5305C89.3998 85.7225 91.852 84.7068 94.4089 84.7068H171.536C174.092 84.7068 176.545 85.7225 178.353 87.5305C180.161 89.3385 181.176 91.7907 181.176 94.3476V113.629C181.176 116.186 180.161 118.638 178.353 120.446C176.545 122.254 174.092 123.27 171.536 123.27H161.895M104.05 142.552H94.4089C91.852 142.552 89.3998 143.567 87.5918 145.376C85.7838 147.184 84.7681 149.636 84.7681 152.193V171.474C84.7681 174.031 85.7838 176.483 87.5918 178.291C89.3998 180.099 91.852 181.115 94.4089 181.115H171.536C174.092 181.115 176.545 180.099 178.353 178.291C180.161 176.483 181.176 174.031 181.176 171.474V152.193C181.176 149.636 180.161 147.184 178.353 145.376C176.545 143.567 174.092 142.552 171.536 142.552H161.895M104.05 103.988H104.098M104.05 161.833H104.098M137.793 103.988L118.511 132.911H147.433L128.152 161.833" stroke="black" stroke-width="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<circle cx="132.531" cy="132.469" r="91.3779" stroke="url(#paint1_linear_1_25)" stroke-width="8"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_1_25" x1="107.361" y1="54.754" x2="230.116" y2="225.198" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="white"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_1_25" x1="132.531" y1="37.0914" x2="132.531" y2="227.847" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#EFEC32"/>
|
||||||
|
<stop offset="1" stop-color="#98FF60"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.7 KiB |
25
public/svg/icon-p.svg
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<svg width="265" height="265" viewBox="0 0 265 265" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="264.939" height="264.939" rx="66" fill="url(#paint0_linear_1_30)"/>
|
||||||
|
<path d="M103.988 123.27H94.3476C91.7907 123.27 89.3385 122.254 87.5305 120.446C85.7225 118.638 84.7068 116.186 84.7068 113.629V94.3476C84.7068 91.7907 85.7225 89.3385 87.5305 87.5305C89.3385 85.7225 91.7907 84.7068 94.3476 84.7068H171.474C174.031 84.7068 176.483 85.7225 178.291 87.5305C180.099 89.3385 181.115 91.7907 181.115 94.3476V113.629C181.115 116.186 180.099 118.638 178.291 120.446C176.483 122.254 174.031 123.27 171.474 123.27H161.833M103.988 142.552H94.3476C91.7907 142.552 89.3385 143.567 87.5305 145.376C85.7225 147.184 84.7068 149.636 84.7068 152.193V171.474C84.7068 174.031 85.7225 176.483 87.5305 178.291C89.3385 180.099 91.7907 181.115 94.3476 181.115H171.474C174.031 181.115 176.483 180.099 178.291 178.291C180.099 176.483 181.115 174.031 181.115 171.474V152.193C181.115 149.636 180.099 147.184 178.291 145.376C176.483 143.567 174.031 142.552 171.474 142.552H161.833M103.988 103.988H104.037M103.988 161.833H104.037M137.731 103.988L118.45 132.911H147.372L128.091 161.833" stroke="white" stroke-width="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<circle cx="132.469" cy="132.469" r="91.3779" stroke="url(#paint1_linear_1_30)" stroke-width="8"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_1_30" x1="51.6631" y1="26.9354" x2="222.549" y2="213.717" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#FF0000"/>
|
||||||
|
<stop offset="0.110405" stop-color="#FF6200"/>
|
||||||
|
<stop offset="0.225785" stop-color="#FFAE00"/>
|
||||||
|
<stop offset="0.326294" stop-color="#FFD500"/>
|
||||||
|
<stop offset="0.422381" stop-color="#99EA00"/>
|
||||||
|
<stop offset="0.498373" stop-color="#4DF457"/>
|
||||||
|
<stop offset="0.593491" stop-color="#26D3AB"/>
|
||||||
|
<stop offset="0.699814" stop-color="#13A9D5"/>
|
||||||
|
<stop offset="0.805673" stop-color="#A200FF"/>
|
||||||
|
<stop offset="0.884464" stop-color="#C62AEB"/>
|
||||||
|
<stop offset="0.957056" stop-color="white"/>
|
||||||
|
<stop offset="0.997383"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_1_30" x1="132.469" y1="37.0914" x2="132.469" y2="227.847" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#EFEC32"/>
|
||||||
|
<stop offset="1" stop-color="#98FF60"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.2 KiB |
15
public/svg/icon-scf.svg
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<svg width="129" height="128" viewBox="0 0 129 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="0.775696" width="127.238" height="127.238" rx="63.6192" fill="url(#paint0_linear_1_189)"/>
|
||||||
|
<path d="M50.7168 59.2012H46.0867C44.8588 59.2012 43.6811 58.7134 42.8128 57.8451C41.9445 56.9768 41.4567 55.7991 41.4567 54.5712V45.311C41.4567 44.0831 41.9445 42.9054 42.8128 42.0371C43.6811 41.1688 44.8588 40.681 46.0867 40.681H83.1273C84.3552 40.681 85.5329 41.1688 86.4012 42.0371C87.2695 42.9054 87.7573 44.0831 87.7573 45.311V54.5712C87.7573 55.7991 87.2695 56.9768 86.4012 57.8451C85.5329 58.7134 84.3552 59.2012 83.1273 59.2012H78.4972M50.7168 68.4614H46.0867C44.8588 68.4614 43.6811 68.9492 42.8128 69.8175C41.9445 70.6858 41.4567 71.8635 41.4567 73.0914V82.3516C41.4567 83.5795 41.9445 84.7572 42.8128 85.6255C43.6811 86.4938 44.8588 86.9816 46.0867 86.9816H83.1273C84.3552 86.9816 85.5329 86.4938 86.4012 85.6255C87.2695 84.7572 87.7573 83.5795 87.7573 82.3516V73.0914C87.7573 71.8635 87.2695 70.6858 86.4012 69.8175C85.5329 68.9492 84.3552 68.4614 83.1273 68.4614H78.4972M50.7168 49.9411H50.7399M50.7168 77.7215H50.7399M66.922 49.9411L57.6619 63.8313H71.5521L62.292 77.7215" stroke="white" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<circle cx="64.395" cy="63.6192" r="41.8058" stroke="url(#paint1_linear_1_189)" stroke-width="8"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_1_189" x1="52.3073" y1="26.296" x2="111.261" y2="108.153" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#007BFF"/>
|
||||||
|
<stop offset="1" stop-color="#BF00FF" stop-opacity="0.5"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_1_189" x1="64.395" y1="17.8134" x2="64.395" y2="109.425" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#EFEC32"/>
|
||||||
|
<stop offset="1" stop-color="#98FF60"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
14
public/svg/icon-sgd.svg
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="0.761536" y="0.293091" width="127.238" height="127.238" rx="63.6192" fill="url(#paint0_linear_1_199)"/>
|
||||||
|
<path d="M50.7026 59.4943H46.0726C44.8446 59.4943 43.6669 59.0065 42.7986 58.1382C41.9303 57.2699 41.4425 56.0922 41.4425 54.8643V45.6041C41.4425 44.3762 41.9303 43.1985 42.7986 42.3302C43.6669 41.4619 44.8446 40.9741 46.0726 40.9741H83.1131C84.3411 40.9741 85.5187 41.4619 86.3871 42.3302C87.2554 43.1985 87.7432 44.3762 87.7432 45.6041V54.8643C87.7432 56.0922 87.2554 57.2699 86.3871 58.1382C85.5187 59.0065 84.3411 59.4943 83.1131 59.4943H78.483M50.7026 68.7545H46.0726C44.8446 68.7545 43.6669 69.2423 42.7986 70.1106C41.9303 70.9789 41.4425 72.1566 41.4425 73.3845V82.6447C41.4425 83.8726 41.9303 85.0503 42.7986 85.9186C43.6669 86.7869 44.8446 87.2747 46.0726 87.2747H83.1131C84.3411 87.2747 85.5187 86.7869 86.3871 85.9186C87.2554 85.0503 87.7432 83.8726 87.7432 82.6447V73.3845C87.7432 72.1566 87.2554 70.9789 86.3871 70.1106C85.5187 69.2423 84.3411 68.7545 83.1131 68.7545H78.483M50.7026 50.2342H50.7258M50.7026 78.0146H50.7258M66.9079 50.2342L57.6477 64.1244H71.5379L62.2778 78.0146" stroke="white" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<circle cx="64.3808" cy="63.9123" r="41.8058" stroke="url(#paint1_linear_1_199)" stroke-width="8"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_1_199" x1="52.2931" y1="26.589" x2="111.247" y2="108.446" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_1_199" x1="64.3808" y1="18.1065" x2="64.3808" y2="109.718" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#EFEC32"/>
|
||||||
|
<stop offset="1" stop-color="#98FF60"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.7 KiB |
14
public/svg/icon-sgl.svg
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="0.761536" width="127.238" height="127.238" rx="63.6192" fill="url(#paint0_linear_1_204)"/>
|
||||||
|
<path d="M50.7026 59.2012H46.0726C44.8446 59.2012 43.6669 58.7134 42.7986 57.8451C41.9303 56.9768 41.4425 55.7991 41.4425 54.5712V45.311C41.4425 44.0831 41.9303 42.9054 42.7986 42.0371C43.6669 41.1688 44.8446 40.681 46.0726 40.681H83.1131C84.3411 40.681 85.5187 41.1688 86.3871 42.0371C87.2554 42.9054 87.7432 44.0831 87.7432 45.311V54.5712C87.7432 55.7991 87.2554 56.9768 86.3871 57.8451C85.5187 58.7134 84.3411 59.2012 83.1131 59.2012H78.483M50.7026 68.4614H46.0726C44.8446 68.4614 43.6669 68.9492 42.7986 69.8175C41.9303 70.6858 41.4425 71.8635 41.4425 73.0914V82.3516C41.4425 83.5795 41.9303 84.7572 42.7986 85.6255C43.6669 86.4938 44.8446 86.9816 46.0726 86.9816H83.1131C84.3411 86.9816 85.5187 86.4938 86.3871 85.6255C87.2554 84.7572 87.7432 83.5795 87.7432 82.3516V73.0914C87.7432 71.8635 87.2554 70.6858 86.3871 69.8175C85.5187 68.9492 84.3411 68.4614 83.1131 68.4614H78.483M50.7026 49.9411H50.7258M50.7026 77.7215H50.7258M66.9079 49.9411L57.6477 63.8313H71.5379L62.2778 77.7215" stroke="black" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<circle cx="64.3808" cy="63.6192" r="41.8058" stroke="url(#paint1_linear_1_204)" stroke-width="8"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_1_204" x1="52.2931" y1="26.296" x2="111.247" y2="108.153" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="white"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_1_204" x1="64.3808" y1="17.8134" x2="64.3808" y2="109.425" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#EFEC32"/>
|
||||||
|
<stop offset="1" stop-color="#98FF60"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.7 KiB |
25
public/svg/icon-sp.svg
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="127.238" height="127.238" rx="63.6192" fill="url(#paint0_linear_1_194)"/>
|
||||||
|
<path d="M49.9411 59.2012H45.311C44.0831 59.2012 42.9054 58.7134 42.0371 57.8451C41.1688 56.9768 40.681 55.7991 40.681 54.5712V45.311C40.681 44.0831 41.1688 42.9054 42.0371 42.0371C42.9054 41.1688 44.0831 40.681 45.311 40.681H82.3516C83.5795 40.681 84.7572 41.1688 85.6255 42.0371C86.4938 42.9054 86.9816 44.0831 86.9816 45.311V54.5712C86.9816 55.7991 86.4938 56.9768 85.6255 57.8451C84.7572 58.7134 83.5795 59.2012 82.3516 59.2012H77.7215M49.9411 68.4614H45.311C44.0831 68.4614 42.9054 68.9492 42.0371 69.8175C41.1688 70.6858 40.681 71.8635 40.681 73.0914V82.3516C40.681 83.5795 41.1688 84.7572 42.0371 85.6255C42.9054 86.4938 44.0831 86.9816 45.311 86.9816H82.3516C83.5795 86.9816 84.7572 86.4938 85.6255 85.6255C86.4938 84.7572 86.9816 83.5795 86.9816 82.3516V73.0914C86.9816 71.8635 86.4938 70.6858 85.6255 69.8175C84.7572 68.9492 83.5795 68.4614 82.3516 68.4614H77.7215M49.9411 49.9411H49.9643M49.9411 77.7215H49.9643M66.1463 49.9411L56.8862 63.8313H70.7764L61.5163 77.7215" stroke="white" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<circle cx="63.6193" cy="63.6192" r="41.8058" stroke="url(#paint1_linear_1_194)" stroke-width="8"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_1_194" x1="24.8115" y1="12.9359" x2="106.88" y2="102.639" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#FF0000"/>
|
||||||
|
<stop offset="0.110405" stop-color="#FF6200"/>
|
||||||
|
<stop offset="0.225785" stop-color="#FFAE00"/>
|
||||||
|
<stop offset="0.326294" stop-color="#FFD500"/>
|
||||||
|
<stop offset="0.422381" stop-color="#99EA00"/>
|
||||||
|
<stop offset="0.498373" stop-color="#4DF457"/>
|
||||||
|
<stop offset="0.593491" stop-color="#26D3AB"/>
|
||||||
|
<stop offset="0.699814" stop-color="#13A9D5"/>
|
||||||
|
<stop offset="0.805673" stop-color="#A200FF"/>
|
||||||
|
<stop offset="0.884464" stop-color="#C62AEB"/>
|
||||||
|
<stop offset="0.957056" stop-color="white"/>
|
||||||
|
<stop offset="0.997383"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_1_194" x1="63.6193" y1="17.8134" x2="63.6193" y2="109.425" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#EFEC32"/>
|
||||||
|
<stop offset="1" stop-color="#98FF60"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.2 KiB |
@ -39,6 +39,10 @@
|
|||||||
--chart-3: 216 92% 60%;
|
--chart-3: 216 92% 60%;
|
||||||
--chart-4: 210 98% 78%;
|
--chart-4: 210 98% 78%;
|
||||||
--chart-5: 212 97% 87%;
|
--chart-5: 212 97% 87%;
|
||||||
|
|
||||||
|
--color-one: #37ecba;
|
||||||
|
--color-two: #72afd3;
|
||||||
|
--color-three: #ff2e63;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
|
|||||||
37
src/app/help/how-to-customize/page.tsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
|
export default function HelpPage() {
|
||||||
|
return (
|
||||||
|
<main className="sm:grid sm:grid-cols-6 pt-20">
|
||||||
|
<div className="pl-3 border rounded ml-4">
|
||||||
|
<strong className="pt-2">MHSF Help Guides</strong>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<L H="/">Go back to server list</L>
|
||||||
|
</div>
|
||||||
|
<div className="prose dark:prose-invert p-4 pl-[50px] max-w-[100%] col-span-5">
|
||||||
|
<h1>How to customize your server</h1>
|
||||||
|
<p>
|
||||||
|
Customizing a part of your server is easy, as long as you have access
|
||||||
|
to the account the server is owned under. To own a server, first go to
|
||||||
|
the server from the <L H="/">server list</L>. Make sure you own an
|
||||||
|
account by linking an account or creating a new one,{" "}
|
||||||
|
<L H="/help/how-to-link">
|
||||||
|
and make sure you linked your Minecraft account.
|
||||||
|
</L>{" "}
|
||||||
|
</p>
|
||||||
|
<h2>Guide</h2>
|
||||||
|
<p>
|
||||||
|
After going to the server page, click the Customization tab, and if
|
||||||
|
you own the server, click the button "Click to own this server". You
|
||||||
|
have successfully owned your Minehut server!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function L({ H, children }: { H: string; children: ReactNode }) {
|
||||||
|
return <Link href={H}>{children}</Link>;
|
||||||
|
}
|
||||||
43
src/app/help/how-to-link/page.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
|
export default function HelpPage() {
|
||||||
|
return (
|
||||||
|
<main className="sm:grid sm:grid-cols-6 pt-20">
|
||||||
|
<div className="pl-3 border rounded ml-4">
|
||||||
|
<strong className="pt-2">MHSF Help Guides</strong>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<L H="/">Go back to server list</L>
|
||||||
|
</div>
|
||||||
|
<div className="prose dark:prose-invert p-4 pl-[50px] max-w-[100%] col-span-5">
|
||||||
|
<h1>How to link your Minecraft account</h1>
|
||||||
|
<p>
|
||||||
|
To link your Minecraft account, make sure you have a MHSF account
|
||||||
|
created, and go into the settings in the top right, and press
|
||||||
|
"Security/Profile settings". Click "Link Account".
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Joining the server</h2>
|
||||||
|
<p>
|
||||||
|
After launching Minecraft, join the server{" "}
|
||||||
|
<code>MHSFPV.minehut.gg</code> and take note of the code being said in
|
||||||
|
chat. <i>(You may need to go into the lobby to start up MHSFPV)</i>{" "}
|
||||||
|
Put this code the number selector, click "Submit", and you have linked
|
||||||
|
your account!
|
||||||
|
<br /> Congratulations!
|
||||||
|
</p>
|
||||||
|
<br />
|
||||||
|
<b>Related Articles:</b>
|
||||||
|
<p>
|
||||||
|
{" "}
|
||||||
|
- <L H="/help/how-to-customize">How to customize your server</L>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function L({ H, children }: { H: string; children: ReactNode }) {
|
||||||
|
return <Link href={H}>{children}</Link>;
|
||||||
|
}
|
||||||
@ -1,5 +1,4 @@
|
|||||||
import { GeistSans } from "geist/font/sans";
|
import { GeistSans } from "geist/font/sans";
|
||||||
import { Server } from "lucide-react";
|
|
||||||
import { SpeedInsights } from "@vercel/speed-insights/next";
|
import { SpeedInsights } from "@vercel/speed-insights/next";
|
||||||
import { Analytics } from "@vercel/analytics/react";
|
import { Analytics } from "@vercel/analytics/react";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
@ -21,7 +20,7 @@ import { CommandBarer } from "@/components/CommandBar";
|
|||||||
import ThemedToaster from "@/components/misc/ThemedToaster";
|
import ThemedToaster from "@/components/misc/ThemedToaster";
|
||||||
import UnofficalDialog from "@/components/misc/UnofficalDialog";
|
import UnofficalDialog from "@/components/misc/UnofficalDialog";
|
||||||
import ClientFadeIn from "@/components/ClientFadeIn";
|
import ClientFadeIn from "@/components/ClientFadeIn";
|
||||||
import toast from "react-hot-toast";
|
import { BrandingGenericIcon } from "@/components/Icon";
|
||||||
|
|
||||||
const inter = interFont({ variable: "--font-inter", subsets: ["latin"] });
|
const inter = interFont({ variable: "--font-inter", subsets: ["latin"] });
|
||||||
export default async function RootLayout({
|
export default async function RootLayout({
|
||||||
@ -45,16 +44,16 @@ export default async function RootLayout({
|
|||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
"w-screen h-12 border-b fixed backdrop-blur flex z-10 " +
|
"w-screen h-[3rem] border-b fixed backdrop-blur flex z-10 " +
|
||||||
(banner.isBanner == true ? "mt-8" : "")
|
(banner.isBanner == true ? "mt-8" : "")
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="items-center me-auto mt-3 pl-7">
|
<div className="items-center me-auto mt-2 pl-7">
|
||||||
<Breadcrumb>
|
<Breadcrumb>
|
||||||
<BreadcrumbList>
|
<BreadcrumbList>
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
<BreadcrumbPage className="max-sm:hidden">
|
<BreadcrumbPage className="max-sm:hidden">
|
||||||
<Server />
|
<BrandingGenericIcon className="max-w-[32px] max-h-[32px] " />
|
||||||
</BreadcrumbPage>
|
</BreadcrumbPage>
|
||||||
</Link>
|
</Link>
|
||||||
<TextFromPathname />
|
<TextFromPathname />
|
||||||
|
|||||||
@ -12,7 +12,7 @@ export const metadata: Metadata = {
|
|||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
<div className=" pt-[60px] p-4">
|
<div className="pt-[60px]">
|
||||||
<ServerList />
|
<ServerList />
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@ -4,9 +4,15 @@ import FadeIn from "react-fade-in";
|
|||||||
export default function ClientFadeIn({
|
export default function ClientFadeIn({
|
||||||
children,
|
children,
|
||||||
delay = 0,
|
delay = 0,
|
||||||
|
id,
|
||||||
}: {
|
}: {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
delay?: number;
|
delay?: number;
|
||||||
|
id?: string;
|
||||||
}) {
|
}) {
|
||||||
return <FadeIn delay={delay}>{children}</FadeIn>;
|
return (
|
||||||
|
<div id={id}>
|
||||||
|
<FadeIn delay={delay}>{children}</FadeIn>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
276
src/components/Icon.tsx
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
"use client";
|
||||||
|
import { useTheme } from "next-themes";
|
||||||
|
import type { SVGProps } from "react";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a colorful version of the branding icon.
|
||||||
|
*
|
||||||
|
* The stored SVG file is at `/public/svg/icon-cf.svg`
|
||||||
|
*
|
||||||
|
* @param props The props for the SVG element.
|
||||||
|
* @returns A JSX element representing the colorful branding icon.
|
||||||
|
*/
|
||||||
|
export function BrandingColorfulIcon(props: SVGProps<SVGSVGElement>) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width="266"
|
||||||
|
height="265"
|
||||||
|
viewBox="0 0 266 265"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<rect
|
||||||
|
x="0.524048"
|
||||||
|
width="264.939"
|
||||||
|
height="264.939"
|
||||||
|
rx="66"
|
||||||
|
fill="url(#paint0_linear_1_19)"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M104.513 123.27H94.8717C92.3148 123.27 89.8626 122.254 88.0546 120.446C86.2466 118.638 85.2309 116.186 85.2309 113.629V94.3476C85.2309 91.7907 86.2466 89.3385 88.0546 87.5305C89.8626 85.7225 92.3148 84.7068 94.8717 84.7068H171.998C174.555 84.7068 177.007 85.7225 178.815 87.5305C180.623 89.3385 181.639 91.7907 181.639 94.3476V113.629C181.639 116.186 180.623 118.638 178.815 120.446C177.007 122.254 174.555 123.27 171.998 123.27H162.357M104.513 142.552H94.8717C92.3148 142.552 89.8626 143.567 88.0546 145.376C86.2466 147.184 85.2309 149.636 85.2309 152.193V171.474C85.2309 174.031 86.2466 176.483 88.0546 178.291C89.8626 180.099 92.3148 181.115 94.8717 181.115H171.998C174.555 181.115 177.007 180.099 178.815 178.291C180.623 176.483 181.639 174.031 181.639 171.474V152.193C181.639 149.636 180.623 147.184 178.815 145.376C177.007 143.567 174.555 142.552 171.998 142.552H162.357M104.513 103.988H104.561M104.513 161.833H104.561M138.255 103.988L118.974 132.911H147.896L128.615 161.833"
|
||||||
|
stroke="white"
|
||||||
|
stroke-width="10"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
cx="132.993"
|
||||||
|
cy="132.469"
|
||||||
|
r="91.3779"
|
||||||
|
stroke="url(#paint1_linear_1_19)"
|
||||||
|
stroke-width="8"
|
||||||
|
/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient
|
||||||
|
id="paint0_linear_1_19"
|
||||||
|
x1="107.824"
|
||||||
|
y1="54.754"
|
||||||
|
x2="230.579"
|
||||||
|
y2="225.198"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<stop stop-color="#007BFF" />
|
||||||
|
<stop offset="1" stop-color="#BF00FF" stop-opacity="0.5" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="paint1_linear_1_19"
|
||||||
|
x1="132.993"
|
||||||
|
y1="37.0914"
|
||||||
|
x2="132.993"
|
||||||
|
y2="227.847"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<stop stop-color="#EFEC32" />
|
||||||
|
<stop offset="1" stop-color="#98FF60" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns the optional Pride icon
|
||||||
|
*
|
||||||
|
* The stored SVG file is at `/public/svg/icon-p.svg`
|
||||||
|
*
|
||||||
|
* @param {SVGProps<SVGSVGElement>} props The props for the SVG element.
|
||||||
|
* @returns A JSX element representing the branding icon.
|
||||||
|
*/
|
||||||
|
export function BrandingPrideIcon(props: SVGProps<SVGSVGElement>) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width="265"
|
||||||
|
height="265"
|
||||||
|
viewBox="0 0 265 265"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<rect
|
||||||
|
width="264.939"
|
||||||
|
height="264.939"
|
||||||
|
rx="66"
|
||||||
|
fill="url(#paint0_linear_1_30)"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M103.988 123.27H94.3476C91.7907 123.27 89.3385 122.254 87.5305 120.446C85.7225 118.638 84.7068 116.186 84.7068 113.629V94.3476C84.7068 91.7907 85.7225 89.3385 87.5305 87.5305C89.3385 85.7225 91.7907 84.7068 94.3476 84.7068H171.474C174.031 84.7068 176.483 85.7225 178.291 87.5305C180.099 89.3385 181.115 91.7907 181.115 94.3476V113.629C181.115 116.186 180.099 118.638 178.291 120.446C176.483 122.254 174.031 123.27 171.474 123.27H161.833M103.988 142.552H94.3476C91.7907 142.552 89.3385 143.567 87.5305 145.376C85.7225 147.184 84.7068 149.636 84.7068 152.193V171.474C84.7068 174.031 85.7225 176.483 87.5305 178.291C89.3385 180.099 91.7907 181.115 94.3476 181.115H171.474C174.031 181.115 176.483 180.099 178.291 178.291C180.099 176.483 181.115 174.031 181.115 171.474V152.193C181.115 149.636 180.099 147.184 178.291 145.376C176.483 143.567 174.031 142.552 171.474 142.552H161.833M103.988 103.988H104.037M103.988 161.833H104.037M137.731 103.988L118.45 132.911H147.372L128.091 161.833"
|
||||||
|
stroke="white"
|
||||||
|
stroke-width="10"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
cx="132.469"
|
||||||
|
cy="132.469"
|
||||||
|
r="91.3779"
|
||||||
|
stroke="url(#paint1_linear_1_30)"
|
||||||
|
stroke-width="8"
|
||||||
|
/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient
|
||||||
|
id="paint0_linear_1_30"
|
||||||
|
x1="51.6631"
|
||||||
|
y1="26.9354"
|
||||||
|
x2="222.549"
|
||||||
|
y2="213.717"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<stop stop-color="#FF0000" />
|
||||||
|
<stop offset="0.110405" stop-color="#FF6200" />
|
||||||
|
<stop offset="0.225785" stop-color="#FFAE00" />
|
||||||
|
<stop offset="0.326294" stop-color="#FFD500" />
|
||||||
|
<stop offset="0.422381" stop-color="#99EA00" />
|
||||||
|
<stop offset="0.498373" stop-color="#4DF457" />
|
||||||
|
<stop offset="0.593491" stop-color="#26D3AB" />
|
||||||
|
<stop offset="0.699814" stop-color="#13A9D5" />
|
||||||
|
<stop offset="0.805673" stop-color="#A200FF" />
|
||||||
|
<stop offset="0.884464" stop-color="#C62AEB" />
|
||||||
|
<stop offset="0.957056" stop-color="white" />
|
||||||
|
<stop offset="0.997383" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="paint1_linear_1_30"
|
||||||
|
x1="132.469"
|
||||||
|
y1="37.0914"
|
||||||
|
x2="132.469"
|
||||||
|
y2="227.847"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<stop stop-color="#EFEC32" />
|
||||||
|
<stop offset="1" stop-color="#98FF60" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the branding icon based on the current theme.
|
||||||
|
*
|
||||||
|
* If the theme is dark, the branding icon is a dark version of the logo.
|
||||||
|
* If the theme is light, the branding icon is a light version of the logo.
|
||||||
|
*
|
||||||
|
* The stored SVG file is at `/public/svg/icon-gl.svg` or `/public/svg/icon-gd.svg`
|
||||||
|
*
|
||||||
|
* @param {SVGProps<SVGSVGElement>} props The props for the SVG element.
|
||||||
|
*
|
||||||
|
* @returns A JSX element representing the branding icon.
|
||||||
|
*/
|
||||||
|
export function BrandingGenericIcon(props: SVGProps<SVGSVGElement>) {
|
||||||
|
const { resolvedTheme } = useTheme();
|
||||||
|
|
||||||
|
if (resolvedTheme == "dark") {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width="265"
|
||||||
|
height="266"
|
||||||
|
viewBox="0 0 265 266"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<rect
|
||||||
|
x="0.0612793"
|
||||||
|
y="0.86145"
|
||||||
|
width="264.939"
|
||||||
|
height="264.939"
|
||||||
|
rx="66"
|
||||||
|
fill="url(#paint0_linear_1_20)"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M104.05 124.132H94.4089C91.852 124.132 89.3998 123.116 87.5918 121.308C85.7838 119.5 84.7681 117.048 84.7681 114.491V95.2091C84.7681 92.6522 85.7838 90.2 87.5918 88.392C89.3998 86.584 91.852 85.5683 94.4089 85.5683H171.536C174.092 85.5683 176.545 86.584 178.353 88.392C180.161 90.2 181.176 92.6522 181.176 95.2091V114.491C181.176 117.048 180.161 119.5 178.353 121.308C176.545 123.116 174.092 124.132 171.536 124.132H161.895M104.05 143.413H94.4089C91.852 143.413 89.3998 144.429 87.5918 146.237C85.7838 148.045 84.7681 150.497 84.7681 153.054V172.336C84.7681 174.893 85.7838 177.345 87.5918 179.153C89.3998 180.961 91.852 181.977 94.4089 181.977H171.536C174.092 181.977 176.545 180.961 178.353 179.153C180.161 177.345 181.176 174.893 181.176 172.336V153.054C181.176 150.497 180.161 148.045 178.353 146.237C176.545 144.429 174.092 143.413 171.536 143.413H161.895M104.05 104.85H104.098M104.05 162.695H104.098M137.793 104.85L118.511 133.772H147.433L128.152 162.695"
|
||||||
|
stroke="white"
|
||||||
|
stroke-width="10"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
cx="132.531"
|
||||||
|
cy="133.331"
|
||||||
|
r="91.3779"
|
||||||
|
stroke="url(#paint1_linear_1_20)"
|
||||||
|
stroke-width="8"
|
||||||
|
/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient
|
||||||
|
id="paint0_linear_1_20"
|
||||||
|
x1="107.361"
|
||||||
|
y1="55.6155"
|
||||||
|
x2="230.116"
|
||||||
|
y2="226.059"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<stop />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="paint1_linear_1_20"
|
||||||
|
x1="132.531"
|
||||||
|
y1="37.9529"
|
||||||
|
x2="132.531"
|
||||||
|
y2="228.709"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<stop stop-color="#EFEC32" />
|
||||||
|
<stop offset="1" stop-color="#98FF60" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width="265"
|
||||||
|
height="265"
|
||||||
|
viewBox="0 0 265 265"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<rect
|
||||||
|
x="0.0612793"
|
||||||
|
width="264.939"
|
||||||
|
height="264.939"
|
||||||
|
rx="66"
|
||||||
|
fill="url(#paint0_linear_1_25)"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M104.05 123.27H94.4089C91.852 123.27 89.3998 122.254 87.5918 120.446C85.7838 118.638 84.7681 116.186 84.7681 113.629V94.3476C84.7681 91.7907 85.7838 89.3385 87.5918 87.5305C89.3998 85.7225 91.852 84.7068 94.4089 84.7068H171.536C174.092 84.7068 176.545 85.7225 178.353 87.5305C180.161 89.3385 181.176 91.7907 181.176 94.3476V113.629C181.176 116.186 180.161 118.638 178.353 120.446C176.545 122.254 174.092 123.27 171.536 123.27H161.895M104.05 142.552H94.4089C91.852 142.552 89.3998 143.567 87.5918 145.376C85.7838 147.184 84.7681 149.636 84.7681 152.193V171.474C84.7681 174.031 85.7838 176.483 87.5918 178.291C89.3998 180.099 91.852 181.115 94.4089 181.115H171.536C174.092 181.115 176.545 180.099 178.353 178.291C180.161 176.483 181.176 174.031 181.176 171.474V152.193C181.176 149.636 180.161 147.184 178.353 145.376C176.545 143.567 174.092 142.552 171.536 142.552H161.895M104.05 103.988H104.098M104.05 161.833H104.098M137.793 103.988L118.511 132.911H147.433L128.152 161.833"
|
||||||
|
stroke="black"
|
||||||
|
stroke-width="10"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
cx="132.531"
|
||||||
|
cy="132.469"
|
||||||
|
r="91.3779"
|
||||||
|
stroke="url(#paint1_linear_1_25)"
|
||||||
|
stroke-width="8"
|
||||||
|
/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient
|
||||||
|
id="paint0_linear_1_25"
|
||||||
|
x1="107.361"
|
||||||
|
y1="54.754"
|
||||||
|
x2="230.116"
|
||||||
|
y2="225.198"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<stop stop-color="white" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="paint1_linear_1_25"
|
||||||
|
x1="132.531"
|
||||||
|
y1="37.0914"
|
||||||
|
x2="132.531"
|
||||||
|
y2="227.847"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<stop stop-color="#EFEC32" />
|
||||||
|
<stop offset="1" stop-color="#98FF60" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,10 +1,20 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Badge } from "./ui/badge";
|
import { Badge } from "./ui/badge";
|
||||||
import ServersList from "@/lib/list";
|
import ServersList from "@/lib/list";
|
||||||
import { CircleUser, Network, Sun, Check, XIcon, Info } from "lucide-react";
|
import {
|
||||||
|
CircleUser,
|
||||||
|
Network,
|
||||||
|
Sun,
|
||||||
|
Check,
|
||||||
|
XIcon,
|
||||||
|
Info,
|
||||||
|
ArrowDownZA,
|
||||||
|
LogIn,
|
||||||
|
ImageIcon,
|
||||||
|
} from "lucide-react";
|
||||||
import Stat from "./Stat";
|
import Stat from "./Stat";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@ -49,6 +59,50 @@ import {
|
|||||||
import ClientFadeIn from "./ClientFadeIn";
|
import ClientFadeIn from "./ClientFadeIn";
|
||||||
import { Skeleton } from "./ui/skeleton";
|
import { Skeleton } from "./ui/skeleton";
|
||||||
import useClipboard from "@/lib/useClipboard";
|
import useClipboard from "@/lib/useClipboard";
|
||||||
|
import { SignedIn, SignedOut, useUser } from "@clerk/nextjs";
|
||||||
|
import Link from "next/link";
|
||||||
|
import SparklesText from "./effects/sparkles-text";
|
||||||
|
import Particles from "./effects/particles";
|
||||||
|
import { useTheme } from "next-themes";
|
||||||
|
import { ChatBubbleIcon, InputIcon } from "@radix-ui/react-icons";
|
||||||
|
import Marquee from "./effects/marquee";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
|
||||||
|
import { SignInPopover } from "./clerk/SignInPopoverButton";
|
||||||
|
import { BentoCard, BentoGrid } from "./effects/bento-grid";
|
||||||
|
|
||||||
|
const features = [
|
||||||
|
{
|
||||||
|
Icon: ChatBubbleIcon,
|
||||||
|
name: "Add a Discord widget",
|
||||||
|
description:
|
||||||
|
"Show where your players talk to each-other, including an online users count.",
|
||||||
|
href: "/help/how-to-customize",
|
||||||
|
cta: "Learn more",
|
||||||
|
background: <span />,
|
||||||
|
className: "lg:row-start-1 lg:row-end-2 lg:col-start-2 lg:col-end-3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Icon: InputIcon,
|
||||||
|
name: "Descriptions",
|
||||||
|
href: "/help/how-to-customize",
|
||||||
|
cta: "Learn more",
|
||||||
|
description:
|
||||||
|
"Format your descriptions using Markdown to show what your server has to offer.",
|
||||||
|
background: <span />,
|
||||||
|
className: "lg:col-start-1 lg:col-end-2 lg:row-start-1 lg:row-end-2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Icon: ImageIcon,
|
||||||
|
name: "Banners",
|
||||||
|
href: "/help/how-to-customize",
|
||||||
|
cta: "Learn more",
|
||||||
|
description:
|
||||||
|
"Show a banner with can contain images that show on your server page.",
|
||||||
|
background: <span />,
|
||||||
|
className: "lg:col-start-3 lg:col-end-4 lg:row-start-1 lg:row-end-2",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
export default function ServerList() {
|
export default function ServerList() {
|
||||||
const [loading, setLoading]: any = useState(true);
|
const [loading, setLoading]: any = useState(true);
|
||||||
@ -62,6 +116,7 @@ export default function ServerList() {
|
|||||||
const [random, setRandom] = useState(false);
|
const [random, setRandom] = useState(false);
|
||||||
const [serverList, setServerList] = useState(new ServersList([]));
|
const [serverList, setServerList] = useState(new ServersList([]));
|
||||||
const [textCopied, setTextCopied] = useState(false);
|
const [textCopied, setTextCopied] = useState(false);
|
||||||
|
const [padding, setPadding] = useState(0);
|
||||||
const bigger = async (server: OnlineServer) =>
|
const bigger = async (server: OnlineServer) =>
|
||||||
server.playerData.playerCount > 15;
|
server.playerData.playerCount > 15;
|
||||||
const smaller = async (server: OnlineServer) =>
|
const smaller = async (server: OnlineServer) =>
|
||||||
@ -73,6 +128,7 @@ export default function ServerList() {
|
|||||||
const [servers, setServers] = useState<Array<OnlineServer>>([]);
|
const [servers, setServers] = useState<Array<OnlineServer>>([]);
|
||||||
const clipboard = useClipboard();
|
const clipboard = useClipboard();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [pOS, setpOS] = useState(false);
|
||||||
const [ipr, setIPR] = useState("4");
|
const [ipr, setIPR] = useState("4");
|
||||||
const [filters, setFilters] = useState<
|
const [filters, setFilters] = useState<
|
||||||
Array<(server: OnlineServer) => Promise<boolean>>
|
Array<(server: OnlineServer) => Promise<boolean>>
|
||||||
@ -80,6 +136,12 @@ export default function ServerList() {
|
|||||||
const [randomData, setRandomData] = useState<OnlineServer | undefined>(
|
const [randomData, setRandomData] = useState<OnlineServer | undefined>(
|
||||||
undefined
|
undefined
|
||||||
);
|
);
|
||||||
|
const { resolvedTheme } = useTheme();
|
||||||
|
const [color, setColor] = useState("#ffffff");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setColor(resolvedTheme === "dark" ? "#ffffff" : "#000000");
|
||||||
|
}, [resolvedTheme]);
|
||||||
|
|
||||||
useEffectOnce(() => {
|
useEffectOnce(() => {
|
||||||
setRandomText(getRandomText());
|
setRandomText(getRandomText());
|
||||||
@ -106,6 +168,11 @@ export default function ServerList() {
|
|||||||
})
|
})
|
||||||
.catch(() => setErrState(true));
|
.catch(() => setErrState(true));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const ref = useRef(null);
|
||||||
|
const [clickedPage, setClickedPage] = useState("banners");
|
||||||
|
const [hero, setHero] = useState(false);
|
||||||
|
const { isSignedIn } = useUser();
|
||||||
if (inErrState) {
|
if (inErrState) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -123,6 +190,7 @@ export default function ServerList() {
|
|||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<SignedIn>
|
||||||
<div className="md:grid md:grid-cols-3 gap-4 max-lg:grid-cols-2">
|
<div className="md:grid md:grid-cols-3 gap-4 max-lg:grid-cols-2">
|
||||||
<Skeleton className="h-[112px] rounded-xl" />
|
<Skeleton className="h-[112px] rounded-xl" />
|
||||||
<Skeleton className="h-[112px] rounded-xl" />
|
<Skeleton className="h-[112px] rounded-xl" />
|
||||||
@ -137,13 +205,203 @@ export default function ServerList() {
|
|||||||
<Skeleton className="h-[450px] rounded-xl" />
|
<Skeleton className="h-[450px] rounded-xl" />
|
||||||
<Skeleton className="h-[450px] rounded-xl" />
|
<Skeleton className="h-[450px] rounded-xl" />
|
||||||
</div>
|
</div>
|
||||||
|
</SignedIn>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div style={!pOS ? { padding } : undefined}>
|
||||||
|
<div className="p-0 branding-hero">
|
||||||
<>
|
<>
|
||||||
<ClientFadeIn>
|
{(!isSignedIn || hero) && (
|
||||||
|
<div className=" py-2 h-[300vh] relative mx-auto mt-20 max-w-7xl px-6 text-center md:px-8 ">
|
||||||
|
<Particles
|
||||||
|
className="absolute inset-0 -z-10 block"
|
||||||
|
quantity={100}
|
||||||
|
ease={70}
|
||||||
|
size={0.05}
|
||||||
|
staticity={40}
|
||||||
|
color={color}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
backgroundImage:
|
||||||
|
"radial-gradient(at 27% 37%,#3a8bfd 0,transparent 0),radial-gradient(at 97% 21%,#ff6 0,transparent 50%),radial-gradient(at 52% 99%,#6ff 0,transparent 50%),radial-gradient(at 10% 29%,#f6f 0,transparent 50%),radial-gradient(at 97% 96%,#f96 0,transparent 50%),radial-gradient(at 33% 50%,#69f 0,transparent 50%),radial-gradient(at 79% 53%,#f69 0,transparent 50%)",
|
||||||
|
opacity: 0.3,
|
||||||
|
filter: "blur(100px) saturate(150%)",
|
||||||
|
top: 80,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<SparklesText className="animate-fade-in -translate-y-4 text-balance bg-gradient-to-br from-black from-30% to-black/40 bg-clip-text pb-6 text-5xl font-semibold leading-none tracking-tighter text-transparent opacity-0 [--animation-delay:200ms] sm:text-6xl md:text-7xl lg:text-8xl dark:from-white dark:to-white/40">
|
||||||
|
<>
|
||||||
|
Meet MHSF, <br className="hidden md:block" /> the modern
|
||||||
|
server finder
|
||||||
|
</>
|
||||||
|
</SparklesText>
|
||||||
|
<p className="animate-fade-in mb-12 -translate-y-4 text-balance text-lg tracking-tight text-gray-400 opacity-0 [--animation-delay:400ms] md:text-xl">
|
||||||
|
MHSF is the next generation server list for Minehut, with
|
||||||
|
interactive filters, <br className="hidden md:block" />{" "}
|
||||||
|
intuitive keyboard shortcuts, and everything between.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<Link href="#serverlist">
|
||||||
|
<Button className="animate-fade-in -translate-y-4 gap-1 rounded-lg text-white opacity-0 ease-in-out [--animation-delay:600ms] dark:text-black">
|
||||||
|
Look and see <ArrowDownZA size={18} />
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger>
|
||||||
|
<SignedOut>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="animate-fade-in -translate-y-4 ml-2 gap-1 rounded-lg opacity-0 ease-in-out [--animation-delay:600ms]"
|
||||||
|
>
|
||||||
|
Login <LogIn size={18} />
|
||||||
|
</Button>
|
||||||
|
</SignedOut>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-full">
|
||||||
|
<SignInPopover />
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className="relative mt-[8rem] animate-fade-up opacity-0 [--animation-delay:400ms] [perspective:2000px] after:absolute after:inset-0 after:z-50 after:[background:linear-gradient(to_top,hsl(var(--background))_30%,transparent)]"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={` rounded-xl border border-white/10 bg-white bg-opacity-[0.01] before:absolute before:bottom-1/2 before:left-0 before:top-0 before:h-full before:w-full before:opacity-0 before:[filter:blur(180px)] before:[background-image:linear-gradient(to_bottom,var(--color-one),var(--color-one),transparent_40%)] before:animate-image-glow`}
|
||||||
|
>
|
||||||
|
<BorderBeam
|
||||||
|
size={200}
|
||||||
|
duration={12}
|
||||||
|
delay={11}
|
||||||
|
colorFrom="var(--color-one)"
|
||||||
|
colorTo="var(--color-two)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<img
|
||||||
|
src="/branding/full-desktop.png"
|
||||||
|
alt="Hero Image"
|
||||||
|
className="relative hidden size-full rounded-[inherit] border object-contain dark:block"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
src="/branding/full-desktop-light.png"
|
||||||
|
alt="Hero Image"
|
||||||
|
className="relative block size-full rounded-[inherit] border object-contain dark:hidden"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<span className="animate-fade-in -translate-y-4 bg-green-400/60 px-4 py-2 rounded">
|
||||||
|
For players
|
||||||
|
</span>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<h1 className="animate-fade-in -translate-y-4 text-balance bg-gradient-to-br from-black from-30% to-black/40 bg-clip-text pb-6 text-2xl font-semibold leading-none tracking-tighter text-transparent opacity-0 [--animation-delay:200ms] sm:text-2xl md:text-3xl lg:text-4xl dark:from-white dark:to-white/40">
|
||||||
|
Find what you want now, not later
|
||||||
|
</h1>
|
||||||
|
<p className="animate-fade-in mb-12 -translate-y-4 text-balance text-lg tracking-tight text-gray-400 opacity-0 [--animation-delay:400ms] md:text-xl">
|
||||||
|
Use interactive filters and customization modes to find the
|
||||||
|
server of your choice
|
||||||
|
<br className="hidden md:block" /> in less than 10 minutes.
|
||||||
|
</p>
|
||||||
|
<div className="relative flex h-[300px] w-full flex-col items-center justify-center overflow-hidden rounded-lg bg-background ">
|
||||||
|
<Marquee className="[--duration:30s]">
|
||||||
|
{serverList.currentServers.slice(0, 20).map((server) => (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"relative w-64 cursor-pointer overflow-hidden rounded-xl border no-underline " +
|
||||||
|
"border-gray-950/[.1] bg-gray-950/[.01] hover:bg-gray-950/[.05] " +
|
||||||
|
"dark:border-gray-50/[.1] dark:bg-gray-50/[.10] dark:hover:bg-gray-50/[.15]"
|
||||||
|
)}
|
||||||
|
onClick={() => router.push(`/server/${server.name}`)}
|
||||||
|
>
|
||||||
|
<div className="items-center gap-2 p-4">
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
"text-lg font-bold dark:text-white" +
|
||||||
|
(server.author == undefined ? " mt-2" : "")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<IconDisplay server={server} className="mr-2" />
|
||||||
|
{server.name}
|
||||||
|
</div>
|
||||||
|
{server.author && (
|
||||||
|
<p className="text-sm dark:text-white/40">
|
||||||
|
by {server.author}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</Marquee>
|
||||||
|
<Marquee reverse className="[--duration:30s]">
|
||||||
|
{serverList.currentServers.slice(0, 20).map((server) => (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"relative w-64 cursor-pointer overflow-hidden rounded-xl border no-underline " +
|
||||||
|
"border-gray-950/[.1] bg-gray-950/[.01] hover:bg-gray-950/[.05] " +
|
||||||
|
"dark:border-gray-50/[.1] dark:bg-gray-50/[.10] dark:hover:bg-gray-50/[.15]"
|
||||||
|
)}
|
||||||
|
onClick={() => router.push(`/server/${server.name}`)}
|
||||||
|
>
|
||||||
|
<div className="items-center gap-2 p-4">
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
"text-lg font-bold dark:text-white" +
|
||||||
|
(server.author == undefined ? " mt-2" : "")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<IconDisplay server={server} className="mr-2" />
|
||||||
|
{server.name}
|
||||||
|
</div>
|
||||||
|
{server.author && (
|
||||||
|
<p className="text-sm dark:text-white/40">
|
||||||
|
by {server.author}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</Marquee>
|
||||||
|
<div className="pointer-events-none absolute inset-y-0 left-0 w-1/3 bg-gradient-to-r from-white dark:from-background" />
|
||||||
|
|
||||||
|
<div className="pointer-events-none absolute inset-y-0 right-0 w-1/3 bg-gradient-to-l from-white dark:from-background" />
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<span className="animate-fade-in -translate-y-4 bg-yellow-400/60 px-4 py-2 rounded">
|
||||||
|
For server owners
|
||||||
|
</span>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<h1 className="animate-fade-in -translate-y-4 text-balance bg-gradient-to-br from-black from-30% to-black/40 bg-clip-text pb-6 text-2xl font-semibold leading-none tracking-tighter text-transparent opacity-0 [--animation-delay:200ms] sm:text-2xl md:text-3xl lg:text-4xl dark:from-white dark:to-white/40">
|
||||||
|
Make your server stand out
|
||||||
|
</h1>
|
||||||
|
<p className="animate-fade-in mb-12 -translate-y-4 text-balance text-lg tracking-tight text-gray-400 opacity-0 [--animation-delay:400ms] md:text-xl">
|
||||||
|
Servers can have custom banners, Discord widgets, color schemes,
|
||||||
|
and descriptions, making your server stand out with information
|
||||||
|
that can be shown to players.
|
||||||
|
</p>
|
||||||
|
<BentoGrid className="max-h-[100px]">
|
||||||
|
{features.map((feature, idx) => (
|
||||||
|
<BentoCard key={idx} {...feature} />
|
||||||
|
))}
|
||||||
|
</BentoGrid>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Separator />
|
||||||
|
<br />
|
||||||
|
</>
|
||||||
|
</div>
|
||||||
|
<div className="p-4">
|
||||||
|
<ClientFadeIn id="serverlist">
|
||||||
<div className="max-lg:grid-cols-2 grid grid-cols-3 gap-4">
|
<div className="max-lg:grid-cols-2 grid grid-cols-3 gap-4">
|
||||||
<Stat
|
<Stat
|
||||||
title="Players online"
|
title="Players online"
|
||||||
@ -220,14 +478,16 @@ export default function ServerList() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ClientFadeIn>
|
</ClientFadeIn>
|
||||||
<br />
|
<br id="serverlist" className="pb-14" />
|
||||||
<Separator />
|
<Separator />
|
||||||
<ClientFadeIn delay={100}>
|
<ClientFadeIn delay={100}>
|
||||||
<Menubar className="mt-3 ml-2 border rounded p-2">
|
<Menubar className="mt-3 ml-2 border rounded p-2">
|
||||||
<MenubarMenu>
|
<MenubarMenu>
|
||||||
<MenubarTrigger>Servers</MenubarTrigger>
|
<MenubarTrigger>Servers</MenubarTrigger>
|
||||||
<MenubarContent>
|
<MenubarContent>
|
||||||
<MenubarItem onSelect={() => events.emit("search-request-event")}>
|
<MenubarItem
|
||||||
|
onSelect={() => events.emit("search-request-event")}
|
||||||
|
>
|
||||||
Search Servers
|
Search Servers
|
||||||
<MenubarShortcut className="flex items-center ml-3">
|
<MenubarShortcut className="flex items-center ml-3">
|
||||||
<CommandIcon size={14} />
|
<CommandIcon size={14} />
|
||||||
@ -265,9 +525,11 @@ export default function ServerList() {
|
|||||||
|
|
||||||
serverList.getMOTDs(stringList).then((c) => {
|
serverList.getMOTDs(stringList).then((c) => {
|
||||||
var updatedSL = motdList;
|
var updatedSL = motdList;
|
||||||
c.forEach((b: { server: string; motd: string }) => {
|
c.forEach(
|
||||||
|
(b: { server: string; motd: string }) => {
|
||||||
updatedSL[b.server] = b.motd;
|
updatedSL[b.server] = b.motd;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
setMotdList(updatedSL);
|
setMotdList(updatedSL);
|
||||||
setServers(serverList.currentServers);
|
setServers(serverList.currentServers);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@ -326,9 +588,11 @@ export default function ServerList() {
|
|||||||
|
|
||||||
serverList.getMOTDs(stringList).then((c) => {
|
serverList.getMOTDs(stringList).then((c) => {
|
||||||
var updatedSL = motdList;
|
var updatedSL = motdList;
|
||||||
c.forEach((b: { server: string; motd: string }) => {
|
c.forEach(
|
||||||
|
(b: { server: string; motd: string }) => {
|
||||||
updatedSL[b.server] = b.motd;
|
updatedSL[b.server] = b.motd;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
setMotdList(updatedSL);
|
setMotdList(updatedSL);
|
||||||
setServers(serverList.currentServers);
|
setServers(serverList.currentServers);
|
||||||
g(undefined);
|
g(undefined);
|
||||||
@ -363,9 +627,11 @@ export default function ServerList() {
|
|||||||
|
|
||||||
serverList.getMOTDs(stringList).then((c) => {
|
serverList.getMOTDs(stringList).then((c) => {
|
||||||
var updatedSL = motdList;
|
var updatedSL = motdList;
|
||||||
c.forEach((b: { server: string; motd: string }) => {
|
c.forEach(
|
||||||
|
(b: { server: string; motd: string }) => {
|
||||||
updatedSL[b.server] = b.motd;
|
updatedSL[b.server] = b.motd;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
setMotdList(updatedSL);
|
setMotdList(updatedSL);
|
||||||
setServers(serverList.currentServers);
|
setServers(serverList.currentServers);
|
||||||
g(undefined);
|
g(undefined);
|
||||||
@ -400,9 +666,11 @@ export default function ServerList() {
|
|||||||
|
|
||||||
serverList.getMOTDs(stringList).then((c) => {
|
serverList.getMOTDs(stringList).then((c) => {
|
||||||
var updatedSL = motdList;
|
var updatedSL = motdList;
|
||||||
c.forEach((b: { server: string; motd: string }) => {
|
c.forEach(
|
||||||
|
(b: { server: string; motd: string }) => {
|
||||||
updatedSL[b.server] = b.motd;
|
updatedSL[b.server] = b.motd;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
setMotdList(updatedSL);
|
setMotdList(updatedSL);
|
||||||
setServers(serverList.currentServers);
|
setServers(serverList.currentServers);
|
||||||
g(undefined);
|
g(undefined);
|
||||||
@ -453,7 +721,9 @@ export default function ServerList() {
|
|||||||
</MenubarRadioGroup>
|
</MenubarRadioGroup>
|
||||||
<MenubarSeparator />
|
<MenubarSeparator />
|
||||||
<MenubarSub>
|
<MenubarSub>
|
||||||
<span className="text-sm text-muted-foreground ml-2">Tags</span>
|
<span className="text-sm text-muted-foreground ml-2">
|
||||||
|
Tags
|
||||||
|
</span>
|
||||||
</MenubarSub>
|
</MenubarSub>
|
||||||
{allTags.map((tag) => (
|
{allTags.map((tag) => (
|
||||||
<div key={tag.docsName}>
|
<div key={tag.docsName}>
|
||||||
@ -493,9 +763,11 @@ export default function ServerList() {
|
|||||||
|
|
||||||
serverList.getMOTDs(stringList).then((c) => {
|
serverList.getMOTDs(stringList).then((c) => {
|
||||||
var updatedSL = motdList;
|
var updatedSL = motdList;
|
||||||
c.forEach((b: { server: string; motd: string }) => {
|
c.forEach(
|
||||||
|
(b: { server: string; motd: string }) => {
|
||||||
updatedSL[b.server] = b.motd;
|
updatedSL[b.server] = b.motd;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
setMotdList(updatedSL);
|
setMotdList(updatedSL);
|
||||||
setServers(serverList.currentServers);
|
setServers(serverList.currentServers);
|
||||||
});
|
});
|
||||||
@ -536,8 +808,10 @@ export default function ServerList() {
|
|||||||
serverList.fetchDataAndFilter().then(() => {
|
serverList.fetchDataAndFilter().then(() => {
|
||||||
serverList.moveListDown();
|
serverList.moveListDown();
|
||||||
|
|
||||||
let stringList: Array<{ server: string; motd: string }> =
|
let stringList: Array<{
|
||||||
[];
|
server: string;
|
||||||
|
motd: string;
|
||||||
|
}> = [];
|
||||||
let obj: any = {};
|
let obj: any = {};
|
||||||
|
|
||||||
serverList.currentServers.forEach((b) => {
|
serverList.currentServers.forEach((b) => {
|
||||||
@ -584,6 +858,28 @@ export default function ServerList() {
|
|||||||
</MenubarRadioGroup>
|
</MenubarRadioGroup>
|
||||||
</MenubarSubContent>
|
</MenubarSubContent>
|
||||||
</MenubarSub>
|
</MenubarSub>
|
||||||
|
<MenubarSub>
|
||||||
|
<MenubarSubTrigger>Padding</MenubarSubTrigger>
|
||||||
|
<MenubarSubContent>
|
||||||
|
<MenubarRadioGroup
|
||||||
|
value={padding.toString()}
|
||||||
|
onValueChange={(v) => setPadding(Number(v))}
|
||||||
|
>
|
||||||
|
<MenubarRadioItem value="0">Default</MenubarRadioItem>
|
||||||
|
<MenubarSeparator />
|
||||||
|
<MenubarRadioItem value="15">15px</MenubarRadioItem>
|
||||||
|
<MenubarRadioItem value="30">30px</MenubarRadioItem>
|
||||||
|
<MenubarRadioItem value="40">40px</MenubarRadioItem>
|
||||||
|
<MenubarRadioItem value="60">60px</MenubarRadioItem>
|
||||||
|
<MenubarRadioItem value="100">100px</MenubarRadioItem>
|
||||||
|
<MenubarRadioItem value="200">200px</MenubarRadioItem>
|
||||||
|
</MenubarRadioGroup>
|
||||||
|
<MenubarSeparator />
|
||||||
|
<MenubarCheckboxItem checked={pOS} onCheckedChange={setpOS}>
|
||||||
|
Only use padding on servers
|
||||||
|
</MenubarCheckboxItem>
|
||||||
|
</MenubarSubContent>
|
||||||
|
</MenubarSub>
|
||||||
<MenubarSub>
|
<MenubarSub>
|
||||||
<MenubarSubTrigger>Sort</MenubarSubTrigger>
|
<MenubarSubTrigger>Sort</MenubarSubTrigger>
|
||||||
<MenubarSubContent>
|
<MenubarSubContent>
|
||||||
@ -602,6 +898,12 @@ export default function ServerList() {
|
|||||||
</MenubarRadioGroup>
|
</MenubarRadioGroup>
|
||||||
</MenubarSubContent>
|
</MenubarSubContent>
|
||||||
</MenubarSub>
|
</MenubarSub>
|
||||||
|
<SignedIn>
|
||||||
|
<MenubarSeparator />
|
||||||
|
<MenubarCheckboxItem checked={hero} onCheckedChange={setHero}>
|
||||||
|
Show Hero
|
||||||
|
</MenubarCheckboxItem>
|
||||||
|
</SignedIn>
|
||||||
</MenubarContent>
|
</MenubarContent>
|
||||||
</MenubarMenu>
|
</MenubarMenu>
|
||||||
</Menubar>
|
</Menubar>
|
||||||
@ -665,7 +967,9 @@ export default function ServerList() {
|
|||||||
className="ml-1 h-[20px]"
|
className="ml-1 h-[20px]"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setTextCopied(true);
|
setTextCopied(true);
|
||||||
clipboard.writeText(randomData.name + ".mshf.minehut.gg");
|
clipboard.writeText(
|
||||||
|
randomData.name + ".mshf.minehut.gg"
|
||||||
|
);
|
||||||
toast.success("Copied!");
|
toast.success("Copied!");
|
||||||
setTimeout(() => setTextCopied(false), 1000);
|
setTimeout(() => setTextCopied(false), 1000);
|
||||||
}}
|
}}
|
||||||
@ -709,16 +1013,30 @@ export default function ServerList() {
|
|||||||
<p
|
<p
|
||||||
style={{ textAlign: "center" }}
|
style={{ textAlign: "center" }}
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: randomText + "<br /> <strong>You've seen it all</strong>",
|
__html:
|
||||||
|
randomText + "<br /> <strong>You've seen it all</strong>",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
style={{ overflow: "hidden !important", paddingLeft: 6 }}
|
style={{
|
||||||
|
overflow: "hidden !important",
|
||||||
|
paddingLeft: pOS ? padding : 6,
|
||||||
|
paddingRight: pOS ? padding : 6,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<ClientFadeIn delay={200}>
|
<ClientFadeIn delay={200}>
|
||||||
|
{/** This looks stupid, but its the only way that works */}
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
" sm:grid " + "lg:grid-cols-" + ipr + " gap-4 sm:grid-cols-2"
|
" sm:grid " +
|
||||||
|
(ipr == "4"
|
||||||
|
? "lg:grid-cols-4"
|
||||||
|
: ipr == "5"
|
||||||
|
? "lg:grid-cols-5"
|
||||||
|
: ipr == "6"
|
||||||
|
? "lg:grid-cols-6"
|
||||||
|
: "") +
|
||||||
|
" gap-4 sm:grid-cols-2"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{servers.map((b: any) => (
|
{servers.map((b: any) => (
|
||||||
@ -729,7 +1047,8 @@ export default function ServerList() {
|
|||||||
</div>
|
</div>
|
||||||
</ClientFadeIn>
|
</ClientFadeIn>
|
||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
</>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -21,8 +21,6 @@ export default function SignInPopoverButton({
|
|||||||
| "ghost"
|
| "ghost"
|
||||||
| "link";
|
| "link";
|
||||||
}) {
|
}) {
|
||||||
const clerk = useClerk();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
@ -31,10 +29,19 @@ export default function SignInPopoverButton({
|
|||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="w-full">
|
<PopoverContent className="w-full">
|
||||||
|
<SignInPopover />
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SignInPopover() {
|
||||||
|
const clerk = useClerk();
|
||||||
|
return (
|
||||||
<div className=" grid w-[200px]">
|
<div className=" grid w-[200px]">
|
||||||
<strong className="text-center">Login</strong>
|
<strong className="text-center">Login</strong>
|
||||||
<small className="text-center pb-6">
|
<small className="text-center pb-6">
|
||||||
Make comments about servers and favorite servers. Secured by Clerk
|
Customize your own servers and favorite other servers. Secured by Clerk
|
||||||
</small>
|
</small>
|
||||||
<br />
|
<br />
|
||||||
<Button variant={"ghost"} onClick={() => clerk.openSignIn()}>
|
<Button variant={"ghost"} onClick={() => clerk.openSignIn()}>
|
||||||
@ -46,7 +53,5 @@ export default function SignInPopoverButton({
|
|||||||
Sign-up
|
Sign-up
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
79
src/components/effects/bento-grid.tsx
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import { ReactNode } from "react";
|
||||||
|
import { ArrowRightIcon } from "@radix-ui/react-icons";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
const BentoGrid = ({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
}: {
|
||||||
|
children: ReactNode;
|
||||||
|
className?: string;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"grid w-full auto-rows-[22rem] grid-cols-3 gap-4",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const BentoCard = ({
|
||||||
|
name,
|
||||||
|
className,
|
||||||
|
background,
|
||||||
|
Icon,
|
||||||
|
description,
|
||||||
|
href,
|
||||||
|
cta,
|
||||||
|
}: {
|
||||||
|
name: string;
|
||||||
|
className: string;
|
||||||
|
background: ReactNode;
|
||||||
|
Icon: any;
|
||||||
|
description: string;
|
||||||
|
href: string;
|
||||||
|
cta: string;
|
||||||
|
}) => (
|
||||||
|
<div
|
||||||
|
key={name}
|
||||||
|
className={cn(
|
||||||
|
"group relative col-span-3 flex flex-col justify-between overflow-hidden rounded-xl",
|
||||||
|
// light styles
|
||||||
|
"bg-white [box-shadow:0_0_0_1px_rgba(0,0,0,.03),0_2px_4px_rgba(0,0,0,.05),0_12px_24px_rgba(0,0,0,.05)]",
|
||||||
|
// dark styles
|
||||||
|
"transform-gpu dark:bg-black dark:[border:1px_solid_rgba(255,255,255,.1)] dark:[box-shadow:0_-20px_80px_-20px_#ffffff1f_inset]",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div>{background}</div>
|
||||||
|
<div className="pointer-events-none z-10 flex transform-gpu flex-col gap-1 p-6 transition-all duration-300 group-hover:-translate-y-10">
|
||||||
|
<Icon className="h-12 w-12 origin-left transform-gpu text-neutral-700 transition-all duration-300 ease-in-out group-hover:scale-75" />
|
||||||
|
<h3 className="text-xl font-semibold text-neutral-700 dark:text-neutral-300">
|
||||||
|
{name}
|
||||||
|
</h3>
|
||||||
|
<p className="max-w-lg text-neutral-400">{description}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"pointer-events-none absolute bottom-0 flex w-full translate-y-10 transform-gpu flex-row items-center p-4 opacity-0 transition-all duration-300 group-hover:translate-y-0 group-hover:opacity-100"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Button variant="ghost" asChild size="sm" className="pointer-events-auto">
|
||||||
|
<a href={href}>
|
||||||
|
{cta}
|
||||||
|
<ArrowRightIcon className="ml-2 h-4 w-4" />
|
||||||
|
</a>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="pointer-events-none absolute inset-0 transform-gpu transition-all duration-300 group-hover:bg-black/[.03] group-hover:dark:bg-neutral-800/10" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export { BentoCard, BentoGrid };
|
||||||
51
src/components/effects/marquee.tsx
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
interface MarqueeProps {
|
||||||
|
className?: string;
|
||||||
|
reverse?: boolean;
|
||||||
|
pauseOnHover?: boolean;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
vertical?: boolean;
|
||||||
|
repeat?: number;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Marquee({
|
||||||
|
className,
|
||||||
|
reverse,
|
||||||
|
pauseOnHover = false,
|
||||||
|
children,
|
||||||
|
vertical = false,
|
||||||
|
repeat = 4,
|
||||||
|
...props
|
||||||
|
}: MarqueeProps) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
{...props}
|
||||||
|
className={cn(
|
||||||
|
"group flex overflow-hidden p-2 [--duration:40s] [--gap:1rem] [gap:var(--gap)]",
|
||||||
|
{
|
||||||
|
"flex-row": !vertical,
|
||||||
|
"flex-col": vertical,
|
||||||
|
},
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{Array(repeat)
|
||||||
|
.fill(0)
|
||||||
|
.map((_, i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className={cn("flex shrink-0 justify-around [gap:var(--gap)]", {
|
||||||
|
"animate-marquee flex-row": !vertical,
|
||||||
|
"animate-marquee-vertical flex-col": vertical,
|
||||||
|
"group-hover:[animation-play-state:paused]": pauseOnHover,
|
||||||
|
"[animation-direction:reverse]": reverse,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
278
src/components/effects/particles.tsx
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
|
interface MousePosition {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function MousePosition(): MousePosition {
|
||||||
|
const [mousePosition, setMousePosition] = useState<MousePosition>({
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleMouseMove = (event: MouseEvent) => {
|
||||||
|
setMousePosition({ x: event.clientX, y: event.clientY });
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("mousemove", handleMouseMove);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("mousemove", handleMouseMove);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return mousePosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ParticlesProps {
|
||||||
|
className?: string;
|
||||||
|
quantity?: number;
|
||||||
|
staticity?: number;
|
||||||
|
ease?: number;
|
||||||
|
size?: number;
|
||||||
|
refresh?: boolean;
|
||||||
|
color?: string;
|
||||||
|
vx?: number;
|
||||||
|
vy?: number;
|
||||||
|
}
|
||||||
|
function hexToRgb(hex: string): number[] {
|
||||||
|
hex = hex.replace("#", "");
|
||||||
|
|
||||||
|
if (hex.length === 3) {
|
||||||
|
hex = hex
|
||||||
|
.split("")
|
||||||
|
.map((char) => char + char)
|
||||||
|
.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
const hexInt = parseInt(hex, 16);
|
||||||
|
const red = (hexInt >> 16) & 255;
|
||||||
|
const green = (hexInt >> 8) & 255;
|
||||||
|
const blue = hexInt & 255;
|
||||||
|
return [red, green, blue];
|
||||||
|
}
|
||||||
|
|
||||||
|
const Particles: React.FC<ParticlesProps> = ({
|
||||||
|
className = "",
|
||||||
|
quantity = 100,
|
||||||
|
staticity = 50,
|
||||||
|
ease = 50,
|
||||||
|
size = 0.4,
|
||||||
|
refresh = false,
|
||||||
|
color = "#ffffff",
|
||||||
|
vx = 0,
|
||||||
|
vy = 0,
|
||||||
|
}) => {
|
||||||
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
|
const canvasContainerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const context = useRef<CanvasRenderingContext2D | null>(null);
|
||||||
|
const circles = useRef<any[]>([]);
|
||||||
|
const mousePosition = MousePosition();
|
||||||
|
const mouse = useRef<{ x: number; y: number }>({ x: 0, y: 0 });
|
||||||
|
const canvasSize = useRef<{ w: number; h: number }>({ w: 0, h: 0 });
|
||||||
|
const dpr = typeof window !== "undefined" ? window.devicePixelRatio : 1;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (canvasRef.current) {
|
||||||
|
context.current = canvasRef.current.getContext("2d");
|
||||||
|
}
|
||||||
|
initCanvas();
|
||||||
|
animate();
|
||||||
|
window.addEventListener("resize", initCanvas);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("resize", initCanvas);
|
||||||
|
};
|
||||||
|
}, [color]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onMouseMove();
|
||||||
|
}, [mousePosition.x, mousePosition.y]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
initCanvas();
|
||||||
|
}, [refresh]);
|
||||||
|
|
||||||
|
const initCanvas = () => {
|
||||||
|
resizeCanvas();
|
||||||
|
drawParticles();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseMove = () => {
|
||||||
|
if (canvasRef.current) {
|
||||||
|
const rect = canvasRef.current.getBoundingClientRect();
|
||||||
|
const { w, h } = canvasSize.current;
|
||||||
|
const x = mousePosition.x - rect.left - w / 2;
|
||||||
|
const y = mousePosition.y - rect.top - h / 2;
|
||||||
|
const inside = x < w / 2 && x > -w / 2 && y < h / 2 && y > -h / 2;
|
||||||
|
if (inside) {
|
||||||
|
mouse.current.x = x;
|
||||||
|
mouse.current.y = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
type Circle = {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
translateX: number;
|
||||||
|
translateY: number;
|
||||||
|
size: number;
|
||||||
|
alpha: number;
|
||||||
|
targetAlpha: number;
|
||||||
|
dx: number;
|
||||||
|
dy: number;
|
||||||
|
magnetism: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const resizeCanvas = () => {
|
||||||
|
if (canvasContainerRef.current && canvasRef.current && context.current) {
|
||||||
|
circles.current.length = 0;
|
||||||
|
canvasSize.current.w = canvasContainerRef.current.offsetWidth;
|
||||||
|
canvasSize.current.h = canvasContainerRef.current.offsetHeight;
|
||||||
|
canvasRef.current.width = canvasSize.current.w * dpr;
|
||||||
|
canvasRef.current.height = canvasSize.current.h * dpr;
|
||||||
|
canvasRef.current.style.width = `${canvasSize.current.w}px`;
|
||||||
|
canvasRef.current.style.height = `${canvasSize.current.h}px`;
|
||||||
|
context.current.scale(dpr, dpr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const circleParams = (): Circle => {
|
||||||
|
const x = Math.floor(Math.random() * canvasSize.current.w);
|
||||||
|
const y = Math.floor(Math.random() * canvasSize.current.h);
|
||||||
|
const translateX = 0;
|
||||||
|
const translateY = 0;
|
||||||
|
const pSize = Math.floor(Math.random() * 2) + size;
|
||||||
|
const alpha = 0;
|
||||||
|
const targetAlpha = parseFloat((Math.random() * 0.6 + 0.1).toFixed(1));
|
||||||
|
const dx = (Math.random() - 0.5) * 0.1;
|
||||||
|
const dy = (Math.random() - 0.5) * 0.1;
|
||||||
|
const magnetism = 0.1 + Math.random() * 4;
|
||||||
|
return {
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
translateX,
|
||||||
|
translateY,
|
||||||
|
size: pSize,
|
||||||
|
alpha,
|
||||||
|
targetAlpha,
|
||||||
|
dx,
|
||||||
|
dy,
|
||||||
|
magnetism,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const rgb = hexToRgb(color);
|
||||||
|
|
||||||
|
const drawCircle = (circle: Circle, update = false) => {
|
||||||
|
if (context.current) {
|
||||||
|
const { x, y, translateX, translateY, size, alpha } = circle;
|
||||||
|
context.current.translate(translateX, translateY);
|
||||||
|
context.current.beginPath();
|
||||||
|
context.current.arc(x, y, size, 0, 2 * Math.PI);
|
||||||
|
context.current.fillStyle = `rgba(${rgb.join(", ")}, ${alpha})`;
|
||||||
|
context.current.fill();
|
||||||
|
context.current.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||||
|
|
||||||
|
if (!update) {
|
||||||
|
circles.current.push(circle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearContext = () => {
|
||||||
|
if (context.current) {
|
||||||
|
context.current.clearRect(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
canvasSize.current.w,
|
||||||
|
canvasSize.current.h,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const drawParticles = () => {
|
||||||
|
clearContext();
|
||||||
|
const particleCount = quantity;
|
||||||
|
for (let i = 0; i < particleCount; i++) {
|
||||||
|
const circle = circleParams();
|
||||||
|
drawCircle(circle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const remapValue = (
|
||||||
|
value: number,
|
||||||
|
start1: number,
|
||||||
|
end1: number,
|
||||||
|
start2: number,
|
||||||
|
end2: number,
|
||||||
|
): number => {
|
||||||
|
const remapped =
|
||||||
|
((value - start1) * (end2 - start2)) / (end1 - start1) + start2;
|
||||||
|
return remapped > 0 ? remapped : 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const animate = () => {
|
||||||
|
clearContext();
|
||||||
|
circles.current.forEach((circle: Circle, i: number) => {
|
||||||
|
// Handle the alpha value
|
||||||
|
const edge = [
|
||||||
|
circle.x + circle.translateX - circle.size, // distance from left edge
|
||||||
|
canvasSize.current.w - circle.x - circle.translateX - circle.size, // distance from right edge
|
||||||
|
circle.y + circle.translateY - circle.size, // distance from top edge
|
||||||
|
canvasSize.current.h - circle.y - circle.translateY - circle.size, // distance from bottom edge
|
||||||
|
];
|
||||||
|
const closestEdge = edge.reduce((a, b) => Math.min(a, b));
|
||||||
|
const remapClosestEdge = parseFloat(
|
||||||
|
remapValue(closestEdge, 0, 20, 0, 1).toFixed(2),
|
||||||
|
);
|
||||||
|
if (remapClosestEdge > 1) {
|
||||||
|
circle.alpha += 0.02;
|
||||||
|
if (circle.alpha > circle.targetAlpha) {
|
||||||
|
circle.alpha = circle.targetAlpha;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
circle.alpha = circle.targetAlpha * remapClosestEdge;
|
||||||
|
}
|
||||||
|
circle.x += circle.dx + vx;
|
||||||
|
circle.y += circle.dy + vy;
|
||||||
|
circle.translateX +=
|
||||||
|
(mouse.current.x / (staticity / circle.magnetism) - circle.translateX) /
|
||||||
|
ease;
|
||||||
|
circle.translateY +=
|
||||||
|
(mouse.current.y / (staticity / circle.magnetism) - circle.translateY) /
|
||||||
|
ease;
|
||||||
|
|
||||||
|
drawCircle(circle, true);
|
||||||
|
|
||||||
|
// circle gets out of the canvas
|
||||||
|
if (
|
||||||
|
circle.x < -circle.size ||
|
||||||
|
circle.x > canvasSize.current.w + circle.size ||
|
||||||
|
circle.y < -circle.size ||
|
||||||
|
circle.y > canvasSize.current.h + circle.size
|
||||||
|
) {
|
||||||
|
// remove the circle from the array
|
||||||
|
circles.current.splice(i, 1);
|
||||||
|
// create a new circle
|
||||||
|
const newCircle = circleParams();
|
||||||
|
drawCircle(newCircle);
|
||||||
|
// update the circle position
|
||||||
|
}
|
||||||
|
});
|
||||||
|
window.requestAnimationFrame(animate);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={className} ref={canvasContainerRef} aria-hidden="true">
|
||||||
|
<canvas ref={canvasRef} className="size-full" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Particles;
|
||||||
152
src/components/effects/sparkles-text.tsx
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { CSSProperties, ReactElement, useEffect, useState } from "react";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
interface Sparkle {
|
||||||
|
id: string;
|
||||||
|
x: string;
|
||||||
|
y: string;
|
||||||
|
color: string;
|
||||||
|
delay: number;
|
||||||
|
scale: number;
|
||||||
|
lifespan: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SparklesTextProps {
|
||||||
|
/**
|
||||||
|
* @default <div />
|
||||||
|
* @type ReactElement
|
||||||
|
* @description
|
||||||
|
* The component to be rendered as the text
|
||||||
|
* */
|
||||||
|
as?: ReactElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @default ""
|
||||||
|
* @type string
|
||||||
|
* @description
|
||||||
|
* The className of the text
|
||||||
|
*/
|
||||||
|
className?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @required
|
||||||
|
* @type string
|
||||||
|
* @description
|
||||||
|
* The text to be displayed
|
||||||
|
* */
|
||||||
|
children: JSX.Element;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @default 10
|
||||||
|
* @type number
|
||||||
|
* @description
|
||||||
|
* The count of sparkles
|
||||||
|
* */
|
||||||
|
sparklesCount?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @default "{first: '#9E7AFF', second: '#FE8BBB'}"
|
||||||
|
* @type string
|
||||||
|
* @description
|
||||||
|
* The colors of the sparkles
|
||||||
|
* */
|
||||||
|
colors?: {
|
||||||
|
first: string;
|
||||||
|
second: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const SparklesText: React.FC<SparklesTextProps> = ({
|
||||||
|
children,
|
||||||
|
colors = { first: "#9E7AFF", second: "#FE8BBB" },
|
||||||
|
className,
|
||||||
|
sparklesCount = 10,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
|
const [sparkles, setSparkles] = useState<Sparkle[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const generateStar = (): Sparkle => {
|
||||||
|
const starX = `${Math.random() * 100}%`;
|
||||||
|
const starY = `${Math.random() * 100}%`;
|
||||||
|
const color = Math.random() > 0.5 ? colors.first : colors.second;
|
||||||
|
const delay = Math.random() * 2;
|
||||||
|
const scale = Math.random() * 1 + 0.3;
|
||||||
|
const lifespan = Math.random() * 10 + 5;
|
||||||
|
const id = `${starX}-${starY}-${Date.now()}`;
|
||||||
|
return { id, x: starX, y: starY, color, delay, scale, lifespan };
|
||||||
|
};
|
||||||
|
|
||||||
|
const initializeStars = () => {
|
||||||
|
const newSparkles = Array.from({ length: sparklesCount }, generateStar);
|
||||||
|
setSparkles(newSparkles);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateStars = () => {
|
||||||
|
setSparkles((currentSparkles) =>
|
||||||
|
currentSparkles.map((star) => {
|
||||||
|
if (star.lifespan <= 0) {
|
||||||
|
return generateStar();
|
||||||
|
} else {
|
||||||
|
return { ...star, lifespan: star.lifespan - 0.1 };
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
initializeStars();
|
||||||
|
const interval = setInterval(updateStars, 100);
|
||||||
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [colors.first, colors.second]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn("text-6xl font-bold", className)}
|
||||||
|
{...props}
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"--sparkles-first-color": `${colors.first}`,
|
||||||
|
"--sparkles-second-color": `${colors.second}`,
|
||||||
|
} as CSSProperties
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{sparkles.map((sparkle) => (
|
||||||
|
<Sparkle key={sparkle.id} {...sparkle} />
|
||||||
|
))}
|
||||||
|
<span>{children}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Sparkle: React.FC<Sparkle> = ({ id, x, y, color, delay, scale }) => {
|
||||||
|
return (
|
||||||
|
<motion.svg
|
||||||
|
key={id}
|
||||||
|
className="pointer-events-none absolute z-20"
|
||||||
|
initial={{ opacity: 0, left: x, top: y }}
|
||||||
|
animate={{
|
||||||
|
opacity: [0, 1, 0],
|
||||||
|
scale: [0, scale, 0],
|
||||||
|
rotate: [75, 120, 150],
|
||||||
|
}}
|
||||||
|
transition={{ duration: 0.8, repeat: Infinity, delay }}
|
||||||
|
width="21"
|
||||||
|
height="21"
|
||||||
|
viewBox="0 0 21 21"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M9.82531 0.843845C10.0553 0.215178 10.9446 0.215178 11.1746 0.843845L11.8618 2.72026C12.4006 4.19229 12.3916 6.39157 13.5 7.5C14.6084 8.60843 16.8077 8.59935 18.2797 9.13822L20.1561 9.82534C20.7858 10.0553 20.7858 10.9447 20.1561 11.1747L18.2797 11.8618C16.8077 12.4007 14.6084 12.3916 13.5 13.5C12.3916 14.6084 12.4006 16.8077 11.8618 18.2798L11.1746 20.1562C10.9446 20.7858 10.0553 20.7858 9.82531 20.1562L9.13819 18.2798C8.59932 16.8077 8.60843 14.6084 7.5 13.5C6.39157 12.3916 4.19225 12.4007 2.72023 11.8618L0.843814 11.1747C0.215148 10.9447 0.215148 10.0553 0.843814 9.82534L2.72023 9.13822C4.19225 8.59935 6.39157 8.60843 7.5 7.5C8.60843 6.39157 8.59932 4.19229 9.13819 2.72026L9.82531 0.843845Z"
|
||||||
|
fill={color}
|
||||||
|
/>
|
||||||
|
</motion.svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SparklesText;
|
||||||
@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority";
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const buttonVariants = cva(
|
const buttonVariants = cva(
|
||||||
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
"inline-flex items-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
@ -39,14 +39,22 @@ export interface ButtonProps
|
|||||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||||
VariantProps<typeof buttonVariants> {
|
VariantProps<typeof buttonVariants> {
|
||||||
asChild?: boolean;
|
asChild?: boolean;
|
||||||
|
noJustify?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
(
|
||||||
|
{ className, variant, size, asChild = false, noJustify = false, ...props },
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
const Comp = asChild ? Slot : "button";
|
const Comp = asChild ? Slot : "button";
|
||||||
return (
|
return (
|
||||||
<Comp
|
<Comp
|
||||||
className={cn(buttonVariants({ variant, size, className }))}
|
className={cn(
|
||||||
|
buttonVariants({ variant, size, className }) +
|
||||||
|
" " +
|
||||||
|
(noJustify ? "" : "justify-center")
|
||||||
|
)}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -2,16 +2,15 @@ import Image from "next/image";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Separator } from "./components/ui/separator";
|
import { Separator } from "./components/ui/separator";
|
||||||
import { Button } from "./components/ui/button";
|
import { Button } from "./components/ui/button";
|
||||||
import Confetti, { ConfettiButton } from "./components/effects/confetti";
|
import confetti from "canvas-confetti";
|
||||||
|
|
||||||
export const version = "1.0";
|
export const version = "1.1.0";
|
||||||
|
|
||||||
const User = ({ user }: { user: string }) => (
|
const User = ({ user }: { user: string }) => (
|
||||||
<span className="cursor-pointer bg-[rgba(255,165,0,0.25);] rounded p-[2.5px]">
|
<span className="cursor-pointer bg-[rgba(255,165,0,0.25);] rounded p-[2.5px]">
|
||||||
{user}
|
{user}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
import confetti from "canvas-confetti";
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
const duration = 5 * 1000;
|
const duration = 5 * 1000;
|
||||||
const animationEnd = Date.now() + duration;
|
const animationEnd = Date.now() + duration;
|
||||||
@ -78,6 +77,17 @@ export const Changelog = () => (
|
|||||||
</code>
|
</code>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
|
<div>
|
||||||
|
<strong className="flex items-center">
|
||||||
|
Version 1.1.0 (August 24rd 2024)
|
||||||
|
</strong>
|
||||||
|
<ul>
|
||||||
|
<li>• Brand new hero page</li>
|
||||||
|
<li>• New padding option on server list</li>
|
||||||
|
<li>• New help guide</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
<div>
|
<div>
|
||||||
<strong className="flex items-center">
|
<strong className="flex items-center">
|
||||||
Version 1.0.0 (August 22nd 2024)
|
Version 1.0.0 (August 22nd 2024)
|
||||||
|
|||||||
@ -59,6 +59,19 @@ const config = {
|
|||||||
sm: "calc(var(--radius) - 4px)",
|
sm: "calc(var(--radius) - 4px)",
|
||||||
},
|
},
|
||||||
keyframes: {
|
keyframes: {
|
||||||
|
'image-glow': {
|
||||||
|
'0%': {
|
||||||
|
'opacity': '0',
|
||||||
|
'animation-timing-function': 'cubic-bezier(0.74, 0.25, 0.76, 1)',
|
||||||
|
},
|
||||||
|
'10%': {
|
||||||
|
'opacity': '0.7',
|
||||||
|
'animation-timing-function': 'cubic-bezier(0.12, 0.01, 0.08, 0.99)',
|
||||||
|
},
|
||||||
|
'100%': {
|
||||||
|
opacity: '0.4',
|
||||||
|
},
|
||||||
|
},
|
||||||
"border-beam": {
|
"border-beam": {
|
||||||
"100%": {
|
"100%": {
|
||||||
"offset-distance": "100%",
|
"offset-distance": "100%",
|
||||||
@ -77,12 +90,45 @@ const config = {
|
|||||||
from: { height: "var(--radix-accordion-content-height)" },
|
from: { height: "var(--radix-accordion-content-height)" },
|
||||||
to: { height: "0" },
|
to: { height: "0" },
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"fade-in": {
|
||||||
|
from: { opacity: "0", transform: "translateY(-10px)" },
|
||||||
|
to: { opacity: "1", transform: "none" },
|
||||||
|
},
|
||||||
|
marquee: {
|
||||||
|
from: { transform: "translateX(0)" },
|
||||||
|
to: { transform: "translateX(calc(-100% - var(--gap)))" },
|
||||||
|
},
|
||||||
|
"marquee-vertical": {
|
||||||
|
from: { transform: "translateY(0)" },
|
||||||
|
to: { transform: "translateY(calc(-100% - var(--gap)))" },
|
||||||
|
},
|
||||||
|
"fade-up": {
|
||||||
|
from: { opacity: "0", transform: "translateY(20px)" },
|
||||||
|
to: { opacity: "1", transform: "none" },
|
||||||
|
},
|
||||||
|
|
||||||
|
'shimmer': {
|
||||||
|
'0%, 90%, 100%': {
|
||||||
|
'background-position': 'calc(-100% - var(--shimmer-width)) 0',
|
||||||
|
},
|
||||||
|
'30%, 60%': {
|
||||||
|
'background-position': 'calc(100% + var(--shimmer-width)) 0',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
animation: {
|
animation: {
|
||||||
|
marquee: "marquee var(--duration) linear infinite",
|
||||||
|
"marquee-vertical": "marquee-vertical var(--duration) linear infinite",
|
||||||
"accordion-down": "accordion-down 0.2s ease-out",
|
"accordion-down": "accordion-down 0.2s ease-out",
|
||||||
"accordion-up": "accordion-up 0.2s ease-out",
|
"accordion-up": "accordion-up 0.2s ease-out",
|
||||||
|
'image-glow': 'image-glow 4100ms 600ms ease-out forwards',
|
||||||
|
'shimmer': 'shimmer 8s infinite',
|
||||||
|
"fade-in": "fade-in 1000ms var(--animation-delay, 0ms) ease forwards",
|
||||||
"caret-blink": "caret-blink 1.25s ease-out infinite",
|
"caret-blink": "caret-blink 1.25s ease-out infinite",
|
||||||
"border-beam": "border-beam calc(var(--duration)*1s) infinite linear",
|
"border-beam": "border-beam calc(var(--duration)*1s) infinite linear",
|
||||||
|
|
||||||
|
"fade-up": "fade-up 1000ms var(--animation-delay, 0ms) ease forwards",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||