Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
0bfc5de
feat(web): Sprint 3 — 404 page, loading skeleton, philosophy section,…
claude Jun 14, 2026
fb852dc
feat(web): Sprint 4 — error page, typewriter hero, activity feed, liv…
claude Jun 14, 2026
b69330c
feat(web): Sprint 4b — portfolio filter, product section nav, Animate…
claude Jun 14, 2026
fa681dd
feat(web): Sprint 4c — tabbed DesignPreview, constellation center hub
claude Jun 14, 2026
b84b0b3
feat(web): Sprint 4d — philosophy parallax, animated status list
claude Jun 14, 2026
2f955c4
feat(web): Sprint 4e — footer dot tooltips, live portfolio count
claude Jun 14, 2026
c2a5818
feat(web): Sprint 4f — metric range bar, animated event log
claude Jun 14, 2026
d1374e7
feat(web): Sprint 4g — system health badge in site nav
claude Jun 14, 2026
9a5c8b7
feat(web): Sprint 4h — floating dock (back-to-top + palette)
claude Jun 14, 2026
f69f081
a11y(web): add focus-visible rings to all interactive elements
claude Jun 14, 2026
6ecb5cc
feat(web): add TerminalSection CLI showcase and hero glyph parallax
claude Jun 14, 2026
e2e5ac7
feat(web): add BenchmarkSection, animated 404, and palette auto-scroll
claude Jun 14, 2026
e6e92d3
feat(web): add ProductCodeSnippet with syntax highlighting to product…
claude Jun 14, 2026
cb33696
feat(web): add page transitions, 90-day uptime calendar on status page
claude Jun 14, 2026
c01e512
feat(web): add keyboard shortcuts overlay (?) and g-key navigation
claude Jun 14, 2026
c4a4232
feat(web): add product text search to ProjectGrid and random product …
claude Jun 14, 2026
6defa1f
feat(web): clickable activity events, hero scroll parallax
claude Jun 14, 2026
bba2b23
feat(web): add recently viewed section to CommandPalette
claude Jun 14, 2026
8e4fa07
feat(web): toast notifications, dock tooltips, / search shortcut
claude Jun 14, 2026
adc1825
feat(web): product page polish — share button, section nav, live metric
claude Jun 14, 2026
4501d33
feat(web): constellation map node breathing and animated edge flow
claude Jun 14, 2026
b845c9f
feat(web): live indicators and result counts
claude Jun 14, 2026
a10e521
feat(web): global ambient cursor glow across all pages
claude Jun 14, 2026
9852ff9
feat(web): page-title easter egg and animated benchmark gain badges
claude Jun 14, 2026
ac10fed
feat(web): [ / ] keyboard navigation between products
claude Jun 14, 2026
7532182
feat(web): sitemap, robots.txt, and tab keyboard navigation
claude Jun 14, 2026
6d3d7e3
feat(web): OpenGraph image generation for homepage and product pages
claude Jun 14, 2026
2db2227
chore(web): accessibility polish and constellation hub animation
claude Jun 14, 2026
bf4556c
feat(web): personalized welcome-back toast for returning visitors
claude Jun 14, 2026
89eb412
feat(web): section navigation dots + terminal replay + activity fresh…
claude Jun 14, 2026
e5c8717
feat(web): command palette global actions + hero particle field
claude Jun 14, 2026
c3cdc1f
feat(web): toast progress bar, 1-9 product shortcuts, keyboard event …
claude Jun 14, 2026
3daccf0
feat(web): constellation map auto-cycles through product metrics when…
claude Jun 14, 2026
ecf812d
feat(web): magnetic hero buttons + grid spotlight hover effect
claude Jun 14, 2026
ef6c396
feat(web): ScrambleText component applied to all section headings
claude Jun 14, 2026
5473f02
feat(web): status ribbon + live online count in hero badge
claude Jun 14, 2026
a375ec2
feat(web): animated sparklines, terminal copy, feed pause + ScrambleT…
claude Jun 14, 2026
11c8fa3
feat(web): product page overhaul — ScrambleText headings, live badge,…
claude Jun 14, 2026
7d7e668
feat(web): film-grain overlay + status dots in live ticker
claude Jun 14, 2026
d65b081
feat(web): palette shows live metrics + DesignPreview cursor spotlight
claude Jun 14, 2026
38f8598
feat(web): colorized terminal output + macOS-dock magnification
claude Jun 14, 2026
09689b6
feat(web): activity feed accent borders + constellation hub crossfade
claude Jun 14, 2026
f95b8a8
feat(web): add LiveDemo interactive inference section
claude Jun 14, 2026
2bfa2db
feat(web): add ProductMarquee divider and CTASection pre-footer
claude Jun 14, 2026
0b30dd6
feat(web): add SignalMonitor oscilloscope section
claude Jun 14, 2026
c4d0f35
feat(web): enhance ScrollProgressBar and status page animations
claude Jun 14, 2026
f3492e3
feat(web): add product filter to ActivityFeed
claude Jun 14, 2026
ab9c214
feat(web): add cycling CJK watermark to PhilosophySection
claude Jun 14, 2026
f841d23
feat(web): add copy button and response history to LiveDemo
claude Jun 14, 2026
4a10c23
feat(web): add edge-fade masks to LiveTicker and ProductMarquee
claude Jun 14, 2026
4c3b0c4
feat(web): product-specific card glows and RecentEvents accent borders
claude Jun 14, 2026
7c89d11
feat(web): active-row waveform glow and footer aurora gradient
claude Jun 14, 2026
d146866
feat(web): constellation edge particles + live inference heatmap section
claude Jun 14, 2026
d66d279
feat(web): benchmark comparison toggle + live token velocity grid
claude Jun 14, 2026
6e67f1c
feat(web): live product leaderboard with animated rank reordering
claude Jun 14, 2026
3a650d6
feat(web): design system token palette tab in DesignPreview
claude Jun 14, 2026
95fd77e
feat(web): multi-model LiveDemo with per-model speed, color, and exam…
claude Jun 14, 2026
574721e
feat(web): add heatmap/velocity/leaderboard/signals navigation to Com…
claude Jun 14, 2026
3b51e50
feat(web): Konami code Easter egg — 根性 unlocked
claude Jun 14, 2026
8517515
feat(web): add Konami Easter egg to KeyboardHelp shortcuts modal
claude Jun 14, 2026
7718ff0
feat(web): table view toggle for ProjectGrid
claude Jun 14, 2026
1ef8102
feat(web): keyboard product navigation, SystemPulse strip, expanded t…
claude Jun 14, 2026
baba53e
feat(web): PipelineTrace — animated request flowing through 5 KonjoAI…
claude Jun 14, 2026
d2ed90f
feat(web): ChangelogFeed — vertical timeline of cross-portfolio commits
claude Jun 14, 2026
1426f9d
feat(web): HeroParticles constellation lines; fix duplicate [ ] keybo…
claude Jun 14, 2026
551a16f
feat(web): DemoChat — multi-turn conversation with memory + retrieval…
claude Jun 14, 2026
9a87034
feat(web): StatCounters — four large animated KPI numbers before the CTA
claude Jun 14, 2026
ec066a6
feat(web): add AgentFlow section and leaderboard endorse mechanic
claude Jun 14, 2026
3057723
feat(web): add TrackVisit component for recent product history
claude Jun 14, 2026
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
229 changes: 229 additions & 0 deletions apps/web/app/_components/ActivityFeed.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
"use client";

import { useState, useEffect, useRef, useCallback } from "react";
import Link from "next/link";
import { motion, AnimatePresence, useReducedMotion } from "motion/react";
import { ease, cn } from "@konjoai/ui";

type FeedEvent = {
id: number;
slug: string;
glyph: string;
message: string;
label: string;
toneClass: string;
dotColor: string;
age: number; // seconds since fired
};

const EVENT_POOL: Omit<FeedEvent, "id" | "age">[] = [
{ slug: "squish", glyph: "◐", message: "Decoded 512 tokens in 12.2 ms (42 tok/s)", label: "throughput", toneClass: "text-konjo-accent", dotColor: "var(--color-konjo-accent)" },
{ slug: "kyro", glyph: "✸", message: "Hybrid retrieval completed — NDCG@10 = 0.911", label: "retrieval", toneClass: "text-konjo-good", dotColor: "var(--color-konjo-good)" },
{ slug: "vectro", glyph: "◇", message: "Compressed 2 048 vectors at 32× ratio (87% recall)", label: "codec", toneClass: "text-konjo-good", dotColor: "var(--color-konjo-good)" },
{ slug: "kairu", glyph: "▲", message: "p99 latency = 8.2 ms — within SLO budget", label: "latency", toneClass: "text-konjo-warm", dotColor: "var(--color-konjo-warm)" },
{ slug: "miru", glyph: "◉", message: "Vision trace complete — 76% attention coverage", label: "attention", toneClass: "text-konjo-accent", dotColor: "var(--color-konjo-accent)" },
{ slug: "toki", glyph: "✕", message: "Blocked 1 jailbreak probe (DAN v4.3)", label: "safety", toneClass: "text-konjo-hot", dotColor: "var(--color-konjo-hot)" },
{ slug: "kohaku", glyph: "❖", message: "Recalled 3 episodic memories (half-life: 4.2 h)", label: "recall", toneClass: "text-konjo-violet", dotColor: "var(--color-konjo-violet)" },
{ slug: "lopi", glyph: "⌬", message: "Agent task completed in 2 branches — merged", label: "agent", toneClass: "text-konjo-good", dotColor: "var(--color-konjo-good)" },
{ slug: "drex", glyph: "✦", message: "Training loss = 0.072 — epoch 14/40 done", label: "training", toneClass: "text-konjo-accent", dotColor: "var(--color-konjo-accent)" },
{ slug: "squish", glyph: "◐", message: "Prefill 1 024 tokens: 18 ms (Metal path)", label: "prefill", toneClass: "text-konjo-accent", dotColor: "var(--color-konjo-accent)" },
{ slug: "kyro", glyph: "✸", message: "Semantic cache hit — latency 2.1 ms vs 68 ms", label: "cache", toneClass: "text-konjo-cool", dotColor: "var(--color-konjo-cool)" },
{ slug: "vectro", glyph: "◇", message: "INT8 codec: 87 recall / 91 baseline — shipped", label: "quality", toneClass: "text-konjo-good", dotColor: "var(--color-konjo-good)" },
];

const MAX_EVENTS = 7;
let nextId = 0;

/** Unique product slugs + glyphs from the pool, for the filter row. */
const POOL_PRODUCTS = [
...new Map(EVENT_POOL.map((e) => [e.slug, { slug: e.slug, glyph: e.glyph }])).values(),
];

/**
* Simulated real-time product activity feed.
* New events trickle in every 2-4 s; old ones slide out once MAX_EVENTS is reached.
*/
export function ActivityFeed() {
const reduce = useReducedMotion();
const [events, setEvents] = useState<FeedEvent[]>([]);
const [poolIdx, setPoolIdx] = useState(0);
const [totalCount, setTotalCount] = useState(0);
const [paused, setPaused] = useState(false);
const [filterSlug, setFilterSlug] = useState<string | null>(null);
const pausedRef = useRef(false);

useEffect(() => { pausedRef.current = paused; }, [paused]);

const pushEvent = useCallback(() => {
setEvents((prev) => {
const template = EVENT_POOL[poolIdx % EVENT_POOL.length];
const next: FeedEvent = { ...template, id: nextId++, age: 0 };
return [next, ...prev].slice(0, MAX_EVENTS);
});
setPoolIdx((i) => i + 1);
setTotalCount((n) => n + 1);
}, [poolIdx]);

// Initial batch + recurring trickle — pauses when hovering
useEffect(() => {
pushEvent();
const id = setInterval(() => {
if (pausedRef.current) return;
setEvents((prev) => prev.map((e) => ({ ...e, age: e.age + 1 })));
if (Math.random() > 0.3) pushEvent();
}, 2600);
return () => clearInterval(id);
}, [pushEvent]);

return (
<section
id="activity"
className="mx-auto max-w-6xl px-6 pb-16"
aria-label="Live product activity feed"
>
<motion.div
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.55, ease: ease.kanjo }}
className="mb-6 flex items-center gap-3"
>
<p className="text-konjo-mono text-xs uppercase tracking-[0.24em] text-konjo-fg-faint">
Live activity
</p>
<span
className="text-konjo-mono inline-flex items-center gap-1.5 rounded-full border border-konjo-good/30 bg-konjo-good/10 px-2 py-0.5 text-[10px] uppercase tracking-widest text-konjo-good"
aria-label="Live stream"
>
<span className="konjo-pulse inline-block size-1.5 rounded-full bg-konjo-good" aria-hidden />
Stream
</span>
{paused && (
<span className="text-konjo-mono text-[10px] uppercase tracking-widest text-konjo-fg-faint">
paused
</span>
)}
{filterSlug && (
<span className="text-konjo-mono text-[10px] uppercase tracking-widest text-konjo-accent">
· {filterSlug}
</span>
)}
{totalCount > 0 && (
<span
className="text-konjo-mono ml-auto text-[10px] tabular-nums text-konjo-fg-faint"
aria-live="polite"
aria-label={`${totalCount} events this session`}
>
{totalCount} event{totalCount !== 1 ? "s" : ""}
</span>
)}
</motion.div>

{/* Product filter chips */}
<div className="mb-3 flex flex-wrap gap-1.5" role="group" aria-label="Filter by product">
{POOL_PRODUCTS.map(({ slug, glyph }) => {
const active = filterSlug === slug;
return (
<button
key={slug}
type="button"
onClick={() => setFilterSlug(active ? null : slug)}
aria-pressed={active}
className={cn(
"text-konjo-mono inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-[10px] transition-all duration-150 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-konjo-accent",
active
? "border-konjo-brand/50 bg-konjo-brand/10 text-konjo-fg"
: "border-konjo-line/40 bg-konjo-surface/30 text-konjo-fg-faint hover:border-konjo-line hover:text-konjo-fg",
)}
>
<span aria-hidden>{glyph}</span>
{slug}
</button>
);
})}
</div>

<ul
role="list"
className="flex flex-col gap-2"
aria-live="polite"
aria-atomic="false"
aria-relevant="additions"
onMouseEnter={() => setPaused(true)}
onMouseLeave={() => setPaused(false)}
>
<AnimatePresence initial={false}>
{events.filter((ev) => !filterSlug || ev.slug === filterSlug).map((ev) => (
<motion.li
key={ev.id}
layout={!reduce}
initial={reduce ? { opacity: 1 } : { opacity: 0, x: -16, height: 0 }}
animate={{ opacity: 1, x: 0, height: "auto" }}
exit={reduce ? { opacity: 0 } : { opacity: 0, x: 16, height: 0 }}
transition={{ duration: 0.35, ease: ease.nehan }}
className="glass-konjo rounded-konjo group relative overflow-hidden"
>
{/* Colored left accent border */}
<div
className="absolute left-0 top-0 bottom-0 w-[2px] rounded-l"
style={{ background: ev.dotColor, opacity: 0.6 }}
aria-hidden
/>
<Link
href={`/products/${ev.slug}`}
className="flex items-center gap-4 px-4 py-3 transition-colors hover:bg-konjo-surface/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-konjo-accent focus-visible:ring-inset"
aria-label={`${ev.slug}: ${ev.message}`}
>
<span
className="text-konjo-mono shrink-0 text-xl leading-none"
style={{ color: ev.dotColor }}
aria-hidden
>
{ev.glyph}
</span>

<div className="min-w-0 flex-1">
<div className="flex flex-wrap items-baseline gap-x-2 gap-y-0.5">
<span className="text-konjo-mono text-[11px] font-medium text-konjo-fg">
{ev.slug}
</span>
<span className={`text-konjo-mono text-xs tabular-nums ${ev.toneClass}`}>
{ev.message}
</span>
</div>
</div>

<div className="text-konjo-mono shrink-0 text-right">
<span className="block text-[10px] uppercase tracking-widest text-konjo-fg-faint">
{ev.label}
</span>
<span className="block text-[10px] text-konjo-fg-faint tabular-nums">
{ev.age === 0 ? "now" : `${ev.age}s ago`}
</span>
</div>

<span
className="text-konjo-mono shrink-0 text-[11px] text-konjo-fg-faint opacity-0 transition-opacity group-hover:opacity-100"
aria-hidden
>
</span>
</Link>
{/* Freshness decay bar — shrinks from full to empty over 10 s */}
{!reduce && (
<motion.div
className="absolute bottom-0 left-0 h-[1.5px] rounded-full"
style={{ background: ev.dotColor }}
initial={{ width: "100%", opacity: 0.5 }}
animate={{ width: `${Math.max(0, 100 - ev.age * 10)}%`, opacity: ev.age >= 10 ? 0 : 0.4 }}
transition={{ duration: 0.6, ease: "linear" }}
aria-hidden
/>
)}
</motion.li>
))}
</AnimatePresence>
</ul>
</section>
);
}
Loading
Loading