Initial website

This commit is contained in:
continuist 2025-09-26 01:12:03 -04:00
parent c66119cfe6
commit 6b87400334
28 changed files with 8862 additions and 0 deletions

View file

41
frontend/.gitignore vendored Normal file
View file

@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

36
frontend/README.md Normal file
View file

@ -0,0 +1,36 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.

22
frontend/components.json Normal file
View file

@ -0,0 +1,22 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/app/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"registries": {}
}

View file

@ -0,0 +1,25 @@
import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
});
const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
{
ignores: [
"node_modules/**",
".next/**",
"out/**",
"build/**",
"next-env.d.ts",
],
},
];
export default eslintConfig;

7
frontend/next.config.ts Normal file
View file

@ -0,0 +1,7 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
};
export default nextConfig;

7661
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

37
frontend/package.json Normal file
View file

@ -0,0 +1,37 @@
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"build": "next build --turbopack",
"start": "next start",
"lint": "eslint"
},
"dependencies": {
"@radix-ui/react-slot": "^1.2.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.544.0",
"next": "15.5.4",
"react": "19.1.0",
"react-dom": "19.1.0",
"react-markdown": "^10.1.0",
"remark": "^15.0.1",
"remark-gfm": "^4.0.1",
"remark-parse": "^11.0.0",
"tailwind-merge": "^3.3.1"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "15.5.4",
"tailwindcss": "^4",
"tw-animate-css": "^1.3.8",
"typescript": "^5"
}
}

View file

@ -0,0 +1,5 @@
const config = {
plugins: ["@tailwindcss/postcss"],
};
export default config;

1
frontend/public/file.svg Normal file
View file

@ -0,0 +1 @@
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 391 B

View file

@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 1 KiB

1
frontend/public/next.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 128 B

View file

@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>

After

Width:  |  Height:  |  Size: 385 B

View file

@ -0,0 +1,148 @@
import { Navigation } from '@/components/navigation'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { ArrowLeft, BookOpen, Code, Users, Shield } from 'lucide-react'
import Link from 'next/link'
export default function DocsPage() {
return (
<div className="min-h-screen bg-gradient-to-br from-background via-background to-orange-50/30">
{/* Navigation */}
<Navigation />
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
{/* Header */}
<div className="mb-8">
<Link href="/">
<Button variant="ghost" className="mb-4">
<ArrowLeft className="h-4 w-4 mr-2" />
Back to Home
</Button>
</Link>
<h1 className="text-4xl md:text-6xl font-black text-foreground mb-4 space-age-glow inline-block px-6 py-3 rounded-lg">
DOCUMENTATION
</h1>
<p className="text-lg text-muted-foreground font-mono">
Complete guide to Sharenet - from installation to advanced usage
</p>
</div>
{/* Documentation Sections */}
<div className="grid md:grid-cols-2 gap-8">
{/* Getting Started */}
<Card className="border-2 border-primary/20 bg-card/50 hover:space-age-glow transition-all">
<CardHeader>
<CardTitle className="flex items-center text-foreground">
<BookOpen className="h-6 w-6 mr-2 text-primary" />
Getting Started
</CardTitle>
</CardHeader>
<CardContent>
<ul className="space-y-2 text-muted-foreground">
<li> Quick installation guide</li>
<li> Setting up your first node</li>
<li> Basic configuration</li>
<li> Joining the network</li>
</ul>
<Button className="mt-4 w-full" variant="outline">
Read Guide
</Button>
</CardContent>
</Card>
{/* API Reference */}
<Card className="border-2 border-primary/20 bg-card/50 hover:space-age-glow transition-all">
<CardHeader>
<CardTitle className="flex items-center text-foreground">
<Code className="h-6 w-6 mr-2 text-primary" />
API Reference
</CardTitle>
</CardHeader>
<CardContent>
<ul className="space-y-2 text-muted-foreground">
<li> REST API endpoints</li>
<li> WebSocket connections</li>
<li> Authentication methods</li>
<li> Rate limiting</li>
</ul>
<Button className="mt-4 w-full" variant="outline">
View API
</Button>
</CardContent>
</Card>
{/* Community Guide */}
<Card className="border-2 border-primary/20 bg-card/50 hover:space-age-glow transition-all">
<CardHeader>
<CardTitle className="flex items-center text-foreground">
<Users className="h-6 w-6 mr-2 text-primary" />
Community Guide
</CardTitle>
</CardHeader>
<CardContent>
<ul className="space-y-2 text-muted-foreground">
<li> Contributing guidelines</li>
<li> Code of conduct</li>
<li> Community forums</li>
<li> Getting help</li>
</ul>
<Button className="mt-4 w-full" variant="outline">
Join Community
</Button>
</CardContent>
</Card>
{/* SPL License */}
<Card className="border-2 border-primary/20 bg-card/50 hover:space-age-glow transition-all">
<CardHeader>
<CardTitle className="flex items-center text-foreground">
<Shield className="h-6 w-6 mr-2 text-primary" />
Sharenet Public License
</CardTitle>
</CardHeader>
<CardContent>
<ul className="space-y-2 text-muted-foreground">
<li> Full license text</li>
<li> Usage restrictions</li>
<li> Commercial prohibition</li>
<li> Modification rights</li>
</ul>
<Button className="mt-4 w-full" variant="outline">
Read License
</Button>
</CardContent>
</Card>
</div>
{/* Quick Start Section */}
<Card className="mt-12 border-2 border-primary/20 bg-card/50">
<CardHeader>
<CardTitle className="text-2xl text-foreground">Quick Start</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div>
<h3 className="font-bold text-foreground mb-2">1. Install Sharenet</h3>
<code className="block bg-muted px-4 py-2 rounded font-mono text-sm terminal-border">
curl -s https://sharenet.org/install.sh | bash
</code>
</div>
<div>
<h3 className="font-bold text-foreground mb-2">2. Configure your node</h3>
<code className="block bg-muted px-4 py-2 rounded font-mono text-sm terminal-border">
sharenet config init
</code>
</div>
<div>
<h3 className="font-bold text-foreground mb-2">3. Start the service</h3>
<code className="block bg-muted px-4 py-2 rounded font-mono text-sm terminal-border">
sharenet start
</code>
</div>
</div>
</CardContent>
</Card>
</div>
</div>
)
}

View file

@ -0,0 +1,83 @@
import { Navigation } from '@/components/navigation'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { ArrowLeft, HelpCircle } from 'lucide-react'
import Link from 'next/link'
import ReactMarkdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
import fs from 'fs'
import path from 'path'
async function getFAQContent() {
try {
const filePath = path.join(process.cwd(), 'src', 'content', 'faq.md')
const content = fs.readFileSync(filePath, 'utf8')
return content
} catch (error) {
console.error('Error reading FAQ content:', error)
return '# FAQ Content Not Available\n\nPlease check the FAQ markdown file.'
}
}
function parseFAQContent(content: string) {
// Split by '## ' and preserve the exact Markdown structure
const sections = content.split(/## (?=\S)/).slice(1) // Skip the first section (title)
return sections.map(section => {
const lines = section.split('\n')
const title = lines[0].trim()
// Preserve all content including blank lines and formatting
const answer = lines.slice(1).join('\n').trim()
return { title, answer }
})
}
export default async function FAQPage() {
const faqContent = await getFAQContent()
const questions = parseFAQContent(faqContent)
return (
<div className="min-h-screen bg-gradient-to-br from-background via-background to-orange-50/30">
{/* Navigation */}
<Navigation />
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
{/* Header */}
<div className="mb-8">
<Link href="/">
<Button variant="ghost" className="mb-4">
<ArrowLeft className="h-4 w-4 mr-2" />
Back to Home
</Button>
</Link>
<h1 className="text-4xl md:text-6xl font-black text-foreground mb-4 space-age-glow inline-block px-6 py-3 rounded-lg">
FREQUENTLY ASKED QUESTIONS
</h1>
<p className="text-lg text-muted-foreground font-mono">
Common questions about Sharenet and its usage
</p>
</div>
{/* FAQ Section */}
<div className="space-y-6">
{questions.map((question, index) => (
<Card key={index} className="border-2 border-primary/20 bg-card/50 hover:space-age-glow transition-all">
<CardHeader>
<CardTitle className="flex items-center text-foreground">
<HelpCircle className="h-6 w-6 mr-2 text-primary" />
{question.title}
</CardTitle>
</CardHeader>
<CardContent>
<div className="text-muted-foreground prose prose-orange max-w-none">
<ReactMarkdown remarkPlugins={[remarkGfm]}>{question.answer}</ReactMarkdown>
</div>
</CardContent>
</Card>
))}
</div>
</div>
</div>
)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View file

@ -0,0 +1,185 @@
@import url('https://fonts.googleapis.com/css2?family=Ubuntu:wght@300;400;500;700&family=Ubuntu+Mono&display=swap');
@import "tailwindcss";
@import "tw-animate-css";
@custom-variant dark (&:is(.dark *));
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: 'Ubuntu', sans-serif;
--font-mono: 'Ubuntu Mono', monospace;
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar: var(--sidebar);
--color-chart-5: var(--chart-5);
--color-chart-4: var(--chart-4);
--color-chart-3: var(--chart-3);
--color-chart-2: var(--chart-2);
--color-chart-1: var(--chart-1);
--color-ring: var(--ring);
--color-input: var(--input);
--color-border: var(--border);
--color-destructive: var(--destructive);
--color-accent-foreground: var(--accent-foreground);
--color-accent: var(--accent);
--color-muted-foreground: var(--muted-foreground);
--color-muted: var(--muted);
--color-secondary-foreground: var(--secondary-foreground);
--color-secondary: var(--secondary);
--color-primary-foreground: var(--primary-foreground);
--color-primary: var(--primary);
--color-popover-foreground: var(--popover-foreground);
--color-popover: var(--popover);
--color-card-foreground: var(--card-foreground);
--color-card: var(--card);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
}
:root {
--radius: 0.625rem;
--background: #fff9eb;
--foreground: #c03012;
--card: #fff9eb;
--card-foreground: #c03012;
--popover: #fff9eb;
--popover-foreground: #c03012;
--primary: #f4840a;
--primary-foreground: #fff9eb;
--secondary: #ed650b;
--secondary-foreground: #fff9eb;
--muted: #fdf4e3;
--muted-foreground: #a82a0f;
--accent: #ed650b;
--accent-foreground: #fff9eb;
--destructive: #c03012;
--border: #f4840a;
--input: #fdf4e3;
--ring: #ed650b;
--chart-1: #f4840a;
--chart-2: #ed650b;
--chart-3: #c03012;
--chart-4: #a82a0f;
--chart-5: #fff9eb;
--sidebar: #fff9eb;
--sidebar-foreground: #c03012;
--sidebar-primary: #f4840a;
--sidebar-primary-foreground: #fff9eb;
--sidebar-accent: #ed650b;
--sidebar-accent-foreground: #fff9eb;
--sidebar-border: #f4840a;
--sidebar-ring: #ed650b;
}
.dark {
--background: #2a2214;
--foreground: #f4840a;
--card: #3a3224;
--card-foreground: #f4840a;
--popover: #3a3224;
--popover-foreground: #f4840a;
--primary: #ed650b;
--primary-foreground: #2a2214;
--secondary: #c03012;
--secondary-foreground: #2a2214;
--muted: #453c2e;
--muted-foreground: #ed650b;
--accent: #c03012;
--accent-foreground: #2a2214;
--destructive: #ff6b6b;
--border: #f4840a;
--input: #453c2e;
--ring: #ed650b;
--chart-1: #f4840a;
--chart-2: #ed650b;
--chart-3: #c03012;
--chart-4: #a82a0f;
--chart-5: #2a2214;
--sidebar: #3a3224;
--sidebar-foreground: #f4840a;
--sidebar-primary: #ed650b;
--sidebar-primary-foreground: #2a2214;
--sidebar-accent: #c03012;
--sidebar-accent-foreground: #2a2214;
--sidebar-border: #f4840a;
--sidebar-ring: #ed650b;
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground text-lg;
background-image:
radial-gradient(circle at 20% 80%, rgba(244, 132, 10, 0.1) 0%, transparent 50%),
radial-gradient(circle at 80% 20%, rgba(237, 101, 11, 0.1) 0%, transparent 50%),
radial-gradient(circle at 40% 40%, rgba(192, 48, 18, 0.05) 0%, transparent 50%);
}
}
.space-age-glow {
box-shadow:
0 0 20px rgba(244, 132, 10, 0.3),
0 0 40px rgba(237, 101, 11, 0.2),
0 0 60px rgba(192, 48, 18, 0.1);
}
.terminal-border {
border: 2px solid;
border-image: linear-gradient(45deg, #f4840a, #ed650b, #c03012) 1;
}
/* Table styling for Markdown content */
.prose table {
width: 100%;
border-collapse: collapse;
margin: 1rem 0;
}
.prose th,
.prose td {
border: 1px solid var(--border);
padding: 0.75rem;
text-align: left;
}
.prose th {
background-color: var(--muted);
font-weight: 600;
}
.prose tr:nth-child(even) {
background-color: var(--muted);
}
/* Ensure proper spacing for Markdown content */
.prose p {
margin: 1rem 0;
}
.prose ul,
.prose ol {
margin: 1rem 0;
padding-left: 1.5rem;
}
.prose li {
margin: 0.5rem 0;
}
.prose ul li {
list-style-type: disc;
}
.prose ol li {
list-style-type: decimal;
}

View file

@ -0,0 +1,34 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html>
);
}

114
frontend/src/app/page.tsx Normal file
View file

@ -0,0 +1,114 @@
import { Navigation } from '@/components/navigation'
import { InstallationCommand } from '@/components/installation-command'
import { Button } from '@/components/ui/button'
import { Card, CardContent } from '@/components/ui/card'
import { GitBranch, Users, BookOpen } from 'lucide-react'
import Link from 'next/link'
export default function Home() {
return (
<div className="min-h-screen bg-gradient-to-br from-background via-background to-orange-50/30">
{/* Navigation */}
<Navigation />
{/* Hero Section */}
<section className="relative py-20 px-4 sm:px-6 lg:px-8">
<div className="max-w-6xl mx-auto">
<div className="text-center mb-12">
<h1 className="text-5xl md:text-7xl font-black text-foreground mb-6 tracking-tight space-age-glow inline-block px-8 py-4 rounded-lg">
SHARENET
</h1>
<p className="text-xl md:text-2xl text-muted-foreground max-w-3xl mx-auto leading-relaxed font-mono">
OPEN-SOURCE DECENTRALIZED RESOURCE SHARING SYSTEM BUILT ON RUST
<span className="block text-lg text-destructive font-bold mt-2 tracking-wider">
NON-COMMERCIAL NON-TRADE COMMUNITY-DRIVEN
</span>
</p>
</div>
{/* Installation Command */}
<div className="max-w-2xl mx-auto mb-16">
<InstallationCommand />
</div>
{/* Mission Statement */}
<Card className="max-w-4xl mx-auto border-2 border-primary/20 bg-card/50 backdrop-blur-sm">
<CardContent className="p-8">
<h2 className="text-2xl font-bold text-foreground mb-4">Our Mission</h2>
<p className="text-lg text-muted-foreground leading-relaxed">
Sharenet is an open-source, non-commercial decentralized resource sharing system
that empowers communities to share resources, labor, and knowledge without money or trade as a medium of exchange.
Built with Rust for performance and reliability, Sharenet ensures that even the
development and provenance of the technology remains decentralized.
</p>
<p className="text-lg text-destructive font-semibold mt-4">
Usage for commercial or trade purposes is strictly forbidden under the Sharenet Public License (SPL).
</p>
</CardContent>
</Card>
</div>
</section>
{/* Features Section */}
<section className="py-16 px-4 sm:px-6 lg:px-8 bg-muted/20">
<div className="max-w-6xl mx-auto">
<h2 className="text-3xl font-bold text-center text-foreground mb-12">Why Sharenet?</h2>
<div className="grid md:grid-cols-3 gap-8">
<Card className="border-2 border-primary/20 bg-card/50">
<CardContent className="p-6 text-center">
<GitBranch className="h-12 w-12 text-primary mx-auto mb-4" />
<h3 className="text-xl font-bold text-foreground mb-2">Decentralized</h3>
<p className="text-muted-foreground">
No central authority. Every node is equal and the network grows organically.
</p>
</CardContent>
</Card>
<Card className="border-2 border-primary/20 bg-card/50">
<CardContent className="p-6 text-center">
<Users className="h-12 w-12 text-primary mx-auto mb-4" />
<h3 className="text-xl font-bold text-foreground mb-2">Community-Driven</h3>
<p className="text-muted-foreground">
Development and improvement are open to anyone. Truly community-owned technology.
</p>
</CardContent>
</Card>
<Card className="border-2 border-primary/20 bg-card/50">
<CardContent className="p-6 text-center">
<BookOpen className="h-12 w-12 text-primary mx-auto mb-4" />
<h3 className="text-xl font-bold text-foreground mb-2">Open Source</h3>
<p className="text-muted-foreground">
Complete transparency. All code is available for review, audit, and improvement.
</p>
</CardContent>
</Card>
</div>
</div>
</section>
{/* Footer */}
<footer className="py-8 px-4 sm:px-6 lg:px-8 border-t border-border">
<div className="max-w-6xl mx-auto text-center">
<div className="flex justify-center space-x-6 mb-4">
<a href="https://git.sharenet.sh/devteam/sharenet" target="_blank" rel="noopener noreferrer">
<Button variant="ghost" size="sm" className="text-muted-foreground hover:text-foreground">
<GitBranch className="h-5 w-5 mr-2" />
Repo
</Button>
</a>
<Link href="/docs">
<Button variant="ghost" size="sm" className="text-muted-foreground hover:text-foreground">
Docs
</Button>
</Link>
<Button variant="ghost" size="sm" className="text-muted-foreground hover:text-foreground">
Community
</Button>
</div>
<p className="text-sm text-muted-foreground">
Sharenet Public License (SPL) Built with Rust Decentralized by design
</p>
</div>
</footer>
</div>
)
}

View file

@ -0,0 +1,53 @@
'use client'
import { useState } from 'react'
import { Button } from '@/components/ui/button'
import { Card, CardContent } from '@/components/ui/card'
import { Copy, Check } from 'lucide-react'
export function InstallationCommand() {
const [copied, setCopied] = useState(false)
const command = 'curl -fsSL https://get.sharenet.sh | sh'
const copyToClipboard = async () => {
try {
await navigator.clipboard.writeText(command)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
} catch (err) {
console.error('Failed to copy text: ', err)
}
}
return (
<Card className="border-2 border-primary/20 bg-card/50 backdrop-blur-sm">
<CardContent className="p-6">
<div className="flex items-start justify-between">
<div className="flex-1">
<div className="flex items-center gap-4">
<code className="text-lg font-mono text-foreground bg-muted px-4 py-3 rounded border flex-1">
{command}
</code>
<Button
onClick={copyToClipboard}
variant="outline"
size="lg"
className="border-primary/30 hover:bg-primary hover:text-primary-foreground transition-colors"
>
{copied ? (
<Check className="h-4 w-4 text-green-600" />
) : (
<Copy className="h-4 w-4" />
)}
{copied ? 'Copied!' : 'Copy'}
</Button>
</div>
<p className="text-base text-muted-foreground mt-2">
(This command does not yet work)
</p>
</div>
</div>
</CardContent>
</Card>
)
}

View file

@ -0,0 +1,115 @@
'use client'
import { useState } from 'react'
import Link from 'next/link'
import { Button } from '@/components/ui/button'
import { Sun, Moon, Globe, GitBranch } from 'lucide-react'
export function Navigation() {
const [isDarkMode, setIsDarkMode] = useState(false)
const [language, setLanguage] = useState('EN')
const toggleDarkMode = () => {
setIsDarkMode(!isDarkMode)
document.documentElement.classList.toggle('dark')
}
const navLinks = [
{ href: '/install', label: 'Install' },
{ href: 'https://try.sharenet.sh', label: 'Try It Out', external: true },
{ href: '/faq', label: 'FAQ' },
{ href: '/docs', label: 'Docs' },
{ href: '/governance', label: 'Governance' },
{ href: '/community', label: 'Community' },
{ href: 'https://blog.sharenet.sh', label: 'Blog' },
{ href: 'https://git.sharenet.sh/devteam/sharenet', label: 'Repo', external: true, icon: 'git' },
]
const languages = ['EN', 'ES', 'FR', 'DE', 'ZH']
return (
<nav className="w-full py-6 px-4 sm:px-6 lg:px-8">
<div className="max-w-6xl mx-auto flex items-center justify-between">
{/* Empty space on left to balance the right-side controls */}
<div className="flex-1"></div>
{/* Centered Navigation Links */}
<div className="hidden md:flex items-center space-x-8">
{navLinks.map((link) => {
const LinkComponent = link.external ? 'a' : Link
const linkProps = link.external
? { href: link.href, target: '_blank', rel: 'noopener noreferrer' }
: { href: link.href }
return (
<LinkComponent
key={link.href}
{...linkProps}
className="text-lg text-foreground hover:text-primary transition-colors font-medium flex items-center gap-1"
>
{link.icon === 'git' && <GitBranch className="h-4 w-4" />}
{link.label}
</LinkComponent>
)
})}
</div>
{/* Right Side Controls */}
<div className="flex items-center space-x-4 flex-1 justify-end">
{/* Language Selector */}
<div className="relative">
<select
value={language}
onChange={(e) => setLanguage(e.target.value)}
className="appearance-none bg-transparent border-none text-lg text-foreground hover:text-primary cursor-pointer pr-6 focus:outline-none"
>
{languages.map((lang) => (
<option key={lang} value={lang}>
{lang}
</option>
))}
</select>
<Globe className="absolute right-0 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none" />
</div>
{/* Dark/Light Mode Toggle */}
<Button
variant="ghost"
size="icon"
onClick={toggleDarkMode}
className="text-foreground hover:text-primary hover:bg-transparent"
>
{isDarkMode ? (
<Sun className="h-5 w-5" />
) : (
<Moon className="h-5 w-5" />
)}
</Button>
</div>
</div>
{/* Mobile Navigation */}
<div className="md:hidden mt-4">
<div className="flex flex-wrap gap-4 justify-center">
{navLinks.map((link) => {
const LinkComponent = link.external ? 'a' : Link
const linkProps = link.external
? { href: link.href, target: '_blank', rel: 'noopener noreferrer' }
: { href: link.href }
return (
<LinkComponent
key={link.href}
{...linkProps}
className="text-base text-foreground hover:text-primary transition-colors font-medium flex items-center gap-1"
>
{link.icon === 'git' && <GitBranch className="h-3 w-3" />}
{link.label}
</LinkComponent>
)
})}
</div>
</div>
</nav>
)
}

View file

@ -0,0 +1,58 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost:
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3",
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
icon: "size-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
function Button({
className,
variant,
size,
asChild = false,
...props
}: React.ComponentProps<"button"> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean
}) {
const Comp = asChild ? Slot : "button"
return (
<Comp
data-slot="button"
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
)
}
export { Button, buttonVariants }

View file

@ -0,0 +1,92 @@
import * as React from "react"
import { cn } from "@/lib/utils"
function Card({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card"
className={cn(
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
className
)}
{...props}
/>
)
}
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-header"
className={cn(
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
className
)}
{...props}
/>
)
}
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-title"
className={cn("leading-none font-semibold", className)}
{...props}
/>
)
}
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-description"
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
)
}
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-action"
className={cn(
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
className
)}
{...props}
/>
)
}
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-content"
className={cn("px-6", className)}
{...props}
/>
)
}
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-footer"
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
{...props}
/>
)
}
export {
Card,
CardHeader,
CardFooter,
CardTitle,
CardAction,
CardDescription,
CardContent,
}

View file

@ -0,0 +1,21 @@
import * as React from "react"
import { cn } from "@/lib/utils"
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
return (
<input
type={type}
data-slot="input"
className={cn(
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
className
)}
{...props}
/>
)
}
export { Input }

View file

@ -0,0 +1,87 @@
# Frequently Asked Questions
## What is Sharenet?
Sharenet is a decentralized, not-for-commercial-use open-source infrastructure for sharing and mutual aid.
It helps neighbors, organizers, and communities request, offer, and coordinate material support — tools, food, rides, housing, care — without relying on corporate platforms, central authorities, or extractive systems.
## Is this like Buy Nothing?
We love what Buy Nothing inspired.
But Buy Nothing is registered as business: The Buy Nothing Project Inc. It is a centrally-controlled organization and brand. The Buy Nothing app is not open source and it does not allow community control of its infrastructure. You are locked into a single platform, controlled by a central bureaucracy which can disown or shut down your local Buy Nothing group down over a single incorrect judgment.
Sharenet on the other hand is decentralized and open source in all aspects-- infrastructure, software, and governance. It has no a single point of failure. It's carefully designed to be robust, survivable, and true to its stated mission.
## Is this another app?
No — it's a system and a practice.
Sharenet can take the form of a lightweight app, but more importantly, it's an open toolkit and network that any group, neighborhood, or city can adopt, extend, and host.
Think of it as digital plumbing for the sharing and solidarity economy.
## How is this different from GoFundMe, Craigslist, Nextdoor, or Freecycle?
| Platform | What They Do | What Sharenet Does |
| ---------- | ---------------------------- | ------------------------------------------ |
| GoFundMe | Monetizes crisis | Supports mutual aid, no fees |
| Craigslist | Matches stuff to people | Adds context, trust, and care |
| Nextdoor | Neighborhood gossip & alerts | Coordination for needs, not ads |
| Freecycle | One-way giving | Two-way sharing, tracking, & collaboration |
Sharenet doesn't monetize pain, lock you into a system, or sell your data. It's built for people, by people, to strengthen the real connections that keep us alive and help keep life worth living.
## Can Sharenet work without the Internet?
In many cases, yes.
We're designing for low-connectivity and offline-first use — including peer-to-peer protocols, SMS fallback, and even paper backups.
Sharenet can also plug into community mesh networks or run on a Raspberry Pi at a local fridge.
## Is this just for techies?
Not at all.
Sharenet is being designed so anyone — a neighbor, a pantry volunteer, a mutual aid group member — can use it with little to no training.
That said, we do welcome developers, designers, and organizers to help us build it better. If you want to contribute code, shape features, or help test — reach out.
## Who owns Sharenet?
No one and everyone.
Its a commons — licensed open-source, designed for collective governance, and intended to be forked, remixed, and run locally.
Were working with cooperators, mutual aid groups, and digital commons advocates to ensure it's governed by its users, not investors.
At the same time, you are not locked into a single Sharenet network — you can create your own and build in parallel with what already exists.
## What if Im already in a mutual aid group or community fridge team?
Great! Sharenet can help you:
- Track needs and offers over time
- Connect with other groups
- Visualize patterns (whos being served, whos being missed)
- Reduce burnout from spreadsheets and chat overload
- Share templates and best practices with other groups
You're already doing the work — we want to support it, not replace it.
## Whats the business model?
There isn't one. Sharenet is not-for-profit, values-aligned, and community-governed. It does not rely on investors, grants, or large donors. If youre really interested, read the Sharenet Public License (SPL).
No ads. No fees. No user exploitation. Just connecting at grassroots level.
## Are you trying to get me to list my stuff for free and Sharenet will take it from me?
Theres no obligation to share or not share. You can take as much as youd like. You can give as much as youd like (or not at all). This is a tool for communities to help each other communicate and share their resources.
## Can I start a Sharenet in my community?
Yes — that's the goal. We're developing a simple one-step Sharenet Installer that lets any community create their own node. Until then, were running early pilots with partner groups.
If you're interested, join the waitlist, or reach out directly: <a href="mailto:sharenetproject@gmail.com">sharenetproject@gmail.com</a>

View file

@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}

27
frontend/tsconfig.json Normal file
View file

@ -0,0 +1,27 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}