diff --git a/package.json b/package.json index fef64f1..ab65b92 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "names-poweredbyotm", - "version": "0.3.1", + "version": "0.4.0", "private": true, "scripts": { "dev": "next dev --port 3000", diff --git a/src/app/globals.css b/src/app/globals.css index 44b7f53..a550c8f 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -292,6 +292,44 @@ h2 { border-color: #4a3f1c; } +.results-bar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + flex-wrap: wrap; + margin-top: 16px; + font-size: 13px; + color: var(--muted); +} + +.results-bar + .results { + margin-top: 8px; +} + +.results-toggle { + display: inline-flex; + align-items: center; + gap: 6px; + cursor: pointer; +} + +.results-toggle input { + width: auto; +} + +.ai-badge { + display: inline-block; + padding: 1px 6px; + border-radius: 999px; + background: #1f2a3f; + color: var(--accent); + font-size: 10px; + font-weight: 700; + letter-spacing: 0.08em; + vertical-align: middle; +} + /* ---------- wishlist ---------- */ .wishlist { diff --git a/src/components/Finder.tsx b/src/components/Finder.tsx index d30bca4..344570d 100644 --- a/src/components/Finder.tsx +++ b/src/components/Finder.tsx @@ -53,6 +53,12 @@ export default function Finder() { const [platformAi, setPlatformAi] = useState(false); const [usePlatformAi, setUsePlatformAi] = useState(true); const [signinNote, setSigninNote] = useState(null); + // How many names the last run got from AI vs the built-in generator — shown + // as a one-line summary so it's obvious whether AI actually contributed + // (issue #3). null until a run completes. + const [aiCount, setAiCount] = useState(null); + // Hide names whose every checked extension is taken (issue #3). Persisted. + const [hideTaken, setHideTaken] = useState(false); const [saved, setSaved] = useState([]); const [savedLoaded, setSavedLoaded] = useState(false); const [rechecking, setRechecking] = useState(false); @@ -165,8 +171,22 @@ export default function Finder() { // can't change underneath us mid-session. useEffect(() => { setAi(loadAiSettings()); + try { + setHideTaken(window.localStorage.getItem("names.hideTaken") === "1"); + } catch { + /* private mode */ + } }, []); + const toggleHideTaken = (v: boolean) => { + setHideTaken(v); + try { + window.localStorage.setItem("names.hideTaken", v ? "1" : "0"); + } catch { + /* private mode */ + } + }; + useEffect(() => { let alive = true; fetch("/api/auth/session", { cache: "no-store" }) @@ -287,6 +307,7 @@ export default function Finder() { // here is never fatal — the built-in list is already computed. const wantsOwnKey = useAi && ai; const wantsPlatform = !ai && platformAi && account && usePlatformAi; + let gotAi = 0; if (wantsOwnKey || wantsPlatform) { try { const aiNames = wantsOwnKey @@ -298,6 +319,7 @@ export default function Finder() { .filter((n) => !seen.has(n)) .map((name) => ({ name, strategy: "ai" as const, score: scoreName(name, parsedKeywords) })); list = [...extra, ...list].slice(0, NAME_COUNT + extra.length); + gotAi = extra.length; } catch (err) { setError( `AI names unavailable (${err instanceof Error ? err.message : "unknown error"}). Showing built-in suggestions.`, @@ -307,6 +329,7 @@ export default function Finder() { if (runId.current !== myRun) return; setIdeas(list); + setAiCount(wantsOwnKey || wantsPlatform ? gotAi : null); setBusy(false); await lookup(domainsFor(list, tlds), myRun); }, @@ -325,6 +348,18 @@ export default function Finder() { const showsUnverifiable = tlds.some((t) => UNVERIFIABLE_TLDS.includes(t)); + /** A name is "all taken" once every domain we checked for it resolved taken. + * Still-checking or unknown names stay visible — hiding is for certainty. */ + const isAllTaken = useCallback( + (idea: Idea): boolean => { + const ds = domainsFor([idea], tlds); + return ds.length > 0 && ds.every((d) => checks[d]?.status === "taken"); + }, + [checks, domainsFor, tlds], + ); + const visibleIdeas = hideTaken ? ideas.filter((i) => !isAllTaken(i)) : ideas; + const hiddenCount = ideas.length - visibleIdeas.length; + return ( <>
@@ -489,20 +524,40 @@ export default function Finder() { {busy ? "Working…" : "Describe the business above and press Find names."}

) : ( -
- {ideas.map((idea) => ( - - ))} -
+ <> +
+ + {aiCount != null ? ( + <> + AI {aiCount} {aiCount === 1 ? "name" : "names"} ·{" "} + {ideas.length - aiCount} built-in + + ) : ( + <>{ideas.length} names + )} + {hideTaken && hiddenCount > 0 ? <> · {hiddenCount} taken hidden : null} + + +
+
+ {visibleIdeas.map((idea) => ( + + ))} +
+ )} ); @@ -516,6 +571,7 @@ function NameCard({ registrar, saved, onToggleSave, + hideTaken, }: { idea: Idea; tlds: string[]; @@ -524,6 +580,7 @@ function NameCard({ registrar: string; saved: boolean; onToggleSave: (name: string, domains: string[]) => void; + hideTaken: boolean; }) { const isHack = Boolean(idea.hackTld && idea.hackStem); const domains = isHack ? [`${idea.hackStem}.${idea.hackTld}`] : tlds.map((t) => `${idea.name}.${t}`); @@ -563,6 +620,7 @@ function NameCard({ const isPending = pending.has(domain) && !check; const status: Availability | "checking" = isPending ? "checking" : (check?.status ?? "unknown"); const free = status === "available" || status === "unverified-available"; + if (hideTaken && status === "taken") return null; return (
@@ -584,7 +642,9 @@ function NameCard({ ); })} - {strategyLabel(idea.strategy)} + + {idea.strategy === "ai" ? AI : null} {strategyLabel(idea.strategy)} +
); } @@ -687,7 +747,7 @@ function strategyLabel(strategy: Idea["strategy"]): string { case "hack": return "domain hack"; case "ai": - return "your AI key"; + return "generated"; default: return ""; } diff --git a/src/lib/version.ts b/src/lib/version.ts index 4e8ae7b..8cbd9e8 100644 --- a/src/lib/version.ts +++ b/src/lib/version.ts @@ -2,7 +2,7 @@ // /changelog. Bump VERSION and prepend an entry when shipping a user-facing // change. Same convention as rpo. -export const VERSION = "0.3.1"; +export const VERSION = "0.4.0"; export interface ChangelogEntry { version: string; @@ -11,6 +11,14 @@ export interface ChangelogEntry { } export const CHANGELOG: ChangelogEntry[] = [ + { + version: "0.4.0", + date: "2026-08-16", + changes: [ + "Hide taken: a toggle above the results hides names whose every checked extension is taken, and taken rows inside mixed cards. Remembered between visits.", + "You can now see what AI contributed: AI-generated cards carry an AI badge and a summary line above the results reads e.g. 'AI 9 names · 24 built-in' (or 'AI 0 names' when the AI call failed — the error explains why).", + ], + }, { version: "0.3.1", date: "2026-08-16",