modified: client/index.html

This commit is contained in:
MrPiglr 2026-02-03 11:59:47 -07:00
parent 293d3c0d02
commit 4642d7a76a
18 changed files with 2103 additions and 64 deletions

View file

@ -58,22 +58,29 @@ public class MainActivity extends BridgeActivity {
private void enableImmersiveMode() { private void enableImmersiveMode() {
View decorView = getWindow().getDecorView(); View decorView = getWindow().getDecorView();
// Full immersive mode - hide everything
WindowCompat.setDecorFitsSystemWindows(getWindow(), false); WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
WindowInsetsControllerCompat controller = WindowCompat.getInsetsController(getWindow(), decorView); WindowInsetsControllerCompat controller = WindowCompat.getInsetsController(getWindow(), decorView);
if (controller != null) { if (controller != null) {
// Hide both status and navigation bars // Hide BOTH status bar and navigation bar completely
controller.hide(WindowInsetsCompat.Type.systemBars()); controller.hide(WindowInsetsCompat.Type.systemBars());
// Make them sticky so they stay hidden // Swipe from edge to temporarily show bars
controller.setSystemBarsBehavior(WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); controller.setSystemBarsBehavior(WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
} }
// Additional flags for fullscreen // Set bars to transparent when they do show
getWindow().setStatusBarColor(android.graphics.Color.TRANSPARENT);
getWindow().setNavigationBarColor(android.graphics.Color.TRANSPARENT);
// Keep screen on + extend into cutout areas
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS getWindow().getAttributes().layoutInDisplayCutoutMode =
); WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
}
} }
} }

View file

@ -1,23 +1,32 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<!-- Base application theme. --> <!-- Base application theme with Edge-to-Edge support -->
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar"> <style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<!-- Customize your theme here. --> <!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item> <item name="colorAccent">@color/colorAccent</item>
<item name="android:windowBackground">@color/splash_background</item> <item name="android:windowBackground">@color/splash_background</item>
<item name="android:statusBarColor">@color/status_bar</item> <!-- Transparent system bars for edge-to-edge -->
<item name="android:navigationBarColor">@color/navigation_bar</item> <item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowTranslucentNavigation">false</item>
<item name="android:windowLightStatusBar">false</item>
<item name="android:windowLightNavigationBar">false</item>
<item name="android:enforceNavigationBarContrast">false</item>
<item name="android:enforceStatusBarContrast">false</item>
</style> </style>
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.DayNight.NoActionBar"> <style name="AppTheme.NoActionBar" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="windowActionBar">false</item> <item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item> <item name="windowNoTitle">true</item>
<item name="android:background">@null</item> <item name="android:background">@null</item>
<item name="android:statusBarColor">@color/status_bar</item> <item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@color/navigation_bar</item> <item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar">false</item>
<item name="android:windowLightNavigationBar">false</item>
</style> </style>
<style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen"> <style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen">

View file

@ -35,8 +35,8 @@ const config: CapacitorConfig = {
}, },
StatusBar: { StatusBar: {
style: 'DARK', style: 'DARK',
backgroundColor: '#0a0a0a', backgroundColor: '#00000000',
overlaysWebView: false overlaysWebView: true
}, },
App: { App: {
backButtonEnabled: true backButtonEnabled: true

View file

@ -4,6 +4,12 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=no, viewport-fit=cover" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=no, viewport-fit=cover" />
<!-- Samsung OneUI / Android Integration -->
<meta name="theme-color" content="#0a0a0a" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<title>AeThex OS - Operating System for the Metaverse</title> <title>AeThex OS - Operating System for the Metaverse</title>
<meta name="description" content="AeThex is the Operating System for the Metaverse. Join the network, earn credentials, and build the future with Axiom, Codex, and Aegis." /> <meta name="description" content="AeThex is the Operating System for the Metaverse. Join the network, earn credentials, and build the future with Axiom, Codex, and Aegis." />
<meta name="keywords" content="metaverse, web3, architects, game development, digital ecosystem, AeThex, Aegis, Codex, Axiom" /> <meta name="keywords" content="metaverse, web3, architects, game development, digital ecosystem, AeThex, Aegis, Codex, Axiom" />

View file

@ -164,4 +164,43 @@
background-size: 100% 4px; background-size: 100% 4px;
pointer-events: none; pointer-events: none;
} }
/* Safe Area Utilities for Edge-to-Edge */
.safe-top {
padding-top: env(safe-area-inset-top);
}
.safe-bottom {
padding-bottom: env(safe-area-inset-bottom);
}
.safe-left {
padding-left: env(safe-area-inset-left);
}
.safe-right {
padding-right: env(safe-area-inset-right);
}
.safe-all {
padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
}
}
/* Edge-to-Edge support for Capacitor apps */
html {
/* Ensure we use viewport-fit=cover for edge-to-edge */
overflow: hidden;
}
body {
/* Let content extend behind system bars */
position: fixed;
width: 100%;
height: 100%;
overflow: hidden;
}
/* Ensure WebView fills entire viewport including notch/punch-hole areas */
@supports (padding: env(safe-area-inset-top)) {
html, body {
min-height: 100vh;
min-height: -webkit-fill-available;
}
} }

View file

@ -10,3 +10,62 @@ export const isEmbedded = (): boolean => {
return true; return true;
} }
}; };
/**
* Detect if running on mobile device
*/
export const isMobileDevice = (): boolean => {
return typeof window !== 'undefined' && window.innerWidth < 768;
};
/**
* Get mobile theme based on Foundation/Corp mode
* Retrieves theme from localStorage (set by OS) or defaults to Foundation
*/
export const getMobileTheme = () => {
const clearanceMode = typeof localStorage !== 'undefined'
? localStorage.getItem('aethex-clearance') || 'foundation'
: 'foundation';
const isFoundation = clearanceMode === 'foundation';
return {
mode: clearanceMode as 'foundation' | 'corp',
isFoundation,
// Colors
primary: isFoundation ? 'rgb(220, 38, 38)' : 'rgb(59, 130, 246)',
secondary: isFoundation ? 'rgb(212, 175, 55)' : 'rgb(148, 163, 184)',
// Tailwind classes
primaryClass: isFoundation ? 'text-red-500' : 'text-blue-500',
secondaryClass: isFoundation ? 'text-amber-400' : 'text-slate-300',
borderClass: isFoundation ? 'border-red-900/50' : 'border-blue-900/50',
bgAccent: isFoundation ? 'bg-red-900/20' : 'bg-blue-900/20',
iconClass: isFoundation ? 'text-red-400' : 'text-blue-400',
activeBorder: isFoundation ? 'border-red-500' : 'border-blue-500',
activeBtn: isFoundation ? 'bg-red-600' : 'bg-blue-600',
hoverBtn: isFoundation ? 'hover:bg-red-700' : 'hover:bg-blue-700',
gradientBg: isFoundation
? 'linear-gradient(135deg, #0a0a0a 0%, #1a0505 50%, #0a0a0a 100%)'
: 'linear-gradient(135deg, #0a0a0a 0%, #050a14 50%, #0a0a0a 100%)',
cardBg: 'bg-zinc-900/80',
inputBg: 'bg-zinc-800/80',
};
};
/**
* Hook-like function to get mobile-aware styling
* Returns desktop styling when not on mobile, mobile styling otherwise
*/
export const getResponsiveStyles = () => {
const embedded = isEmbedded();
const mobile = isMobileDevice();
const theme = getMobileTheme();
return {
embedded,
mobile,
theme,
// Use mobile styles when embedded (in OS) or on mobile device
useMobileStyles: embedded || mobile,
};
};

View file

@ -16,6 +16,7 @@ import {
Download, Download,
Loader2 Loader2
} from "lucide-react"; } from "lucide-react";
import { isEmbedded, getResponsiveStyles } from "@/lib/embed-utils";
import { supabase } from "@/lib/supabase"; import { supabase } from "@/lib/supabase";
import { useAuth } from "@/lib/auth"; import { useAuth } from "@/lib/auth";
@ -128,6 +129,135 @@ export default function Analytics() {
...activityData.map(d => Math.max(d.projects, d.messages, d.earnings / 50, d.achievements)) ...activityData.map(d => Math.max(d.projects, d.messages, d.earnings / 50, d.achievements))
); );
const embedded = isEmbedded();
const { useMobileStyles, theme } = getResponsiveStyles();
// Mobile-optimized layout when embedded or on mobile device
if (useMobileStyles) {
return (
<div className="min-h-screen" style={{ background: theme.gradientBg }}>
<div className="p-4 pb-20">
{/* Mobile Header */}
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-3">
<div className={`w-10 h-10 rounded-xl ${theme.bgAccent} border ${theme.borderClass} flex items-center justify-center`}>
<BarChart3 className={`w-5 h-5 ${theme.iconClass}`} />
</div>
<div>
<h1 className={`${theme.primaryClass} font-bold text-lg`}>Analytics</h1>
<p className="text-zinc-500 text-xs">Track your growth</p>
</div>
</div>
<select
value={timeRange}
onChange={(e) => setTimeRange(e.target.value)}
className={`px-3 py-1.5 ${theme.inputBg} border border-zinc-700 rounded-lg text-xs text-zinc-300`}
>
<option value="7d">7 days</option>
<option value="30d">30 days</option>
<option value="90d">90 days</option>
</select>
</div>
{/* Loading State */}
{loading && (
<div className="flex items-center justify-center py-12">
<Loader2 className={`w-6 h-6 ${theme.iconClass} animate-spin`} />
</div>
)}
{/* Stats Grid */}
{!loading && (
<>
<div className="grid grid-cols-2 gap-2 mb-6">
{stats.slice(0, 4).map((stat, idx) => (
<div key={idx} className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-3`}>
<div className="flex items-center justify-between mb-2">
<div className={`p-2 bg-zinc-800 rounded-lg ${stat.color}`}>
{stat.icon}
</div>
<span className="text-[10px] font-semibold text-green-400 bg-green-500/10 px-1.5 py-0.5 rounded">
+{stat.change}%
</span>
</div>
<p className="text-[10px] text-zinc-500 mb-1">{stat.label}</p>
<p className="text-lg font-bold text-white">{stat.value}</p>
</div>
))}
</div>
{/* Activity Chart */}
<div className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-4 mb-4`}>
<h3 className="text-white font-bold text-sm mb-4 flex items-center gap-2">
<TrendingUp className={`w-4 h-4 ${theme.iconClass}`} />
Weekly Activity
</h3>
<div className="flex items-end justify-between gap-1 h-20 mb-2">
{activityData.map((data, idx) => (
<div key={idx} className="flex-1 flex flex-col items-center">
<div
className={`w-full ${theme.isFoundation ? 'bg-gradient-to-t from-red-500/40 to-red-500' : 'bg-gradient-to-t from-blue-500/40 to-blue-500'} rounded-t`}
style={{ height: `${(data.projects / maxValue) * 60}px` }}
/>
<p className="text-[8px] text-zinc-500 mt-1">{data.date}</p>
</div>
))}
</div>
</div>
{/* Top Activities */}
<div className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-4 mb-4`}>
<h3 className="text-white font-bold text-sm mb-3 flex items-center gap-2">
<Target className={`w-4 h-4 ${theme.iconClass}`} />
Top Activities
</h3>
<div className="space-y-2">
{topActivities.slice(0, 3).map((activity, idx) => (
<div key={idx} className="bg-zinc-800/50 p-3 rounded-lg">
<div className="flex items-center justify-between">
<span className="text-xs text-zinc-300">{activity.name}</span>
<span className="text-[10px] text-green-400 font-semibold">{activity.growth}</span>
</div>
<div className="text-sm font-bold text-white mt-1">{activity.count}</div>
</div>
))}
</div>
</div>
{/* Goals Progress */}
<div className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-4`}>
<h3 className="text-white font-bold text-sm mb-3 flex items-center gap-2">
<Zap className={`w-4 h-4 ${theme.iconClass}`} />
Goals
</h3>
<div className="space-y-3">
{[
{ goal: "Complete 15 Projects", current: 12, target: 15 },
{ goal: "Earn 5,000 LP", current: 2450, target: 5000 },
{ goal: "Unlock 30 Achievements", current: 23, target: 30 }
].map((item, idx) => (
<div key={idx}>
<div className="flex items-center justify-between mb-1">
<span className="text-xs text-zinc-400">{item.goal}</span>
<span className="text-[10px] text-zinc-500">{item.current}/{item.target}</span>
</div>
<div className="w-full bg-zinc-800 rounded-full h-1.5">
<div
className={`h-1.5 rounded-full ${theme.isFoundation ? 'bg-red-500' : 'bg-blue-500'}`}
style={{ width: `${(item.current / item.target) * 100}%` }}
/>
</div>
</div>
))}
</div>
</div>
</>
)}
</div>
</div>
);
}
return ( return (
<div className="min-h-screen bg-gradient-to-b from-slate-900 to-slate-950 text-slate-50 p-6"> <div className="min-h-screen bg-gradient-to-b from-slate-900 to-slate-950 text-slate-50 p-6">
<div className="max-w-7xl mx-auto"> <div className="max-w-7xl mx-auto">

View file

@ -3,7 +3,7 @@ import { Link } from "wouter";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card"; import { Card } from "@/components/ui/card";
import { ArrowLeft, TrendingUp, Code, Star, Eye, Heart, Share2, Loader2 } from "lucide-react"; import { ArrowLeft, TrendingUp, Code, Star, Eye, Heart, Share2, Loader2 } from "lucide-react";
import { isEmbedded } from "@/lib/embed-utils"; import { isEmbedded, getResponsiveStyles } from "@/lib/embed-utils";
import { supabase } from "@/lib/supabase"; import { supabase } from "@/lib/supabase";
import { useAuth } from "@/lib/auth"; import { useAuth } from "@/lib/auth";
@ -55,6 +55,120 @@ export default function CodeGallery() {
}; };
const embedded = isEmbedded(); const embedded = isEmbedded();
const { useMobileStyles, theme } = getResponsiveStyles();
// Mobile-optimized layout when embedded or on mobile device
if (useMobileStyles) {
return (
<div className="min-h-screen" style={{ background: theme.gradientBg }}>
<div className="p-4 pb-20">
{/* Mobile Header */}
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-3">
<div className={`w-10 h-10 rounded-xl ${theme.bgAccent} border ${theme.borderClass} flex items-center justify-center`}>
<Code className={`w-5 h-5 ${theme.iconClass}`} />
</div>
<div>
<h1 className={`${theme.primaryClass} font-bold text-lg`}>Code Gallery</h1>
<p className="text-zinc-500 text-xs">{snippets.length} snippets</p>
</div>
</div>
<Button className={`${theme.activeBtn} ${theme.hoverBtn} gap-1`} size="sm">
Share
</Button>
</div>
{/* Loading State */}
{loading && (
<div className="flex items-center justify-center py-12">
<Loader2 className={`w-6 h-6 ${theme.iconClass} animate-spin`} />
</div>
)}
{/* Snippets List */}
{!loading && (
<div className="space-y-3">
{snippets.length === 0 ? (
<div className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-8 text-center`}>
<Code className={`w-12 h-12 ${theme.iconClass} mx-auto mb-3 opacity-50`} />
<p className="text-zinc-500 text-sm">No snippets yet</p>
<p className="text-zinc-600 text-xs mt-1">Share your first code snippet</p>
</div>
) : (
snippets.map((snippet) => (
<div
key={snippet.id}
onClick={() => setSelectedSnippet(snippet)}
className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-4 active:scale-[0.98] transition-all ${
selectedSnippet?.id === snippet.id ? `border-2 ${theme.isFoundation ? 'border-red-500' : 'border-blue-500'}` : ''
}`}
>
{/* Language & Category */}
<div className="flex gap-2 mb-2">
<span className={`${theme.isFoundation ? 'bg-red-500' : 'bg-blue-500'} text-white text-[10px] px-2 py-0.5 rounded`}>
{snippet.language.toUpperCase()}
</span>
<span className="bg-purple-500 text-white text-[10px] px-2 py-0.5 rounded capitalize">
{snippet.category}
</span>
</div>
{/* Title & Author */}
<h3 className="text-white font-bold text-sm mb-1">{snippet.title}</h3>
<p className="text-zinc-400 text-xs mb-2">by {snippet.creator}</p>
{/* Stats */}
<div className="flex gap-4 text-xs text-zinc-500">
<span className="flex items-center gap-1">
<Eye className="w-3 h-3" /> {snippet.views}
</span>
<span className="flex items-center gap-1">
<Heart className="w-3 h-3" /> {snippet.likes}
</span>
</div>
</div>
))
)}
</div>
)}
{/* Selected Snippet Preview */}
{selectedSnippet && (
<div className={`mt-4 ${theme.cardBg} border ${theme.borderClass} rounded-xl overflow-hidden`}>
<div className={`px-4 py-3 border-b ${theme.borderClass} flex items-center justify-between`}>
<span className="text-white font-medium text-sm">{selectedSnippet.title}</span>
<button onClick={() => setSelectedSnippet(null)} className="text-zinc-400"></button>
</div>
{/* Code Preview */}
<div className="bg-zinc-950 p-4 font-mono text-xs text-zinc-300 overflow-x-auto max-h-40">
{selectedSnippet.code}
</div>
{/* Tags */}
<div className="px-4 py-3 flex flex-wrap gap-1">
{selectedSnippet.tags.map((tag) => (
<span key={tag} className={`${theme.bgAccent} ${theme.primaryClass} text-[10px] px-2 py-0.5 rounded`}>
#{tag}
</span>
))}
</div>
{/* Actions */}
<div className={`px-4 py-3 border-t ${theme.borderClass} flex gap-2`}>
<Button className={`flex-1 ${theme.activeBtn} ${theme.hoverBtn} gap-1 text-xs`} size="sm">
<Heart className="w-3 h-3" /> Like
</Button>
<Button variant="outline" className="flex-1 border-zinc-700 text-zinc-300 gap-1 text-xs" size="sm">
<Share2 className="w-3 h-3" /> Share
</Button>
</div>
</div>
)}
</div>
</div>
);
}
return ( return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 to-slate-800"> <div className="min-h-screen bg-gradient-to-br from-slate-900 to-slate-800">

View file

@ -2,8 +2,8 @@ import { useState, useEffect } from "react";
import { Link } from "wouter"; import { Link } from "wouter";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card"; import { Card } from "@/components/ui/card";
import { ArrowLeft, FileText, Folder, Plus, Trash2, Download, Copy, Loader2 } from "lucide-react"; import { ArrowLeft, FileText, Folder, Plus, Trash2, Download, Copy, Loader2, HardDrive } from "lucide-react";
import { isEmbedded } from "@/lib/embed-utils"; import { isEmbedded, getResponsiveStyles } from "@/lib/embed-utils";
import { supabase } from "@/lib/supabase"; import { supabase } from "@/lib/supabase";
import { useAuth } from "@/lib/auth"; import { useAuth } from "@/lib/auth";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
@ -72,6 +72,115 @@ export default function FileManager() {
}; };
const embedded = isEmbedded(); const embedded = isEmbedded();
const { useMobileStyles, theme } = getResponsiveStyles();
// Mobile-optimized layout when embedded or on mobile device
if (useMobileStyles) {
return (
<div className="min-h-screen" style={{ background: theme.gradientBg }}>
<div className="p-4 pb-20">
{/* Mobile Header */}
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-3">
<div className={`w-10 h-10 rounded-xl ${theme.bgAccent} border ${theme.borderClass} flex items-center justify-center`}>
<HardDrive className={`w-5 h-5 ${theme.iconClass}`} />
</div>
<div>
<h1 className={`${theme.primaryClass} font-bold text-lg`}>File Manager</h1>
<p className="text-zinc-500 text-xs">{files.length} files</p>
</div>
</div>
<Button className={`${theme.activeBtn} ${theme.hoverBtn} gap-1`} size="sm">
<Plus className="w-4 h-4" /> New
</Button>
</div>
{/* Breadcrumb */}
<div className={`${theme.cardBg} border ${theme.borderClass} rounded-xl px-4 py-2 mb-4 text-xs text-zinc-400`}>
<span className={theme.primaryClass}>root</span>
{currentPath.split("/").filter(Boolean).map((part, idx) => (
<span key={idx}> / <span className="text-zinc-400">{part}</span></span>
))}
</div>
{/* Loading State */}
{loading && (
<div className="flex items-center justify-center py-12">
<Loader2 className={`w-6 h-6 ${theme.iconClass} animate-spin`} />
</div>
)}
{/* File List */}
{!loading && (
<div className="space-y-2">
{files.length === 0 ? (
<div className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-8 text-center`}>
<Folder className={`w-12 h-12 ${theme.iconClass} mx-auto mb-3 opacity-50`} />
<p className="text-zinc-500 text-sm">No files yet</p>
<p className="text-zinc-600 text-xs mt-1">Upload or create a new file</p>
</div>
) : (
files.map((file) => (
<div
key={file.id}
onClick={() => setSelectedFile(file)}
className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-4 active:scale-[0.98] transition-all ${
selectedFile?.id === file.id ? `border-2 ${theme.isFoundation ? 'border-red-500' : 'border-blue-500'}` : ''
}`}
>
<div className="flex items-center gap-3">
{file.type === "folder" ? (
<Folder className={`w-8 h-8 ${theme.isFoundation ? 'text-red-400' : 'text-blue-400'}`} />
) : (
<FileText className="w-8 h-8 text-zinc-400" />
)}
<div className="flex-1 min-w-0">
<p className="text-white font-medium text-sm truncate">{file.name}</p>
<p className="text-[10px] text-zinc-500">
{file.type === "file" && file.language && `${file.language.toUpperCase()}`}
{formatFileSize(file.size)} {file.modified}
</p>
</div>
<button
onClick={(e) => {
e.stopPropagation();
deleteFile(file.id);
}}
className="text-red-400 p-2"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
</div>
))
)}
</div>
)}
{/* Selected File Actions */}
{selectedFile && selectedFile.type === "file" && (
<div className={`fixed bottom-20 left-4 right-4 ${theme.cardBg} border ${theme.borderClass} rounded-xl p-4`}>
<div className="flex items-center justify-between mb-3">
<div>
<p className="text-white font-medium text-sm">{selectedFile.name}</p>
<p className="text-[10px] text-zinc-500">{formatFileSize(selectedFile.size)}</p>
</div>
<button onClick={() => setSelectedFile(null)} className="text-zinc-400"></button>
</div>
<div className="flex gap-2">
<Button className={`flex-1 ${theme.activeBtn} ${theme.hoverBtn} gap-1 text-xs`} size="sm">
<Copy className="w-3 h-3" /> Copy
</Button>
<Button variant="outline" className="flex-1 border-zinc-700 text-zinc-300 gap-1 text-xs" size="sm">
<Download className="w-3 h-3" /> Download
</Button>
</div>
</div>
)}
</div>
</div>
);
}
return ( return (
<div className="h-screen flex flex-col bg-slate-900"> <div className="h-screen flex flex-col bg-slate-900">

View file

@ -8,7 +8,7 @@ import {
ArrowLeft, ShoppingCart, Star, Plus, Loader2, Gamepad2, ArrowLeft, ShoppingCart, Star, Plus, Loader2, Gamepad2,
Zap, Trophy, Users, DollarSign, TrendingUp, Filter, Search Zap, Trophy, Users, DollarSign, TrendingUp, Filter, Search
} from "lucide-react"; } from "lucide-react";
import { isEmbedded } from "@/lib/embed-utils"; import { isEmbedded, getResponsiveStyles } from "@/lib/embed-utils";
import { supabase } from "@/lib/supabase"; import { supabase } from "@/lib/supabase";
import { useAuth } from "@/lib/auth"; import { useAuth } from "@/lib/auth";
@ -198,6 +198,155 @@ export default function GameMarketplace() {
); );
const embedded = isEmbedded(); const embedded = isEmbedded();
const { useMobileStyles, theme } = getResponsiveStyles();
// Mobile-optimized layout when embedded or on mobile device
if (useMobileStyles) {
return (
<div className="min-h-screen" style={{ background: theme.gradientBg }}>
<div className="p-4 pb-20">
{/* Mobile Header */}
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-3">
<div className={`w-10 h-10 rounded-xl ${theme.bgAccent} border ${theme.borderClass} flex items-center justify-center`}>
<Gamepad2 className={`w-5 h-5 ${theme.iconClass}`} />
</div>
<div>
<h1 className={`${theme.primaryClass} font-bold text-lg`}>Game Shop</h1>
<p className="text-zinc-500 text-xs">{items.length} items</p>
</div>
</div>
<div className={`${theme.cardBg} px-3 py-1.5 rounded-lg border ${theme.borderClass} flex items-center gap-1`}>
<DollarSign className="w-3 h-3 text-yellow-400" />
<span className={`text-sm font-bold ${theme.primaryClass}`}>{wallet.balance} LP</span>
</div>
</div>
{/* Search */}
<div className="relative mb-4">
<Search className="absolute left-3 top-2.5 w-4 h-4 text-zinc-500" />
<input
placeholder="Search games, assets..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className={`w-full pl-10 pr-4 py-2 ${theme.inputBg} border border-zinc-700 rounded-xl text-white text-sm`}
/>
</div>
{/* Category Pills */}
<div className="flex gap-2 overflow-x-auto pb-2 mb-4 scrollbar-hide">
{["all", "game", "cosmetic", "pass", "asset"].map((cat) => (
<button
key={cat}
onClick={() => setSelectedCategory(cat)}
className={`px-3 py-1.5 rounded-lg text-xs font-medium whitespace-nowrap transition-colors ${
selectedCategory === cat
? `${theme.activeBtn} text-white`
: `${theme.cardBg} text-zinc-400 border ${theme.borderClass}`
}`}
>
{cat === "all" ? "All" : cat.charAt(0).toUpperCase() + cat.slice(1) + "s"}
</button>
))}
</div>
{/* Platform Pills */}
<div className="flex gap-2 overflow-x-auto pb-2 mb-4 scrollbar-hide">
{["all", "minecraft", "roblox", "steam", "meta"].map((platform) => (
<button
key={platform}
onClick={() => setSelectedPlatform(platform)}
className={`px-3 py-1 rounded-lg text-[10px] font-medium capitalize whitespace-nowrap ${
selectedPlatform === platform
? `${theme.bgAccent} ${theme.primaryClass}`
: 'bg-zinc-800 text-zinc-500'
}`}
>
{platform === "all" ? "All" : platform}
</button>
))}
</div>
{/* Loading State */}
{loading && (
<div className="flex items-center justify-center py-12">
<Loader2 className={`w-6 h-6 ${theme.iconClass} animate-spin`} />
</div>
)}
{/* Items Grid */}
{!loading && (
<div className="space-y-3">
{filteredItems.length === 0 ? (
<div className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-8 text-center`}>
<Gamepad2 className={`w-12 h-12 ${theme.iconClass} mx-auto mb-3 opacity-50`} />
<p className="text-zinc-500 text-sm">No items found</p>
</div>
) : (
filteredItems.map((item) => (
<div
key={item.id}
className={`${theme.cardBg} border ${theme.borderClass} rounded-xl overflow-hidden active:scale-[0.98] transition-transform`}
>
{/* Image */}
<div className="h-24 bg-gradient-to-br from-zinc-700 to-zinc-900 flex items-center justify-center text-4xl">
{item.image}
</div>
{/* Content */}
<div className="p-4">
{/* Badges */}
<div className="flex gap-2 mb-2">
<span className={`text-[10px] font-bold px-2 py-0.5 rounded capitalize ${
item.type === "game" ? "bg-purple-500/20 text-purple-400" :
item.type === "cosmetic" ? "bg-pink-500/20 text-pink-400" :
item.type === "pass" ? `${theme.bgAccent} ${theme.primaryClass}` :
"bg-green-500/20 text-green-400"
}`}>
{item.type}
</span>
<span className="text-[10px] text-zinc-500 bg-zinc-800 px-2 py-0.5 rounded capitalize">
{item.platform}
</span>
</div>
{/* Name */}
<h3 className="text-white font-bold text-sm mb-1 line-clamp-1">{item.name}</h3>
<p className="text-[10px] text-zinc-500 mb-2 line-clamp-1">{item.description}</p>
{/* Stats */}
<div className="flex items-center gap-3 mb-3 text-[10px] text-zinc-500">
<span className="flex items-center gap-1">
<Star className="w-3 h-3 text-yellow-400 fill-yellow-400" /> {item.rating}
</span>
<span>{item.purchases.toLocaleString()} sold</span>
</div>
{/* Price & Buy */}
<div className="flex items-center justify-between">
<div className={`text-lg font-bold ${theme.primaryClass}`}>
{item.price}
<span className="text-xs text-zinc-500 ml-1">LP</span>
</div>
<Button
onClick={() => handlePurchase(item)}
className={`${theme.activeBtn} ${theme.hoverBtn} gap-1 text-xs`}
size="sm"
disabled={wallet.balance < item.price}
>
<ShoppingCart className="w-3 h-3" /> Buy
</Button>
</div>
</div>
</div>
))
)}
</div>
)}
</div>
</div>
);
}
return ( return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 to-slate-950 text-white"> <div className="min-h-screen bg-gradient-to-br from-slate-900 to-slate-950 text-white">

View file

@ -5,9 +5,9 @@ import { Card } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { import {
ArrowLeft, Radio, Eye, Heart, MessageCircle, Share2, ArrowLeft, Radio, Eye, Heart, MessageCircle, Share2,
Twitch, Youtube, Play, Clock, Users, TrendingUp, Filter, Search Twitch, Youtube, Play, Clock, Users, TrendingUp, Filter, Search, Loader2
} from "lucide-react"; } from "lucide-react";
import { isEmbedded } from "@/lib/embed-utils"; import { isEmbedded, getResponsiveStyles } from "@/lib/embed-utils";
interface Stream { interface Stream {
id: string; id: string;
@ -182,6 +182,156 @@ export default function GameStreaming() {
const recordedStreams = filteredStreams.filter(s => !s.isLive); const recordedStreams = filteredStreams.filter(s => !s.isLive);
const embedded = isEmbedded(); const embedded = isEmbedded();
const { useMobileStyles, theme } = getResponsiveStyles();
// Mobile-optimized layout when embedded or on mobile device
if (useMobileStyles) {
return (
<div className="min-h-screen" style={{ background: theme.gradientBg }}>
<div className="p-4 pb-20">
{/* Mobile Header */}
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-3">
<div className={`w-10 h-10 rounded-xl ${theme.bgAccent} border ${theme.borderClass} flex items-center justify-center`}>
<Radio className={`w-5 h-5 ${theme.iconClass}`} />
</div>
<div>
<h1 className={`${theme.primaryClass} font-bold text-lg`}>Streaming</h1>
<p className="text-zinc-500 text-xs">{liveStreams.length} live now</p>
</div>
</div>
</div>
{/* Search */}
<div className="relative mb-4">
<Search className="absolute left-3 top-2.5 w-4 h-4 text-zinc-500" />
<input
placeholder="Search streams..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className={`w-full pl-10 pr-4 py-2 ${theme.inputBg} border border-zinc-700 rounded-xl text-white text-sm`}
/>
</div>
{/* Platform Pills */}
<div className="flex gap-2 mb-4">
{(["all", "twitch", "youtube"] as const).map((platform) => (
<button
key={platform}
onClick={() => setSelectedPlatform(platform)}
className={`px-3 py-1.5 rounded-lg text-xs font-medium capitalize ${
selectedPlatform === platform
? `${theme.activeBtn} text-white`
: `${theme.cardBg} text-zinc-400 border ${theme.borderClass}`
}`}
>
{platform === "all" ? "All" : platform}
</button>
))}
</div>
{/* Stats Cards */}
<div className="grid grid-cols-2 gap-2 mb-6">
{mockStats.slice(0, 2).map((stat, idx) => (
<div key={idx} className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-3`}>
<div className="flex items-center justify-between mb-1">
<span className="text-[10px] text-zinc-500 uppercase">{stat.label}</span>
{stat.icon}
</div>
<div className="text-lg font-bold text-white">{stat.value}</div>
{stat.change && <span className="text-[10px] text-green-400">{stat.change}</span>}
</div>
))}
</div>
{/* Loading State */}
{loading && (
<div className="flex items-center justify-center py-12">
<Loader2 className={`w-6 h-6 ${theme.iconClass} animate-spin`} />
</div>
)}
{/* Live Streams */}
{!loading && liveStreams.length > 0 && (
<div className="mb-6">
<div className="flex items-center gap-2 mb-3">
<Radio className="w-4 h-4 text-red-500 animate-pulse" />
<h2 className="text-white font-bold text-sm">Live Now ({liveStreams.length})</h2>
</div>
<div className="space-y-3">
{liveStreams.map((stream) => (
<div
key={stream.id}
className={`${theme.cardBg} border-2 border-red-500/30 rounded-xl overflow-hidden active:scale-[0.98] transition-transform`}
>
{/* Thumbnail */}
<div className="relative h-32 bg-gradient-to-br from-zinc-700 to-zinc-900 flex items-center justify-center text-3xl">
{stream.thumbnail}
<div className="absolute top-2 left-2 bg-red-500 text-white text-[10px] px-2 py-0.5 rounded font-bold flex items-center gap-1">
<Radio className="w-2 h-2 animate-pulse" /> LIVE
</div>
</div>
{/* Info */}
<div className="p-3">
<h3 className="text-white font-bold text-sm mb-1 line-clamp-2">{stream.title}</h3>
<p className="text-[10px] text-zinc-400 mb-2">{stream.channel} {stream.game}</p>
<div className="flex items-center justify-between">
<span className="flex items-center gap-1 text-[10px] text-zinc-500">
<Eye className="w-3 h-3" /> {stream.viewers.toLocaleString()}
</span>
<Button className={`${theme.activeBtn} ${theme.hoverBtn} gap-1 text-xs`} size="sm">
<Play className="w-3 h-3" /> Watch
</Button>
</div>
</div>
</div>
))}
</div>
</div>
)}
{/* Recorded Streams */}
{!loading && recordedStreams.length > 0 && (
<div>
<h2 className="text-white font-bold text-sm mb-3">Recorded</h2>
<div className="space-y-2">
{recordedStreams.map((stream) => (
<div
key={stream.id}
className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-3 active:scale-[0.98] transition-transform`}
>
<div className="flex gap-3">
<div className="w-20 h-14 bg-gradient-to-br from-zinc-700 to-zinc-900 rounded flex items-center justify-center text-xl flex-shrink-0 relative">
{stream.thumbnail}
{stream.duration && (
<div className="absolute bottom-1 right-1 bg-black/80 text-white text-[8px] px-1 rounded">
{stream.duration}
</div>
)}
</div>
<div className="flex-1 min-w-0">
<h3 className="text-white font-medium text-xs mb-1 line-clamp-2">{stream.title}</h3>
<p className="text-[10px] text-zinc-500">{stream.channel}</p>
<div className="flex items-center gap-2 mt-1 text-[10px] text-zinc-500">
<span className="flex items-center gap-1">
<Eye className="w-2 h-2" /> {stream.viewers.toLocaleString()}
</span>
<span className="flex items-center gap-1">
<Heart className="w-2 h-2" /> {stream.likes.toLocaleString()}
</span>
</div>
</div>
</div>
</div>
))}
</div>
</div>
)}
</div>
</div>
);
}
return ( return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 to-slate-950 text-white"> <div className="min-h-screen bg-gradient-to-br from-slate-900 to-slate-950 text-white">

View file

@ -7,7 +7,7 @@ import {
Trash2, Award, User, Calendar, Search, Filter, Plus, Loader2, Trash2, Award, User, Calendar, Search, Filter, Plus, Loader2,
Package, AlertCircle, CheckCircle Package, AlertCircle, CheckCircle
} from "lucide-react"; } from "lucide-react";
import { isEmbedded } from "@/lib/embed-utils"; import { isEmbedded, getResponsiveStyles } from "@/lib/embed-utils";
interface Mod { interface Mod {
id: string; id: string;
@ -201,6 +201,233 @@ export default function ModWorkshop() {
const games = ["all", "Minecraft", "Roblox", "Steam Games", "All Games"]; const games = ["all", "Minecraft", "Roblox", "Steam Games", "All Games"];
const embedded = isEmbedded(); const embedded = isEmbedded();
const { useMobileStyles, theme } = getResponsiveStyles();
// Mobile-optimized layout when embedded or on mobile device
if (useMobileStyles) {
return (
<div className="min-h-screen" style={{ background: theme.gradientBg }}>
<div className="p-4 pb-20">
{/* Mobile Header */}
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-3">
<div className={`w-10 h-10 rounded-xl ${theme.bgAccent} border ${theme.borderClass} flex items-center justify-center`}>
<Package className={`w-5 h-5 ${theme.iconClass}`} />
</div>
<div>
<h1 className={`${theme.primaryClass} font-bold text-lg`}>Mod Workshop</h1>
<p className="text-zinc-500 text-xs">{sortedMods.length} mods</p>
</div>
</div>
<Button
onClick={() => setShowUploadModal(true)}
className={`${theme.activeBtn} ${theme.hoverBtn} gap-1`}
size="sm"
>
<Upload className="w-4 h-4" />
</Button>
</div>
{/* Search */}
<div className="relative mb-4">
<Search className="absolute left-3 top-2.5 w-4 h-4 text-zinc-500" />
<input
placeholder="Search mods..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className={`w-full pl-10 pr-4 py-2 ${theme.inputBg} border border-zinc-700 rounded-xl text-white text-sm`}
/>
</div>
{/* Category Pills */}
<div className="flex gap-2 overflow-x-auto pb-2 mb-4 scrollbar-hide">
{(["all", "gameplay", "cosmetic", "utility", "enhancement"] as const).map((cat) => (
<button
key={cat}
onClick={() => setSelectedCategory(cat)}
className={`px-3 py-1.5 rounded-lg text-xs font-medium whitespace-nowrap transition-colors capitalize ${
selectedCategory === cat
? `${theme.activeBtn} text-white`
: `${theme.cardBg} text-zinc-400 border ${theme.borderClass}`
}`}
>
{cat === "all" ? "All" : cat}
</button>
))}
</div>
{/* Sort & Game Filter */}
<div className="flex gap-2 mb-4">
<select
value={sortBy}
onChange={(e) => setSortBy(e.target.value as any)}
className={`flex-1 px-3 py-2 ${theme.inputBg} border border-zinc-700 rounded-lg text-white text-xs`}
>
<option value="trending">Trending</option>
<option value="newest">Newest</option>
<option value="popular">Most Downloaded</option>
<option value="rating">Highest Rated</option>
</select>
<select
value={selectedGame}
onChange={(e) => setSelectedGame(e.target.value)}
className={`flex-1 px-3 py-2 ${theme.inputBg} border border-zinc-700 rounded-lg text-white text-xs`}
>
{games.map(game => (
<option key={game} value={game}>
{game === "all" ? "All Games" : game}
</option>
))}
</select>
</div>
{/* Mods Grid */}
<div className="space-y-3">
{sortedMods.length === 0 ? (
<div className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-8 text-center`}>
<Package className={`w-12 h-12 ${theme.iconClass} mx-auto mb-3 opacity-50`} />
<p className="text-zinc-500 text-sm">No mods found</p>
</div>
) : (
sortedMods.map((mod) => (
<div
key={mod.id}
className={`${theme.cardBg} border ${theme.borderClass} rounded-xl overflow-hidden active:scale-[0.98] transition-transform`}
>
{/* Image */}
<div className="h-24 bg-gradient-to-br from-zinc-700 to-zinc-900 flex items-center justify-center text-4xl relative">
{mod.image}
<div className={`absolute top-2 right-2 px-2 py-0.5 rounded text-[10px] font-bold flex items-center gap-1 ${
mod.status === "approved" ? "bg-green-500/20 text-green-400" :
mod.status === "reviewing" ? "bg-yellow-500/20 text-yellow-400" :
"bg-red-500/20 text-red-400"
}`}>
{mod.status === "approved" ? <CheckCircle className="w-2 h-2" /> : <AlertCircle className="w-2 h-2" />}
{mod.status}
</div>
</div>
{/* Content */}
<div className="p-4">
{/* Badges */}
<div className="flex gap-2 mb-2">
<span className={`text-[10px] font-bold px-2 py-0.5 rounded capitalize ${
mod.category === "gameplay" ? "bg-purple-500/20 text-purple-400" :
mod.category === "cosmetic" ? "bg-pink-500/20 text-pink-400" :
mod.category === "utility" ? `${theme.bgAccent} ${theme.primaryClass}` :
"bg-green-500/20 text-green-400"
}`}>
{mod.category}
</span>
<span className="text-[10px] text-zinc-500 bg-zinc-800 px-2 py-0.5 rounded">
v{mod.version}
</span>
</div>
{/* Name & Author */}
<h3 className="text-white font-bold text-sm mb-1 line-clamp-1">{mod.name}</h3>
<p className="text-[10px] text-zinc-400 flex items-center gap-1 mb-2">
<User className="w-2 h-2" /> {mod.author}
</p>
{/* Stats */}
<div className="grid grid-cols-2 gap-2 mb-3 text-[10px]">
<div className="bg-zinc-800/50 p-2 rounded">
<div className="text-zinc-500">Rating</div>
<div className="font-bold text-yellow-400">{mod.rating} </div>
</div>
<div className="bg-zinc-800/50 p-2 rounded">
<div className="text-zinc-500">Downloads</div>
<div className={`font-bold ${theme.primaryClass}`}>{(mod.downloads / 1000).toFixed(0)}K</div>
</div>
</div>
{/* Download Button */}
<Button className={`w-full ${theme.activeBtn} ${theme.hoverBtn} gap-1 text-xs`} size="sm">
<Download className="w-3 h-3" /> Download ({mod.fileSize})
</Button>
</div>
</div>
))
)}
</div>
</div>
{/* Upload Modal */}
{showUploadModal && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
<div className={`${theme.cardBg} border ${theme.borderClass} rounded-xl w-full max-w-sm p-4`}>
<div className="flex items-center justify-between mb-4">
<h2 className="text-white font-bold text-sm">Upload Mod</h2>
<button onClick={() => setShowUploadModal(false)} className="text-zinc-400"></button>
</div>
{uploadStatus === "idle" && (
<div className="space-y-3">
<div
className={`border-2 border-dashed ${theme.borderClass} rounded-xl p-6 text-center cursor-pointer`}
onClick={handleUploadClick}
>
<Upload className={`w-6 h-6 mx-auto mb-2 ${theme.iconClass}`} />
<p className="text-xs text-zinc-400">Tap to select mod file</p>
<input
ref={fileInputRef}
type="file"
accept=".zip,.rar"
onChange={handleFileChange}
className="hidden"
/>
</div>
<input
type="text"
placeholder="Mod Title"
className={`w-full px-3 py-2 ${theme.inputBg} border border-zinc-700 rounded-lg text-white text-xs`}
/>
<textarea
placeholder="Description..."
rows={2}
className={`w-full px-3 py-2 ${theme.inputBg} border border-zinc-700 rounded-lg text-white text-xs resize-none`}
/>
<div className="flex gap-2">
<Button onClick={() => setShowUploadModal(false)} variant="outline" className="flex-1 border-zinc-700 text-xs" size="sm">
Cancel
</Button>
<Button onClick={() => setUploadStatus("uploading")} className={`flex-1 ${theme.activeBtn} text-xs`} size="sm">
Upload
</Button>
</div>
</div>
)}
{uploadStatus === "uploading" && (
<div className="text-center py-6">
<Loader2 className={`w-6 h-6 mx-auto mb-3 animate-spin ${theme.iconClass}`} />
<p className="text-zinc-400 text-xs">Uploading...</p>
</div>
)}
{uploadStatus === "success" && (
<div className="text-center py-6">
<CheckCircle className="w-6 h-6 mx-auto mb-3 text-green-400" />
<p className="text-white text-xs font-bold">Upload Complete!</p>
</div>
)}
{uploadStatus === "error" && (
<div className="text-center py-6">
<AlertCircle className="w-6 h-6 mx-auto mb-3 text-red-400" />
<p className="text-white text-xs font-bold">Upload Failed</p>
<Button onClick={() => setUploadStatus("idle")} className={`mt-3 ${theme.activeBtn} text-xs`} size="sm">
Try Again
</Button>
</div>
)}
</div>
</div>
)}
</div>
);
}
return ( return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 to-slate-950 text-white"> <div className="min-h-screen bg-gradient-to-br from-slate-900 to-slate-950 text-white">
@ -271,25 +498,26 @@ export default function ModWorkshop() {
{cat === "all" ? "All Categories" : cat} {cat === "all" ? "All Categories" : cat}
</button> </button>
))} ))}
</div> </div>
<div className="ml-auto"> <div className="ml-auto">
<select <select
value={selectedGame} value={selectedGame}
onChange={(e) => setSelectedGame(e.target.value)} onChange={(e) => setSelectedGame(e.target.value)}
className="px-3 py-1 bg-slate-800 border border-slate-700 rounded-lg text-white text-xs" className="px-3 py-1 bg-slate-800 border border-slate-700 rounded-lg text-white text-xs"
> >
{games.map(game => ( {games.map(game => (
<option key={game} value={game}> <option key={game} value={game}>
{game === "all" ? "All Games" : game} {game === "all" ? "All Games" : game}
</option> </option>
))} ))}
</select> </select>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> )}
{/* Mods Grid */} {/* Mods Grid */}
<div className="max-w-7xl mx-auto p-4 md:p-6"> <div className="max-w-7xl mx-auto p-4 md:p-6">

View file

@ -5,7 +5,7 @@ import { Input } from "@/components/ui/input";
import { Card } from "@/components/ui/card"; import { Card } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { ArrowLeft, ShoppingCart, Star, Plus, Loader2 } from "lucide-react"; import { ArrowLeft, ShoppingCart, Star, Plus, Loader2 } from "lucide-react";
import { isEmbedded } from "@/lib/embed-utils"; import { isEmbedded, getResponsiveStyles } from "@/lib/embed-utils";
import { supabase } from "@/lib/supabase"; import { supabase } from "@/lib/supabase";
import { useAuth } from "@/lib/auth"; import { useAuth } from "@/lib/auth";
@ -77,6 +77,106 @@ export default function Marketplace() {
}; };
const embedded = isEmbedded(); const embedded = isEmbedded();
const { useMobileStyles, theme } = getResponsiveStyles();
// Mobile-optimized layout when embedded or on mobile device
if (useMobileStyles) {
return (
<div className="min-h-screen" style={{ background: theme.gradientBg }}>
<div className="p-4 pb-20">
{/* Mobile Header */}
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-3">
<div className={`w-10 h-10 rounded-xl ${theme.bgAccent} border ${theme.borderClass} flex items-center justify-center`}>
<ShoppingCart className={`w-5 h-5 ${theme.iconClass}`} />
</div>
<div>
<h1 className={`${theme.primaryClass} font-bold text-lg`}>Marketplace</h1>
<p className="text-zinc-500 text-xs">{listings.length} items</p>
</div>
</div>
<div className={`${theme.cardBg} px-3 py-1.5 rounded-lg border ${theme.borderClass}`}>
<p className={`text-sm font-bold ${theme.primaryClass}`}>{balance} LP</p>
</div>
</div>
{/* Category Pills */}
<div className="flex gap-2 overflow-x-auto pb-2 mb-4 scrollbar-hide">
{["all", "code", "achievement", "service", "credential"].map((cat) => (
<button
key={cat}
onClick={() => setSelectedCategory(cat)}
className={`px-3 py-1.5 rounded-lg text-xs font-medium whitespace-nowrap transition-colors ${
selectedCategory === cat
? `${theme.activeBtn} text-white`
: `${theme.cardBg} text-zinc-400 border ${theme.borderClass}`
}`}
>
{cat === "all" ? "All" : cat.charAt(0).toUpperCase() + cat.slice(1)}
</button>
))}
</div>
{/* Loading State */}
{loading && (
<div className="flex items-center justify-center py-12">
<Loader2 className={`w-6 h-6 ${theme.iconClass} animate-spin`} />
</div>
)}
{/* Listings Grid */}
{!loading && (
<div className="space-y-3">
{filteredListings.length === 0 ? (
<div className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-8 text-center`}>
<ShoppingCart className={`w-12 h-12 ${theme.iconClass} mx-auto mb-3 opacity-50`} />
<p className="text-zinc-500 text-sm">No items found</p>
</div>
) : (
filteredListings.map((listing) => (
<div
key={listing.id}
className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-4 active:scale-[0.98] transition-transform`}
>
{/* Category Badge */}
<div className="mb-2">
<span className={`${getCategoryColor(listing.category)} text-white text-[10px] font-bold px-2 py-0.5 rounded capitalize`}>
{listing.category}
</span>
</div>
{/* Title */}
<h3 className="text-white font-bold text-sm mb-1">{listing.title}</h3>
<p className="text-zinc-400 text-xs mb-2">by {listing.seller}</p>
{/* Rating & Stats */}
<div className="flex items-center gap-3 mb-3 text-xs text-zinc-500">
<span className="flex items-center gap-1">
<Star className="w-3 h-3 text-yellow-400 fill-yellow-400" />
{listing.rating}
</span>
<span>{listing.purchases} sold</span>
</div>
{/* Price & Buy */}
<div className="flex items-center justify-between">
<div className={`text-lg font-bold ${theme.primaryClass}`}>
{listing.price}
<span className="text-xs text-zinc-500 ml-1">LP</span>
</div>
<Button className={`${theme.activeBtn} ${theme.hoverBtn} gap-1 text-xs`} size="sm">
<ShoppingCart className="w-3 h-3" /> Buy
</Button>
</div>
</div>
))
)}
</div>
)}
</div>
</div>
);
}
return ( return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 to-slate-800"> <div className="min-h-screen bg-gradient-to-br from-slate-900 to-slate-800">

View file

@ -3,9 +3,9 @@ import { Link, useLocation } from "wouter";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Card } from "@/components/ui/card"; import { Card } from "@/components/ui/card";
import { ArrowLeft, Send, Search, Loader2 } from "lucide-react"; import { ArrowLeft, Send, Search, Loader2, MessageCircle } from "lucide-react";
import { MobileHeader } from "@/components/mobile/MobileHeader"; import { MobileHeader } from "@/components/mobile/MobileHeader";
import { isEmbedded } from "@/lib/embed-utils"; import { isEmbedded, getResponsiveStyles } from "@/lib/embed-utils";
import { supabase } from "@/lib/supabase"; import { supabase } from "@/lib/supabase";
import { useAuth } from "@/lib/auth"; import { useAuth } from "@/lib/auth";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
@ -98,6 +98,127 @@ export default function Messaging() {
); );
const embedded = isEmbedded(); const embedded = isEmbedded();
const { useMobileStyles, theme } = getResponsiveStyles();
// Mobile-optimized layout when embedded or on mobile device
if (useMobileStyles) {
return (
<div className="min-h-screen" style={{ background: theme.gradientBg }}>
<div className="p-4 pb-20">
{/* Mobile Header */}
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-3">
<div className={`w-10 h-10 rounded-xl ${theme.bgAccent} border ${theme.borderClass} flex items-center justify-center`}>
<MessageCircle className={`w-5 h-5 ${theme.iconClass}`} />
</div>
<div>
<h1 className={`${theme.primaryClass} font-bold text-lg`}>Messages</h1>
<p className="text-zinc-500 text-xs">{chats.length} conversations</p>
</div>
</div>
</div>
{/* Search */}
<div className="relative mb-4">
<Search className="absolute left-3 top-2.5 w-4 h-4 text-zinc-500" />
<Input
placeholder="Search conversations..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className={`${theme.inputBg} border-zinc-700 text-white pl-10 text-sm`}
/>
</div>
{/* Loading State */}
{loading && (
<div className="flex items-center justify-center py-12">
<Loader2 className={`w-6 h-6 ${theme.iconClass} animate-spin`} />
</div>
)}
{/* Chat List */}
{!loading && (
<div className="space-y-2">
{filteredChats.length === 0 ? (
<div className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-8 text-center`}>
<MessageCircle className={`w-12 h-12 ${theme.iconClass} mx-auto mb-3 opacity-50`} />
<p className="text-zinc-500 text-sm">No conversations yet</p>
<p className="text-zinc-600 text-xs mt-1">Start a new conversation</p>
</div>
) : (
filteredChats.map((chat) => (
<button
key={chat.id}
onClick={() => setSelectedChatId(chat.id)}
className={`w-full text-left ${theme.cardBg} border ${theme.borderClass} rounded-xl p-4 active:scale-[0.98] transition-all ${
selectedChatId === chat.id ? `border-2 ${theme.isFoundation ? 'border-red-500' : 'border-blue-500'}` : ''
}`}
>
<div className="flex items-center gap-3">
<div className={`w-10 h-10 rounded-full ${theme.bgAccent} flex items-center justify-center`}>
<span className="text-white font-bold text-sm">{chat.username[0]}</span>
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center justify-between mb-1">
<span className="text-white font-medium text-sm">{chat.username}</span>
<span className="text-xs text-zinc-500">{chat.timestamp}</span>
</div>
<p className={`text-xs truncate ${chat.unread ? 'text-white font-semibold' : 'text-zinc-400'}`}>
{chat.lastMessage}
</p>
</div>
{chat.unread && (
<div className={`w-2 h-2 rounded-full ${theme.isFoundation ? 'bg-red-500' : 'bg-blue-500'}`} />
)}
</div>
</button>
))
)}
</div>
)}
{/* Selected Chat Messages */}
{selectedChat && (
<div className={`mt-4 ${theme.cardBg} border ${theme.borderClass} rounded-xl overflow-hidden`}>
<div className={`px-4 py-3 border-b ${theme.borderClass} flex items-center justify-between`}>
<div className="flex items-center gap-2">
<span className="text-white font-medium text-sm">{selectedChat.username}</span>
<span className="text-xs text-green-400">Online</span>
</div>
<button onClick={() => setSelectedChatId("")} className="text-zinc-400"></button>
</div>
<div className="h-48 overflow-y-auto p-4 space-y-3">
{messages.map((msg) => (
<div key={msg.id} className={`flex ${msg.isOwn ? 'justify-end' : 'justify-start'}`}>
<div className={`max-w-[80%] px-3 py-2 rounded-lg text-xs ${
msg.isOwn
? `${theme.isFoundation ? 'bg-red-600' : 'bg-blue-600'} text-white`
: 'bg-zinc-700 text-zinc-100'
}`}>
<p>{msg.content}</p>
<span className="text-[10px] opacity-70 mt-1 block">{msg.timestamp}</span>
</div>
</div>
))}
</div>
<div className={`p-3 border-t ${theme.borderClass} flex gap-2`}>
<Input
placeholder="Type a message..."
value={messageInput}
onChange={(e) => setMessageInput(e.target.value)}
onKeyPress={(e) => e.key === "Enter" && handleSendMessage()}
className={`${theme.inputBg} border-zinc-700 text-white text-sm flex-1`}
/>
<Button onClick={handleSendMessage} className={`${theme.activeBtn} ${theme.hoverBtn} px-3`} size="sm">
<Send className="w-4 h-4" />
</Button>
</div>
</div>
)}
</div>
</div>
);
}
return ( return (
<div className="h-screen flex flex-col bg-slate-900"> <div className="h-screen flex flex-col bg-slate-900">

View file

@ -3,6 +3,7 @@ import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card"; import { Card } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Bell, Check, Trash2, Filter, CheckCircle, Loader2 } from "lucide-react"; import { Bell, Check, Trash2, Filter, CheckCircle, Loader2 } from "lucide-react";
import { isEmbedded, getResponsiveStyles } from "@/lib/embed-utils";
import { supabase } from "@/lib/supabase"; import { supabase } from "@/lib/supabase";
import { useAuth } from "@/lib/auth"; import { useAuth } from "@/lib/auth";
@ -122,6 +123,148 @@ export default function Notifications() {
return date.toLocaleDateString(); return date.toLocaleDateString();
}; };
const embedded = isEmbedded();
const { useMobileStyles, theme } = getResponsiveStyles();
// Mobile-optimized layout when embedded or on mobile device
if (useMobileStyles) {
return (
<div className="min-h-screen" style={{ background: theme.gradientBg }}>
<div className="p-4 pb-20">
{/* Mobile Header */}
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-3">
<div className={`w-10 h-10 rounded-xl ${theme.bgAccent} border ${theme.borderClass} flex items-center justify-center`}>
<Bell className={`w-5 h-5 ${theme.iconClass}`} />
</div>
<div>
<h1 className={`${theme.primaryClass} font-bold text-lg`}>Notifications</h1>
<p className="text-zinc-500 text-xs">
{unreadCount > 0 ? `${unreadCount} unread` : "All caught up!"}
</p>
</div>
</div>
{unreadCount > 0 && (
<Button
onClick={handleMarkAllAsRead}
variant="outline"
size="sm"
className={`border ${theme.borderClass} text-xs gap-1`}
>
<CheckCircle className="w-3 h-3" />
All
</Button>
)}
</div>
{/* Filter Pills */}
<div className="flex gap-2 overflow-x-auto pb-2 mb-4 scrollbar-hide">
{[
{ key: null, label: "All" },
{ key: "achievement", label: "🏆" },
{ key: "message", label: "💬" },
{ key: "event", label: "📅" },
{ key: "marketplace", label: "🛍️" }
].map((filter) => (
<button
key={filter.key || "all"}
onClick={() => setFilterType(filter.key)}
className={`px-3 py-1.5 rounded-lg text-xs font-medium whitespace-nowrap transition-colors ${
filterType === filter.key
? `${theme.activeBtn} text-white`
: `${theme.cardBg} text-zinc-400 border ${theme.borderClass}`
}`}
>
{filter.label}
</button>
))}
</div>
{/* Loading State */}
{loading && (
<div className="flex items-center justify-center py-12">
<Loader2 className={`w-6 h-6 ${theme.iconClass} animate-spin`} />
</div>
)}
{/* Notifications List */}
{!loading && (
<div className="space-y-2">
{filteredNotifications.length === 0 ? (
<div className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-8 text-center`}>
<Bell className={`w-12 h-12 ${theme.iconClass} mx-auto mb-3 opacity-50`} />
<p className="text-zinc-500 text-sm">No notifications</p>
</div>
) : (
filteredNotifications.map((notification) => (
<div
key={notification.id}
className={`${theme.cardBg} border-l-4 ${
notification.read
? `border ${theme.borderClass} border-l-zinc-600`
: `border ${theme.borderClass} ${theme.isFoundation ? 'border-l-red-400' : 'border-l-blue-400'}`
} rounded-xl p-4 active:scale-[0.98] transition-all`}
>
<div className="flex items-start gap-3">
<div className="text-xl mt-0.5">{getTypeIcon(notification.type)}</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1">
<h3 className="text-white font-medium text-sm">{notification.title}</h3>
{!notification.read && (
<span className={`w-2 h-2 rounded-full ${theme.isFoundation ? 'bg-red-400' : 'bg-blue-400'}`} />
)}
</div>
<p className="text-zinc-400 text-xs mb-2 line-clamp-2">{notification.description}</p>
<p className="text-zinc-600 text-[10px]">{formatTime(notification.timestamp)}</p>
</div>
<div className="flex flex-col gap-1">
{!notification.read && (
<button
onClick={() => handleMarkAsRead(notification.id)}
className={`p-1.5 rounded ${theme.bgAccent}`}
>
<Check className={`w-3 h-3 ${theme.iconClass}`} />
</button>
)}
<button
onClick={() => handleDelete(notification.id)}
className="p-1.5 rounded bg-red-500/10"
>
<Trash2 className="w-3 h-3 text-red-400" />
</button>
</div>
</div>
</div>
))
)}
</div>
)}
{/* Notification Preferences */}
<div className={`mt-6 ${theme.cardBg} border ${theme.borderClass} rounded-xl p-4`}>
<h3 className="text-white font-bold text-sm mb-3 flex items-center gap-2">
<Filter className={`w-4 h-4 ${theme.iconClass}`} />
Preferences
</h3>
<div className="space-y-3">
{[
{ label: "Achievement Notifications", enabled: true },
{ label: "Message Alerts", enabled: true },
{ label: "Event Reminders", enabled: true },
{ label: "Marketplace Updates", enabled: false }
].map((pref, idx) => (
<div key={idx} className="flex items-center justify-between">
<label className="text-xs text-zinc-400">{pref.label}</label>
<div className={`w-8 h-4 rounded-full ${pref.enabled ? (theme.isFoundation ? 'bg-red-600' : 'bg-blue-600') : 'bg-zinc-700'}`} />
</div>
))}
</div>
</div>
</div>
</div>
);
}
return ( return (
<div className="min-h-screen bg-gradient-to-b from-slate-900 to-slate-950 text-slate-50 p-6"> <div className="min-h-screen bg-gradient-to-b from-slate-900 to-slate-950 text-slate-50 p-6">
<div className="max-w-4xl mx-auto"> <div className="max-w-4xl mx-auto">

View file

@ -4,9 +4,9 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Card } from "@/components/ui/card"; import { Card } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { ArrowLeft, Plus, Trash2, ExternalLink, Github, Globe, Loader2 } from "lucide-react"; import { ArrowLeft, Plus, Trash2, ExternalLink, Github, Globe, Loader2, FolderKanban } from "lucide-react";
import { MobileHeader } from "@/components/mobile/MobileHeader"; import { MobileHeader } from "@/components/mobile/MobileHeader";
import { isEmbedded } from "@/lib/embed-utils"; import { isEmbedded, getResponsiveStyles } from "@/lib/embed-utils";
import { supabase } from "@/lib/supabase"; import { supabase } from "@/lib/supabase";
import { useAuth } from "@/lib/auth"; import { useAuth } from "@/lib/auth";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
@ -104,7 +104,168 @@ export default function Projects() {
}; };
const embedded = isEmbedded(); const embedded = isEmbedded();
const { useMobileStyles, theme } = getResponsiveStyles();
// Mobile-optimized layout when embedded or on mobile device
if (useMobileStyles) {
return (
<div className="min-h-screen" style={{ background: theme.gradientBg }}>
<div className="p-4 pb-20">
{/* Mobile Header */}
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-3">
<div className={`w-10 h-10 rounded-xl ${theme.bgAccent} border ${theme.borderClass} flex items-center justify-center`}>
<FolderKanban className={`w-5 h-5 ${theme.iconClass}`} />
</div>
<div>
<h1 className={`${theme.primaryClass} font-bold text-lg`}>Projects</h1>
<p className="text-zinc-500 text-xs">{projects.length} total</p>
</div>
</div>
<Button
onClick={() => setShowForm(!showForm)}
className={`${theme.activeBtn} ${theme.hoverBtn} gap-2`}
size="sm"
>
<Plus className="w-4 h-4" />
New
</Button>
</div>
{/* Add Project Form */}
{showForm && (
<div className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-4 mb-6`}>
<h2 className={`text-sm font-bold ${theme.secondaryClass} mb-4`}>Create New Project</h2>
<div className="space-y-3">
<Input
placeholder="Project Title"
value={newProject.title}
onChange={(e) => setNewProject({ ...newProject, title: e.target.value })}
className={`${theme.inputBg} border-zinc-700 text-white text-sm`}
/>
<textarea
placeholder="Description..."
value={newProject.description}
onChange={(e) => setNewProject({ ...newProject, description: e.target.value })}
className={`w-full ${theme.inputBg} border border-zinc-700 text-white rounded-lg px-3 py-2 text-sm focus:outline-none focus:${theme.activeBorder}`}
rows={2}
/>
<Input
placeholder="Technologies (comma-separated)"
value={newProject.technologies}
onChange={(e) => setNewProject({ ...newProject, technologies: e.target.value })}
className={`${theme.inputBg} border-zinc-700 text-white text-sm`}
/>
<div className="flex gap-2">
<Button onClick={handleAddProject} className={`flex-1 ${theme.activeBtn} ${theme.hoverBtn}`} size="sm">
Create
</Button>
<Button onClick={() => setShowForm(false)} variant="outline" className="border-zinc-700 text-zinc-400" size="sm">
Cancel
</Button>
</div>
</div>
</div>
)}
{/* Loading State */}
{loading && (
<div className="flex items-center justify-center py-12">
<Loader2 className={`w-6 h-6 ${theme.iconClass} animate-spin`} />
</div>
)}
{/* Projects Grid */}
{!loading && (
<div className="space-y-3">
{projects.length === 0 ? (
<div className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-8 text-center`}>
<FolderKanban className={`w-12 h-12 ${theme.iconClass} mx-auto mb-3 opacity-50`} />
<p className="text-zinc-500 text-sm">No projects yet</p>
<p className="text-zinc-600 text-xs mt-1">Create your first project to get started</p>
</div>
) : (
projects.map((project) => (
<div
key={project.id}
className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-4 active:scale-[0.98] transition-transform`}
>
{/* Header */}
<div className="flex items-start justify-between mb-3">
<div className="flex-1">
<div className="flex items-center gap-2 mb-1">
<span className={`${getStatusColor(project.status)} text-white text-[10px] font-bold px-2 py-0.5 rounded capitalize`}>
{project.status}
</span>
</div>
<h3 className="text-white font-bold text-sm">{project.title}</h3>
</div>
<button onClick={() => deleteProject(project.id)} className="text-red-400 p-2 -m-2">
<Trash2 className="w-4 h-4" />
</button>
</div>
{/* Description */}
{project.description && (
<p className="text-zinc-400 text-xs mb-3 line-clamp-2">{project.description}</p>
)}
{/* Progress */}
<div className="mb-3">
<div className="flex justify-between mb-1">
<span className="text-xs text-zinc-500">Progress</span>
<span className={`text-xs ${theme.primaryClass}`}>{project.progress}%</span>
</div>
<div className="w-full bg-zinc-800 rounded-full h-1.5">
<div
className={`h-1.5 rounded-full ${theme.isFoundation ? 'bg-red-500' : 'bg-blue-500'}`}
style={{ width: `${project.progress}%` }}
/>
</div>
</div>
{/* Technologies */}
{Array.isArray(project.tech_stack) && project.tech_stack.length > 0 && (
<div className="flex flex-wrap gap-1.5 mb-3">
{project.tech_stack.slice(0, 4).map((tech) => (
<span key={tech} className={`${theme.bgAccent} ${theme.primaryClass} text-[10px] px-2 py-0.5 rounded`}>
{tech}
</span>
))}
{project.tech_stack.length > 4 && (
<span className="text-zinc-500 text-[10px] px-2 py-0.5">+{project.tech_stack.length - 4}</span>
)}
</div>
)}
{/* Links */}
{(project.live_url || project.github_url) && (
<div className="flex gap-2">
{project.live_url && (
<a href={project.live_url} target="_blank" rel="noopener noreferrer"
className={`flex-1 flex items-center justify-center gap-1.5 ${theme.activeBtn} text-white text-xs font-medium py-2 rounded-lg`}>
<Globe className="w-3.5 h-3.5" /> Live
</a>
)}
{project.github_url && (
<a href={project.github_url} target="_blank" rel="noopener noreferrer"
className="flex-1 flex items-center justify-center gap-1.5 bg-zinc-800 text-white text-xs font-medium py-2 rounded-lg">
<Github className="w-3.5 h-3.5" /> Code
</a>
)}
</div>
)}
</div>
))
)}
</div>
)}
</div>
</div>
);
}
// Desktop layout (original)
return ( return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 to-slate-800"> <div className="min-h-screen bg-gradient-to-br from-slate-900 to-slate-800">
{/* Headers - hidden when embedded in OS iframe */} {/* Headers - hidden when embedded in OS iframe */}

View file

@ -3,7 +3,7 @@ import { Link } from "wouter";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card"; import { Card } from "@/components/ui/card";
import { ArrowLeft, Settings, Bell, Lock, Palette, HardDrive, User, Loader2 } from "lucide-react"; import { ArrowLeft, Settings, Bell, Lock, Palette, HardDrive, User, Loader2 } from "lucide-react";
import { isEmbedded } from "@/lib/embed-utils"; import { isEmbedded, getResponsiveStyles } from "@/lib/embed-utils";
import { supabase } from "@/lib/supabase"; import { supabase } from "@/lib/supabase";
import { useAuth } from "@/lib/auth"; import { useAuth } from "@/lib/auth";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
@ -90,6 +90,189 @@ export default function SettingsWorkspace() {
}; };
const embedded = isEmbedded(); const embedded = isEmbedded();
const { useMobileStyles, theme } = getResponsiveStyles();
// Mobile-optimized layout when embedded or on mobile device
if (useMobileStyles) {
return (
<div className="min-h-screen" style={{ background: theme.gradientBg }}>
<div className="p-4 pb-20">
{/* Mobile Header */}
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-3">
<div className={`w-10 h-10 rounded-xl ${theme.bgAccent} border ${theme.borderClass} flex items-center justify-center`}>
<Settings className={`w-5 h-5 ${theme.iconClass}`} />
</div>
<div>
<h1 className={`${theme.primaryClass} font-bold text-lg`}>Settings</h1>
<p className="text-zinc-500 text-xs">Workspace preferences</p>
</div>
</div>
</div>
{/* Loading State */}
{loading && (
<div className="flex items-center justify-center py-12">
<Loader2 className={`w-6 h-6 ${theme.iconClass} animate-spin`} />
</div>
)}
{/* Settings Sections */}
{!loading && (
<div className="space-y-4">
{/* Appearance */}
<div className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-4`}>
<div className="flex items-center gap-2 mb-4">
<Palette className={`w-4 h-4 ${theme.iconClass}`} />
<h2 className="text-white font-bold text-sm">Appearance</h2>
</div>
<div className="space-y-3">
<div>
<label className="block text-xs text-zinc-400 mb-1.5">Theme</label>
<select
value={settings.theme}
onChange={(e) => handleChange("theme", e.target.value)}
className={`w-full ${theme.inputBg} border border-zinc-700 text-white text-sm rounded-lg px-3 py-2`}
>
<option value="dark">Dark</option>
<option value="light">Light</option>
<option value="auto">Auto (System)</option>
</select>
</div>
<div>
<label className="block text-xs text-zinc-400 mb-1.5">Font Size</label>
<select
value={settings.fontSize}
onChange={(e) => handleChange("fontSize", e.target.value)}
className={`w-full ${theme.inputBg} border border-zinc-700 text-white text-sm rounded-lg px-3 py-2`}
>
<option value="small">Small</option>
<option value="medium">Medium</option>
<option value="large">Large</option>
</select>
</div>
<div className="flex items-center justify-between pt-2">
<span className="text-xs text-zinc-400">Collapse Sidebar</span>
<button
onClick={() => handleToggle("sidebarCollapsed")}
className={`w-10 h-5 rounded-full transition-colors ${
settings.sidebarCollapsed ? (theme.isFoundation ? 'bg-red-600' : 'bg-blue-600') : 'bg-zinc-700'
}`}
/>
</div>
</div>
</div>
{/* Notifications */}
<div className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-4`}>
<div className="flex items-center gap-2 mb-4">
<Bell className={`w-4 h-4 ${theme.iconClass}`} />
<h2 className="text-white font-bold text-sm">Notifications</h2>
</div>
<div className="space-y-3">
<div className="flex items-center justify-between">
<div>
<p className="text-xs text-zinc-300">Push Notifications</p>
<p className="text-[10px] text-zinc-500">Get alerts for events</p>
</div>
<button
onClick={() => handleToggle("notificationsEnabled")}
className={`w-10 h-5 rounded-full transition-colors ${
settings.notificationsEnabled ? (theme.isFoundation ? 'bg-red-600' : 'bg-blue-600') : 'bg-zinc-700'
}`}
/>
</div>
<div className="flex items-center justify-between">
<div>
<p className="text-xs text-zinc-300">Email Notifications</p>
<p className="text-[10px] text-zinc-500">Receive email digests</p>
</div>
<button
onClick={() => handleToggle("emailNotifications")}
className={`w-10 h-5 rounded-full transition-colors ${
settings.emailNotifications ? (theme.isFoundation ? 'bg-red-600' : 'bg-blue-600') : 'bg-zinc-700'
}`}
/>
</div>
<div className="flex items-center justify-between">
<div>
<p className="text-xs text-zinc-300">Sound Effects</p>
<p className="text-[10px] text-zinc-500">Play notification sounds</p>
</div>
<button
onClick={() => handleToggle("soundEnabled")}
className={`w-10 h-5 rounded-full transition-colors ${
settings.soundEnabled ? (theme.isFoundation ? 'bg-red-600' : 'bg-blue-600') : 'bg-zinc-700'
}`}
/>
</div>
</div>
</div>
{/* Editor */}
<div className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-4`}>
<div className="flex items-center gap-2 mb-4">
<HardDrive className={`w-4 h-4 ${theme.iconClass}`} />
<h2 className="text-white font-bold text-sm">Editor</h2>
</div>
<div className="flex items-center justify-between">
<div>
<p className="text-xs text-zinc-300">Auto-save</p>
<p className="text-[10px] text-zinc-500">Automatically save work</p>
</div>
<button
onClick={() => handleToggle("autoSave")}
className={`w-10 h-5 rounded-full transition-colors ${
settings.autoSave ? (theme.isFoundation ? 'bg-red-600' : 'bg-blue-600') : 'bg-zinc-700'
}`}
/>
</div>
</div>
{/* Privacy */}
<div className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-4`}>
<div className="flex items-center gap-2 mb-4">
<Lock className={`w-4 h-4 ${theme.iconClass}`} />
<h2 className="text-white font-bold text-sm">Privacy</h2>
</div>
<div className="space-y-3">
<div>
<label className="block text-xs text-zinc-400 mb-1.5">Profile Privacy</label>
<select
value={settings.privacyLevel}
onChange={(e) => handleChange("privacyLevel", e.target.value)}
className={`w-full ${theme.inputBg} border border-zinc-700 text-white text-sm rounded-lg px-3 py-2`}
>
<option value="private">Private (Only you)</option>
<option value="friends">Friends Only</option>
<option value="public">Public</option>
</select>
</div>
</div>
</div>
{/* Account */}
<div className={`${theme.cardBg} border ${theme.borderClass} rounded-xl p-4`}>
<div className="flex items-center gap-2 mb-4">
<User className={`w-4 h-4 ${theme.iconClass}`} />
<h2 className="text-white font-bold text-sm">Account</h2>
</div>
<div className="space-y-3">
<div className={`${theme.inputBg} p-3 rounded-lg`}>
<p className="text-[10px] text-zinc-500">Email</p>
<p className="text-white text-xs font-medium">user@example.com</p>
</div>
<Button variant="outline" className={`w-full border-red-600 text-red-400 text-xs`} size="sm">
Log Out
</Button>
</div>
</div>
</div>
)}
</div>
</div>
);
}
return ( return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 to-slate-800"> <div className="min-h-screen bg-gradient-to-br from-slate-900 to-slate-800">

View file

@ -171,8 +171,8 @@ export default function AeThexOS() {
const native = useNativeFeatures(); const native = useNativeFeatures();
const biometric = useBiometricAuth(); const biometric = useBiometricAuth();
// Skip boot sequence on mobile // Mobile also gets a quick boot sequence
const [isBooting, setIsBooting] = useState(!layout.isMobile); const [isBooting, setIsBooting] = useState(true);
const [bootProgress, setBootProgress] = useState(0); const [bootProgress, setBootProgress] = useState(0);
const [bootStep, setBootStep] = useState(''); const [bootStep, setBootStep] = useState('');
const [windows, setWindows] = useState<WindowState[]>([]); const [windows, setWindows] = useState<WindowState[]>([]);
@ -869,6 +869,8 @@ export default function AeThexOS() {
theme={theme} theme={theme}
setTheme={setTheme} setTheme={setTheme}
savedLayouts={savedLayouts} savedLayouts={savedLayouts}
clearanceMode={clearanceMode}
setClearanceMode={setClearanceMode}
onSaveLayout={(name) => { onSaveLayout={(name) => {
const layout: DesktopLayout = { const layout: DesktopLayout = {
name, name,
@ -959,6 +961,122 @@ export default function AeThexOS() {
high: 'bg-red-500/20 border-red-500/50' high: 'bg-red-500/20 border-red-500/50'
}; };
// Mobile boot screen - simplified and themed
if (layout.isMobile) {
const isFoundation = clearanceMode === 'foundation';
const bootTheme = {
primary: isFoundation ? 'text-red-500' : 'text-blue-500',
secondary: isFoundation ? 'text-amber-400' : 'text-slate-300',
glow: isFoundation ? 'from-red-600 to-amber-500' : 'from-blue-600 to-cyan-400',
bar: isFoundation ? 'from-red-600 via-red-500 to-amber-500' : 'from-blue-600 via-blue-500 to-cyan-400',
};
return (
<div className="h-screen w-screen bg-black flex flex-col items-center justify-center relative overflow-hidden">
{/* Animated background grid */}
<div className="absolute inset-0 opacity-10">
<div className="absolute inset-0" style={{
backgroundImage: `linear-gradient(${isFoundation ? 'rgba(220,38,38,0.3)' : 'rgba(59,130,246,0.3)'} 1px, transparent 1px),
linear-gradient(90deg, ${isFoundation ? 'rgba(220,38,38,0.3)' : 'rgba(59,130,246,0.3)'} 1px, transparent 1px)`,
backgroundSize: '50px 50px',
animation: 'pulse 2s ease-in-out infinite'
}} />
</div>
{/* Logo with animated glow */}
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
className="relative mb-8"
>
<div className={`absolute inset-0 bg-gradient-to-br ${bootTheme.glow} rounded-3xl blur-2xl opacity-50 animate-pulse`} />
<div className={`relative w-24 h-24 bg-gradient-to-br ${bootTheme.glow} rounded-3xl flex items-center justify-center`}>
<div className="absolute inset-1 bg-black rounded-2xl" />
<span className="relative text-4xl font-display font-bold text-white z-10">Æ</span>
</div>
</motion.div>
{/* Brand name */}
<motion.h1
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className={`text-3xl font-display font-bold ${bootTheme.primary} mb-2`}
>
AETHEX OS
</motion.h1>
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.3 }}
className={`text-sm font-mono ${bootTheme.secondary} opacity-60 uppercase tracking-widest mb-12`}
>
{isFoundation ? 'FOUNDATION' : 'CORPORATION'}
</motion.p>
{/* Boot step with cursor */}
<motion.div
key={bootStep}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className={`${bootTheme.primary} font-mono text-xs mb-6 h-5 text-center px-8`}
>
{bootStep}
<motion.span
animate={{ opacity: [1, 0] }}
transition={{ duration: 0.5, repeat: Infinity }}
>
_
</motion.span>
</motion.div>
{/* Progress bar */}
<div className="w-64 px-4">
<div className="flex justify-between text-[10px] font-mono text-white/40 mb-2">
<span>INITIALIZING</span>
<span>{bootProgress}%</span>
</div>
<div className="h-1.5 bg-zinc-900 rounded-full overflow-hidden border border-white/10">
<motion.div
className={`h-full bg-gradient-to-r ${bootTheme.bar} relative`}
initial={{ width: 0 }}
animate={{ width: `${bootProgress}%` }}
transition={{ duration: 0.3 }}
>
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/30 to-transparent animate-pulse" />
</motion.div>
</div>
</div>
{/* Continue button when ready */}
<AnimatePresence>
{showLoginPrompt && (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0 }}
className="mt-12"
>
<motion.button
onClick={handleGuestContinue}
whileTap={{ scale: 0.95 }}
className={`px-8 py-4 bg-gradient-to-r ${bootTheme.bar} rounded-2xl font-mono font-bold uppercase tracking-wider text-black shadow-lg`}
>
Enter System
</motion.button>
</motion.div>
)}
</AnimatePresence>
{/* Version info */}
<div className="absolute bottom-8 text-white/20 text-[10px] font-mono">
v4.2.1 {isFoundation ? 'FOUNDATION' : 'CORP'} BUILD
</div>
</div>
);
}
// Desktop boot screen
return ( return (
<div className="h-screen w-screen bg-black relative overflow-hidden"> <div className="h-screen w-screen bg-black relative overflow-hidden">
{/* Scan lines overlay */} {/* Scan lines overlay */}
@ -1221,23 +1339,31 @@ export default function AeThexOS() {
useEffect(() => { useEffect(() => {
if (!layout.isMobile || !isMobile()) return; if (!layout.isMobile || !isMobile()) return;
const backHandler = CapacitorApp.addListener('backButton', () => { let backHandler: any = null;
// Get current active windows (non-minimized)
const activeWindows = windows.filter(w => !w.minimized); const setupBackHandler = async () => {
backHandler = await CapacitorApp.addListener('backButton', () => {
if (activeWindows.length > 0) { // Get current active windows (non-minimized)
// Close the topmost window const activeWindows = windows.filter(w => !w.minimized);
const topWindow = activeWindows[activeWindows.length - 1];
closeWindow(topWindow.id); if (activeWindows.length > 0) {
impact('light'); // Close the topmost window
} else { const topWindow = activeWindows[activeWindows.length - 1];
// No windows open - minimize app (don't exit) closeWindow(topWindow.id);
CapacitorApp.minimizeApp(); impact('light');
} } else {
}); // No windows open - minimize app (don't exit)
CapacitorApp.minimizeApp();
}
});
};
setupBackHandler();
return () => { return () => {
backHandler.remove(); if (backHandler && typeof backHandler.remove === 'function') {
backHandler.remove();
}
}; };
}, [layout.isMobile, windows, closeWindow, impact]); }, [layout.isMobile, windows, closeWindow, impact]);
@ -1262,9 +1388,16 @@ export default function AeThexOS() {
}; };
return ( return (
<div className="h-screen w-screen overflow-hidden flex flex-col" style={{ background: mobileTheme.gradientBg }}> <div
className="h-screen w-screen overflow-hidden flex flex-col"
style={{
background: mobileTheme.gradientBg,
paddingTop: 'env(safe-area-inset-top)',
paddingBottom: 'env(safe-area-inset-bottom)',
}}
>
{/* AeThex Mobile Status Bar */} {/* AeThex Mobile Status Bar */}
<div className={`relative h-10 bg-black/90 ${mobileTheme.borderClass} border-b shrink-0`} style={{ paddingTop: 'env(safe-area-inset-top)' }}> <div className={`relative h-10 bg-black/80 backdrop-blur-md ${mobileTheme.borderClass} border-b shrink-0`}>
<div className="relative flex items-center justify-between px-4 h-full"> <div className="relative flex items-center justify-between px-4 h-full">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<span className={`${mobileTheme.primaryClass} font-bold text-sm font-mono`}>AeThex</span> <span className={`${mobileTheme.primaryClass} font-bold text-sm font-mono`}>AeThex</span>
@ -1425,10 +1558,9 @@ export default function AeThexOS() {
</AnimatePresence> </AnimatePresence>
</div> </div>
{/* Bottom Navigation */} {/* Bottom Navigation - transparent for gesture nav integration */}
<div <div
className={`bg-black/95 border-t ${mobileTheme.borderClass} shrink-0`} className={`bg-black/80 backdrop-blur-md border-t ${mobileTheme.borderClass} shrink-0`}
style={{ paddingBottom: 'env(safe-area-inset-bottom)' }}
> >
<div className="flex items-center justify-around py-2 px-4"> <div className="flex items-center justify-around py-2 px-4">
<button <button
@ -4951,7 +5083,7 @@ function PitchApp({ onNavigate }: { onNavigate: () => void }) {
); );
} }
function SettingsApp({ wallpaper, setWallpaper, soundEnabled, setSoundEnabled, secretsUnlocked, theme, setTheme, savedLayouts, onSaveLayout, onLoadLayout, onDeleteLayout }: { function SettingsApp({ wallpaper, setWallpaper, soundEnabled, setSoundEnabled, secretsUnlocked, theme, setTheme, savedLayouts, onSaveLayout, onLoadLayout, onDeleteLayout, clearanceMode, setClearanceMode }: {
wallpaper: typeof WALLPAPERS[0]; wallpaper: typeof WALLPAPERS[0];
setWallpaper: (w: typeof WALLPAPERS[0]) => void; setWallpaper: (w: typeof WALLPAPERS[0]) => void;
soundEnabled: boolean; soundEnabled: boolean;
@ -4963,11 +5095,210 @@ function SettingsApp({ wallpaper, setWallpaper, soundEnabled, setSoundEnabled, s
onSaveLayout: (name: string) => void; onSaveLayout: (name: string) => void;
onLoadLayout: (layout: DesktopLayout) => void; onLoadLayout: (layout: DesktopLayout) => void;
onDeleteLayout: (name: string) => void; onDeleteLayout: (name: string) => void;
clearanceMode?: 'foundation' | 'corp';
setClearanceMode?: (mode: 'foundation' | 'corp') => void;
}) { }) {
const [layoutName, setLayoutName] = useState(''); const [layoutName, setLayoutName] = useState('');
const [activeTab, setActiveTab] = useState<'appearance' | 'layouts' | 'system'>('appearance'); const [activeTab, setActiveTab] = useState<'appearance' | 'layouts' | 'system'>('appearance');
const visibleWallpapers = WALLPAPERS.filter(wp => !wp.secret || secretsUnlocked); const visibleWallpapers = WALLPAPERS.filter(wp => !wp.secret || secretsUnlocked);
const isMobileView = typeof window !== 'undefined' && window.innerWidth < 768;
// Mobile-specific theme colors based on clearance mode
const isFoundation = clearanceMode === 'foundation';
const mobileTheme = {
primary: isFoundation ? 'rgb(220, 38, 38)' : 'rgb(59, 130, 246)',
primaryClass: isFoundation ? 'text-red-500' : 'text-blue-500',
secondaryClass: isFoundation ? 'text-amber-400' : 'text-slate-300',
borderClass: isFoundation ? 'border-red-900/50' : 'border-blue-900/50',
bgAccent: isFoundation ? 'bg-red-900/20' : 'bg-blue-900/20',
iconClass: isFoundation ? 'text-red-400' : 'text-blue-400',
activeBtn: isFoundation ? 'bg-red-600' : 'bg-blue-500',
activeBorder: isFoundation ? 'border-red-500' : 'border-blue-500',
};
// Mobile Settings UI
if (isMobileView) {
return (
<div className="h-full bg-black flex flex-col overflow-hidden">
{/* Mobile Tab Bar */}
<div className={`flex border-b ${mobileTheme.borderClass} bg-black/90 shrink-0`}>
{(['appearance', 'system'] as const).map(tab => (
<button
key={tab}
onClick={() => setActiveTab(tab as any)}
className={`flex-1 px-4 py-4 text-xs font-mono uppercase tracking-wider transition-colors ${
activeTab === tab
? `${mobileTheme.primaryClass} border-b-2 ${mobileTheme.activeBorder}`
: 'text-zinc-500'
}`}
>
{tab}
</button>
))}
</div>
{/* Mobile Content */}
<div className="flex-1 overflow-auto p-4 pb-20">
{activeTab === 'appearance' && (
<div className="space-y-6">
{/* Theme Toggle */}
{setClearanceMode && (
<div className={`p-4 rounded-xl bg-zinc-900/80 border ${mobileTheme.borderClass}`}>
<div className={`text-xs ${mobileTheme.secondaryClass} uppercase tracking-wider mb-3 font-bold`}>
Identity Mode
</div>
<div className="grid grid-cols-2 gap-3">
<button
onClick={() => setClearanceMode('foundation')}
className={`p-4 rounded-xl border transition-all ${
clearanceMode === 'foundation'
? 'border-red-500 bg-red-900/30'
: 'border-zinc-800 bg-zinc-900/50'
}`}
>
<div className="text-red-500 font-bold text-sm mb-1">Foundation</div>
<div className="text-zinc-500 text-xs">Red & Gold</div>
</button>
<button
onClick={() => setClearanceMode('corp')}
className={`p-4 rounded-xl border transition-all ${
clearanceMode === 'corp'
? 'border-blue-500 bg-blue-900/30'
: 'border-zinc-800 bg-zinc-900/50'
}`}
>
<div className="text-blue-500 font-bold text-sm mb-1">Corporation</div>
<div className="text-zinc-500 text-xs">Blue & Silver</div>
</button>
</div>
</div>
)}
{/* Accent Color */}
<div className={`p-4 rounded-xl bg-zinc-900/80 border ${mobileTheme.borderClass}`}>
<div className={`text-xs ${mobileTheme.secondaryClass} uppercase tracking-wider mb-3 font-bold`}>
Accent Color
</div>
<div className="grid grid-cols-6 gap-3">
{ACCENT_COLORS.map(color => (
<button
key={color.id}
onClick={() => setTheme({ ...theme, accentColor: color.id })}
className={`w-12 h-12 rounded-xl transition-all ${color.bg} ${
theme.accentColor === color.id
? 'ring-2 ring-white ring-offset-2 ring-offset-black scale-110'
: 'active:scale-95'
}`}
/>
))}
</div>
</div>
{/* Wallpaper Selection */}
<div className={`p-4 rounded-xl bg-zinc-900/80 border ${mobileTheme.borderClass}`}>
<div className={`text-xs ${mobileTheme.secondaryClass} uppercase tracking-wider mb-3 font-bold`}>
Wallpaper {secretsUnlocked && <span className="text-yellow-400 ml-2"> UNLOCKED</span>}
</div>
<div className="grid grid-cols-2 gap-3">
{visibleWallpapers.map(wp => (
<button
key={wp.id}
onClick={() => setWallpaper(wp)}
className={`p-3 rounded-xl border transition-all active:scale-95 ${
wallpaper.id === wp.id
? `${mobileTheme.activeBorder} ${mobileTheme.bgAccent}`
: wp.secret
? 'border-yellow-500/30'
: 'border-zinc-800'
}`}
>
<div className="w-full h-16 rounded-lg mb-2" style={{ background: wp.bg }} />
<div className="text-xs text-zinc-300 font-medium">{wp.name}</div>
</button>
))}
</div>
</div>
{/* Transparency */}
<div className={`p-4 rounded-xl bg-zinc-900/80 border ${mobileTheme.borderClass}`}>
<div className={`text-xs ${mobileTheme.secondaryClass} uppercase tracking-wider mb-3 font-bold`}>
Transparency
</div>
<input
type="range"
min="50"
max="100"
value={theme.transparency}
onChange={e => setTheme({ ...theme, transparency: parseInt(e.target.value) })}
className={`w-full h-2 rounded-full appearance-none bg-zinc-800 ${isFoundation ? 'accent-red-500' : 'accent-blue-500'}`}
/>
<div className="flex justify-between text-xs text-zinc-500 mt-2">
<span>Glass</span>
<span className={mobileTheme.primaryClass}>{theme.transparency}%</span>
<span>Solid</span>
</div>
</div>
</div>
)}
{activeTab === 'system' && (
<div className="space-y-4">
{/* Sound Toggle */}
<div className={`flex items-center justify-between p-4 rounded-xl bg-zinc-900/80 border ${mobileTheme.borderClass}`}>
<div>
<div className="text-white text-sm font-medium">Sound Effects</div>
<div className="text-zinc-500 text-xs">UI interaction feedback</div>
</div>
<button
onClick={() => setSoundEnabled(!soundEnabled)}
className={`w-14 h-8 rounded-full relative transition-all ${soundEnabled ? mobileTheme.activeBtn : 'bg-zinc-700'}`}
>
<div className={`absolute top-1 w-6 h-6 bg-white rounded-full transition-all shadow-lg ${soundEnabled ? 'right-1' : 'left-1'}`} />
</button>
</div>
{/* Version Info */}
<div className={`p-4 rounded-xl ${mobileTheme.bgAccent} border ${mobileTheme.borderClass}`}>
<div className={`${mobileTheme.primaryClass} text-sm font-mono font-bold`}>AeThex OS v3.0.0</div>
<div className="text-zinc-500 text-xs mt-1">Build 2025.12.17 Mobile</div>
</div>
{/* Secrets Hint */}
{!secretsUnlocked && (
<div className={`p-4 rounded-xl bg-zinc-900/80 border ${mobileTheme.borderClass} text-center`}>
<div className="text-zinc-500 text-xs font-mono">🔒 Hidden features available...</div>
<div className="text-zinc-600 text-[10px] mt-1">Try the Konami Code</div>
</div>
)}
{/* Device Info */}
<div className={`p-4 rounded-xl bg-zinc-900/80 border ${mobileTheme.borderClass}`}>
<div className={`text-xs ${mobileTheme.secondaryClass} uppercase tracking-wider mb-3 font-bold`}>
Device
</div>
<div className="space-y-2 text-xs">
<div className="flex justify-between">
<span className="text-zinc-500">Platform</span>
<span className="text-zinc-300">Android (Capacitor)</span>
</div>
<div className="flex justify-between">
<span className="text-zinc-500">Mode</span>
<span className={mobileTheme.primaryClass}>{isFoundation ? 'Foundation' : 'Corporation'}</span>
</div>
<div className="flex justify-between">
<span className="text-zinc-500">Session</span>
<span className="text-zinc-300">Active</span>
</div>
</div>
</div>
</div>
)}
</div>
</div>
);
}
// Desktop Settings UI (original)
return ( return (
<div className="h-full bg-slate-950 flex flex-col"> <div className="h-full bg-slate-950 flex flex-col">
<div className="flex border-b border-white/10"> <div className="flex border-b border-white/10">