Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions _TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -321,14 +321,6 @@ The bigger problem on this homepage is still total shipped JS and chunk fan-out,

If you want, I can next turn that into a plain-English takeaway for your _TODO.md, like: "fan-out is the main issue; dependency waterfall is present but shallow."

### Search page

One route that is dynamic now but probably does not need to be:

/search

It is currently marked prerender = false in index.astro:2, but the UI is already client-driven. index.astro:8 reads q, and the real search happens through the action in action.ts:12. That means /search can very likely be a static shell page and let the client read window.location.search and call the action. So I would not keep this dynamic unless you specifically want SSR-rendered search results for SEO.

### Tags page

One caution:
Expand All @@ -341,7 +333,6 @@ In src/pages/tags/[tag].astro, the route sets ITEMS_PER_PAGE = 12, then reads th

src/pages/tags/[tag].astro


const currentPage = parseInt(Astro.url.searchParams.get('page') || '1')
It uses that value to:

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"lint:actions": "FORCE_COLOR=1 npx node-actionlint && FORCE_COLOR=1 python3 -m pylint $(find .github/actions -type f -path '*/src/*.py')",
"lint:code": "npx eslint \"@types/**/*.{js,ts}\" \"src/**/*.{js,ts,tsx,astro}\" \"test/**/*.{js,ts,tsx,astro}\"",
"lint:inclusive-language": "npx alex src/content",
"lint:json": "FORCE_COLOR=1 npx prettier \"**/*.json\" --cache --check --ignore-path .gitignore --ignore-path .prettierignore",
"lint:json": "FORCE_COLOR=1 npx prettier \"**/*.json\" '!**/www.*.json' --cache --check --ignore-path .gitignore --ignore-path .prettierignore",
"lint:md": "FORCE_COLOR=1 npx markdownlint-cli2 \"**/*.{md,mdx}\" \"!**/node_modules/**\" \"!**/dist/**\" \"!**/.astro/**\" \"!**/dev-dist/**\" \"!**/__blobstorage__/**\"",
"lint:style": "FORCE_COLOR=1 npx stylelint \"src/**/*.{css,astro}\"",
"lint:tsc:check": "npm run sync && tsc --noEmit -p tsconfig.json --pretty false",
Expand Down
6 changes: 3 additions & 3 deletions src/actions/search/__tests__/responder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ describe('mapUpstashSearchResults', () => {
const raw = [
{
id: 'doc-1',
score: 0.39,
score: 0.009,
content: {
url: '/articles/low-score',
title: 'Low Score',
Expand All @@ -60,7 +60,7 @@ describe('mapUpstashSearchResults', () => {
},
{
id: 'doc-2',
score: 0.4,
score: 0.01,
content: {
url: '/articles/high-enough',
title: 'High Enough',
Expand All @@ -73,7 +73,7 @@ describe('mapUpstashSearchResults', () => {

expect(hits).toHaveLength(1)
expect(hits[0]?.title).toBe('High Enough')
expect(hits[0]?.score).toBe(0.4)
expect(hits[0]?.score).toBe(0.01)
})

it('deduplicates hits that resolve to the same canonical path', () => {
Expand Down
5 changes: 4 additions & 1 deletion src/actions/search/responder.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import type { DefaultSearchResult, SearchHit } from '@actions/search/@types'

const MIN_RELEVANCY_SCORE = 0.4
// Upstash reranking now returns normalized scores where strong matches commonly
// land well below 0.4. Keep a small floor to drop near-zero noise while still
// surfacing legitimate results.
const MIN_RELEVANCY_SCORE = 0.01

const getCanonicalResultPath = (url: string): string => {
try {
Expand Down
25 changes: 25 additions & 0 deletions src/components/Search/SearchResults/client/__tests__/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,4 +296,29 @@ describe('SearchResults web component', () => {
}
)
})

it('seeds the initial query from the location when the rendered input starts empty', async () => {
searchQueryMock.mockResolvedValue({
data: {
hits: [
{
title: 'Astro Search',
url: '/articles/astro-search',
snippet: '...',
},
],
},
})

await runComponentRender({ query: '' }, async ({ element, window }) => {
window.history.replaceState(window.history.state, '', '/search?q=astro')

await (element as unknown as { run: () => Promise<void> }).run()
await flushMicrotasks()

const input = element.querySelector('[data-search-input]') as HTMLInputElement | null
expect(input?.value).toBe('astro')
expect(searchQueryMock).toHaveBeenCalledWith({ q: 'astro', limit: 20 })
})
})
})
33 changes: 32 additions & 1 deletion src/components/Search/SearchResults/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,31 @@ export class SearchResultsElement extends LitElement {
this.micBtn = micBtn
this.clearBtn = clearBtn

this.syncInitialQueryState()
this.updateClearButtonVisibility()
this.updateMicButtonVisibility()
}

private getInitialQuerySeed(): string {
return (this.query ?? '').trim() || this.getQueryFromLocation()
}

private syncInitialQueryState(): void {
const initialQuery = this.getInitialQuerySeed()

if (!initialQuery) {
return
}

if (!this.query?.trim()) {
this.query = initialQuery
}

if (this.input && this.input.value.trim().length === 0) {
this.input.value = initialQuery
}
}

private attachListeners(): void {
if (!this.input || !this.form || !this.micBtn || !this.clearBtn) {
return
Expand Down Expand Up @@ -509,8 +530,18 @@ export class SearchResultsElement extends LitElement {
this.startSpeechRecognition()
}

private async run(queryOverride?: string): Promise<void> {
private resolveRunQuery(queryOverride?: string): string {
const query = (queryOverride ?? this.getQuery()).trim()

if (query.length > 0 || typeof queryOverride === 'string') {
return query
}

return this.getInitialQuerySeed()
}

private async run(queryOverride?: string): Promise<void> {
const query = this.resolveRunQuery(queryOverride)
this.query = query
this.clearError()
this.replaceLocationQuery(query)
Expand Down
6 changes: 2 additions & 4 deletions src/pages/search/index.astro
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
---
export const prerender = false
export const prerender = true

import PageLayout from '@layouts/PageLayout.astro'
import SearchResults from '@components/Search/SearchResults/index.astro'

const query = (Astro.url.searchParams.get('q') ?? '').trim()

const pageTitle = 'Search'
const pageDescription = 'Search the Site'
const path = '/search'
Expand All @@ -19,6 +17,6 @@ const path = '/search'
path={path}
>
<div class="max-w-4xl mx-auto">
<SearchResults query={query} />
<SearchResults />
</div>
</PageLayout>
2 changes: 2 additions & 0 deletions test/unit/helpers/litRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ export const renderInJsdom = async <TModule extends WebComponentModule>(
const { container, component, args, moduleLoader, selector, waitForReady = defaultWaitForReady, assert } = _options

await withJsdomEnvironment(async ({ window }) => {
window.history.replaceState(window.history.state, '', 'http://localhost/')

const module = await moduleLoader()
await module.registerWebComponent(module.registeredName)

Expand Down
12,244 changes: 12,244 additions & 0 deletions www.webstackbuilders.com-home-desktop-20260423T190519.json

Large diffs are not rendered by default.

12,266 changes: 12,266 additions & 0 deletions www.webstackbuilders.com-home-mobile-20260423T184028.json

Large diffs are not rendered by default.

Loading