@farming-labs/docs 0.2.105 → 0.2.107

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -40,6 +40,7 @@ const DEFAULT_COMPRESSION_BASE_URL = "https://api.farming-labs.dev";
40
40
  const DEFAULT_COMPRESSION_MODEL = "docs-cloud-compress-v1";
41
41
  const DEFAULT_COMPRESSION_AGGRESSIVENESS = .3;
42
42
  const DEFAULT_DOCS_CLOUD_API_KEY_ENV = "DOCS_CLOUD_API_KEY";
43
+ const MAX_COMPRESSION_INPUT_CHARACTERS = 2e5;
43
44
  const INDEX_PAGE_BASENAMES = new Set([
44
45
  "index",
45
46
  "page",
@@ -76,6 +77,10 @@ function parseAgentCompactArgs(argv) {
76
77
  parsed.dryRun = true;
77
78
  continue;
78
79
  }
80
+ if (arg === "--check") {
81
+ parsed.check = true;
82
+ continue;
83
+ }
79
84
  if (arg === "--changed") {
80
85
  parsed.changed = true;
81
86
  continue;
@@ -414,7 +419,7 @@ function inspectAgentCompactionState(page, target, defaults) {
414
419
  tokenBudget
415
420
  };
416
421
  }
417
- function protectForCompression(input) {
422
+ function protectForCompression(input, protectInlineCode = true) {
418
423
  const segments = [];
419
424
  const stash = (value) => {
420
425
  const token = `__DOCS_SAFE_${segments.length}__`;
@@ -424,14 +429,21 @@ function protectForCompression(input) {
424
429
  let result = input;
425
430
  result = result.replace(/```[\s\S]*?```/g, stash);
426
431
  result = result.replace(/\[[^\]]+\]\([^)]+\)/g, stash);
427
- result = result.replace(/`[^`\n]+`/g, stash);
432
+ if (protectInlineCode) result = result.replace(/`[^`\n]+`/g, stash);
428
433
  result = result.replace(/^(URL|Description|Related):[^\n]*$/gm, stash);
429
434
  result = result.replace(/https?:\/\/[^\s)]+/g, stash);
430
435
  for (let index = 0; index < segments.length; index += 1) result = result.replace(`__DOCS_SAFE_${index}__`, `<docs_safe>${segments[index]}</docs_safe>`);
431
436
  return result;
432
437
  }
438
+ function buildCompressionInput(input) {
439
+ const fullyProtected = protectForCompression(input);
440
+ if (fullyProtected.length <= MAX_COMPRESSION_INPUT_CHARACTERS) return fullyProtected;
441
+ const withoutInlineCodeProtection = protectForCompression(input, false);
442
+ if (withoutInlineCodeProtection.length <= MAX_COMPRESSION_INPUT_CHARACTERS) return withoutInlineCodeProtection;
443
+ throw new Error(`Docs Cloud compression input is ${withoutInlineCodeProtection.length} characters after adaptive protection; the maximum is ${MAX_COMPRESSION_INPUT_CHARACTERS}. Split or shorten the source page before compacting it.`);
444
+ }
433
445
  function sanitizeCompressedOutput(output) {
434
- return output.replace(/<\/?docs_safe>/g, "");
446
+ return output.replace(/<\/?docs_safe>/g, "").replace(/[ \t]+$/gm, "");
435
447
  }
436
448
  async function compressDocument(input, options) {
437
449
  const apiKey = resolveCompressionApiKey(options.apiKey, options.apiKeyEnv);
@@ -440,7 +452,7 @@ async function compressDocument(input, options) {
440
452
  if (aggressiveness < 0 || aggressiveness > 1) throw new Error("Aggressiveness must be between 0.0 and 1.0.");
441
453
  const payload = {
442
454
  model: options.model ?? DEFAULT_COMPRESSION_MODEL,
443
- input: protectForCompression(input),
455
+ input: buildCompressionInput(input),
444
456
  compression_settings: {
445
457
  aggressiveness,
446
458
  ...options.maxOutputTokens !== void 0 ? { max_output_tokens: options.maxOutputTokens } : {},
@@ -515,7 +527,8 @@ async function compactAgentDocs(options = {}) {
515
527
  if (resolvedOptions.all && resolvedOptions.pages && resolvedOptions.pages.length > 0) throw new Error("Use either --all or specific page arguments, not both.");
516
528
  if (resolvedOptions.includeMissing && !resolvedOptions.stale) throw new Error("Use --include-missing together with --stale.");
517
529
  const requestedPages = resolvedOptions.pages?.filter((value) => value.trim().length > 0) ?? [];
518
- if (!resolvedOptions.all && requestedPages.length === 0 && !resolvedOptions.stale && !resolvedOptions.changed) throw new Error("Pass --all, --changed, --stale, or at least one docs page slug/path to compact.");
530
+ if (resolvedOptions.check && (resolvedOptions.all || resolvedOptions.stale || resolvedOptions.changed || resolvedOptions.includeMissing || resolvedOptions.dryRun || requestedPages.length > 0)) throw new Error("Use --check by itself; it inspects every compactable docs page without writing.");
531
+ if (!resolvedOptions.all && requestedPages.length === 0 && !resolvedOptions.stale && !resolvedOptions.changed && !resolvedOptions.check) throw new Error("Pass --all, --changed, --stale, or at least one docs page slug/path to compact.");
519
532
  const pages = await createFilesystemDocsMcpSource({
520
533
  rootDir,
521
534
  entry,
@@ -523,7 +536,7 @@ async function compactAgentDocs(options = {}) {
523
536
  siteTitle
524
537
  }).getPages();
525
538
  if (pages.length === 0) throw new Error(`No docs content was found under ${contentDir}.`);
526
- const selectedPages = resolveSelectedPages(pages, scanDocsPageTargets(rootDir, contentDir, entry), entry, requestedPages, resolvedOptions.all === true || resolvedOptions.stale === true && requestedPages.length === 0 || resolvedOptions.changed === true && requestedPages.length === 0);
539
+ const selectedPages = resolveSelectedPages(pages, scanDocsPageTargets(rootDir, contentDir, entry), entry, requestedPages, resolvedOptions.all === true || resolvedOptions.check === true || resolvedOptions.stale === true && requestedPages.length === 0 || resolvedOptions.changed === true && requestedPages.length === 0);
527
540
  const filteredPages = resolvedOptions.changed ? filterChangedPages(rootDir, contentDir, selectedPages) : selectedPages;
528
541
  if (filteredPages.length === 0) {
529
542
  if (resolvedOptions.changed) {
@@ -532,6 +545,27 @@ async function compactAgentDocs(options = {}) {
532
545
  }
533
546
  throw new Error("No compactable docs pages matched the request.");
534
547
  }
548
+ if (resolvedOptions.check) {
549
+ let fresh = 0;
550
+ let stale = 0;
551
+ let modified = 0;
552
+ let unknown = 0;
553
+ let requiredMissing = 0;
554
+ let optionalMissing = 0;
555
+ for (const { page, target } of filteredPages) {
556
+ const state = inspectAgentCompactionState(page, target, resolvedOptions);
557
+ if (state.status === "fresh") fresh += 1;
558
+ else if (state.status === "stale") stale += 1;
559
+ else if (state.status === "modified" || state.status === "stale-modified") modified += 1;
560
+ else if (state.status === "unknown") unknown += 1;
561
+ else if (state.tokenBudget !== void 0) requiredMissing += 1;
562
+ else optionalMissing += 1;
563
+ }
564
+ const summary = `${fresh} fresh, ${stale} stale, ${modified} modified, ${unknown} unknown, ${requiredMissing} required missing, and ${optionalMissing} optional missing`;
565
+ if (stale > 0 || requiredMissing > 0) throw new Error(`Agent compaction freshness check failed: ${summary}. Run docs agent compact --stale --include-missing, then review generated changes.`);
566
+ console.log(pc.green(`Agent compaction freshness check passed: ${summary}.`));
567
+ return;
568
+ }
535
569
  let created = 0;
536
570
  let overwritten = 0;
537
571
  let processed = 0;
@@ -598,6 +632,7 @@ ${pc.dim("Examples:")}
598
632
  ${pc.cyan("npx @farming-labs/docs@latest agent compact --changed")}
599
633
  ${pc.cyan("npx @farming-labs/docs@latest agent compact --stale")}
600
634
  ${pc.cyan("npx @farming-labs/docs@latest agent compact --stale --include-missing")}
635
+ ${pc.cyan("npx @farming-labs/docs@latest agent compact --check")}
601
636
 
602
637
  ${pc.dim("Per-page override:")}
603
638
  Add ${pc.cyan("agent.tokenBudget")} to a page frontmatter block to override the compact output target for that page.
@@ -608,6 +643,7 @@ ${pc.dim("Options:")}
608
643
  ${pc.cyan("--changed")} Compact only docs pages changed in the current git working tree
609
644
  ${pc.cyan("--stale")} Re-compact only stale generated agent.md files
610
645
  ${pc.cyan("--include-missing")} With ${pc.cyan("--stale")}, also create missing agent.md files for explicit pages or pages that define ${pc.cyan("agent.tokenBudget")}
646
+ ${pc.cyan("--check")} Check generated agent.md freshness without an API key, network request, or file write
611
647
  ${pc.cyan("--config <path>")} Use a custom docs config path instead of ${pc.dim("docs.config.ts[x]")}
612
648
  ${pc.cyan("--api-key <key>")} Use an API key directly; prefer ${pc.dim("cloud.apiKey.env")}
613
649
  ${pc.cyan("--api-key-env <name>")} Env var name for the Docs Cloud API key; prefer ${pc.dim("cloud.apiKey.env")}
@@ -11,5 +11,6 @@ declare function parseCommandAlias(rawCommand?: string): {
11
11
  };
12
12
  /** Parse flags like --template next, --name my-docs, --theme concrete, --entry docs, --framework astro (exported for tests). */
13
13
  declare function parseFlags(argv: string[]): Record<string, string | boolean | undefined>;
14
+ declare function printAgentHelp(): void;
14
15
  //#endregion
15
- export { formatCliError, parseCommandAlias, parseFlags };
16
+ export { formatCliError, parseCommandAlias, parseFlags, printAgentHelp };
@@ -154,7 +154,8 @@ async function main() {
154
154
  } else if (parsedCommand.command === "mcp") {
155
155
  const { runMcp } = await import("../mcp-DsjWxF7g.mjs");
156
156
  await runMcp(mcpOptions);
157
- } else if (parsedCommand.command === "agent" && subcommand === "feedback") {
157
+ } else if (parsedCommand.command === "agent" && (subcommand === "--help" || subcommand === "-h")) printAgentHelp();
158
+ else if (parsedCommand.command === "agent" && subcommand === "feedback") {
158
159
  const { parseAgentFeedbackImproveArgs, printAgentFeedbackImproveHelp, runAgentFeedbackImprove } = await import("../feedback-DWVUMrxy.mjs");
159
160
  const feedbackOptions = parseAgentFeedbackImproveArgs(args.slice(2));
160
161
  if (feedbackOptions.help) {
@@ -179,7 +180,7 @@ async function main() {
179
180
  }
180
181
  await runAgentMaintenancePropose(proposeOptions);
181
182
  } else if (parsedCommand.command === "agent" && subcommand === "compact") {
182
- const { compactAgentDocs, parseAgentCompactArgs, printAgentCompactHelp } = await import("../agent-CaCWThBY.mjs").then((n) => n.t);
183
+ const { compactAgentDocs, parseAgentCompactArgs, printAgentCompactHelp } = await import("../agent-B0eRMIyt.mjs").then((n) => n.t);
183
184
  const agentCompactOptions = parseAgentCompactArgs(args.slice(2));
184
185
  if (agentCompactOptions.help) {
185
186
  printAgentCompactHelp();
@@ -197,16 +198,7 @@ async function main() {
197
198
  } else if (parsedCommand.command === "agent") {
198
199
  console.error(pc.red(`Unknown agent subcommand: ${subcommand ?? "(missing)"}`));
199
200
  console.error();
200
- const { printAgentCompactHelp } = await import("../agent-CaCWThBY.mjs").then((n) => n.t);
201
- const { printAgentExportHelp } = await import("../agent-export-GcTZPLfB.mjs");
202
- const { printAgentFeedbackImproveHelp } = await import("../feedback-DWVUMrxy.mjs");
203
- const { printAgentFeedbackEvaluationsHelp } = await import("../feedback-evals-k0sFDdms.mjs");
204
- const { printAgentMaintenanceProposeHelp } = await import("../propose-DJOiHu9Y.mjs");
205
- printAgentCompactHelp();
206
- printAgentExportHelp();
207
- printAgentFeedbackImproveHelp();
208
- printAgentFeedbackEvaluationsHelp();
209
- printAgentMaintenanceProposeHelp();
201
+ printAgentHelp();
210
202
  process.exit(1);
211
203
  } else if (parsedCommand.command === "agents" && subcommand === "generate") {
212
204
  const { generateAgents, parseAgentsGenerateArgs, printAgentsGenerateHelp } = await import("../agents-e1SYT64z.mjs");
@@ -240,7 +232,7 @@ async function main() {
240
232
  printSkillScaffoldHelp();
241
233
  process.exit(1);
242
234
  } else if (parsedCommand.command === "doctor") {
243
- const { parseDoctorArgs, printDoctorHelp, runDoctor } = await import("../doctor-CtK1CRPj.mjs");
235
+ const { parseDoctorArgs, printDoctorHelp, runDoctor } = await import("../doctor-CPGUlDVt.mjs");
244
236
  const doctorOptions = parseDoctorArgs(args.slice(1));
245
237
  if (doctorOptions.help) {
246
238
  printDoctorHelp();
@@ -248,7 +240,7 @@ async function main() {
248
240
  }
249
241
  await runDoctor(doctorOptions);
250
242
  } else if (parsedCommand.command === "review") {
251
- const { parseReviewArgs, printReviewHelp, runReview } = await import("../review-XI9FXeos.mjs");
243
+ const { parseReviewArgs, printReviewHelp, runReview } = await import("../review-BvjSFiD_.mjs");
252
244
  const reviewOptions = parseReviewArgs(args.slice(1));
253
245
  if (reviewOptions.help) {
254
246
  printReviewHelp();
@@ -334,6 +326,22 @@ async function main() {
334
326
  process.exit(1);
335
327
  }
336
328
  }
329
+ function printAgentHelp() {
330
+ console.log(`${pc.bold("docs agent")} — Agent documentation utilities
331
+
332
+ Usage:
333
+ docs agent <command> [options]
334
+
335
+ Commands:
336
+ ${pc.cyan("compact")} Generate or refresh page-level ${pc.dim("agent.md")} files
337
+ ${pc.cyan("export")} Export or check a static Agent Bundle
338
+ ${pc.cyan("feedback")} Cluster recurring agent feedback into improvement drafts
339
+ ${pc.cyan("feedback-evals")} Create reviewed evaluation candidates and regression baselines
340
+ ${pc.cyan("propose")} Combine recurring signals into maintenance proposals
341
+
342
+ Run ${pc.cyan("docs agent <command> --help")} for command-specific options.
343
+ `);
344
+ }
337
345
  function printHelp() {
338
346
  console.log(`
339
347
  ${pc.bold("@farming-labs/docs")} — Documentation framework CLI
@@ -415,6 +423,7 @@ ${pc.dim("Options for agent compact:")}
415
423
  ${pc.cyan("agent compact --all")} Compact every folder-based docs page
416
424
  ${pc.cyan("agent compact --changed")} Compact only docs pages changed in the current git working tree
417
425
  ${pc.cyan("agent compact --stale")} Refresh only stale generated ${pc.dim("agent.md")} files
426
+ ${pc.cyan("agent compact --check")} Fail when generated ${pc.dim("agent.md")} files are stale or required files are missing
418
427
  ${pc.cyan("--page <slug|path>")} Repeatable explicit page flag; positional page args work too
419
428
  ${pc.cyan("--include-missing")} With ${pc.cyan("--stale")}, also create explicit or token-budget pages missing ${pc.dim("agent.md")}
420
429
  ${pc.cyan("--api-key <key>")} Use an API key directly; prefer ${pc.dim("cloud.apiKey.env")}
@@ -550,4 +559,4 @@ main().catch((err) => {
550
559
  });
551
560
 
552
561
  //#endregion
553
- export { formatCliError, parseCommandAlias, parseFlags };
562
+ export { formatCliError, parseCommandAlias, parseFlags, printAgentHelp };
@@ -1,4 +1,4 @@
1
- import { i as scanDocsPageTargets, n as compactAgentDocs, r as inspectAgentCompactionState } from "./agent-CaCWThBY.mjs";
1
+ import { i as scanDocsPageTargets, n as compactAgentDocs, r as inspectAgentCompactionState } from "./agent-B0eRMIyt.mjs";
2
2
  import { f as PAGE_AGENT_CONTRACT_FIELDS } from "./markdown-sections-BKy4labo.mjs";
3
3
  import { d as httpLinkHeaderHasTargetRelation, f as AGENT_SKILL_ARCHIVE_MAX_UNCOMPRESSED_BYTES, p as readAgentSkillDocumentFromTar } from "./prompt-references-1Wce71c7.mjs";
4
4
  import { A as buildDocsAgentDiscoverySpec, At as DEFAULT_SITEMAP_MD_WELL_KNOWN_ROUTE, F as buildDocsMcpEndpointCandidates, Gn as getDocsMcpProtectedResourceMetadataRoutes, Jn as isDocsMcpOAuthScopeToken, Lt as resolveDocsSitemapConfig, M as buildDocsConfigMap, Qn as normalizeDocsMcpAuthorizationServerUrls, S as DEFAULT_SKILL_MD_WELL_KNOWN_ROUTE, T as DOCS_CONFIG_MAP_TOP_LEVEL_KEYS, Un as DEFAULT_MCP_WELL_KNOWN_ROUTE, Vn as DEFAULT_MCP_PUBLIC_ROUTE, c as DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE, cr as resolveDocsOkfTrustMetadata, h as DEFAULT_LLMS_FULL_TXT_ROUTE, i as DEFAULT_AGENT_FEEDBACK_ROUTE, jt as DEFAULT_SITEMAP_XML_ROUTE, kt as DEFAULT_SITEMAP_MD_ROUTE, l as DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE, mn as resolveAskAISearchRequestConfig, n as DEFAULT_AGENTS_MD_WELL_KNOWN_ROUTE, sr as resolveDocsOkfConfig, t as DEFAULT_AGENTS_MD_ROUTE, v as DEFAULT_LLMS_TXT_ROUTE, x as DEFAULT_SKILL_MD_ROUTE } from "./agent-DCTGj6J7.mjs";
@@ -16,7 +16,7 @@ import "./code-blocks-D90CCJFs.mjs";
16
16
  import "./server.mjs";
17
17
  import { _ as resolveDocsContentDir, d as readNavTitle, g as resolveDocsConfigPath, h as readTopLevelStringProperty, l as readBooleanProperty, r as extractTopLevelConfigObject, s as loadDocsConfigModuleResultWithProjectEnv, t as extractNestedObjectLiteral, v as resolveDocsProjectRoot } from "./config-DrZ3fXgf.mjs";
18
18
  import { t as detectFramework } from "./utils-Cc1SrVza.mjs";
19
- import { i as createAgentUsefulnessPagesFromMcp, n as analyzeAgentSurfaceDrift, r as analyzeAgentUsefulness, t as resolveGoldenEvaluationInput } from "./golden-evaluations-De4KwoYr.mjs";
19
+ import { i as createAgentUsefulnessPagesFromMcp, n as analyzeAgentSurfaceDrift, r as analyzeAgentUsefulness, t as resolveGoldenEvaluationInput } from "./golden-evaluations-Bnlljxq3.mjs";
20
20
  import { t as analyzeConfiguredAgentSkillsProgressiveDisclosure } from "./agent-skills-progressive-disclosure-DL-q4JZo.mjs";
21
21
  import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
22
22
  import path from "node:path";
@@ -43,6 +43,9 @@ const DEFAULT_DOCS_COMMANDS = [
43
43
  "skills scaffold",
44
44
  "agent compact",
45
45
  "agent export",
46
+ "agent feedback",
47
+ "agent feedback-evals",
48
+ "agent propose",
46
49
  "agents generate",
47
50
  "doctor",
48
51
  "review",
@@ -9,7 +9,7 @@ import { createFilesystemDocsMcpSource, getDocsConfigSchema, resolveDocsMcpConfi
9
9
  import "./code-blocks-D90CCJFs.mjs";
10
10
  import { _ as resolveDocsContentDir, g as resolveDocsConfigPath, h as readTopLevelStringProperty, s as loadDocsConfigModuleResultWithProjectEnv } from "./config-DrZ3fXgf.mjs";
11
11
  import { t as detectFramework } from "./utils-Cc1SrVza.mjs";
12
- import { a as extractAgentBlocks, i as createAgentUsefulnessPagesFromMcp, n as analyzeAgentSurfaceDrift, r as analyzeAgentUsefulness, t as resolveGoldenEvaluationInput } from "./golden-evaluations-De4KwoYr.mjs";
12
+ import { a as extractAgentBlocks, i as createAgentUsefulnessPagesFromMcp, n as analyzeAgentSurfaceDrift, r as analyzeAgentUsefulness, t as resolveGoldenEvaluationInput } from "./golden-evaluations-Bnlljxq3.mjs";
13
13
  import { t as analyzeConfiguredAgentSkillsProgressiveDisclosure } from "./agent-skills-progressive-disclosure-DL-q4JZo.mjs";
14
14
  import matter from "gray-matter";
15
15
  import { existsSync, lstatSync, readFileSync, readdirSync } from "node:fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/docs",
3
- "version": "0.2.105",
3
+ "version": "0.2.107",
4
4
  "description": "Modern, flexible MDX-based docs framework — core types, config, and CLI",
5
5
  "keywords": [
6
6
  "docs",
@@ -13,13 +13,43 @@
13
13
  --fd-framework-article-padding: 32px 0 64px;
14
14
  }
15
15
 
16
- #nd-docs-layout:not([data-fd-framework]) article#nd-page {
16
+ #nd-docs-layout:not([data-fd-framework]) article#nd-page,
17
+ #nd-docs-layout[data-fd-browser-adapter] article#nd-page {
17
18
  width: 100%;
18
19
  max-width: calc(var(--fd-content-width, 640px) + 64px);
19
20
  padding: 32px 32px 64px;
20
21
  justify-self: center;
21
22
  }
22
23
 
24
+ #nd-docs-layout[data-fd-browser-adapter] .fd-below-title-block {
25
+ margin: 0.75rem 0 2.5rem;
26
+ }
27
+
28
+ #nd-docs-layout[data-fd-browser-adapter] .fd-title-separator {
29
+ margin: 0 0 0.75rem;
30
+ }
31
+
32
+ #nd-docs-layout[data-fd-browser-adapter] .fd-page-actions,
33
+ #nd-docs-layout[data-fd-browser-adapter] [data-page-actions] {
34
+ margin: 0 0 0.625rem;
35
+ }
36
+
37
+ #nd-docs-layout[data-fd-browser-adapter] .fd-page-meta {
38
+ margin: 0;
39
+ }
40
+
41
+ @media (max-width: 1279px) {
42
+ #nd-docs-layout[data-fd-browser-adapter] {
43
+ --fd-toc-width: 0px !important;
44
+ }
45
+ }
46
+
47
+ @media (max-width: 767px) {
48
+ #nd-docs-layout[data-fd-browser-adapter] {
49
+ --fd-sidebar-width: 0px !important;
50
+ }
51
+ }
52
+
23
53
  :root {
24
54
  --color-fd-primary: oklch(0 0 0);
25
55
  --color-fd-primary-foreground: oklch(0.985 0 0);
@@ -136,10 +166,10 @@ button[data-search-full],
136
166
  [data-search-full],
137
167
  .fd-sidebar-search-btn,
138
168
  .fd-search-trigger-mobile {
139
- min-height: 2rem;
140
- border: 0 !important;
169
+ min-height: 2.25rem;
170
+ border: 1px solid var(--fd-shadcn-soft-border) !important;
141
171
  border-radius: calc(var(--radius) * 0.8) !important;
142
- background: var(--fd-shadcn-control) !important;
172
+ background: color-mix(in oklab, var(--fd-shadcn-control) 82%, transparent) !important;
143
173
  color: var(--color-fd-muted-foreground) !important;
144
174
  box-shadow: none !important;
145
175
  font-size: 0.8125rem !important;
@@ -154,14 +184,101 @@ button[data-search-full]:hover,
154
184
  color: var(--color-fd-foreground) !important;
155
185
  }
156
186
 
187
+ #nd-docs-layout .fd-sidebar-search-ai-row {
188
+ display: flex;
189
+ width: 100%;
190
+ align-items: stretch;
191
+ gap: 0.5rem;
192
+ }
193
+
194
+ #nd-docs-layout .fd-sidebar-search-ai-row .fd-sidebar-search-btn {
195
+ display: flex;
196
+ min-width: 0;
197
+ flex: 1 1 auto;
198
+ align-items: center;
199
+ gap: 0.5rem;
200
+ padding: 0.5rem 0.625rem !important;
201
+ cursor: pointer;
202
+ transition:
203
+ background-color 150ms,
204
+ border-color 150ms,
205
+ color 150ms;
206
+ }
207
+
208
+ #nd-docs-layout .fd-sidebar-search-ai-row .fd-sidebar-search-btn > svg {
209
+ width: 1rem;
210
+ height: 1rem;
211
+ flex: 0 0 auto;
212
+ opacity: 0.72;
213
+ }
214
+
215
+ #nd-docs-layout
216
+ .fd-sidebar-search-ai-row
217
+ .fd-sidebar-search-btn
218
+ > span:not(.fd-sidebar-search-kbd) {
219
+ min-width: 0;
220
+ flex: 0 1 auto;
221
+ overflow: hidden;
222
+ text-align: left;
223
+ text-overflow: ellipsis;
224
+ white-space: nowrap;
225
+ }
226
+
227
+ button[data-search-full] > :is(div, span):has(> kbd),
228
+ [data-search-full] > :is(div, span):has(> kbd),
229
+ .fd-sidebar-search-kbd {
230
+ display: inline-flex !important;
231
+ min-width: 0 !important;
232
+ height: 1.375rem !important;
233
+ flex: 0 0 auto !important;
234
+ align-items: center !important;
235
+ justify-content: center !important;
236
+ gap: 0.125rem !important;
237
+ margin-inline-start: auto !important;
238
+ border: 1px solid var(--fd-shadcn-soft-border) !important;
239
+ border-radius: calc(var(--radius) * 0.55) !important;
240
+ background: var(--color-fd-background) !important;
241
+ box-shadow:
242
+ 0 1px 2px rgb(0 0 0 / 6%),
243
+ inset 0 -1px 0 color-mix(in oklab, var(--color-fd-border) 70%, transparent) !important;
244
+ padding: 0 0.375rem !important;
245
+ line-height: 1 !important;
246
+ }
247
+
157
248
  button[data-search-full] kbd,
158
249
  [data-search-full] kbd,
159
250
  .fd-sidebar-search-kbd kbd {
251
+ min-width: 0 !important;
160
252
  border: 0 !important;
161
253
  background: transparent !important;
162
254
  color: var(--color-fd-muted-foreground) !important;
255
+ font-family: var(--fd-font-mono, "Geist Mono", ui-monospace, monospace) !important;
163
256
  font-size: 0.6875rem !important;
164
257
  font-weight: 500 !important;
258
+ line-height: 1 !important;
259
+ padding: 0 !important;
260
+ }
261
+
262
+ #nd-docs-layout .fd-sidebar-search-ai-row .fd-sidebar-ai-btn {
263
+ width: 2.25rem;
264
+ min-width: 2.25rem;
265
+ min-height: 2.25rem;
266
+ border: 1px solid var(--fd-shadcn-soft-border) !important;
267
+ border-radius: calc(var(--radius) * 0.8) !important;
268
+ background: color-mix(in oklab, var(--fd-shadcn-control) 82%, transparent) !important;
269
+ color: var(--color-fd-muted-foreground) !important;
270
+ box-shadow: none !important;
271
+ }
272
+
273
+ #nd-docs-layout .fd-sidebar-search-ai-row .fd-sidebar-ai-btn:hover {
274
+ border-color: var(--color-fd-border) !important;
275
+ background: var(--fd-shadcn-control-hover) !important;
276
+ color: var(--color-fd-foreground) !important;
277
+ }
278
+
279
+ #nd-docs-layout .fd-sidebar-search-ai-row :is(.fd-sidebar-search-btn, .fd-sidebar-ai-btn):focus-visible {
280
+ outline: 2px solid var(--color-fd-ring);
281
+ outline-offset: 2px;
165
282
  }
166
283
 
167
284
  aside#nd-sidebar,
@@ -555,6 +672,7 @@ aside#nd-sidebar button.text-fd-muted-foreground,
555
672
  }
556
673
 
557
674
  .fd-docs-content article h2,
675
+ .fd-page-body h2,
558
676
  #nd-page h2,
559
677
  .fd-docs-content h2 {
560
678
  color: var(--color-fd-foreground) !important;
@@ -562,10 +680,11 @@ aside#nd-sidebar button.text-fd-muted-foreground,
562
680
  font-weight: 600 !important;
563
681
  letter-spacing: -0.015em !important;
564
682
  line-height: 1.4 !important;
565
- margin-top: 2.25rem;
683
+ margin: 2.75rem 0 1rem !important;
566
684
  }
567
685
 
568
686
  .fd-docs-content article h3,
687
+ .fd-page-body h3,
569
688
  #nd-page h3,
570
689
  .fd-docs-content h3 {
571
690
  color: var(--color-fd-foreground) !important;
@@ -573,6 +692,7 @@ aside#nd-sidebar button.text-fd-muted-foreground,
573
692
  font-weight: 600 !important;
574
693
  letter-spacing: -0.01em !important;
575
694
  line-height: 1.45 !important;
695
+ margin: 2rem 0 0.75rem !important;
576
696
  }
577
697
 
578
698
  #nd-page :is(h1, h2, h3, h4) > a.peer,
@@ -602,13 +722,36 @@ aside#nd-sidebar button.text-fd-muted-foreground,
602
722
  }
603
723
 
604
724
  .fd-docs-content,
725
+ .fd-page-body,
605
726
  #nd-page .prose {
606
727
  color: var(--color-fd-foreground);
607
728
  font-size: 0.9375rem;
608
729
  line-height: 1.75;
609
730
  }
610
731
 
732
+ #nd-docs-layout :is(.fd-docs-content, .fd-page-body, #nd-page .prose) p {
733
+ margin: 0 0 1.25rem;
734
+ }
735
+
736
+ #nd-docs-layout
737
+ :is(.fd-docs-content, .fd-page-body, #nd-page .prose)
738
+ :is(ul, ol):not(:where(.not-prose, .not-prose *)) {
739
+ margin: 0 0 1.5rem;
740
+ }
741
+
742
+ #nd-docs-layout
743
+ :is(.fd-docs-content, .fd-page-body, #nd-page .prose)
744
+ li:not(:where(.not-prose, .not-prose *))
745
+ + li {
746
+ margin-top: 0.5rem;
747
+ }
748
+
749
+ #nd-docs-layout :is(.fd-docs-content, .fd-page-body, #nd-page .prose) .fd-table-wrapper {
750
+ margin: 1.25rem 0 2.5rem !important;
751
+ }
752
+
611
753
  .fd-docs-content :is(p, li, td, blockquote),
754
+ .fd-page-body :is(p, li, td, blockquote),
612
755
  #nd-page .prose :is(p, li, td, blockquote) {
613
756
  color: var(--color-fd-foreground);
614
757
  line-height: 1.75;
@@ -635,7 +778,7 @@ aside#nd-sidebar button.text-fd-muted-foreground,
635
778
  .fd-page-nav-card,
636
779
  .fd-page-nav a,
637
780
  #nd-page > div[class~="@container"] > a {
638
- border: 0 !important;
781
+ border: 1px solid var(--fd-shadcn-soft-border) !important;
639
782
  border-radius: calc(var(--radius) * 0.8) !important;
640
783
  background: var(--fd-shadcn-control) !important;
641
784
  color: var(--color-fd-foreground) !important;
@@ -674,7 +817,7 @@ aside#nd-sidebar button.text-fd-muted-foreground,
674
817
  display: grid !important;
675
818
  grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
676
819
  gap: 0.75rem !important;
677
- margin-top: 1.5rem !important;
820
+ margin-top: 2.25rem !important;
678
821
  padding-top: 0 !important;
679
822
  border-top: 0 !important;
680
823
  }
@@ -688,12 +831,34 @@ aside#nd-sidebar button.text-fd-muted-foreground,
688
831
  #nd-page > div[class~="@container"] > a {
689
832
  display: flex !important;
690
833
  min-width: 0;
691
- min-height: 4rem !important;
834
+ min-height: 4.75rem !important;
692
835
  flex-direction: column !important;
693
836
  justify-content: center !important;
694
- gap: 0.125rem !important;
695
- padding: 0.75rem !important;
837
+ gap: 0.375rem !important;
838
+ padding: 0.875rem 1rem !important;
839
+ background: var(--color-fd-card) !important;
840
+ box-shadow: 0 1px 2px rgb(0 0 0 / 4%) !important;
696
841
  text-decoration: none !important;
842
+ transition:
843
+ background-color 150ms,
844
+ border-color 150ms,
845
+ color 150ms,
846
+ box-shadow 150ms !important;
847
+ }
848
+
849
+ .fd-page-nav-card:hover,
850
+ .fd-page-nav a:hover,
851
+ #nd-page > div[class~="@container"] > a:hover {
852
+ border-color: var(--color-fd-border) !important;
853
+ background: color-mix(in oklab, var(--color-fd-accent) 72%, var(--color-fd-card)) !important;
854
+ box-shadow: 0 1px 3px rgb(0 0 0 / 7%) !important;
855
+ }
856
+
857
+ .fd-page-nav-card:focus-visible,
858
+ .fd-page-nav a:focus-visible,
859
+ #nd-page > div[class~="@container"] > a:focus-visible {
860
+ outline: 2px solid var(--color-fd-ring);
861
+ outline-offset: 2px;
697
862
  }
698
863
 
699
864
  .fd-page-nav-next,
@@ -703,6 +868,9 @@ aside#nd-sidebar button.text-fd-muted-foreground,
703
868
 
704
869
  .fd-page-nav-title,
705
870
  #nd-page > div[class~="@container"] > a > div:first-child {
871
+ display: inline-flex !important;
872
+ align-items: center !important;
873
+ gap: 0.375rem !important;
706
874
  min-height: 0 !important;
707
875
  color: var(--color-fd-foreground) !important;
708
876
  font-size: 0.8125rem !important;
@@ -710,6 +878,13 @@ aside#nd-sidebar button.text-fd-muted-foreground,
710
878
  line-height: 1.35 !important;
711
879
  }
712
880
 
881
+ .fd-page-nav-title svg {
882
+ width: 0.875rem;
883
+ height: 0.875rem;
884
+ flex: 0 0 auto;
885
+ color: var(--color-fd-muted-foreground);
886
+ }
887
+
713
888
  .fd-page-nav-description,
714
889
  #nd-page > div[class~="@container"] > a > p:last-child {
715
890
  min-height: 0 !important;
@@ -814,7 +989,7 @@ aside#nd-sidebar button.text-fd-muted-foreground,
814
989
  outline-offset: -2px !important;
815
990
  }
816
991
 
817
- :is(.fd-docs-content, #nd-page .prose)
992
+ :is(.fd-docs-content, .fd-page-body, #nd-page .prose)
818
993
  [data-orientation="horizontal"]:has(> [role="tablist"]) {
819
994
  overflow: hidden;
820
995
  border: 1px solid var(--fd-shadcn-soft-border) !important;
@@ -823,15 +998,18 @@ aside#nd-sidebar button.text-fd-muted-foreground,
823
998
  box-shadow: none !important;
824
999
  }
825
1000
 
826
- :is(.fd-docs-content, #nd-page .prose)
1001
+ :is(.fd-docs-content, .fd-page-body, #nd-page .prose)
827
1002
  [data-orientation="horizontal"]:has(> [role="tablist"])
828
1003
  > [role="tabpanel"] {
829
1004
  background: var(--color-fd-card) !important;
830
1005
  }
831
1006
 
832
1007
  .fd-docs-content pre,
1008
+ .fd-page-body pre,
833
1009
  .fd-docs-content .shiki,
1010
+ .fd-page-body .shiki,
834
1011
  .fd-docs-content [data-codeblock],
1012
+ .fd-page-body [data-codeblock],
835
1013
  figure.shiki,
836
1014
  .fd-codeblock,
837
1015
  .fd-ai-code-block {
@@ -869,6 +1047,7 @@ figure.shiki,
869
1047
  }
870
1048
 
871
1049
  .fd-docs-content pre,
1050
+ .fd-page-body pre,
872
1051
  figure.shiki pre,
873
1052
  .fd-codeblock pre {
874
1053
  background: transparent !important;
@@ -877,19 +1056,28 @@ figure.shiki pre,
877
1056
 
878
1057
  /* Compact code surfaces modeled after the current shadcn docs. */
879
1058
  figure.shiki,
880
- .fd-docs-content figure.shiki {
881
- margin-block: 1.25rem !important;
882
- border: 0 !important;
883
- border-radius: calc(var(--radius) * 1.8) !important;
1059
+ .fd-docs-content figure.shiki,
1060
+ .fd-page-body figure.shiki {
1061
+ margin: 1.5rem 0 2rem !important;
1062
+ border: 1px solid var(--fd-shadcn-soft-border) !important;
1063
+ border-radius: calc(var(--radius) * 0.9) !important;
884
1064
  background: var(--fd-shadcn-code) !important;
885
1065
  padding-block: 0 !important;
886
1066
  }
887
1067
 
1068
+ /* Framework browser shells intentionally clear Shiki backgrounds so token
1069
+ * colors can be themed. Restore only the code surface at equal specificity. */
1070
+ :is(html.dark, body.dark) #nd-docs-layout[data-fd-framework] figure.shiki,
1071
+ #nd-docs-layout[data-fd-framework].dark figure.shiki {
1072
+ background: var(--fd-shadcn-code) !important;
1073
+ }
1074
+
888
1075
  figure.shiki pre,
889
- .fd-docs-content figure.shiki pre {
1076
+ .fd-docs-content figure.shiki pre,
1077
+ .fd-page-body figure.shiki pre {
890
1078
  max-width: 100%;
891
1079
  margin: 0 !important;
892
- padding: 0.625rem 0.75rem !important;
1080
+ padding: 0.75rem 0 !important;
893
1081
  overflow-x: auto !important;
894
1082
  overscroll-behavior-inline: contain;
895
1083
  scrollbar-color: color-mix(in oklab, var(--color-fd-foreground) 18%, transparent)
@@ -898,16 +1086,18 @@ figure.shiki pre,
898
1086
  }
899
1087
 
900
1088
  figure.shiki pre > code,
901
- .fd-docs-content figure.shiki pre > code {
1089
+ .fd-docs-content figure.shiki pre > code,
1090
+ .fd-page-body figure.shiki pre > code {
902
1091
  font-family: var(--fd-font-mono, "Geist Mono", ui-monospace, monospace) !important;
903
- font-size: 0.875rem !important;
1092
+ font-size: 0.8125rem !important;
904
1093
  font-variant-ligatures: none;
905
- line-height: 1.75 !important;
1094
+ line-height: 1.6 !important;
906
1095
  tab-size: 2;
907
1096
  }
908
1097
 
909
1098
  figure.shiki pre > code > :is(.line, [data-line]) {
910
- min-height: 1.53125rem;
1099
+ min-height: 1.3rem;
1100
+ padding-inline: 1rem 3rem !important;
911
1101
  }
912
1102
 
913
1103
  figure.shiki pre > code > :is(.line, [data-line])[data-highlighted-line] {
@@ -1068,7 +1258,8 @@ article :not(pre) > code,
1068
1258
 
1069
1259
  @media (max-width: 767px) {
1070
1260
  figure.shiki pre,
1071
- .fd-docs-content figure.shiki pre {
1261
+ .fd-docs-content figure.shiki pre,
1262
+ .fd-page-body figure.shiki pre {
1072
1263
  padding: 0.625rem 0.75rem !important;
1073
1264
  }
1074
1265
  }
@@ -1128,8 +1319,8 @@ article :not(pre) > code,
1128
1319
  font-weight: 600;
1129
1320
  }
1130
1321
 
1131
- :is(.fd-docs-content, #nd-page .prose) blockquote {
1132
- margin: 1.25rem 0 !important;
1322
+ :is(.fd-docs-content, .fd-page-body, #nd-page .prose) blockquote {
1323
+ margin: 1.5rem 0 2rem !important;
1133
1324
  border-inline-start: 2px solid var(--color-fd-border) !important;
1134
1325
  background: transparent !important;
1135
1326
  color: var(--color-fd-muted-foreground) !important;
@@ -1137,19 +1328,19 @@ article :not(pre) > code,
1137
1328
  padding: 0.125rem 0 0.125rem 1rem !important;
1138
1329
  }
1139
1330
 
1140
- :is(.fd-docs-content, #nd-page .prose) blockquote :is(p, li) {
1331
+ :is(.fd-docs-content, .fd-page-body, #nd-page .prose) blockquote :is(p, li) {
1141
1332
  color: inherit !important;
1142
1333
  }
1143
1334
 
1144
- :is(.fd-docs-content, #nd-page .prose) details {
1335
+ :is(.fd-docs-content, .fd-page-body, #nd-page .prose) details {
1145
1336
  overflow: hidden;
1146
- margin: 1rem 0;
1337
+ margin: 1.5rem 0 2rem;
1147
1338
  border: 1px solid var(--fd-shadcn-soft-border);
1148
1339
  border-radius: calc(var(--radius) * 1.2);
1149
1340
  background: var(--color-fd-card);
1150
1341
  }
1151
1342
 
1152
- :is(.fd-docs-content, #nd-page .prose) details > summary {
1343
+ :is(.fd-docs-content, .fd-page-body, #nd-page .prose) details > summary {
1153
1344
  min-height: 2.75rem;
1154
1345
  cursor: pointer;
1155
1346
  color: var(--color-fd-card-foreground);
@@ -1159,19 +1350,19 @@ article :not(pre) > code,
1159
1350
  transition: background-color 150ms ease;
1160
1351
  }
1161
1352
 
1162
- :is(.fd-docs-content, #nd-page .prose) details > summary:hover {
1353
+ :is(.fd-docs-content, .fd-page-body, #nd-page .prose) details > summary:hover {
1163
1354
  background: color-mix(in oklab, var(--color-fd-accent) 55%, transparent);
1164
1355
  }
1165
1356
 
1166
- :is(.fd-docs-content, #nd-page .prose) details[open] > summary {
1357
+ :is(.fd-docs-content, .fd-page-body, #nd-page .prose) details[open] > summary {
1167
1358
  border-bottom: 1px solid var(--fd-shadcn-soft-border);
1168
1359
  }
1169
1360
 
1170
- :is(.fd-docs-content, #nd-page .prose) details > :not(summary) {
1361
+ :is(.fd-docs-content, .fd-page-body, #nd-page .prose) details > :not(summary) {
1171
1362
  margin-inline: 1rem;
1172
1363
  }
1173
1364
 
1174
- :is(.fd-docs-content, #nd-page .prose)
1365
+ :is(.fd-docs-content, .fd-page-body, #nd-page .prose)
1175
1366
  [data-orientation="vertical"][class~="divide-y"] {
1176
1367
  overflow: hidden;
1177
1368
  border: 1px solid var(--fd-shadcn-soft-border) !important;
@@ -1180,7 +1371,7 @@ article :not(pre) > code,
1180
1371
  box-shadow: none !important;
1181
1372
  }
1182
1373
 
1183
- :is(.fd-docs-content, #nd-page .prose)
1374
+ :is(.fd-docs-content, .fd-page-body, #nd-page .prose)
1184
1375
  [data-orientation="vertical"][class~="divide-y"]
1185
1376
  [data-radix-collection-item] {
1186
1377
  min-height: 2.75rem;
@@ -1190,7 +1381,7 @@ article :not(pre) > code,
1190
1381
  transition: background-color 150ms ease;
1191
1382
  }
1192
1383
 
1193
- :is(.fd-docs-content, #nd-page .prose)
1384
+ :is(.fd-docs-content, .fd-page-body, #nd-page .prose)
1194
1385
  [data-orientation="vertical"][class~="divide-y"]
1195
1386
  [data-radix-collection-item]:hover {
1196
1387
  background: color-mix(in oklab, var(--color-fd-accent) 55%, transparent) !important;
@@ -1211,7 +1402,7 @@ article :not(pre) > code,
1211
1402
  box-shadow: 0 0 0 4px var(--color-fd-background);
1212
1403
  }
1213
1404
 
1214
- :is(.fd-docs-content, #nd-page .prose) kbd {
1405
+ :is(.fd-docs-content, .fd-page-body, #nd-page .prose) kbd {
1215
1406
  display: inline-flex;
1216
1407
  min-width: 1.5rem;
1217
1408
  min-height: 1.5rem;
@@ -1228,7 +1419,8 @@ article :not(pre) > code,
1228
1419
  box-shadow: 0 1px 0 color-mix(in oklab, var(--color-fd-border) 80%, transparent);
1229
1420
  }
1230
1421
 
1231
- :is(.fd-docs-content, #nd-page .prose) hr {
1422
+ :is(.fd-docs-content, .fd-page-body, #nd-page .prose) hr {
1423
+ margin: 3rem 0 !important;
1232
1424
  border-color: var(--fd-shadcn-soft-border) !important;
1233
1425
  }
1234
1426
 
@@ -1666,7 +1858,8 @@ article :not(pre) > code,
1666
1858
  --fd-framework-article-padding: 24px 16px 48px;
1667
1859
  }
1668
1860
 
1669
- #nd-docs-layout:not([data-fd-framework]) article#nd-page {
1861
+ #nd-docs-layout:not([data-fd-framework]) article#nd-page,
1862
+ #nd-docs-layout[data-fd-browser-adapter] article#nd-page {
1670
1863
  max-width: 100%;
1671
1864
  padding: 24px 16px 48px;
1672
1865
  }
@@ -1685,6 +1878,7 @@ article :not(pre) > code,
1685
1878
  }
1686
1879
 
1687
1880
  .fd-docs-content,
1881
+ .fd-page-body,
1688
1882
  #nd-page .prose {
1689
1883
  font-size: 0.9375rem;
1690
1884
  }