import { PageHead } from "~/components/PageHead"; import { A } from "@solidjs/router"; import { createSignal, onMount, onCleanup, Switch, Match } from "solid-js"; import DownloadOnAppStore from "~/components/icons/DownloadOnAppStore"; import { glitchText } from "~/lib/client-utils"; import { buildSubdomainUrl } from "~/lib/subdomain-url"; import Button from "~/components/ui/Button"; import { useSite } from "~/context/SiteContext"; import LineageDownloadsPage from "./lineage/downloads"; import GazeDownloadsPage from "./gaze/downloads"; import InputHaloDownloadsPage from "./inputhalo/downloads"; function MainDownloadsPage() { const [LaLText, setLaLText] = createSignal("Life and Lineage"); const [SwAText, setSwAText] = createSignal("Shapes with Abigail!"); const [corkText, setCorkText] = createSignal("Cork"); const [gazeText, setGazeText] = createSignal("Gaze"); const [inputHaloText, setInputHaloText] = createSignal("InputHalo"); const [loadingState, setLoadingState] = createSignal>( { lineage: false, cork: false, gaze: false, "shapes-with-abigail": false, inputhalo: false } ); const download = (assetName: string) => { // Prevent multiple rapid clicks if (loadingState()[assetName]) return; setLoadingState((prev) => ({ ...prev, [assetName]: true })); import("~/lib/api").then(({ api }) => { api.downloads.getDownloadUrl .query({ asset_name: assetName }) .then((data) => { const url = data.downloadURL; window.location.href = url; }) .catch((error) => { console.error("Download error:", error); alert("Failed to initiate download. Please try again."); }) .finally(() => { setLoadingState((prev) => ({ ...prev, [assetName]: false })); }); }); }; onMount(() => { const lalInterval = glitchText(LaLText(), setLaLText); const swaInterval = glitchText(SwAText(), setSwAText); const corkInterval = glitchText(corkText(), setCorkText); const gazeInterval = glitchText(gazeText(), setGazeText); const inputHaloInterval = glitchText(inputHaloText(), setInputHaloText); onCleanup(() => { clearInterval(lalInterval); clearInterval(swaInterval); clearInterval(corkInterval); clearInterval(gazeInterval); clearInterval(inputHaloInterval); }); }); return ( <>
{/* Subtle scanline effect */}
Ordered by date of initial release
{/* InputHalo */}

{">"}{" "} {inputHaloText()}

platform: macOS (14.6+)
variant: paid (coming soon)
variant: free (coming soon)
{/* Gaze */}

{">"}{" "} {gazeText()}

platform: macOS (14.6+)
variant: paid
variant: free (coming soon)

{">"}{" "} {LaLText()}

platform: android # android build not optimized
platform: ios
{/* Cork */}

{">"} {corkText()}

platform: macOS (13.0+) # unzip → drag to /Applications
{/* Shapes with Abigail */}

{">"} {SwAText()}

platform: android
platform: ios
); } /** * Host-aware downloads route handler. * * Dispatches to the per-subdomain download surface based on the resolved * site (see `src/routes/index.tsx` for the pattern). Nessa has no dedicated * downloads page, so it falls back to the unified downloads listing — * matching the pre-middleware behavior. Resolving the public path on both * server and client avoids the hydration mismatch the path-rewriting * middleware caused. */ export default function DownloadsPage() { const site = useSite(); return ( }> ); }