Skip to content

chore(content): search code blocks and preview results as rendered pages - #1821

Merged
dinwwwh merged 5 commits into
mainfrom
claude/blume-search-code-prerender-517fd3
Aug 8, 2026
Merged

chore(content): search code blocks and preview results as rendered pages#1821
dinwwwh merged 5 commits into
mainfrom
claude/blume-search-code-prerender-517fd3

Conversation

@dinwwwh

@dinwwwh dinwwwh commented Aug 8, 2026

Copy link
Copy Markdown
Member

Blume strips fenced code before indexing, so searching for an API the docs demonstrate but never name in prose returned nothing: createSafeClient matched 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. createSafeClient went from zero results to three, and queries like os.middleware and interceptors reach 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 eslint and tsc --noEmit pass, and blume build succeeds 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 after blume-tabs upgrades, cache eviction rebuilds a page to the same position, and the hidden-pane paths make zero fetches. No console errors.

dinwwwh added 5 commits August 8, 2026 19:59
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.
@vercel

vercel Bot commented Aug 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
orpc Ready Ready Preview Aug 8, 2026 1:56pm

@pkg-pr-new

pkg-pr-new Bot commented Aug 8, 2026

Copy link
Copy Markdown
More templates

@orpc/ai-sdk

npm i https://pkg.pr.new/@orpc/ai-sdk@1821

@orpc/arktype

npm i https://pkg.pr.new/@orpc/arktype@1821

@orpc/bun

npm i https://pkg.pr.new/@orpc/bun@1821

@orpc/client

npm i https://pkg.pr.new/@orpc/client@1821

@orpc/cloudflare

npm i https://pkg.pr.new/@orpc/cloudflare@1821

@orpc/contract

npm i https://pkg.pr.new/@orpc/contract@1821

@orpc/experimental-effect

npm i https://pkg.pr.new/@orpc/experimental-effect@1821

@orpc/evlog

npm i https://pkg.pr.new/@orpc/evlog@1821

@orpc/hibernation

npm i https://pkg.pr.new/@orpc/hibernation@1821

@orpc/json-schema

npm i https://pkg.pr.new/@orpc/json-schema@1821

@orpc/nest

npm i https://pkg.pr.new/@orpc/nest@1821

@orpc/next

npm i https://pkg.pr.new/@orpc/next@1821

@orpc/openapi

npm i https://pkg.pr.new/@orpc/openapi@1821

@orpc/opentelemetry

npm i https://pkg.pr.new/@orpc/opentelemetry@1821

@orpc/pinia-colada

npm i https://pkg.pr.new/@orpc/pinia-colada@1821

@orpc/pino

npm i https://pkg.pr.new/@orpc/pino@1821

@orpc/publisher

npm i https://pkg.pr.new/@orpc/publisher@1821

@orpc/ratelimit

npm i https://pkg.pr.new/@orpc/ratelimit@1821

@orpc/server

npm i https://pkg.pr.new/@orpc/server@1821

@orpc/shared

npm i https://pkg.pr.new/@orpc/shared@1821

@orpc/swr

npm i https://pkg.pr.new/@orpc/swr@1821

@orpc/tanstack-query

npm i https://pkg.pr.new/@orpc/tanstack-query@1821

@orpc/trpc

npm i https://pkg.pr.new/@orpc/trpc@1821

@orpc/valibot

npm i https://pkg.pr.new/@orpc/valibot@1821

@orpc/zod

npm i https://pkg.pr.new/@orpc/zod@1821

commit: 027a7f6

@codecov

codecov Bot commented Aug 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@codspeed-hq

codspeed-hq Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 25 untouched benchmarks


Comparing claude/blume-search-code-prerender-517fd3 (027a7f6) with main (99ce3de)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (18fcc59) during the generation of this report, so 99ce3de was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ 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), sibling search.json/raw-markdown.json both keyed by route.path, the .prose wrapper, and the zoom scroll math).

Reviewed changes

  • blume.config.ts — registers the search-code-index integration: a Vite plugin that folds fenced code back into the generated search index, plus a page-injectScript for the new preview client.
  • search/code-index.ts — an enforce: 'pre' transform on /.blume/src/generated/search.json that appends each page's fenced code (notations/Twoslash stripped, lines deduped per page) to doc.content, sourcing the fences from the sibling raw-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 .prose zoomed wrapper, marks the query, opens the containing tab, measures content-visibility/contain-intrinsic-size offsets 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.ts transform: consider FENCE/notation regexes are run over every page at build — fine, but the extracted content append makes createSafeClient-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.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏


const raw = JSON.parse(
await readFile(join(dirname(id), RAW_MARKDOWN), 'utf8'),
) as Record<string, { mdx?: string }>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@dinwwwh dinwwwh changed the title feat(content): search code blocks and preview results as rendered pages chore(content): search code blocks and preview results as rendered pages Aug 8, 2026
@dinwwwh
dinwwwh merged commit 147da9c into main Aug 8, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant