Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions src/app/[...skillPath]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -221,14 +221,15 @@ export default function SkillPage({
}
};

// Invalid path
if (!isValidPath) {
return (
<main className="relative min-h-screen bg-[#0a0a0a] flex flex-col overflow-hidden">
<GlowMesh />
<SiteHeader />
<div className="flex-1 flex items-center justify-center relative z-10 px-6">
<div className="flex flex-col items-center animate-fade-in">
<div className="text-white font-semibold mb-2">Invalid skill path</div>
<h1 className="text-white font-semibold mb-2">Invalid skill path</h1>
<div className="text-white/50 text-sm mb-6">Expected format: /owner/repo/skill-name</div>
<Link href="/" className="px-6 py-3 bg-white text-black text-sm font-medium hover:bg-white/90 transition-colors">Go home</Link>
</div>
Expand All @@ -248,15 +249,15 @@ export default function SkillPage({
<div className="flex-1 flex items-center justify-center relative z-10 px-6">
<div className="w-full max-w-[640px]">
{phase === "config" && !authLoaded && (
<div className="flex items-center justify-center py-20">
<div className="w-8 h-8 rounded-full border-2 border-white/10 border-t-white/50 animate-spin" />
<div className="flex items-center justify-center py-20" role="status" aria-label="Loading">
<div className="w-8 h-8 rounded-full border-2 border-white/10 border-t-white/50 animate-spin" aria-hidden="true" />
</div>
)}

{phase === "config" && authLoaded && !isSignedIn && (
<div className="animate-fade-in">
<div className="border border-white/20 bg-black/40 backdrop-blur-sm p-8 text-center">
<svg className="w-10 h-10 mx-auto mb-4 text-white/30" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<div className="border border-white/20 bg-black/40 p-8 text-center">
<svg className="w-10 h-10 mx-auto mb-4 text-white/30" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5} aria-hidden="true">
<path strokeLinecap="round" strokeLinejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H6.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z" />
</svg>
<h2 className="text-lg font-semibold text-white/90 mb-2">Sign in to continue</h2>
Expand All @@ -269,8 +270,8 @@ export default function SkillPage({
)}

{phase === "config" && isSignedIn && keysLoading && (
<div className="flex items-center justify-center py-20">
<div className="w-8 h-8 rounded-full border-2 border-white/10 border-t-white/50 animate-spin" />
<div className="flex items-center justify-center py-20" role="status" aria-label="Loading keys">
<div className="w-8 h-8 rounded-full border-2 border-white/10 border-t-white/50 animate-spin" aria-hidden="true" />
</div>
)}

Expand All @@ -289,8 +290,8 @@ export default function SkillPage({
)}

{phase === "config" && readyToAutoLaunch && (
<div className="flex items-center justify-center py-20">
<div className="w-8 h-8 rounded-full border-2 border-white/10 border-t-white/50 animate-spin" />
<div className="flex items-center justify-center py-20" role="status" aria-label="Preparing launch">
<div className="w-8 h-8 rounded-full border-2 border-white/10 border-t-white/50 animate-spin" aria-hidden="true" />
</div>
)}

Expand Down
24 changes: 14 additions & 10 deletions src/app/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,25 +72,27 @@ export default function DashboardPage() {
await removeSandbox({ sandboxId });
};

// Loading state
if (!isLoaded) {
return (
<main className="relative min-h-screen bg-[#0a0a0a] flex flex-col overflow-hidden">
<GlowMesh />
<SiteHeader />
<div className="flex-1 flex items-center justify-center">
<div className="w-8 h-8 rounded-full border-2 border-white/10 border-t-white/50 animate-spin" />
<div className="flex-1 flex items-center justify-center" role="status" aria-label="Loading">
<div className="w-8 h-8 rounded-full border-2 border-white/10 border-t-white/50 animate-spin" aria-hidden="true" />
</div>
</main>
);
}

// Not signed in
if (!isSignedIn) {
return (
<main className="relative min-h-screen bg-[#0a0a0a] flex flex-col overflow-hidden">
<GlowMesh />
<SiteHeader />
<div className="flex-1 flex items-center justify-center relative z-10 px-6">
<div className="border border-white/20 bg-black/40 backdrop-blur-sm p-8 text-center max-w-md">
<div className="border border-white/20 bg-black/40 p-8 text-center max-w-md">
<h2 className="text-lg font-semibold text-white/90 mb-2">Sign in to view dashboard</h2>
<p className="text-sm text-white/50 mb-6">Manage your active sandbox sessions.</p>
<SignInButton mode="modal">
Expand Down Expand Up @@ -143,6 +145,7 @@ export default function DashboardPage() {
return "text-yellow-400/70";
};

// Main dashboard content
return (
<main className="relative min-h-screen bg-[#0a0a0a] flex flex-col overflow-hidden">
<GlowMesh />
Expand All @@ -158,12 +161,12 @@ export default function DashboardPage() {
</div>

{sandboxList === undefined ? (
<div className="flex items-center justify-center py-20">
<div className="w-8 h-8 rounded-full border-2 border-white/10 border-t-white/50 animate-spin" />
<div className="flex items-center justify-center py-20" role="status" aria-label="Loading sandboxes">
<div className="w-8 h-8 rounded-full border-2 border-white/10 border-t-white/50 animate-spin" aria-hidden="true" />
</div>
) : sandboxList.length === 0 ? (
<div className="border border-white/10 bg-black/40 backdrop-blur-sm p-12 text-center">
<svg className="w-12 h-12 mx-auto mb-4 text-white/20" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1}>
<div className="border border-white/10 bg-black/40 p-12 text-center">
<svg className="w-12 h-12 mx-auto mb-4 text-white/20" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1} aria-hidden="true">
<path strokeLinecap="round" strokeLinejoin="round" d="M5.25 14.25h13.5m-13.5 0a3 3 0 01-3-3m3 3a3 3 0 100 6h13.5a3 3 0 100-6m-16.5-3a3 3 0 013-3h13.5a3 3 0 013 3m-19.5 0a4.5 4.5 0 01.9-2.7L5.737 5.1a3.375 3.375 0 012.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 01.9 2.7m0 0a3 3 0 01-3 3m0 3h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008zm-3 6h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008z" />
</svg>
<h2 className="text-lg font-semibold text-white/60 mb-2">No active sandboxes</h2>
Expand All @@ -173,7 +176,7 @@ export default function DashboardPage() {
</a>
</div>
) : (
<div className="space-y-3">
<div className="space-y-3" role="list" aria-label="Active sandboxes">
{sandboxList.map((sb) => {
const live = liveInfo[sb.sandboxId];
const displayState = live?.state ?? sb.state;
Expand All @@ -186,11 +189,12 @@ export default function DashboardPage() {
return (
<div
key={sb._id}
className="border border-white/10 bg-black/40 backdrop-blur-sm px-6 py-4"
role="listitem"
className="border border-white/10 bg-black/40 px-6 py-4"
>
<div className="flex items-start justify-between gap-4">
<div className="flex items-start gap-4 min-w-0 flex-1">
<div className={`w-2.5 h-2.5 rounded-full shrink-0 mt-1.5 ${stateColor(displayState)}`} />
<div className={`w-2.5 h-2.5 rounded-full shrink-0 mt-1.5 ${stateColor(displayState)}`} aria-hidden="true" />
<div className="min-w-0 flex-1">
<div className="font-mono text-sm text-white/80 truncate">
{sb.skillPath}
Expand Down
93 changes: 37 additions & 56 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -54,45 +54,52 @@ body {
color: var(--text-primary);
}

@keyframes fade-in {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
:focus-visible {
outline: 2px solid rgba(255, 255, 255, 0.5);
outline-offset: 2px;
}

@keyframes fade-in-up {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: no-preference) {
@keyframes fade-in {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}

@keyframes slide-in-right {
from { opacity: 0; transform: translateX(-16px); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes fade-in-up {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}

@keyframes pulse-dot {
0%, 100% { opacity: 0.4; }
50% { opacity: 1; }
}
@keyframes slide-in-right {
from { opacity: 0; transform: translateX(-16px); }
to { opacity: 1; transform: translateX(0); }
}

@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
@keyframes pulse-dot {
0%, 100% { opacity: 0.4; }
50% { opacity: 1; }
}

.animate-fade-in {
animation: fade-in 0.5s ease-out both;
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}

.animate-fade-in-up {
animation: fade-in-up 0.6s ease-out both;
}
.animate-fade-in {
animation: fade-in 0.5s ease-out both;
}

.animate-slide-in-right {
animation: slide-in-right 0.4s ease-out both;
}
.animate-fade-in-up {
animation: fade-in-up 0.6s ease-out both;
}

.animate-blink {
animation: blink 1s step-end infinite;
.animate-slide-in-right {
animation: slide-in-right 0.4s ease-out both;
}

.animate-blink {
animation: blink 1s step-end infinite;
}
}

.delay-100 { animation-delay: 100ms; }
Expand All @@ -101,29 +108,3 @@ body {
.delay-400 { animation-delay: 400ms; }
.delay-500 { animation-delay: 500ms; }
.delay-600 { animation-delay: 600ms; }

.card {
background: var(--bg-primary);
border: 1px solid var(--border);
border-radius: 12px;
transition: all 0.2s ease;
}

.card:hover {
border-color: var(--border-accent);
box-shadow: 0 4px 24px rgba(59, 130, 246, 0.06);
}

.text-gradient {
background: linear-gradient(135deg, var(--accent) 0%, #8b5cf6 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}

.text-gradient-blue {
background: linear-gradient(135deg, #2563eb 0%, #3b82f6 50%, #60a5fa 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
36 changes: 14 additions & 22 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,14 @@ function Footer() {
return (
<footer className="relative z-10 border-t border-white/10">
<div className="max-w-6xl mx-auto px-6 h-14 flex items-center justify-between">
<div className="flex items-center gap-6">
<nav aria-label="Footer links" className="flex items-center gap-6">
<a
href="https://skills.sh"
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-2 text-sm text-white/50 hover:text-white/80 transition-colors"
>
<svg
className="w-4 h-4"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={1.5}
>
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} aria-hidden="true">
<path
strokeLinecap="round"
strokeLinejoin="round"
Expand All @@ -43,13 +37,7 @@ function Footer() {
rel="noopener noreferrer"
className="flex items-center gap-2 text-sm text-white/50 hover:text-white/80 transition-colors"
>
<svg
className="w-4 h-4"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={1.5}
>
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} aria-hidden="true">
<path
strokeLinecap="round"
strokeLinejoin="round"
Expand All @@ -64,12 +52,12 @@ function Footer() {
rel="noopener noreferrer"
className="flex items-center gap-2 text-sm text-white/50 hover:text-white/80 transition-colors"
>
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="currentColor">
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" />
</svg>
GitHub
</a>
</div>
</nav>

<span className="text-sm text-white/30">ReScience Lab Inc.</span>
</div>
Expand Down Expand Up @@ -174,14 +162,17 @@ export default function Home() {
<form
onSubmit={handleSubmit}
className="w-full max-w-[640px] animate-fade-in-up delay-300"
role="search"
>
<label htmlFor="skill-url-input" className="sr-only">Skill URL</label>
<div className="flex items-center bg-white overflow-hidden shadow-2xl shadow-black/30">
<input
id="skill-url-input"
type="text"
value={url}
onChange={(e) => setUrl(e.target.value)}
placeholder="https://skills.sh/owner/repo/skill-name"
className="flex-1 px-5 py-3.5 text-[#111] text-sm font-mono bg-transparent outline-none placeholder:text-gray-400"
className="flex-1 px-5 py-3.5 text-[#111] text-sm font-mono bg-transparent placeholder:text-gray-400"
/>
<button
type="submit"
Expand All @@ -191,7 +182,7 @@ export default function Home() {
</button>
</div>
{urlError && (
<div className="mt-3 px-4 py-2.5 bg-red-500/10 border border-red-500/20 text-red-400 text-xs font-mono">
<div className="mt-3 px-4 py-2.5 bg-red-500/10 border border-red-500/20 text-red-400 text-xs font-mono" role="alert">
{urlError}
</div>
)}
Expand All @@ -201,9 +192,10 @@ export default function Home() {
<div className="flex items-center justify-between">
<button
onClick={() => { setPhase("input"); setTreeData(null); setTreeError(null); setIsRateLimited(false); }}
aria-label="Change URL"
className="flex items-center gap-2 text-sm text-white/40 hover:text-white/70 transition-colors"
>
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2} aria-hidden="true">
<path strokeLinecap="round" strokeLinejoin="round" d="M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18" />
</svg>
Change URL
Expand All @@ -214,8 +206,8 @@ export default function Home() {
</div>

{treeLoading ? (
<div className="border border-white/10 bg-white/[0.02] px-6 py-10 flex flex-col items-center">
<div className="w-8 h-8 rounded-full border-2 border-white/10 border-t-white/50 animate-spin mb-4" />
<div className="border border-white/10 bg-white/[0.02] px-6 py-10 flex flex-col items-center" role="status" aria-label="Fetching skill structure">
<div className="w-8 h-8 rounded-full border-2 border-white/10 border-t-white/50 animate-spin mb-4" aria-hidden="true" />
<span className="text-sm text-white/40">Fetching skill structure...</span>
</div>
) : treeData ? (
Expand Down
Loading