@inferencesh/ui 0.0.0-stage → 0.1.1
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.
- package/LICENSE +22 -0
- package/README.md +68 -2
- package/components.json +22 -0
- package/package.json +231 -4
- package/src/components/audio-player.tsx +549 -0
- package/src/components/button.tsx +13 -0
- package/src/components/chart.tsx +403 -0
- package/src/components/circuit.tsx +276 -0
- package/src/components/code-block/code-block.tsx +260 -0
- package/src/components/code-block/index.ts +18 -0
- package/src/components/code-block/languages.ts +311 -0
- package/src/components/code-block/styles.ts +39 -0
- package/src/components/code-block/tokenizer.ts +290 -0
- package/src/components/code-block/types.ts +38 -0
- package/src/components/code-block/utils.ts +39 -0
- package/src/components/code.tsx +48 -0
- package/src/components/cookie-consent.tsx +137 -0
- package/src/components/copy-button.tsx +44 -0
- package/src/components/country-dropdown.tsx +188 -0
- package/src/components/glow-loader-fast.tsx +32 -0
- package/src/components/glow-loader-mono.tsx +33 -0
- package/src/components/glow-loader-sm-fast.tsx +32 -0
- package/src/components/glow-loader-sm-mono.tsx +34 -0
- package/src/components/glow-loader-sm.tsx +32 -0
- package/src/components/glow-loader.tsx +32 -0
- package/src/components/glow-logo.tsx +32 -0
- package/src/components/gradient.tsx +80 -0
- package/src/components/index.ts +45 -0
- package/src/components/inline-sidebar.tsx +351 -0
- package/src/components/input-color.tsx +349 -0
- package/src/components/logo-base.tsx +213 -0
- package/src/components/logo-color.tsx +23 -0
- package/src/components/logo-full.tsx +71 -0
- package/src/components/logo-svg.tsx +23 -0
- package/src/components/menu-navigation.tsx +359 -0
- package/src/components/multi-select.tsx +721 -0
- package/src/components/pastel-gradient.tsx +25 -0
- package/src/components/pixel-symmetry.tsx +151 -0
- package/src/components/progress.tsx +28 -0
- package/src/components/scroll-more.tsx +131 -0
- package/src/components/select.tsx +85 -0
- package/src/components/shimmering-text.tsx +108 -0
- package/src/components/sidebar-light.tsx +102 -0
- package/src/components/sonner.tsx +38 -0
- package/src/components/theme-switcher-sidebar.tsx +56 -0
- package/src/components/theme-switcher-simple.tsx +110 -0
- package/src/components/theme-switcher.tsx +47 -0
- package/src/components/toggle-group.tsx +55 -0
- package/src/components/tooltip-button.tsx +91 -0
- package/src/components/tooltip.tsx +71 -0
- package/src/components/tree-view.tsx +499 -0
- package/src/components/unglow-loader.tsx +33 -0
- package/src/components/waveform.tsx +1632 -0
- package/src/components/zoomable-image.tsx +76 -0
- package/src/docs/api-docs.tsx +763 -0
- package/src/docs/callout.tsx +160 -0
- package/src/docs/code-group.tsx +215 -0
- package/src/docs/copy-menu.tsx +143 -0
- package/src/docs/doc-tabs.tsx +84 -0
- package/src/docs/index.ts +16 -0
- package/src/docs/steps.tsx +105 -0
- package/src/docs/table-of-contents.tsx +174 -0
- package/src/examples/guide-markdown.ts +129 -0
- package/src/examples/index.ts +1153 -0
- package/src/global.d.ts +1 -0
- package/src/hooks/use-copy-to-clipboard.ts +36 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/lib/display-name.ts +15 -0
- package/src/lib/example-input.ts +130 -0
- package/src/lib/plans.ts +44 -0
- package/src/lib/safe-content.ts +137 -0
- package/src/lib/schema.ts +320 -0
- package/src/lib/storage.ts +108 -0
- package/src/markdown/docs-markdown-renderer.tsx +518 -0
- package/src/markdown/helpers.ts +95 -0
- package/src/markdown/index.ts +24 -0
- package/src/markdown/markdown-renderer.tsx +232 -0
- package/src/markdown/youtube-embed.tsx +43 -0
- package/src/og/image.ts +312 -0
- package/src/og/index.tsx +430 -0
- package/src/og/query.ts +26 -0
- package/src/shadcn/accordion.tsx +71 -0
- package/src/shadcn/alert-dialog.tsx +187 -0
- package/src/shadcn/alert.tsx +75 -0
- package/src/shadcn/aspect-ratio.tsx +22 -0
- package/src/shadcn/attachment.tsx +207 -0
- package/src/shadcn/avatar.tsx +106 -0
- package/src/shadcn/badge.tsx +51 -0
- package/src/shadcn/breadcrumb.tsx +124 -0
- package/src/shadcn/bubble.tsx +127 -0
- package/src/shadcn/button-group.tsx +87 -0
- package/src/shadcn/button.tsx +57 -0
- package/src/shadcn/calendar.tsx +219 -0
- package/src/shadcn/card.tsx +102 -0
- package/src/shadcn/carousel.tsx +242 -0
- package/src/shadcn/chart.tsx +372 -0
- package/src/shadcn/checkbox.tsx +28 -0
- package/src/shadcn/collapsible.tsx +19 -0
- package/src/shadcn/combobox.tsx +297 -0
- package/src/shadcn/command.tsx +194 -0
- package/src/shadcn/context-menu.tsx +270 -0
- package/src/shadcn/dialog.tsx +158 -0
- package/src/shadcn/direction.tsx +4 -0
- package/src/shadcn/drawer.tsx +227 -0
- package/src/shadcn/dropdown-menu.tsx +265 -0
- package/src/shadcn/empty.tsx +103 -0
- package/src/shadcn/field.tsx +238 -0
- package/src/shadcn/form.tsx +173 -0
- package/src/shadcn/hover-card.tsx +50 -0
- package/src/shadcn/index.ts +53 -0
- package/src/shadcn/input-group.tsx +156 -0
- package/src/shadcn/input-otp.tsx +84 -0
- package/src/shadcn/input.tsx +19 -0
- package/src/shadcn/item.tsx +201 -0
- package/src/shadcn/kbd.tsx +26 -0
- package/src/shadcn/label.tsx +19 -0
- package/src/shadcn/marker.tsx +70 -0
- package/src/shadcn/menubar.tsx +278 -0
- package/src/shadcn/message-scroller.tsx +131 -0
- package/src/shadcn/message.tsx +91 -0
- package/src/shadcn/native-select.tsx +60 -0
- package/src/shadcn/navigation-menu.tsx +167 -0
- package/src/shadcn/pagination.tsx +130 -0
- package/src/shadcn/popover.tsx +87 -0
- package/src/shadcn/progress.tsx +82 -0
- package/src/shadcn/radio-group.tsx +35 -0
- package/src/shadcn/resizable.tsx +49 -0
- package/src/shadcn/scroll-area.tsx +52 -0
- package/src/shadcn/select.tsx +200 -0
- package/src/shadcn/separator.tsx +22 -0
- package/src/shadcn/sheet.tsx +136 -0
- package/src/shadcn/sidebar.tsx +721 -0
- package/src/shadcn/skeleton.tsx +13 -0
- package/src/shadcn/slider.tsx +51 -0
- package/src/shadcn/sonner.tsx +49 -0
- package/src/shadcn/spinner.tsx +10 -0
- package/src/shadcn/switch.tsx +29 -0
- package/src/shadcn/table.tsx +115 -0
- package/src/shadcn/tabs.tsx +79 -0
- package/src/shadcn/textarea.tsx +17 -0
- package/src/shadcn/toggle-group.tsx +89 -0
- package/src/shadcn/toggle.tsx +44 -0
- package/src/shadcn/tooltip.tsx +63 -0
- package/src/theme/config.ts +48 -0
- package/src/theme/css/base.css +92 -0
- package/src/theme/css/bitboy.css +75 -0
- package/src/theme/css/dark.css +47 -0
- package/src/theme/css/designer.css +122 -0
- package/src/theme/css/fonts.css +33 -0
- package/src/theme/css/globals.css +14 -0
- package/src/theme/css/hackintosh.css +57 -0
- package/src/theme/css/llama.css +103 -0
- package/src/theme/css/safelist.css +39 -0
- package/src/theme/css/shadcn.css +77 -0
- package/src/theme/css/theme.css +216 -0
- package/src/theme/embed-theme.ts +146 -0
- package/src/theme/fonts/FiraCode-VF.woff2 +0 -0
- package/src/theme/fonts/Hack-Regular.woff2 +0 -0
- package/src/theme/fonts/LICENSE-FiraCode.txt +93 -0
- package/src/theme/fonts/LICENSE-Hack.txt +45 -0
- package/src/theme/fonts/LICENSE-MonaSans.txt +93 -0
- package/src/theme/fonts/Mona-Sans.woff2 +0 -0
- package/src/theme/index.ts +15 -0
- package/src/utils/cn.ts +3 -0
- package/src/utils/index.ts +2 -0
|
@@ -0,0 +1,763 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
4
|
+
import { CodeBlock } from "../components/code-block"
|
|
5
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../components/select"
|
|
6
|
+
import { Separator } from "../shadcn/separator"
|
|
7
|
+
import { TableOfContents, TocItem } from "./table-of-contents"
|
|
8
|
+
import { TaskStatusCancelled, TaskStatusCompleted, TaskStatusFailed, TaskStatusRejected } from '@inferencesh/sdk'
|
|
9
|
+
import { apiKeySetup, generateBackgroundExamples, generateFileExamples, generateResultOnlyExamples, generateStreamingExamples, generateSwiftWebhookExample, installCommands } from '../examples'
|
|
10
|
+
import { exampleInput } from '../lib/example-input'
|
|
11
|
+
import { getFieldsFromSchema, SchemaField, Schema } from '../lib/schema'
|
|
12
|
+
import { cn } from 'cn'
|
|
13
|
+
import { localStore } from '../lib/storage'
|
|
14
|
+
import { Hash } from 'lucide-react'
|
|
15
|
+
|
|
16
|
+
// Types
|
|
17
|
+
export interface ApiDocsApp {
|
|
18
|
+
/** The slug that addresses the app. Stays in refs, URLs and code samples. */
|
|
19
|
+
name: string
|
|
20
|
+
/** The name a person reads, when the app has one. Headings and prose only. */
|
|
21
|
+
title?: string
|
|
22
|
+
namespace?: string
|
|
23
|
+
description?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ApiDocsVersion {
|
|
27
|
+
short_id?: string
|
|
28
|
+
input_schema?: Schema
|
|
29
|
+
output_schema?: Schema
|
|
30
|
+
setup_schema?: Schema
|
|
31
|
+
functions?: Record<string, { name?: string; description?: string; input_schema?: Schema; output_schema?: Schema }>
|
|
32
|
+
default_function?: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface ApiDocsFormData {
|
|
36
|
+
input?: Record<string, unknown>
|
|
37
|
+
setup?: Record<string, unknown>
|
|
38
|
+
infra?: string
|
|
39
|
+
workers?: string[]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface ApiDocsProps {
|
|
43
|
+
/** App metadata */
|
|
44
|
+
app: ApiDocsApp
|
|
45
|
+
/** Version containing schemas and functions */
|
|
46
|
+
version: ApiDocsVersion
|
|
47
|
+
/** Active function name (falls back to version.default_function → "run") */
|
|
48
|
+
functionName?: string | null
|
|
49
|
+
/** Current form data for generating examples */
|
|
50
|
+
formData?: ApiDocsFormData
|
|
51
|
+
/** Optional className */
|
|
52
|
+
className?: string
|
|
53
|
+
/** Link component for routing (defaults to <a>) */
|
|
54
|
+
LinkComponent?: React.ComponentType<{ to: string; className?: string; children: React.ReactNode }>
|
|
55
|
+
/** Base path for docs links (defaults to /docs) */
|
|
56
|
+
docsBasePath?: string
|
|
57
|
+
/** Base path for settings links (defaults to /settings) */
|
|
58
|
+
settingsBasePath?: string
|
|
59
|
+
/** Controlled language (if provided, component is controlled) */
|
|
60
|
+
language?: Language
|
|
61
|
+
/** Callback when language changes */
|
|
62
|
+
onLanguageChange?: (language: Language) => void
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type Language = 'python' | 'typescript' | 'javascript' | 'swift' | 'curl' | 'belt'
|
|
66
|
+
|
|
67
|
+
/** The language a page shows first: plain HTTP works from anywhere. */
|
|
68
|
+
export const DEFAULT_LANGUAGE: Language = 'curl'
|
|
69
|
+
|
|
70
|
+
const LANGUAGE_KEY = 'api-docs-language'
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The page language, remembered per viewer across app pages. Starts at
|
|
74
|
+
* DEFAULT_LANGUAGE so server and first client render agree, then switches
|
|
75
|
+
* to the saved choice.
|
|
76
|
+
*/
|
|
77
|
+
export function useApiDocsLanguage(): [Language, (lang: Language) => void] {
|
|
78
|
+
const [language, setLanguageState] = useState<Language>(DEFAULT_LANGUAGE)
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
const saved = localStore.getItem(LANGUAGE_KEY)
|
|
81
|
+
if (saved && LANGUAGES.some(l => l.value === saved)) setLanguageState(saved as Language)
|
|
82
|
+
}, [])
|
|
83
|
+
const setLanguage = useCallback((lang: Language) => {
|
|
84
|
+
setLanguageState(lang)
|
|
85
|
+
localStore.setItem(LANGUAGE_KEY, lang)
|
|
86
|
+
}, [])
|
|
87
|
+
return [language, setLanguage]
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const LANGUAGES: { value: Language; label: string }[] = [
|
|
91
|
+
{ value: 'curl', label: 'rest (curl)' },
|
|
92
|
+
{ value: 'python', label: 'python' },
|
|
93
|
+
{ value: 'typescript', label: 'typescript' },
|
|
94
|
+
{ value: 'javascript', label: 'javascript' },
|
|
95
|
+
{ value: 'swift', label: 'swift' },
|
|
96
|
+
{ value: 'belt', label: 'belt cli' },
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
// Table of Contents structure (lowercase style)
|
|
100
|
+
function buildTocItems(hasSetupSchema: boolean): TocItem[] {
|
|
101
|
+
const schemaChildren: TocItem[] = [
|
|
102
|
+
{ id: 'schema-input', title: 'input' },
|
|
103
|
+
{ id: 'schema-output', title: 'output' },
|
|
104
|
+
]
|
|
105
|
+
if (hasSetupSchema) {
|
|
106
|
+
schemaChildren.push({ id: 'schema-setup', title: 'setup' })
|
|
107
|
+
}
|
|
108
|
+
return [
|
|
109
|
+
{ id: 'api-key', title: 'get an api key' },
|
|
110
|
+
{
|
|
111
|
+
id: 'api-call',
|
|
112
|
+
title: '1. calling the api',
|
|
113
|
+
children: [
|
|
114
|
+
{ id: 'api-call-install', title: 'install the client' },
|
|
115
|
+
{ id: 'api-call-result', title: 'run and get result' },
|
|
116
|
+
{ id: 'api-call-stream', title: 'stream live updates' },
|
|
117
|
+
{ id: 'api-call-background', title: 'run in the background' },
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
id: 'auth',
|
|
122
|
+
title: '2. authentication',
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
id: 'files',
|
|
126
|
+
title: '3. files',
|
|
127
|
+
children: [
|
|
128
|
+
{ id: 'files-auto-upload', title: 'automatic upload' },
|
|
129
|
+
{ id: 'files-manual', title: 'manual upload' },
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
id: 'webhooks',
|
|
134
|
+
title: '4. webhooks',
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: 'schema',
|
|
138
|
+
title: '5. schema',
|
|
139
|
+
children: schemaChildren,
|
|
140
|
+
},
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// The webhook body: the task result in an event envelope (go/api projector deliverWebhook).
|
|
145
|
+
const WEBHOOK_FIELDS = [
|
|
146
|
+
{ key: 'event', type: 'string', desc: 'task.completed, task.failed or task.cancelled' },
|
|
147
|
+
{ key: 'timestamp', type: 'string', desc: 'iso timestamp of the delivery' },
|
|
148
|
+
{ key: 'data.id', type: 'string', desc: 'task id' },
|
|
149
|
+
{ key: 'data.short_id', type: 'string', desc: 'short task id' },
|
|
150
|
+
{ key: 'data.status', type: 'number', desc: `terminal status (${TaskStatusCompleted}=completed, ${TaskStatusFailed}=failed, ${TaskStatusCancelled}=cancelled, ${TaskStatusRejected}=rejected)` },
|
|
151
|
+
{ key: 'data.status_text', type: 'string', desc: 'the status as a word: completed, failed or cancelled' },
|
|
152
|
+
{ key: 'data.output', type: 'object', desc: 'task output (when completed)' },
|
|
153
|
+
{ key: 'data.error', type: 'string', desc: 'error message (when failed, omitted otherwise)' },
|
|
154
|
+
{ key: 'data.session_id', type: 'string', desc: 'session id (when the task ran in a session, omitted otherwise)' },
|
|
155
|
+
{ key: 'data.created_at', type: 'string', desc: 'iso timestamp' },
|
|
156
|
+
{ key: 'data.updated_at', type: 'string', desc: 'iso timestamp' },
|
|
157
|
+
]
|
|
158
|
+
|
|
159
|
+
// Default link component (plain anchor)
|
|
160
|
+
function DefaultLink({ to, className, children }: { to: string; className?: string; children: React.ReactNode }) {
|
|
161
|
+
return <a href={to} className={className}>{children}</a>
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Helper Components
|
|
165
|
+
function SectionHeading({
|
|
166
|
+
id,
|
|
167
|
+
children,
|
|
168
|
+
level = 2
|
|
169
|
+
}: {
|
|
170
|
+
id: string
|
|
171
|
+
children: React.ReactNode
|
|
172
|
+
level?: 2 | 3
|
|
173
|
+
}) {
|
|
174
|
+
const Tag = level === 2 ? 'h2' : 'h3'
|
|
175
|
+
const textSize = level === 2 ? 'text-lg' : 'text-base'
|
|
176
|
+
|
|
177
|
+
return (
|
|
178
|
+
<Tag
|
|
179
|
+
id={id}
|
|
180
|
+
className={cn("flex flex-row items-center gap-2 pt-2 font-medium text-foreground", textSize)}
|
|
181
|
+
>
|
|
182
|
+
<span>{children}</span>
|
|
183
|
+
<a
|
|
184
|
+
href={`#${id}`}
|
|
185
|
+
className="font-mono font-extralight text-muted-foreground/50 transition-colors hover:text-muted-foreground"
|
|
186
|
+
>
|
|
187
|
+
<Hash className="h-3.5 w-3.5" />
|
|
188
|
+
</a>
|
|
189
|
+
</Tag>
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function SchemaProperty({
|
|
194
|
+
field,
|
|
195
|
+
isOutput = false,
|
|
196
|
+
}: {
|
|
197
|
+
field: SchemaField
|
|
198
|
+
isOutput?: boolean
|
|
199
|
+
}) {
|
|
200
|
+
const resolved = field.resolved || field
|
|
201
|
+
const type = field.type || resolved.type || 'any'
|
|
202
|
+
const description = field.description || resolved.description || field.title
|
|
203
|
+
const defaultValue = field.default ?? resolved.default
|
|
204
|
+
const example = Array.isArray(field.examples) ? field.examples[0] : field.examples
|
|
205
|
+
const resolvedExample = Array.isArray(resolved.examples) ? resolved.examples[0] : resolved.examples
|
|
206
|
+
const exampleValue = example || resolvedExample
|
|
207
|
+
const enumValues = resolved.enum || field.enum
|
|
208
|
+
const minimum = resolved.minimum ?? field.minimum
|
|
209
|
+
const maximum = resolved.maximum ?? field.maximum
|
|
210
|
+
const minLength = resolved.minLength ?? field.minLength
|
|
211
|
+
const maxLength = resolved.maxLength ?? field.maxLength
|
|
212
|
+
const pattern = resolved.pattern || field.pattern
|
|
213
|
+
const format = resolved.format || field.format
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
<div className="border border-border rounded-lg p-3 space-y-2">
|
|
217
|
+
<div className="flex items-center flex-wrap">
|
|
218
|
+
<span className="text-sm font-semibold">
|
|
219
|
+
{field.key}
|
|
220
|
+
</span>
|
|
221
|
+
<span className="text-xs font-mono text-muted-foreground ml-1">
|
|
222
|
+
{type}
|
|
223
|
+
{format && <span className="text-muted-foreground ml-1">({format})</span>}
|
|
224
|
+
</span>
|
|
225
|
+
{field.isRequired && <span className="text-brand-pink ml-1 text-xs">*</span>}
|
|
226
|
+
</div>
|
|
227
|
+
|
|
228
|
+
{description && (
|
|
229
|
+
<p className="text-xs text-muted-foreground">{description.toLowerCase()}</p>
|
|
230
|
+
)}
|
|
231
|
+
|
|
232
|
+
{!isOutput && (
|
|
233
|
+
<>
|
|
234
|
+
<div className="flex flex-wrap gap-2 text-xs">
|
|
235
|
+
{defaultValue !== undefined && (
|
|
236
|
+
<span className="text-muted-foreground">
|
|
237
|
+
default: <span className="text-xs tracking-wider bg-muted px-1 rounded text-foreground">{JSON.stringify(defaultValue)}</span>
|
|
238
|
+
</span>
|
|
239
|
+
)}
|
|
240
|
+
{exampleValue !== undefined && (
|
|
241
|
+
<span className="text-muted-foreground">
|
|
242
|
+
example: <span className="text-xs tracking-wider bg-muted px-1 rounded text-foreground">{JSON.stringify(exampleValue)}</span>
|
|
243
|
+
</span>
|
|
244
|
+
)}
|
|
245
|
+
{minimum !== undefined && (
|
|
246
|
+
<span className="text-muted-foreground">min:
|
|
247
|
+
<span className="text-xs tracking-wider bg-muted px-1 rounded text-foreground">{minimum}</span>
|
|
248
|
+
</span>
|
|
249
|
+
)}
|
|
250
|
+
{maximum !== undefined && (
|
|
251
|
+
<span className="text-muted-foreground">max:
|
|
252
|
+
<span className="text-xs tracking-wider bg-muted px-1 rounded text-foreground">{maximum}</span>
|
|
253
|
+
</span>
|
|
254
|
+
)}
|
|
255
|
+
{minLength !== undefined && (
|
|
256
|
+
<span className="text-muted-foreground">minLength:
|
|
257
|
+
<span className="text-xs tracking-wider bg-muted px-1 rounded text-foreground">{minLength}</span>
|
|
258
|
+
</span>
|
|
259
|
+
)}
|
|
260
|
+
{maxLength !== undefined && (
|
|
261
|
+
<span className="text-muted-foreground">maxLength:
|
|
262
|
+
<span className="text-xs tracking-wider bg-muted px-1 rounded text-foreground">{maxLength}</span>
|
|
263
|
+
</span>
|
|
264
|
+
)}
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
{enumValues && enumValues.length > 0 && (
|
|
268
|
+
<div className="flex flex-wrap gap-1">
|
|
269
|
+
<span className="text-xs text-muted-foreground">options:</span>
|
|
270
|
+
{enumValues.map((v, i) => (
|
|
271
|
+
<span key={i} className="text-xs bg-muted px-1.5 py-0.5 rounded tracking-wider">
|
|
272
|
+
{JSON.stringify(v)}
|
|
273
|
+
</span>
|
|
274
|
+
))}
|
|
275
|
+
</div>
|
|
276
|
+
)}
|
|
277
|
+
|
|
278
|
+
{pattern && (
|
|
279
|
+
<div className="text-xs text-muted-foreground">
|
|
280
|
+
pattern: <span className="bg-muted px-1 rounded">{pattern}</span>
|
|
281
|
+
</div>
|
|
282
|
+
)}
|
|
283
|
+
</>
|
|
284
|
+
)}
|
|
285
|
+
</div>
|
|
286
|
+
)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Language selector for ToC header
|
|
290
|
+
function LanguageSelector({
|
|
291
|
+
language,
|
|
292
|
+
onLanguageChange
|
|
293
|
+
}: {
|
|
294
|
+
language: Language
|
|
295
|
+
onLanguageChange: (lang: Language) => void
|
|
296
|
+
}) {
|
|
297
|
+
return (
|
|
298
|
+
<div className="mb-4">
|
|
299
|
+
<Select value={language} onValueChange={(v) => onLanguageChange(v as Language)}>
|
|
300
|
+
<SelectTrigger className="w-full">
|
|
301
|
+
<SelectValue />
|
|
302
|
+
</SelectTrigger>
|
|
303
|
+
<SelectContent>
|
|
304
|
+
{LANGUAGES.map((lang) => (
|
|
305
|
+
<SelectItem key={lang.value} value={lang.value}>
|
|
306
|
+
{lang.label}
|
|
307
|
+
</SelectItem>
|
|
308
|
+
))}
|
|
309
|
+
</SelectContent>
|
|
310
|
+
</Select>
|
|
311
|
+
</div>
|
|
312
|
+
)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Main Component
|
|
316
|
+
export function ApiDocs({
|
|
317
|
+
app,
|
|
318
|
+
version,
|
|
319
|
+
functionName,
|
|
320
|
+
formData,
|
|
321
|
+
className,
|
|
322
|
+
LinkComponent = DefaultLink,
|
|
323
|
+
docsBasePath = '/docs',
|
|
324
|
+
settingsBasePath = '/settings',
|
|
325
|
+
language: controlledLanguage,
|
|
326
|
+
onLanguageChange,
|
|
327
|
+
}: ApiDocsProps) {
|
|
328
|
+
const [internalLanguage, setInternalLanguage] = useState<Language>(DEFAULT_LANGUAGE)
|
|
329
|
+
const language = controlledLanguage ?? internalLanguage
|
|
330
|
+
const setLanguage = (lang: Language) => {
|
|
331
|
+
if (onLanguageChange) onLanguageChange(lang)
|
|
332
|
+
else setInternalLanguage(lang)
|
|
333
|
+
}
|
|
334
|
+
const Link = LinkComponent
|
|
335
|
+
|
|
336
|
+
// Resolve effective schemas from version + function
|
|
337
|
+
const effectiveFn = functionName || version?.default_function || 'run'
|
|
338
|
+
|
|
339
|
+
// Build app reference — append :function when non-default and multiple functions exist
|
|
340
|
+
// Version is only appended when short_id is provided (caller controls via pinVersion toggle)
|
|
341
|
+
const namespace = app.namespace || ''
|
|
342
|
+
const shortId = version?.short_id
|
|
343
|
+
const defaultFn = version?.default_function || 'run'
|
|
344
|
+
const hasManyFunctions = version?.functions && Object.keys(version.functions).length > 1
|
|
345
|
+
const fnSuffix = (hasManyFunctions && effectiveFn !== defaultFn) ? `:${effectiveFn}` : ''
|
|
346
|
+
const versionSuffix = shortId ? `@${shortId}` : ''
|
|
347
|
+
const appRef = `${namespace}/${app.name}${versionSuffix}${fnSuffix}`
|
|
348
|
+
const func = version?.functions?.[effectiveFn]
|
|
349
|
+
const schema = func?.input_schema || version?.input_schema || {}
|
|
350
|
+
const outputSchema = func?.output_schema || version?.output_schema
|
|
351
|
+
const setupSchema = version?.setup_schema
|
|
352
|
+
|
|
353
|
+
// Check if setup has any actual values
|
|
354
|
+
const hasSetup = formData?.setup && Object.keys(formData.setup).length > 0
|
|
355
|
+
|
|
356
|
+
// Process input with schema defaults. Without form data (a public app page),
|
|
357
|
+
// the examples run the app's own example input, or one built from its schema.
|
|
358
|
+
const processedInput = useMemo(() => {
|
|
359
|
+
const input = formData?.input ?? exampleInput(schema)
|
|
360
|
+
if (!schema?.properties) return input
|
|
361
|
+
|
|
362
|
+
const result: Record<string, unknown> = {}
|
|
363
|
+
for (const [key, value] of Object.entries(input)) {
|
|
364
|
+
if (value === undefined && schema.properties[key]) {
|
|
365
|
+
const prop = schema.properties[key]
|
|
366
|
+
if (prop.default !== undefined) {
|
|
367
|
+
result[key] = prop.default
|
|
368
|
+
} else {
|
|
369
|
+
switch (prop.type) {
|
|
370
|
+
case "string": result[key] = ""; break
|
|
371
|
+
case "number":
|
|
372
|
+
case "integer": result[key] = 0; break
|
|
373
|
+
case "boolean": result[key] = false; break
|
|
374
|
+
case "array": result[key] = []; break
|
|
375
|
+
case "object": result[key] = {}; break
|
|
376
|
+
default: result[key] = value
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
} else {
|
|
380
|
+
result[key] = value
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return result
|
|
384
|
+
}, [formData?.input, schema])
|
|
385
|
+
|
|
386
|
+
// Example params for code generation
|
|
387
|
+
const exampleParams = useMemo(() => ({
|
|
388
|
+
appRef,
|
|
389
|
+
shortId: version?.short_id,
|
|
390
|
+
input: processedInput,
|
|
391
|
+
setup: hasSetup ? formData?.setup : undefined,
|
|
392
|
+
infra: formData?.infra || undefined,
|
|
393
|
+
workers: formData?.infra === 'private' ? (formData?.workers || undefined) : undefined,
|
|
394
|
+
}), [appRef, version?.short_id, processedInput, formData?.setup, hasSetup, formData?.infra, formData?.workers])
|
|
395
|
+
|
|
396
|
+
// Generate separate examples for result-only and streaming
|
|
397
|
+
const resultOnlyExamples = useMemo(() => generateResultOnlyExamples(exampleParams), [exampleParams])
|
|
398
|
+
const streamingExamples = useMemo(() => generateStreamingExamples(exampleParams), [exampleParams])
|
|
399
|
+
|
|
400
|
+
// Get current code for selected language
|
|
401
|
+
const currentResultCode = resultOnlyExamples[language] || ''
|
|
402
|
+
const currentStreamCode = streamingExamples[language] || ''
|
|
403
|
+
const backgroundExamples = useMemo(() => generateBackgroundExamples(exampleParams), [exampleParams])
|
|
404
|
+
const currentBackgroundCode = backgroundExamples[language] || ''
|
|
405
|
+
const fileExamples = useMemo(() => generateFileExamples(appRef), [appRef])
|
|
406
|
+
const currentFileExamples = fileExamples[language] ?? fileExamples.curl
|
|
407
|
+
const currentInstall = installCommands[language]
|
|
408
|
+
const currentKeySetup = apiKeySetup[language]
|
|
409
|
+
|
|
410
|
+
// Parse schema properties for display
|
|
411
|
+
const inputFields = useMemo(() => {
|
|
412
|
+
if (!schema) return []
|
|
413
|
+
const { fields } = getFieldsFromSchema(schema)
|
|
414
|
+
return fields
|
|
415
|
+
}, [schema])
|
|
416
|
+
|
|
417
|
+
const outputFields = useMemo(() => {
|
|
418
|
+
if (!outputSchema) return []
|
|
419
|
+
const { fields } = getFieldsFromSchema(outputSchema)
|
|
420
|
+
return fields
|
|
421
|
+
}, [outputSchema])
|
|
422
|
+
|
|
423
|
+
const setupFields = useMemo(() => {
|
|
424
|
+
if (!setupSchema) return []
|
|
425
|
+
const { fields } = getFieldsFromSchema(setupSchema)
|
|
426
|
+
return fields
|
|
427
|
+
}, [setupSchema])
|
|
428
|
+
|
|
429
|
+
const hasSetupFields = setupFields.length > 0
|
|
430
|
+
|
|
431
|
+
// Build ToC with conditional setup section
|
|
432
|
+
const tocItems = useMemo(() => buildTocItems(hasSetupFields), [hasSetupFields])
|
|
433
|
+
|
|
434
|
+
// Footer link for ToC
|
|
435
|
+
const TocFooter = () => (
|
|
436
|
+
<div className="mt-4 pt-4 border-t border-border">
|
|
437
|
+
<Link
|
|
438
|
+
to={`${docsBasePath}/api/authentication`}
|
|
439
|
+
className="text-xs text-muted-foreground hover:text-foreground transition-colors"
|
|
440
|
+
>
|
|
441
|
+
→ full api documentation
|
|
442
|
+
</Link>
|
|
443
|
+
</div>
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
return (
|
|
447
|
+
<div className={cn("", className)}>
|
|
448
|
+
<div className="grid grid-cols-1 gap-8 lg:grid-cols-12">
|
|
449
|
+
{/* table of contents - sticky sidebar */}
|
|
450
|
+
<div className="col-span-1 lg:col-span-3">
|
|
451
|
+
<div className="lg:sticky lg:top-4 border border-border rounded-xl bg-card p-4">
|
|
452
|
+
<TableOfContents
|
|
453
|
+
items={tocItems}
|
|
454
|
+
header={<LanguageSelector language={language} onLanguageChange={setLanguage} />}
|
|
455
|
+
footer={<TocFooter />}
|
|
456
|
+
/>
|
|
457
|
+
</div>
|
|
458
|
+
</div>
|
|
459
|
+
|
|
460
|
+
{/* main content */}
|
|
461
|
+
<div className="col-span-1 lg:col-span-9 border border-border rounded-xl bg-card p-6 space-y-8">
|
|
462
|
+
{/* about section */}
|
|
463
|
+
<div>
|
|
464
|
+
<h3 className="text-lg font-medium mb-2">about</h3>
|
|
465
|
+
<p className="text-sm text-muted-foreground">{app.description?.toLowerCase() || `run ${app.title || app.name} via api.`}</p>
|
|
466
|
+
</div>
|
|
467
|
+
|
|
468
|
+
{/* get an api key */}
|
|
469
|
+
<div className="space-y-3">
|
|
470
|
+
<SectionHeading id="api-key">get an api key</SectionHeading>
|
|
471
|
+
<p className="text-sm text-muted-foreground">
|
|
472
|
+
create one in <Link to={`${settingsBasePath}/keys`} className="text-primary hover:underline">settings → api keys</Link>
|
|
473
|
+
{language === 'belt'
|
|
474
|
+
? <>, or skip it and sign in with <code className="rounded bg-muted px-1 py-0.5 text-xs">belt login</code>. the examples read it from </>
|
|
475
|
+
: <>. the examples read it from </>}
|
|
476
|
+
<code className="rounded bg-muted px-1 py-0.5 text-xs">{currentKeySetup.variable}</code>.
|
|
477
|
+
</p>
|
|
478
|
+
<CodeBlock language="bash" textSize="text-xs">
|
|
479
|
+
{currentKeySetup.command}
|
|
480
|
+
</CodeBlock>
|
|
481
|
+
</div>
|
|
482
|
+
|
|
483
|
+
<Separator />
|
|
484
|
+
|
|
485
|
+
{/* 1. calling the api */}
|
|
486
|
+
<SectionHeading id="api-call">1. calling the api</SectionHeading>
|
|
487
|
+
|
|
488
|
+
<div className="space-y-6">
|
|
489
|
+
{/* install */}
|
|
490
|
+
<div className="space-y-3">
|
|
491
|
+
<SectionHeading id="api-call-install" level={3}>
|
|
492
|
+
install the client
|
|
493
|
+
</SectionHeading>
|
|
494
|
+
<p className="text-sm text-muted-foreground">
|
|
495
|
+
{currentInstall
|
|
496
|
+
? 'the client provides a convenient way to interact with the api.'
|
|
497
|
+
: 'nothing to install. any http client works; the examples use curl, and jq to read responses. every response wraps its result in a data field.'}
|
|
498
|
+
</p>
|
|
499
|
+
{currentInstall && (
|
|
500
|
+
<CodeBlock
|
|
501
|
+
language={language === 'swift' ? 'swift' : 'bash'}
|
|
502
|
+
textSize="text-xs"
|
|
503
|
+
className="max-h-[200px]"
|
|
504
|
+
>
|
|
505
|
+
{currentInstall.command}
|
|
506
|
+
</CodeBlock>
|
|
507
|
+
)}
|
|
508
|
+
</div>
|
|
509
|
+
|
|
510
|
+
{/* run and get result */}
|
|
511
|
+
<div className="space-y-3">
|
|
512
|
+
<SectionHeading id="api-call-result" level={3}>
|
|
513
|
+
run and get result
|
|
514
|
+
</SectionHeading>
|
|
515
|
+
<p className="text-sm text-muted-foreground">
|
|
516
|
+
{language === 'curl'
|
|
517
|
+
? <>send <code className="rounded bg-muted px-1 py-0.5 text-xs">"wait": true</code> to hold the connection until the task finishes. without it the call returns right away with the task still running.</>
|
|
518
|
+
: "submit a request and wait for the final result. best for batch processing or when you don't need progress updates."}
|
|
519
|
+
</p>
|
|
520
|
+
<CodeBlock
|
|
521
|
+
language={(language === 'curl' || language === 'belt') ? 'bash' : language}
|
|
522
|
+
textSize="text-xs"
|
|
523
|
+
className="max-h-[300px]"
|
|
524
|
+
>
|
|
525
|
+
{currentResultCode}
|
|
526
|
+
</CodeBlock>
|
|
527
|
+
</div>
|
|
528
|
+
|
|
529
|
+
{/* stream live updates */}
|
|
530
|
+
<div className="space-y-3">
|
|
531
|
+
<SectionHeading id="api-call-stream" level={3}>
|
|
532
|
+
stream live updates
|
|
533
|
+
</SectionHeading>
|
|
534
|
+
<p className="text-sm text-muted-foreground">
|
|
535
|
+
get real-time progress updates as the task runs. ideal for showing progress bars, partial results, or long-running tasks.
|
|
536
|
+
{language === 'curl' && <> send <code className="rounded bg-muted px-1 py-0.5 text-xs">"stream": true</code> and the response is a server-sent event stream that ends when the task does.</>}
|
|
537
|
+
</p>
|
|
538
|
+
<CodeBlock
|
|
539
|
+
language={(language === 'curl' || language === 'belt') ? 'bash' : language}
|
|
540
|
+
textSize="text-xs"
|
|
541
|
+
className="max-h-[300px]"
|
|
542
|
+
>
|
|
543
|
+
{currentStreamCode}
|
|
544
|
+
</CodeBlock>
|
|
545
|
+
</div>
|
|
546
|
+
|
|
547
|
+
{/* run in the background */}
|
|
548
|
+
<div className="space-y-3">
|
|
549
|
+
<SectionHeading id="api-call-background" level={3}>
|
|
550
|
+
run in the background
|
|
551
|
+
</SectionHeading>
|
|
552
|
+
<p className="text-sm text-muted-foreground">
|
|
553
|
+
start the task and get its id back right away, then check on it later. for long tasks, or when you'd rather be called back, see <a href="#webhooks" className="text-primary hover:underline">webhooks</a>.
|
|
554
|
+
</p>
|
|
555
|
+
<CodeBlock
|
|
556
|
+
language={(language === 'curl' || language === 'belt') ? 'bash' : language}
|
|
557
|
+
textSize="text-xs"
|
|
558
|
+
className="max-h-[300px]"
|
|
559
|
+
>
|
|
560
|
+
{currentBackgroundCode}
|
|
561
|
+
</CodeBlock>
|
|
562
|
+
</div>
|
|
563
|
+
</div>
|
|
564
|
+
|
|
565
|
+
<Separator />
|
|
566
|
+
|
|
567
|
+
{/* 2. authentication */}
|
|
568
|
+
<SectionHeading id="auth">2. authentication</SectionHeading>
|
|
569
|
+
|
|
570
|
+
<p className="text-sm text-muted-foreground">
|
|
571
|
+
the api uses api keys for authentication. see the{' '}
|
|
572
|
+
<Link to={`${docsBasePath}/api/authentication`} className="text-primary hover:underline">authentication docs</Link>{' '}
|
|
573
|
+
for detailed setup instructions.
|
|
574
|
+
</p>
|
|
575
|
+
|
|
576
|
+
<Separator />
|
|
577
|
+
|
|
578
|
+
{/* 3. files */}
|
|
579
|
+
<SectionHeading id="files">3. files</SectionHeading>
|
|
580
|
+
|
|
581
|
+
<p className="text-sm text-muted-foreground">
|
|
582
|
+
file inputs are automatically handled by the sdk. you can pass local paths, urls, or base64 data.
|
|
583
|
+
</p>
|
|
584
|
+
|
|
585
|
+
<div className="space-y-6">
|
|
586
|
+
{/* automatic upload */}
|
|
587
|
+
<div className="space-y-3">
|
|
588
|
+
<SectionHeading id="files-auto-upload" level={3}>
|
|
589
|
+
automatic upload
|
|
590
|
+
</SectionHeading>
|
|
591
|
+
<p className="text-sm text-muted-foreground">
|
|
592
|
+
{language === 'python'
|
|
593
|
+
? 'the python sdk automatically detects local file paths and uploads them. urls are passed through as-is.'
|
|
594
|
+
: language === 'typescript' || language === 'javascript'
|
|
595
|
+
? 'the javascript sdk automatically uploads blob/file objects and data uris. urls are passed through as-is.'
|
|
596
|
+
: language === 'swift'
|
|
597
|
+
? 'pass urls directly. upload local files with client.files.upload (below) and pass the returned uri.'
|
|
598
|
+
: language === 'belt'
|
|
599
|
+
? 'belt detects local file paths in the input and uploads them. urls are passed through as-is.'
|
|
600
|
+
: 'pass urls directly or use data uris for inline content.'}
|
|
601
|
+
</p>
|
|
602
|
+
<CodeBlock
|
|
603
|
+
language={(language === 'curl' || language === 'belt') ? 'bash' : language}
|
|
604
|
+
textSize="text-xs"
|
|
605
|
+
>
|
|
606
|
+
{currentFileExamples.auto}
|
|
607
|
+
</CodeBlock>
|
|
608
|
+
</div>
|
|
609
|
+
|
|
610
|
+
{/* manual upload */}
|
|
611
|
+
<div className="space-y-3">
|
|
612
|
+
<SectionHeading id="files-manual" level={3}>
|
|
613
|
+
manual upload
|
|
614
|
+
</SectionHeading>
|
|
615
|
+
<p className="text-sm text-muted-foreground">
|
|
616
|
+
you can also upload files manually and use the returned url.
|
|
617
|
+
</p>
|
|
618
|
+
<CodeBlock
|
|
619
|
+
language={(language === 'curl' || language === 'belt') ? 'bash' : language}
|
|
620
|
+
textSize="text-xs"
|
|
621
|
+
>
|
|
622
|
+
{currentFileExamples.manual}
|
|
623
|
+
</CodeBlock>
|
|
624
|
+
</div>
|
|
625
|
+
</div>
|
|
626
|
+
|
|
627
|
+
<Separator />
|
|
628
|
+
|
|
629
|
+
{/* 4. webhooks */}
|
|
630
|
+
<SectionHeading id="webhooks">4. webhooks</SectionHeading>
|
|
631
|
+
|
|
632
|
+
<p className="text-sm text-muted-foreground">
|
|
633
|
+
get notified when a task completes by providing a <code className="rounded bg-muted px-1 py-0.5 text-xs">webhook</code> url.
|
|
634
|
+
when the task reaches a terminal state (completed, failed, or cancelled), a POST request is sent to your url with the task result.
|
|
635
|
+
</p>
|
|
636
|
+
|
|
637
|
+
<CodeBlock
|
|
638
|
+
language={(language === 'curl' || language === 'belt') ? 'bash' : language}
|
|
639
|
+
textSize="text-xs"
|
|
640
|
+
className="max-h-[300px]"
|
|
641
|
+
>
|
|
642
|
+
{language === 'python' ? `result = client.run({
|
|
643
|
+
"app": "${appRef}",
|
|
644
|
+
"input": ${JSON.stringify(processedInput, null, 4).split('\n').map((l, i) => i === 0 ? l : ' ' + l).join('\n')},
|
|
645
|
+
"webhook": "https://your-server.com/webhook"
|
|
646
|
+
}, wait=False)` :
|
|
647
|
+
language === 'typescript' || language === 'javascript' ? `const result = await client.run({
|
|
648
|
+
app: "${appRef}",
|
|
649
|
+
input: ${JSON.stringify(processedInput, null, 2).split('\n').map((l, i) => i === 0 ? l : ' ' + l).join('\n')},
|
|
650
|
+
webhook: "https://your-server.com/webhook"
|
|
651
|
+
}, { wait: false });` :
|
|
652
|
+
language === 'swift' ? generateSwiftWebhookExample(appRef, processedInput) :
|
|
653
|
+
`curl -X POST https://api.inference.sh/apps/run \\
|
|
654
|
+
-H "Authorization: Bearer $INFERENCE_API_KEY" \\
|
|
655
|
+
-H "Content-Type: application/json" \\
|
|
656
|
+
-d '{
|
|
657
|
+
"app": "${appRef}",
|
|
658
|
+
"input": ${JSON.stringify(processedInput, null, 4).split('\n').map((l, i) => i === 0 ? l : ' ' + l).join('\n')},
|
|
659
|
+
"webhook": "https://your-server.com/webhook"
|
|
660
|
+
}'`}
|
|
661
|
+
</CodeBlock>
|
|
662
|
+
|
|
663
|
+
<div className="space-y-3">
|
|
664
|
+
<p className="text-xs font-medium text-foreground">webhook payload</p>
|
|
665
|
+
<p className="text-xs text-muted-foreground">
|
|
666
|
+
your endpoint receives a JSON POST with the task result:
|
|
667
|
+
</p>
|
|
668
|
+
<CodeBlock language="json" textSize="text-xs">
|
|
669
|
+
{`{
|
|
670
|
+
"event": "task.completed",
|
|
671
|
+
"timestamp": "2024-01-15T10:30:05Z",
|
|
672
|
+
"data": {
|
|
673
|
+
"id": "task_abc123",
|
|
674
|
+
"short_id": "abc123",
|
|
675
|
+
"status": ${TaskStatusCompleted},
|
|
676
|
+
"status_text": "completed",
|
|
677
|
+
"output": { ... },
|
|
678
|
+
"created_at": "2024-01-15T10:30:00Z",
|
|
679
|
+
"updated_at": "2024-01-15T10:30:05Z"
|
|
680
|
+
}
|
|
681
|
+
}`}
|
|
682
|
+
</CodeBlock>
|
|
683
|
+
<div className="space-y-2">
|
|
684
|
+
{WEBHOOK_FIELDS.map(f => (
|
|
685
|
+
<div key={f.key} className="flex items-center gap-2 text-xs">
|
|
686
|
+
<span className="font-semibold">{f.key}</span>
|
|
687
|
+
<span className="font-mono text-muted-foreground">{f.type}</span>
|
|
688
|
+
<span className="text-muted-foreground">{f.desc}</span>
|
|
689
|
+
</div>
|
|
690
|
+
))}
|
|
691
|
+
</div>
|
|
692
|
+
</div>
|
|
693
|
+
|
|
694
|
+
<Separator />
|
|
695
|
+
|
|
696
|
+
{/* 5. schema */}
|
|
697
|
+
<SectionHeading id="schema">5. schema</SectionHeading>
|
|
698
|
+
|
|
699
|
+
<div className="space-y-6">
|
|
700
|
+
{/* input schema */}
|
|
701
|
+
<div className="space-y-4">
|
|
702
|
+
<SectionHeading id="schema-input" level={3}>
|
|
703
|
+
input
|
|
704
|
+
</SectionHeading>
|
|
705
|
+
{inputFields.length > 0 ? (
|
|
706
|
+
<div className="space-y-3">
|
|
707
|
+
{inputFields.map((field) => (
|
|
708
|
+
<SchemaProperty
|
|
709
|
+
key={field.key}
|
|
710
|
+
field={field}
|
|
711
|
+
/>
|
|
712
|
+
))}
|
|
713
|
+
</div>
|
|
714
|
+
) : (
|
|
715
|
+
<p className="text-sm text-muted-foreground">no input parameters defined.</p>
|
|
716
|
+
)}
|
|
717
|
+
</div>
|
|
718
|
+
|
|
719
|
+
{/* output schema */}
|
|
720
|
+
<div className="space-y-4">
|
|
721
|
+
<SectionHeading id="schema-output" level={3}>
|
|
722
|
+
output
|
|
723
|
+
</SectionHeading>
|
|
724
|
+
{outputFields.length > 0 ? (
|
|
725
|
+
<div className="space-y-3">
|
|
726
|
+
{outputFields.map((field) => (
|
|
727
|
+
<SchemaProperty
|
|
728
|
+
key={field.key}
|
|
729
|
+
field={field}
|
|
730
|
+
isOutput={true}
|
|
731
|
+
/>
|
|
732
|
+
))}
|
|
733
|
+
</div>
|
|
734
|
+
) : (
|
|
735
|
+
<p className="text-sm text-muted-foreground">see task output for result format.</p>
|
|
736
|
+
)}
|
|
737
|
+
</div>
|
|
738
|
+
|
|
739
|
+
{/* setup schema */}
|
|
740
|
+
{hasSetupFields && (
|
|
741
|
+
<div className="space-y-4">
|
|
742
|
+
<SectionHeading id="schema-setup" level={3}>
|
|
743
|
+
setup
|
|
744
|
+
</SectionHeading>
|
|
745
|
+
<p className="text-xs text-muted-foreground">setup parameters are provided once via the <code className="rounded bg-muted px-1 py-0.5 text-xs">setup</code> field and are persisted for a session.</p>
|
|
746
|
+
<div className="space-y-3">
|
|
747
|
+
{setupFields.map((field) => (
|
|
748
|
+
<SchemaProperty
|
|
749
|
+
key={field.key}
|
|
750
|
+
field={field}
|
|
751
|
+
/>
|
|
752
|
+
))}
|
|
753
|
+
</div>
|
|
754
|
+
</div>
|
|
755
|
+
)}
|
|
756
|
+
</div>
|
|
757
|
+
</div>
|
|
758
|
+
</div>
|
|
759
|
+
</div>
|
|
760
|
+
)
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
export default ApiDocs
|