* fix: preserve cmd+click on tracked domain rows
Tracked-domain rows used <div role="button" onClick={navigate(...)}>,
which prevented standard browser open-in-new-tab behavior (cmd+click,
middle-click, right-click → open). Use TanStack Router's <Link> as a
stretched overlay so the row is a real <a href> while the archive
button stays interactive.
* fix: use <Link> for tab toggles and history navigation
Replace onClick={() => navigate(...)} / setSearchParams patterns with
TanStack Router's <Link> across surfaces that change the URL on click.
<Link> renders a real <a href> and only intercepts plain left-clicks,
so cmd/ctrl+click, middle-click, and right-click → open-in-new-tab
all work natively.
- Audit: history "View" button + Pages/Performance tab toggles.
- Domain overview: Top Keywords / Top Pages tab toggles. The keyword-
only sort fallback now happens in the Link's search updater.
- Backlinks: history items, Backlinks/Domains/Pages tab toggles, and
"Recent searches" back-link. Removes now-unused
navigateToBacklinksHistory / navigateToBacklinksTab helpers.
Keyword research and domain history items, and AI search histories,
are not converted: those pages don't trigger their data fetch from
URL params alone, so a plain link target wouldn't reproduce the
current click behavior without a deeper refactor.
* refactor: unify search-page state around URL-driven fetching
Drive Keyword Research, Brand Lookup, and Prompt Explorer from URL
search params so a search is reproducible from a link alone. With
that, all three history surfaces become <Link>s and cmd+click /
right-click → "open in new tab" work natively.
- Shared SearchHistorySection now takes a renderItemLink slot so
callers wrap history items in a <Link> with the right destination.
- Prompt Explorer: added URL search params (q, models, web, cc, hb)
via promptExplorerSearchSchema; switched the explore mutation to
useQuery keyed on the URL params; addSearch now fires from a
success effect; "Recent searches" back-button is a <Link>.
- Brand Lookup: history items + "Recent searches" back-button are
<Link>s; local form state stays in sync with URL via an effect.
- Keyword Research: form submit still navigates+kicks off a search
for the same-URL re-submit case, but the controller also runs an
URL-driven search trigger (with a dedup ref against the form path).
Direct URLs, cmd+click on history, and browser back/forward all
reproduce the same fetch. "Recent searches" back-button and the
history items are <Link>s; the bespoke resetView path is gone.
Also drops now-unused clearKeywordSearchParams,
navigateToBacklinksHistory/Tab helpers' last consumers, and the
PromptExplorerPage's onQueryChange/onSelectHistoryItem callbacks.
* format
* refactor: replace useMutation with manual state in keyword research
---------
Co-authored-by: Claude <noreply@anthropic.com>
Drop the .toLowerCase() call when building the cache key's targetValue so
it matches whatever detectTarget returns. detectTarget already normalizes
domains to lowercase; keywords are passed through as-is, so lowercasing
again folded otherwise-distinct keyword queries into the same cache
entry.
The callers of startAudit and beginRankCheckRun were forwarding the
incoming billingCustomer object directly into workflow.create params.
At runtime the object can be an EnsuredUserContext with extra fields
beyond BillingCustomerContext, which pollutes the workflow instance's
persisted params payload. Explicitly pick the four required fields so
only the intended data is serialized into the workflow.
INTERNAL_ERROR was showing "An unexpected error occurred" to users and
triggering PostHog error monitoring for expected validation cases
(duplicate domain+country, max configs reached).
Replace DOMAIN_REGEX with URL-based normalization so users can paste
full URLs (e.g. https://example.com/path) into the domain config form
without getting a ZodError. The URL class handles protocol stripping,
hostname extraction, and validation natively.
* feat: group navigation into Keywords, Domain, and AI dropdowns
Reorganize the top nav from 7 flat items into grouped dropdowns:
- Keywords: Keyword Research, Saved Keywords, Rank Tracking
- Domain: Domain Overview, Backlinks, Site Audit
- AI: standalone link
Mobile sidebar uses section headers for the same groupings.
Active dropdown items are highlighted with primary tint.
* fix: use matchSegment lookup instead of hardcoded array indices in nav groups
* landing: convert main CTA to sign-up, add shared marketing layout
- Replace email waitlist form with "Try now" sign-up button as primary CTA
- Add shared _marketing layout route with consistent nav (Pricing, GitHub, Sign in)
- Move email form to bottom of homepage as "Stay in the loop" newsletter
- Update self-host card copy for live managed version
- Reorder self-host card below video on mobile via flex order
- Add /pricing to sitemap and SiteFooter
* change footer and add social card
* landing: move newsletter and footer to shared marketing layout
Move divider line above the newsletter form instead of between
the form and footer links.
The full middleware context object passed as billingCustomer contained
non-serializable properties (Drizzle project result, TanStack internals)
that caused a DataCloneError in Miniflare's workflow binding during
Docker self-hosting.
* fix: default backlinks search to site-wide scope for root URLs
URLs with an explicit protocol but no path (e.g. https://example.com/)
were incorrectly auto-selecting "Exact page" scope. Now only URLs with
an actual path beyond "/" trigger page scope auto-selection.
* test: update backlinks scope tests for root URL behavior change