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
23 changes: 9 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
# @inth/docs

Shared docs tooling for Inth docs projects: React MDX rendering, MDX-to-markdown conversion, LLM bundles, validation, and static search.
Shared docs tooling for Inth docs projects: framework-neutral MDX-to-markdown conversion, LLM bundles, validation, and static search.

`@inth/docs` is split into focused public entry points:

- `@inth/docs`: React MDX component adapters via `mdxComponents`
- `@inth/docs/remark`: remark plugins plus `defaultRemarkPlugins`
- `@inth/docs/convert`: MDX-to-markdown conversion APIs
- `@inth/docs/llm`: `llms.txt` and topic-scoped full-context generation
Expand All @@ -23,21 +22,15 @@ pnpm add @inth/docs

## Basic Usage

### Render MDX components
### Own MDX components in your app

```tsx
import { mdxComponents } from "@inth/docs";

const components = {
...mdxComponents,
};
```
`@inth/docs` does not export prebuilt React, Vue, Nuxt, Svelte, or Astro components. Define the MDX component map in the docs app that renders your pages.

## Live Example App

The repo includes a canonical consumer demo at `apps/docs-smoke`.

- Renders real `.mdx` fixture files through the package's exported `mdxComponents`.
- Renders real `.mdx` fixture files through app-owned `mdxComponents`.
- Uses TanStack Start for SSR and hydration coverage.
- Shows extracted `ExtractedTypeTable` output while keeping pipeline fixtures in the validation path.

Expand All @@ -58,21 +51,23 @@ bun run --filter docs-smoke test:e2e

Validation layers:

- Package unit tests in `packages/docs/src/**/*.test.ts*` cover component semantics and pure library behavior.
- Package unit tests in `packages/docs/src/**/*.test.ts*` cover framework-neutral conversion, search, linting, and generated docs behavior.
- Pipeline fixtures in `apps/docs-smoke/scripts` and `apps/docs-smoke/content` cover MDX conversion, LLM generation, and `ExtractedTypeTable`.
- The TanStack Start demo app in `apps/docs-smoke/src` covers real browser rendering and hydration.

## Where This Fits

`@inth/docs` is not a hosted docs platform or a complete docs-site framework. Use tools such as Mintlify, Fumadocs, or Starlight when the primary job is shipping a polished docs website quickly.

Use this package when the primary job is shared docs infrastructure: MDX rendering adapters, MDX-to-markdown conversion, LLM bundles, linting, static search artifacts, answer helpers, and agent-facing docs output that can feed multiple apps and tools.
Use this package when the primary job is shared docs infrastructure: MDX-to-markdown conversion, LLM bundles, linting, static search artifacts, answer helpers, and agent-facing docs output that can feed multiple apps and tools.

The pipeline entry points are framework-neutral. React, Vue, Nuxt, Svelte, Astro, and other stacks can use conversion, LLM, lint, and search APIs while owning their own runtime component rendering.

## Wiring It Into An App

In a c15t-style repo with a top-level `docs/` directory, wire `@inth/docs` into the docs app and docs scripts:

- The docs app imports `mdxComponents` only if it renders MDX directly.
- The docs app owns `mdxComponents` if it renders MDX directly.
- A conversion script runs `convertAllMdx({ srcDir: process.cwd(), outDir: "public" })`.
- LLM and search scripts read the converted markdown under `public/docs/`.
- Product code does not import `@inth/docs` unless it also renders docs pages.
Expand Down
16 changes: 8 additions & 8 deletions apps/docs-smoke/content/docs/guides/components-fixture.mdx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
---
title: "Runtime Components"
description: "Render the browser-facing @inth/docs adapters through authored MDX."
description: "Render app-owned MDX components through authored docs content."
---

# Runtime Components

<Callout variant="success" title="Runtime fixture">
This page exercises the exported MDX adapters without replacing them with app-local variants.
This page exercises the smoke app's MDX components without relying on package-owned runtime UI.
</Callout>

## Authoring Contract

```mdx
<Callout variant="success" title="Runtime fixture">
Render exported adapters through your shared `mdxComponents` map.
Render app-owned components through your shared `mdxComponents` map.
</Callout>

<CommandTabs command="@inth/docs" mode="install" />
Expand Down Expand Up @@ -70,7 +70,7 @@ description: "Render the browser-facing @inth/docs adapters through authored MDX
Use semantic components such as `Callout`, `Tabs`, `Cards`, `Steps`, `CommandTabs`, `Accordion`, `Example`, `TopicSwitcher`, and `TypeTable`.
</Step>
<Step title="Render in the app">
Import the `.mdx` file directly and provide `mdxComponents` through the shared runtime map.
Import the `.mdx` file directly and provide the app's `mdxComponents` through the shared runtime map.
</Step>
<Step title="Validate the pipeline separately">
Keep `ExtractedTypeTable` coverage in the conversion pipeline where source extraction has a stable file-system base path.
Expand All @@ -90,7 +90,7 @@ description: "Render the browser-facing @inth/docs adapters through authored MDX

<Tabs items={["Overview", "Tables", "Pipeline note"]}>
<Tab value="Overview">
This tabset proves the package adapters hydrate correctly inside the demo app.
This tabset proves the app-owned components hydrate correctly inside the demo app.
</Tab>
<Tab value="Tables">
`TypeTable` is safe to render live because all of its data is already present in the MDX payload.
Expand Down Expand Up @@ -130,14 +130,14 @@ description: "Render the browser-facing @inth/docs adapters through authored MDX
description="Preview the output and inspect the source."
filename="mdx-components.tsx"
language="tsx"
code={`import { mdxComponents } from "@inth/docs";
code={`import { mdxComponents } from "@/components/docs-mdx";

export const components = {
...mdxComponents,
};`}
>
<Callout title="Runtime adapter" variant="success">
The host app owns styling while `@inth/docs` owns the MDX component contract.
<Callout title="Runtime components" variant="success">
The host app owns styling and runtime components while `@inth/docs` owns conversion.
</Callout>
</Example>

Expand Down
10 changes: 6 additions & 4 deletions apps/docs-smoke/content/docs/guides/quickstart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ description: "Wire @inth/docs into a docs app and docs pipeline."

# Quickstart

`@inth/docs` is docs infrastructure. In a product repo such as c15t, you wire it into the docs app and docs scripts, not into the product runtime unless that runtime renders docs pages.
`@inth/docs` is docs infrastructure. In a product repo such as c15t, you wire it into the docs app and docs scripts, not into the product runtime unless that runtime renders or serves docs pages.

## What You Are Wiring

<TypeTable
properties={{
"Docs app": {
type: "runtime",
description: "Use `mdxComponents` when your React docs app imports and renders MDX files.",
description: "Define `mdxComponents` in your docs app when it imports and renders MDX files.",
required: true,
},
"Markdown export": {
Expand Down Expand Up @@ -63,7 +63,7 @@ If your docs live under `content/docs/` instead, set `srcDir: "content"` and the
Use this only in the app that renders documentation pages.

```tsx
import { mdxComponents } from "@inth/docs";
import { mdxComponents } from "@/components/docs-mdx";

export const components = {
...mdxComponents,
Expand All @@ -72,7 +72,9 @@ export const components = {
};
```

`@inth/docs` does not own your routing, sidebar, layout, hosting, or framework. A Next.js, TanStack Start, Vite, Fumadocs, or Astro-backed docs app can still own those pieces.
`@inth/docs` does not own your routing, sidebar, layout, hosting, framework, or runtime UI components. A React, Next.js, TanStack Start, Vite, Fumadocs, or Astro-backed docs app can still own those pieces.

React, Vue, Nuxt, Svelte, Astro, and other stacks can use the conversion, LLM, lint, and search entry points. Runtime MDX components should live in the consuming docs app.

Framework docs can be exposed through `TopicSwitcher` while the conversion pipeline still resolves `{framework}` placeholders from `/docs/frameworks/:framework` routes.

Expand Down
27 changes: 13 additions & 14 deletions apps/docs-smoke/content/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,12 @@ description: "Developer reference for rendering MDX, converting docs, generating

# @inth/docs

`@inth/docs` is the shared package for docs rendering, docs pipelines, LLM-friendly output, validation, and local search. Use the smallest entry point that matches where your code runs.
`@inth/docs` is the shared package for framework-neutral docs pipelines, LLM-friendly output, validation, and local search. Use the smallest entry point that matches where your code runs.

## Package Surfaces

<TypeTable
properties={{
"@inth/docs": {
type: "runtime",
description: "React MDX adapters and individual components.",
required: true,
},
"@inth/docs/remark": {
type: "build time",
description: "remark plugins and `defaultRemarkPlugins`.",
Expand Down Expand Up @@ -60,8 +55,8 @@ description: "Developer reference for rendering MDX, converting docs, generating
## Common Implementation Paths

<Steps>
<Step title="Render MDX in React">
Import `mdxComponents` from `@inth/docs`, spread it into your MDX provider, and override individual entries only when your app needs custom styling.
<Step title="Own runtime MDX components">
Define `mdxComponents` in the docs app that renders your pages. `@inth/docs` intentionally does not export prebuilt UI components.
</Step>
<Step title="Convert MDX for agents">
Use `@inth/docs/convert` with `@inth/docs/remark` to flatten authored MDX into markdown that works in LLM bundles and search indexes.
Expand All @@ -82,21 +77,25 @@ Use Mintlify, Fumadocs, or Starlight when the primary job is to ship a complete
| [Mintlify](https://mintlify.com/) | Managed developer documentation platform with hosted docs, API docs, and AI-oriented product features. | Teams that want a polished docs product without owning the whole site pipeline. |
| [Fumadocs](https://fumadocs.dev/) | Documentation framework and toolchain for React docs sites, especially Next.js. | Teams that want framework-level routing, navigation, UI, content adapters, search, and OpenAPI support. |
| [Starlight](https://starlight.astro.build/) | Full-featured documentation framework built on Astro. | Teams already using Astro or wanting an Astro-native docs site. |
| `@inth/docs` | Shared docs infrastructure package: React MDX adapters, MDX-to-markdown conversion, LLM bundles, linting, static search, and answer helpers. | Teams that want to own the app shell while reusing one docs pipeline across sites, agents, search APIs, and internal tools. |
| `@inth/docs` | Shared docs infrastructure package: framework-neutral MDX-to-markdown conversion, LLM bundles, linting, static search, and answer helpers. | Teams that want to own the app shell and runtime components while reusing one docs pipeline across sites, agents, search APIs, and internal tools. |

These tools are not mutually exclusive. A site framework can own the public docs experience while `@inth/docs` owns conversion, validation, search artifacts, and agent-facing bundles.

## Other Frameworks

React, Vue, Nuxt, Svelte, Astro, and other stacks can use `@inth/docs/convert`, `@inth/docs/remark`, `@inth/docs/llm`, `@inth/docs/search`, and `@inth/docs/lint` today. Runtime MDX components belong in the consuming docs app, not this package.

## Render MDX

```tsx
import { mdxComponents } from "@inth/docs";
import { mdxComponents } from "@/components/docs-mdx";

export const components = {
...mdxComponents,
};
```

The root export is a runtime contract. It is not tied to TanStack Start, shadcn, or this demo shell.
This smoke app keeps its React MDX components under `src/components/docs-mdx`. Consumers should create their own components that match their framework, design system, and accessibility requirements.

## Convert And Generate

Expand All @@ -123,7 +122,7 @@ After conversion, use `@inth/docs/llm` to write `llms.txt` and topic-scoped full
/>
<Card
title="Runtime Components"
description="Exercise browser-facing adapters including Accordion, Example, and TopicSwitcher through authored MDX."
description="Exercise app-owned MDX components, including Accordion, Example, and TopicSwitcher, through authored docs content."
href="/docs/guides/components-fixture"
/>
<Card
Expand All @@ -133,7 +132,7 @@ After conversion, use `@inth/docs/llm` to write `llms.txt` and topic-scoped full
/>
<Card
title="Recipes Playground"
description="Switch between guided implementation recipes with live package components."
description="Switch between guided implementation recipes with live app components."
href="/playground"
/>
</Cards>
Expand All @@ -142,7 +141,7 @@ After conversion, use `@inth/docs/llm` to write `llms.txt` and topic-scoped full

<Steps>
<Step title="Package tests">
Cover semantic HTML and safe runtime behavior in `packages/docs/src/**/*.test.ts*`.
Cover framework-neutral conversion, search, linting, and generated docs behavior in `packages/docs/src/**/*.test.ts*`.
</Step>
<Step title="Pipeline fixtures">
Cover conversion, type extraction, LLM output, and search generation in `apps/docs-smoke/scripts` and `apps/docs-smoke/content`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ const MANAGERS = ["npm", "pnpm", "yarn", "bun"] as const;
export type PackageManager = (typeof MANAGERS)[number];
export type CommandMode = "run" | "install" | "create";

type BaseCommandTabsProps = {
interface BaseCommandTabsProps {
children?: ReactNode;
/** Or pass pre-rendered commands per manager */
commands?: Partial<Record<PackageManager, string>>;
defaultManager?: PackageManager;
children?: ReactNode;
};
}

type ModeCommandTabsProps = BaseCommandTabsProps & {
/** Command template. `{pm}` is replaced with the active package manager. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import type { HTMLAttributes, ReactNode } from "react";

const SOURCE_FILE_KEY_HASH_MODULUS = 2_147_483_647;

export type ExampleSourceFile = {
id?: string;
export interface ExampleSourceFile {
code: string;
filename: string;
id?: string;
language?: string;
code: string;
};
}

export type ExampleProps = HTMLAttributes<HTMLDivElement> & {
title?: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/** @biome-ignore lint/performance/noBarrelFile: package entry point */

// biome-ignore lint/performance/noBarrelFile: app-local MDX component entry point
export {
Accordion,
AccordionItem,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,6 @@ import { Tab, Tabs } from "./tabs";
import { TopicSwitcher } from "./topic-switcher";
import { ExtractedTypeTable, TypeTable } from "./type-table";

/**
* Default MDX component adapter map. Spread this into your MDXProvider (or
* framework-specific equivalent) and override individual entries with your
* own styled components:
*
* import { mdxComponents } from "@inth/docs";
* import { MyCallout } from "./my-callout";
*
* const components = { ...mdxComponents, Callout: MyCallout };
*/
export const mdxComponents = {
Accordion,
AccordionItem,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { ReactNode } from "react";

export type MermaidProps = {
export interface MermaidProps {
chart?: string;
children?: ReactNode;
};
}

/**
* Placeholder Mermaid renderer. Emits a `<pre data-mermaid>` block so
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@

import { type ReactNode, useId, useState } from "react";

export type SelectorOption = {
value: string;
export interface SelectorOption {
label: string;
};
value: string;
}

export type SelectorProps = {
export interface SelectorProps {
children?: (activeValue: string) => ReactNode;
defaultValue?: string;
label?: string;
options: SelectorOption[];
defaultValue?: string;
children?: (activeValue: string) => ReactNode;
};
}

/**
* Minimal dropdown-style selector. Consumers typically replace this with
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import {
useState,
} from "react";

type TabsContextValue = {
items: string[];
interface TabsContextValue {
activeValue: string;
setActiveValue: (value: string) => void;
groupId: string;
};
items: string[];
setActiveValue: (value: string) => void;
}

const TabsContext = createContext<TabsContextValue | null>(null);

Expand Down Expand Up @@ -44,11 +44,11 @@ function panelId(groupId: string, normalized: string, index: number): string {
return `${groupId}-panel-${normalized}-${index}`;
}

export type TabsProps = {
items?: string[];
defaultIndex?: number;
export interface TabsProps {
children?: ReactNode;
};
defaultIndex?: number;
items?: string[];
}

export function Tabs({ items = [], defaultIndex = 0, children }: TabsProps) {
const initial = items[defaultIndex] ?? items[0] ?? "";
Expand Down Expand Up @@ -126,10 +126,10 @@ export function Tabs({ items = [], defaultIndex = 0, children }: TabsProps) {
);
}

export type TabProps = {
value: string;
export interface TabProps {
children?: ReactNode;
};
value: string;
}

export function Tab({ value, children }: TabProps) {
const { items, activeValue, groupId } = useTabsContext();
Expand Down
Loading
Loading