Compare commits

...

8 Commits

Author SHA1 Message Date
3fe3516b9e nook helper 2026-09-22 22:22:13 -04:00
bef8f8414b utilization reduction 2026-09-22 16:38:43 -04:00
f3e9cdf0bd fix sentry + note email delivery 2026-09-06 13:53:49 -04:00
7ecc0f68d7 increase width cap 2026-09-06 01:08:52 -04:00
00a26a9e49 resized+compressed 2026-09-06 01:06:58 -04:00
163c68fcb8 new with temp demo 2026-09-05 20:02:10 -04:00
3149eba196 fix missing height 2026-09-05 17:20:12 -04:00
bc65789431 fix mobile overlap 2026-09-05 16:59:36 -04:00
38 changed files with 200 additions and 45 deletions

View File

@@ -10,21 +10,7 @@ export default defineConfig({
org: "mikefreno", org: "mikefreno",
project: "freno-dev", project: "freno-dev",
authToken: process.env.SENTRY_AUTH_TOKEN, authToken: process.env.SENTRY_AUTH_TOKEN,
telemetry: false, telemetry: false
sourcemaps: {
assets: [
{
type: "bundle",
path: "dist/client/assets/",
urlPrefix: "~/assets/"
},
{
type: "sourcemap",
path: "dist/client/assets/",
urlPrefix: "~/assets/"
}
]
}
}) })
], ],
build: { build: {

View File

@@ -11,7 +11,8 @@
"test:watch": "bun test --watch", "test:watch": "bun test --watch",
"test:coverage": "bun test --coverage", "test:coverage": "bun test --coverage",
"perf": "bun run scripts/perf-test.ts", "perf": "bun run scripts/perf-test.ts",
"perf:compare": "bun run scripts/perf-compare.ts" "perf:compare": "bun run scripts/perf-compare.ts",
"nook:grant": "NODE_ENV=production bun --env-file=.env scripts/grant-nook-license.ts"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "^3.953.0", "@aws-sdk/client-s3": "^3.953.0",

Binary file not shown.

View File

@@ -1,9 +1,21 @@
User-agent: * User-agent: *
Allow: / Allow: /
Allow: /blog Allow: /blog
Allow: /projects
Disallow: /login # Private / dynamic paths — not for indexing.
Disallow: /debug/ # Keeps crawlers off auth, admin, and API pages so they don't burn
# uncached SSR function invocations.
Disallow: /account
Disallow: /analytics
Disallow: /api/
Disallow: /blog/create
Disallow: /blog/edit
Disallow: /checkout
Disallow: /databaseMGMT Disallow: /databaseMGMT
Disallow: /debug/
Disallow: /error-test
Disallow: /login
Disallow: /success
Disallow: /test
Sitemap: https://www.freno.me/sitemap.xml Sitemap: https://www.freno.me/sitemap.xml

View File

@@ -0,0 +1,29 @@
import { HttpHeader } from "@solidjs/start";
/**
* Renders edge-cache response headers for a page route.
*
* Sets `CDN-Cache-Control` so Vercel serves the rendered HTML from the edge
* (no origin re-render) for `maxAge` seconds, then stale-while-revalidate up
* to `staleSeconds`. `Cache-Control: public, max-age=0` keeps browsers
* revalidating, so visitors always get the latest version — only the CDN
* holds a cached copy. Function/CND-Cache-Control overrides the Vercel
* default, so repeated visits and bot crawls stop re-rendering at origin.
*/
export function EdgeCacheHeaders(props: {
/** Seconds the CDN may serve the response fresh. */
maxAge: number;
/** Seconds the CDN serves stale while revalidating (default: 1 day). */
staleSeconds?: number;
}) {
const stale = props.staleSeconds ?? 86400;
return (
<>
<HttpHeader name="Cache-Control" value="public, max-age=0" />
<HttpHeader
name="CDN-Cache-Control"
value={`public, s-maxage=${props.maxAge}, stale-while-revalidate=${stale}`}
/>
</>
);
}

View File

@@ -4,16 +4,16 @@ import { buildMainSiteUrl } from "~/lib/site-context";
export default function SubdomainFooter() { export default function SubdomainFooter() {
return ( return (
<footer class="border-surface0 bg-surface0 relative z-10 border-t py-8"> <footer class="border-surface0 bg-surface0 relative z-10 border-t py-8">
<div class="relative flex items-center text-sm"> <div class="relative flex flex-col items-center gap-2 text-sm sm:flex-row sm:justify-center">
<A <A
href={buildMainSiteUrl()} href={buildMainSiteUrl()}
class="text-text/60 hover:text-text/80 mx-auto text-center underline underline-offset-4 transition-colors" class="text-text/60 hover:text-text/80 text-center underline underline-offset-4 transition-colors"
> >
made with <span class="text-red-400">&lt;3</span> made with <span class="text-red-400">&lt;3</span>
</A> </A>
<A <A
href={buildMainSiteUrl("/downloads")} href={buildMainSiteUrl("/downloads")}
class="text-text/80 hover:text-text absolute right-4 underline underline-offset-4 transition-colors" class="text-text/80 hover:text-text underline underline-offset-4 transition-colors sm:absolute sm:right-4"
> >
see more products see more products
</A> </A>

View File

@@ -764,8 +764,12 @@ function CameraDemo() {
}); });
}); });
createEffect(() => { createEffect(() => {
if (phase() === "live") videoRef?.play(); if (phase() === "live" && videoRef) {
else videoRef?.pause(); videoRef.currentTime = 0;
videoRef.play();
} else {
videoRef?.pause();
}
}); });
return ( return (
<ModuleCard class="relative mx-auto w-full max-w-[300px]"> <ModuleCard class="relative mx-auto w-full max-w-[300px]">
@@ -809,9 +813,9 @@ function CameraDemo() {
ref={videoRef} ref={videoRef}
src="/nook/cam-recording.mp4" src="/nook/cam-recording.mp4"
muted muted
loop
playsinline playsinline
preload="metadata" preload="metadata"
onEnded={() => setPhase("idle")}
class="absolute inset-0 h-full w-full object-cover transition-opacity duration-700" class="absolute inset-0 h-full w-full object-cover transition-opacity duration-700"
style={{ opacity: phase() === "live" ? 1 : 0 }} style={{ opacity: phase() === "live" ? 1 : 0 }}
/> />
@@ -1027,7 +1031,7 @@ export default function FeatureBreakdowns() {
body="Gated tool calls hold right in the panel. Allow, deny, or demand a reason. Walking away? The same request lands on your phone and your tap flies back to the agent." body="Gated tool calls hold right in the panel. Allow, deny, or demand a reason. Walking away? The same request lands on your phone and your tap flies back to the agent."
bullets={[ bullets={[
"Permission and question cards pin above the session list", "Permission and question cards pin above the session list",
"Push-to-phone over your own network — no cloud middleman", "Push-to-phone over your own network — no cloud middleman (coming soon)",
"Works across every hooked agent, local or remote" "Works across every hooked agent, local or remote"
]} ]}
reversed reversed

View File

@@ -46,7 +46,10 @@ function CollageCell(props: {
*/ */
function InkStage(props: { children: JSX.Element; class?: string }) { function InkStage(props: { children: JSX.Element; class?: string }) {
return ( return (
<div class={`flex justify-center rounded-xl py-5 ${props.class ?? ""}`} style={{ background: "#0d0d0f" }}> <div
class={`flex justify-center rounded-xl py-5 ${props.class ?? ""}`}
style={{ background: "#0d0d0f" }}
>
{props.children} {props.children}
</div> </div>
); );
@@ -153,7 +156,10 @@ function RemoteMock() {
<div class="space-y-1.5"> <div class="space-y-1.5">
<div <div
class="rounded-[10px] p-2.5" class="rounded-[10px] p-2.5"
style={{ background: "rgba(255,255,255,0.055)", "box-shadow": "inset 0 0 0 1px rgba(255,255,255,0.07)" }} style={{
background: "rgba(255,255,255,0.055)",
"box-shadow": "inset 0 0 0 1px rgba(255,255,255,0.07)"
}}
> >
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span <span
@@ -172,7 +178,10 @@ function RemoteMock() {
</div> </div>
<div <div
class="rounded-[10px] p-2.5" class="rounded-[10px] p-2.5"
style={{ background: "rgba(255,255,255,0.055)", "box-shadow": "inset 0 0 0 1px rgba(255,255,255,0.07)" }} style={{
background: "rgba(255,255,255,0.055)",
"box-shadow": "inset 0 0 0 1px rgba(255,255,255,0.07)"
}}
> >
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span <span
@@ -237,7 +246,7 @@ function GaugesMock() {
export default function FeatureCollage() { export default function FeatureCollage() {
return ( return (
<section <section
class="bg-base relative z-20 -mt-28 px-4 pb-16 pt-40 md:px-8" class="bg-base relative z-20 px-4 pt-40 pb-16 md:px-8"
id="feature-collage" id="feature-collage"
> >
<div class="mx-auto max-w-5xl"> <div class="mx-auto max-w-5xl">
@@ -250,9 +259,7 @@ export default function FeatureCollage() {
<div class="grid grid-cols-1 gap-5 md:grid-cols-3"> <div class="grid grid-cols-1 gap-5 md:grid-cols-3">
{/* Hero: real collapsed pill on the island surface */} {/* Hero: real collapsed pill on the island surface */}
<div <div class="border-overlay1 bg-surface1 relative col-span-1 flex flex-col items-center overflow-hidden rounded-2xl border p-6 md:col-span-2">
class="border-overlay1 bg-surface1 relative col-span-1 flex flex-col items-center overflow-hidden rounded-2xl border p-6 md:col-span-2"
>
<div class="mb-5 self-start"> <div class="mb-5 self-start">
<p class="text-subtext1 mb-3 text-[11px] font-semibold tracking-[0.14em] uppercase"> <p class="text-subtext1 mb-3 text-[11px] font-semibold tracking-[0.14em] uppercase">
The island The island
@@ -261,9 +268,9 @@ export default function FeatureCollage() {
Every agent, one glance Every agent, one glance
</h3> </h3>
<p class="text-subtext0 max-w-sm text-sm leading-relaxed"> <p class="text-subtext0 max-w-sm text-sm leading-relaxed">
A pill tucked into your notch a campfire for your whole A pill tucked into your notch a campfire for your whole fleet
fleet on the left, live data on the right. Blue running, on the left, live data on the right. Blue running, green ready,
green ready, amber needs you. amber needs you.
</p> </p>
</div> </div>
<div class="my-10 flex w-full justify-center"> <div class="my-10 flex w-full justify-center">
@@ -304,7 +311,7 @@ export default function FeatureCollage() {
<CollageCell <CollageCell
kicker="Approvals" kicker="Approvals"
title="Unblock agents, anywhere" title="Unblock agents, anywhere"
body="Permission prompts surface as cards in the panel — Allow once, always, or deny. Away from the desk, push them to your phone and answer from there." body="Permission prompts surface as cards in the panel — Allow once, always, or deny. Away from the desk, push them to your phone and answer from there (coming soon)."
> >
<InkStage> <InkStage>
<div class="w-full max-w-sm px-3"> <div class="w-full max-w-sm px-3">

View File

@@ -1,5 +1,5 @@
import { PageHead } from "~/components/PageHead"; import { PageHead } from "~/components/PageHead";
import { HttpStatusCode } from "@solidjs/start"; import { HttpHeader, HttpStatusCode } from "@solidjs/start";
import { useLocation, useNavigate } from "@solidjs/router"; import { useLocation, useNavigate } from "@solidjs/router";
import { createSignal, onCleanup, onMount, Show } from "solid-js"; import { createSignal, onCleanup, onMount, Show } from "solid-js";
import { TerminalErrorPage } from "~/components/TerminalErrorPage"; import { TerminalErrorPage } from "~/components/TerminalErrorPage";
@@ -90,6 +90,14 @@ export default function NotFound() {
description="404 - Page not found. The page you're looking for doesn't exist." description="404 - Page not found. The page you're looking for doesn't exist."
/> />
<HttpStatusCode code={404} /> <HttpStatusCode code={404} />
{/* Cache 404/fallback responses at the edge (Vercel caches 404s) so
bots/monitors that probe dead paths don't re-render the full page
on every request. Function headers override vercel.json here. */}
<HttpHeader name="Cache-Control" value="public, max-age=0" />
<HttpHeader
name="CDN-Cache-Control"
value="public, s-maxage=300, stale-while-revalidate=86400"
/>
<TerminalErrorPage <TerminalErrorPage
errorContent={errorContent} errorContent={errorContent}
quickActions={quickActions} quickActions={quickActions}

23
src/routes/api/health.ts Normal file
View File

@@ -0,0 +1,23 @@
import type { APIEvent } from "@solidjs/start/server";
/**
* Lightweight uptime/monitoring probe.
*
* Returns a tiny 200 that the edge CDN caches (`CDN-Cache-Control`), so
* health checks (Sentry uptime monitors, external monitors, `curl`) cost a
* fraction of a rendered page instead of a full SSR response. Point monitors
* at `https://freno.me/api/health`.
*/
export async function GET(_event: APIEvent) {
return new Response(JSON.stringify({ status: "ok" }), {
status: 200,
headers: {
"Content-Type": "application/json; charset=utf-8",
// Browser: always revalidate (tiny body, no reason to cache).
"Cache-Control": "public, max-age=0",
// Edge CDN: serve from cache for 1 min, then stale-while-revalidate
// for a day so repeated probe hits never touch the origin.
"CDN-Cache-Control": "public, s-maxage=60, stale-while-revalidate=86400"
}
});
}

View File

@@ -7,6 +7,7 @@ import {
useSearchParams useSearchParams
} from "@solidjs/router"; } from "@solidjs/router";
import { PageHead } from "~/components/PageHead"; import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import { createAsync } from "@solidjs/router"; import { createAsync } from "@solidjs/router";
import { getRequestEvent } from "solid-js/web"; import { getRequestEvent } from "solid-js/web";
import AuthenticatedLike from "~/components/blog/AuthenticatedLike"; import AuthenticatedLike from "~/components/blog/AuthenticatedLike";
@@ -327,6 +328,7 @@ export default function PostPage() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={60} staleSeconds={3600} />
<PageHead <PageHead
title={p().title.replaceAll("_", " ")} title={p().title.replaceAll("_", " ")}
description={ description={

View File

@@ -1,6 +1,7 @@
import { Show } from "solid-js"; import { Show } from "solid-js";
import { useSearchParams, A, query } from "@solidjs/router"; import { useSearchParams, A, query } from "@solidjs/router";
import { PageHead } from "~/components/PageHead"; import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import { createAsync } from "@solidjs/router"; import { createAsync } from "@solidjs/router";
import PostSortingSelect from "~/components/blog/PostSortingSelect"; import PostSortingSelect from "~/components/blog/PostSortingSelect";
import TagSelector from "~/components/blog/TagSelector"; import TagSelector from "~/components/blog/TagSelector";
@@ -90,6 +91,7 @@ export default function BlogIndex() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={60} staleSeconds={3600} />
<PageHead <PageHead
title="Blog" title="Blog"
description="Technical blog posts about web development, programming, and software engineering." description="Technical blog posts about web development, programming, and software engineering."

View File

@@ -3,6 +3,7 @@ import { useSearchParams } from "@solidjs/router";
import { A } from "@solidjs/router"; import { A } from "@solidjs/router";
import RevealDropDown from "~/components/RevealDropDown"; import RevealDropDown from "~/components/RevealDropDown";
import { ContactForm } from "~/components/ContactForm"; import { ContactForm } from "~/components/ContactForm";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import { buildSubdomainUrl } from "~/lib/site-context"; import { buildSubdomainUrl } from "~/lib/site-context";
import { useSite } from "~/context/SiteContext"; import { useSite } from "~/context/SiteContext";
import NessaContactPage from "./nessa/contact"; import NessaContactPage from "./nessa/contact";
@@ -125,7 +126,9 @@ function MainContactPage() {
const viewer = () => searchParams.viewer ?? "default"; const viewer = () => searchParams.viewer ?? "default";
return ( return (
<ContactForm <>
<EdgeCacheHeaders maxAge={60} staleSeconds={3600} />
<ContactForm
subline={ subline={
<Show when={viewer() !== "lineage"}> <Show when={viewer() !== "lineage"}>
(for this website or any of my apps...) (for this website or any of my apps...)
@@ -134,6 +137,7 @@ function MainContactPage() {
> >
<LineageContactQuestions /> <LineageContactQuestions />
</ContactForm> </ContactForm>
</>
); );
} }

View File

@@ -1,4 +1,5 @@
import { PageHead } from "~/components/PageHead"; import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import { A } from "@solidjs/router"; import { A } from "@solidjs/router";
import { createSignal, onMount, onCleanup, Switch, Match } from "solid-js"; import { createSignal, onMount, onCleanup, Switch, Match } from "solid-js";
import DownloadOnAppStore from "~/components/icons/DownloadOnAppStore"; import DownloadOnAppStore from "~/components/icons/DownloadOnAppStore";
@@ -71,6 +72,7 @@ function MainDownloadsPage() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={300} />
<PageHead <PageHead
title="Downloads" title="Downloads"
description="Download The Nook, InputHalo, Gaze, Life and Lineage, Shapes with Abigail, and Cork. Available on macOS, iOS, and Android." description="Download The Nook, InputHalo, Gaze, Life and Lineage, Shapes with Abigail, and Cork. Available on macOS, iOS, and Android."

View File

@@ -1,4 +1,5 @@
import { ContactForm } from "~/components/ContactForm"; import { ContactForm } from "~/components/ContactForm";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import SubdomainHeader from "~/components/SubdomainHeader"; import SubdomainHeader from "~/components/SubdomainHeader";
/** /**
@@ -19,6 +20,7 @@ import SubdomainHeader from "~/components/SubdomainHeader";
export default function GazeContactPage() { export default function GazeContactPage() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={60} staleSeconds={3600} />
<SubdomainHeader /> <SubdomainHeader />
<ContactForm /> <ContactForm />
</> </>

View File

@@ -11,6 +11,7 @@
import { A } from "@solidjs/router"; import { A } from "@solidjs/router";
import { createSignal } from "solid-js"; import { createSignal } from "solid-js";
import { PageHead } from "~/components/PageHead"; import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import SubdomainHeader from "~/components/SubdomainHeader"; import SubdomainHeader from "~/components/SubdomainHeader";
import DownloadOnAppStoreDark from "~/components/icons/DownloadOnAppStoreDark"; import DownloadOnAppStoreDark from "~/components/icons/DownloadOnAppStoreDark";
import Button from "~/components/ui/Button"; import Button from "~/components/ui/Button";
@@ -48,6 +49,7 @@ export default function GazeDownloadsPage() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={300} />
<PageHead <PageHead
title="Download Gaze" title="Download Gaze"
description="Download Gaze for macOS — menu bar app for eye and posture health." description="Download Gaze for macOS — menu bar app for eye and posture health."

View File

@@ -1,5 +1,6 @@
import { createSignal, For } from "solid-js"; import { createSignal, For } from "solid-js";
import { PageHead } from "~/components/PageHead"; import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import SubdomainHeader from "~/components/SubdomainHeader"; import SubdomainHeader from "~/components/SubdomainHeader";
import DownloadOnAppStoreDark from "~/components/icons/DownloadOnAppStoreDark"; import DownloadOnAppStoreDark from "~/components/icons/DownloadOnAppStoreDark";
import Button from "~/components/ui/Button"; import Button from "~/components/ui/Button";
@@ -60,6 +61,7 @@ export default function GazeLanding() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={300} />
<PageHead <PageHead
title="Home" title="Home"
description="Gaze is a macOS menu bar app for eye and posture health — blink reminders, 20-20-20 breaks, posture check-ins, and customizable reminder intervals." description="Gaze is a macOS menu bar app for eye and posture health — blink reminders, 20-20-20 breaks, posture check-ins, and customizable reminder intervals."

View File

@@ -15,11 +15,13 @@
*/ */
import { A } from "@solidjs/router"; import { A } from "@solidjs/router";
import { PageHead } from "~/components/PageHead"; import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import SubdomainHeader from "~/components/SubdomainHeader"; import SubdomainHeader from "~/components/SubdomainHeader";
export default function GazePrivacyPolicy() { export default function GazePrivacyPolicy() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={300} />
<PageHead <PageHead
title="Privacy Policy" title="Privacy Policy"
description="Privacy policy for Gaze, a macOS eye health reminder app." description="Privacy policy for Gaze, a macOS eye health reminder app."

View File

@@ -1,8 +1,10 @@
import { Switch, Match, type JSX } from "solid-js"; import { Switch, Match, type JSX } from "solid-js";
import { PageHead } from "~/components/PageHead"; import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import { DarkModeToggle } from "~/components/DarkModeToggle"; import { DarkModeToggle } from "~/components/DarkModeToggle";
import { Typewriter } from "~/components/Typewriter"; import { Typewriter } from "~/components/Typewriter";
import { useSite } from "~/context/SiteContext"; import { useSite } from "~/context/SiteContext";
import { buildSubdomainUrl } from "~/lib/site-context";
import NessaLanding from "./nessa"; import NessaLanding from "./nessa";
import LineageLanding from "./lineage"; import LineageLanding from "./lineage";
import GazeLanding from "./gaze"; import GazeLanding from "./gaze";
@@ -56,6 +58,7 @@ export default function Home(): JSX.Element {
function MainHome(): JSX.Element { function MainHome(): JSX.Element {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={300} />
<PageHead <PageHead
title="Home" title="Home"
description="Michael Freno - Software Engineer based in Brooklyn, NY" description="Michael Freno - Software Engineer based in Brooklyn, NY"
@@ -101,6 +104,32 @@ function MainHome(): JSX.Element {
</Typewriter> </Typewriter>
<div class="pt-8 text-center"> <div class="pt-8 text-center">
<div class="pb-4">Some of my recent projects:</div> <div class="pb-4">Some of my recent projects:</div>
{/* The Nook */}
<div class="border-surface0 mb-2 flex w-full flex-col gap-2 rounded-md border-2 p-4 text-center">
<div>My macOS notch utility:</div>
<a
href={buildSubdomainUrl("nook")}
class="text-blue hover-underline-animation mx-auto w-fit"
>
The Nook
</a>
<div class="mx-auto w-full max-w-4xl overflow-hidden rounded-lg">
<video
src="/nook/demo-expansion.mp4"
class="h-full w-full object-cover"
autoplay
loop
muted
playsinline
/>
</div>
<div class="pt-2 text-left text-sm">
A native macOS island that lives in the notch: orchestration for
a fleet of coding agents, fan curve control, thermal gauges,
calendar and camera modules. One-time purchase - no
subscription, ever.
</div>
</div>
<div class="flex flex-col items-center gap-2 xl:flex-row xl:items-start xl:justify-center"> <div class="flex flex-col items-center gap-2 xl:flex-row xl:items-start xl:justify-center">
{/* FlexLöve */} {/* FlexLöve */}
<div class="border-surface0 flex w-full flex-col rounded-md border-2 p-4 text-center"> <div class="border-surface0 flex w-full flex-col rounded-md border-2 p-4 text-center">

View File

@@ -1,4 +1,5 @@
import { ContactForm } from "~/components/ContactForm"; import { ContactForm } from "~/components/ContactForm";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import SubdomainHeader from "~/components/SubdomainHeader"; import SubdomainHeader from "~/components/SubdomainHeader";
/** /**
@@ -19,6 +20,7 @@ import SubdomainHeader from "~/components/SubdomainHeader";
export default function InputHaloContactPage() { export default function InputHaloContactPage() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={60} staleSeconds={3600} />
<SubdomainHeader /> <SubdomainHeader />
<ContactForm /> <ContactForm />
</> </>

View File

@@ -11,6 +11,7 @@
import { A } from "@solidjs/router"; import { A } from "@solidjs/router";
import { createSignal } from "solid-js"; import { createSignal } from "solid-js";
import { PageHead } from "~/components/PageHead"; import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import SubdomainHeader from "~/components/SubdomainHeader"; import SubdomainHeader from "~/components/SubdomainHeader";
import DownloadOnAppStoreDark from "~/components/icons/DownloadOnAppStoreDark"; import DownloadOnAppStoreDark from "~/components/icons/DownloadOnAppStoreDark";
import Button from "~/components/ui/Button"; import Button from "~/components/ui/Button";
@@ -51,6 +52,7 @@ export default function InputHaloDownloadsPage() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={300} />
<PageHead <PageHead
title="Download InputHalo" title="Download InputHalo"
description="Download InputHalo for macOS — menu bar app for keyboard, mouse, and scroll visualization." description="Download InputHalo for macOS — menu bar app for keyboard, mouse, and scroll visualization."

View File

@@ -13,6 +13,7 @@
import { For, createSignal } from "solid-js"; import { For, createSignal } from "solid-js";
import { A } from "@solidjs/router"; import { A } from "@solidjs/router";
import { PageHead } from "~/components/PageHead"; import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import SubdomainHeader from "~/components/SubdomainHeader"; import SubdomainHeader from "~/components/SubdomainHeader";
import Button from "~/components/ui/Button"; import Button from "~/components/ui/Button";
import DownloadOnAppStoreDark from "~/components/icons/DownloadOnAppStoreDark"; import DownloadOnAppStoreDark from "~/components/icons/DownloadOnAppStoreDark";
@@ -98,6 +99,7 @@ export default function InputHaloLanding() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={300} />
<PageHead <PageHead
title="Home" title="Home"
description="A polished macOS menu bar app that visualizes keyboard presses, mouse clicks, cursor halos, and scroll events on screen — for streamers, presenters, and developers." description="A polished macOS menu bar app that visualizes keyboard presses, mouse clicks, cursor halos, and scroll events on screen — for streamers, presenters, and developers."

View File

@@ -23,11 +23,13 @@
*/ */
import { A } from "@solidjs/router"; import { A } from "@solidjs/router";
import { PageHead } from "~/components/PageHead"; import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import SubdomainHeader from "~/components/SubdomainHeader"; import SubdomainHeader from "~/components/SubdomainHeader";
export default function InputHaloPrivacyPolicy() { export default function InputHaloPrivacyPolicy() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={300} />
<PageHead <PageHead
title="Privacy Policy" title="Privacy Policy"
description="Privacy policy for InputHalo, a macOS menu bar productivity app." description="Privacy policy for InputHalo, a macOS menu bar productivity app."

View File

@@ -1,4 +1,5 @@
import { ContactForm } from "~/components/ContactForm"; import { ContactForm } from "~/components/ContactForm";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import SubdomainHeader from "~/components/SubdomainHeader"; import SubdomainHeader from "~/components/SubdomainHeader";
import { LineageContactQuestions } from "../contact"; import { LineageContactQuestions } from "../contact";
@@ -24,6 +25,7 @@ import { LineageContactQuestions } from "../contact";
export default function LineageContactPage() { export default function LineageContactPage() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={60} staleSeconds={3600} />
<SubdomainHeader /> <SubdomainHeader />
<ContactForm> <ContactForm>
<LineageContactQuestions /> <LineageContactQuestions />

View File

@@ -33,6 +33,7 @@
* the form posts to the correct tRPC mutation (Lineage-branded email). * the form posts to the correct tRPC mutation (Lineage-branded email).
*/ */
import { PageHead } from "~/components/PageHead"; import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import SubdomainHeader from "~/components/SubdomainHeader"; import SubdomainHeader from "~/components/SubdomainHeader";
import DeletionForm from "~/components/DeletionForm"; import DeletionForm from "~/components/DeletionForm";
import { import {
@@ -45,6 +46,7 @@ import {
export default function LineageDeletionPage() { export default function LineageDeletionPage() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={300} />
<PageHead title={PAGE_META.title} description={PAGE_META.description} /> <PageHead title={PAGE_META.title} description={PAGE_META.description} />
<SubdomainHeader /> <SubdomainHeader />
<div class="pt-20"> <div class="pt-20">

View File

@@ -30,6 +30,7 @@
import { A } from "@solidjs/router"; import { A } from "@solidjs/router";
import { createSignal, onMount, onCleanup } from "solid-js"; import { createSignal, onMount, onCleanup } from "solid-js";
import { PageHead } from "~/components/PageHead"; import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import SubdomainHeader from "~/components/SubdomainHeader"; import SubdomainHeader from "~/components/SubdomainHeader";
import DownloadOnAppStore from "~/components/icons/DownloadOnAppStore"; import DownloadOnAppStore from "~/components/icons/DownloadOnAppStore";
import Button from "~/components/ui/Button"; import Button from "~/components/ui/Button";
@@ -71,6 +72,7 @@ export default function LineageDownloadsPage() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={300} />
<PageHead title={PAGE_META.title} description={PAGE_META.description} /> <PageHead title={PAGE_META.title} description={PAGE_META.description} />
<SubdomainHeader /> <SubdomainHeader />

View File

@@ -11,6 +11,7 @@
*/ */
import { A } from "@solidjs/router"; import { A } from "@solidjs/router";
import { PageHead } from "~/components/PageHead"; import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import SubdomainHeader from "~/components/SubdomainHeader"; import SubdomainHeader from "~/components/SubdomainHeader";
import SimpleParallax from "~/components/SimpleParallax"; import SimpleParallax from "~/components/SimpleParallax";
import DownloadOnAppStoreDark from "~/components/icons/DownloadOnAppStoreDark"; import DownloadOnAppStoreDark from "~/components/icons/DownloadOnAppStoreDark";
@@ -25,6 +26,7 @@ import {
export default function LineageLandingPage() { export default function LineageLandingPage() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={300} />
<PageHead title={PAGE_META.title} description={PAGE_META.description} /> <PageHead title={PAGE_META.title} description={PAGE_META.description} />
<SubdomainHeader /> <SubdomainHeader />
<SimpleParallax> <SimpleParallax>

View File

@@ -17,11 +17,13 @@
*/ */
import { A } from "@solidjs/router"; import { A } from "@solidjs/router";
import { PageHead } from "~/components/PageHead"; import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import SubdomainHeader from "~/components/SubdomainHeader"; import SubdomainHeader from "~/components/SubdomainHeader";
export default function LineagePrivacyPolicy() { export default function LineagePrivacyPolicy() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={300} />
<PageHead <PageHead
title="Privacy Policy" title="Privacy Policy"
description="Privacy policy for Life and Lineage mobile game, outlining data collection, usage, and user rights." description="Privacy policy for Life and Lineage mobile game, outlining data collection, usage, and user rights."

View File

@@ -1,4 +1,5 @@
import { ContactForm } from "~/components/ContactForm"; import { ContactForm } from "~/components/ContactForm";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import SubdomainHeader from "~/components/SubdomainHeader"; import SubdomainHeader from "~/components/SubdomainHeader";
/** /**
@@ -20,6 +21,7 @@ import SubdomainHeader from "~/components/SubdomainHeader";
export default function NessaContactPage() { export default function NessaContactPage() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={60} staleSeconds={3600} />
<SubdomainHeader /> <SubdomainHeader />
<ContactForm /> <ContactForm />
</> </>

View File

@@ -24,6 +24,7 @@
* Acceptance: `nessa.localhost:3000/deletion` renders the deletion form. * Acceptance: `nessa.localhost:3000/deletion` renders the deletion form.
*/ */
import { PageHead } from "~/components/PageHead"; import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import SubdomainHeader from "~/components/SubdomainHeader"; import SubdomainHeader from "~/components/SubdomainHeader";
import DeletionForm from "~/components/DeletionForm"; import DeletionForm from "~/components/DeletionForm";
import { import {
@@ -36,6 +37,7 @@ import {
export default function NessaDeletionPage() { export default function NessaDeletionPage() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={300} />
<PageHead title={PAGE_META.title} description={PAGE_META.description} /> <PageHead title={PAGE_META.title} description={PAGE_META.description} />
<SubdomainHeader /> <SubdomainHeader />
<div class="pt-20"> <div class="pt-20">

View File

@@ -12,6 +12,7 @@
*/ */
import { For, Show } from "solid-js"; import { For, Show } from "solid-js";
import { PageHead } from "~/components/PageHead"; import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import SubdomainHeader from "~/components/SubdomainHeader"; import SubdomainHeader from "~/components/SubdomainHeader";
import { useDarkMode } from "~/context/darkMode"; import { useDarkMode } from "~/context/darkMode";
import { useSite } from "~/context/SiteContext"; import { useSite } from "~/context/SiteContext";
@@ -59,6 +60,7 @@ export default function NessaLanding() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={300} />
<PageHead {...NESSA_LANDING_META} /> <PageHead {...NESSA_LANDING_META} />
<SubdomainHeader /> <SubdomainHeader />

View File

@@ -29,11 +29,13 @@
*/ */
import { A } from "@solidjs/router"; import { A } from "@solidjs/router";
import { PageHead } from "~/components/PageHead"; import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import SubdomainHeader from "~/components/SubdomainHeader"; import SubdomainHeader from "~/components/SubdomainHeader";
export default function NessaPrivacyPolicy() { export default function NessaPrivacyPolicy() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={300} />
<PageHead <PageHead
title="Privacy Policy" title="Privacy Policy"
description="Privacy policy for Nessa, a community platform for clubs, challenges, and social features." description="Privacy policy for Nessa, a community platform for clubs, challenges, and social features."

View File

@@ -110,8 +110,8 @@ export default function NookCheckout() {
{error() && <p class="text-red mt-4 text-sm">{error()}</p>} {error() && <p class="text-red mt-4 text-sm">{error()}</p>}
<p class="text-subtext1 mt-4 text-xs"> <p class="text-subtext1 mt-4 text-xs">
Billed once through Stripe. License delivered immediately after Billed once through Stripe. License delivered immediately and by
payment. email after payment.
</p> </p>
</div> </div>
</div> </div>

View File

@@ -1,4 +1,5 @@
import { PageHead } from "~/components/PageHead"; import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import SubdomainHeader from "~/components/SubdomainHeader"; import SubdomainHeader from "~/components/SubdomainHeader";
import FeatureCollage from "~/components/nook/FeatureCollage"; import FeatureCollage from "~/components/nook/FeatureCollage";
import FeatureBreakdowns from "~/components/nook/FeatureBreakdowns"; import FeatureBreakdowns from "~/components/nook/FeatureBreakdowns";
@@ -57,6 +58,7 @@ export default function NookLanding() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={300} />
<PageHead <PageHead
title="Home" title="Home"
description="The Nook — a native macOS utility for coding-agent orchestration, fan and thermal control." description="The Nook — a native macOS utility for coding-agent orchestration, fan and thermal control."
@@ -68,7 +70,9 @@ export default function NookLanding() {
<SubdomainHeader /> <SubdomainHeader />
{/* ── Hero ─────────────────────────────────────────────────────── */} {/* ── Hero ─────────────────────────────────────────────────────── */}
<div class="relative flex min-h-screen flex-col overflow-hidden"> {/* -mt-14 pulls the hero under the sticky header; pt-14 re-clears
content, so the gradient fills the full viewport incl. the bar */}
<div class="relative -mt-14 flex min-h-svh flex-col overflow-hidden pt-14">
<div <div
class="fixed inset-0 z-0" class="fixed inset-0 z-0"
style={{ style={{
@@ -77,13 +81,13 @@ export default function NookLanding() {
: "radial-gradient(ellipse at top, #d7eef5 0%, #f5f5f5 70%)" : "radial-gradient(ellipse at top, #d7eef5 0%, #f5f5f5 70%)"
}} }}
/> />
<div class="relative z-10 flex min-h-screen flex-col items-center justify-center px-4 py-24 text-center"> <div class="relative z-10 flex flex-1 flex-col items-center justify-center px-4 py-12 text-center sm:py-24">
<img <img
src="/nook/icon.png" src="/nook/icon.png"
alt="The Nook App Icon" alt="The Nook App Icon"
height={128} height={128}
width={128} width={128}
class="mb-6 h-32 w-32 rounded-[22%] object-cover object-center shadow-2xl" class="mb-4 h-24 w-24 rounded-[22%] object-cover object-center shadow-2xl sm:mb-6 sm:h-32 sm:w-32"
/> />
<div <div
class="text-text/90 mb-6 rounded-2xl px-5 py-3 text-sm font-semibold tracking-wide backdrop-blur-sm" class="text-text/90 mb-6 rounded-2xl px-5 py-3 text-sm font-semibold tracking-wide backdrop-blur-sm"
@@ -94,7 +98,7 @@ export default function NookLanding() {
> >
The Nook The Nook
</div> </div>
<h1 class="text-text mb-4 text-5xl font-bold tracking-tight"> <h1 class="text-text mb-4 text-4xl font-bold tracking-tight sm:text-5xl">
Your Mac and Agents, under control Your Mac and Agents, under control
</h1> </h1>
<p class="text-subtext0 mb-2 max-w-xl text-xl"> <p class="text-subtext0 mb-2 max-w-xl text-xl">

View File

@@ -7,11 +7,13 @@
*/ */
import { buildMainSiteUrl } from "~/lib/subdomain-url"; import { buildMainSiteUrl } from "~/lib/subdomain-url";
import { PageHead } from "~/components/PageHead"; import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import SubdomainHeader from "~/components/SubdomainHeader"; import SubdomainHeader from "~/components/SubdomainHeader";
export default function NookPrivacyPolicy() { export default function NookPrivacyPolicy() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={300} />
<PageHead <PageHead
title="Privacy Policy" title="Privacy Policy"
description="Privacy policy for The Nook, a coding-agent orchestration and hardware control app." description="Privacy policy for The Nook, a coding-agent orchestration and hardware control app."

View File

@@ -1,5 +1,6 @@
import { A } from "@solidjs/router"; import { A } from "@solidjs/router";
import { PageHead } from "~/components/PageHead"; import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
/** /**
* Main-site privacy policy — `freno.me/privacy-policy`. * Main-site privacy policy — `freno.me/privacy-policy`.
@@ -14,6 +15,7 @@ import { PageHead } from "~/components/PageHead";
export default function PrivacyPolicy() { export default function PrivacyPolicy() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={300} />
<PageHead <PageHead
title="Privacy Policy" title="Privacy Policy"
description="Privacy policy for the freno.me blog and personal site, covering accounts, comments, and contact forms." description="Privacy policy for the freno.me blog and personal site, covering accounts, comments, and contact forms."

View File

@@ -1,9 +1,11 @@
import { A } from "@solidjs/router"; import { A } from "@solidjs/router";
import { PageHead } from "~/components/PageHead"; import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
export default function PrivacyPolicy() { export default function PrivacyPolicy() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={300} />
<PageHead <PageHead
title="Privacy Policy - Shapes with Abigail" title="Privacy Policy - Shapes with Abigail"
description="Privacy policy for Shapes with Abigail app, explaining our commitment to child safety and non-collection of personal data." description="Privacy policy for Shapes with Abigail app, explaining our commitment to child safety and non-collection of personal data."

View File

@@ -1,5 +1,6 @@
import { onCleanup, onMount } from "solid-js"; import { onCleanup, onMount } from "solid-js";
import { PageHead } from "~/components/PageHead"; import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
export default function Resume() { export default function Resume() {
let iframeRef: HTMLIFrameElement | undefined; let iframeRef: HTMLIFrameElement | undefined;
@@ -25,6 +26,7 @@ export default function Resume() {
return ( return (
<> <>
<EdgeCacheHeaders maxAge={300} />
<PageHead <PageHead
title="Resume" title="Resume"
description="View Michael Freno's resume - Software Engineer." description="View Michael Freno's resume - Software Engineer."