import { For, Show, type JSX } from "solid-js"; import { STATUS, ACCENT, CALM, WARM, CARD_FILL, CARD_STROKE } from "./IslandMock"; /** * Module-level mocks rebuilt from the app's SwiftUI views, so the site shows * the same anatomy the island does: the session row (SessionRowView), the * approval card (AgentsActionCards), the timer tape (TimerTape), a * temperature line (TemperaturesPanelView), and the Wings (WingPanels). */ /* ── Glyphs: the SF Symbols the mocked views use ─────────────────── */ /* Factories, not shared nodes: a Solid JSX node can mount in only one place. */ const GLYPHS: Record JSX.Element> = { speaker: () => ( <> ), speakerOff: () => ( <> ), mic: () => ( <> ), micOff: () => ( <> ), pause: () => ( <> ), play: () => , stop: () => , viewfinder: () => ( ), record: () => ( <> ), dashed: () => ( ), display: () => ( ), window: () => ( <> ), chevron: () => , film: () => ( ), xmark: () => , bell: () => ( ), restart: () => ( ), plus: () => , refresh: () => ( ) }; export type GlyphName = keyof typeof GLYPHS; export function Glyph(props: { name: GlyphName; size?: number; class?: string; style?: JSX.CSSProperties }) { return ( ); } /* ── Agents ──────────────────────────────────────────────────────── */ export interface AgentLook { name: string; /** brandColorHex from the app's AgentRegistry row. */ color: string; /** The brand mark the app bundles (AgentBrands/*.svg). */ icon: string; } export const CLAUDE: AgentLook = { name: "Claude Code", color: "#D97742", icon: "/nook/brands/claude.svg" }; export const CODEX: AgentLook = { name: "Codex", color: "#4AA3DF", icon: "/nook/brands/codex.svg" }; /** A row badge: brand-tinted for the agent chip, neutral otherwise. */ function Badge(props: { text: string; tint?: string }) { return ( {props.text} ); } /** * SessionRowView: "project · branch", the latest prompt, a status-colored * state line; on the right the agent chip (named "Agent · host" for a * remote session), the terminal origin, the status dot and age. */ export function SessionRowMock(props: { title: string; prompt: string; state: string; status: keyof typeof STATUS; agent: AgentLook; /** Remote sessions name their machine in the agent chip. */ host?: string; origin?: string; age: string; }) { return (
{/* The app's layout (badges in a right-hand third) wherever the row is wide enough; narrow site cells stack the badges beneath. */}

{props.title}

User: {props.prompt}

{props.state}

{props.age}
); } /** SwiftUI's dark-mode .yellow, which the approval card tints with. */ const YELLOW = "255,214,10"; /** * ApprovalCard: brand mark, session title, "approval" capsule; "Run ", * the input in mono; Allow (accent, the island's tint), Allow for session, * Deny, and Deny with reason…. */ export function ApprovalCardMock(props: { agent: AgentLook; title: string; tool: string; input: string; pressed?: "allow" | "session" | "deny"; }) { const button = (key: "allow" | "session" | "deny", label: string, prominent = false) => ( {label} ); return (
{props.title} approval

Run {props.tool}

{props.input}

{button("allow", "Allow", true)} {button("session", "Allow for session")} {button("deny", "Deny")} Deny with reason…
); } /* ── Timer tape ──────────────────────────────────────────────────── */ /** TimerPalette.accent. */ export const TIMER_ORANGE = "#FA9E4D"; /** * TimerTapeDrawing.ticks, ported: fixed-pitch 1.5 px ticks resting at 40 % * of the tape height, swelling to a 72 % crest (raised cosine) that leads * into the remaining side and trails a shorter shoulder behind. */ function tapeTicks(remaining: number, w: number, h: number) { const span = w - 2; const count = Math.floor(span / 6) + 1; const pitch = span / Math.max(1, count - 1); const boundary = 1 + span * remaining; const baseline = h * 0.4; const peak = h * 0.72; const lead = Math.min(84, w * 0.4); const trail = lead * 0.5; const swell = (d: number) => (d < 1 ? (1 + Math.cos(Math.PI * d)) / 2 : 0); return Array.from({ length: count }, (_, i) => { const cx = 1 + i * pitch; const offset = boundary - cx; const rise = remaining > 0 ? swell(Math.abs(offset) / (offset >= 0 ? lead : trail)) : 0; const height = baseline + (peak - baseline) * rise; const fill = remaining > 0 ? Math.min(1, Math.max(0, offset / pitch + 1)) : 0; return { x: cx - 0.75, y: (h - height) / 2, height, fill }; }); } export function TimerTapeMock(props: { remaining: number; width: number; height: number; color?: string; opacity?: number; }) { return ( ); } /* ── Temperatures ────────────────────────────────────────────────── */ /** MetricPalette.usage: calm below 70 %, warm to 90 %, critical above. */ function usageColor(fraction: number) { if (fraction >= 0.9) return "rgba(229,72,77,0.9)"; if (fraction >= 0.7) return WARM; return CALM; } /** Tier color at the app's default thresholds (warn 70 °C, alert 85 °C). */ function tierColor(celsius: number) { if (celsius >= 85) return "rgba(229,72,77,0.9)"; if (celsius >= 70) return WARM; return "white"; } /** * One die line: label, its 60 s usage trace (filled at 20 %, 1.5 px line, * over a hairline floor), and the temperature in mono, tier-tinted. */ export function DieLine(props: { label: string; usage: number[]; celsius: number }) { const path = () => { const n = props.usage.length; return props.usage .map((u, i) => `${i === 0 ? "M" : "L"}${((i / (n - 1)) * 100).toFixed(2)},${(26 - u * 26).toFixed(2)}`) .join(" "); }; const color = () => usageColor(props.usage[props.usage.length - 1] ?? 0); return (
{props.label} {Math.round(props.celsius)}°
); } /* ── Wings ───────────────────────────────────────────────────────── */ export interface WingControlMock { glyph: GlyphName; on?: boolean; pressed?: boolean; } /** * One edge's capsule: black at 94 %, a 1 px white-18 % stroke, and each * control a circle as tall as the Compact island, icon at 42 %. */ export function WingCapsule(props: { controls: WingControlMock[]; height: number }) { return (
{(c) => ( )}
); }