chore(content): search code blocks and preview results as rendered pages - #1821
Conversation
Blume strips fenced code before indexing, so queries for an API the docs demonstrate but never name in prose returned nothing. A Vite transform folds each page's fences back into the generated index, and the search preview pane now shows the matched block lifted from the page's own build-time Shiki output.
The preview pane showed the matched code block alone. It now shows the passage around the match — heading, prose, callouts, tables, lists — cloned from the page's own HTML into a `.prose` wrapper and scaled down, so a result reads like a small view of the page rather than a stripped snippet. A match inside a tab panel opens that tab, and the sponsor slot is left behind.
Selecting a result showed Blume's text preview first and replaced it a moment later, so moving through results flickered. A page already loaded now renders in the same tick the selection changed, and the rows just past the selection are fetched while the reader reads the current one, so walking a result list is instant after the first row.
A search result previewed a passage around the match. It now renders the page in full and opens it scrolled to the section the query matched, so a result reads as a small view of the real page rather than a clipping of one. The page is built once per query and kept, and its off-screen blocks are handed to the browser to skip after being measured, so showing a page again costs an append rather than a full layout.
No behavior change. The page cache and its synchronous view were two maps kept in step by hand and are now one entry type; the render bookkeeping dropped the two fields that only fed the scroll measurement; and the wiring takes its elements as arguments rather than asserting them non-null at every use. The index plugin rewrites documents in place instead of copying each one.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
More templates
@orpc/ai-sdk
@orpc/arktype
@orpc/bun
@orpc/client
@orpc/cloudflare
@orpc/contract
@orpc/experimental-effect
@orpc/evlog
@orpc/hibernation
@orpc/json-schema
@orpc/nest
@orpc/next
@orpc/openapi
@orpc/opentelemetry
@orpc/pinia-colada
@orpc/pino
@orpc/publisher
@orpc/ratelimit
@orpc/server
@orpc/shared
@orpc/swr
@orpc/tanstack-query
@orpc/trpc
@orpc/valibot
@orpc/zod
commit: |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
ℹ️ Two minor items inline / below — nothing blocking. The PR is well-built and its load-bearing assumptions all check out against Blume v1.3.1's source (DOM hooks,
blume-tabs.activate(index, sync, updateHash), siblingsearch.json/raw-markdown.jsonboth keyed byroute.path, the.prosewrapper, and thezoomscroll math).
Reviewed changes
blume.config.ts— registers thesearch-code-indexintegration: a Vite plugin that folds fenced code back into the generated search index, plus a page-injectScriptfor the new preview client.search/code-index.ts— anenforce: 'pre'transform on/.blume/src/generated/search.jsonthat appends each page's fenced code (notations/Twoslash stripped, lines deduped per page) todoc.content, sourcing the fences from the siblingraw-markdown.json.search/page-preview.ts— a MutationObserver-driven client that replaces Blume's text preview with the real rendered page: fetches + clones the<article>, wraps in a.prosezoomed wrapper, marks the query, opens the containing tab, measurescontent-visibility/contain-intrinsic-sizeoffsets once, and scrolls to the matched section, with page/section caches and idle prefetch; it degrades to Blume's own preview on any miss.
I verified the key contracts directly in node_modules/blume@1.3.1: the data-blume-search-* hooks and bg-muted/h3 + div preview shape (Search.astro), BlumeTabs.activate(index, sync, updateHash) (Tabs.astro), the sibling generation of search.json and raw-markdown.json keyed by route.path (generate.ts/eject.ts), and the .prose article styling (RootLayout.astro). Nothing there contradicts the PR.
ℹ️ Data-contract mismatch: web index enriched from agent-audience markdown
The transform reads raw-markdown.json as its code source, but that file is built with agent visibility (applyAgentVisibility in blume/src/ai/markdown.ts), while the search index's content is built with web visibility (applyAudienceVisibility(source, "web")). For a page that gates code behind <Visibility audience="…">, agents-only code would be folded into the public web index it otherwise hides, and web-only code would be missed. oRPC's docs use no <Visibility> wrapping today, so this is inert for this repo — flagging it so it's a conscious contract rather than a silent one if gated content is ever added.
ℹ️ Nitpicks
code-index.tstransform: considerFENCE/notation regexes are run over every page at build — fine, but the extracted content append makescreateSafeClient-class identifiers searchable at the cost of a larger client index (~411 KB); the dedup holds it down, just noting the tradeoff is intentional.
Technical details
# Unguarded transform read can hard-fail the docs build
## Affected sites
- apps/content/search/code-index.ts:107-109 — `JSON.parse(await readFile(join(dirname(id), RAW_MARKDOWN), 'utf8'))`
## Required outcome
- A missing or malformed `raw-markdown.json` should skip enrichment (transform returns null), not reject and fail the whole site build, matching the PR's stated "fall back gracefully" philosophy (the client pane already degrades to Blume's text preview).
## Suggested approach (optional)
- Wrap the read + parse in a `try/catch` and `return null` on failure.DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
|
|
||
| const raw = JSON.parse( | ||
| await readFile(join(dirname(id), RAW_MARKDOWN), 'utf8'), | ||
| ) as Record<string, { mdx?: string }> |
There was a problem hiding this comment.
This readFile + JSON.parse is unguarded: if Blume ever renames/moves raw-markdown.json (or it's mid-write), the transform rejects and the whole docs build fails — where the client side of this same PR deliberately degrades to Blume's own preview. Considering the stated fall-back philosophy, wrapping the read+parse in a try/catch and returning null (skip enrichment) would keep the build resilient. Minor robustness nit, not blocking.

Blume strips fenced code before indexing, so searching for an API the docs demonstrate but never name in prose returned nothing:
createSafeClientmatched no page at all. This folds each page's code back into the search index, and replaces the dialog's plain-text preview with the page itself, opened at the section the query matched.Both are customizations layered on Blume rather than a fork of its search dialog, so upgrades stay drop-in. Every DOM detail borrowed from Blume is checked before use; if a future version moves one, the pane falls back to Blume's own text preview.
Search
Code is now indexed.
createSafeClientwent from zero results to three, and queries likeos.middlewareandinterceptorsreach the pages that demonstrate them. Twoslash directives and Shiki notations are stripped, and repeated lines are deduplicated per page, which holds the index at 411 KB against 250 KB before.Preview
A result now shows the real page, scaled to preview size, scrolled to the matched section with the query highlighted. Code keeps its build-time Shiki highlighting, and tables, callouts and images render as they do on the page, with no renderer shipped to the browser. A match inside a tab panel opens that tab. Sponsor slots stay out of previews.
Performance
Selecting a result no longer flashes the placeholder preview before the real one: a loaded page renders in the same tick the selection changed, and the rows just past the selection are fetched and built while the reader reads the current one. Showing a page again costs 8-11 ms rather than a 45-60 ms full-page layout, because its off-screen blocks are measured once and then skipped. Below
md, or with the preview toggled off, no page is fetched at all.Testing
pnpm eslintandtsc --noEmitpass, andblume buildsucceeds with the built index carrying the folded-in code. Behavior was verified against the dev server: landing sections and scroll offsets are identical on first and repeat views, tab reveal works both before and afterblume-tabsupgrades, cache eviction rebuilds a page to the same position, and the hidden-pane paths make zero fetches. No console errors.