- Replace fake-site footer with dark-web launch CTA; fix HubChatter inside AppProviders (client crash) - Add /launch, DEPLOY.md, onions:list|status|health, list-onion-urls, install-systemd, onion-status - start.sh CYBERLUX_PREPARE_ONLY; copy pass on satellite pages; Navbar launch link - README: operator cheat sheet, phone Tor note, systemd via install-systemd.sh Made-with: Cursor
218 lines
8.9 KiB
TypeScript
218 lines
8.9 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import { FormEvent, useMemo, useState } from "react";
|
|
import {
|
|
CYBERLUX_SEARCH_INDEX_SIZE,
|
|
searchCyberluxIndex,
|
|
searchZoneLabel,
|
|
VOID_LEXICON_WORDS,
|
|
type CyberluxSearchDoc,
|
|
} from "@/lib/cyberluxSearchIndex";
|
|
|
|
const LEXICON_FOG_SAMPLE = VOID_LEXICON_WORDS.slice(0, 96);
|
|
|
|
function keywordSample(doc: CyberluxSearchDoc, limit = 20): string[] {
|
|
const seen = new Set<string>();
|
|
const out: string[] = [];
|
|
for (const w of doc.keywords.split(/\s+/)) {
|
|
const t = w.trim().toLowerCase();
|
|
if (t.length < 3 || seen.has(t)) continue;
|
|
seen.add(t);
|
|
out.push(t);
|
|
if (out.length >= limit) break;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
/** v3-style hostnames — one per deployment node; paths are in-app routes. */
|
|
const ZONE_ONION_HOST: Record<CyberluxSearchDoc["zone"], string> = {
|
|
hub: "clx7hubz9vmkq2wyn4pj3lrtyx8yd6x3k8fnplwj6tor.onion",
|
|
forum: "voidagg8n3kx2wz7m4pj6lrtyx8yd5xq9kfvnplwjtor.onion",
|
|
exchange: "clxexch4kz8vm7nq2wyn9pj3lrtyx8yd2x6kfnplwj.onion",
|
|
wiki: "clxwiki3jq8vmkq2wyn4pj6lrtyx8yd7x2k9fmwnplwj.onion",
|
|
syndicate: "clxsyn7kq2wz8vm4n9pj3lrtyx8yd4x3k6fnplwjtor.onion",
|
|
account: "clxacct9vmkq2wz8n4pj6lrtyx8yd1x7k3fnplwjtor.onion",
|
|
};
|
|
|
|
function onionDisplayLine(doc: CyberluxSearchDoc): string {
|
|
const host = ZONE_ONION_HOST[doc.zone];
|
|
const p = doc.path === "/" ? "" : doc.path;
|
|
return `http://${host}${p}`;
|
|
}
|
|
|
|
export default function VoidCrawlerSearchPage() {
|
|
const [query, setQuery] = useState("");
|
|
const [submitted, setSubmitted] = useState("");
|
|
const [busy, setBusy] = useState(false);
|
|
|
|
const results = useMemo(() => {
|
|
if (!submitted.trim()) return [];
|
|
return searchCyberluxIndex(submitted);
|
|
}, [submitted]);
|
|
|
|
const onSubmit = (e: FormEvent) => {
|
|
e.preventDefault();
|
|
const q = query.trim();
|
|
if (!q) return;
|
|
setBusy(true);
|
|
window.setTimeout(() => {
|
|
setSubmitted(q);
|
|
setBusy(false);
|
|
}, 320);
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen bg-[#030806] text-[#c8e6d0]">
|
|
<div
|
|
className="pointer-events-none fixed inset-0 z-0 opacity-[0.12]"
|
|
style={{
|
|
backgroundImage:
|
|
"repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(0,255,65,0.06) 3px)",
|
|
}}
|
|
/>
|
|
|
|
<header className="relative z-10 border-b border-[#00ff4122] bg-[#020504]/95 px-4 py-10 backdrop-blur-sm">
|
|
<div className="mx-auto max-w-3xl text-center">
|
|
<p className="font-mono text-[10px] uppercase tracking-[0.45em] text-[#00ff41]/55">closed corpus · internal index</p>
|
|
<h1 className="mt-3 font-mono text-4xl font-black tracking-tight text-white md:text-5xl">
|
|
VOID<span className="text-[#00ff41]">CRAWLER</span>
|
|
</h1>
|
|
<p className="mx-auto mt-4 max-w-xl font-mono text-xs leading-relaxed text-[#6b9b78]">
|
|
Crawls <strong className="text-[#9bffb8]">{CYBERLUX_SEARCH_INDEX_SIZE}</strong> signed routes on{" "}
|
|
<strong className="text-[#9bffb8]">this</strong> ring only — hub, forum, exchange, wiki, syndicate shells, and account surfaces.
|
|
Foreign onions and clearnet bridges are deliberately excluded from the corpus.
|
|
</p>
|
|
<div className="mx-auto mt-8 max-w-4xl border border-[#00ff4120] bg-[#040a06]/80 px-3 py-3 text-left">
|
|
<p className="mb-2 font-mono text-[9px] uppercase tracking-[0.4em] text-[#00ff41]/40">corpus lexicon fog · sample tokens</p>
|
|
<div className="flex flex-wrap justify-center gap-1">
|
|
{LEXICON_FOG_SAMPLE.map((w) => (
|
|
<span
|
|
key={w}
|
|
className="rounded border border-[#00ff41]/12 px-1 py-0.5 font-mono text-[8px] lowercase text-[#5a8f6a]"
|
|
>
|
|
{w}
|
|
</span>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<main className="relative z-10 mx-auto max-w-3xl px-4 pb-32 pt-10">
|
|
<form onSubmit={onSubmit} className="relative">
|
|
<label htmlFor="void-query" className="sr-only">
|
|
Search CyberLux sites
|
|
</label>
|
|
<input
|
|
id="void-query"
|
|
value={query}
|
|
onChange={(e) => setQuery(e.target.value)}
|
|
placeholder="e.g. forum barter vault checkout atlas…"
|
|
className="w-full border-2 border-[#00ff4133] bg-black/80 py-4 pl-4 pr-28 font-mono text-sm text-[#00ff41] placeholder:text-[#00ff41]/25 focus:border-[#00ff41] focus:outline-none"
|
|
/>
|
|
<button
|
|
type="submit"
|
|
disabled={busy || !query.trim()}
|
|
className="absolute right-1 top-1 bottom-1 bg-[#00ff41] px-5 font-mono text-xs font-black uppercase text-black hover:bg-[#00cc33] disabled:opacity-40"
|
|
>
|
|
{busy ? "…" : "Crawl"}
|
|
</button>
|
|
</form>
|
|
|
|
<p className="mt-4 font-mono text-[10px] uppercase tracking-widest text-[#4a6b55]">
|
|
Tip: multi-word queries match <span className="text-[#6b9b78]">all</span> tokens (AND).
|
|
</p>
|
|
|
|
<div className="mt-10 space-y-6">
|
|
{busy ? (
|
|
<p className="animate-pulse text-center font-mono text-xs uppercase tracking-[0.3em] text-[#00ff41]/45">
|
|
Walking local routes…
|
|
</p>
|
|
) : null}
|
|
|
|
{!busy && submitted && (
|
|
<p className="font-mono text-xs text-[#6b9b78]">
|
|
<span className="text-[#00ff41]">{results.length}</span> hit{results.length === 1 ? "" : "s"} for{" "}
|
|
<span className="text-[#c8e6d0]">{submitted}</span>
|
|
</p>
|
|
)}
|
|
|
|
{results.map((doc) => (
|
|
<article
|
|
key={doc.path}
|
|
className="border-l-4 border-[#00ff4140] bg-[#050c08]/90 py-4 pl-5 pr-4 shadow-[inset_0_0_0_1px_rgba(0,255,65,0.06)]"
|
|
>
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
<span className="rounded border border-[#00ff4133] px-2 py-0.5 font-mono text-[9px] uppercase tracking-wider text-[#7af598]">
|
|
{searchZoneLabel(doc.zone)}
|
|
</span>
|
|
</div>
|
|
<h2 className="mt-2 font-mono text-lg font-bold">
|
|
<Link href={doc.path} className="text-[#7af598] hover:text-[#b4ffcc] hover:underline">
|
|
{doc.title}
|
|
</Link>
|
|
</h2>
|
|
<p className="mt-1 break-all font-mono text-[10px] leading-relaxed text-[#00ff41]/35">
|
|
{onionDisplayLine(doc)}
|
|
</p>
|
|
<p className="mt-2 font-mono text-xs leading-relaxed text-[#8eb89a]/95">{doc.description}</p>
|
|
<div className="mt-3 flex flex-wrap gap-1">
|
|
{keywordSample(doc).map((kw) => (
|
|
<span
|
|
key={kw}
|
|
className="rounded bg-[#00ff41]/08 px-1.5 py-0.5 font-mono text-[8px] lowercase text-[#6b9b78]"
|
|
>
|
|
{kw}
|
|
</span>
|
|
))}
|
|
</div>
|
|
<p className="mt-2 font-mono text-[10px] text-[#4a6b55]">
|
|
Route:{" "}
|
|
<Link href={doc.path} className="text-[#6b9b78] hover:text-[#00ff41]">
|
|
{doc.path}
|
|
</Link>
|
|
</p>
|
|
</article>
|
|
))}
|
|
|
|
{!busy && submitted && results.length === 0 ? (
|
|
<div className="border-2 border-dashed border-[#00ff4122] py-16 text-center">
|
|
<p className="font-mono text-sm text-[#6b9b78]">No in-app routes match that query.</p>
|
|
<p className="mt-3 font-mono text-[11px] text-[#4a6b55]">
|
|
Try <button type="button" className="text-[#00ff41] underline" onClick={() => setQuery("forum")}>forum</button>,{" "}
|
|
<button type="button" className="text-[#00ff41] underline" onClick={() => setQuery("checkout")}>
|
|
checkout
|
|
</button>
|
|
, or <button type="button" className="text-[#00ff41] underline" onClick={() => setQuery("atlas")}>atlas</button>.
|
|
</p>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
|
|
<nav className="mt-16 flex flex-wrap justify-center gap-4 border-t border-[#00ff4118] pt-10 font-mono text-[11px]">
|
|
<Link href="/" className="text-[#6b9b78] hover:text-[#00ff41]">
|
|
← Hub
|
|
</Link>
|
|
<Link href="/forum" className="text-[#6b9b78] hover:text-[#00ff41]">
|
|
Forum
|
|
</Link>
|
|
<Link href="/hidden-wiki" className="text-[#6b9b78] hover:text-[#00ff41]">
|
|
Wiki
|
|
</Link>
|
|
<Link href="/darknet-atlas" className="text-[#6b9b78] hover:text-[#00ff41]">
|
|
Atlas
|
|
</Link>
|
|
<Link href="/links" className="text-[#6b9b78] hover:text-[#00ff41]">
|
|
Link garden
|
|
</Link>
|
|
</nav>
|
|
|
|
<p className="mt-10 text-center font-mono text-[9px] uppercase tracking-[0.25em] text-[#00ff41]/20">
|
|
void crawler v1 · {CYBERLUX_SEARCH_INDEX_SIZE} documents · internal index only
|
|
</p>
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|