mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-10-27 13:40:37 +00:00
* fix(frontend): make docs search work on desktop and mobile Docs search on deerflow.tech was broken twice over: no build step ever generated the Pagefind index that Nextra's <Search> loads, and the custom docs header that replaces Nextra's navbar never rendered a search box. - `pnpm build` now runs `search:index` (pagefind 1.5.2, pinned) to write public/_pagefind/ (gitignored). - The landing Header and its mobile sheet take an optional `search` slot; only the docs layout fills it, with localized labels and an aria-label. - Each docs page carries a Pagefind `lang` filter and queries are scoped to it, since the root <html lang> is fixed and would mix en/zh results. - globals.css restyles the results panel with app tokens because Nextra's <Head> variables are unset, lifts it above the mobile sheet, and keeps it tappable; the sheet no longer closes before a result navigates and closes after navigation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(frontend): add DESIGN.md describing the shipped design system Records the existing tokens, typography, components, docs (Nextra) overrides, motion, accessibility and copy rules, plus the landing-only decorative exceptions, so UI changes have something to be checked against. Linked from frontend/AGENTS.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(frontend): address Codex review of docs search - Chinese search: the root <html lang> is fixed, so Pagefind indexed zh pages with the English tokenizer and multi-character words (e.g. 偏好) never matched. scripts/build-search-index.mjs now sets each page's lang from its route before indexing, producing separate en-US and zh-CN indexes, and DocsHtmlLang syncs <html lang> at runtime (resetting Pagefind on a language switch) so queries hit the right index. - Mobile menu: reset the open state when the route changes instead of comparing against the saved path, so Back no longer reopens the sheet. - Shortcut: stop mounting a second <Search> in Nextra's unused mobile menu, which captured Cmd/Ctrl+K and focused an offscreen input at 640-767px. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(frontend): keep guidance within size budget * fix(frontend): address docs search review feedback Keep Nextra results within the mobile sheet scroll boundary and dismiss same-hash selections by pointer or keyboard. Rebuild Pagefind output without stale fragments and replace UI source-string checks with behavioral coverage. --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
85 lines
2.7 KiB
JavaScript
85 lines
2.7 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
// Builds the Pagefind index that Nextra's docs <Search> loads from
|
|
// /_pagefind/. The root layout renders a fixed <html lang>, so the Pagefind
|
|
// CLI would index every page as English and leave Chinese text unsegmented.
|
|
// Rewrite each page's lang from its /{lang}/ route prefix first, so Pagefind
|
|
// writes one correctly tokenized index per language.
|
|
|
|
import { readdir, readFile, rm } from "node:fs/promises";
|
|
import path from "node:path";
|
|
import process from "node:process";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
import * as pagefind from "pagefind";
|
|
|
|
const SITE_DIR = ".next/server/app";
|
|
const OUTPUT_DIR = "public/_pagefind";
|
|
const LOCALE_BY_PREFIX = { en: "en-US", zh: "zh-CN" };
|
|
|
|
async function* htmlFiles(dir) {
|
|
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
const fullPath = path.join(dir, entry.name);
|
|
if (entry.isDirectory()) {
|
|
yield* htmlFiles(fullPath);
|
|
} else if (entry.name.endsWith(".html")) {
|
|
yield fullPath;
|
|
}
|
|
}
|
|
}
|
|
|
|
export function localeForPage(relativePath) {
|
|
const prefix = relativePath.split(/[/\\.]/)[0];
|
|
return LOCALE_BY_PREFIX[prefix];
|
|
}
|
|
|
|
export function withHtmlLang(html, locale) {
|
|
return html.replace(
|
|
/<html\b([^>]*?)\blang="[^"]*"/,
|
|
`<html$1lang="${locale}"`,
|
|
);
|
|
}
|
|
|
|
export async function buildSearchIndex({
|
|
siteDir = SITE_DIR,
|
|
outputDir = OUTPUT_DIR,
|
|
} = {}) {
|
|
try {
|
|
const { index, errors } = await pagefind.createIndex();
|
|
if (!index) throw new Error(errors.join("\n"));
|
|
|
|
let pages = 0;
|
|
for await (const file of htmlFiles(siteDir)) {
|
|
const sourcePath = path.relative(siteDir, file);
|
|
const html = await readFile(file, "utf8");
|
|
const locale = localeForPage(sourcePath);
|
|
const result = await index.addHTMLFile({
|
|
// Pagefind derives the result URL from sourcePath, relative to the site.
|
|
url: `/${sourcePath.split(path.sep).join("/")}`,
|
|
content: locale ? withHtmlLang(html, locale) : html,
|
|
});
|
|
if (result.errors.length) throw new Error(result.errors.join("\n"));
|
|
pages += 1;
|
|
}
|
|
|
|
// Pagefind never deletes older content-hashed fragments. Rebuild this
|
|
// generated directory so removed pages and previous builds do not accumulate.
|
|
await rm(outputDir, { recursive: true, force: true });
|
|
const written = await index.writeFiles({ outputPath: outputDir });
|
|
if (written.errors.length) throw new Error(written.errors.join("\n"));
|
|
console.log(`Pagefind: scanned ${pages} HTML files into ${outputDir}`);
|
|
} finally {
|
|
await pagefind.close();
|
|
}
|
|
}
|
|
|
|
if (
|
|
process.argv[1] &&
|
|
path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)
|
|
) {
|
|
buildSearchIndex().catch((error) => {
|
|
console.error(error);
|
|
process.exitCode = 1;
|
|
});
|
|
}
|