docs/docs-api.md
Goal: Migrate the existing API doc to the new format.
Return Format: Follow the existing pattern with:
<> if it's a component name (e.g. <Button>).<APIReturns> to avoid redundancy.<API name="sectionName"> if missing.API Components:
<APIState> - For state-related sections (e.g. store state)<APIProps> - For component props<APIAttributes> - For general attributes/properties<APIMethods> - For methods documentation<APIListAPI> - For plugin API documentation<APITransforms> - For transform functions<APIParameters> - For function parameters<APIOptions> - For options objects (replaces APISubList for options)<APIReturns type="ReturnType"> - For return values (must include type prop)Rules:
Always wrap API sections in <API name="SectionName">
Use appropriate API component based on content type
For parameters and options:
<APIOptions> directly under <API> (no <APIParameters> wrapper)<APIParameters>options, add/move <APIOptions> and its children as a sibling to <APIParameters>, never nest <APIOptions> inside <APIParameters>, and convert all its <APISubListItem> to <APIItem>, removing parent prop (when converting to <APIOptions> > <APIItem>)For return values:
type prop in <APIReturns><APIReturns> if return type is void/undefinedExample of correct parameter/options structure:
// Single parameter with options
<API name="method">
<APIOptions type="object">
<APIItem name="setting1" type="boolean" optional>
First setting description
</APIItem>
<APIItem name="setting2" type="string" optional>
Second setting description
</APIItem>
</APIOptions>
</API>
// Multiple parameters, one with options
<API name="method">
<APIParameters>
<APIItem name="path" type="Path">
The path to transform.
</APIItem>
<APIItem name="options" type="MethodOptions" optional>
Options for the method.
</APIItem>
</APIParameters>
<APIOptions type="MethodOptions">
<APIItem name="setting1" type="boolean" optional>
First setting description
</APIItem>
</APIOptions>
</API>
<APIItem>
<description>
- **Default:** `true`
</APIItem>
For parameters:
<APIItem> if only one parameter<APIParameters> + <APIOptions> e.g. for (path, options) => void pattern<APISubList> for other nested object parametersKeep code examples minimal and only include if:
Complete Example:
transformTransform a path by an operation.
// Transform a path by an insert operation
path.transform([0, 1], {
type: 'insert_node',
path: [0],
node: { type: 'paragraph' },
});
// Transform with affinity
path.transform([0, 2], op, { affinity: 'forward' });
Warnings:
type Item = {
children: ReactNode;
name: string;
type: string;
default?: boolean | string;
description?: string;
optional?: boolean;
required?: boolean;
value?: string;
};
const APIContext = createContext<{ listType?: string; name?: string }>({});
const listTypeToId: any = {
attributes: 'attrs',
options: 'opt',
parameters: 'params',
props: 'props',
returns: 'returns',
state: 'state',
};
export function API({ children, name }: { children: ReactNode; name: string }) {
return <APIContext.Provider value={{ name }}>{children}</APIContext.Provider>;
}
export function APIItem({
children,
name,
optional,
required,
type,
value,
}: Item) {
const { listType, name: contextName } = React.useContext(APIContext);
const id = contextName
? `${contextName}-${listType ? `${listTypeToId[listType]}-` : ''}${name}`
.toLowerCase()
.replace(/[^\da-z]+/g, '-')
.replace(/^-|-$/g, '')
: undefined;
return (
<AccordionItem className="select-text" value={value ?? name}>
<AccordionTrigger className="group hover:no-underline">
<li id={id} className="scroll-mt-20">
<h4 className="relative py-2 text-start font-semibold leading-none tracking-tight">
{id && (
<a
className={cn(
'opacity-0 hover:opacity-100 group-hover:opacity-100'
)}
onClick={(e) => e.stopPropagation()}
href={`#${id}`}
>
<div className="absolute -left-5 top-2 pr-1 leading-none">
<Icons.pragma className="text-muted-foreground size-4" />
</div>
</a>
)}
<span className="font-mono font-semibold leading-none group-hover:underline">
{name}
</span>
{required && (
<span className="font-mono text-xs leading-none text-orange-500">
{' '}
REQUIRED
</span>
)}
<span className="text-muted-foreground text-left font-mono text-sm font-medium leading-none">
{!required && optional && ' optional'} {type}
</span>
</h4>
</li>
</AccordionTrigger>
<AccordionContent>{children}</AccordionContent>
</AccordionItem>
);
}
export function APIAttributes({ children, ...props }: APIListProps) {
return (
<APIList listType="attributes" {...props}>
{children}
</APIList>
);
}
export function APIOptions({ children, ...props }: APIListProps) {
return (
<APIList listType="options" {...props}>
{children}
</APIList>
);
}
export function APIProps({ children, ...props }: APIListProps) {
return (
<APIList listType="props" {...props}>
{children}
</APIList>
);
}
export function APIState({ children, ...props }: APIListProps) {
return (
<APIList listType="state" {...props}>
{children}
</APIList>
);
}
export function APIReturns({ children, ...props }: APIListProps) {
return (
<APIList listType="returns" {...props}>
{children}
</APIList>
);
}
export function APIParameters({ children, ...props }: APIListProps) {
return (
<APIList listType="parameters" {...props}>
{children}
</APIList>
);
}
type APIListProps = {
children: ReactNode;
collapsed?: boolean;
listType?: string;
type?: string;
};
export function APIList({
children,
collapsed = false,
listType = 'parameters',
type,
}: APIListProps) {
const { name } = React.useContext(APIContext);
const childCount = React.Children.count(children);
const hasItems = React.Children.toArray(children).some(
(child) => (child as any)?.type?.name === 'APIItem'
);
const newValues = Array.from(Array.from({ length: childCount }).keys()).map(
(i) => i.toString()
);
const defaultValues = collapsed ? [] : newValues;
const [values, setValues] = useState<string[]>(defaultValues);
const [expanded, setExpanded] = useState(!collapsed);
if (listType === 'returns' && !childCount) return null;
const id = name ? `${name}-${listTypeToId[listType]}` : undefined;
return (
<APIContext.Provider value={{ listType, name }}>
<section className="flex w-full flex-col items-center">
<div className="w-full">
<div className="mt-10 pb-3">
<div className="mt-5 flex items-center justify-between pb-4">
<h3
id={id}
className="group relative scroll-mt-20 text-lg font-medium leading-none tracking-tight"
>
{id && (
<a
className={cn(
'opacity-0 hover:opacity-100 group-hover:opacity-100'
)}
onClick={(e) => e.stopPropagation()}
href={`#${id}`}
>
<div className="absolute -left-5 top-0 pr-1 leading-none">
<Icons.pragma className="text-muted-foreground size-4" />
</div>
</a>
)}
{listType === 'parameters' && 'Parameters'}
{listType === 'attributes' && 'Attributes'}
{listType === 'returns' && 'Returns'}
{listType === 'props' && 'Props'}
{listType === 'state' && 'State'}
{listType === 'options' && 'Options'}
{type && (
<span className="text-muted-foreground ml-2 font-mono text-sm font-medium">
{type}
</span>
)}
</h3>
{hasItems && (
<div
className="text-muted-foreground cursor-pointer select-none text-sm"
onClick={() => {
values.length === childCount
? setValues([])
: setValues(newValues);
setExpanded(!expanded);
}}
>
{values.length === childCount ? 'Collapse all' : 'Expand all'}
</div>
)}
</div>
<ul className="m-0 list-none p-0">
<Separator />
{hasItems ? (
<Accordion
className="w-full"
value={values}
onValueChange={setValues}
type="multiple"
>
{React.Children.map(children, (child, i) => {
return React.cloneElement(child as any, {
className: 'pt-4',
value: i.toString(),
});
})}
</Accordion>
) : childCount > 0 ? (
children
) : (
<div className="text-muted-foreground py-4 text-sm">
No parameters.
</div>
)}
</ul>
</div>
</div>
</section>
</APIContext.Provider>
);
}
export function APISubListItem({
children,
name,
optional,
parent,
required,
type,
}: {
children: ReactNode;
name: string;
parent: string;
type: string;
optional?: boolean;
required?: boolean;
}) {
const { listType, name: contextName } = React.useContext(APIContext);
const id = contextName
? `${contextName}-${listType ? `${listTypeToId[listType]}-` : ''}${parent}-${name}`
.toLowerCase()
.replace(/[^\da-z]+/g, '-')
.replace(/^-|-$/g, '')
: undefined;
return (
<div className="border-t-border border-t p-3">
<h4 className="relative py-2 font-mono font-semibold tracking-tight">
{id && (
<a
className={cn(
'opacity-0 hover:opacity-100 group-hover:opacity-100'
)}
onClick={(e) => e.stopPropagation()}
href={`#${id}`}
>
<div className="absolute -left-5 top-2 pr-1 leading-none">
<Icons.pragma className="text-muted-foreground size-4" />
</div>
</a>
)}
<span className="text-muted-foreground font-semibold leading-none">
{parent}.
</span>
<span className="font-semibold leading-none">{name}</span>
{required && (
<span className="ml-1 font-mono text-xs leading-none text-orange-500">
{' '}
REQUIRED
</span>
)}
<span className="text-muted-foreground text-left font-mono text-sm font-medium leading-none group-hover:no-underline">
{!required && optional && ' optional'} {type}
</span>
</h4>
<div>{children}</div>
</div>
);
}
export function APISubList({
children,
open,
}: {
children: ReactNode;
open?: boolean;
}) {
const [value, setValue] = useState(open ? '1' : '');
return (
<Card className="my-2">
<Accordion
className="w-full"
defaultValue={open ? '1' : ''}
onValueChange={setValue}
type="single"
collapsible
>
<AccordionItem className="border-none" value="1">
<AccordionTrigger className="group px-3" iconVariant="plus">
{value ? 'Hide' : 'Show'} child attributes
</AccordionTrigger>
<AccordionContent>{children}</AccordionContent>
</AccordionItem>
</Accordion>
</Card>
);
}