feat(www): restore native API reference

This commit is contained in:
Dax Raad 2026-08-24 15:21:26 -04:00
parent 575bbd6ea1
commit 563943c52e
7 changed files with 907 additions and 25 deletions

View file

@ -0,0 +1,151 @@
---
import openapi from "../../../openapi.json"
import type { OpenApiOperation, OpenApiSchema } from "../lib/openapi"
import { openApiSchemaLabel } from "../lib/openapi"
import OpenApiSchemaView from "./OpenApiSchema.astro"
const methods = ["get", "post", "put", "patch", "delete", "options", "head"] as const
type Method = (typeof methods)[number]
const paths = openapi.paths as unknown as Record<string, Partial<Record<Method, OpenApiOperation>>>
const operations = Object.entries(paths).flatMap(([path, item]) =>
methods.flatMap((method) => {
const operation = item[method]
if (!operation) return []
return [{ ...operation, path, method, id: operation.operationId ?? `${method}-${path}`.replace(/[^a-z0-9]+/gi, "-") }]
}),
)
const groups = [...new Set(operations.map((operation) => operation.tags?.[0] ?? "other"))].map((tag) => ({
tag,
id: `tag-${tag.replace(/[^a-z0-9]+/gi, "-").toLowerCase()}`,
description: openapi.tags.find((item) => item.name === tag)?.description,
operations: operations.filter((operation) => (operation.tags?.[0] ?? "other") === tag),
}))
const schemas = Object.entries(openapi.components.schemas) as [string, OpenApiSchema][]
---
<div class="api-reference">
<nav class="api-navigation" aria-label="API endpoints">
<strong>API reference</strong>
<a class="api-navigation-overview" href="#overview">Overview</a>
{
groups.map((group) => (
<details class="api-navigation-group">
<summary><span>{group.tag}</span><small>{group.operations.length}</small></summary>
{group.operations.map((operation) => (
<a href={`#${operation.id}`}>
<span class:list={["api-navigation-method", `api-navigation-method-${operation.method}`]}>{operation.method}</span>
<span>{operation.summary}</span>
</a>
))}
</details>
))
}
<a class="api-navigation-overview" href="#schemas"><span>Schemas</span><small>{schemas.length}</small></a>
</nav>
<article class="api-content" id="overview" data-pagefind-body data-pagefind-meta="title:API">
<header class="api-intro">
<p class="api-eyebrow">OpenAPI {openapi.openapi}</p>
<h1>HTTP API</h1>
<p>{openapi.info.description}</p>
<div class="api-meta">
<span>{operations.length} operations</span>
<span>{schemas.length} schemas</span>
<a href={`${import.meta.env.BASE_URL}openapi.json`}>OpenAPI JSON</a>
</div>
</header>
{
groups.map((group) => (
<section class="api-group" id={group.id}>
<header>
<p class="api-eyebrow">Resource</p>
<h2>{group.tag}</h2>
{group.description && <p>{group.description}</p>}
</header>
<div class="api-operations">
{group.operations.map((operation) => (
<details class="api-operation" id={operation.id}>
<summary>
<span class:list={["api-method", `api-method-${operation.method}`]}>{operation.method}</span>
<code>{operation.path}</code>
<span>{operation.summary}</span>
</summary>
<div class="api-operation-body">
{operation.description && <p>{operation.description}</p>}
<p class="api-operation-id"><span>Operation ID</span><code>{operation.operationId}</code></p>
{!!operation.parameters?.length && (
<section class="api-block">
<h3>Parameters</h3>
<div class="api-table-scroll">
<table>
<thead><tr><th>Name</th><th>Location</th><th>Type</th><th>Description</th></tr></thead>
<tbody>
{operation.parameters.map((parameter) => (
<tr>
<td><code>{parameter.name}</code>{parameter.required && <span class="api-required">required</span>}</td>
<td>{parameter.in}</td>
<td><OpenApiSchemaView schema={parameter.schema} /></td>
<td>{parameter.description ?? <span class="api-muted">No description</span>}</td>
</tr>
))}
</tbody>
</table>
</div>
</section>
)}
{operation.requestBody && (
<section class="api-block">
<h3>Request body {operation.requestBody.required && <span class="api-required">required</span>}</h3>
{operation.requestBody.description && <p>{operation.requestBody.description}</p>}
<div class="api-content-schemas">
{Object.entries(operation.requestBody.content ?? {}).map(([mediaType, content]) => (
<div><code class="api-media-type">{mediaType}</code><OpenApiSchemaView schema={content.schema} /></div>
))}
</div>
</section>
)}
<section class="api-block">
<h3>Responses</h3>
<div class="api-responses">
{Object.entries(operation.responses ?? {}).map(([status, response]) => (
<div class="api-response">
<div class="api-response-heading">
<code class:list={["api-status", status.startsWith("2") && "api-status-success"]}>{status}</code>
<span>{response.description ?? "Response"}</span>
</div>
{Object.entries(response.content ?? {}).map(([mediaType, content]) => (
<div class="api-response-schema"><code class="api-media-type">{mediaType}</code><OpenApiSchemaView schema={content.schema} /></div>
))}
</div>
))}
</div>
</section>
</div>
</details>
))}
</div>
</section>
))
}
<section class="api-group api-schemas" id="schemas">
<header>
<p class="api-eyebrow">Components</p>
<h2>Schemas</h2>
<p>Reusable data types referenced by API operations.</p>
</header>
<div class="api-operations">
{
schemas.map(([name, schema]) => (
<details class="api-schema" id={`schema-${name}`}>
<summary><code>{name}</code><span>{openApiSchemaLabel(schema)}</span></summary>
<div class="api-schema-body"><OpenApiSchemaView schema={schema} /></div>
</details>
))
}
</div>
</section>
</article>
</div>

View file

@ -0,0 +1,69 @@
---
import { formatOpenApiValue, openApiSchemaLabel, type OpenApiSchema } from "../lib/openapi"
interface Props {
schema?: OpenApiSchema
required?: boolean
}
const schema = Astro.props.schema
const reference = schema?.$ref?.split("/").at(-1)
const variants = schema?.anyOf ?? schema?.oneOf ?? schema?.allOf
const variantOperator = schema?.allOf ? "and" : "or"
const constraints = schema
? [
schema.pattern && `pattern ${schema.pattern}`,
schema.minimum !== undefined && `min ${schema.minimum}`,
schema.maximum !== undefined && `max ${schema.maximum}`,
schema.exclusiveMinimum !== undefined && `> ${schema.exclusiveMinimum}`,
schema.exclusiveMaximum !== undefined && `< ${schema.exclusiveMaximum}`,
schema.minLength !== undefined && `min length ${schema.minLength}`,
schema.maxLength !== undefined && `max length ${schema.maxLength}`,
schema.minItems !== undefined && `min items ${schema.minItems}`,
schema.maxItems !== undefined && `max items ${schema.maxItems}`,
].filter((value): value is string => typeof value === "string")
: []
---
{
reference ? (
<a class="api-schema-link" href={`#schema-${reference}`}>{reference}</a>
) : (
<div class="api-schema-node">
<div class="api-schema-heading">
<code>{openApiSchemaLabel(schema)}</code>
{Astro.props.required && <span class="api-required">required</span>}
{schema?.deprecated && <span class="api-deprecated">deprecated</span>}
</div>
{schema?.description && <p>{schema.description}</p>}
{schema?.enum && <div class="api-schema-values"><span>Values</span><code>{schema.enum.map(formatOpenApiValue).join(" | ")}</code></div>}
{schema?.const !== undefined && <div class="api-schema-values"><span>Constant</span><code>{formatOpenApiValue(schema.const)}</code></div>}
{schema?.default !== undefined && <div class="api-schema-values"><span>Default</span><code>{formatOpenApiValue(schema.default)}</code></div>}
{constraints.length > 0 && <div class="api-schema-constraints">{constraints.join(", ")}</div>}
{variants && (
<div class="api-schema-variants">
{variants.map((variant, index) => (
<div>
{index > 0 && <span class="api-schema-operator">{variantOperator}</span>}
<Astro.self schema={variant} />
</div>
))}
</div>
)}
{schema?.type === "array" && schema.items && <div class="api-schema-items"><span>Items</span><Astro.self schema={schema.items} /></div>}
{schema?.properties && (
<div class="api-schema-properties">
{Object.entries(schema.properties).map(([name, property]) => (
<div class="api-schema-property">
<code>{name}</code>
<Astro.self schema={property} required={schema.required?.includes(name)} />
</div>
))}
</div>
)}
{typeof schema?.additionalProperties === "object" && (
<div class="api-schema-items"><span>Additional properties</span><Astro.self schema={schema.additionalProperties} /></div>
)}
</div>
)
}

View file

@ -13,9 +13,11 @@ interface Props {
currentSlug: string
headings: MarkdownHeading[]
showTableOfContents?: boolean
fullWidth?: boolean
}
const section = getDocsSection(Astro.props.currentSlug)
const fullWidth = Astro.props.fullWidth ?? false
const description = Astro.props.description ?? `${Astro.props.title} documentation for OpenCode.`
const canonical = new URL(Astro.url.pathname, "https://opencode.ai").href
---
@ -35,24 +37,34 @@ const canonical = new URL(Astro.url.pathname, "https://opencode.ai").href
<body>
<a class="skip-link" href="#docs-content">Skip to content</a>
<DocsHeader currentSlug={Astro.props.currentSlug} />
<details class="mobile-navigation">
<summary>{section.title} navigation</summary>
<DocsSidebar groups={section.groups} currentSlug={Astro.props.currentSlug} />
</details>
<div class="docs-frame">
<aside class="sidebar-column">
<DocsSidebar groups={section.groups} currentSlug={Astro.props.currentSlug} />
</aside>
<main id="docs-content">
<article class="prose" data-pagefind-body data-pagefind-meta={`title:${Astro.props.title}`}>
<h1>{Astro.props.title}</h1>
{
fullWidth ? (
<main id="docs-content" class="full-width-content">
<slot />
</article>
</main>
<aside class="toc-column">
{Astro.props.showTableOfContents !== false && <DocsTableOfContents headings={Astro.props.headings} />}
</aside>
</div>
</main>
) : (
<>
<details class="mobile-navigation">
<summary>{section.title} navigation</summary>
<DocsSidebar groups={section.groups} currentSlug={Astro.props.currentSlug} />
</details>
<div class="docs-frame">
<aside class="sidebar-column">
<DocsSidebar groups={section.groups} currentSlug={Astro.props.currentSlug} />
</aside>
<main id="docs-content">
<article class="prose" data-pagefind-body data-pagefind-meta={`title:${Astro.props.title}`}>
<h1>{Astro.props.title}</h1>
<slot />
</article>
</main>
<aside class="toc-column">
{Astro.props.showTableOfContents !== false && <DocsTableOfContents headings={Astro.props.headings} />}
</aside>
</div>
</>
)
}
<script is:inline>
const addHeadingAnchors = () => {
for (const heading of document.querySelectorAll(".prose :is(h2, h3)[id]")) {

View file

@ -88,12 +88,11 @@ export const docsSections: DocsSection[] = [
landingSlug: "build",
groups: [
{
title: "Build",
items: [
{ title: "SDK", slug: "build/sdk" },
{ title: "Build", slug: "build" },
{ title: "Client", slug: "build/client" },
{ title: "Plugins", slug: "build/plugins" },
{ title: "Client", slug: "build/client" },
{ title: "SDK", slug: "build/sdk" },
],
},
],

View file

@ -0,0 +1,67 @@
export type OpenApiSchema = {
$ref?: string
type?: string | string[]
title?: string
description?: string
format?: string
deprecated?: boolean
enum?: unknown[]
const?: unknown
default?: unknown
anyOf?: OpenApiSchema[]
oneOf?: OpenApiSchema[]
allOf?: OpenApiSchema[]
items?: OpenApiSchema
properties?: Record<string, OpenApiSchema>
required?: string[]
additionalProperties?: boolean | OpenApiSchema
pattern?: string
minimum?: number
maximum?: number
exclusiveMinimum?: number
exclusiveMaximum?: number
minLength?: number
maxLength?: number
minItems?: number
maxItems?: number
}
export type OpenApiContent = Record<string, { schema?: OpenApiSchema }>
export type OpenApiParameter = {
name: string
in: string
description?: string
required?: boolean
schema?: OpenApiSchema
}
export type OpenApiOperation = {
tags?: string[]
operationId?: string
summary?: string
description?: string
parameters?: OpenApiParameter[]
requestBody?: {
description?: string
required?: boolean
content?: OpenApiContent
}
responses?: Record<string, { description?: string; content?: OpenApiContent }>
}
export function openApiSchemaLabel(schema?: OpenApiSchema): string {
if (!schema) return "empty"
if (schema.$ref) return schema.$ref.split("/").at(-1) ?? schema.$ref
if (schema.enum) return schema.enum.map(formatOpenApiValue).join(" | ")
if (schema.anyOf) return schema.anyOf.map(openApiSchemaLabel).join(" | ")
if (schema.oneOf) return schema.oneOf.map(openApiSchemaLabel).join(" | ")
if (schema.allOf) return schema.allOf.map(openApiSchemaLabel).join(" & ")
const type = Array.isArray(schema.type) ? schema.type.join(" | ") : (schema.type ?? "object")
if (type === "array") return `${openApiSchemaLabel(schema.items)}[]`
return schema.format ? `${type}<${schema.format}>` : type
}
export function formatOpenApiValue(value: unknown) {
return typeof value === "string" ? JSON.stringify(value) : JSON.stringify(value) ?? String(value)
}

View file

@ -47,6 +47,18 @@
* {
box-sizing: border-box;
scrollbar-color: var(--border) transparent;
scrollbar-width: thin;
}
*::-webkit-scrollbar {
width: 6px;
height: 6px;
}
*::-webkit-scrollbar-thumb {
border-radius: 999px;
background: var(--border);
}
::view-transition-group(*),
@ -244,6 +256,502 @@ main {
padding: 3rem clamp(2rem, 4vw, 4rem) 5rem;
}
.full-width-content {
min-height: calc(100vh - var(--header-height));
padding: 0;
}
.api-reference {
display: grid;
width: min(100%, var(--frame-width));
min-height: calc(100vh - var(--header-height));
margin-inline: auto;
grid-template-columns: 17rem minmax(0, 1fr);
}
.api-navigation {
position: sticky;
top: var(--header-height);
height: calc(100vh - var(--header-height));
padding: 2rem var(--gutter) 3rem;
border-right: 1px solid var(--border);
overflow-y: auto;
}
.api-navigation > strong {
display: block;
margin-bottom: 1rem;
font-size: 0.75rem;
letter-spacing: 0.04em;
text-transform: uppercase;
}
.api-navigation-overview,
.api-navigation-group summary {
display: flex;
width: 100%;
padding: 0.375rem 0;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
}
.api-navigation-overview:hover,
.api-navigation-group summary:hover {
color: var(--foreground);
text-decoration: none;
}
.api-navigation-overview small,
.api-navigation-group small {
color: var(--muted);
font: inherit;
font-size: 0.75rem;
}
.api-navigation-group {
color: var(--muted);
}
.api-navigation-group summary {
cursor: pointer;
list-style: none;
}
.api-navigation-group summary::-webkit-details-marker,
.api-operation > summary::-webkit-details-marker,
.api-schema > summary::-webkit-details-marker {
display: none;
}
.api-navigation-group[open] summary {
color: var(--foreground);
font-weight: 700;
}
.api-navigation-group > a {
display: grid;
padding: 0.25rem 0 0.25rem 0.625rem;
grid-template-columns: 2.75rem minmax(0, 1fr);
align-items: baseline;
gap: 0.5rem;
color: var(--muted);
font-size: 0.75rem;
}
.api-navigation-group > a:hover {
color: var(--foreground);
text-decoration: none;
}
.api-navigation-group > a > span:last-child {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.api-navigation-method {
font-size: 0.6875rem;
font-weight: 700;
text-transform: uppercase;
}
.api-navigation-method-get {
color: #73a7ff;
}
.api-navigation-method-post {
color: #70c997;
}
.api-navigation-method-put,
.api-navigation-method-patch {
color: #e0af68;
}
.api-navigation-method-delete {
color: #e67c7c;
}
.api-content {
min-width: 0;
padding: 4rem clamp(2rem, 5vw, 5rem) 7rem;
}
.api-intro,
.api-group {
max-width: 58rem;
margin-inline: auto;
}
.api-intro {
padding-bottom: 4rem;
border-bottom: 1px solid var(--border);
}
.api-eyebrow {
margin: 0 0 0.625rem;
color: var(--muted);
font-size: 0.75rem;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.api-intro h1,
.api-group h2 {
margin: 0;
font-weight: 700;
line-height: 1.2;
}
.api-intro h1 {
font-size: clamp(2rem, 5vw, 3.25rem);
}
.api-intro > p:not(.api-eyebrow),
.api-group > header > p:not(.api-eyebrow) {
max-width: 46rem;
margin: 1rem 0 0;
color: var(--muted);
}
.api-meta {
display: flex;
margin-top: 2rem;
flex-wrap: wrap;
gap: 0.625rem;
}
.api-meta > * {
padding: 0.375rem 0.625rem;
border: 1px solid var(--border);
color: var(--muted);
font-size: 0.75rem;
}
.api-meta a {
color: var(--link);
}
.api-group {
padding-top: 4.5rem;
scroll-margin-top: calc(var(--header-height) + 1rem);
}
.api-group h2 {
font-size: 1.5rem;
text-transform: capitalize;
}
.api-operations {
margin-top: 1.5rem;
border-top: 1px solid var(--border);
}
.api-operation,
.api-schema {
border-bottom: 1px solid var(--border);
scroll-margin-top: calc(var(--header-height) + 1rem);
}
.api-operation:target,
.api-schema:target {
background: var(--surface);
outline: 1px solid var(--border);
}
.api-operation > summary,
.api-schema > summary {
position: relative;
display: grid;
min-height: 4rem;
padding: 0.875rem 2.5rem 0.875rem 0.75rem;
cursor: pointer;
list-style: none;
align-items: center;
gap: 1rem;
}
.api-operation > summary {
grid-template-columns: 4.25rem minmax(11rem, 1fr) minmax(11rem, 1fr);
}
.api-operation > summary::after,
.api-schema > summary::after {
position: absolute;
right: 0.75rem;
content: "+";
color: var(--muted);
}
.api-operation[open] > summary::after,
.api-schema[open] > summary::after {
content: "-";
}
.api-operation > summary:hover,
.api-schema > summary:hover {
background: var(--surface);
}
.api-operation > summary > code {
min-width: 0;
overflow-wrap: anywhere;
}
.api-operation > summary > span:last-of-type,
.api-schema > summary > span {
color: var(--muted);
}
.api-method {
display: inline-flex;
width: 4.25rem;
height: 1.625rem;
border: 1px solid currentColor;
align-items: center;
justify-content: center;
font-size: 0.6875rem;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
}
.api-method-get {
color: #73a7ff;
}
.api-method-post {
color: #70c997;
}
.api-method-put,
.api-method-patch {
color: #e0af68;
}
.api-method-delete {
color: #e67c7c;
}
.api-operation-body,
.api-schema-body {
padding: 1.5rem 1.25rem 2rem;
border-top: 1px solid var(--border);
background: color-mix(in srgb, var(--surface) 55%, transparent);
}
.api-operation-body > p {
margin: 0 0 1.5rem;
}
.api-operation-id {
display: flex;
align-items: baseline;
gap: 0.75rem;
}
.api-operation-id span,
.api-media-type,
.api-schema-items > span,
.api-schema-values > span {
color: var(--muted);
font-size: 0.75rem;
}
.api-block {
margin-top: 2rem;
}
.api-block h3 {
margin: 0 0 0.75rem;
font-size: 0.875rem;
text-transform: uppercase;
}
.api-block > p {
margin: 0 0 1rem;
color: var(--muted);
}
.api-table-scroll {
overflow-x: auto;
}
.api-table-scroll table {
width: 100%;
border-collapse: collapse;
font-size: 0.8125rem;
}
.api-table-scroll th,
.api-table-scroll td {
min-width: 7rem;
padding: 0.625rem 1rem 0.625rem 0;
border-bottom: 1px solid var(--border);
text-align: left;
vertical-align: top;
}
.api-table-scroll th {
color: var(--muted);
font-size: 0.75rem;
font-weight: 400;
}
.api-required,
.api-deprecated {
display: inline-block;
margin-left: 0.5rem;
color: #e0af68;
font-size: 0.6875rem;
font-weight: 400;
text-transform: uppercase;
}
.api-deprecated {
color: #e67c7c;
}
.api-muted {
color: var(--muted);
}
.api-content-schemas > div,
.api-response-schema {
display: grid;
grid-template-columns: minmax(8rem, auto) minmax(0, 1fr);
align-items: start;
gap: 1rem;
}
.api-content-schemas > div + div,
.api-response-schema + .api-response-schema {
margin-top: 0.75rem;
}
.api-responses {
border-top: 1px solid var(--border);
}
.api-response {
padding: 0.75rem 0;
border-bottom: 1px solid var(--border);
}
.api-response-heading {
display: grid;
grid-template-columns: 3.5rem minmax(0, 1fr);
align-items: baseline;
gap: 0.75rem;
}
.api-response-schema {
margin: 0.625rem 0 0 4.25rem;
}
.api-status {
color: #e0af68;
font-weight: 700;
}
.api-status-success {
color: #70c997;
}
.api-schema-link {
color: var(--link);
text-decoration: underline;
text-decoration-color: color-mix(in srgb, var(--link) 45%, transparent);
text-underline-offset: 0.15em;
}
.api-schema-node {
min-width: 0;
}
.api-schema-heading {
display: flex;
align-items: baseline;
flex-wrap: wrap;
}
.api-schema-node > p {
margin: 0.375rem 0 0;
color: var(--muted);
font-size: 0.8125rem;
}
.api-schema-values,
.api-schema-items {
display: grid;
margin-top: 0.5rem;
grid-template-columns: 7rem minmax(0, 1fr);
align-items: baseline;
gap: 0.75rem;
}
.api-schema-values code {
overflow-wrap: anywhere;
}
.api-schema-constraints {
margin-top: 0.375rem;
color: var(--muted);
font-size: 0.75rem;
}
.api-schema-variants {
margin-top: 0.625rem;
padding-left: 0.75rem;
border-left: 1px solid var(--border);
}
.api-schema-variants > div {
position: relative;
}
.api-schema-variants > div + div {
margin-top: 0.75rem;
}
.api-schema-operator {
display: block;
margin-bottom: 0.25rem;
color: var(--muted);
font-size: 0.6875rem;
text-transform: uppercase;
}
.api-schema-properties {
margin-top: 0.75rem;
border-top: 1px solid var(--border);
}
.api-schema-property {
display: grid;
padding: 0.625rem 0;
border-bottom: 1px solid var(--border);
grid-template-columns: minmax(8rem, 0.35fr) minmax(0, 1fr);
align-items: start;
gap: 1rem;
}
.api-schema-property > code {
overflow-wrap: anywhere;
}
.api-schema > summary {
grid-template-columns: minmax(12rem, 1fr) minmax(10rem, 1fr);
}
.api-schema > summary > code,
.api-schema > summary > span {
min-width: 0;
overflow-wrap: anywhere;
}
.toc-column {
padding: 3rem var(--gutter) 3rem 2rem;
}
@ -561,6 +1069,18 @@ main {
grid-template-columns: 14rem minmax(0, 1fr);
}
.api-reference {
grid-template-columns: 14rem minmax(0, 1fr);
}
.api-content {
padding-inline: 2rem;
}
.api-operation > summary {
grid-template-columns: 4.25rem minmax(9rem, 1fr) minmax(9rem, 1fr);
}
.toc-column {
display: none;
}
@ -626,4 +1146,70 @@ main {
.docs-card-group {
grid-template-columns: 1fr;
}
.api-reference {
display: block;
}
.api-navigation {
position: relative;
top: auto;
height: auto;
max-height: 20rem;
padding: 1.25rem var(--gutter);
border-right: 0;
border-bottom: 1px solid var(--border);
}
.api-navigation > strong {
margin-bottom: 0.5rem;
}
.api-content {
padding: 2.5rem var(--gutter) 5rem;
}
.api-intro {
padding-bottom: 3rem;
}
.api-group {
padding-top: 3.5rem;
}
.api-operation > summary {
grid-template-columns: 4.25rem minmax(0, 1fr);
gap: 0.625rem 0.75rem;
}
.api-operation > summary > span:last-of-type {
grid-column: 1 / -1;
}
.api-operation-body,
.api-schema-body {
padding-inline: 0.75rem;
}
.api-content-schemas > div,
.api-response-schema,
.api-schema-values,
.api-schema-items {
grid-template-columns: 1fr;
gap: 0.25rem;
}
.api-response-schema {
margin-left: 0;
}
.api-schema-property {
grid-template-columns: 1fr;
gap: 0.375rem;
}
.api-schema > summary {
grid-template-columns: minmax(0, 1fr);
gap: 0.25rem;
}
}

View file

@ -1,4 +1,5 @@
---
import OpenApiReference from "../../../docs/components/OpenApiReference.astro"
import DocsLayout from "../../../docs/layouts/DocsLayout.astro"
export const prerender = true
@ -10,10 +11,7 @@ export const prerender = true
currentSlug="api"
headings={[]}
showTableOfContents={false}
fullWidth
>
<p>
OpenCode exposes an HTTP API from its server. Use <code>opencode2 api</code> for authenticated local requests, or
build against the generated OpenAPI specification.
</p>
<p><a href={`${import.meta.env.BASE_URL}openapi.json`}>View the OpenAPI specification</a>.</p>
<OpenApiReference />
</DocsLayout>