@doccov/ui 0.2.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.
@@ -0,0 +1,109 @@
1
+ interface ParameterSchema {
2
+ /** Type name (e.g., "string", "number", "object") */
3
+ type?: string;
4
+ /** Formatted type string for display */
5
+ typeString?: string;
6
+ /** Description of the parameter/property */
7
+ description?: string;
8
+ /** Nested properties for object types */
9
+ properties?: Record<string, ParameterSchema>;
10
+ /** Required property names */
11
+ required?: string[];
12
+ }
13
+ interface ParameterItemProps {
14
+ /** Parameter name */
15
+ name: string;
16
+ /** Parameter schema */
17
+ schema: ParameterSchema;
18
+ /** Whether this parameter is required */
19
+ required?: boolean;
20
+ /** Description (overrides schema.description) */
21
+ description?: string;
22
+ /** Nesting depth for indentation */
23
+ depth?: number;
24
+ /** Custom className */
25
+ className?: string;
26
+ }
27
+ /**
28
+ * Single parameter with expand capability for nested objects.
29
+ * Features expandable nested params, type annotations, and required/optional badges.
30
+ */
31
+ declare function ParameterItem({ name, schema, required: isRequired, description, depth, className }: ParameterItemProps): React.ReactNode;
32
+ import { VariantProps } from "class-variance-authority";
33
+ import * as React2 from "react";
34
+ /**
35
+ * Type coloring for syntax display.
36
+ * Follows Stripe-style: consistent colors for primitives vs complex types.
37
+ */
38
+ declare const typeBadgeVariants: unknown;
39
+ interface TypeBadgeProps extends React2.HTMLAttributes<HTMLSpanElement>, VariantProps<typeof typeBadgeVariants> {
40
+ /** Type string to display */
41
+ type: string;
42
+ /** Override color detection */
43
+ typeColor?: VariantProps<typeof typeBadgeVariants>["typeColor"];
44
+ }
45
+ /**
46
+ * Inline type display with syntax coloring.
47
+ * Automatically detects type category and applies appropriate color.
48
+ */
49
+ declare const TypeBadge: unknown;
50
+ interface ImportSectionProps {
51
+ /** Import statement text */
52
+ importStatement: string;
53
+ /** Custom className */
54
+ className?: string;
55
+ }
56
+ /**
57
+ * Displays a copyable import statement with one-click copy.
58
+ * Monospace styling with copy button.
59
+ */
60
+ declare function ImportSection({ importStatement, className }: ImportSectionProps): React.ReactNode;
61
+ import { ReactNode as ReactNode2 } from "react";
62
+ interface CodeTab {
63
+ /** Tab label */
64
+ label: string;
65
+ /** Tab content (code block) */
66
+ content: ReactNode2;
67
+ /** Raw code for copy button */
68
+ code: string;
69
+ }
70
+ interface CodeTabsProps {
71
+ /** Array of tabs */
72
+ tabs: CodeTab[];
73
+ /** Default selected tab index */
74
+ defaultIndex?: number;
75
+ /** Enable sticky positioning for the header */
76
+ sticky?: boolean;
77
+ /** Custom className */
78
+ className?: string;
79
+ }
80
+ /**
81
+ * Tabbed code block wrapper with copy button per tab.
82
+ * Uses docskit --dk-* CSS variables for consistent theming.
83
+ */
84
+ declare function CodeTabs({ tabs, defaultIndex, sticky, className }: CodeTabsProps): React.ReactNode2;
85
+ import { VariantProps as VariantProps2 } from "class-variance-authority";
86
+ import * as React3 from "react";
87
+ type ExportKind = "function" | "type" | "variable" | "class" | "interface" | "enum";
88
+ /**
89
+ * Kind badge variants for export cards.
90
+ */
91
+ declare const kindBadgeVariants: unknown;
92
+ interface ExportCardProps extends React3.AnchorHTMLAttributes<HTMLAnchorElement>, VariantProps2<typeof kindBadgeVariants> {
93
+ /** Export name */
94
+ name: string;
95
+ /** Description snippet */
96
+ description?: string;
97
+ /** Link to detail page */
98
+ href: string;
99
+ /** Export kind */
100
+ kind?: ExportKind;
101
+ /** Custom link component (for Next.js Link) */
102
+ as?: React3.ElementType;
103
+ }
104
+ /**
105
+ * Card component for displaying exports in an index grid.
106
+ * Features function name styling, description, kind badge, and hover effects.
107
+ */
108
+ declare const ExportCard: unknown;
109
+ export { typeBadgeVariants, kindBadgeVariants, TypeBadgeProps, TypeBadge, ParameterSchema, ParameterItemProps, ParameterItem, ImportSectionProps, ImportSection, ExportKind, ExportCardProps, ExportCard, CodeTabsProps, CodeTabs, CodeTab };
@@ -0,0 +1,430 @@
1
+ "use client";
2
+ import {
3
+ cn
4
+ } from "../../lib/utils.js";
5
+
6
+ // src/components/api/ParameterItem.tsx
7
+ import { ChevronRight } from "lucide-react";
8
+ import { useState } from "react";
9
+ import { jsx, jsxs } from "react/jsx-runtime";
10
+
11
+ function getNestedProperties(schema) {
12
+ if (!schema || typeof schema !== "object")
13
+ return null;
14
+ if (schema.type === "object" && schema.properties) {
15
+ return schema.properties;
16
+ }
17
+ return null;
18
+ }
19
+ function getRequiredFields(schema) {
20
+ return schema.required ?? [];
21
+ }
22
+ function countProperties(schema) {
23
+ const props = getNestedProperties(schema);
24
+ return props ? Object.keys(props).length : 0;
25
+ }
26
+ function getTypeDisplay(schema) {
27
+ if (schema.typeString)
28
+ return schema.typeString;
29
+ return schema.type ?? "unknown";
30
+ }
31
+ function NestedPropertyItem({
32
+ name,
33
+ schema,
34
+ required = false,
35
+ depth = 0
36
+ }) {
37
+ const [expanded, setExpanded] = useState(false);
38
+ const type = getTypeDisplay(schema);
39
+ const nestedProps = getNestedProperties(schema);
40
+ const nestedCount = countProperties(schema);
41
+ const hasNested = nestedCount > 0;
42
+ return /* @__PURE__ */ jsxs("div", {
43
+ className: cn("border-t border-border first:border-t-0", depth > 0 && "ml-4"),
44
+ children: [
45
+ /* @__PURE__ */ jsx("div", {
46
+ className: "py-3",
47
+ children: /* @__PURE__ */ jsxs("div", {
48
+ className: "flex items-start gap-2",
49
+ children: [
50
+ hasNested && /* @__PURE__ */ jsx("button", {
51
+ type: "button",
52
+ onClick: () => setExpanded(!expanded),
53
+ className: "mt-0.5 p-0.5 text-muted-foreground hover:text-foreground transition-colors cursor-pointer",
54
+ "aria-label": expanded ? "Collapse" : "Expand",
55
+ children: /* @__PURE__ */ jsx(ChevronRight, {
56
+ size: 14,
57
+ className: cn("transition-transform duration-200", expanded && "rotate-90")
58
+ })
59
+ }),
60
+ !hasNested && /* @__PURE__ */ jsx("div", {
61
+ className: "w-5"
62
+ }),
63
+ /* @__PURE__ */ jsxs("div", {
64
+ className: "flex-1 min-w-0",
65
+ children: [
66
+ /* @__PURE__ */ jsxs("div", {
67
+ className: "flex items-baseline gap-2 flex-wrap",
68
+ children: [
69
+ /* @__PURE__ */ jsxs("span", {
70
+ className: "font-mono text-sm text-foreground",
71
+ children: [
72
+ name,
73
+ !required && /* @__PURE__ */ jsx("span", {
74
+ className: "text-muted-foreground",
75
+ children: "?"
76
+ })
77
+ ]
78
+ }),
79
+ /* @__PURE__ */ jsx("span", {
80
+ className: "font-mono text-sm text-muted-foreground",
81
+ children: hasNested ? "object" : type
82
+ }),
83
+ hasNested && /* @__PURE__ */ jsxs("button", {
84
+ type: "button",
85
+ onClick: () => setExpanded(!expanded),
86
+ className: "text-xs text-primary hover:underline cursor-pointer",
87
+ children: [
88
+ nestedCount,
89
+ " ",
90
+ nestedCount === 1 ? "property" : "properties"
91
+ ]
92
+ })
93
+ ]
94
+ }),
95
+ schema.description && /* @__PURE__ */ jsx("p", {
96
+ className: "text-sm text-muted-foreground mt-1",
97
+ children: schema.description
98
+ })
99
+ ]
100
+ })
101
+ ]
102
+ })
103
+ }),
104
+ hasNested && expanded && nestedProps && /* @__PURE__ */ jsx("div", {
105
+ className: "border-l border-border ml-2",
106
+ children: Object.entries(nestedProps).map(([propName, propSchema]) => /* @__PURE__ */ jsx(NestedPropertyItem, {
107
+ name: propName,
108
+ schema: propSchema,
109
+ required: getRequiredFields(schema).includes(propName),
110
+ depth: depth + 1
111
+ }, propName))
112
+ })
113
+ ]
114
+ });
115
+ }
116
+ function ParameterItem({
117
+ name,
118
+ schema,
119
+ required: isRequired = true,
120
+ description,
121
+ depth = 0,
122
+ className
123
+ }) {
124
+ const [expanded, setExpanded] = useState(false);
125
+ const type = getTypeDisplay(schema);
126
+ const nestedProps = getNestedProperties(schema);
127
+ const nestedCount = countProperties(schema);
128
+ const hasNested = nestedCount > 0;
129
+ const displayDescription = description ?? schema.description;
130
+ return /* @__PURE__ */ jsxs("div", {
131
+ className: cn("border-b border-border last:border-b-0", className),
132
+ children: [
133
+ /* @__PURE__ */ jsx("div", {
134
+ className: "py-3",
135
+ children: /* @__PURE__ */ jsxs("div", {
136
+ className: "flex items-start gap-2",
137
+ children: [
138
+ hasNested && /* @__PURE__ */ jsx("button", {
139
+ type: "button",
140
+ onClick: () => setExpanded(!expanded),
141
+ className: "mt-0.5 p-0.5 text-muted-foreground hover:text-foreground transition-colors cursor-pointer",
142
+ "aria-label": expanded ? "Collapse" : "Expand",
143
+ children: /* @__PURE__ */ jsx(ChevronRight, {
144
+ size: 14,
145
+ className: cn("transition-transform duration-200", expanded && "rotate-90")
146
+ })
147
+ }),
148
+ !hasNested && /* @__PURE__ */ jsx("div", {
149
+ className: "w-5"
150
+ }),
151
+ /* @__PURE__ */ jsxs("div", {
152
+ className: "flex-1 min-w-0",
153
+ children: [
154
+ /* @__PURE__ */ jsxs("div", {
155
+ className: "flex items-baseline gap-2 flex-wrap",
156
+ children: [
157
+ /* @__PURE__ */ jsx("span", {
158
+ className: "font-mono text-sm font-medium text-foreground",
159
+ children: name
160
+ }),
161
+ isRequired && /* @__PURE__ */ jsx("span", {
162
+ className: "text-[10px] font-semibold px-1.5 py-0.5 rounded border border-border bg-muted text-muted-foreground uppercase tracking-wide",
163
+ children: "Required"
164
+ }),
165
+ /* @__PURE__ */ jsx("span", {
166
+ className: "font-mono text-sm text-muted-foreground",
167
+ children: hasNested ? "object" : type
168
+ }),
169
+ hasNested && /* @__PURE__ */ jsxs("button", {
170
+ type: "button",
171
+ onClick: () => setExpanded(!expanded),
172
+ className: "text-xs text-primary hover:underline cursor-pointer",
173
+ children: [
174
+ nestedCount,
175
+ " ",
176
+ nestedCount === 1 ? "property" : "properties"
177
+ ]
178
+ })
179
+ ]
180
+ }),
181
+ displayDescription && /* @__PURE__ */ jsx("p", {
182
+ className: "text-sm text-muted-foreground mt-1",
183
+ children: displayDescription
184
+ })
185
+ ]
186
+ })
187
+ ]
188
+ })
189
+ }),
190
+ hasNested && expanded && nestedProps && /* @__PURE__ */ jsx("div", {
191
+ className: "border-l border-border ml-2 mb-3",
192
+ children: Object.entries(nestedProps).map(([propName, propSchema]) => /* @__PURE__ */ jsx(NestedPropertyItem, {
193
+ name: propName,
194
+ schema: propSchema,
195
+ required: getRequiredFields(schema).includes(propName),
196
+ depth: depth + 1
197
+ }, propName))
198
+ })
199
+ ]
200
+ });
201
+ }
202
+ // src/components/api/TypeBadge.tsx
203
+ import { cva } from "class-variance-authority";
204
+ import * as React from "react";
205
+ import { jsx as jsx2 } from "react/jsx-runtime";
206
+ var typeBadgeVariants = cva("font-mono text-sm", {
207
+ variants: {
208
+ typeColor: {
209
+ string: "text-emerald-600 dark:text-emerald-400",
210
+ number: "text-blue-600 dark:text-blue-400",
211
+ boolean: "text-amber-600 dark:text-amber-400",
212
+ null: "text-gray-500 dark:text-gray-400",
213
+ undefined: "text-gray-500 dark:text-gray-400",
214
+ object: "text-purple-600 dark:text-purple-400",
215
+ array: "text-cyan-600 dark:text-cyan-400",
216
+ function: "text-fuchsia-600 dark:text-fuchsia-400",
217
+ union: "text-orange-600 dark:text-orange-400",
218
+ generic: "text-rose-600 dark:text-rose-400",
219
+ default: "text-muted-foreground"
220
+ }
221
+ },
222
+ defaultVariants: {
223
+ typeColor: "default"
224
+ }
225
+ });
226
+ function detectTypeColor(type) {
227
+ const normalized = type.toLowerCase().trim();
228
+ if (normalized === "string" || normalized.startsWith('"') || normalized.startsWith("'")) {
229
+ return "string";
230
+ }
231
+ if (normalized === "number" || /^\d+$/.test(normalized)) {
232
+ return "number";
233
+ }
234
+ if (normalized === "boolean" || normalized === "true" || normalized === "false") {
235
+ return "boolean";
236
+ }
237
+ if (normalized === "null") {
238
+ return "null";
239
+ }
240
+ if (normalized === "undefined" || normalized === "void") {
241
+ return "undefined";
242
+ }
243
+ if (normalized === "object" || normalized.startsWith("{")) {
244
+ return "object";
245
+ }
246
+ if (normalized.endsWith("[]") || normalized.startsWith("array")) {
247
+ return "array";
248
+ }
249
+ if (normalized.startsWith("(") || normalized.includes("=>") || normalized.startsWith("function")) {
250
+ return "function";
251
+ }
252
+ if (normalized.includes("|")) {
253
+ return "union";
254
+ }
255
+ if (normalized.includes("<") && normalized.includes(">")) {
256
+ return "generic";
257
+ }
258
+ return "default";
259
+ }
260
+ var TypeBadge = React.forwardRef(({ className, type, typeColor, ...props }, ref) => {
261
+ const color = typeColor ?? detectTypeColor(type);
262
+ return /* @__PURE__ */ jsx2("span", {
263
+ ref,
264
+ className: cn(typeBadgeVariants({ typeColor: color }), className),
265
+ ...props,
266
+ children: type
267
+ });
268
+ });
269
+ TypeBadge.displayName = "TypeBadge";
270
+ // src/components/api/ImportSection.tsx
271
+ import { Check, Copy } from "lucide-react";
272
+ import { useState as useState2 } from "react";
273
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
274
+
275
+ function ImportSection({ importStatement, className }) {
276
+ const [copied, setCopied] = useState2(false);
277
+ const handleCopy = () => {
278
+ navigator.clipboard.writeText(importStatement);
279
+ setCopied(true);
280
+ setTimeout(() => setCopied(false), 1200);
281
+ };
282
+ return /* @__PURE__ */ jsxs2("div", {
283
+ className: cn("group flex items-center justify-between gap-3", "rounded-lg border border-border bg-muted/30 px-4 py-3", className),
284
+ children: [
285
+ /* @__PURE__ */ jsx3("code", {
286
+ className: "font-mono text-sm text-foreground overflow-x-auto",
287
+ children: importStatement
288
+ }),
289
+ /* @__PURE__ */ jsx3("button", {
290
+ type: "button",
291
+ onClick: handleCopy,
292
+ className: cn("shrink-0 p-1.5 rounded", "text-muted-foreground hover:text-foreground", "opacity-0 group-hover:opacity-100 transition-opacity duration-200", "cursor-pointer"),
293
+ "aria-label": "Copy import statement",
294
+ children: copied ? /* @__PURE__ */ jsx3(Check, {
295
+ size: 16
296
+ }) : /* @__PURE__ */ jsx3(Copy, {
297
+ size: 16
298
+ })
299
+ })
300
+ ]
301
+ });
302
+ }
303
+ // src/components/api/CodeTabs.tsx
304
+ import { Check as Check2, Copy as Copy2 } from "lucide-react";
305
+ import { useState as useState3 } from "react";
306
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
307
+
308
+ function CodeTabs({
309
+ tabs,
310
+ defaultIndex = 0,
311
+ sticky = false,
312
+ className
313
+ }) {
314
+ const [activeIndex, setActiveIndex] = useState3(defaultIndex);
315
+ const [copied, setCopied] = useState3(false);
316
+ const activeTab = tabs[activeIndex];
317
+ const handleCopy = () => {
318
+ if (!activeTab)
319
+ return;
320
+ navigator.clipboard.writeText(activeTab.code);
321
+ setCopied(true);
322
+ setTimeout(() => setCopied(false), 1200);
323
+ };
324
+ if (!tabs.length)
325
+ return null;
326
+ return /* @__PURE__ */ jsxs3("div", {
327
+ className: cn("group rounded-lg border border-dk-border bg-dk-background overflow-hidden", "selection:bg-dk-selection selection:text-current", className),
328
+ children: [
329
+ /* @__PURE__ */ jsxs3("div", {
330
+ className: cn("flex items-center border-b border-dk-border bg-dk-tabs-background", sticky && "sticky top-0 z-10"),
331
+ children: [
332
+ /* @__PURE__ */ jsx4("div", {
333
+ className: "flex-1 flex items-stretch",
334
+ children: tabs.map((tab, index) => /* @__PURE__ */ jsx4("button", {
335
+ type: "button",
336
+ onClick: () => setActiveIndex(index),
337
+ className: cn("px-4 py-2 text-sm font-mono transition-colors duration-200", "border-r border-dk-border last:border-r-0", index === activeIndex ? "text-dk-tab-active-foreground bg-dk-background/50" : "text-dk-tab-inactive-foreground hover:text-dk-tab-active-foreground"),
338
+ children: tab.label
339
+ }, tab.label))
340
+ }),
341
+ /* @__PURE__ */ jsx4("button", {
342
+ type: "button",
343
+ onClick: handleCopy,
344
+ className: cn("p-2 mx-2", "text-dk-tab-inactive-foreground hover:text-dk-tab-active-foreground", "opacity-0 group-hover:opacity-100 transition-opacity", "cursor-pointer"),
345
+ "aria-label": "Copy code",
346
+ children: copied ? /* @__PURE__ */ jsx4(Check2, {
347
+ size: 16
348
+ }) : /* @__PURE__ */ jsx4(Copy2, {
349
+ size: 16
350
+ })
351
+ })
352
+ ]
353
+ }),
354
+ /* @__PURE__ */ jsx4("div", {
355
+ className: "bg-dk-background",
356
+ children: activeTab?.content
357
+ })
358
+ ]
359
+ });
360
+ }
361
+ // src/components/api/ExportCard.tsx
362
+ import { cva as cva2 } from "class-variance-authority";
363
+ import * as React2 from "react";
364
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
365
+
366
+ var kindBadgeVariants = cva2("inline-flex items-center justify-center font-mono font-medium rounded shrink-0 h-5 px-1.5 text-xs", {
367
+ variants: {
368
+ kind: {
369
+ function: "bg-fuchsia-500/15 text-fuchsia-600 dark:text-fuchsia-400",
370
+ class: "bg-amber-500/15 text-amber-600 dark:text-amber-400",
371
+ interface: "bg-cyan-500/15 text-cyan-600 dark:text-cyan-400",
372
+ type: "bg-purple-500/15 text-purple-600 dark:text-purple-400",
373
+ enum: "bg-emerald-500/15 text-emerald-600 dark:text-emerald-400",
374
+ variable: "bg-blue-500/15 text-blue-600 dark:text-blue-400"
375
+ }
376
+ },
377
+ defaultVariants: {
378
+ kind: "function"
379
+ }
380
+ });
381
+ var kindLabels = {
382
+ function: "fn",
383
+ class: "cls",
384
+ interface: "int",
385
+ type: "type",
386
+ enum: "enum",
387
+ variable: "var"
388
+ };
389
+ var ExportCard = React2.forwardRef(({ name, description, href, kind = "function", as: Component = "a", className, ...props }, ref) => {
390
+ const isFunction = kind === "function";
391
+ return /* @__PURE__ */ jsxs4(Component, {
392
+ ref,
393
+ href,
394
+ className: cn("group block rounded-lg border border-border bg-card/50 p-4", "transition-all duration-200", "hover:border-border/80 hover:bg-card hover:shadow-md", "hover:-translate-y-0.5", className),
395
+ ...props,
396
+ children: [
397
+ /* @__PURE__ */ jsxs4("div", {
398
+ className: "flex items-center gap-2 mb-2",
399
+ children: [
400
+ /* @__PURE__ */ jsx5("span", {
401
+ className: kindBadgeVariants({ kind }),
402
+ children: kindLabels[kind]
403
+ }),
404
+ /* @__PURE__ */ jsx5("span", {
405
+ className: "font-mono text-base font-medium text-foreground group-hover:text-primary transition-colors",
406
+ children: name
407
+ }),
408
+ isFunction && /* @__PURE__ */ jsx5("span", {
409
+ className: "font-mono text-base text-muted-foreground",
410
+ children: "()"
411
+ })
412
+ ]
413
+ }),
414
+ description && /* @__PURE__ */ jsx5("p", {
415
+ className: "text-sm text-muted-foreground line-clamp-2 leading-relaxed",
416
+ children: description
417
+ })
418
+ ]
419
+ });
420
+ });
421
+ ExportCard.displayName = "ExportCard";
422
+ export {
423
+ typeBadgeVariants,
424
+ kindBadgeVariants,
425
+ TypeBadge,
426
+ ParameterItem,
427
+ ImportSection,
428
+ ExportCard,
429
+ CodeTabs
430
+ };