Compare commits
2 Commits
main
...
developmen
| Author | SHA1 | Date | |
|---|---|---|---|
| 4f63c20aa6 | |||
| 74dd974da5 |
@@ -1 +0,0 @@
|
|||||||
google-site-verification: google5dd3a8b700455c0e.html
|
|
||||||
@@ -2,13 +2,10 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/png" href="/favicon-32.png" sizes="32x32" />
|
|
||||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="google-site-verification" content="rRrZkMEhdC4yFe_BrENEzYmy2bRfD-VE6RTRiDJNLkg" />
|
<title>Project Mycelium - Unleash the Power of Decentralized Networks</title>
|
||||||
<title>Project Mycelium - Built for Digital Sovereignty</title>
|
<meta name="description" content="Project Mycelium's technology enables anyone to deploy their own Internet infrastructure, anywhere." />
|
||||||
<meta name="description" content="Discover Project Mycelium. A sovereign peer-to-peer network for private communication, storage, and compute. Build and run your digital environment on infrastructure you control." />
|
|
||||||
<meta name="keywords" content="Project Mycelium, Mycelium, digital sovereignty, decentralized network, peer-to-peer infrastructure, private storage, secure compute, sovereign cloud, edge cloud" />
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Mulish:wght@400;500;700&display=swap" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/css2?family=Mulish:wght@400;500;700&display=swap" rel="stylesheet" />
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 618 KiB |
|
Before Width: | Height: | Size: 508 KiB After Width: | Height: | Size: 510 KiB |
BIN
public/images/audience/1.jpg
Normal file
|
After Width: | Height: | Size: 306 KiB |
BIN
public/images/audience/2.jpg
Normal file
|
After Width: | Height: | Size: 205 KiB |
BIN
public/images/audience/3.jpg
Normal file
|
After Width: | Height: | Size: 192 KiB |
BIN
public/images/audience/4.jpg
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
public/images/audience/5.jpg
Normal file
|
After Width: | Height: | Size: 128 KiB |
BIN
public/images/audience/6.jpg
Normal file
|
After Width: | Height: | Size: 234 KiB |
BIN
public/images/audience/7.jpg
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
public/images/audience/8.jpg
Normal file
|
After Width: | Height: | Size: 228 KiB |
BIN
public/images/audiences/1.jpg
Normal file
|
After Width: | Height: | Size: 229 KiB |
BIN
public/images/audiences/2.jpg
Normal file
|
After Width: | Height: | Size: 164 KiB |
BIN
public/images/audiences/3.jpg
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
public/images/audiences/4.jpg
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
public/images/audiences/5.jpg
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
public/images/audiences/6.jpg
Normal file
|
After Width: | Height: | Size: 184 KiB |
BIN
public/images/audiences/7.jpg
Normal file
|
After Width: | Height: | Size: 124 KiB |
BIN
public/images/audiences/8.jpg
Normal file
|
After Width: | Height: | Size: 182 KiB |
|
Before Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 792 KiB After Width: | Height: | Size: 796 KiB |
|
Before Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" zoomAndPan="magnify" viewBox="0 0 75 74.999997" height="100" preserveAspectRatio="xMidYMid meet" version="1.0"><defs><clipPath id="17cc7c511f"><path d="M 0.679688 0.28125 L 48.441406 0.28125 L 48.441406 5 L 0.679688 5 Z M 0.679688 0.28125 " clip-rule="nonzero"/></clipPath><clipPath id="871aafab52"><path d="M 0.679688 36 L 48.441406 36 L 48.441406 40.601562 L 0.679688 40.601562 Z M 0.679688 36 " clip-rule="nonzero"/></clipPath><clipPath id="08bb39558f"><rect x="0" width="49" y="0" height="41"/></clipPath></defs><g transform="matrix(1, 0, 0, 1, 13, 17)"><g clip-path="url(#08bb39558f)"><g clip-path="url(#17cc7c511f)"><path stroke-linecap="butt" transform="matrix(0.559221, 0, 0, 0.559221, 3.099571, 0.434943)" fill="none" stroke-linejoin="miter" d="M -0.00342307 4.000082 L 76.539934 4.000082 " stroke="#22d3ee" stroke-width="8" stroke-opacity="1" stroke-miterlimit="4"/></g><path fill="#22d3ee" d="M 3.097656 18.003906 L 29.941406 18.003906 L 29.941406 22.480469 L 3.097656 22.480469 M 34.414062 18.003906 L 45.902344 18.003906 L 45.902344 22.480469 L 34.414062 22.480469 " fill-opacity="1" fill-rule="nonzero"/><g clip-path="url(#871aafab52)"><path stroke-linecap="butt" transform="matrix(0.559221, 0, 0, 0.559221, 3.099571, 36.091279)" fill="none" stroke-linejoin="miter" d="M -0.00342307 3.999929 L 76.539934 3.999929 " stroke="#22d3ee" stroke-width="8" stroke-opacity="1" stroke-miterlimit="4"/></g></g></g></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" zoomAndPan="magnify" viewBox="0 0 75 74.999997" height="100" preserveAspectRatio="xMidYMid meet" version="1.0"><defs><clipPath id="17cc7c511f"><path d="M 0.679688 0.28125 L 48.441406 0.28125 L 48.441406 5 L 0.679688 5 Z M 0.679688 0.28125 " clip-rule="nonzero"/></clipPath><clipPath id="871aafab52"><path d="M 0.679688 36 L 48.441406 36 L 48.441406 40.601562 L 0.679688 40.601562 Z M 0.679688 36 " clip-rule="nonzero"/></clipPath><clipPath id="08bb39558f"><rect x="0" width="49" y="0" height="41"/></clipPath></defs><g transform="matrix(1, 0, 0, 1, 13, 17)"><g clip-path="url(#08bb39558f)"><g clip-path="url(#17cc7c511f)"><path stroke-linecap="butt" transform="matrix(0.559221, 0, 0, 0.559221, 3.099571, 0.434943)" fill="none" stroke-linejoin="miter" d="M -0.00342307 4.000082 L 76.539934 4.000082 " stroke="#22d3ee" stroke-width="8" stroke-opacity="1" stroke-miterlimit="4"/></g><path fill="#22d3ee" d="M 3.097656 18.003906 L 29.941406 18.003906 L 29.941406 22.480469 L 3.097656 22.480469 M 34.414062 18.003906 L 45.902344 18.003906 L 45.902344 22.480469 L 34.414062 22.480469 " fill-opacity="1" fill-rule="nonzero"/><g clip-path="url(#871aafab52)"><path stroke-linecap="butt" transform="matrix(0.559221, 0, 0, 0.559221, 3.099571, 36.091279)" fill="none" stroke-linejoin="miter" d="M -0.00342307 3.999929 L 76.539934 3.999929 " stroke="#22d3ee" stroke-width="8" stroke-opacity="1" stroke-miterlimit="4"/></g></g></g></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 52 KiB |
@@ -1,6 +0,0 @@
|
|||||||
User-agent: *
|
|
||||||
Allow: /
|
|
||||||
|
|
||||||
Sitemap: https://www.projectmycelium.com/sitemap.xml
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
||||||
<url>
|
|
||||||
<loc>https://www.projectmycelium.com/</loc>
|
|
||||||
<changefreq>weekly</changefreq>
|
|
||||||
<priority>1.0</priority>
|
|
||||||
</url>
|
|
||||||
<url>
|
|
||||||
<loc>https://www.projectmycelium.com/cloud</loc>
|
|
||||||
<changefreq>weekly</changefreq>
|
|
||||||
<priority>0.9</priority>
|
|
||||||
</url>
|
|
||||||
<url>
|
|
||||||
<loc>https://www.projectmycelium.com/network</loc>
|
|
||||||
<changefreq>weekly</changefreq>
|
|
||||||
<priority>0.9</priority>
|
|
||||||
</url>
|
|
||||||
<url>
|
|
||||||
<loc>https://www.projectmycelium.com/agents</loc>
|
|
||||||
<changefreq>weekly</changefreq>
|
|
||||||
<priority>0.8</priority>
|
|
||||||
</url>
|
|
||||||
<url>
|
|
||||||
<loc>https://www.projectmycelium.com/download</loc>
|
|
||||||
<changefreq>monthly</changefreq>
|
|
||||||
<priority>0.7</priority>
|
|
||||||
</url>
|
|
||||||
<url>
|
|
||||||
<loc>https://www.projectmycelium.com/compute</loc>
|
|
||||||
<changefreq>weekly</changefreq>
|
|
||||||
<priority>0.8</priority>
|
|
||||||
</url>
|
|
||||||
<url>
|
|
||||||
<loc>https://www.projectmycelium.com/storage</loc>
|
|
||||||
<changefreq>weekly</changefreq>
|
|
||||||
<priority>0.8</priority>
|
|
||||||
</url>
|
|
||||||
<url>
|
|
||||||
<loc>https://www.projectmycelium.com/gpu</loc>
|
|
||||||
<changefreq>weekly</changefreq>
|
|
||||||
<priority>0.8</priority>
|
|
||||||
</url>
|
|
||||||
<url>
|
|
||||||
<loc>https://www.projectmycelium.com/pods</loc>
|
|
||||||
<changefreq>weekly</changefreq>
|
|
||||||
<priority>0.7</priority>
|
|
||||||
</url>
|
|
||||||
<url>
|
|
||||||
<loc>https://www.projectmycelium.com/nodes</loc>
|
|
||||||
<changefreq>weekly</changefreq>
|
|
||||||
<priority>0.7</priority>
|
|
||||||
</url>
|
|
||||||
<url>
|
|
||||||
<loc>https://www.projectmycelium.com/mediakit</loc>
|
|
||||||
<changefreq>monthly</changefreq>
|
|
||||||
<priority>0.7</priority>
|
|
||||||
</url>
|
|
||||||
</urlset>
|
|
||||||
|
|
||||||
BIN
public/videos/agent.mp4
Normal file
BIN
public/videos/benefits.jpg
Normal file
|
After Width: | Height: | Size: 156 KiB |
BIN
public/videos/benefits.mp4
Normal file
BIN
public/videos/chip_vid.mp4
Normal file
BIN
public/videos/cloud.mp4
Normal file
BIN
public/videos/cta.mp4
Normal file
BIN
public/videos/deterministic.mp4
Normal file
BIN
public/videos/fungistor.mp4
Normal file
BIN
public/videos/herodb.mp4
Normal file
BIN
public/videos/mesh.mp4
Normal file
BIN
public/videos/mhero.mp4
Normal file
BIN
public/videos/mycelium2.mp4
Normal file
BIN
public/videos/sandbox.mp4
Normal file
BIN
public/videos/universal.mp4
Normal file
@@ -12,7 +12,6 @@ const StoragePage = lazy(() => import('./pages/storage/StoragePage'));
|
|||||||
const GpuPage = lazy(() => import('./pages/gpu/GpuPage'));
|
const GpuPage = lazy(() => import('./pages/gpu/GpuPage'));
|
||||||
const PodsPage = lazy(() => import('./pages/pods/PodsPage'));
|
const PodsPage = lazy(() => import('./pages/pods/PodsPage'));
|
||||||
const NodesPage = lazy(() => import('./pages/nodes/NodesPage'));
|
const NodesPage = lazy(() => import('./pages/nodes/NodesPage'));
|
||||||
const MediaPage = lazy(() => import('./pages/mediakit/MediaPage'));
|
|
||||||
|
|
||||||
function ScrollToTop() {
|
function ScrollToTop() {
|
||||||
const { pathname, hash } = useLocation();
|
const { pathname, hash } = useLocation();
|
||||||
@@ -50,7 +49,6 @@ function App() {
|
|||||||
<Route path="gpu" element={<GpuPage />} />
|
<Route path="gpu" element={<GpuPage />} />
|
||||||
<Route path="pods" element={<PodsPage />} />
|
<Route path="pods" element={<PodsPage />} />
|
||||||
<Route path="nodes" element={<NodesPage />} />
|
<Route path="nodes" element={<NodesPage />} />
|
||||||
<Route path="mediakit" element={<MediaPage />} />
|
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
Copyright 2016 The Mulish Project Authors (https://github.com/googlefonts/mulish)
|
|
||||||
|
|
||||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
||||||
This license is copied below, and is also available with a FAQ at:
|
|
||||||
https://openfontlicense.org
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------------------------------
|
|
||||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
||||||
-----------------------------------------------------------
|
|
||||||
|
|
||||||
PREAMBLE
|
|
||||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
||||||
development of collaborative font projects, to support the font creation
|
|
||||||
efforts of academic and linguistic communities, and to provide a free and
|
|
||||||
open framework in which fonts may be shared and improved in partnership
|
|
||||||
with others.
|
|
||||||
|
|
||||||
The OFL allows the licensed fonts to be used, studied, modified and
|
|
||||||
redistributed freely as long as they are not sold by themselves. The
|
|
||||||
fonts, including any derivative works, can be bundled, embedded,
|
|
||||||
redistributed and/or sold with any software provided that any reserved
|
|
||||||
names are not used by derivative works. The fonts and derivatives,
|
|
||||||
however, cannot be released under any other type of license. The
|
|
||||||
requirement for fonts to remain under this license does not apply
|
|
||||||
to any document created using the fonts or their derivatives.
|
|
||||||
|
|
||||||
DEFINITIONS
|
|
||||||
"Font Software" refers to the set of files released by the Copyright
|
|
||||||
Holder(s) under this license and clearly marked as such. This may
|
|
||||||
include source files, build scripts and documentation.
|
|
||||||
|
|
||||||
"Reserved Font Name" refers to any names specified as such after the
|
|
||||||
copyright statement(s).
|
|
||||||
|
|
||||||
"Original Version" refers to the collection of Font Software components as
|
|
||||||
distributed by the Copyright Holder(s).
|
|
||||||
|
|
||||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
||||||
or substituting -- in part or in whole -- any of the components of the
|
|
||||||
Original Version, by changing formats or by porting the Font Software to a
|
|
||||||
new environment.
|
|
||||||
|
|
||||||
"Author" refers to any designer, engineer, programmer, technical
|
|
||||||
writer or other person who contributed to the Font Software.
|
|
||||||
|
|
||||||
PERMISSION & CONDITIONS
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
||||||
redistribute, and sell modified and unmodified copies of the Font
|
|
||||||
Software, subject to the following conditions:
|
|
||||||
|
|
||||||
1) Neither the Font Software nor any of its individual components,
|
|
||||||
in Original or Modified Versions, may be sold by itself.
|
|
||||||
|
|
||||||
2) Original or Modified Versions of the Font Software may be bundled,
|
|
||||||
redistributed and/or sold with any software, provided that each copy
|
|
||||||
contains the above copyright notice and this license. These can be
|
|
||||||
included either as stand-alone text files, human-readable headers or
|
|
||||||
in the appropriate machine-readable metadata fields within text or
|
|
||||||
binary files as long as those fields can be easily viewed by the user.
|
|
||||||
|
|
||||||
3) No Modified Version of the Font Software may use the Reserved Font
|
|
||||||
Name(s) unless explicit written permission is granted by the corresponding
|
|
||||||
Copyright Holder. This restriction only applies to the primary font name as
|
|
||||||
presented to the users.
|
|
||||||
|
|
||||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
||||||
Software shall not be used to promote, endorse or advertise any
|
|
||||||
Modified Version, except to acknowledge the contribution(s) of the
|
|
||||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
||||||
permission.
|
|
||||||
|
|
||||||
5) The Font Software, modified or unmodified, in part or in whole,
|
|
||||||
must be distributed entirely under this license, and must not be
|
|
||||||
distributed under any other license. The requirement for fonts to
|
|
||||||
remain under this license does not apply to any document created
|
|
||||||
using the Font Software.
|
|
||||||
|
|
||||||
TERMINATION
|
|
||||||
This license becomes null and void if any of the above conditions are
|
|
||||||
not met.
|
|
||||||
|
|
||||||
DISCLAIMER
|
|
||||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
||||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
||||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
||||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
||||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
Mulish Variable Font
|
|
||||||
====================
|
|
||||||
|
|
||||||
This download contains Mulish as both variable fonts and static fonts.
|
|
||||||
|
|
||||||
Mulish is a variable font with this axis:
|
|
||||||
wght
|
|
||||||
|
|
||||||
This means all the styles are contained in these files:
|
|
||||||
Mulish/Mulish-VariableFont_wght.ttf
|
|
||||||
Mulish/Mulish-Italic-VariableFont_wght.ttf
|
|
||||||
|
|
||||||
If your app fully supports variable fonts, you can now pick intermediate styles
|
|
||||||
that aren’t available as static fonts. Not all apps support variable fonts, and
|
|
||||||
in those cases you can use the static font files for Mulish:
|
|
||||||
Mulish/static/Mulish-ExtraLight.ttf
|
|
||||||
Mulish/static/Mulish-Light.ttf
|
|
||||||
Mulish/static/Mulish-Regular.ttf
|
|
||||||
Mulish/static/Mulish-Medium.ttf
|
|
||||||
Mulish/static/Mulish-SemiBold.ttf
|
|
||||||
Mulish/static/Mulish-Bold.ttf
|
|
||||||
Mulish/static/Mulish-ExtraBold.ttf
|
|
||||||
Mulish/static/Mulish-Black.ttf
|
|
||||||
Mulish/static/Mulish-ExtraLightItalic.ttf
|
|
||||||
Mulish/static/Mulish-LightItalic.ttf
|
|
||||||
Mulish/static/Mulish-Italic.ttf
|
|
||||||
Mulish/static/Mulish-MediumItalic.ttf
|
|
||||||
Mulish/static/Mulish-SemiBoldItalic.ttf
|
|
||||||
Mulish/static/Mulish-BoldItalic.ttf
|
|
||||||
Mulish/static/Mulish-ExtraBoldItalic.ttf
|
|
||||||
Mulish/static/Mulish-BlackItalic.ttf
|
|
||||||
|
|
||||||
Get started
|
|
||||||
-----------
|
|
||||||
|
|
||||||
1. Install the font files you want to use
|
|
||||||
|
|
||||||
2. Use your app's font picker to view the font family and all the
|
|
||||||
available styles
|
|
||||||
|
|
||||||
Learn more about variable fonts
|
|
||||||
-------------------------------
|
|
||||||
|
|
||||||
https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts
|
|
||||||
https://variablefonts.typenetwork.com
|
|
||||||
https://medium.com/variable-fonts
|
|
||||||
|
|
||||||
In desktop apps
|
|
||||||
|
|
||||||
https://theblog.adobe.com/can-variable-fonts-illustrator-cc
|
|
||||||
https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts
|
|
||||||
|
|
||||||
Online
|
|
||||||
|
|
||||||
https://developers.google.com/fonts/docs/getting_started
|
|
||||||
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide
|
|
||||||
https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts
|
|
||||||
|
|
||||||
Installing fonts
|
|
||||||
|
|
||||||
MacOS: https://support.apple.com/en-us/HT201749
|
|
||||||
Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux
|
|
||||||
Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows
|
|
||||||
|
|
||||||
Android Apps
|
|
||||||
|
|
||||||
https://developers.google.com/fonts/docs/android
|
|
||||||
https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts
|
|
||||||
|
|
||||||
License
|
|
||||||
-------
|
|
||||||
Please read the full license text (OFL.txt) to understand the permissions,
|
|
||||||
restrictions and requirements for usage, redistribution, and modification.
|
|
||||||
|
|
||||||
You can use them in your products & projects – print or digital,
|
|
||||||
commercial or otherwise.
|
|
||||||
|
|
||||||
This isn't legal advice, please consider consulting a lawyer and see the full
|
|
||||||
license for all details.
|
|
||||||
@@ -3,30 +3,23 @@ import clsx from 'clsx'
|
|||||||
|
|
||||||
const baseStyles = {
|
const baseStyles = {
|
||||||
solid:
|
solid:
|
||||||
'inline-flex justify-center rounded-full py-2 px-5 text-sm md:text-base font-semibold transition-colors',
|
'inline-flex justify-center rounded-full py-2 px-5 text-base font-semibold transition-colors',
|
||||||
outline:
|
outline:
|
||||||
'inline-flex justify-center bg-transparent font-semibold rounded-full border border-2 py-[calc(--spacing(2)-1px)] px-[calc(--spacing(5)-1px)] text-sm md:text-base transition-colors',
|
'inline-flex justify-center bg-transparent rounded-full border py-[calc(--spacing(2)-1px)] px-[calc(--spacing(5)-1px)] text-base transition-colors',
|
||||||
link:
|
|
||||||
'inline-flex items-center text-sm md:text-base font-semibold transition-colors',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const variantStyles = {
|
const variantStyles = {
|
||||||
solid: {
|
solid: {
|
||||||
cyan: 'relative overflow-hidden bg-cyan-500 text-white before:absolute before:inset-0 hover:bg-cyan-400 active:before:bg-transparent hover:before:bg-white/10 active:bg-cyan-500 active:text-white/80 before:transition-colors',
|
cyan: 'relative overflow-hidden bg-cyan-500 text-white before:absolute before:inset-0 active:before:bg-transparent hover:before:bg-white/10 active:bg-cyan-600 active:text-white/80 before:transition-colors',
|
||||||
white:
|
white:
|
||||||
'bg-white text-cyan-900 hover:bg-white/90 active:bg-white/90 active:text-cyan-900/70',
|
'bg-white text-cyan-900 hover:bg-white/90 active:bg-white/90 active:text-cyan-900/70',
|
||||||
gray: 'bg-gray-800 text-white hover:bg-gray-900 active:bg-gray-800 active:text-white/80',
|
gray: 'bg-gray-800 text-white hover:bg-gray-900 active:bg-gray-800 active:text-white/80',
|
||||||
green: 'bg-green-500 text-white hover:bg-green-600',
|
green: 'bg-green-500 text-white hover:bg-green-600',
|
||||||
},
|
},
|
||||||
outline: {
|
outline: {
|
||||||
cyan: 'border-cyan-500 text-cyan-500 hover:border-cyan-400 hover:text-cyan-400 active:border-cyan-400',
|
cyan: 'border-cyan-500 text-cyan-500',
|
||||||
gray: 'border-gray-200 text-gray-600 hover:text-cyan-400 hover:border-cyan-400 active:border-cyan-400',
|
gray: 'border-gray-300 text-gray-700 hover:border-cyan-500 active:border-cyan-500',
|
||||||
white: 'border-gray-300 text-white hover:text-cyan-400 hover:border-cyan-400 active:border-cyan-400',
|
white: 'border-gray-300 text-white hover:border-cyan-500 active:border-cyan-500',
|
||||||
},
|
|
||||||
link: {
|
|
||||||
cyan: 'text-cyan-400 underline hover:text-cyan-300',
|
|
||||||
white: 'text-white underline hover:text-cyan-300',
|
|
||||||
dark: 'text-gray-900 underline hover:text-cyan-400',
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,11 +30,7 @@ type ButtonProps = (
|
|||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
variant: 'outline'
|
variant: 'outline'
|
||||||
color?: keyof typeof variantStyles.outline
|
color?: (keyof typeof variantStyles.outline) | 'cyan'
|
||||||
}
|
|
||||||
| {
|
|
||||||
variant: 'link'
|
|
||||||
color?: keyof typeof variantStyles.link
|
|
||||||
}
|
}
|
||||||
) &
|
) &
|
||||||
(
|
(
|
||||||
@@ -54,33 +43,16 @@ type ButtonProps = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
export function Button({ className, as, ...props }: ButtonProps) {
|
export function Button({ className, as, ...props }: ButtonProps) {
|
||||||
// Set safe defaults per variant so color always matches a valid palette key
|
props.variant ??= 'solid'
|
||||||
if (!props.variant) {
|
props.color ??= 'gray'
|
||||||
props.variant = 'solid'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!props.color) {
|
|
||||||
if (props.variant === 'solid') {
|
|
||||||
props.color = 'gray'
|
|
||||||
} else if (props.variant === 'outline') {
|
|
||||||
props.color = 'gray'
|
|
||||||
} else if (props.variant === 'link') {
|
|
||||||
props.color = 'cyan'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const variant = props.variant
|
|
||||||
const color = props.color as string
|
|
||||||
|
|
||||||
className = clsx(
|
className = clsx(
|
||||||
baseStyles[variant],
|
baseStyles[props.variant],
|
||||||
variant === 'outline'
|
props.variant === 'outline'
|
||||||
? variantStyles.outline[color as keyof typeof variantStyles.outline]
|
? variantStyles.outline[props.color]
|
||||||
: variant === 'solid'
|
: props.variant === 'solid'
|
||||||
? variantStyles.solid[color as keyof typeof variantStyles.solid]
|
? variantStyles.solid[props.color]
|
||||||
: variant === 'link'
|
: undefined,
|
||||||
? variantStyles.link[color as keyof typeof variantStyles.link]
|
|
||||||
: undefined,
|
|
||||||
className,
|
className,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ export function Footer() {
|
|||||||
<div className="flex flex-col items-start justify-between gap-y-12 pt-16 pb-6 lg:flex-row lg:items-center lg:py-8">
|
<div className="flex flex-col items-start justify-between gap-y-12 pt-16 pb-6 lg:flex-row lg:items-center lg:py-8">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center text-gray-900">
|
<div className="flex items-center text-gray-900">
|
||||||
<img src="/images/logomark.svg" alt="Mycelium Logomark" className="h-20 w-20 flex-none" />
|
<img src="/images/logomark.svg" alt="Mycelium Logomark" className="h-15 w-15 flex-none" />
|
||||||
<div className="">
|
<div className="ml-4">
|
||||||
<p className="text-base lg:text-lg font-semibold">Project Mycelium</p>
|
<p className="text-base font-semibold">Project Mycelium</p>
|
||||||
<p className="mt-1 text-sm">Built for Digital Sovereignty</p>
|
<p className="mt-1 text-sm">Unleash the Power of Decentralization</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<nav className="mt-10 flex gap-8">
|
<nav className="mt-10 flex gap-8">
|
||||||
@@ -30,9 +30,6 @@ export function Footer() {
|
|||||||
<Link to="/nodes" className="text-sm text-gray-700 hover:text-cyan-500 transition-colors">
|
<Link to="/nodes" className="text-sm text-gray-700 hover:text-cyan-500 transition-colors">
|
||||||
Nodes
|
Nodes
|
||||||
</Link>
|
</Link>
|
||||||
<Link to="/mediakit" className="text-sm text-gray-700 hover:text-cyan-500 transition-colors">
|
|
||||||
Media Kit
|
|
||||||
</Link>
|
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<div className="group relative -mx-4 flex items-center self-stretch p-4 transition-colors hover:bg-gray-100 sm:self-auto sm:rounded-2xl lg:mx-0 lg:self-auto lg:p-6">
|
<div className="group relative -mx-4 flex items-center self-stretch p-4 transition-colors hover:bg-gray-100 sm:self-auto sm:rounded-2xl lg:mx-0 lg:self-auto lg:p-6">
|
||||||
@@ -52,11 +49,11 @@ export function Footer() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-center border-t border-gray-100 py-8 md:flex-row-reverse md:justify-between md:pt-6">
|
<div className="flex flex-col items-center border-t border-gray-100 pt-8 pb-12 md:flex-row-reverse md:justify-between md:pt-6">
|
||||||
<p className="mt-6 text-sm text-gray-500 md:mt-0">
|
<p className="mt-6 text-sm text-gray-500 md:mt-0">
|
||||||
© Copyright{' '}
|
© Copyright{' '}
|
||||||
<a href="https://ourworld.tf/" target="_blank" rel="noopener noreferrer" className="font-semibold hover:text-cyan-500 transition-colors">
|
<a href="https://www.threefold.io" target="_blank" rel="noopener noreferrer" className="hover:text-cyan-500 transition-colors">
|
||||||
OurWorld
|
ThreeFold
|
||||||
</a>{' '}
|
</a>{' '}
|
||||||
{new Date().getFullYear()}. All rights reserved.
|
{new Date().getFullYear()}. All rights reserved.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useState } from 'react'
|
|||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { Container } from './Container'
|
import { Container } from './Container'
|
||||||
import { Button } from './Button'
|
import { Button } from './Button'
|
||||||
import pmyceliumLogo from '../images/logos/mainlogo.svg'
|
import pmyceliumLogo from '../images/logos/logo_1.png'
|
||||||
import { Dialog } from '@headlessui/react'
|
import { Dialog } from '@headlessui/react'
|
||||||
import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline'
|
import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline'
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useState } from 'react'
|
|||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { Container } from './Container'
|
import { Container } from './Container'
|
||||||
import { Button } from './Button'
|
import { Button } from './Button'
|
||||||
import pmyceliumLogo from '../images/logos/mainlogo.svg'
|
import pmyceliumLogo from '../images/logos/logo_1.png'
|
||||||
import { Dialog } from '@headlessui/react'
|
import { Dialog } from '@headlessui/react'
|
||||||
import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline'
|
import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline'
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +1,8 @@
|
|||||||
import { useEffect } from 'react'
|
|
||||||
import { Outlet } from 'react-router-dom'
|
import { Outlet } from 'react-router-dom'
|
||||||
import { Footer } from './Footer'
|
import { Footer } from './Footer'
|
||||||
import { Header } from './Header'
|
import { Header } from './Header'
|
||||||
|
|
||||||
export function Layout() {
|
export function Layout() {
|
||||||
useEffect(() => {
|
|
||||||
if (typeof window === 'undefined') return
|
|
||||||
|
|
||||||
if (document.getElementById('mailerlite-universal')) return
|
|
||||||
|
|
||||||
const script = document.createElement('script')
|
|
||||||
script.id = 'mailerlite-universal'
|
|
||||||
script.innerHTML = `
|
|
||||||
(function(m,a,i,l,e,r){
|
|
||||||
m['MailerLiteObject']=e;
|
|
||||||
function f(){
|
|
||||||
var c={a:arguments,q:[]};
|
|
||||||
var r=this.push(c);
|
|
||||||
return "number"!=typeof r?r:f.bind(c.q);
|
|
||||||
}
|
|
||||||
f.q=f.q||[];
|
|
||||||
m[e]=m[e]||f.bind(f.q);
|
|
||||||
m[e].q=m[e].q||f.q;
|
|
||||||
r=a.createElement(i);
|
|
||||||
var _=a.getElementsByTagName(i)[0];
|
|
||||||
r.async=1;
|
|
||||||
r.src=l+'?v'+(~~(new Date().getTime()/1000000));
|
|
||||||
_.parentNode.insertBefore(r,_);
|
|
||||||
})(window, document, 'script', 'https://static.mailerlite.com/js/universal.js', 'ml');
|
|
||||||
window.ml_account = ml('accounts', '1778010', 'x2d3d9f8n1', 'load');
|
|
||||||
`
|
|
||||||
|
|
||||||
document.body.appendChild(script)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
script.remove()
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-[#fdfdfd] antialiased relative" style={{ fontFamily: 'var(--font-inter)' }}>
|
<div className="bg-[#fdfdfd] antialiased relative" style={{ fontFamily: 'var(--font-inter)' }}>
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ const colorVariants = {
|
|||||||
primary: 'text-gray-900',
|
primary: 'text-gray-900',
|
||||||
secondary: 'text-gray-600',
|
secondary: 'text-gray-600',
|
||||||
light: 'text-gray-50',
|
light: 'text-gray-50',
|
||||||
accent: 'text-cyan-400',
|
accent: 'text-cyan-500',
|
||||||
cyan: 'text-cyan-400',
|
cyan: 'text-cyan-50',
|
||||||
white: 'text-white',
|
white: 'text-white',
|
||||||
dark: 'text-gray-950',
|
dark: 'text-gray-950',
|
||||||
tertiary: 'text-gray-700',
|
tertiary: 'text-gray-700',
|
||||||
@@ -162,5 +162,5 @@ export const DownloadCardDescription = createTextComponent(
|
|||||||
'text-base/7 leading-normal tracking-normal'
|
'text-base/7 leading-normal tracking-normal'
|
||||||
)
|
)
|
||||||
|
|
||||||
export const CT = createTextComponent('span', 'text-base lg:text-lg leading-normal font-medium')
|
export const CT = createTextComponent('span', 'text-base lg:text-lg font-medium')
|
||||||
export const CP = createTextComponent('p', 'text-sm lg:text-base tracking-wide leading-normal font-light')
|
export const CP = createTextComponent('p', 'text-sm lg:text-base tracking-wide leading-tight font-light')
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const XAILogo = () => (
|
|||||||
xmlSpace="preserve"
|
xmlSpace="preserve"
|
||||||
width="30"
|
width="30"
|
||||||
height="30"
|
height="30"
|
||||||
|
fill="white"
|
||||||
>
|
>
|
||||||
<g>
|
<g>
|
||||||
<polygon points="557.09,211.99 565.4,538.36 631.96,538.36 640.28,93.18 " />
|
<polygon points="557.09,211.99 565.4,538.36 631.96,538.36 640.28,93.18 " />
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import React, { useEffect, useRef, useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
export const InfiniteMovingCards = ({
|
export const InfiniteMovingCards = ({
|
||||||
items,
|
items,
|
||||||
direction = "left",
|
direction = "left",
|
||||||
speed = "slow",
|
speed = "fast",
|
||||||
pauseOnHover = true,
|
pauseOnHover = true,
|
||||||
className,
|
className,
|
||||||
}: {
|
}: {
|
||||||
@@ -15,39 +15,43 @@ export const InfiniteMovingCards = ({
|
|||||||
speed?: "fast" | "normal" | "slow";
|
speed?: "fast" | "normal" | "slow";
|
||||||
pauseOnHover?: boolean;
|
pauseOnHover?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
}) => {
|
}): JSX.Element => {
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = React.useRef<HTMLDivElement>(null);
|
||||||
const scrollerRef = useRef<HTMLUListElement>(null);
|
const scrollerRef = React.useRef<HTMLUListElement>(null);
|
||||||
const [isReady, setIsReady] = useState(false);
|
const [start, setStart] = useState(false);
|
||||||
|
|
||||||
|
|
||||||
|
const getSpeed = useCallback(() => {
|
||||||
|
if (containerRef.current) {
|
||||||
|
if (speed === "fast") {
|
||||||
|
containerRef.current.style.setProperty("--animation-duration", "20s");
|
||||||
|
} else if (speed === "normal") {
|
||||||
|
containerRef.current.style.setProperty("--animation-duration", "40s");
|
||||||
|
} else {
|
||||||
|
containerRef.current.style.setProperty("--animation-duration", "80s");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [speed]);
|
||||||
|
|
||||||
|
const addAnimation = useCallback(() => {
|
||||||
|
if (containerRef.current && scrollerRef.current) {
|
||||||
|
const scrollerContent = Array.from(scrollerRef.current.children);
|
||||||
|
|
||||||
|
scrollerContent.forEach((item) => {
|
||||||
|
const duplicatedItem = item.cloneNode(true);
|
||||||
|
if (scrollerRef.current) {
|
||||||
|
scrollerRef.current.appendChild(duplicatedItem);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
getSpeed();
|
||||||
|
setStart(true);
|
||||||
|
}
|
||||||
|
}, [getSpeed]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!scrollerRef.current) return;
|
addAnimation();
|
||||||
|
}, [addAnimation]);
|
||||||
const children = Array.from(scrollerRef.current.children);
|
|
||||||
|
|
||||||
// duplicate each item ONCE
|
|
||||||
children.forEach((item) => {
|
|
||||||
const clone = item.cloneNode(true);
|
|
||||||
scrollerRef.current!.appendChild(clone);
|
|
||||||
});
|
|
||||||
|
|
||||||
// set speed variable
|
|
||||||
const duration =
|
|
||||||
speed === "fast" ? "20s" : speed === "normal" ? "40s" : "80s";
|
|
||||||
|
|
||||||
containerRef.current?.style.setProperty(
|
|
||||||
"--animation-duration",
|
|
||||||
duration
|
|
||||||
);
|
|
||||||
|
|
||||||
// set direction variable
|
|
||||||
containerRef.current?.style.setProperty(
|
|
||||||
"--animation-direction",
|
|
||||||
direction === "left" ? "forwards" : "reverse"
|
|
||||||
);
|
|
||||||
|
|
||||||
setIsReady(true);
|
|
||||||
}, [direction, speed]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -57,16 +61,13 @@ export const InfiniteMovingCards = ({
|
|||||||
<ul
|
<ul
|
||||||
ref={scrollerRef}
|
ref={scrollerRef}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex w-max shrink-0 gap-16 py-4",
|
"flex min-w-full shrink-0 gap-16 py-4 w-max flex-nowrap",
|
||||||
isReady &&
|
start && (direction === "left" ? "animate-scroll-left" : "animate-scroll-right"),
|
||||||
(direction === "left"
|
|
||||||
? "animate-infinite-scroll"
|
|
||||||
: "animate-infinite-scroll-right"),
|
|
||||||
pauseOnHover && "hover:[animation-play-state:paused]"
|
pauseOnHover && "hover:[animation-play-state:paused]"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{items.map((item, i) => (
|
{items.map((item, idx) => (
|
||||||
<li key={i} className="flex-shrink-0">
|
<li className="relative flex-shrink-0" key={idx}>
|
||||||
{item}
|
{item}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,122 +0,0 @@
|
|||||||
import { useState, useEffect } from 'react';
|
|
||||||
import WorldMap from './world-map';
|
|
||||||
import { motion } from 'framer-motion';
|
|
||||||
|
|
||||||
// Interface for the simplified data passed to WorldMap
|
|
||||||
interface GeoNode {
|
|
||||||
lat: number;
|
|
||||||
lng: number;
|
|
||||||
label?: string;
|
|
||||||
color?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface for the raw data structure expected from the gridproxy API
|
|
||||||
interface RawNode {
|
|
||||||
node_id: number;
|
|
||||||
location: {
|
|
||||||
latitude: string; // API often returns these as strings
|
|
||||||
longitude: string; // API often returns these as strings
|
|
||||||
city: string;
|
|
||||||
country: string;
|
|
||||||
};
|
|
||||||
// ... other raw fields you don't need
|
|
||||||
}
|
|
||||||
|
|
||||||
const clusterNodes = (nodeList: GeoNode[], cellSize = 2) => {
|
|
||||||
const buckets = new Map<
|
|
||||||
string,
|
|
||||||
{ latSum: number; lngSum: number; count: number }
|
|
||||||
>();
|
|
||||||
|
|
||||||
nodeList.forEach((node) => {
|
|
||||||
const latBucket = Math.round(node.lat / cellSize) * cellSize;
|
|
||||||
const lngBucket = Math.round(node.lng / cellSize) * cellSize;
|
|
||||||
const key = `${latBucket}|${lngBucket}`;
|
|
||||||
|
|
||||||
const bucket = buckets.get(key);
|
|
||||||
if (bucket) {
|
|
||||||
bucket.latSum += node.lat;
|
|
||||||
bucket.lngSum += node.lng;
|
|
||||||
bucket.count += 1;
|
|
||||||
} else {
|
|
||||||
buckets.set(key, {
|
|
||||||
latSum: node.lat,
|
|
||||||
lngSum: node.lng,
|
|
||||||
count: 1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return Array.from(buckets.values()).map((bucket) => {
|
|
||||||
const avgLat = bucket.latSum / bucket.count;
|
|
||||||
const avgLng = bucket.lngSum / bucket.count;
|
|
||||||
const count = bucket.count;
|
|
||||||
|
|
||||||
let color = "#06b6d4";
|
|
||||||
if (count > 20) {
|
|
||||||
color = "#0891b2";
|
|
||||||
} else if (count > 5) {
|
|
||||||
color = "#22d3ee";
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
lat: avgLat,
|
|
||||||
lng: avgLng,
|
|
||||||
color,
|
|
||||||
label: `${count} nodes`,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function DynamicMapContainer() {
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [nodes, setNodes] = useState<GeoNode[]>([]);
|
|
||||||
const API_URL = "https://gridproxy.grid.tf/nodes?healthy=true&size=500";
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function fetchNodeData() {
|
|
||||||
try {
|
|
||||||
const response = await fetch(API_URL);
|
|
||||||
const data: RawNode[] = await response.json();
|
|
||||||
|
|
||||||
const geoNodes: GeoNode[] = data
|
|
||||||
.filter((node: RawNode) => node.location && node.location.latitude && node.location.longitude)
|
|
||||||
.map((node: RawNode) => ({
|
|
||||||
lat: parseFloat(node.location.latitude),
|
|
||||||
lng: parseFloat(node.location.longitude),
|
|
||||||
label: `${node.location.city}, ${node.location.country} (${node.node_id})`,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const clusteredNodes = clusterNodes(geoNodes);
|
|
||||||
setNodes(clusteredNodes);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Failed to fetch node data:", error);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchNodeData();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// While fetching, show a loading state
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<div className="flex justify-center items-center w-full aspect-[2/1] bg-[#111111] rounded-lg text-cyan-500">
|
|
||||||
<motion.span
|
|
||||||
animate={{ rotate: 360 }}
|
|
||||||
transition={{ duration: 1, repeat: Infinity, ease: "linear" }}
|
|
||||||
className="text-4xl"
|
|
||||||
>
|
|
||||||
🌎
|
|
||||||
</motion.span>
|
|
||||||
<p className="ml-4">Loading nodes...</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// After data is loaded, render the map
|
|
||||||
return <WorldMap nodes={nodes} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DynamicMapContainer;
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useRef } from "react";
|
import { useRef } from "react";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "motion/react";
|
||||||
import DottedMap from "dotted-map";
|
import DottedMap from "dotted-map";
|
||||||
|
|
||||||
interface MapProps {
|
interface MapProps {
|
||||||
@@ -9,38 +9,33 @@ interface MapProps {
|
|||||||
start: { lat: number; lng: number; label?: string };
|
start: { lat: number; lng: number; label?: string };
|
||||||
end: { lat: number; lng: number; label?: string };
|
end: { lat: number; lng: number; label?: string };
|
||||||
}>;
|
}>;
|
||||||
// New prop for dynamic standalone nodes
|
|
||||||
nodes?: Array<{ lat: number; lng: number; label?: string; color?: string }>;
|
|
||||||
lineColor?: string;
|
lineColor?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function WorldMap({
|
export default function WorldMap({
|
||||||
dots = [],
|
dots = [],
|
||||||
nodes = [],
|
lineColor = "#06b6d4", // cyan-500
|
||||||
lineColor = "#06b6d4",
|
|
||||||
}: MapProps) {
|
}: MapProps) {
|
||||||
const svgRef = useRef<SVGSVGElement>(null);
|
const svgRef = useRef<SVGSVGElement>(null);
|
||||||
|
|
||||||
// ✅ Force dark-dotted map theme
|
// ✅ Force dark-dotted map theme
|
||||||
const map = new DottedMap({ height: 100, grid: "diagonal" });
|
const map = new DottedMap({ height: 100, grid: "diagonal" });
|
||||||
const svgMap = map.getSVG({
|
const svgMap = map.getSVG({
|
||||||
radius: 0.22,
|
radius: 0.22,
|
||||||
color: "#06b6d480",
|
color: "#06b6d480", // cyan-500 at 50% opacity
|
||||||
shape: "circle",
|
shape: "circle",
|
||||||
backgroundColor: "#111111",
|
backgroundColor: "#111111",
|
||||||
});
|
});
|
||||||
|
|
||||||
// ✅ Point projection stays the same
|
// ✅ Point projection stays the same
|
||||||
// Projects lat/lng to the SVG's 800x400 viewBox coordinates
|
|
||||||
const projectPoint = (lat: number, lng: number) => {
|
const projectPoint = (lat: number, lng: number) => {
|
||||||
const x = (lng + 180) * (800 / 360);
|
const x = (lng + 180) * (800 / 360);
|
||||||
const y = (90 - lat) * (400 / 180) + 45;
|
const y = (90 - lat) * (400 / 180);
|
||||||
return { x, y };
|
return { x, y };
|
||||||
};
|
};
|
||||||
|
|
||||||
const createCurvedPath = (start: any, end: any) => {
|
const createCurvedPath = (start: any, end: any) => {
|
||||||
const midX = (start.x + end.x) / 2;
|
const midX = (start.x + end.x) / 2;
|
||||||
// Creates an arc that bows upward by 50 units
|
|
||||||
const midY = Math.min(start.y, end.y) - 50;
|
const midY = Math.min(start.y, end.y) - 50;
|
||||||
return `M ${start.x} ${start.y} Q ${midX} ${midY} ${end.x} ${end.y}`;
|
return `M ${start.x} ${start.y} Q ${midX} ${midY} ${end.x} ${end.y}`;
|
||||||
};
|
};
|
||||||
@@ -54,53 +49,13 @@ export default function WorldMap({
|
|||||||
draggable={false}
|
draggable={false}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* ✅ Lines + points + new standalone nodes */}
|
{/* ✅ Lines + points */}
|
||||||
<svg
|
<svg
|
||||||
ref={svgRef}
|
ref={svgRef}
|
||||||
viewBox="0 0 800 400"
|
viewBox="0 0 800 400"
|
||||||
className="w-full h-full absolute inset-0 pointer-events-none select-none"
|
className="w-full h-full absolute inset-0 pointer-events-none select-none"
|
||||||
>
|
>
|
||||||
{/* Glowing path gradient DEFS */}
|
{/* ✅ animated curved travel lines */}
|
||||||
<defs>
|
|
||||||
<linearGradient id="path-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
||||||
<stop offset="0%" stopColor="black" stopOpacity="0" />
|
|
||||||
<stop offset="5%" stopColor={lineColor} stopOpacity="1" />
|
|
||||||
<stop offset="95%" stopColor={lineColor} stopOpacity="1" />
|
|
||||||
<stop offset="100%" stopColor="black" stopOpacity="0" />
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
{/* ✅ DYNAMIC STANDALONE NODE DOTS (New Section) */}
|
|
||||||
{nodes.map((node, i) => {
|
|
||||||
const p = projectPoint(node.lat, node.lng);
|
|
||||||
const dotColor = node.color || lineColor;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<g key={`node-${i}`}>
|
|
||||||
{/* Outer pulsing circle */}
|
|
||||||
<circle cx={p.x} cy={p.y} r="2" fill={dotColor} opacity="0.5">
|
|
||||||
<animate
|
|
||||||
attributeName="r"
|
|
||||||
from="2"
|
|
||||||
to="7"
|
|
||||||
dur="1.4s"
|
|
||||||
repeatCount="indefinite"
|
|
||||||
/>
|
|
||||||
<animate
|
|
||||||
attributeName="opacity"
|
|
||||||
from="0.6"
|
|
||||||
to="0"
|
|
||||||
dur="1.4s"
|
|
||||||
repeatCount="indefinite"
|
|
||||||
/>
|
|
||||||
</circle>
|
|
||||||
{/* Inner fixed circle */}
|
|
||||||
<circle cx={p.x} cy={p.y} r="2" fill={dotColor} />
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
|
|
||||||
{/* ✅ Animated curved travel lines (Existing Logic) */}
|
|
||||||
{dots.map((dot, i) => {
|
{dots.map((dot, i) => {
|
||||||
const startPoint = projectPoint(dot.start.lat, dot.start.lng);
|
const startPoint = projectPoint(dot.start.lat, dot.start.lng);
|
||||||
const endPoint = projectPoint(dot.end.lat, dot.end.lng);
|
const endPoint = projectPoint(dot.end.lat, dot.end.lng);
|
||||||
@@ -123,7 +78,17 @@ export default function WorldMap({
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{/* ✅ Start & end points with pulsing cyan glow (Existing Logic) */}
|
{/* ✅ glowing path gradient */}
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="path-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||||
|
<stop offset="0%" stopColor="black" stopOpacity="0" />
|
||||||
|
<stop offset="5%" stopColor={lineColor} stopOpacity="1" />
|
||||||
|
<stop offset="95%" stopColor={lineColor} stopOpacity="1" />
|
||||||
|
<stop offset="100%" stopColor="black" stopOpacity="0" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
{/* ✅ start & end points with pulsing cyan glow */}
|
||||||
{dots.map((dot, i) => {
|
{dots.map((dot, i) => {
|
||||||
const s = projectPoint(dot.start.lat, dot.start.lng);
|
const s = projectPoint(dot.start.lat, dot.start.lng);
|
||||||
const e = projectPoint(dot.end.lat, dot.end.lng);
|
const e = projectPoint(dot.end.lat, dot.end.lng);
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="77" zoomAndPan="magnify" viewBox="0 0 57.75 54" height="72" preserveAspectRatio="xMidYMid meet" version="1.0"><defs><clipPath id="6b820d2194"><path d="M 0.402344 0 L 57.101562 0 L 57.101562 6 L 0.402344 6 Z M 0.402344 0 " clip-rule="nonzero"/></clipPath><clipPath id="3794aac157"><path d="M 0.402344 23 L 57 23 L 57 30 L 0.402344 30 Z M 0.402344 23 " clip-rule="nonzero"/></clipPath><clipPath id="a8068b094c"><path d="M 0.402344 46 L 57.101562 46 L 57.101562 53 L 0.402344 53 Z M 0.402344 46 " clip-rule="nonzero"/></clipPath></defs><g clip-path="url(#6b820d2194)"><path stroke-linecap="butt" transform="matrix(0.736364, 0, 0, 0.736364, 0.402273, 0.00000196364)" fill="none" stroke-linejoin="miter" d="M 0.000096737 3.999805 L 76.537522 3.999805 " stroke="#43d7ff" stroke-width="8" stroke-opacity="1" stroke-miterlimit="4"/></g><g clip-path="url(#3794aac157)"><path fill="#43d7ff" d="M 0.402344 23.136719 L 35.746094 23.136719 L 35.746094 29.027344 L 0.402344 29.027344 M 41.636719 23.136719 L 56.761719 23.136719 L 56.761719 29.027344 L 41.636719 29.027344 " fill-opacity="1" fill-rule="nonzero"/></g><g clip-path="url(#a8068b094c)"><path stroke-linecap="butt" transform="matrix(0.736364, 0, 0, 0.736364, 0.402273, 46.951043)" fill="none" stroke-linejoin="miter" d="M 0.000096737 4.002635 L 76.537522 4.002635 " stroke="#43d7ff" stroke-width="8" stroke-opacity="1" stroke-miterlimit="4"/></g></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
@@ -23,7 +23,7 @@ const bentos: {
|
|||||||
{
|
{
|
||||||
id: "core",
|
id: "core",
|
||||||
eyebrow: "ARCHITECTURE",
|
eyebrow: "ARCHITECTURE",
|
||||||
title: "Intelligence Fabric",
|
title: "Augmented Intelligence Fabric",
|
||||||
description:
|
description:
|
||||||
"The sovereign substrate for autonomous AI. Stateless, geo-aware, end-to-end encrypted—and verifiable from intent to execution.",
|
"The sovereign substrate for autonomous AI. Stateless, geo-aware, end-to-end encrypted—and verifiable from intent to execution.",
|
||||||
animation: null,
|
animation: null,
|
||||||
@@ -123,7 +123,7 @@ export function AgentBento() {
|
|||||||
<div className="w-full h-full object-cover"><card.animation /></div>
|
<div className="w-full h-full object-cover"><card.animation /></div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="hidden md:flex md:h-48 w-full items-center justify-center bg-transparent" />
|
<div className="h-48 w-full flex items-center justify-center bg-transparent" />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="px-8 pt-4 pb-6">
|
<div className="px-8 pt-4 pb-6">
|
||||||
@@ -136,7 +136,7 @@ export function AgentBento() {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{/* ✅ NEW SUBTITLE */}
|
{/* ✅ NEW SUBTITLE */}
|
||||||
<p className="text-sm text-cyan-500">{card.subtitle}</p>
|
<p className="text-sm text-cyan-400">{card.subtitle}</p>
|
||||||
|
|
||||||
<p className="mt-1 text-lg font-medium lg:text-xl tracking-tight text-white">
|
<p className="mt-1 text-lg font-medium lg:text-xl tracking-tight text-white">
|
||||||
{card.title}
|
{card.title}
|
||||||
@@ -161,9 +161,6 @@ export function AgentBento() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* ✅ Bottom horizontal line with spacing */}
|
|
||||||
<div className="w-full border-b border-gray-800" />
|
|
||||||
<div className="max-w-7xl bg-transparent mx-auto py-6 border border-t-0 border-b-0 border-gray-800"></div>
|
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
|
import { Button } from '@/components/Button'
|
||||||
import { Eyebrow, H3, P } from '@/components/Texts'
|
import { Eyebrow, H3, P } from '@/components/Texts'
|
||||||
|
|
||||||
export function AgentHeroAlt() {
|
export function AgentHeroAlt() {
|
||||||
@@ -7,32 +8,28 @@ export function AgentHeroAlt() {
|
|||||||
<div className="">
|
<div className="">
|
||||||
{/* Boxed container */}
|
{/* Boxed container */}
|
||||||
<div
|
<div
|
||||||
className="relative mx-auto max-w-7xl border border-t-0 border-b-0 border-gray-100 bg-white overflow-hidden md:bg-[url('/images/agents.webp')] md:bg-contain md:bg-right md:bg-no-repeat"
|
className="relative mx-auto max-w-7xl border border-t-0 border-b-0 border-gray-100 bg-white overflow-hidden bg-contain bg-right bg-no-repeat"
|
||||||
|
style={{ backgroundImage: "url('/images/agents.webp')", backgroundSize: "contain" }}
|
||||||
>
|
>
|
||||||
{/* Inner padding */}
|
{/* Inner padding */}
|
||||||
<div className="px-6 pt-4 pb-12 lg:py-24">
|
<div className="px-6 py-16 lg:py-16">
|
||||||
{/* Mobile-only hero image */}
|
<div className="max-w-2xl lg:pl-6">
|
||||||
<img
|
|
||||||
src="/images/mobile/agents.jpg"
|
|
||||||
alt="Mycelium Agents visual"
|
|
||||||
className="mb-8 w-full object-cover md:hidden"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="max-w-xl lg:pl-6">
|
|
||||||
<Eyebrow>MYCELIUM AGENTS - COMING IN 2026</Eyebrow>
|
<Eyebrow>MYCELIUM AGENTS - COMING IN 2026</Eyebrow>
|
||||||
<H3 as="h1" className="mt-4">
|
<H3 as="h1" className="mt-4">
|
||||||
Private, Sovereign and Distributed AI You Control
|
Private, Sovereign and Distributed AI You Control
|
||||||
</H3>
|
</H3>
|
||||||
<P className="mt-6 text-gray-600">
|
<P className="mt-6 text-gray-800">
|
||||||
Mycelium Agents let you deploy and run intelligent systems on your own infrastructure.
|
Mycelium Agents let you deploy and run intelligent systems on your own infrastructure.
|
||||||
</P>
|
|
||||||
<P className="mt-4 text-gray-600">
|
|
||||||
Private, local, and autonomous by design, they give you everything you need to build, host, and connect AI agents without relying on centralized clouds.
|
Private, local, and autonomous by design, they give you everything you need to build, host, and connect AI agents without relying on centralized clouds.
|
||||||
</P>
|
</P>
|
||||||
|
|
||||||
<div className="mt-10 flex items-center gap-x-6">
|
<div className="mt-10 flex items-center gap-x-6">
|
||||||
{/* TODO: Hero CTAs (Follow Development / Explore Docs) to be added when links are ready.
|
<Button href="#" variant="solid" color="cyan">
|
||||||
Previously two Buttons here with href="#". */}
|
Follow Development
|
||||||
|
</Button>
|
||||||
|
<Button href="#" variant="outline">
|
||||||
|
Explore Docs <span aria-hidden="true">→</span>
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
65
src/pages/agents/AgentLogos.tsx
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { InfiniteMovingCards } from "@/components/magicui/infinite-moving-cards";
|
||||||
|
import Ai21Logo from "@/components/logos/Ai21";
|
||||||
|
import ClaudeLogo from "@/components/logos/Claude";
|
||||||
|
import BaiduCloudLogo from "@/components/logos/BaiduCloud";
|
||||||
|
import ByteDanceLogo from "@/components/logos/ByteDance";
|
||||||
|
import DeepSeekLogo from "@/components/logos/DeepSeek";
|
||||||
|
import DeepMindLogo from "@/components/logos/DeepMind";
|
||||||
|
import MinimaxLogo from "@/components/logos/Minimax";
|
||||||
|
import MistralLogo from "@/components/logos/Mistral";
|
||||||
|
import MoonshotLogo from "@/components/logos/Moonshot";
|
||||||
|
import TencentCloudLogo from "@/components/logos/TencentCloud";
|
||||||
|
import OpenAILogo from "@/components/logos/OpenAI";
|
||||||
|
import XAILogo from "@/components/logos/XAI";
|
||||||
|
|
||||||
|
const logos = [
|
||||||
|
{ id: "ai21", Component: Ai21Logo, label: "AI21" },
|
||||||
|
{ id: "claude", Component: ClaudeLogo, label: "Claude" },
|
||||||
|
{ id: "baidu", Component: BaiduCloudLogo, label: "Baidu Cloud" },
|
||||||
|
{ id: "bytedance", Component: ByteDanceLogo, label: "ByteDance" },
|
||||||
|
{ id: "deepseek", Component: DeepSeekLogo, label: "DeepSeek" },
|
||||||
|
{ id: "deepmind", Component: DeepMindLogo, label: "DeepMind" },
|
||||||
|
{ id: "minimax", Component: MinimaxLogo, label: "Minimax" },
|
||||||
|
{ id: "mistral", Component: MistralLogo, label: "Mistral" },
|
||||||
|
{ id: "moonshot", Component: MoonshotLogo, label: "Moonshot" },
|
||||||
|
{ id: "tencent", Component: TencentCloudLogo, label: "Tencent Cloud" },
|
||||||
|
{ id: "openai", Component: OpenAILogo, label: "OpenAI" },
|
||||||
|
{ id: "xai", Component: XAILogo, label: "xAI" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const splitLogoRows = () => {
|
||||||
|
const midpoint = Math.ceil(logos.length / 2);
|
||||||
|
return [logos.slice(0, midpoint), logos.slice(midpoint)];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AgentLogos = () => {
|
||||||
|
const rows = splitLogoRows();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="relative isolate overflow-hidden bg-[#121212] py-4">
|
||||||
|
<div className="pointer-events-none absolute inset-y-0 left-0 w-32 bg-gradient-to-r from-black to-transparent" />
|
||||||
|
<div className="pointer-events-none absolute inset-y-0 right-0 w-32 bg-gradient-to-l from-black to-transparent" />
|
||||||
|
<div className="flex w-full flex-col gap-1 px-0">
|
||||||
|
{rows.map((row, idx) => (
|
||||||
|
<InfiniteMovingCards
|
||||||
|
key={`logos-row-${idx}`}
|
||||||
|
items={row.map(({ id, Component, label }) => (
|
||||||
|
<div
|
||||||
|
key={id}
|
||||||
|
className="flex h-24 w-36 items-center justify-center bg-transparent px-4"
|
||||||
|
aria-label={label}
|
||||||
|
>
|
||||||
|
<Component />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
direction={idx % 2 === 0 ? "left" : "right"}
|
||||||
|
speed="slow"
|
||||||
|
pauseOnHover={true}
|
||||||
|
className="w-full"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||