zudoku 0.35.2 → 0.35.4

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.
Files changed (73) hide show
  1. package/dist/lib/components/InlineCode.js +2 -9
  2. package/dist/lib/components/InlineCode.js.map +1 -1
  3. package/dist/lib/components/Layout.js +2 -2
  4. package/dist/lib/components/Layout.js.map +1 -1
  5. package/dist/lib/errors/ErrorAlert.js +6 -1
  6. package/dist/lib/errors/ErrorAlert.js.map +1 -1
  7. package/dist/lib/plugins/openapi/OperationListItem.js +3 -11
  8. package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
  9. package/dist/lib/plugins/openapi/ParamInfos.d.ts +6 -0
  10. package/dist/lib/plugins/openapi/ParamInfos.js +34 -0
  11. package/dist/lib/plugins/openapi/ParamInfos.js.map +1 -0
  12. package/dist/lib/plugins/openapi/ParameterListItem.js +6 -4
  13. package/dist/lib/plugins/openapi/ParameterListItem.js.map +1 -1
  14. package/dist/lib/plugins/openapi/components/EnumValues.d.ts +5 -0
  15. package/dist/lib/plugins/openapi/components/EnumValues.js +15 -0
  16. package/dist/lib/plugins/openapi/components/EnumValues.js.map +1 -0
  17. package/dist/lib/plugins/openapi/components/SelectOnClick.d.ts +5 -0
  18. package/dist/lib/plugins/openapi/components/SelectOnClick.js +16 -0
  19. package/dist/lib/plugins/openapi/components/SelectOnClick.js.map +1 -0
  20. package/dist/lib/plugins/openapi/schema/SchemaPropertyItem.js +7 -8
  21. package/dist/lib/plugins/openapi/schema/SchemaPropertyItem.js.map +1 -1
  22. package/dist/lib/ui/Button.d.ts +2 -2
  23. package/dist/lib/ui/Button.js +1 -0
  24. package/dist/lib/ui/Button.js.map +1 -1
  25. package/dist/lib/ui/SyntaxHighlight.js +2 -0
  26. package/dist/lib/ui/SyntaxHighlight.js.map +1 -1
  27. package/lib/{Markdown-D1Y3cd9l.js → Markdown-hBN9vkm5.js} +1045 -1026
  28. package/lib/Markdown-hBN9vkm5.js.map +1 -0
  29. package/lib/{MdxPage-CUL_SQzW.js → MdxPage-UCWwxhzC.js} +2 -2
  30. package/lib/{MdxPage-CUL_SQzW.js.map → MdxPage-UCWwxhzC.js.map} +1 -1
  31. package/lib/{OasProvider-_ye9MUAd.js → OasProvider-BbSqUQka.js} +2 -2
  32. package/lib/{OasProvider-_ye9MUAd.js.map → OasProvider-BbSqUQka.js.map} +1 -1
  33. package/lib/{OperationList-Br5x22KD.js → OperationList-CENzwqY8.js} +1992 -1935
  34. package/lib/OperationList-CENzwqY8.js.map +1 -0
  35. package/lib/SlotletProvider-D-XPr1Wg.js +338 -0
  36. package/lib/SlotletProvider-D-XPr1Wg.js.map +1 -0
  37. package/lib/{Spinner-1KrEmx1V.js → Spinner-C6n4eOvh.js} +13 -12
  38. package/lib/Spinner-C6n4eOvh.js.map +1 -0
  39. package/lib/SyntaxHighlight-BEoSoPEo.js +2890 -0
  40. package/lib/SyntaxHighlight-BEoSoPEo.js.map +1 -0
  41. package/lib/{index-C7TIhpXK.js → index-BVhQWA89.js} +5 -5
  42. package/lib/{index-C7TIhpXK.js.map → index-BVhQWA89.js.map} +1 -1
  43. package/lib/prism-jsstacktrace.min-BfobCF2F.js +2 -0
  44. package/lib/prism-jsstacktrace.min-BfobCF2F.js.map +1 -0
  45. package/lib/ui/ActionButton.js +1 -1
  46. package/lib/ui/Button.js +12 -11
  47. package/lib/ui/Button.js.map +1 -1
  48. package/lib/ui/SyntaxHighlight.js +8 -2883
  49. package/lib/ui/SyntaxHighlight.js.map +1 -1
  50. package/lib/zudoku.components.js +40 -40
  51. package/lib/zudoku.components.js.map +1 -1
  52. package/lib/zudoku.plugin-api-catalog.js +1 -1
  53. package/lib/zudoku.plugin-api-keys.js +1 -1
  54. package/lib/zudoku.plugin-custom-pages.js +1 -1
  55. package/lib/zudoku.plugin-markdown.js +1 -1
  56. package/lib/zudoku.plugin-openapi.js +1 -1
  57. package/package.json +1 -1
  58. package/src/lib/components/InlineCode.tsx +11 -16
  59. package/src/lib/components/Layout.tsx +2 -2
  60. package/src/lib/errors/ErrorAlert.tsx +24 -17
  61. package/src/lib/plugins/openapi/OperationListItem.tsx +4 -16
  62. package/src/lib/plugins/openapi/ParamInfos.tsx +64 -0
  63. package/src/lib/plugins/openapi/ParameterListItem.tsx +12 -12
  64. package/src/lib/plugins/openapi/components/EnumValues.tsx +58 -0
  65. package/src/lib/plugins/openapi/components/SelectOnClick.tsx +29 -0
  66. package/src/lib/plugins/openapi/schema/SchemaPropertyItem.tsx +19 -28
  67. package/src/lib/ui/Button.tsx +3 -1
  68. package/src/lib/ui/SyntaxHighlight.tsx +2 -0
  69. package/lib/Markdown-D1Y3cd9l.js.map +0 -1
  70. package/lib/OperationList-Br5x22KD.js.map +0 -1
  71. package/lib/SlotletProvider-iDmNlxD5.js +0 -221
  72. package/lib/SlotletProvider-iDmNlxD5.js.map +0 -1
  73. package/lib/Spinner-1KrEmx1V.js.map +0 -1
@@ -5,7 +5,7 @@ import { j as d } from "./joinUrl-10po2Jdj.js";
5
5
  import { u as j, b as y } from "./hook-CfCFKZ-2.js";
6
6
  import { H as v } from "./index.esm-CltAN0Tf.js";
7
7
  import { Link as N } from "./zudoku.components.js";
8
- import { H as S, M as w } from "./Markdown-D1Y3cd9l.js";
8
+ import { H as S, M as w } from "./Markdown-hBN9vkm5.js";
9
9
  const H = ({
10
10
  items: s,
11
11
  filterCatalogItems: l = (n) => n,
@@ -1,6 +1,6 @@
1
1
  import { j as e } from "./jsx-runtime-CYK1ROHF.js";
2
2
  import { RotateCwIcon as j, TrashIcon as v, EyeOffIcon as w, EyeIcon as K, CheckIcon as b, CopyIcon as k, FileKey2Icon as N } from "lucide-react";
3
- import { D as I, S as x, R as S } from "./SlotletProvider-iDmNlxD5.js";
3
+ import { D as I, S as x, R as S } from "./SlotletProvider-D-XPr1Wg.js";
4
4
  import { i as c } from "./invariant-Caa8-XvF.js";
5
5
  import { u as h } from "./useQuery-CQUwWR9i.js";
6
6
  import { u as d, S as A, a as C, b as E, c as P, d as D, e as p } from "./Select-FAYHOYTy.js";
@@ -1,6 +1,6 @@
1
1
  import { j as o } from "./jsx-runtime-CYK1ROHF.js";
2
2
  import a from "react";
3
- import { P as n } from "./Markdown-D1Y3cd9l.js";
3
+ import { P as n } from "./Markdown-hBN9vkm5.js";
4
4
  import { c } from "./cn-qaFjX9_3.js";
5
5
  import { u as p } from "./useExposedProps-BslIn-FE.js";
6
6
  const u = ({
@@ -53,7 +53,7 @@ const P = (e) => ({
53
53
  const u = {
54
54
  path: r,
55
55
  lazy: async () => {
56
- const { MdxPage: p } = await import("./MdxPage-CUL_SQzW.js"), { default: f, ...l } = await i();
56
+ const { MdxPage: p } = await import("./MdxPage-UCWwxhzC.js"), { default: f, ...l } = await i();
57
57
  return {
58
58
  element: /* @__PURE__ */ d.jsx(
59
59
  p,
@@ -5,7 +5,7 @@ import "./chunk-HA7DTUK3-ZGg2W6yV.js";
5
5
  import "./hook-CfCFKZ-2.js";
6
6
  import "./ui/Button.js";
7
7
  import "./joinUrl-10po2Jdj.js";
8
- import { U as n, o as s } from "./index-C7TIhpXK.js";
8
+ import { U as n, o as s } from "./index-BVhQWA89.js";
9
9
  export {
10
10
  n as UNTAGGED_PATH,
11
11
  s as openApiPlugin
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zudoku",
3
- "version": "0.35.2",
3
+ "version": "0.35.4",
4
4
  "type": "module",
5
5
  "homepage": "https://zudoku.dev",
6
6
  "repository": {
@@ -1,4 +1,5 @@
1
1
  import type { ReactNode } from "react";
2
+ import { SelectOnClick } from "../plugins/openapi/components/SelectOnClick.js";
2
3
  import { cn } from "../util/cn.js";
3
4
 
4
5
  export const InlineCode = ({
@@ -10,20 +11,14 @@ export const InlineCode = ({
10
11
  children: ReactNode;
11
12
  selectOnClick?: boolean;
12
13
  }) => (
13
- <code
14
- onClick={(e) => {
15
- if (!selectOnClick) return;
16
- const selection = window.getSelection();
17
- const range = document.createRange();
18
- range.selectNodeContents(e.currentTarget);
19
- selection?.removeAllRanges();
20
- selection?.addRange(range);
21
- }}
22
- className={cn(
23
- "font-mono border p-1 py-0.5 rounded bg-border/50 dark:bg-border/70 [overflow-wrap:anywhere]",
24
- className,
25
- )}
26
- >
27
- {children}
28
- </code>
14
+ <SelectOnClick asChild enabled={selectOnClick}>
15
+ <code
16
+ className={cn(
17
+ "font-mono border p-1 py-0.5 rounded bg-border/50 dark:bg-border/70 [overflow-wrap:anywhere]",
18
+ className,
19
+ )}
20
+ >
21
+ {children}
22
+ </code>
23
+ </SelectOnClick>
29
24
  );
@@ -12,7 +12,7 @@ import { Slotlet } from "./SlotletProvider.js";
12
12
  import { Spinner } from "./Spinner.js";
13
13
 
14
14
  const LoadingFallback = () => (
15
- <main className="col-span-full grid place-items-center">
15
+ <main className="col-span-full row-span-full grid place-items-center">
16
16
  <Spinner />
17
17
  </main>
18
18
  );
@@ -62,7 +62,7 @@ export const Layout = ({ children }: { children?: ReactNode }) => {
62
62
  <Header />
63
63
  <Slotlet name="layout-after-head" />
64
64
 
65
- <div className="grid lg:grid-cols-[var(--side-nav-width)_1fr] max-w-screen-2xl w-full lg:mx-auto px-4 lg:px-8 2xl:border-x">
65
+ <div className="grid grid-cols-1 grid-rows-[min-content_1fr] lg:grid-cols-[var(--side-nav-width)_1fr] max-w-screen-2xl w-full lg:mx-auto px-4 lg:px-8 2xl:border-x">
66
66
  {showSpinner ? (
67
67
  <LoadingFallback />
68
68
  ) : (
@@ -1,5 +1,9 @@
1
1
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
2
+ import { SyntaxHighlight } from "zudoku/ui/SyntaxHighlight.js";
2
3
  import { DeveloperHint } from "../components/DeveloperHint.js";
4
+ import { Heading } from "../components/Heading.js";
5
+ import { ProseClasses } from "../components/Markdown.js";
6
+ import { cn } from "../util/cn.js";
3
7
  import { ZudokuError } from "../util/invariant.js";
4
8
 
5
9
  export function ErrorAlert({ error }: { error: unknown }) {
@@ -12,24 +16,27 @@ export function ErrorAlert({ error }: { error: unknown }) {
12
16
  const stack = error instanceof Error ? error.stack : undefined;
13
17
  const cause = error instanceof Error ? error.cause : undefined;
14
18
 
19
+ const stringError = cause instanceof Error ? String(cause.stack) : stack;
20
+
15
21
  return (
16
- <div className="flex h-screen max-h-[calc(100vh-var(--header-height))] min-h-full items-center justify-center bg-primary-background px-4 py-16 lg:px-8">
17
- <div className="mx-auto max-w-[85%] sm:max-w-[50%]">
18
- <h1 className="text-4xl font-bold tracking-tight text-h1-text sm:text-5xl">
19
- {title}
20
- </h1>
21
- <p className="mt-5 text-h1-text">{message}</p>
22
- {hint && <DeveloperHint className="mb-4">{hint}</DeveloperHint>}
23
- {cause instanceof Error ? (
24
- <pre className="mt-5 max-h-[400px] w-full overflow-scroll rounded-md border border-input-border bg-input-background p-3 text-property-name-text text-red-700">
25
- {cause.stack}
26
- </pre>
27
- ) : stack ? (
28
- <pre className="mt-5 max-h-[400px] w-full overflow-scroll rounded-md border border-input-border bg-input-background p-3 text-property-name-text text-red-700">
29
- {stack}
30
- </pre>
31
- ) : null}
32
- </div>
22
+ <div
23
+ className={cn(
24
+ ProseClasses,
25
+ "grid grid-cols-1 !max-w-none pt-[--padding-content-top]",
26
+ )}
27
+ >
28
+ <Heading level={1}>{title}</Heading>
29
+ Error: {message}
30
+ {hint && <DeveloperHint className="mb-4">{hint}</DeveloperHint>}
31
+ {stringError && (
32
+ <div>
33
+ <SyntaxHighlight
34
+ className="max-h-[400px] border mt-2"
35
+ language="jsstacktrace"
36
+ code={stringError}
37
+ />
38
+ </div>
39
+ )}
33
40
  </div>
34
41
  );
35
42
  }
@@ -1,5 +1,5 @@
1
1
  import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
2
- import { useRef, useState } from "react";
2
+ import { useState } from "react";
3
3
  import { Badge } from "zudoku/ui/Badge.js";
4
4
  import { Heading } from "../../components/Heading.js";
5
5
  import { Markdown, ProseClasses } from "../../components/Markdown.js";
@@ -10,6 +10,7 @@ import { renderIf } from "../../util/renderIf.js";
10
10
  import { OperationsFragment } from "./OperationList.js";
11
11
  import { ParameterList } from "./ParameterList.js";
12
12
  import { Sidecar } from "./Sidecar.js";
13
+ import { SelectOnClick } from "./components/SelectOnClick.js";
13
14
  import { type FragmentType, useFragment } from "./graphql/index.js";
14
15
  import { SchemaView } from "./schema/SchemaView.js";
15
16
  import { methodForColor } from "./util/methodToColor.js";
@@ -29,7 +30,6 @@ export const OperationListItem = ({
29
30
  operation.parameters ?? [],
30
31
  (param) => param.in,
31
32
  );
32
- const parentRef = useRef<HTMLDivElement>(null);
33
33
 
34
34
  const first = operation.responses.at(0);
35
35
  const [selectedResponse, setSelectedResponse] = useState(first?.statusCode);
@@ -60,19 +60,7 @@ export const OperationListItem = ({
60
60
  <span className={methodForColor(operation.method)}>
61
61
  {operation.method.toUpperCase()}
62
62
  </span>
63
- <div
64
- ref={parentRef}
65
- className="max-w-full truncate flex cursor-pointer"
66
- onClick={() => {
67
- if (parentRef.current) {
68
- const range = document.createRange();
69
- range.selectNodeContents(parentRef.current);
70
- const selection = window.getSelection();
71
- selection?.removeAllRanges();
72
- selection?.addRange(range);
73
- }
74
- }}
75
- >
63
+ <SelectOnClick className="max-w-full truncate flex cursor-pointer">
76
64
  {serverUrl && (
77
65
  <div className="text-neutral-400 dark:text-neutral-500 truncate">
78
66
  {serverUrl}
@@ -81,7 +69,7 @@ export const OperationListItem = ({
81
69
  <div className="text-neutral-900 dark:text-neutral-200">
82
70
  {operation.path}
83
71
  </div>
84
- </div>
72
+ </SelectOnClick>
85
73
  </div>
86
74
 
87
75
  <div className="flex flex-col gap-4">
@@ -0,0 +1,64 @@
1
+ import { isValidElement } from "react";
2
+ import { InlineCode } from "../../components/InlineCode.js";
3
+ import { type SchemaObject } from "../../oas/parser/index.js";
4
+
5
+ const getSchemaInfos = (schema?: SchemaObject) => {
6
+ if (!schema) return [];
7
+
8
+ return [
9
+ schema.type === "array" && schema.items.type
10
+ ? `${schema.items.type}[]`
11
+ : Array.isArray(schema.type)
12
+ ? schema.type.join(" | ")
13
+ : schema.type,
14
+
15
+ schema.enum && "enum",
16
+ schema.format,
17
+ schema.minimum && `min: ${schema.minimum}`,
18
+ schema.maximum && `max: ${schema.maximum}`,
19
+ schema.minLength && `minLength: ${schema.minLength}`,
20
+ schema.maxLength && `maxLength: ${schema.maxLength}`,
21
+ schema.minItems && `minItems: ${schema.minItems}`,
22
+ schema.maxItems && `maxItems: ${schema.maxItems}`,
23
+ schema.uniqueItems && "unique",
24
+ schema.minProperties && `minProps: ${schema.minProperties}`,
25
+ schema.maxProperties && `maxProps: ${schema.maxProperties}`,
26
+ schema.readOnly && "readOnly",
27
+ schema.writeOnly && "writeOnly",
28
+ schema.deprecated && "deprecated",
29
+ schema.pattern && (
30
+ <>
31
+ pattern: <InlineCode className="text-xs">{schema.pattern}</InlineCode>
32
+ </>
33
+ ),
34
+ ];
35
+ };
36
+
37
+ export const ParamInfos = ({
38
+ schema,
39
+ extraItems = [],
40
+ className,
41
+ }: {
42
+ schema?: SchemaObject;
43
+ extraItems?: unknown[];
44
+ className?: string;
45
+ }) => {
46
+ const filteredItems = [...extraItems, ...getSchemaInfos(schema)].flatMap(
47
+ (item) => (typeof item === "string" || isValidElement(item) ? item : []),
48
+ );
49
+
50
+ return (
51
+ <div className={className}>
52
+ {filteredItems.map((item, index) => (
53
+ <span className="text-muted-foreground" key={index}>
54
+ {item}
55
+ {index < filteredItems.length - 1 && (
56
+ <span className="text-muted-foreground/50">
57
+ &nbsp;&middot;&nbsp;
58
+ </span>
59
+ )}
60
+ </span>
61
+ ))}
62
+ </div>
63
+ );
64
+ };
@@ -1,9 +1,10 @@
1
- import { Badge } from "zudoku/ui/Badge.js";
2
1
  import { Markdown } from "../../components/Markdown.js";
3
2
  import { type SchemaObject } from "../../oas/graphql/index.js";
4
3
  import { ColorizedParam } from "./ColorizedParam.js";
5
4
  import type { OperationListItemResult } from "./OperationList.js";
6
5
  import type { ParameterGroup } from "./OperationListItem.js";
6
+ import { ParamInfos } from "./ParamInfos.js";
7
+ import { EnumValues } from "./components/EnumValues.js";
7
8
 
8
9
  const getParameterSchema = (
9
10
  parameter: ParameterListItemResult,
@@ -32,29 +33,27 @@ export const ParameterListItem = ({
32
33
  const paramSchema = getParameterSchema(parameter);
33
34
 
34
35
  return (
35
- <li className="p-4 bg-border/20 text-sm flex flex-col gap-1">
36
+ <li className="p-4 bg-border/20 text-sm flex flex-col gap-1.5">
36
37
  <div className="flex items-center gap-2">
37
38
  <code>
38
39
  {group === "path" ? (
39
40
  <ColorizedParam
40
41
  name={parameter.name}
41
42
  backgroundOpacity="15%"
42
- className="px-1"
43
+ className="px-2"
43
44
  slug={`${id}-${parameter.name}`}
44
45
  />
45
46
  ) : (
46
47
  parameter.name
47
48
  )}
48
49
  </code>
49
- {paramSchema.type && (
50
- <Badge variant="muted">
51
- {paramSchema.type === "array"
52
- ? `${paramSchema.items.type}[]`
53
- : paramSchema.type}
54
- </Badge>
55
- )}
56
- {parameter.required && <Badge variant="outline">required</Badge>}
57
- {parameter.style === "form" && <Badge variant="secondary">form</Badge>}
50
+ <ParamInfos
51
+ schema={paramSchema}
52
+ extraItems={[
53
+ parameter.required && "required",
54
+ parameter.style === "form" && "form",
55
+ ]}
56
+ />
58
57
  </div>
59
58
  {parameter.description && (
60
59
  <Markdown
@@ -62,6 +61,7 @@ export const ParameterListItem = ({
62
61
  className="text-sm prose-p:my-1 prose-code:whitespace-pre-line"
63
62
  />
64
63
  )}
64
+ {paramSchema.enum && <EnumValues values={paramSchema.enum} />}
65
65
  </li>
66
66
  );
67
67
  };
@@ -0,0 +1,58 @@
1
+ import { ChevronDownIcon, ChevronUpIcon } from "lucide-react";
2
+ import { useState } from "react";
3
+ import { Button } from "zudoku/ui/Button.js";
4
+ import { cn } from "../../../util/cn.js";
5
+ import { SelectOnClick } from "./SelectOnClick.js";
6
+
7
+ export const EnumValues = ({
8
+ values,
9
+ className,
10
+ maxVisibleValues = 8,
11
+ }: {
12
+ values: Array<string | number>;
13
+ className?: string;
14
+ maxVisibleValues?: number;
15
+ }) => {
16
+ const [isOpen, setIsOpen] = useState(false);
17
+
18
+ if (!values.length) return null;
19
+
20
+ const shouldCollapse = values.length > maxVisibleValues;
21
+ const visibleValues =
22
+ shouldCollapse && !isOpen ? values.slice(0, maxVisibleValues) : values;
23
+
24
+ return (
25
+ <div className={cn("flex flex-wrap gap-1.5 text-xs", className)}>
26
+ <span className="text-muted-foreground">Enum values: </span>
27
+ {visibleValues.map((value) => (
28
+ <div key={value}>
29
+ <SelectOnClick className="border rounded px-1 font-mono cursor-pointer">
30
+ {value}
31
+ </SelectOnClick>
32
+ </div>
33
+ ))}
34
+ {shouldCollapse && (
35
+ <Button
36
+ variant="ghost"
37
+ size="sm"
38
+ className="h-fit px-0"
39
+ onClick={() => setIsOpen(!isOpen)}
40
+ >
41
+ {isOpen ? (
42
+ <div className="flex items-center gap-1">
43
+ <ChevronUpIcon size={12} />
44
+ <span className="text-muted-foreground">show less</span>
45
+ </div>
46
+ ) : (
47
+ <div className="flex items-center gap-1">
48
+ <ChevronDownIcon size={12} />
49
+ <span className="text-muted-foreground">
50
+ show {values.length - maxVisibleValues} more
51
+ </span>
52
+ </div>
53
+ )}
54
+ </Button>
55
+ )}
56
+ </div>
57
+ );
58
+ };
@@ -0,0 +1,29 @@
1
+ import { Slot, type SlotProps } from "@radix-ui/react-slot";
2
+
3
+ export const SelectOnClick = ({
4
+ asChild,
5
+ onClick,
6
+ enabled = true,
7
+ ...props
8
+ }: {
9
+ asChild?: boolean;
10
+ enabled?: boolean;
11
+ } & SlotProps) => {
12
+ const Component = asChild ? Slot : "span";
13
+
14
+ return (
15
+ <Component
16
+ onClick={(e) => {
17
+ if (enabled) {
18
+ const range = document.createRange();
19
+ range.selectNodeContents(e.currentTarget);
20
+ const selection = window.getSelection();
21
+ selection?.removeAllRanges();
22
+ selection?.addRange(range);
23
+ }
24
+ onClick?.(e);
25
+ }}
26
+ {...props}
27
+ />
28
+ );
29
+ };
@@ -1,12 +1,13 @@
1
1
  import * as Collapsible from "@radix-ui/react-collapsible";
2
- import { ListPlusIcon, RefreshCcwDotIcon } from "lucide-react";
2
+ import { MinusIcon, PlusIcon, RefreshCcwDotIcon } from "lucide-react";
3
3
  import { useCallback, useState } from "react";
4
- import { Badge } from "zudoku/ui/Badge.js";
5
4
  import { Markdown, ProseClasses } from "../../../components/Markdown.js";
6
5
  import type { SchemaObject } from "../../../oas/parser/index.js";
7
6
  import { Button } from "../../../ui/Button.js";
8
7
  import { cn } from "../../../util/cn.js";
9
8
  import { objectEntries } from "../../../util/objectEntries.js";
9
+ import { EnumValues } from "../components/EnumValues.js";
10
+ import { ParamInfos } from "../ParamInfos.js";
10
11
  import { LogicalGroup } from "./LogicalGroup/LogicalGroup.js";
11
12
  import { SchemaView } from "./SchemaView.js";
12
13
  import {
@@ -42,8 +43,8 @@ export const SchemaLogicalGroup = ({
42
43
  };
43
44
 
44
45
  const RecursiveIndicator = () => (
45
- <div className="flex items-center gap-2 italic text-sm text-muted-foreground font-mono bg-muted px-2 py-0.5 rounded-md">
46
- <RefreshCcwDotIcon size={16} />
46
+ <div className="flex items-center gap-1.5 italic text-xs text-muted-foreground font-mono bg-muted px-2 py-0.5 rounded-md">
47
+ <RefreshCcwDotIcon size={13} />
47
48
  <span>circular</span>
48
49
  </div>
49
50
  );
@@ -68,11 +69,13 @@ export const SchemaPropertyItem = ({
68
69
  if (isCircularRef(schema)) {
69
70
  return (
70
71
  <li className="p-4 bg-border/20 hover:bg-border/30">
71
- <div className="flex flex-col gap-1 justify-between text-sm">
72
+ <div className="flex flex-col gap-2.5 justify-between text-sm">
72
73
  <div className="flex gap-2 items-center">
73
74
  <code>{name}</code>
74
- <Badge variant="muted">object</Badge>
75
- {group === "optional" && <Badge variant="outline">optional</Badge>}
75
+ <ParamInfos
76
+ schema={schema}
77
+ extraItems={[group === "optional" && "optional"]}
78
+ />
76
79
  <RecursiveIndicator />
77
80
  </div>
78
81
  </div>
@@ -82,30 +85,24 @@ export const SchemaPropertyItem = ({
82
85
 
83
86
  return (
84
87
  <li className="p-4 bg-border/20 hover:bg-border/30">
85
- <div className="flex flex-col gap-1 justify-between text-sm">
88
+ <div className="flex flex-col gap-2.5 justify-between text-sm">
86
89
  <div className="flex gap-2 items-center">
87
90
  <code>{name}</code>
88
- <Badge variant="muted">
89
- {schema.type === "array" && schema.items.type ? (
90
- <span>{schema.items.type}[]</span>
91
- ) : Array.isArray(schema.type) ? (
92
- <span>{schema.type.join(" | ")}</span>
93
- ) : (
94
- <span>{schema.type}</span>
95
- )}
96
- </Badge>
97
- {group === "optional" && <Badge variant="outline">optional</Badge>}
91
+ <ParamInfos
92
+ schema={schema}
93
+ extraItems={[group === "optional" && "optional"]}
94
+ />
98
95
  {schema.type === "array" &&
99
96
  "items" in schema &&
100
97
  isCircularRef(schema.items) && <RecursiveIndicator />}
101
98
  </div>
102
-
103
99
  {schema.description && (
104
100
  <Markdown
105
101
  className={cn(ProseClasses, "text-sm leading-normal line-clamp-4")}
106
102
  content={schema.description}
107
103
  />
108
104
  )}
105
+ {schema.enum && <EnumValues values={schema.enum} />}
109
106
 
110
107
  {(hasLogicalGroupings(schema) || isComplexType(schema)) && (
111
108
  <Collapsible.Root
@@ -115,15 +112,9 @@ export const SchemaPropertyItem = ({
115
112
  >
116
113
  {showCollapseButton && (
117
114
  <Collapsible.Trigger asChild>
118
- <Button
119
- variant="outline"
120
- size="sm"
121
- className="mt-2 flex gap-1.5"
122
- >
123
- <ListPlusIcon size={18} />
124
- {!isOpen
125
- ? "Show nested properties"
126
- : "Hide nested properties"}
115
+ <Button variant="expand" size="sm" className="h-7">
116
+ {isOpen ? <MinusIcon size={12} /> : <PlusIcon size={12} />}
117
+ {!isOpen ? "Show properties" : "Hide properties"}
127
118
  </Button>
128
119
  </Collapsible.Trigger>
129
120
  )}
@@ -1,5 +1,5 @@
1
1
  import { Slot } from "@radix-ui/react-slot";
2
- import { cva, VariantProps } from "class-variance-authority";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
3
  import * as React from "react";
4
4
  import { cn } from "../util/cn.js";
5
5
 
@@ -18,6 +18,8 @@ export const buttonVariants = cva(
18
18
  "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
19
19
  ghost: "hover:bg-accent hover:text-accent-foreground",
20
20
  link: "text-primary underline-offset-4 hover:underline",
21
+ expand:
22
+ "flex gap-1.5 border bg-transparent rounded-xl text-muted-foreground hover:text-foreground",
21
23
  },
22
24
  size: {
23
25
  default: "h-9 px-4 py-2",
@@ -28,6 +28,8 @@ void import("prismjs/components/prism-markdown.min.js");
28
28
  void import("prismjs/components/prism-javascript.min.js");
29
29
  // @ts-expect-error This is untyped
30
30
  void import("prismjs/components/prism-typescript.min.js");
31
+ // @ts-expect-error This is untyped
32
+ void import("prismjs/components/prism-jsstacktrace.min.js");
31
33
 
32
34
  import { useTheme } from "next-themes";
33
35
  import { memo, useState } from "react";