mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-29 22:23:01 +00:00
Feat/landing revamp (#3)
* docs: add AGENTS.md for repository guidelines and project structure - Introduced AGENTS.md to outline project structure, build commands, coding style, testing guidelines, and commit/pull request practices. - Included detailed sections on module organization and naming conventions to enhance developer onboarding and consistency. * feat: enhance landing page and configuration for improved user experience - Updated next.config.ts to pin Turbopack root to the app directory for better workspace management. - Modified package.json scripts to use Webpack during development and build processes. - Refactored postcss.config.mjs to set the base path for Tailwind CSS. - Replaced CSS imports in globals.css with Tailwind directives for better styling management. - Introduced RotatingTetrahedronCanvas component for dynamic visual effects on the Welcome page. - Revamped Welcome page layout to include the new canvas and improve overall design and user engagement. * refactor: update CSS imports and enhance HeroCTA layout for improved responsiveness - Replaced Tailwind CSS directives in globals.css with an import statement for better compatibility. - Modified the HeroCTA component layout to improve responsiveness and alignment across different screen sizes. * feat: integrate RotatingTetrahedronCanvas and update HeroCTA links for enhanced landing page experience - Added RotatingTetrahedronCanvas component to display a dynamic 3D visual on the landing page. - Replaced outdated links in HeroCTA component to direct users to the updated TinyHumans resources. * update design
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
# Repository Guidelines
|
||||
|
||||
## Project Structure & Module Organization
|
||||
- `src/`: React + TypeScript frontend (pages, components, hooks, store, services, lib).
|
||||
- `src-tauri/`: Rust backend and Tauri desktop integration.
|
||||
- `test/`: Vitest config and E2E/WebDriverIO setup (`test/e2e`, `test/wdio.conf.ts`).
|
||||
- `skills/`: Skill runtime/build artifacts and manifests.
|
||||
- `scripts/`: build/test automation (E2E helpers, tool generation).
|
||||
- `docs/`: architecture and product documentation.
|
||||
- `public/` and `src/assets/`: static assets.
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
- `yarn dev`: run Vite web dev server.
|
||||
- `yarn dev:app` (or `yarn tauri:dev`): run desktop app with Tauri + TDLib bootstrap.
|
||||
- `yarn build`: type-check and build web bundle.
|
||||
- `yarn build:app`: build app bundle plus skills/tools generation.
|
||||
- `yarn compile`: TypeScript check only (`tsc --noEmit`).
|
||||
- `yarn test` / `yarn test:coverage`: run unit tests and coverage.
|
||||
- `yarn test:rust`: run Rust tests in `src-tauri`.
|
||||
- `yarn test:e2e`: run E2E suite (login/auth/payment/telegram/notion/gmail).
|
||||
- `yarn lint` / `yarn lint:fix`, `yarn format:check` / `yarn format`: quality and formatting checks.
|
||||
|
||||
## Coding Style & Naming Conventions
|
||||
- TypeScript-first; keep all app code under `src/`.
|
||||
- Prettier is authoritative: 2 spaces, semicolons, single quotes, max width 100.
|
||||
- Imports are auto-sorted via `@trivago/prettier-plugin-sort-imports`.
|
||||
- ESLint (flat config) enforces React hooks rules, no duplicate imports, `prefer-const`, and TS-aware unused var checks.
|
||||
- Naming: React components `PascalCase.tsx`; hooks `useX.ts`; slices/services/helpers `camelCase.ts`; tests `*.test.ts(x)`.
|
||||
|
||||
## Testing Guidelines
|
||||
- Framework: Vitest + Testing Library (`jsdom`), with MSW utilities in `src/test/`.
|
||||
- Unit tests live beside code (`__tests__`) or as `*.test.ts(x)` in `src/`.
|
||||
- Coverage thresholds (global minimum): lines 15%, statements 15%, functions 15%, branches 12%.
|
||||
- Run `yarn test:coverage` before opening non-trivial PRs.
|
||||
|
||||
## Commit & Pull Request Guidelines
|
||||
- Prefer concise conventional-style subjects: `feat:`, `fix:`, `refactor:`, `docs:`, `chore:`.
|
||||
- Keep commits scoped and descriptive (e.g., `fix: handle socket reconnect on auth expiry`).
|
||||
- If contributing upstream, branch from `develop` and open PRs against `develop`.
|
||||
- PRs should include: purpose, key changes, test evidence (commands run), linked issue (`Fixes #123`), and screenshots/videos for UI changes.
|
||||
- Hooks exist but are lightweight; run lint, format, compile, and relevant tests manually before push.
|
||||
@@ -1,7 +1,7 @@
|
||||
export default function Footer() {
|
||||
return (
|
||||
<footer className="border-t border-zinc-800 bg-zinc-950">
|
||||
<div className="mx-auto max-w-7xl px-6 py-6 sm:px-8">
|
||||
<footer className="border-t border-[var(--border-subtle)] bg-[#0c0c0c]">
|
||||
<div className="w-full px-6 py-6 sm:px-8">
|
||||
<div className="flex flex-wrap items-center justify-center gap-4 text-sm text-zinc-400">
|
||||
<span>© {new Date().getFullYear()} OpenHuman</span>
|
||||
<span className="text-zinc-600">•</span>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import DownloadModal from './DownloadModal';
|
||||
|
||||
export default function HeroCTA() {
|
||||
@@ -9,30 +8,14 @@ export default function HeroCTA() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mt-10 flex items-center justify-center gap-4">
|
||||
<div className="mt-10 flex justify-center">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setDownloadModalOpen(true)}
|
||||
className="rounded-lg cursor-pointer bg-white px-6 py-3 text-sm font-semibold text-zinc-950 transition-colors hover:bg-zinc-200"
|
||||
className="rounded-lg bg-[var(--accent)] px-6 py-2.5 text-sm font-semibold text-white shadow-none transition-colors hover:bg-[var(--accent-hover)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--accent)]"
|
||||
>
|
||||
Download 🔥
|
||||
Download
|
||||
</button>
|
||||
<Link
|
||||
href="https://x.com/openhumanxyz"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="rounded-lg border border-zinc-800 px-6 py-3 text-sm font-semibold text-white transition-colors hover:border-zinc-700"
|
||||
>
|
||||
Join the Community
|
||||
</Link>
|
||||
<Link
|
||||
href="https://openhuman.gitbook.io/docs"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="rounded-lg border border-zinc-800 px-6 py-3 text-sm font-semibold text-white transition-colors hover:border-zinc-700"
|
||||
>
|
||||
Read the Docs
|
||||
</Link>
|
||||
</div>
|
||||
<DownloadModal isOpen={downloadModalOpen} onClose={() => setDownloadModalOpen(false)} />
|
||||
</>
|
||||
|
||||
@@ -2,8 +2,8 @@ import Link from 'next/link';
|
||||
|
||||
export default function Navigation() {
|
||||
return (
|
||||
<nav className="fixed top-0 left-0 right-0 z-50 border-b border-zinc-800 bg-zinc-950/80 backdrop-blur-sm">
|
||||
<div className="mx-auto max-w-7xl px-6 sm:px-8">
|
||||
<nav className="fixed top-0 left-1/2 z-50 w-full max-w-[1280px] -translate-x-1/2 border-x border-b border-zinc-800 bg-zinc-950/80 backdrop-blur-sm">
|
||||
<div className="px-6 sm:px-8">
|
||||
<div className="flex h-16 items-center justify-between">
|
||||
<Link href="/" className="text-xl font-semibold text-white">
|
||||
OpenHuman
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useRef } from 'react';
|
||||
import * as THREE from 'three';
|
||||
import { ConvexGeometry } from 'three/addons/geometries/ConvexGeometry.js';
|
||||
|
||||
/** Canonical vertices of a truncated tetrahedron (permutations of 0, ±1, ±1). Convex hull = Archimedean solid. */
|
||||
function truncatedTetrahedronPoints(scale: number): THREE.Vector3[] {
|
||||
const coords: [number, number, number][] = [
|
||||
[0, 1, 1],
|
||||
[0, 1, -1],
|
||||
[0, -1, 1],
|
||||
[0, -1, -1],
|
||||
[1, 0, 1],
|
||||
[1, 0, -1],
|
||||
[-1, 0, 1],
|
||||
[-1, 0, -1],
|
||||
[1, 1, 0],
|
||||
[1, -1, 0],
|
||||
[-1, 1, 0],
|
||||
[-1, -1, 0],
|
||||
];
|
||||
return coords.map(([x, y, z]) => new THREE.Vector3(x, y, z).multiplyScalar(scale));
|
||||
}
|
||||
|
||||
export default function RotatingTetrahedronCanvas() {
|
||||
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({
|
||||
canvas,
|
||||
antialias: true,
|
||||
alpha: true,
|
||||
powerPreference: 'high-performance',
|
||||
});
|
||||
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
||||
renderer.outputColorSpace = THREE.SRGBColorSpace;
|
||||
renderer.toneMapping = THREE.NoToneMapping;
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
const camera = new THREE.PerspectiveCamera(45, 1, 0.1, 100);
|
||||
camera.position.set(0, 0.15, 4.8);
|
||||
|
||||
const geometry = new ConvexGeometry(truncatedTetrahedronPoints(0.92));
|
||||
|
||||
// Flat, panel-like read: matte off-white / reads as light vs shadow (Oblivion-style blocking).
|
||||
const fillMaterial = new THREE.MeshLambertMaterial({
|
||||
color: '#e4e4e4',
|
||||
emissive: '#080808',
|
||||
emissiveIntensity: 0.06,
|
||||
});
|
||||
|
||||
const fillMesh = new THREE.Mesh(geometry, fillMaterial);
|
||||
fillMesh.rotation.x = 0.35;
|
||||
fillMesh.rotation.y = -0.45;
|
||||
scene.add(fillMesh);
|
||||
|
||||
const ambientLight = new THREE.AmbientLight('#ffffff', 0.08);
|
||||
const sun = new THREE.DirectionalLight('#ffffff', 1.35);
|
||||
sun.position.set(4.5, 6, 5);
|
||||
const bounce = new THREE.DirectionalLight('#8899aa', 0.22);
|
||||
bounce.position.set(-3, -1, -2);
|
||||
|
||||
scene.add(ambientLight);
|
||||
scene.add(sun);
|
||||
scene.add(bounce);
|
||||
|
||||
let animationFrame = 0;
|
||||
|
||||
const resize = () => {
|
||||
const parent = canvas.parentElement;
|
||||
if (!parent) return;
|
||||
|
||||
const { width, height } = parent.getBoundingClientRect();
|
||||
if (!width || !height) return;
|
||||
|
||||
renderer.setSize(width, height, false);
|
||||
camera.aspect = width / height;
|
||||
camera.updateProjectionMatrix();
|
||||
};
|
||||
|
||||
const observer = new ResizeObserver(resize);
|
||||
if (canvas.parentElement) observer.observe(canvas.parentElement);
|
||||
resize();
|
||||
|
||||
const animate = () => {
|
||||
fillMesh.rotation.y += 0.007;
|
||||
fillMesh.rotation.x += 0.0045;
|
||||
|
||||
renderer.render(scene, camera);
|
||||
animationFrame = window.requestAnimationFrame(animate);
|
||||
};
|
||||
|
||||
animate();
|
||||
|
||||
return () => {
|
||||
window.cancelAnimationFrame(animationFrame);
|
||||
observer.disconnect();
|
||||
geometry.dispose();
|
||||
fillMaterial.dispose();
|
||||
renderer.dispose();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<canvas ref={canvasRef} className="h-full w-full block" aria-label="Rotating tetrahedron" />
|
||||
);
|
||||
}
|
||||
@@ -55,7 +55,7 @@ export default function Downloads() {
|
||||
return (
|
||||
<div className="min-h-screen bg-zinc-950 text-white">
|
||||
<Navigation />
|
||||
<main className="mx-auto max-w-7xl px-6 pt-24 sm:px-8 sm:pt-32 pb-16">
|
||||
<main className="w-full px-6 pt-24 sm:px-8 sm:pt-32 pb-16">
|
||||
<div className="mx-auto max-w-4xl">
|
||||
<h1 className="text-4xl font-bold tracking-tight sm:text-5xl">
|
||||
Download for Free
|
||||
|
||||
@@ -468,8 +468,8 @@ export default function FeedbackPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-zinc-950 text-white">
|
||||
{/* Custom Header - Mobile Responsive */}
|
||||
<header className="fixed top-0 left-0 right-0 z-50 border-b border-zinc-800 bg-zinc-950/80 backdrop-blur-sm">
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<header className="fixed top-0 left-1/2 z-50 w-full max-w-[1280px] -translate-x-1/2 border-x border-b border-zinc-800 bg-zinc-950/80 backdrop-blur-sm">
|
||||
<div className="px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex h-14 sm:h-16 items-center justify-between">
|
||||
<div className="flex items-center gap-3 sm:gap-8">
|
||||
<button
|
||||
@@ -514,7 +514,7 @@ export default function FeedbackPage() {
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 pt-20 sm:pt-24 pb-8">
|
||||
<main className="w-full px-4 sm:px-6 lg:px-8 pt-20 sm:pt-24 pb-8">
|
||||
<div className="flex gap-4 lg:gap-8">
|
||||
{/* Mobile Sidebar Overlay */}
|
||||
{sidebarOpen && (
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
:root {
|
||||
--background: #09090b;
|
||||
--background: #121212;
|
||||
--foreground: #fafafa;
|
||||
--surface: #1a1a1a;
|
||||
--surface-raised: #242424;
|
||||
--border-subtle: #2a2a2a;
|
||||
--muted: #a1a1aa;
|
||||
--accent: #4a83dd;
|
||||
--accent-hover: #3d6fc7;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
@@ -15,5 +21,5 @@
|
||||
body {
|
||||
background: var(--background);
|
||||
color: var(--foreground);
|
||||
font-family: var(--font-geist-sans), Arial, Helvetica, sans-serif;
|
||||
font-family: var(--font-geist-sans), system-ui, sans-serif;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ export default function RootLayout({
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
</head>
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased flex min-h-screen flex-col`}
|
||||
className={`${geistSans.variable} ${geistMono.variable} bg-[#121212] antialiased text-zinc-100`}
|
||||
>
|
||||
<Script
|
||||
src="https://www.googletagmanager.com/gtag/js?id=G-VS26CY11RK"
|
||||
@@ -54,10 +54,10 @@ export default function RootLayout({
|
||||
gtag('config', 'G-VS26CY11RK');
|
||||
`}
|
||||
</Script>
|
||||
<div className="flex-grow">
|
||||
{children}
|
||||
<div className="mx-auto flex min-h-[100dvh] w-full max-w-[1280px] flex-col border-x border-[var(--border-subtle)] bg-[#121212]">
|
||||
<div className="flex flex-1 flex-col">{children}</div>
|
||||
<Footer />
|
||||
</div>
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
+64
-30
@@ -1,43 +1,77 @@
|
||||
import Link from 'next/link';
|
||||
import HeroCTA from './components/HeroCTA';
|
||||
import RotatingTetrahedronCanvas from './components/RotatingTetrahedronCanvas';
|
||||
|
||||
const connectLinks = [
|
||||
{ label: 'X', href: 'https://x.com/tinyhumansai' },
|
||||
{ label: 'GitHub', href: 'https://github.com/tinyhumansai/openhuman' },
|
||||
];
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="min-h-screen bg-black text-white relative">
|
||||
<main className="mx-auto max-w-7xl px-6 sm:px-8 sm:pt-32 relative z-10">
|
||||
<div className="mx-auto max-w-3xl text-center">
|
||||
<img src="/android-chrome-192x192.png" alt="OpenHuman" className="w-[150px] h-[150px] mx-auto mb-4" />
|
||||
<h1 className="text-5xl font-bold tracking-tight sm:text-6xl lg:text-7xl">
|
||||
OpenHuman
|
||||
</h1>
|
||||
<p className="mt-6 text-lg leading-8 text-zinc-400 sm:text-xl">
|
||||
Your AI superhuman for your personal and business life. In private beta.
|
||||
</p>
|
||||
<HeroCTA />
|
||||
</div>
|
||||
<div className="relative flex min-h-full flex-1 flex-col bg-[#121212] text-white">
|
||||
<main className="relative z-10 w-full">
|
||||
<section className="px-6 pb-12 pt-16 sm:px-10 sm:pb-16 sm:pt-20">
|
||||
<div className="mx-auto max-w-xl text-center">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-zinc-500">OpenHuman</p>
|
||||
<div className="mx-auto mt-8 h-[min(200px,42vw)] w-[min(200px,42vw)] max-h-[220px] max-w-[220px]">
|
||||
<RotatingTetrahedronCanvas />
|
||||
</div>
|
||||
<h1 className="mt-10 text-4xl font-bold tracking-tight text-white sm:text-5xl">Get started</h1>
|
||||
<p className="mt-4 text-base leading-relaxed text-[var(--muted)] sm:text-lg">
|
||||
Your AI superhuman for personal and business life. Private beta — install the app and connect your
|
||||
tools.
|
||||
</p>
|
||||
<HeroCTA />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div className="mx-auto mt-24 max-w-5xl">
|
||||
<div className="grid gap-8 sm:grid-cols-2 lg:grid-cols-3 text-center">
|
||||
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-6">
|
||||
<h3 className="text-lg font-semibold text-white">Large Scale Data Analysis</h3>
|
||||
<p className="mt-2 text-sm text-zinc-400">
|
||||
Analyze all your emails, messages, meeting notes and more. Control the chaos.
|
||||
<section className="border-t border-[var(--border-subtle)]">
|
||||
<div className="grid md:grid-cols-2">
|
||||
<div className="border-b border-[var(--border-subtle)] px-6 py-12 sm:px-10 md:border-b-0 md:border-r md:border-[var(--border-subtle)]">
|
||||
<h2 className="text-lg font-semibold tracking-tight text-white">Documentation</h2>
|
||||
<p className="mt-2 max-w-sm text-sm leading-relaxed text-zinc-400">
|
||||
Read the guides, learn how skills work, and explore what OpenHuman can do with your Telegram and
|
||||
integrations.
|
||||
</p>
|
||||
<div className="mt-8 flex flex-wrap gap-3">
|
||||
<a
|
||||
href="https://tinyhumans.gitbook.io/openhuman"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center rounded-full bg-[var(--surface-raised)] px-4 py-2 text-sm font-medium text-zinc-200 ring-1 ring-zinc-700/80 transition-colors hover:bg-zinc-800 hover:text-white"
|
||||
>
|
||||
Read the docs
|
||||
</a>
|
||||
<Link
|
||||
href="/downloads"
|
||||
className="inline-flex items-center justify-center rounded-full bg-[var(--surface-raised)] px-4 py-2 text-sm font-medium text-zinc-200 ring-1 ring-zinc-700/80 transition-colors hover:bg-zinc-800 hover:text-white"
|
||||
>
|
||||
View downloads
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-6">
|
||||
<h3 className="text-lg font-semibold text-white text-center">Extreme Intelligence</h3>
|
||||
<p className="mt-2 text-sm text-zinc-400">
|
||||
Get insights from your data that you never thought possible.
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-lg border border-zinc-800 bg-zinc-900/50 p-6">
|
||||
<h3 className="text-lg font-semibold text-white text-center">Take Action</h3>
|
||||
<p className="mt-2 text-sm text-zinc-400">
|
||||
Let OpenHuman do the heavy lifting. Automatically create tasks,
|
||||
send messages, get reports, and more.
|
||||
<div className="px-6 py-12 sm:px-10">
|
||||
<h2 className="text-lg font-semibold tracking-tight text-white">Connect with us</h2>
|
||||
<p className="mt-2 max-w-sm text-sm leading-relaxed text-zinc-400">
|
||||
Follow updates, ship feedback, and join the community while we're in beta.
|
||||
</p>
|
||||
<div className="mt-8 flex flex-wrap gap-2">
|
||||
{connectLinks.map(({ label, href }) => (
|
||||
<a
|
||||
key={href}
|
||||
href={href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center rounded-full bg-[var(--surface-raised)] px-4 py-2 text-sm font-medium text-zinc-200 ring-1 ring-zinc-700/80 transition-colors hover:bg-zinc-800 hover:text-white"
|
||||
>
|
||||
{label}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
|
||||
+10
-2
@@ -1,4 +1,9 @@
|
||||
import type { NextConfig } from "next";
|
||||
import type { NextConfig } from 'next';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
// Pin Turbopack root to this app so a parent lockfile (e.g. ~/yarn.lock) is not chosen as the workspace root.
|
||||
const landerRoot = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
// Enables static export so the landing site can be hosted on GitHub Pages.
|
||||
// `NEXT_PUBLIC_BASE_PATH` is used so project pages can be deployed under `/${repo}`.
|
||||
@@ -6,10 +11,13 @@ const basePath = process.env.NEXT_PUBLIC_BASE_PATH;
|
||||
const normalizedBasePath = basePath && basePath.trim().length > 0 ? basePath : undefined;
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
output: "export",
|
||||
output: 'export',
|
||||
trailingSlash: true,
|
||||
basePath: normalizedBasePath,
|
||||
assetPrefix: normalizedBasePath,
|
||||
turbopack: {
|
||||
root: landerRoot,
|
||||
},
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
||||
+2
-2
@@ -3,8 +3,8 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"dev": "next dev --webpack",
|
||||
"build": "next build --webpack",
|
||||
"start": "next start",
|
||||
"lint": "eslint"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const landerRoot = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const config = {
|
||||
plugins: {
|
||||
"@tailwindcss/postcss": {},
|
||||
'@tailwindcss/postcss': {
|
||||
base: landerRoot,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
"@tauri-apps/plugin-os": "^2.3.2",
|
||||
"@tauri-apps/plugin-shell": "~2",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/three": "^0.183.1",
|
||||
"buffer": "^6.0.3",
|
||||
"debug": "^4.4.3",
|
||||
"immer": "^11.1.3",
|
||||
@@ -76,6 +77,7 @@
|
||||
"redux-persist": "^6.0.0",
|
||||
"socket.io-client": "^4.8.3",
|
||||
"telegram": "^2.26.22",
|
||||
"three": "^0.183.2",
|
||||
"util": "^0.12.5",
|
||||
"zustand": "^5.0.10"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import * as THREE from 'three';
|
||||
|
||||
const RotatingTetrahedronCanvas = () => {
|
||||
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({
|
||||
canvas,
|
||||
antialias: true,
|
||||
alpha: true,
|
||||
powerPreference: 'high-performance',
|
||||
});
|
||||
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
const camera = new THREE.PerspectiveCamera(45, 1, 0.1, 100);
|
||||
camera.position.set(0, 0, 4.6);
|
||||
|
||||
const fillMaterial = new THREE.MeshPhongMaterial({
|
||||
color: '#7f5af0',
|
||||
emissive: '#201040',
|
||||
emissiveIntensity: 0.8,
|
||||
shininess: 50,
|
||||
transparent: true,
|
||||
opacity: 0.65,
|
||||
flatShading: true,
|
||||
});
|
||||
|
||||
const wireMaterial = new THREE.MeshBasicMaterial({
|
||||
color: '#a78bfa',
|
||||
wireframe: true,
|
||||
transparent: true,
|
||||
opacity: 0.95,
|
||||
});
|
||||
|
||||
const geometry = new THREE.TetrahedronGeometry(1.3, 0);
|
||||
const fillMesh = new THREE.Mesh(geometry, fillMaterial);
|
||||
const wireMesh = new THREE.Mesh(geometry, wireMaterial);
|
||||
|
||||
scene.add(fillMesh);
|
||||
scene.add(wireMesh);
|
||||
|
||||
const ambientLight = new THREE.AmbientLight('#b4b1ff', 0.45);
|
||||
const keyLight = new THREE.PointLight('#7f5af0', 1.2, 20);
|
||||
keyLight.position.set(2.8, 2.5, 4);
|
||||
|
||||
scene.add(ambientLight);
|
||||
scene.add(keyLight);
|
||||
|
||||
let animationFrame = 0;
|
||||
|
||||
const resize = () => {
|
||||
const parent = canvas.parentElement;
|
||||
if (!parent) return;
|
||||
|
||||
const { width, height } = parent.getBoundingClientRect();
|
||||
if (!width || !height) return;
|
||||
|
||||
renderer.setSize(width, height, false);
|
||||
camera.aspect = width / height;
|
||||
camera.updateProjectionMatrix();
|
||||
};
|
||||
|
||||
const observer = new ResizeObserver(resize);
|
||||
if (canvas.parentElement) observer.observe(canvas.parentElement);
|
||||
resize();
|
||||
|
||||
const animate = () => {
|
||||
fillMesh.rotation.x += 0.006;
|
||||
fillMesh.rotation.y += 0.009;
|
||||
wireMesh.rotation.copy(fillMesh.rotation);
|
||||
|
||||
renderer.render(scene, camera);
|
||||
animationFrame = window.requestAnimationFrame(animate);
|
||||
};
|
||||
|
||||
animate();
|
||||
|
||||
return () => {
|
||||
window.cancelAnimationFrame(animationFrame);
|
||||
observer.disconnect();
|
||||
geometry.dispose();
|
||||
fillMaterial.dispose();
|
||||
wireMaterial.dispose();
|
||||
renderer.dispose();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return <canvas ref={canvasRef} className="h-full w-full" aria-label="Rotating tetrahedron animation" />;
|
||||
};
|
||||
|
||||
export default RotatingTetrahedronCanvas;
|
||||
+50
-26
@@ -1,39 +1,63 @@
|
||||
import DownloadScreen from '../components/DownloadScreen';
|
||||
import OAuthLoginSection from '../components/oauth/OAuthLoginSection';
|
||||
import TypewriterGreeting from '../components/TypewriterGreeting';
|
||||
import RotatingTetrahedronCanvas from '../components/RotatingTetrahedronCanvas';
|
||||
|
||||
interface WelcomeProps {
|
||||
isWeb: boolean;
|
||||
}
|
||||
|
||||
const Welcome = ({ isWeb }: WelcomeProps) => {
|
||||
const greetings = ['Hello HAL9000! 👋', "Let's cook! 🔥", 'The A-Team is here! 👊'];
|
||||
|
||||
return (
|
||||
<div className="min-h-full relative flex items-center justify-center">
|
||||
{/* Main content */}
|
||||
<div className="relative z-10 max-w-md w-full mx-4 space-y-6">
|
||||
{/* Welcome card */}
|
||||
<div className="glass rounded-3xl p-8 text-center animate-fade-up shadow-large">
|
||||
{/* Greeting */}
|
||||
<TypewriterGreeting greetings={greetings} />
|
||||
<div className="min-h-full w-full bg-[#090b12] px-4 py-6 md:px-10 md:py-10">
|
||||
<div className="mx-auto grid min-h-[calc(100vh-3.5rem)] w-full max-w-5xl grid-rows-[1fr_auto] border border-[#24293d] bg-[#0b0f18] text-white">
|
||||
<section className="relative grid place-items-center border-b border-[#1e2335] px-6 py-14">
|
||||
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_50%_45%,rgba(127,90,240,0.14),transparent_55%)]" />
|
||||
|
||||
<p className="opacity-70 mb-8 leading-relaxed">
|
||||
Welcome to AlphaHuman. Your Telegram assistant here to get you 10x more done in your
|
||||
journey.
|
||||
</p>
|
||||
|
||||
<p className="opacity-70 leading-relaxed">Are you ready for this?</p>
|
||||
|
||||
{/* Show OAuth login options in Tauri app, download screen on web */}
|
||||
{!isWeb && (
|
||||
<div className="mt-6">
|
||||
<OAuthLoginSection />
|
||||
<div className="relative z-10 flex w-full max-w-2xl flex-col items-center gap-7 text-center">
|
||||
<div className="h-52 w-52 md:h-60 md:w-60">
|
||||
<RotatingTetrahedronCanvas />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{isWeb && <DownloadScreen />}
|
||||
<h1 className="text-balance text-4xl font-semibold tracking-tight text-white md:text-6xl">
|
||||
AlphaHuman
|
||||
</h1>
|
||||
|
||||
<p className="max-w-xl text-sm text-[#8e96b8] md:text-base">
|
||||
A focused command center for AI-driven execution. Minimal surfaces, hard edges, and
|
||||
no visual noise.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap items-center justify-center gap-3">
|
||||
<button
|
||||
className="border border-[#3d2f68] bg-[#201732] px-5 py-2 text-sm font-medium tracking-wide text-[#d4c8ff] transition-colors hover:bg-[#2a1d44]"
|
||||
type="button">
|
||||
{isWeb ? 'Download AlphaHuman' : 'Continue'}
|
||||
</button>
|
||||
<button
|
||||
className="border border-[#2a3147] bg-[#121827] px-5 py-2 text-sm font-medium tracking-wide text-[#a7afce] transition-colors hover:bg-[#181f30]"
|
||||
type="button">
|
||||
View Docs
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="grid grid-cols-1 md:grid-cols-2">
|
||||
<article className="border-b border-[#1e2335] p-6 md:border-b-0 md:border-r">
|
||||
<p className="mb-2 text-xs uppercase tracking-[0.2em] text-[#7362b0]">Documentation</p>
|
||||
<h2 className="text-2xl font-semibold tracking-tight">Built for depth</h2>
|
||||
<p className="mt-2 max-w-sm text-sm text-[#8e96b8]">
|
||||
Structured workflows, deliberate constraints, and precise outputs designed for power
|
||||
users.
|
||||
</p>
|
||||
</article>
|
||||
|
||||
<article className="p-6">
|
||||
<p className="mb-2 text-xs uppercase tracking-[0.2em] text-[#7362b0]">Connect</p>
|
||||
<h2 className="text-2xl font-semibold tracking-tight">Join the mission</h2>
|
||||
<p className="mt-2 max-w-sm text-sm text-[#8e96b8]">
|
||||
Collaborate with the AlphaHuman community and shape what this assistant becomes next.
|
||||
</p>
|
||||
</article>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -319,6 +319,11 @@
|
||||
resolved "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz"
|
||||
integrity sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==
|
||||
|
||||
"@dimforge/rapier3d-compat@~0.12.0":
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz#7b3365e1dfdc5cd957b45afe920b4ac06c7cd389"
|
||||
integrity sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==
|
||||
|
||||
"@esbuild/aix-ppc64@0.27.2":
|
||||
version "0.27.2"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz#521cbd968dcf362094034947f76fa1b18d2d403c"
|
||||
@@ -1466,6 +1471,11 @@
|
||||
minimatch "^9.0.0"
|
||||
parse-imports-exports "^0.2.4"
|
||||
|
||||
"@tweenjs/tween.js@~23.1.3":
|
||||
version "23.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@tweenjs/tween.js/-/tween.js-23.1.3.tgz#eff0245735c04a928bb19c026b58c2a56460539d"
|
||||
integrity sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==
|
||||
|
||||
"@types/aria-query@^5.0.1":
|
||||
version "5.0.4"
|
||||
resolved "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz"
|
||||
@@ -1666,11 +1676,29 @@
|
||||
resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz"
|
||||
integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==
|
||||
|
||||
"@types/stats.js@*":
|
||||
version "0.17.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/stats.js/-/stats.js-0.17.4.tgz#1933e5ff153a23c7664487833198d685c22e791e"
|
||||
integrity sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==
|
||||
|
||||
"@types/statuses@^2.0.6":
|
||||
version "2.0.6"
|
||||
resolved "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.6.tgz"
|
||||
integrity sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==
|
||||
|
||||
"@types/three@^0.183.1":
|
||||
version "0.183.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/three/-/three-0.183.1.tgz#d812d028b38ad68843725e3e7bd3268607cef150"
|
||||
integrity sha512-f2Pu5Hrepfgavttdye3PsH5RWyY/AvdZQwIVhrc4uNtvF7nOWJacQKcoVJn0S4f0yYbmAE6AR+ve7xDcuYtMGw==
|
||||
dependencies:
|
||||
"@dimforge/rapier3d-compat" "~0.12.0"
|
||||
"@tweenjs/tween.js" "~23.1.3"
|
||||
"@types/stats.js" "*"
|
||||
"@types/webxr" ">=0.5.17"
|
||||
"@webgpu/types" "*"
|
||||
fflate "~0.8.2"
|
||||
meshoptimizer "~1.0.1"
|
||||
|
||||
"@types/unist@*", "@types/unist@^3.0.0":
|
||||
version "3.0.3"
|
||||
resolved "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz"
|
||||
@@ -1686,6 +1714,11 @@
|
||||
resolved "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz"
|
||||
integrity sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==
|
||||
|
||||
"@types/webxr@>=0.5.17":
|
||||
version "0.5.24"
|
||||
resolved "https://registry.yarnpkg.com/@types/webxr/-/webxr-0.5.24.tgz#734d5d90dadc5809a53e422726c60337fa2f4a44"
|
||||
integrity sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==
|
||||
|
||||
"@types/which@^2.0.1":
|
||||
version "2.0.2"
|
||||
resolved "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz"
|
||||
@@ -2114,6 +2147,11 @@
|
||||
dependencies:
|
||||
"@wdio/logger" "9.18.0"
|
||||
|
||||
"@webgpu/types@*":
|
||||
version "0.1.69"
|
||||
resolved "https://registry.yarnpkg.com/@webgpu/types/-/types-0.1.69.tgz#6b849bf370a1f29c78bd3aeba8e84c1150b237f2"
|
||||
integrity sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ==
|
||||
|
||||
"@xmldom/xmldom@^0.9.5":
|
||||
version "0.9.8"
|
||||
resolved "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.8.tgz"
|
||||
@@ -4132,6 +4170,11 @@ fdir@^6.5.0:
|
||||
resolved "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz"
|
||||
integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==
|
||||
|
||||
fflate@~0.8.2:
|
||||
version "0.8.2"
|
||||
resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea"
|
||||
integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==
|
||||
|
||||
figures@^6.1.0:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz"
|
||||
@@ -5599,6 +5642,11 @@ merge2@^1.3.0:
|
||||
resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz"
|
||||
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
|
||||
|
||||
meshoptimizer@~1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/meshoptimizer/-/meshoptimizer-1.0.1.tgz#c3ef0d509a8b84ac562493dba5a108fd67fa76dc"
|
||||
integrity sha512-Vix+QlA1YYT3FwmBBZ+49cE5y/b+pRrcXKqGpS5ouh33d3lSp2PoTpCw19E0cKDFWalembrHnIaZetf27a+W2g==
|
||||
|
||||
micromark-core-commonmark@^2.0.0:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz"
|
||||
@@ -7709,6 +7757,11 @@ thenify-all@^1.0.0:
|
||||
dependencies:
|
||||
any-promise "^1.0.0"
|
||||
|
||||
three@^0.183.2:
|
||||
version "0.183.2"
|
||||
resolved "https://registry.yarnpkg.com/three/-/three-0.183.2.tgz#606e3195bf210ef8d1eaaca2ab8c59d92d2bbc18"
|
||||
integrity sha512-di3BsL2FEQ1PA7Hcvn4fyJOlxRRgFYBpMTcyOgkwJIaDOdJMebEFPA+t98EvjuljDx4hNulAGwF6KIjtwI5jgQ==
|
||||
|
||||
timers-browserify@^2.0.4:
|
||||
version "2.0.12"
|
||||
resolved "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz"
|
||||
|
||||
Reference in New Issue
Block a user