+
+
+ Battery
+
+
+ Limit 80%
+
+
+
+
+ {pct()}%
+
+ {subline()}
+
+
+
80 ? WARM : CALM,
+ transition: "background 0.4s"
+ }}
+ />
+
+
+
+
+
Top-up mode
+
+ charge past the limit while plugged in
+
+
+
+
+
+
+
+
+
+
+
+
+ {phase() === "held"
+ ? "holding — the pack stays young longer"
+ : phase() === "topup"
+ ? "topping up — re-arms when you unplug"
+ : "on the way to the ceiling"}
+
+
+
+
+ );
+}
+
+/* ── Demo 5: the slot counts down, then opens the day ──────── */
+
+const WEEK_LETTERS = ["S", "M", "T", "W", "T", "F", "S"];
+const DAY_EVENTS = [
+ { time: "09:30–09:45", title: "Standup", loc: "Daily", meeting: true },
+ { time: "15:30–16:00", title: "Design review", loc: "4th floor", meeting: false }
];
-function RemoteDemo() {
- const [tick, setTick] = createSignal(0);
+function CalendarDemo() {
+ const [mins, setMins] = createSignal(14);
+ const [today, setToday] = createSignal(new Date());
onMount(() => {
+ setToday(new Date());
const interval = setInterval(
- () => setTick((t) => (t + 1) % REMOTE_TICKS.length),
- 2600
+ () => setMins((m) => (m <= 0 ? 14 : m - 1)),
+ 900
);
onCleanup(() => clearInterval(interval));
});
+ const imminent = () => mins() <= 5;
+ const dialDays = () => {
+ const base = today();
+ return [-4, -3, -2, -1, 0, 1, 2, 3, 4].map((off) => {
+ const d = new Date(
+ base.getFullYear(),
+ base.getMonth(),
+ base.getDate() + off
+ );
+ return {
+ letter: WEEK_LETTERS[d.getDay()],
+ num: d.getDate(),
+ today: off === 0,
+ past: off < 0,
+ dots: (d.getDate() * 7 + d.getDay() * 3) % 4
+ };
+ });
+ };
return (
-
+
+
+
+
+ {mins() === 0 ? "now" : `${mins()}m`}
+
+
+ }
+ />
+ {/* the island opening onto the day panel */}
+
-
-
- {(host, i) => (
-
-
-
-
- {host.name}
+
+
+
+ Calendar
+
+
+ +
+
+
+
+
+ {(d) => (
+
+
+ {d.letter}
- {host.via}
+ {d.num}
+
+
+
+ {() => (
+
+ )}
+
- {host.line}
+ )}
+
+
+
+
+ {(ev) => (
+
+
+
{ev.time}
+
{ev.title}
+ {ev.loc &&
{ev.loc}
}
+
+ {ev.meeting && (
+
+ Open
+
+ )}
)}
-
- same island, same glance — agents two networks away
-
-
+
);
}
-/* ── Demo 5: campfire states ───────────────────────────────────────── */
+/* ── Demo 6: the lens only wakes for a click ──────────────── */
+
+function CameraDemo() {
+ const [phase, setPhase] = createSignal<"idle" | "press" | "live">("idle");
+ let videoRef: HTMLVideoElement | undefined;
+ onMount(() => {
+ const timers: ReturnType[] = [];
+ const loop = () => {
+ setPhase("idle");
+ timers.push(setTimeout(() => setPhase("press"), 2600));
+ timers.push(setTimeout(() => setPhase("live"), 3000));
+ };
+ loop();
+ const interval = setInterval(loop, 8800);
+ onCleanup(() => {
+ clearInterval(interval);
+ timers.forEach(clearTimeout);
+ });
+ });
+ createEffect(() => {
+ if (phase() === "live") videoRef?.play();
+ else videoRef?.pause();
+ });
+ return (
+
+
+
+ Camera
+
+ FaceTime HD Camera ▾
+
+
+
+ {/* inert dashed start button */}
+
+
+
+
+
+
+ Start camera
+
+
+ {/* live preview */}
+
+
+
+
+ {phase() === "live"
+ ? "live — stops the moment you leave the panel"
+ : "inert — the lens stays dark until you click"}
+
+
+
+ );
+}
+
+/* ── Demo 7: one click copies, sets up, probes — pong ─────── */
+
+const SSH_LINES = [
+ { at: 1, text: "scp nook-hook gpu-rig:~/.nook/bin/" },
+ { at: 2, text: "setup: bake NOOK_HOST, write ~/.nook/endpoint" },
+ { at: 3, text: "nook-hook --probe → pong" }
+];
+
+function SshInstallDemo() {
+ const [step, setStep] = createSignal(0);
+ onMount(() => {
+ const timers: ReturnType[] = [];
+ const loop = () => {
+ setStep(0);
+ timers.push(setTimeout(() => setStep(1), 1600));
+ timers.push(setTimeout(() => setStep(2), 3000));
+ timers.push(setTimeout(() => setStep(3), 4200));
+ timers.push(setTimeout(() => setStep(4), 5400));
+ };
+ loop();
+ const interval = setInterval(loop, 11000);
+ onCleanup(() => {
+ clearInterval(interval);
+ timers.forEach(clearTimeout);
+ });
+ });
+ const cursorPhase = () =>
+ step() === 0
+ ? ("idle" as const)
+ : step() === 1
+ ? ("press" as const)
+ : ("live" as const);
+ return (
+
+
+
+
+
gpu-rig
+
+ mike@192.168.64.3 · linux
+
+
+
= 4 ? 0 : 1,
+ transform: step() === 1 ? "scale(0.94)" : "scale(1)"
+ }}
+ >
+ {step() === 0 ? "Install hook" : "installing…"}
+
+
+
+
+
= 4 ? 0 : 1 }}
+ >
+ {step() === 0 && (
+
+ one click: copy → setup → probe
+
+ )}
+
+ {(l) => (
+ = l.at ? 1 : 0,
+ transform:
+ step() >= l.at ? "translateY(0)" : "translateY(4px)"
+ }}
+ >
+ l.at
+ ? "rgba(150,220,150,0.9)"
+ : "rgba(255,255,255,0.45)"
+ }}
+ >
+ {step() > l.at ? "✓" : "…"}
+
+
+ {l.text}
+
+
+ )}
+
+
+
= 4 ? 1 : 0,
+ transform: step() >= 4 ? "translateY(0)" : "translateY(6px)"
+ }}
+ >
+
+
+
+
+ gpu-rig
+
+
+ remote
+
+
+
+ You: benchmark fp16
+
+
+
+ agents join the same dot grid
+
+
+
+
+
+
+ );
+}
+
+/* ── Demo 8: campfire states ───────────────────────────────────────── */
function CampfireDemo() {
const states: CampfireState[] = [
@@ -489,7 +992,9 @@ function CampfireDemo() {
>
-
{labels[i()]}
+
+ {labels[i()]}
+
)}
@@ -507,7 +1012,8 @@ export default function FeatureBreakdowns() {
bullets={[
"12 agents: Claude Code, Codex, Gemini CLI, OpenCode, Pi, and more",
"The campfire dances while work runs, simmers when all is calm",
- "Slot rankings — you choose which data earns the notch"
+ "Slot rankings — you choose which data earns the notch",
+ "You don't need a notch — external displays get the same island, floating top-center"
]}
>
@@ -530,8 +1036,9 @@ export default function FeatureBreakdowns() {
-
+
+
+
+
+
+
+
+
+
+
+
+
+
- tailscale
+ ssh