forked from Mike/freno-dev
nook: show the full feature set on the landing page
The landing page sold eight features from around 0.6 and summed up the rest in one line, while 0.6–0.10 shipped most of the island's modules. It also said twelve agents (Muse made thirteen) and marked phone approvals "coming soon" although the iPhone companion is in TestFlight beta. Adds live demos for terminal replies, the volume mixer, Capture with its Wings, and floating actions; a bento of module tiles behind a two-way feature marquee; and an iPhone/Watch beta section whose request form posts the same email-only report the app's Settings > Phone card sends. The approval flow, remote sessions and setup, timer, temperatures and Capture mocks are rebuilt from the app's own views, so the site shows what the island actually draws. Tests: report.test.ts pins the landing page's beta request against the /report schema (red when the schema demands a machine snapshot); nook and subdomain suites pass. NOOK-334 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import { describe, expect, it, mock, beforeEach } from "bun:test";
|
||||
// and description, HTML-escaped.
|
||||
// 2. Invalid payloads never reach the mailer.
|
||||
// 3. The rate limiter halts floods per fingerprint (falling back to IP).
|
||||
// 4. The landing page's iPhone beta request (email only) gets through.
|
||||
// sendEmail and the limiter are mocked so the test sends no real mail.
|
||||
|
||||
const sentEmails: { to: string; subject: string; html: string }[] = [];
|
||||
@@ -22,6 +23,9 @@ mock.module("~/server/bug-report-rate-limit", () => ({
|
||||
}));
|
||||
|
||||
const { POST } = await import("./report");
|
||||
const { betaRequestPayload } = await import(
|
||||
"~/components/nook/betaRequest"
|
||||
);
|
||||
|
||||
/** A minimal APIEvent double: JSON body plus optional headers. */
|
||||
function request(body: unknown, headers: Record<string, string> = {}) {
|
||||
@@ -134,4 +138,17 @@ describe("POST /api/the-nook/report", () => {
|
||||
expect(res.status).toBe(200);
|
||||
expect(sentEmails).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("delivers the landing page's beta request with only the email", async () => {
|
||||
const res = await POST(request(betaRequestPayload("tester@example.com")));
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
expect(sentEmails).toHaveLength(1);
|
||||
const email = sentEmails[0]!;
|
||||
expect(email.subject).toBe(
|
||||
"The Nook bug report: iPhone app beta access request"
|
||||
);
|
||||
expect(email.html).toContain("<b>Reply-to:</b> tester@example.com");
|
||||
expect(email.html).not.toContain("<h3>Displays</h3>");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,6 +3,8 @@ import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
|
||||
import SubdomainHeader from "~/components/SubdomainHeader";
|
||||
import FeatureCollage from "~/components/nook/FeatureCollage";
|
||||
import FeatureBreakdowns from "~/components/nook/FeatureBreakdowns";
|
||||
import IslandExtras from "~/components/nook/IslandExtras";
|
||||
import PhoneBeta from "~/components/nook/PhoneBeta";
|
||||
import Button from "~/components/ui/Button";
|
||||
import Input from "~/components/ui/Input";
|
||||
import { createSignal, Show } from "solid-js";
|
||||
@@ -149,6 +151,12 @@ export default function NookLanding() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── Everything else: marquee + live module bento ───────────── */}
|
||||
<IslandExtras />
|
||||
|
||||
{/* ── iPhone + Watch companion, TestFlight beta ──────────────── */}
|
||||
<PhoneBeta brandColor={brandColor()} />
|
||||
|
||||
{/* ── CTA ──────────────────────────────────────────────────────── */}
|
||||
<section class="bg-surface0 relative z-20 px-4 py-20 md:px-8">
|
||||
<div class="mx-auto max-w-4xl text-center">
|
||||
@@ -156,10 +164,10 @@ export default function NookLanding() {
|
||||
Try it free for 14 days
|
||||
</h2>
|
||||
<p class="text-subtext0 mx-auto mb-10 max-w-2xl leading-relaxed">
|
||||
Orchestrate your coding agents, watch temperatures, take control of
|
||||
your fans, and keep background services quiet. When the trial ends,
|
||||
unlock everything with a single one-time payment — no subscription,
|
||||
ever.
|
||||
Orchestrate your coding agents, take control of your fans and
|
||||
battery, give every app its own volume, capture your screen, and
|
||||
keep your day in the notch. When the trial ends, unlock everything
|
||||
with a single one-time payment — no subscription, ever.
|
||||
</p>
|
||||
<div class="flex flex-col items-center justify-center gap-4 sm:flex-row sm:space-x-4">
|
||||
<Button
|
||||
|
||||
Reference in New Issue
Block a user