diff --git a/.cspell/project_words.txt b/.cspell/project_words.txt index 5a12f695b..964f537d6 100644 --- a/.cspell/project_words.txt +++ b/.cspell/project_words.txt @@ -4,6 +4,7 @@ alluxio augusto automount benja +Bento brendonovich chacha codegen diff --git a/Collection.png b/Collection.png new file mode 100644 index 000000000..205253839 Binary files /dev/null and b/Collection.png differ diff --git a/Database.png b/Database.png new file mode 100644 index 000000000..7e0fef2ba Binary files /dev/null and b/Database.png differ diff --git a/Game.png b/Game.png new file mode 100644 index 000000000..d278237bb Binary files /dev/null and b/Game.png differ diff --git a/Heart.png b/Heart.png new file mode 100644 index 000000000..ac1ff1314 Binary files /dev/null and b/Heart.png differ diff --git a/Image.png b/Image.png new file mode 100644 index 000000000..2aef7d0f6 Binary files /dev/null and b/Image.png differ diff --git a/Lock.png b/Lock.png new file mode 100644 index 000000000..75dfd860f Binary files /dev/null and b/Lock.png differ diff --git a/Node.png b/Node.png new file mode 100644 index 000000000..d0caf82a4 Binary files /dev/null and b/Node.png differ diff --git a/Package.png b/Package.png new file mode 100644 index 000000000..89fb14014 Binary files /dev/null and b/Package.png differ diff --git a/TexturedMesh.png b/TexturedMesh.png new file mode 100644 index 000000000..9239788e2 Binary files /dev/null and b/TexturedMesh.png differ diff --git a/Video.png b/Video.png new file mode 100644 index 000000000..44c53d958 Binary files /dev/null and b/Video.png differ diff --git a/apps/landing/package.json b/apps/landing/package.json index d645cb464..4f6c58d46 100644 --- a/apps/landing/package.json +++ b/apps/landing/package.json @@ -22,17 +22,21 @@ "@vercel/edge-config": "^0.1.11", "autoprefixer": "^10.4.14", "clsx": "^1.2.1", - "contentlayer": "^0.3.4", + "contentlayer": "^0.3.2", + "dayjs": "^1.11.8", "drizzle-orm": "^0.26.0", + "framer-motion": "^10.11.5", "markdown-to-jsx": "^7.2.0", "md5": "^2.3.0", "next": "13.4.3", "next-contentlayer": "^0.3.4", "react": "18.2.0", "react-burger-menu": "^3.0.9", + "react-device-detect": "^2.2.3", "react-dom": "^18.2.0", - "react-hook-form": "~7.45.2", - "react-tsparticles": "^2.10.1", + "react-github-btn": "^1.4.0", + "react-hook-form": "^7.43.9", + "react-tsparticles": "^2.9.3", "reading-time": "^1.5.0", "rehype-autolink-headings": "^6.1.1", "rehype-external-links": "^2.1.0", @@ -42,7 +46,8 @@ "remark-gfm": "^3.0.1", "remark-math": "^5.1.1", "sharp": "^0.32.1", - "three": "^0.154", + "tailwind-merge": "^1.14.0", + "three": "^0.153.0", "tsparticles": "^2.9.3", "uuid": "^9.0.0", "zod": "~3.22.2" diff --git a/apps/landing/posts/october-alpha-release.mdx b/apps/landing/posts/october-alpha-release.mdx new file mode 100644 index 000000000..a81fd0e8c --- /dev/null +++ b/apps/landing/posts/october-alpha-release.mdx @@ -0,0 +1,103 @@ +--- +author: Jamie Pine +title: October '23 Alpha Release +tags: [News, Updates, Release] +date: 2023-10-06 +image: images/october-23-alpha-release.png +imageAlt: Spacedrive UI snapshot alongside logo +--- + +Last year, we introduced Spacedrive to the world by open-sourcing the codebase on GitHub. The reception was nothing short of phenomenal, allowing us to secure $2m in venture funding. As of today, our dedicated remote team of nine continues to grow, celebrated by almost 20,000 stars on the repository. It's with immense anticipation and excitement that we present to you the public alpha of Spacedrive. + +Spacedrive reimagines the traditional file explorer. Its modern interface design, cross-platform framework, and constantly expanding toolkit offer a seamless file management experience. + +For this alpha release, downloads are available for [macOS](), [Windows](), and [Linux](). We're aware that there are some bugs and important features yet to be incorporated such as device connectivity, multi-select, and cloud support. Despite that, there is still at lot to explore today. + +You start by adding local folders, network locations, and external drives into your data library. Spacedrive will keep its database in sync with the underlying file system, extracting metadata, uniquely identifying files, and generating thumbnails. This allows you to organise and search your library even when the storage locations go offline. A job manager lets you track the progress for each phase of indexing, copy/paste, encryption, media encoding, and more. + +For a comprehensive overview of the features included in this release, check out our [changelog](). Your participation in this alpha release will help shape the future of Spacedrive, and we can't wait for you to try it. + +Let's take a look at what you can do with Spacedrive today... + +## The Explorer + +The primary interface in the Spacedrive app is the Explorer—designed as a familiar interface, it is the heart of the app. From here you can explore storage locations and devices in your network. It features 4 views: Icons, List, Columns, and Media. All sidebar buttons open some variation of the Explorer. + +![Spacedrive Explorer](/images/explorer.png) + +In an upcoming release, system locations such as "Desktop", "Documents", "Downloads", "Music", etc. will be automatically added to the Explorer with indexing disabled by default. For now, you can add them manually by clicking the `Add location` button in the sidebar. + +## Tagging + +Tags are core to Spacedrive's design. Unlike folders, tags work great even when you're using many devices or storing files in different places. With tags, you can sort files easily, and even set up automatic backups, encryption, and more. + +We've also introduced a "tag assign mode". This handy feature lets you add tags to your files in any view. Just select your files, then click the tag you want in the bar at the bottom.¹ + +Tags are assigned uniquely to files, so every copy of that file across your library will have the same tags. This is achieved by our content-addressable storage system, which identifies files by their content, not their location. + +## Albums + +Spacedrive will be the ultimate photo and video manager. Our personal photo libraries and camera-roll are some of the most important data we own. We've designed Albums to be the best way to manage your media, from family photos to video collaboration.² + +## Quick Preview + +Quick preview is our macOS-like way of viewing files from within Spacedrive. We plan to support many more file types than we do currently, but for now you can preview images, videos, audio, and PDFs. + +In a future release we will have multi-window support, which will allow you to optionally open Quick Preview as its own window. This extends to other parts of the app, such as the Job Manager, and enables multiple windows for the app itself. + +## File identification + +Spacedrive is able to understand over 250 common file types from all operating systems, and differentiate conflicting extensions by reading [magic bytes](). All files are categorized by our [Object]() kind classification. Code files never show up as video files, and all media will have thumbnails and previews. As we evolve, few file types should be unrecognizable by Spacedrive. + +## Search + +Today you can search for files by keyword across your library instantly. Our database-powered filesystem makes for a fast and responsive search experience. In the future we will add more advanced search features such as an array of filters, and boolean operators. + +## Multi-platform support + +We've designed Spacedrive with cross-platform support in mind from the start. Aside from the downloads for macOS, Windows and Linux, Spacedrive's core can be run via Docker, allowing you to easily self host a node on a web server or NAS, and control it with the same interface as the app, but in the browser through your network. + +We've also built a mobile app for iOS and Android, which will be available in the coming weeks. Select users will have access sooner via TestFlight and Google Play Beta. + +## Our cloud service + +At its heart, Spacedrive will always be open source and free. However, we do offer optional paid services to boost your experience. For instance, with a subscription to our cloud service, you get encrypted database and preview media backup, file transfer options, ample storage, and backup plans. These are perfect for individuals, teams, and businesses looking for better collaboration.³ + +Connections between devices in your network are always peer-to-peer, and never go through our servers. We do not have access to your data, and we never will. Our cloud service is completely optional, and you can use Spacedrive without it. + +## Upcoming features + +- Cloud support (Dropbox, Google Drive, Mega, etc.) +- Multi-select, drag and drop, and selection history +- Multi-device connectivity with peer-to-peer sync +- Spacedrop (AirDrop like experience with end-to-end encrypted internet transfer) +- Location backup and sync between devices and clouds +- File sharing via Spaces +- File versioning +- Encryption key manager +- File encryption and encrypted containers +- Extensions +- Custom themes +- Tabs and dual-pane view +- More supported file types for Quick Preview and thumbnails +- Machine learning for face detection, video transcription, automatic labeling and more + +_...and much more!_ + +## Underlying technology + +Reinventing the file explorer is a tremendous task. From OS compatibility to synchronizing a highly scalable distributed database running on-device, we've tackled the most challenging engineering problems and developed entirely new technologies to realize the Spacedrive vision. Our tech stack has never been done before, yet is working beyond expectation—from developer experience to runtime performance. + +Our unique implementation of a VDFS (Virtual Distributed File System) has been a key enabler for Spacedrive. It allows us to abstract away the underlying file system, and provide a unified interface for all storage locations. Utilizing content addressable storage, we can provide an unprecidented view of your data across so many platforms. We've employed techniques such as constant time hashing, a novel indexing strategy to ensure that Spacedrive is fast and responsive, even with millions of files.⁴ + +[Prisma Client Rust](), [rspc]() and [Specta]() are all libraries developed by members of our team initially for Spacedrive, but are now growing into their own communities, with Prisma officially sponsoring PCR and working closely with Tauri to improve the developer experience. + +## Notes: + +_¹ This feature is not functional until the next minor release, in the following few days._ + +_² Collaboration and more advanced photo management features are planned for a future release._ + +³ Our cloud service is not yet available, but will be in the coming weeks.\_ + +_⁴ Our constant time hashing strategy generates what we call a `cas_id`. It is used to assign file paths to a unique Object. We generate this identifier using fixed size samples at specific points in the file's contents, along with the total size. This gives us high certainty that a given file is unique at the same speed regardless of file size. However, for sensitive operations, we generate a full `integrity_hash` which is hashed from the entire file contents. This is used for encryption, file sync, overwrite protection, and other operations where we need to be sure that the file has not been tampered with._ diff --git a/apps/landing/public/images/app/1.webp b/apps/landing/public/images/app/1.webp new file mode 100644 index 000000000..b30be5c2b Binary files /dev/null and b/apps/landing/public/images/app/1.webp differ diff --git a/apps/landing/public/images/app/10.webp b/apps/landing/public/images/app/10.webp new file mode 100644 index 000000000..1f5c64d5d Binary files /dev/null and b/apps/landing/public/images/app/10.webp differ diff --git a/apps/landing/public/images/app/2.webp b/apps/landing/public/images/app/2.webp new file mode 100644 index 000000000..15a06ad78 Binary files /dev/null and b/apps/landing/public/images/app/2.webp differ diff --git a/apps/landing/public/images/app/3.webp b/apps/landing/public/images/app/3.webp new file mode 100644 index 000000000..37130d44f Binary files /dev/null and b/apps/landing/public/images/app/3.webp differ diff --git a/apps/landing/public/images/app/4.webp b/apps/landing/public/images/app/4.webp new file mode 100644 index 000000000..4c9a9020e Binary files /dev/null and b/apps/landing/public/images/app/4.webp differ diff --git a/apps/landing/public/images/app/5.webp b/apps/landing/public/images/app/5.webp new file mode 100644 index 000000000..ffbe45d16 Binary files /dev/null and b/apps/landing/public/images/app/5.webp differ diff --git a/apps/landing/public/images/app/6.webp b/apps/landing/public/images/app/6.webp new file mode 100644 index 000000000..e2658c9a2 Binary files /dev/null and b/apps/landing/public/images/app/6.webp differ diff --git a/apps/landing/public/images/app/7.webp b/apps/landing/public/images/app/7.webp new file mode 100644 index 000000000..f3ba0a3a6 Binary files /dev/null and b/apps/landing/public/images/app/7.webp differ diff --git a/apps/landing/public/images/app/8.webp b/apps/landing/public/images/app/8.webp new file mode 100644 index 000000000..0fa3f9a32 Binary files /dev/null and b/apps/landing/public/images/app/8.webp differ diff --git a/apps/landing/public/images/app/9.webp b/apps/landing/public/images/app/9.webp new file mode 100644 index 000000000..750538904 Binary files /dev/null and b/apps/landing/public/images/app/9.webp differ diff --git a/apps/landing/public/images/app2.webp b/apps/landing/public/images/app2.webp new file mode 100644 index 000000000..afcbf83a8 Binary files /dev/null and b/apps/landing/public/images/app2.webp differ diff --git a/apps/landing/public/images/appgradient.webp b/apps/landing/public/images/appgradient.webp new file mode 100644 index 000000000..c4cecbd5f Binary files /dev/null and b/apps/landing/public/images/appgradient.webp differ diff --git a/apps/landing/public/images/appgradientoverlay.png b/apps/landing/public/images/appgradientoverlay.png new file mode 100644 index 000000000..98469b3b1 Binary files /dev/null and b/apps/landing/public/images/appgradientoverlay.png differ diff --git a/apps/landing/public/images/bento/encrypt-bg.webp b/apps/landing/public/images/bento/encrypt-bg.webp new file mode 100644 index 000000000..ffb88643c Binary files /dev/null and b/apps/landing/public/images/bento/encrypt-bg.webp differ diff --git a/apps/landing/public/images/bento/library.webp b/apps/landing/public/images/bento/library.webp new file mode 100644 index 000000000..bfa2c443d Binary files /dev/null and b/apps/landing/public/images/bento/library.webp differ diff --git a/apps/landing/public/images/bento/lock.webp b/apps/landing/public/images/bento/lock.webp new file mode 100644 index 000000000..c220d533c Binary files /dev/null and b/apps/landing/public/images/bento/lock.webp differ diff --git a/apps/landing/public/images/bento/opensource-bg.webp b/apps/landing/public/images/bento/opensource-bg.webp new file mode 100644 index 000000000..3f5c3ce7c Binary files /dev/null and b/apps/landing/public/images/bento/opensource-bg.webp differ diff --git a/apps/landing/public/images/bento/platforms.svg b/apps/landing/public/images/bento/platforms.svg new file mode 100644 index 000000000..cba28e956 --- /dev/null +++ b/apps/landing/public/images/bento/platforms.svg @@ -0,0 +1,525 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/landing/public/images/bento/platforms.webp b/apps/landing/public/images/bento/platforms.webp new file mode 100644 index 000000000..8f6064217 Binary files /dev/null and b/apps/landing/public/images/bento/platforms.webp differ diff --git a/apps/landing/public/images/bento/search-grid.webp b/apps/landing/public/images/bento/search-grid.webp new file mode 100644 index 000000000..f4c18f090 Binary files /dev/null and b/apps/landing/public/images/bento/search-grid.webp differ diff --git a/apps/landing/public/images/bento/search.webp b/apps/landing/public/images/bento/search.webp new file mode 100644 index 000000000..dfc582179 Binary files /dev/null and b/apps/landing/public/images/bento/search.webp differ diff --git a/apps/landing/public/images/bento/spacedrop.svg b/apps/landing/public/images/bento/spacedrop.svg new file mode 100644 index 000000000..15d50b715 --- /dev/null +++ b/apps/landing/public/images/bento/spacedrop.svg @@ -0,0 +1,611 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/landing/public/images/bento/spacedrop.webp b/apps/landing/public/images/bento/spacedrop.webp new file mode 100644 index 000000000..52f2ebb29 Binary files /dev/null and b/apps/landing/public/images/bento/spacedrop.webp differ diff --git a/apps/landing/public/images/bento/tags.webp b/apps/landing/public/images/bento/tags.webp new file mode 100644 index 000000000..a159870cf Binary files /dev/null and b/apps/landing/public/images/bento/tags.webp differ diff --git a/apps/landing/public/images/blueplanet.png b/apps/landing/public/images/blueplanet.png new file mode 100644 index 000000000..de91fadaa Binary files /dev/null and b/apps/landing/public/images/blueplanet.png differ diff --git a/apps/landing/public/images/cloudart.svg b/apps/landing/public/images/cloudart.svg new file mode 100644 index 000000000..24aa1eda0 --- /dev/null +++ b/apps/landing/public/images/cloudart.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/landing/public/images/cloudgradient.webp b/apps/landing/public/images/cloudgradient.webp new file mode 100644 index 000000000..d5267166a Binary files /dev/null and b/apps/landing/public/images/cloudgradient.webp differ diff --git a/apps/landing/public/images/cloudstorage.webp b/apps/landing/public/images/cloudstorage.webp new file mode 100644 index 000000000..596a21145 Binary files /dev/null and b/apps/landing/public/images/cloudstorage.webp differ diff --git a/apps/landing/public/images/contextmenu.png b/apps/landing/public/images/contextmenu.png new file mode 100644 index 000000000..c19dbd798 Binary files /dev/null and b/apps/landing/public/images/contextmenu.png differ diff --git a/apps/landing/public/images/dropbox.svg b/apps/landing/public/images/dropbox.svg new file mode 100644 index 000000000..84ac86026 --- /dev/null +++ b/apps/landing/public/images/dropbox.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/landing/public/images/explorer.png b/apps/landing/public/images/explorer.png new file mode 100644 index 000000000..f38f1b89b Binary files /dev/null and b/apps/landing/public/images/explorer.png differ diff --git a/apps/landing/public/images/footergradient.webp b/apps/landing/public/images/footergradient.webp new file mode 100644 index 000000000..23630dec1 Binary files /dev/null and b/apps/landing/public/images/footergradient.webp differ diff --git a/apps/landing/public/images/globe.webp b/apps/landing/public/images/globe.webp new file mode 100644 index 000000000..7284b8c91 Binary files /dev/null and b/apps/landing/public/images/globe.webp differ diff --git a/apps/landing/public/images/google-drive.svg b/apps/landing/public/images/google-drive.svg new file mode 100644 index 000000000..cdf499ffd --- /dev/null +++ b/apps/landing/public/images/google-drive.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/apps/landing/public/images/headergradient.webp b/apps/landing/public/images/headergradient.webp new file mode 100644 index 000000000..df38a4d6b Binary files /dev/null and b/apps/landing/public/images/headergradient.webp differ diff --git a/apps/landing/public/images/icloud.svg b/apps/landing/public/images/icloud.svg new file mode 100644 index 000000000..aa0a857a0 --- /dev/null +++ b/apps/landing/public/images/icloud.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/apps/landing/public/images/icongrid.png b/apps/landing/public/images/icongrid.png new file mode 100644 index 000000000..1afe9fe3e Binary files /dev/null and b/apps/landing/public/images/icongrid.png differ diff --git a/apps/landing/public/images/icons/application.svg b/apps/landing/public/images/icons/application.svg new file mode 100644 index 000000000..1a1118e9a --- /dev/null +++ b/apps/landing/public/images/icons/application.svg @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/landing/public/images/icons/collection.svg b/apps/landing/public/images/icons/collection.svg new file mode 100644 index 000000000..2f1f81e40 --- /dev/null +++ b/apps/landing/public/images/icons/collection.svg @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/landing/public/images/icons/database.svg b/apps/landing/public/images/icons/database.svg new file mode 100644 index 000000000..5b2a00d04 --- /dev/null +++ b/apps/landing/public/images/icons/database.svg @@ -0,0 +1,351 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/landing/public/images/icons/game.svg b/apps/landing/public/images/icons/game.svg new file mode 100644 index 000000000..90a8702ee --- /dev/null +++ b/apps/landing/public/images/icons/game.svg @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/landing/public/images/icons/heart.svg b/apps/landing/public/images/icons/heart.svg new file mode 100644 index 000000000..25821b126 --- /dev/null +++ b/apps/landing/public/images/icons/heart.svg @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/landing/public/images/icons/image.png b/apps/landing/public/images/icons/image.png new file mode 100644 index 000000000..4d1ceda55 Binary files /dev/null and b/apps/landing/public/images/icons/image.png differ diff --git a/apps/landing/public/images/icons/image.svg b/apps/landing/public/images/icons/image.svg new file mode 100644 index 000000000..d3e5c818c --- /dev/null +++ b/apps/landing/public/images/icons/image.svg @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/landing/public/images/icons/lock.svg b/apps/landing/public/images/icons/lock.svg new file mode 100644 index 000000000..0b798190b --- /dev/null +++ b/apps/landing/public/images/icons/lock.svg @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/landing/public/images/icons/node.svg b/apps/landing/public/images/icons/node.svg new file mode 100644 index 000000000..b48ae6a8b --- /dev/null +++ b/apps/landing/public/images/icons/node.svg @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/landing/public/images/icons/package.svg b/apps/landing/public/images/icons/package.svg new file mode 100644 index 000000000..1cda2f275 --- /dev/null +++ b/apps/landing/public/images/icons/package.svg @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/landing/public/images/icons/texturedmesh.png b/apps/landing/public/images/icons/texturedmesh.png new file mode 100644 index 000000000..24a9915df Binary files /dev/null and b/apps/landing/public/images/icons/texturedmesh.png differ diff --git a/apps/landing/public/images/icons/video.svg b/apps/landing/public/images/icons/video.svg new file mode 100644 index 000000000..b5344431d --- /dev/null +++ b/apps/landing/public/images/icons/video.svg @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/landing/public/images/june-23-alpha-release.png b/apps/landing/public/images/june-23-alpha-release.png new file mode 100644 index 000000000..d3e4fe17d Binary files /dev/null and b/apps/landing/public/images/june-23-alpha-release.png differ diff --git a/apps/landing/public/images/mega.svg b/apps/landing/public/images/mega.svg new file mode 100644 index 000000000..6efb1249b --- /dev/null +++ b/apps/landing/public/images/mega.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/landing/public/images/october-23-alpha-release.png b/apps/landing/public/images/october-23-alpha-release.png new file mode 100644 index 000000000..f0eb2d60c Binary files /dev/null and b/apps/landing/public/images/october-23-alpha-release.png differ diff --git a/apps/landing/public/images/outlook.svg b/apps/landing/public/images/outlook.svg new file mode 100644 index 000000000..6f49cd96e --- /dev/null +++ b/apps/landing/public/images/outlook.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/landing/public/images/quickpreview.png b/apps/landing/public/images/quickpreview.png new file mode 100644 index 000000000..7ea01c048 Binary files /dev/null and b/apps/landing/public/images/quickpreview.png differ diff --git a/apps/landing/public/images/s3.svg b/apps/landing/public/images/s3.svg new file mode 100644 index 000000000..3f63be51f --- /dev/null +++ b/apps/landing/public/images/s3.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + diff --git a/apps/landing/public/images/stars.webp b/apps/landing/public/images/stars.webp new file mode 100644 index 000000000..2edfae855 Binary files /dev/null and b/apps/landing/public/images/stars.webp differ diff --git a/apps/landing/public/images/wormhole.webp b/apps/landing/public/images/wormhole.webp new file mode 100644 index 000000000..ce4995dba Binary files /dev/null and b/apps/landing/public/images/wormhole.webp differ diff --git a/apps/landing/public/introduction.webp b/apps/landing/public/introduction.webp new file mode 100644 index 000000000..20b307aee Binary files /dev/null and b/apps/landing/public/introduction.webp differ diff --git a/apps/landing/src/app/api/releases/desktop/[version]/[target]/[arch]/route.ts b/apps/landing/src/app/api/releases/desktop/[version]/[target]/[arch]/route.ts new file mode 100644 index 000000000..aaa28cda9 --- /dev/null +++ b/apps/landing/src/app/api/releases/desktop/[version]/[target]/[arch]/route.ts @@ -0,0 +1,74 @@ +import { NextResponse } from 'next/server'; +import { z } from 'zod'; +import { env } from '~/env'; + +const version = z.union([z.literal('stable'), z.literal('alpha')]); +const tauriTarget = z.union([z.literal('linux'), z.literal('windows'), z.literal('darwin')]); +const tauriArch = z.union([z.literal('x86_64'), z.literal('aarch64')]); + +const paramsSchema = z.object({ + target: tauriTarget, + arch: tauriArch, + version: version.or(z.string()) +}); + +export const runtime = 'edge'; + +export async function GET( + _: Request, + { + params: rawParams + }: { + params: { + version: string; + target: string; + arch: string; + }; + } +) { + const params = await paramsSchema.parseAsync(rawParams); + + const release = await getRelease(params); + + if (!release) return NextResponse.json({ error: 'Release not found' }, { status: 404 }); + + params.version = release.tag_name; + + const asset = release.assets.find(({ name }: any) => + name.startsWith(`Spacedrive-${params.target}-${params.arch}`) + ); + + if (!asset) return NextResponse.json({ error: 'Asset not found' }, { status: 404 }); + + return NextResponse.redirect(asset.browser_download_url); +} + +async function getRelease({ version }: z.infer): Promise { + switch (version) { + case 'alpha': { + const data = await githubFetch(`/repos/${env.GITHUB_ORG}/${env.GITHUB_REPO}/releases`); + + return data.find((d: any) => d.tag_name.includes('alpha')); + } + case 'stable': + return githubFetch(`/repos/${env.GITHUB_ORG}/${env.GITHUB_REPO}/releases/latest`); + default: + return githubFetch( + `/repos/$${env.GITHUB_ORG}/${env.GITHUB_REPO}/releases/tags/${version}` + ); + } +} + +const FETCH_META = { + headers: { + Authorization: `Bearer ${env.GITHUB_PAT}`, + Accept: 'application/vnd.github+json' + }, + next: { + revalidate: 60 + } +} as RequestInit; + +async function githubFetch(path: string) { + return fetch(`https://api.github.com${path}`, FETCH_META).then((r) => r.json()); +} diff --git a/apps/landing/src/components/AccessData.tsx b/apps/landing/src/components/AccessData.tsx new file mode 100644 index 000000000..adf0fd18d --- /dev/null +++ b/apps/landing/src/components/AccessData.tsx @@ -0,0 +1,32 @@ +import Image from 'next/image'; +import React from 'react'; + +const AccessData = () => { + return ( +
+ data globe +
+

+ Access data from anywhere +

+

+ users can enjoy the freedom of accessing their important files, documents, and + media assets from any device with an internet connection, ensuring productivity + and convenience on the go. +

+
+
+ ); +}; + +export default AccessData; diff --git a/apps/landing/src/components/AppEmbed.tsx b/apps/landing/src/components/AppEmbed.tsx new file mode 100644 index 000000000..86bc1189b --- /dev/null +++ b/apps/landing/src/components/AppEmbed.tsx @@ -0,0 +1,82 @@ +import clsx from 'clsx'; +import { useEffect, useRef, useState } from 'react'; +import { getWindow } from '~/utils/util'; + +const AppEmbed = () => { + const [showApp, setShowApp] = useState(false); + const [iFrameAppReady, setIframeAppReady] = useState(false); + const [forceImg, setForceImg] = useState(false); + const [imgFallback, setImageFallback] = useState(false); + // const iFrame = useRef(null); + const window = getWindow()!; + + // useEffect(() => { + // function handleResize() { + // if (window.innerWidth < 1000) { + // setForceImg(true); + // } else if (forceImg) { + // setForceImg(false); + // } + // } + // window.addEventListener('resize', handleResize); + // handleResize(); + // return () => window.removeEventListener('resize', handleResize); + // }, [forceImg, window]); + + // after five minutes kill the live demo + // useEffect(() => { + // const timer = setTimeout(() => { + // setIframeAppReady(false); + // }, 300000); + // return () => clearTimeout(timer); + // }, []); + + // useEffect(() => { + // function handleEvent(e: any) { + // if (e.data === 'spacedrive-hello') { + // if (!iFrameAppReady) setIframeAppReady(true); + // } + // } + // window.addEventListener('message', handleEvent, false); + // setShowApp(true); + + // return () => window.removeEventListener('message', handleEvent); + // }, [iFrameAppReady, window]); + + // useEffect(() => { + // setTimeout(() => { + // if (!iFrameAppReady) setImageFallback(true); + // }, 1500); + // }, [iFrameAppReady]); + + const renderImage = (imgFallback && !iFrameAppReady) || forceImg; + + const renderBloom = renderImage || iFrameAppReady; + + return ( +
+ {/* {renderBloom && ( */} +
+
+
+
+
+ {/* )} */} +
+
+
+ +
+
+
+
+ ); +}; + +export const AppEmbedPlaceholder = () => { + return ( +
+ ); +}; + +export default AppEmbed; diff --git a/apps/landing/src/components/BentoBoxes.tsx b/apps/landing/src/components/BentoBoxes.tsx new file mode 100644 index 000000000..17ca6167c --- /dev/null +++ b/apps/landing/src/components/BentoBoxes.tsx @@ -0,0 +1,219 @@ +import dynamic from 'next/dynamic'; +import Image from 'next/image'; +import Link from 'next/link'; +import { useMemo } from 'react'; +import { Button, tw } from '@sd/ui'; +import { useWindowSize } from '~/hooks/useWindowSize'; +import { MagicCard, MagicContainer } from './MagicCard'; +import PlatformsArt from './PlatformsArt'; +import SpacedropArt from './SpacedropArt'; + +const Heading = tw.h1`z-30 text-center font-semibold leading-tight text-white text-lg`; +const Text = tw.p`leading-2 text-zinc-500 z-30 mb-8 mt-1 max-w-4xl text-center text-[14px] lg:leading-8"`; + +interface Props { + rowSpan?: number; + colSpan?: number; + className?: string; + bgUrl?: string; + children?: React.ReactNode; +} + +const BentoBox = ({ rowSpan = 1, colSpan = 1, className = '', children, bgUrl = '' }: Props) => ( +
+ {children} +
+); + +// const AppFrameOuter = tw.div`relative m-auto flex w-full max-w-7xl rounded-lg border border-black transition-opacity`; +// const AppFrameInner = tw.div`z-30 flex w-full rounded-lg border-t border-app-line/50 bg-app/30 backdrop-blur`; + +const GitHubButton = dynamic(() => import('react-github-btn'), { ssr: false }); + +const BentoBoxes = () => { + const { width } = useWindowSize(); + const particleCount = useMemo(() => { + if (width) { + return width > 768 ? 50 : 25; + } + return 50; + }, [width]); + return ( + + +
+
+ Encryption + + Your files and folders are fully encrypted through our algorithm, preventing + unauthorized access and guaranteed protection. + +
+
+ Encryption +
+ + +
+ Powerful tags +
+
+ +
+ Powerful tags + + Create and apply tags to your files and folders, and instantly locate + desired content through filterable tags. + +
+ + +
+ Search everything + + Easily find your files and folders through our search + +
+
+
+ Search +
+ + +
+
+ Library +
+
+ Full Ownership & Control + Make Spacedrive yours +
+ + +
+ Spacedrop + + Send files to other devices quickly and easily + +
+
+
+
+ +
+
+ + +
+ Free & Opensource + + Developers and users can contribute with new ideas and features + +
+
+
+ + + + + Star + +
+ + +
+ +
+
+
+ Cross platform + + Windows, macOS, Linux, iOS, Android, and the web. Spacedrive is everywhere. + +
+
+ + + ); +}; + +export default BentoBoxes; diff --git a/apps/landing/src/components/CloudStorage.tsx b/apps/landing/src/components/CloudStorage.tsx new file mode 100644 index 000000000..cd12f6685 --- /dev/null +++ b/apps/landing/src/components/CloudStorage.tsx @@ -0,0 +1,92 @@ +import clsx from 'clsx'; +import { useInView } from 'framer-motion'; +import Image from 'next/image'; +import React, { useRef } from 'react'; +import CloudStorageArt from './CloudStorageArt'; + +const CloudStorage = () => { + const ref = useRef(null); + const isInView = useInView(ref, { + amount: 0.5, + once: true + }); + + return ( +
+
+
+ +
+ cloud gradient +
+
+ + + +
+ + +
+
+

+ Coming soon +

+

+ Combine all storage locations & clouds +

+
+ ); +}; + +interface Props { + logoUrl: string; + title: string; + imageWidth?: number; + children?: React.ReactNode; +} + +const CloudCard = ({ logoUrl, title, imageWidth = 70, children }: Props) => { + return ( +
+ {children} +
+ cloud storage +

{title}

+
+
+ ); +}; + +export default CloudStorage; diff --git a/apps/landing/src/components/CloudStorageArt.tsx b/apps/landing/src/components/CloudStorageArt.tsx new file mode 100644 index 000000000..870c53c30 --- /dev/null +++ b/apps/landing/src/components/CloudStorageArt.tsx @@ -0,0 +1,126 @@ +const CloudStorageArt = () => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default CloudStorageArt; diff --git a/apps/landing/src/components/CyclingImage.tsx b/apps/landing/src/components/CyclingImage.tsx new file mode 100644 index 000000000..19c3ecb31 --- /dev/null +++ b/apps/landing/src/components/CyclingImage.tsx @@ -0,0 +1,47 @@ +import Image, { ImageProps } from 'next/image'; +import React, { useEffect, useState } from 'react'; + +interface CyclingImageProps extends Omit { + images: string[]; +} + +const CyclingImage: React.FC = ({ images, width, height, ...imgProps }) => { + const [currentIndex, setCurrentIndex] = useState(0); + const [isHovering, setIsHovering] = useState(false); + + console.log({ isHovering }); + + useEffect(() => { + let timeoutId: number; + if (isHovering && images.length > 1) { + const nextIndex = (currentIndex + 1) % images.length; + const img = new window.Image(); + img.src = images[nextIndex]; + img.onload = () => { + timeoutId = window.setTimeout(() => setCurrentIndex(nextIndex), 1500); + }; + } + return () => window.clearTimeout(timeoutId); + }, [isHovering, currentIndex, images]); + + return ( +
setIsHovering(true)} onMouseLeave={() => setIsHovering(false)}> + {images.map((src, index) => ( +
+ +
+ ))} +
+ ); +}; + +export default CyclingImage; diff --git a/apps/landing/src/components/DocsLayout.tsx b/apps/landing/src/components/DocsLayout.tsx index 6931b64c7..9812a6c02 100644 --- a/apps/landing/src/components/DocsLayout.tsx +++ b/apps/landing/src/components/DocsLayout.tsx @@ -1,5 +1,4 @@ import { CaretRight, List, X } from '@phosphor-icons/react'; -import Link from 'next/link'; import { PropsWithChildren, useState } from 'react'; import { slide as Menu } from 'react-burger-menu'; import { Button } from '@sd/ui'; diff --git a/apps/landing/src/components/DotPattern.tsx b/apps/landing/src/components/DotPattern.tsx new file mode 100644 index 000000000..91ff7c1e0 --- /dev/null +++ b/apps/landing/src/components/DotPattern.tsx @@ -0,0 +1,52 @@ +import { useId } from 'react'; +import { cn } from './MagicCard'; + +interface DotPatternProps { + width?: any; + height?: any; + x?: any; + y?: any; + cx?: any; + cy?: any; + cr?: any; + className?: string; + [key: string]: any; +} +export function DotPattern({ + width = 16, + height = 16, + x = 0, + y = 0, + cx = 1, + cy = 1, + cr = 1, + className, + ...props +}: DotPatternProps) { + const id = useId(); + + return ( + + ); +} + +export default DotPattern; diff --git a/apps/landing/src/components/DownloadToday.tsx b/apps/landing/src/components/DownloadToday.tsx new file mode 100644 index 000000000..495b64d87 --- /dev/null +++ b/apps/landing/src/components/DownloadToday.tsx @@ -0,0 +1,39 @@ +import clsx from 'clsx'; +import { useInView } from 'framer-motion'; +import { Download } from '@phosphor-icons/react'; +import React, { useRef } from 'react'; +import { Button } from '@sd/ui'; + +interface Props { + isWindows?: boolean; +} + +const DownloadToday = ({ isWindows }: Props) => { + const ref = useRef(null); + const isInView = useInView(ref, { + amount: 0.5, + once: true + }); + return ( +
+
+

+ Ready to get organized? +

+ +
+
+ ); +}; + +export default DownloadToday; diff --git a/apps/landing/src/components/Footer.tsx b/apps/landing/src/components/Footer.tsx index 9d3933c7f..0925a5df0 100644 --- a/apps/landing/src/components/Footer.tsx +++ b/apps/landing/src/components/Footer.tsx @@ -19,6 +19,7 @@ function FooterLink(props: PropsWithChildren<{ link: string; blank?: boolean }>) target={props.blank ? '_blank' : ''} className="text-gray-300 duration-300 hover:text-white hover:opacity-50" rel="noreferrer" + {...props} > {props.children} @@ -27,12 +28,22 @@ function FooterLink(props: PropsWithChildren<{ link: string; blank?: boolean }>) export function Footer() { return ( -
+
+ footer gradient
Spacedrive logo -

Spacedrive

+

Spacedrive

© Copyright {new Date().getFullYear()} Spacedrive Technology Inc.

@@ -40,26 +51,35 @@ export function Footer() { - + - + - + - + - +
-

About

+

About

Team FAQ @@ -68,7 +88,7 @@ export function Footer() { Blog
-

Downloads

+

Downloads

macOS Windows @@ -78,7 +98,7 @@ export function Footer() {
-

Developers

+

Developers

Documentation @@ -96,7 +116,7 @@ export function Footer() {
-

Org

+

Org

Open Collective @@ -114,6 +134,10 @@ export function Footer() {
+
+
+
+
); } diff --git a/apps/landing/src/components/HomeCTA.tsx b/apps/landing/src/components/HomeCTA.tsx index ea7f81718..5bf4167ce 100644 --- a/apps/landing/src/components/HomeCTA.tsx +++ b/apps/landing/src/components/HomeCTA.tsx @@ -1,177 +1,29 @@ -import { ReactComponent as Alert } from '@sd/assets/svgs/alert.svg'; -import { Github } from '@sd/assets/svgs/brands'; -import { ReactComponent as Info } from '@sd/assets/svgs/info.svg'; -import { ReactComponent as Spinner } from '@sd/assets/svgs/spinner.svg'; +import { IconProps } from '@phosphor-icons/react'; import clsx from 'clsx'; -import { useState } from 'react'; -import { useForm } from 'react-hook-form'; -import { Button, Input } from '@sd/ui'; +import { Button, ButtonBaseProps } from '@sd/ui'; -interface WaitlistInputs { - email: string; +interface Props extends ButtonBaseProps { + className?: string; + text: string; + icon?: IconProps; + onClick?: () => void; } -export function HomeCTA() { - const { register, handleSubmit } = useForm(); - - const [loading, setLoading] = useState(false); - const [showWaitlistInput, setShowWaitlistInput] = useState(false); - const [waitlistError, setWaitlistError] = useState(''); - const [waitlistSubmitted, setWaitlistSubmitted] = useState(false); - - async function handleWaitlistSubmit({ email }: WaitlistInputs) { - if (!email.trim().length) return; - - setLoading(true); - - try { - const req = await fetch(`https://app.spacedrive.com/api/v1/waitlist`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ email }) - }); - - if (!req.ok) { - return setWaitlistError('An error occurred. Please try again.'); - } - - const response = (await req.json()) as { success: boolean; message: string }; - - if (!response.success) { - return setWaitlistError(response.message); - } - setWaitlistSubmitted(true); - } catch (e: any) { - throw new Error(e.message); - } finally { - setLoading(false); - } - // Remove error after a few seconds - setTimeout(() => { - setWaitlistError(''); - }, 5000); - } - +export function HomeCTA({ className, text, icon, ...props }: Props) { return ( - <> -
- {!showWaitlistInput ? ( - <> - - - - ) : ( -
-
- {(waitlistError || waitlistSubmitted) && ( -
- {waitlistError ? ( - - ) : ( - - )} -

- {waitlistError || 'You have been added to the waitlist'} -

-
- )} -
- - {!waitlistSubmitted && ( - - )} -
-
-
- )} -
-

- {showWaitlistInput ? ( - <> - We'll keep your place in the queue for early access. -
-
- - ) : waitlistSubmitted ? ( - <> - You have been added to the waitlist. -
-
- - ) : ( - <> - Coming soon on macOS, Windows and Linux. -
- Shortly after to iOS & Android. - - )} -

- + ); } diff --git a/apps/landing/src/components/MagicCard.tsx b/apps/landing/src/components/MagicCard.tsx new file mode 100644 index 000000000..e839ca6f7 --- /dev/null +++ b/apps/landing/src/components/MagicCard.tsx @@ -0,0 +1,246 @@ +'use client'; + +import clsx, { ClassValue } from 'clsx'; +import { CSSProperties, ReactNode, useEffect, useRef, useState } from 'react'; +import { twMerge } from 'tailwind-merge'; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} + +interface MousePosition { + x: number; + y: number; +} + +function useMousePosition(): MousePosition { + const [mousePosition, setMousePosition] = useState({ + x: 0, + y: 0 + }); + + useEffect(() => { + const handleMouseMove = (event: globalThis.MouseEvent) => { + setMousePosition({ x: event.clientX, y: event.clientY }); + }; + + window.addEventListener('mousemove', handleMouseMove); + + return () => { + window.removeEventListener('mousemove', handleMouseMove); + }; + }, []); + + return mousePosition; +} + +interface MagicContainerProps { + children?: ReactNode; + className?: any; + style?: CSSProperties; +} + +const MagicContainer = ({ children, className, style }: MagicContainerProps) => { + const containerRef = useRef(null); + const mousePosition = useMousePosition(); + const mouse = useRef<{ x: number; y: number }>({ x: 0, y: 0 }); + const containerSize = useRef<{ w: number; h: number }>({ w: 0, h: 0 }); + const [boxes, setBoxes] = useState>([]); + + useEffect(() => { + init(); + containerRef.current && + setBoxes(Array.from(containerRef.current.children).map((el) => el as HTMLElement)); + }, []); + + useEffect(() => { + init(); + window.addEventListener('resize', init); + + return () => { + window.removeEventListener('resize', init); + }; + }, [setBoxes]); + + useEffect(() => { + onMouseMove(); + }, [mousePosition]); + + const init = () => { + if (containerRef.current) { + containerSize.current.w = containerRef.current.offsetWidth; + containerSize.current.h = containerRef.current.offsetHeight; + } + }; + + const onMouseMove = () => { + if (containerRef.current) { + const rect = containerRef.current.getBoundingClientRect(); + const { w, h } = containerSize.current; + const x = mousePosition.x - rect.left; + const y = mousePosition.y - rect.top; + const inside = x < w && x > 0 && y < h && y > 0; + + mouse.current.x = x; + mouse.current.y = y; + boxes.forEach((box) => { + const boxX = -(box.getBoundingClientRect().left - rect.left) + mouse.current.x; + const boxY = -(box.getBoundingClientRect().top - rect.top) + mouse.current.y; + box.style.setProperty('--mouse-x', `${boxX}px`); + box.style.setProperty('--mouse-y', `${boxY}px`); + + if (inside) { + box.style.setProperty('--opacity', `1`); + } else { + box.style.setProperty('--opacity', `0`); + } + }); + } + }; + + return ( +
+ {children} +
+ ); +}; + +interface MagicCardProps { + /** + * @default div + * @type React.ElementType + * @description + * The component to render the card as + * */ + as?: React.ElementType; + /** + * @default "" + * @type string + * @description + * The className of the card + */ + className?: string; + + /** + * @default "" + * @type ReactNode + * @description + * The children of the card + * */ + children?: ReactNode; + + /** + * @default 600 + * @type number + * @description + * The size of the spotlight effect in pixels + * */ + size?: number; + + /** + * ]@default "#475569" + * @type string + * @description + * The border color of the card + */ + borderColor?: string; + + /** + * @default 1 + * @type number + * @description + * The border width of the card + * */ + borderWidth?: number; + + /** + * @default 16 + * @type number + * @description + * The border radius of the card + * */ + borderRadius?: number; + + /** + * @default true + * @type boolean + * @description + * Whether to show the spotlight + * */ + spotlight?: boolean; + + /** + * @default "rgba(255,255,255,0.03)" + * @type string + * @description + * The color of the spotlight + * */ + spotlightColor?: string; + + /** + * @default true + * @type boolean + * @description + * Whether to isolate the card which is being hovered + * */ + isolated?: boolean; + + /** + * @default "transparent" + * @type string + * @description + * The background of the card + * */ + background?: string; +} + +const MagicCard = ({ + as: Component = 'div', + className, + children, + size = 600, + borderColor = 'rgba(86,114,157, 0.2)', + borderWidth = 1, + borderRadius = 10, + spotlight = true, + spotlightColor = 'rgba(86,114,157, 0.01)', + isolated = false, + background = 'rgba(255,255,255,0.03)' +}: MagicCardProps) => { + const spotlightStyles = + 'before:pointer-events-none before:absolute before:w-full before:h-full before:rounded-[var(--border-radius)] before:top-0 before:left-0 before:duration-500 before:transition-opacity before:bg-[radial-gradient(var(--mask-size)_circle_at_var(--mouse-x)_var(--mouse-y),var(--spotlight-color),transparent_40%)] before:z-[3] before:blur-xs'; + + const borderStyles = + 'after:pointer-events-none after:absolute after:w-full after:h-full after:rounded-[var(--border-radius)] after:top-0 after:left-0 after:duration-500 after:transition-opacity after:bg-[radial-gradient(var(--mask-size)_circle_at_var(--mouse-x)_var(--mouse-y),var(--border-color),transparent_40%)] after:z-[1]'; + return ( + +
+ {children} +
+
+ ); +}; + +export { MagicCard, MagicContainer }; diff --git a/apps/landing/src/components/NavBar.tsx b/apps/landing/src/components/NavBar.tsx index 2957b935d..0014aed9c 100644 --- a/apps/landing/src/components/NavBar.tsx +++ b/apps/landing/src/components/NavBar.tsx @@ -16,7 +16,7 @@ function NavLink(props: PropsWithChildren<{ link?: string }>) { {props.children} @@ -58,12 +58,7 @@ export default function NavBar() { }, []); return ( -
+
Spacedrive logo @@ -73,6 +68,7 @@ export default function NavBar() {
Roadmap Team + {/* Pricing */} Blog Docs
@@ -87,7 +83,11 @@ export default function NavBar() {
+ } @@ -115,6 +115,9 @@ export default function NavBar() { Team + {/* + Pricing + */} Blog @@ -136,10 +139,16 @@ export default function NavBar() {
- +
-
-
+
+
); diff --git a/apps/landing/src/components/NewBanner.tsx b/apps/landing/src/components/NewBanner.tsx index a6f49ff9e..408368874 100644 --- a/apps/landing/src/components/NewBanner.tsx +++ b/apps/landing/src/components/NewBanner.tsx @@ -1,9 +1,12 @@ +import clsx from 'clsx'; import Link from 'next/link'; +import { Newspaper } from '@phosphor-icons/react'; export interface NewBannerProps { headline: string; href: string; link: string; + className?: string; } const NewBanner: React.FC = (props) => { @@ -12,13 +15,19 @@ const NewBanner: React.FC = (props) => { return (