feat(web): two-pane "cockpit" redesign with a live board rail
Restructure the workshop flow into the designer's cockpit: a persistent shell (header + 5-step stepper + sticky instrument rail) wrapping the phase routes via a React-Router layout route, so the rail stays mounted across navigation. - Design system: IBM Plex Mono + Newsreader; the full cockpit token set (light + dark) in index.css; a working light/dark theme toggle (store `theme` + useApplyTheme); a `switch` ui primitive. - Shell: CockpitLayout, Stepper (forward-gated), PanelChrome helpers. Every phase page restyled to the editorial panels + the WORKSHOP-FLOW fixes (channels-after-bind, domain framing + L1 prefill, L2/L3 prefill at 3/3, in-place submission finale). Store gains `tried` + `channels.saidHi` (v4). - Live rail (CockpitRail): real node heartbeat + agent activity log + ADD progress; sim telemetry (useTelemetry) for the waveform/accel/I2C behind a seam, marked SIM. - LED-matrix PIXEL MIRROR (real): the rail shows exactly what the physical matrix displays — API GET /nodes/:team/matrix reads the board's framebuffer off the :9999 relay (readMatrixFrame + the `matrixget` relay command); useMatrixMirror polls it and unpacks the 104 bits. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
6c82b79b01
commit
1a39457940
+37
-55
@@ -1,79 +1,61 @@
|
||||
import { Link, useNavigate } from 'react-router-dom'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { PhaseStrip } from '@/components/PhaseStrip'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { OpenYourNode } from '@/components/OpenYourNode'
|
||||
import { DomainPicker } from '@/components/DomainPicker'
|
||||
import { PanelHeading, PanelCard, ProceedButton } from '@/components/cockpit/PanelChrome'
|
||||
import { useSession } from '@/store/session'
|
||||
|
||||
export function EnvSetup() {
|
||||
const navigate = useNavigate()
|
||||
const device = useSession((s) => s.device)
|
||||
const domain = useSession((s) => s.domain)
|
||||
const add = useSession((s) => s.add)
|
||||
const setAddLayer = useSession((s) => s.setAddLayer)
|
||||
const completePhase = useSession((s) => s.completePhase)
|
||||
|
||||
const ready = device.connected && domain.trim().length > 0
|
||||
|
||||
const onProceed = () => {
|
||||
// Carry the domain into Layer 1 as a starting draft (only if untouched).
|
||||
if (!add.L1.trim() && domain.trim()) {
|
||||
setAddLayer('L1', `Domain: ${domain.trim()}. Events: an impact spike, a sustained sway, a stale sensor.`)
|
||||
}
|
||||
completePhase('setup')
|
||||
navigate('/workshop/module1')
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-background">
|
||||
<header className="px-8 py-5 border-b border-border flex items-center justify-between">
|
||||
<div className="font-mono text-xs tracking-widest uppercase">
|
||||
APESS <span className="text-primary font-bold">2026</span>
|
||||
<span className="text-muted-foreground"> · Workshop</span>
|
||||
<section>
|
||||
<PanelHeading
|
||||
eyebrow="PHASE 2 OF 5 · ~15 MIN"
|
||||
title="Meet your agent"
|
||||
intro="Your board runs the APESS agent — a Claude-powered agent on the edge. It reasons about your domain, drives the board's own devices, and keeps working when the cloud drops. Open it to explore, then name the domain it's for."
|
||||
/>
|
||||
|
||||
{/* Open your agent */}
|
||||
<PanelCard className="mt-9">
|
||||
<div className="text-[17px] font-semibold">Open your agent to explore</div>
|
||||
<div className="mt-4">
|
||||
<OpenYourNode variant="hero" />
|
||||
</div>
|
||||
<Link to="/workshop" className="font-mono text-[11px] text-muted-foreground hover:text-foreground tracking-widest uppercase">
|
||||
← Team registration
|
||||
</Link>
|
||||
</header>
|
||||
</PanelCard>
|
||||
|
||||
<PhaseStrip active="setup" />
|
||||
|
||||
<section className="px-8 py-10 max-w-3xl mx-auto space-y-6">
|
||||
<div>
|
||||
<Badge variant="secondary" className="font-mono text-[10px] tracking-widest uppercase mb-2">
|
||||
Phase 2 of 5 · ~15 min
|
||||
</Badge>
|
||||
<h1 className="text-3xl font-bold tracking-tight">Meet your agent</h1>
|
||||
<p className="text-sm text-muted-foreground mt-2 max-w-2xl leading-relaxed">
|
||||
Your board now runs the <span className="font-medium text-foreground">APESS agent</span> — a
|
||||
Claude-powered agent living on the edge. It reasons about your domain, drives the board’s
|
||||
own devices, and keeps working when the cloud drops by falling back to an on-board model.
|
||||
Open it to explore, then name the domain it’s for.
|
||||
</p>
|
||||
{/* Pick your domain — emphasized: this seeds all five layers */}
|
||||
<PanelCard emphasized className="mt-[22px]">
|
||||
<div className="text-[17px] font-semibold">Pick your domain</div>
|
||||
<p className="mt-1.5 text-[14px] leading-[1.5] text-ink-3">
|
||||
This one line seeds all five layers of your Agent Design Document — the domain your agent
|
||||
serves and the events it must notice.
|
||||
</p>
|
||||
<div className="mt-4">
|
||||
<DomainPicker />
|
||||
</div>
|
||||
</PanelCard>
|
||||
|
||||
{/* 1 — Open your agent (hero) */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">Open your agent to explore</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<OpenYourNode variant="hero" />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 2 — Pick your domain */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">Pick your domain</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<DomainPicker />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<div className="flex justify-end pt-4">
|
||||
<Button size="lg" disabled={!ready} onClick={onProceed}>
|
||||
Proceed to Module 1 →
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<div className="mt-9 flex justify-end">
|
||||
<ProceedButton disabled={!ready} onClick={onProceed}>
|
||||
Proceed to Module 1 →
|
||||
</ProceedButton>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user