diff --git a/app/globals.css b/app/globals.css index f7e1c30..766559b 100644 --- a/app/globals.css +++ b/app/globals.css @@ -155,6 +155,12 @@ section { box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); } +/* Carousel progress bar */ +@keyframes progress { + from { width: 0; } + to { width: 100%; } +} + /* Fade in animation for sections */ @keyframes fade-up { from { diff --git a/components/observability.tsx b/components/observability.tsx index c16b708..e96e784 100644 --- a/components/observability.tsx +++ b/components/observability.tsx @@ -1,8 +1,72 @@ +'use client' + +import { useState, useEffect, useRef } from 'react' import { Eye } from 'lucide-react' +const slides = [ + { + label: 'Dashboard', + src: '/dashboard.png', + alt: 'GreyProxy dashboard overview showing connection stats and activity', + }, + { + label: 'Pending requests', + src: '/pending_requests.png', + alt: 'GreyProxy pending network requests with Allow and Deny controls', + }, + { + label: 'Rules', + src: '/rules.png', + alt: 'GreyProxy domain rules configuration for allow and deny policies', + }, + { + label: 'Logs', + src: '/logs.png', + alt: 'GreyProxy connection logs showing all outbound network activity', + }, +] + +const INTERVAL = 4000 + export function Observability() { + const [active, setActive] = useState(0) + const [paused, setPaused] = useState(false) + const timerRef = useRef | null>(null) + // Key to force re-mount of the progress bar so animation restarts + const [tick, setTick] = useState(0) + + function goTo(i: number) { + setActive(i) + setTick((t) => t + 1) + resetTimer() + } + + function advance() { + setActive((i) => (i + 1) % slides.length) + setTick((t) => t + 1) + } + + function resetTimer() { + if (timerRef.current) clearInterval(timerRef.current) + if (!paused) { + timerRef.current = setInterval(advance, INTERVAL) + } + } + + useEffect(() => { + if (paused) { + if (timerRef.current) clearInterval(timerRef.current) + return + } + timerRef.current = setInterval(advance, INTERVAL) + return () => { + if (timerRef.current) clearInterval(timerRef.current) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [paused]) + return ( -
+
@@ -21,21 +85,60 @@ export function Observability() {

-
-
-
- -
-

GreyProxy dashboard

+
setPaused(true)} + onMouseLeave={() => setPaused(false)} + > + {/* Screenshot with crossfade */} +
+ {slides.map((slide, i) => ( + {slide.alt} + ))}
-
- GreyProxy dashboard showing pending network requests with Allow and Deny controls + + {/* Progress indicators + labels */} +
+ {slides.map((slide, i) => ( + + ))}
-

+ +

Every outbound request is visible. Allow trusted domains, block unknown ones, and adjust policies live as your agent works.

diff --git a/components/problem.tsx b/components/problem.tsx index 86b335f..46f0884 100644 --- a/components/problem.tsx +++ b/components/problem.tsx @@ -7,7 +7,7 @@ export function Problem() { {/* Section 1: Stochastic risk */}

- Your agent runs as you. + Your agent runs as you.

Agents inherit your full permissions and decide what to access at runtime. Here's what that looks like... diff --git a/public/dashboard.png b/public/dashboard.png new file mode 100644 index 0000000..2a35888 Binary files /dev/null and b/public/dashboard.png differ diff --git a/public/logs.png b/public/logs.png new file mode 100644 index 0000000..eb2092c Binary files /dev/null and b/public/logs.png differ diff --git a/screenshots/greyproxy.png b/public/pending_requests.png similarity index 100% rename from screenshots/greyproxy.png rename to public/pending_requests.png diff --git a/public/rules.png b/public/rules.png new file mode 100644 index 0000000..2cb197a Binary files /dev/null and b/public/rules.png differ