@tonyclaw/llm-inspector 1.6.0

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 (286) hide show
  1. package/.output/nitro.json +17 -0
  2. package/.output/public/assets/alibaba-TTwafVwX.svg +1 -0
  3. package/.output/public/assets/index-B3RwBPLW.css +1 -0
  4. package/.output/public/assets/index-s4lwsWvq.js +97 -0
  5. package/.output/public/assets/main-Cp8AM0Pa.js +17 -0
  6. package/.output/public/assets/minimax-BPMzvuL-.jpeg +0 -0
  7. package/.output/public/assets/qwen-CONDcHqt.png +0 -0
  8. package/.output/public/assets/zhipuai-BPNAnxo-.svg +219 -0
  9. package/.output/server/_chunks/ssr-renderer.mjs +17 -0
  10. package/.output/server/_libs/@radix-ui/react-accessible-icon+[...].mjs +1 -0
  11. package/.output/server/_libs/@radix-ui/react-dismissable-layer+[...].mjs +210 -0
  12. package/.output/server/_libs/@radix-ui/react-navigation-menu+[...].mjs +1 -0
  13. package/.output/server/_libs/@radix-ui/react-one-time-password-field+[...].mjs +1 -0
  14. package/.output/server/_libs/@radix-ui/react-password-toggle-field+[...].mjs +1 -0
  15. package/.output/server/_libs/@radix-ui/react-use-callback-ref+[...].mjs +11 -0
  16. package/.output/server/_libs/@radix-ui/react-use-controllable-state+[...].mjs +69 -0
  17. package/.output/server/_libs/@radix-ui/react-use-effect-event+[...].mjs +1 -0
  18. package/.output/server/_libs/@radix-ui/react-use-escape-keydown+[...].mjs +17 -0
  19. package/.output/server/_libs/@radix-ui/react-use-is-hydrated+[...].mjs +1 -0
  20. package/.output/server/_libs/@radix-ui/react-use-layout-effect+[...].mjs +6 -0
  21. package/.output/server/_libs/@radix-ui/react-visually-hidden+[...].mjs +34 -0
  22. package/.output/server/_libs/ajv-formats.mjs +330 -0
  23. package/.output/server/_libs/ajv.mjs +11444 -0
  24. package/.output/server/_libs/aria-hidden.mjs +122 -0
  25. package/.output/server/_libs/atomically.mjs +152 -0
  26. package/.output/server/_libs/bail.mjs +8 -0
  27. package/.output/server/_libs/character-entities.mjs +2130 -0
  28. package/.output/server/_libs/class-variance-authority.mjs +44 -0
  29. package/.output/server/_libs/clsx.mjs +16 -0
  30. package/.output/server/_libs/comma-separated-tokens.mjs +10 -0
  31. package/.output/server/_libs/conf.mjs +635 -0
  32. package/.output/server/_libs/cookie-es.mjs +58 -0
  33. package/.output/server/_libs/core-util-is.mjs +75 -0
  34. package/.output/server/_libs/croner.mjs +1 -0
  35. package/.output/server/_libs/crossws.mjs +1 -0
  36. package/.output/server/_libs/debounce-fn.mjs +69 -0
  37. package/.output/server/_libs/decode-named-character-reference+[...].mjs +8 -0
  38. package/.output/server/_libs/detect-node-es.mjs +1 -0
  39. package/.output/server/_libs/devlop.mjs +8 -0
  40. package/.output/server/_libs/dot-prop.mjs +265 -0
  41. package/.output/server/_libs/env-paths.mjs +57 -0
  42. package/.output/server/_libs/estree-util-is-identifier-name.mjs +11 -0
  43. package/.output/server/_libs/extend.mjs +97 -0
  44. package/.output/server/_libs/fast-deep-equal.mjs +38 -0
  45. package/.output/server/_libs/fast-uri.mjs +812 -0
  46. package/.output/server/_libs/floating-ui__core.mjs +725 -0
  47. package/.output/server/_libs/floating-ui__dom.mjs +622 -0
  48. package/.output/server/_libs/floating-ui__react-dom.mjs +292 -0
  49. package/.output/server/_libs/floating-ui__utils.mjs +320 -0
  50. package/.output/server/_libs/get-nonce.mjs +9 -0
  51. package/.output/server/_libs/h3-v2.mjs +276 -0
  52. package/.output/server/_libs/h3.mjs +400 -0
  53. package/.output/server/_libs/hast-util-to-jsx-runtime.mjs +388 -0
  54. package/.output/server/_libs/hast-util-whitespace.mjs +10 -0
  55. package/.output/server/_libs/hookable.mjs +1 -0
  56. package/.output/server/_libs/html-url-attributes.mjs +26 -0
  57. package/.output/server/_libs/immediate.mjs +74 -0
  58. package/.output/server/_libs/inherits.mjs +50 -0
  59. package/.output/server/_libs/inline-style-parser.mjs +142 -0
  60. package/.output/server/_libs/is-plain-obj.mjs +10 -0
  61. package/.output/server/_libs/isarray.mjs +14 -0
  62. package/.output/server/_libs/isbot.mjs +20 -0
  63. package/.output/server/_libs/json-schema-traverse.mjs +180 -0
  64. package/.output/server/_libs/jszip.mjs +3049 -0
  65. package/.output/server/_libs/lie.mjs +273 -0
  66. package/.output/server/_libs/lucide-react.mjs +368 -0
  67. package/.output/server/_libs/mdast-util-from-markdown.mjs +717 -0
  68. package/.output/server/_libs/mdast-util-to-hast.mjs +710 -0
  69. package/.output/server/_libs/mdast-util-to-string.mjs +38 -0
  70. package/.output/server/_libs/micromark-core-commonmark.mjs +2259 -0
  71. package/.output/server/_libs/micromark-factory-destination.mjs +94 -0
  72. package/.output/server/_libs/micromark-factory-label.mjs +63 -0
  73. package/.output/server/_libs/micromark-factory-space.mjs +24 -0
  74. package/.output/server/_libs/micromark-factory-title.mjs +65 -0
  75. package/.output/server/_libs/micromark-factory-whitespace.mjs +22 -0
  76. package/.output/server/_libs/micromark-util-character.mjs +44 -0
  77. package/.output/server/_libs/micromark-util-chunked.mjs +36 -0
  78. package/.output/server/_libs/micromark-util-classify-character+[...].mjs +12 -0
  79. package/.output/server/_libs/micromark-util-combine-extensions+[...].mjs +41 -0
  80. package/.output/server/_libs/micromark-util-decode-numeric-character-reference+[...].mjs +19 -0
  81. package/.output/server/_libs/micromark-util-decode-string.mjs +21 -0
  82. package/.output/server/_libs/micromark-util-encode.mjs +1 -0
  83. package/.output/server/_libs/micromark-util-html-tag-name.mjs +69 -0
  84. package/.output/server/_libs/micromark-util-normalize-identifier+[...].mjs +6 -0
  85. package/.output/server/_libs/micromark-util-resolve-all.mjs +15 -0
  86. package/.output/server/_libs/micromark-util-sanitize-uri.mjs +41 -0
  87. package/.output/server/_libs/micromark-util-subtokenize.mjs +346 -0
  88. package/.output/server/_libs/micromark.mjs +906 -0
  89. package/.output/server/_libs/mimic-function.mjs +47 -0
  90. package/.output/server/_libs/ohash.mjs +1 -0
  91. package/.output/server/_libs/pako.mjs +4223 -0
  92. package/.output/server/_libs/process-nextick-args.mjs +48 -0
  93. package/.output/server/_libs/property-information.mjs +1209 -0
  94. package/.output/server/_libs/radix-ui.mjs +1 -0
  95. package/.output/server/_libs/radix-ui__number.mjs +6 -0
  96. package/.output/server/_libs/radix-ui__primitive.mjs +11 -0
  97. package/.output/server/_libs/radix-ui__react-accordion.mjs +1 -0
  98. package/.output/server/_libs/radix-ui__react-alert-dialog.mjs +1 -0
  99. package/.output/server/_libs/radix-ui__react-arrow.mjs +23 -0
  100. package/.output/server/_libs/radix-ui__react-aspect-ratio.mjs +1 -0
  101. package/.output/server/_libs/radix-ui__react-avatar.mjs +1 -0
  102. package/.output/server/_libs/radix-ui__react-checkbox.mjs +1 -0
  103. package/.output/server/_libs/radix-ui__react-collapsible.mjs +144 -0
  104. package/.output/server/_libs/radix-ui__react-collection.mjs +69 -0
  105. package/.output/server/_libs/radix-ui__react-compose-refs.mjs +39 -0
  106. package/.output/server/_libs/radix-ui__react-context-menu.mjs +1 -0
  107. package/.output/server/_libs/radix-ui__react-context.mjs +78 -0
  108. package/.output/server/_libs/radix-ui__react-dialog.mjs +325 -0
  109. package/.output/server/_libs/radix-ui__react-direction.mjs +9 -0
  110. package/.output/server/_libs/radix-ui__react-dropdown-menu.mjs +1 -0
  111. package/.output/server/_libs/radix-ui__react-focus-guards.mjs +29 -0
  112. package/.output/server/_libs/radix-ui__react-focus-scope.mjs +206 -0
  113. package/.output/server/_libs/radix-ui__react-form.mjs +1 -0
  114. package/.output/server/_libs/radix-ui__react-hover-card.mjs +1 -0
  115. package/.output/server/_libs/radix-ui__react-id.mjs +14 -0
  116. package/.output/server/_libs/radix-ui__react-label.mjs +1 -0
  117. package/.output/server/_libs/radix-ui__react-menu.mjs +1 -0
  118. package/.output/server/_libs/radix-ui__react-menubar.mjs +1 -0
  119. package/.output/server/_libs/radix-ui__react-popover.mjs +1 -0
  120. package/.output/server/_libs/radix-ui__react-popper.mjs +286 -0
  121. package/.output/server/_libs/radix-ui__react-portal.mjs +16 -0
  122. package/.output/server/_libs/radix-ui__react-presence.mjs +128 -0
  123. package/.output/server/_libs/radix-ui__react-primitive.mjs +42 -0
  124. package/.output/server/_libs/radix-ui__react-progress.mjs +1 -0
  125. package/.output/server/_libs/radix-ui__react-radio-group.mjs +1 -0
  126. package/.output/server/_libs/radix-ui__react-roving-focus.mjs +224 -0
  127. package/.output/server/_libs/radix-ui__react-scroll-area.mjs +721 -0
  128. package/.output/server/_libs/radix-ui__react-select.mjs +1163 -0
  129. package/.output/server/_libs/radix-ui__react-separator.mjs +28 -0
  130. package/.output/server/_libs/radix-ui__react-slider.mjs +1 -0
  131. package/.output/server/_libs/radix-ui__react-slot.mjs +99 -0
  132. package/.output/server/_libs/radix-ui__react-switch.mjs +1 -0
  133. package/.output/server/_libs/radix-ui__react-tabs.mjs +189 -0
  134. package/.output/server/_libs/radix-ui__react-toast.mjs +1 -0
  135. package/.output/server/_libs/radix-ui__react-toggle-group.mjs +1 -0
  136. package/.output/server/_libs/radix-ui__react-toggle.mjs +1 -0
  137. package/.output/server/_libs/radix-ui__react-toolbar.mjs +1 -0
  138. package/.output/server/_libs/radix-ui__react-tooltip.mjs +495 -0
  139. package/.output/server/_libs/radix-ui__react-use-previous.mjs +14 -0
  140. package/.output/server/_libs/radix-ui__react-use-size.mjs +39 -0
  141. package/.output/server/_libs/react-dom.mjs +9935 -0
  142. package/.output/server/_libs/react-markdown.mjs +147 -0
  143. package/.output/server/_libs/react-remove-scroll-bar.mjs +82 -0
  144. package/.output/server/_libs/react-remove-scroll.mjs +328 -0
  145. package/.output/server/_libs/react-style-singleton.mjs +69 -0
  146. package/.output/server/_libs/react.mjs +515 -0
  147. package/.output/server/_libs/readable-stream.mjs +1518 -0
  148. package/.output/server/_libs/remark-parse.mjs +19 -0
  149. package/.output/server/_libs/remark-rehype.mjs +21 -0
  150. package/.output/server/_libs/rou3.mjs +8 -0
  151. package/.output/server/_libs/safe-buffer.mjs +64 -0
  152. package/.output/server/_libs/semver.mjs +1984 -0
  153. package/.output/server/_libs/seroval-plugins.mjs +58 -0
  154. package/.output/server/_libs/seroval.mjs +1765 -0
  155. package/.output/server/_libs/setimmediate.mjs +1 -0
  156. package/.output/server/_libs/space-separated-tokens.mjs +6 -0
  157. package/.output/server/_libs/srvx.mjs +334 -0
  158. package/.output/server/_libs/stubborn-fs.mjs +91 -0
  159. package/.output/server/_libs/stubborn-utils.mjs +66 -0
  160. package/.output/server/_libs/style-to-js.mjs +72 -0
  161. package/.output/server/_libs/style-to-object.mjs +38 -0
  162. package/.output/server/_libs/tailwind-merge.mjs +3010 -0
  163. package/.output/server/_libs/tanstack__history.mjs +217 -0
  164. package/.output/server/_libs/tanstack__react-router.mjs +1480 -0
  165. package/.output/server/_libs/tanstack__react-store.mjs +1 -0
  166. package/.output/server/_libs/tanstack__react-virtual.mjs +44 -0
  167. package/.output/server/_libs/tanstack__router-core.mjs +4827 -0
  168. package/.output/server/_libs/tanstack__store.mjs +1 -0
  169. package/.output/server/_libs/tanstack__virtual-core.mjs +1225 -0
  170. package/.output/server/_libs/tiny-invariant.mjs +12 -0
  171. package/.output/server/_libs/tiny-warning.mjs +5 -0
  172. package/.output/server/_libs/trim-lines.mjs +41 -0
  173. package/.output/server/_libs/trough.mjs +85 -0
  174. package/.output/server/_libs/tslib.mjs +576 -0
  175. package/.output/server/_libs/ufo.mjs +54 -0
  176. package/.output/server/_libs/uint8array-extras.mjs +69 -0
  177. package/.output/server/_libs/unctx.mjs +1 -0
  178. package/.output/server/_libs/ungap__structured-clone.mjs +212 -0
  179. package/.output/server/_libs/unified.mjs +661 -0
  180. package/.output/server/_libs/unist-util-is.mjs +100 -0
  181. package/.output/server/_libs/unist-util-position.mjs +27 -0
  182. package/.output/server/_libs/unist-util-stringify-position.mjs +27 -0
  183. package/.output/server/_libs/unist-util-visit-parents.mjs +82 -0
  184. package/.output/server/_libs/unist-util-visit.mjs +24 -0
  185. package/.output/server/_libs/unstorage.mjs +1 -0
  186. package/.output/server/_libs/use-callback-ref.mjs +66 -0
  187. package/.output/server/_libs/use-sidecar.mjs +106 -0
  188. package/.output/server/_libs/use-sync-external-store.mjs +1 -0
  189. package/.output/server/_libs/util-deprecate.mjs +12 -0
  190. package/.output/server/_libs/vfile-message.mjs +138 -0
  191. package/.output/server/_libs/vfile.mjs +467 -0
  192. package/.output/server/_libs/when-exit.mjs +53 -0
  193. package/.output/server/_libs/zod.mjs +4460 -0
  194. package/.output/server/_ssr/index-ByCLZu7J.mjs +3061 -0
  195. package/.output/server/_ssr/index.mjs +1176 -0
  196. package/.output/server/_ssr/router-Bq_mxeNz.mjs +2872 -0
  197. package/.output/server/_ssr/start-HYkvq4Ni.mjs +4 -0
  198. package/.output/server/_tanstack-start-manifest_v-C4E0e9my.mjs +4 -0
  199. package/.output/server/index.mjs +393 -0
  200. package/README.md +196 -0
  201. package/package.json +91 -0
  202. package/src/assets/logos/alibaba.svg +1 -0
  203. package/src/assets/logos/anthropic.svg +1 -0
  204. package/src/assets/logos/deepseek.svg +1 -0
  205. package/src/assets/logos/minimax.jpeg +0 -0
  206. package/src/assets/logos/openai.svg +1 -0
  207. package/src/assets/logos/qwen.png +0 -0
  208. package/src/assets/logos/zhipuai.svg +219 -0
  209. package/src/cli.ts +68 -0
  210. package/src/components/ProxyViewer.tsx +325 -0
  211. package/src/components/ProxyViewerContainer.tsx +211 -0
  212. package/src/components/providers/ProviderCard.tsx +186 -0
  213. package/src/components/providers/ProviderForm.tsx +259 -0
  214. package/src/components/providers/ProviderLogo.tsx +111 -0
  215. package/src/components/providers/ProvidersPanel.tsx +259 -0
  216. package/src/components/providers/SettingsDialog.tsx +39 -0
  217. package/src/components/proxy-viewer/ConversationGroup.tsx +68 -0
  218. package/src/components/proxy-viewer/ConversationHeader.tsx +141 -0
  219. package/src/components/proxy-viewer/LogEntry.tsx +225 -0
  220. package/src/components/proxy-viewer/LogEntryHeader.tsx +250 -0
  221. package/src/components/proxy-viewer/ReplayDialog.tsx +208 -0
  222. package/src/components/proxy-viewer/ResponseView.tsx +161 -0
  223. package/src/components/proxy-viewer/StreamingChunkSequence.tsx +171 -0
  224. package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +139 -0
  225. package/src/components/proxy-viewer/formats/anthropic/ResponseView.tsx +64 -0
  226. package/src/components/proxy-viewer/formats/index.tsx +24 -0
  227. package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +80 -0
  228. package/src/components/proxy-viewer/index.ts +8 -0
  229. package/src/components/ui/badge.tsx +47 -0
  230. package/src/components/ui/button.tsx +47 -0
  231. package/src/components/ui/collapsible.tsx +21 -0
  232. package/src/components/ui/dialog.tsx +129 -0
  233. package/src/components/ui/json-viewer.tsx +464 -0
  234. package/src/components/ui/scroll-area.tsx +54 -0
  235. package/src/components/ui/select.tsx +178 -0
  236. package/src/components/ui/separator.tsx +28 -0
  237. package/src/components/ui/tabs.tsx +88 -0
  238. package/src/components/ui/tooltip.tsx +51 -0
  239. package/src/index.css +11 -0
  240. package/src/lib/export-logs.ts +51 -0
  241. package/src/lib/utils.ts +22 -0
  242. package/src/proxy/chunkStorage.ts +118 -0
  243. package/src/proxy/constants.ts +36 -0
  244. package/src/proxy/formats/anthropic/anthropicProvider.ts +75 -0
  245. package/src/proxy/formats/anthropic/handler.ts +74 -0
  246. package/src/proxy/formats/anthropic/index.ts +14 -0
  247. package/src/proxy/formats/anthropic/register.ts +4 -0
  248. package/src/proxy/formats/anthropic/schemas.ts +217 -0
  249. package/src/proxy/formats/anthropic/stream.ts +167 -0
  250. package/src/proxy/formats/handler.ts +46 -0
  251. package/src/proxy/formats/index.ts +12 -0
  252. package/src/proxy/formats/jsonSchema.ts +24 -0
  253. package/src/proxy/formats/openai/alibabaProvider.ts +38 -0
  254. package/src/proxy/formats/openai/handler.ts +70 -0
  255. package/src/proxy/formats/openai/index.ts +25 -0
  256. package/src/proxy/formats/openai/provider.ts +50 -0
  257. package/src/proxy/formats/openai/register.ts +4 -0
  258. package/src/proxy/formats/openai/schemas.ts +150 -0
  259. package/src/proxy/formats/openai/stream.ts +153 -0
  260. package/src/proxy/formats/protocol.ts +50 -0
  261. package/src/proxy/formats/providerRegistry.ts +51 -0
  262. package/src/proxy/formats/providers/index.ts +3 -0
  263. package/src/proxy/formats/registry.ts +61 -0
  264. package/src/proxy/handler.ts +389 -0
  265. package/src/proxy/logIndex.ts +187 -0
  266. package/src/proxy/logger.ts +99 -0
  267. package/src/proxy/providers.ts +234 -0
  268. package/src/proxy/schemas.ts +160 -0
  269. package/src/proxy/socketTracker.ts +158 -0
  270. package/src/proxy/store.ts +386 -0
  271. package/src/router.tsx +16 -0
  272. package/src/routes/__root.tsx +38 -0
  273. package/src/routes/api/config.paths.ts +14 -0
  274. package/src/routes/api/health.ts +11 -0
  275. package/src/routes/api/logs.$id.chunks.ts +36 -0
  276. package/src/routes/api/logs.$id.replay.ts +262 -0
  277. package/src/routes/api/logs.$id.ts +22 -0
  278. package/src/routes/api/logs.stream.ts +64 -0
  279. package/src/routes/api/logs.ts +30 -0
  280. package/src/routes/api/models.ts +10 -0
  281. package/src/routes/api/providers.$providerId.ts +45 -0
  282. package/src/routes/api/providers.ts +37 -0
  283. package/src/routes/api/sessions.ts +10 -0
  284. package/src/routes/index.tsx +6 -0
  285. package/src/routes/proxy/$.ts +15 -0
  286. package/styles/globals.css +121 -0
@@ -0,0 +1,47 @@
1
+ import { cva, type VariantProps } from "class-variance-authority";
2
+ import { Slot } from "radix-ui";
3
+ import * as React from "react";
4
+
5
+ import { cn } from "../../lib/utils";
6
+
7
+ const badgeVariants = cva(
8
+ "inline-flex items-center justify-center rounded-full border border-transparent px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
13
+ secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
14
+ destructive:
15
+ "bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
16
+ outline:
17
+ "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
18
+ ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
19
+ link: "text-primary underline-offset-4 [a&]:hover:underline",
20
+ },
21
+ },
22
+ defaultVariants: {
23
+ variant: "default",
24
+ },
25
+ },
26
+ );
27
+
28
+ function Badge({
29
+ className,
30
+ variant = "default",
31
+ asChild = false,
32
+ ...props
33
+ }: React.ComponentProps<"span"> &
34
+ VariantProps<typeof badgeVariants> & { asChild?: boolean }): React.JSX.Element {
35
+ const Comp = asChild ? Slot.Root : "span";
36
+
37
+ return (
38
+ <Comp
39
+ data-slot="badge"
40
+ data-variant={variant}
41
+ className={cn(badgeVariants({ variant }), className)}
42
+ {...props}
43
+ />
44
+ );
45
+ }
46
+
47
+ export { Badge, badgeVariants };
@@ -0,0 +1,47 @@
1
+ import { cva, type VariantProps } from "class-variance-authority";
2
+ import * as React from "react";
3
+
4
+ import { cn } from "../../lib/utils";
5
+
6
+ const buttonVariants = cva(
7
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
12
+ destructive: "bg-destructive text-white hover:bg-destructive/90",
13
+ outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
14
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
15
+ ghost: "hover:bg-accent hover:text-accent-foreground",
16
+ link: "text-primary underline-offset-4 hover:underline",
17
+ },
18
+ size: {
19
+ default: "h-9 px-4 py-2",
20
+ sm: "h-8 rounded-md px-3 text-xs",
21
+ lg: "h-10 rounded-md px-8",
22
+ icon: "size-9",
23
+ },
24
+ },
25
+ defaultVariants: {
26
+ variant: "default",
27
+ size: "default",
28
+ },
29
+ },
30
+ );
31
+
32
+ function Button({
33
+ className,
34
+ variant,
35
+ size,
36
+ ...props
37
+ }: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants>): React.JSX.Element {
38
+ return (
39
+ <button
40
+ data-slot="button"
41
+ className={cn(buttonVariants({ variant, size, className }))}
42
+ {...props}
43
+ />
44
+ );
45
+ }
46
+
47
+ export { Button, buttonVariants };
@@ -0,0 +1,21 @@
1
+ import { Collapsible as CollapsiblePrimitive } from "radix-ui";
2
+
3
+ function Collapsible({
4
+ ...props
5
+ }: React.ComponentProps<typeof CollapsiblePrimitive.Root>): React.JSX.Element {
6
+ return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />;
7
+ }
8
+
9
+ function CollapsibleTrigger({
10
+ ...props
11
+ }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>): React.JSX.Element {
12
+ return <CollapsiblePrimitive.CollapsibleTrigger data-slot="collapsible-trigger" {...props} />;
13
+ }
14
+
15
+ function CollapsibleContent({
16
+ ...props
17
+ }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>): React.JSX.Element {
18
+ return <CollapsiblePrimitive.CollapsibleContent data-slot="collapsible-content" {...props} />;
19
+ }
20
+
21
+ export { Collapsible, CollapsibleTrigger, CollapsibleContent };
@@ -0,0 +1,129 @@
1
+ "use client";
2
+
3
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
4
+ import { X } from "lucide-react";
5
+ import * as React from "react";
6
+
7
+ import { cn } from "../../lib/utils";
8
+
9
+ function Dialog({
10
+ ...props
11
+ }: React.ComponentProps<typeof DialogPrimitive.Root>): React.JSX.Element {
12
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />;
13
+ }
14
+
15
+ function DialogTrigger({
16
+ ...props
17
+ }: React.ComponentProps<typeof DialogPrimitive.Trigger>): React.JSX.Element {
18
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
19
+ }
20
+
21
+ function DialogPortal({
22
+ ...props
23
+ }: React.ComponentProps<typeof DialogPrimitive.Portal>): React.JSX.Element {
24
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
25
+ }
26
+
27
+ function DialogClose({
28
+ ...props
29
+ }: React.ComponentProps<typeof DialogPrimitive.Close>): React.JSX.Element {
30
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
31
+ }
32
+
33
+ function DialogOverlay({
34
+ className,
35
+ ...props
36
+ }: React.ComponentProps<typeof DialogPrimitive.Overlay>): React.JSX.Element {
37
+ return (
38
+ <DialogPrimitive.Overlay
39
+ data-slot="dialog-overlay"
40
+ className={cn(
41
+ "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
42
+ className,
43
+ )}
44
+ {...props}
45
+ />
46
+ );
47
+ }
48
+
49
+ function DialogContent({
50
+ className,
51
+ children,
52
+ ...props
53
+ }: React.ComponentProps<typeof DialogPrimitive.Content>): React.JSX.Element {
54
+ return (
55
+ <DialogPortal>
56
+ <DialogOverlay />
57
+ <DialogPrimitive.Content
58
+ data-slot="dialog-content"
59
+ className={cn(
60
+ "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] rounded-lg",
61
+ className,
62
+ )}
63
+ {...props}
64
+ >
65
+ {children}
66
+ <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
67
+ <X className="size-4" />
68
+ <span className="sr-only">Close</span>
69
+ </DialogPrimitive.Close>
70
+ </DialogPrimitive.Content>
71
+ </DialogPortal>
72
+ );
73
+ }
74
+
75
+ function DialogHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element {
76
+ return (
77
+ <div
78
+ data-slot="dialog-header"
79
+ className={cn("flex flex-col space-y-1.5 text-center sm:text-left", className)}
80
+ {...props}
81
+ />
82
+ );
83
+ }
84
+
85
+ function DialogFooter({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element {
86
+ return (
87
+ <div
88
+ data-slot="dialog-footer"
89
+ className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
90
+ {...props}
91
+ />
92
+ );
93
+ }
94
+
95
+ function DialogTitle({
96
+ className,
97
+ ...props
98
+ }: React.ComponentProps<typeof DialogPrimitive.Title>): React.JSX.Element {
99
+ return (
100
+ <DialogPrimitive.Title
101
+ data-slot="dialog-title"
102
+ className={cn("text-lg font-semibold leading-none tracking-tight", className)}
103
+ {...props}
104
+ />
105
+ );
106
+ }
107
+
108
+ function DialogDescription({
109
+ className,
110
+ ...props
111
+ }: React.ComponentProps<typeof DialogPrimitive.Description>): React.JSX.Element {
112
+ return (
113
+ <DialogPrimitive.Description
114
+ data-slot="dialog-description"
115
+ className={cn("text-sm text-muted-foreground", className)}
116
+ {...props}
117
+ />
118
+ );
119
+ }
120
+
121
+ export {
122
+ Dialog,
123
+ DialogPortal,
124
+ DialogOverlay,
125
+ DialogTrigger,
126
+ DialogContent,
127
+ DialogHeader,
128
+ DialogTitle,
129
+ };
@@ -0,0 +1,464 @@
1
+ import { Check, ChevronDown, ChevronRight, ChevronsDown, ChevronsUp, Copy } from "lucide-react";
2
+ import { type JSX, useState } from "react";
3
+ import ReactMarkdown from "react-markdown";
4
+ import { cn } from "../../lib/utils";
5
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./tooltip";
6
+
7
+ type JsonPrimitive = string | number | boolean | null;
8
+ type JsonValue = JsonPrimitive | ReadonlyArray<JsonValue> | Readonly<{ [key: string]: JsonValue }>;
9
+
10
+ type DataType = "string" | "number" | "boolean" | "null" | "array" | "object";
11
+
12
+ function classifyValue(value: JsonValue): DataType {
13
+ if (value === null) return "null";
14
+ if (Array.isArray(value)) return "array";
15
+ switch (typeof value) {
16
+ case "string":
17
+ return "string";
18
+ case "number":
19
+ return "number";
20
+ case "boolean":
21
+ return "boolean";
22
+ case "object":
23
+ return "object";
24
+ case "bigint":
25
+ case "symbol":
26
+ case "undefined":
27
+ case "function":
28
+ return "object";
29
+ }
30
+ }
31
+
32
+ function isExpandable(value: JsonValue): boolean {
33
+ return value !== null && (Array.isArray(value) || typeof value === "object");
34
+ }
35
+
36
+ function getPropertyValue(obj: object, key: string): unknown {
37
+ const descriptor = Object.getOwnPropertyDescriptor(obj, key);
38
+ if (descriptor === undefined) return undefined;
39
+ return descriptor.value;
40
+ }
41
+
42
+ function getEntries(value: JsonValue): ReadonlyArray<readonly [string, JsonValue]> {
43
+ if (Array.isArray(value)) {
44
+ return value.map((item, index) => [String(index), item] as const);
45
+ }
46
+ if (typeof value === "object" && value !== null) {
47
+ return Object.keys(value).map((key) => {
48
+ const raw = getPropertyValue(value, key);
49
+ return [key, safeJsonValue(raw)] as const;
50
+ });
51
+ }
52
+ return [];
53
+ }
54
+
55
+ function getItemCount(value: JsonValue): number {
56
+ if (Array.isArray(value)) return value.length;
57
+ if (typeof value === "object" && value !== null) return Object.keys(value).length;
58
+ return 0;
59
+ }
60
+
61
+ const STRING_TRUNCATE_LIMIT = 120;
62
+
63
+ function StringValue({ text }: { text: string }): JSX.Element {
64
+ const [expanded, setExpanded] = useState(false);
65
+ const isLong = text.length > STRING_TRUNCATE_LIMIT;
66
+
67
+ if (!isLong) {
68
+ return (
69
+ <span className="text-emerald-400 break-all">
70
+ &quot;
71
+ <span className="prose prose-sm dark:prose-invert inline max-w-none [&_p]:inline [&_p]:my-0 [&_code]:text-emerald-300 [&_a]:text-emerald-300">
72
+ <ReactMarkdown>{text}</ReactMarkdown>
73
+ </span>
74
+ &quot;
75
+ </span>
76
+ );
77
+ }
78
+
79
+ return (
80
+ <span className="text-emerald-400 break-all">
81
+ &quot;
82
+ {expanded ? (
83
+ <span
84
+ className="cursor-pointer prose prose-sm dark:prose-invert inline max-w-none [&_p]:inline [&_p]:my-0 [&_code]:text-emerald-300 [&_a]:text-emerald-300"
85
+ onClick={(e) => {
86
+ e.stopPropagation();
87
+ setExpanded(false);
88
+ }}
89
+ onKeyDown={(e) => {
90
+ if (e.key === "Enter" || e.key === " ") {
91
+ e.stopPropagation();
92
+ setExpanded(false);
93
+ }
94
+ }}
95
+ role="button"
96
+ tabIndex={0}
97
+ >
98
+ <ReactMarkdown>{text}</ReactMarkdown>
99
+ </span>
100
+ ) : (
101
+ <Tooltip delayDuration={300}>
102
+ <TooltipTrigger
103
+ onClick={(e) => {
104
+ e.stopPropagation();
105
+ setExpanded(true);
106
+ }}
107
+ className="text-left cursor-pointer"
108
+ >
109
+ <span>{text.slice(0, STRING_TRUNCATE_LIMIT)}</span>
110
+ <span className="text-emerald-400/50">…</span>
111
+ </TooltipTrigger>
112
+ <TooltipContent
113
+ side="bottom"
114
+ className="max-w-md text-xs p-2 break-words whitespace-pre-wrap"
115
+ >
116
+ {text.slice(0, 500)}
117
+ {text.length > 500 ? "…" : ""}
118
+ </TooltipContent>
119
+ </Tooltip>
120
+ )}
121
+ &quot;
122
+ </span>
123
+ );
124
+ }
125
+
126
+ function PrimitiveValue({ value }: { value: JsonValue }): JSX.Element {
127
+ if (value === null) {
128
+ return <span className="text-rose-400 italic">null</span>;
129
+ }
130
+
131
+ switch (typeof value) {
132
+ case "string":
133
+ return <StringValue text={value} />;
134
+ case "number":
135
+ return <span className="text-amber-400">{value}</span>;
136
+ case "boolean":
137
+ return <span className="text-blue-400">{value ? "true" : "false"}</span>;
138
+ case "object":
139
+ case "bigint":
140
+ case "symbol":
141
+ case "undefined":
142
+ case "function":
143
+ return <span className="text-muted-foreground">{JSON.stringify(value)}</span>;
144
+ }
145
+ }
146
+
147
+ function CopyValueButton({ value }: { value: JsonValue }): JSX.Element {
148
+ const [copied, setCopied] = useState(false);
149
+
150
+ function handleCopy(e: React.MouseEvent): void {
151
+ e.stopPropagation();
152
+ void window.navigator.clipboard.writeText(JSON.stringify(value, null, 2)).then(() => {
153
+ setCopied(true);
154
+ setTimeout(() => {
155
+ setCopied(false);
156
+ }, 2000);
157
+ });
158
+ }
159
+
160
+ return (
161
+ <button
162
+ type="button"
163
+ onClick={handleCopy}
164
+ className="inline-flex items-center gap-1 text-[10px] text-muted-foreground hover:text-foreground transition-colors"
165
+ title="Copy JSON"
166
+ >
167
+ {copied ? <Check className="size-3 text-green-500" /> : <Copy className="size-3" />}
168
+ </button>
169
+ );
170
+ }
171
+
172
+ function CopyButton({ value }: { value: JsonValue }): JSX.Element {
173
+ const [copied, setCopied] = useState(false);
174
+
175
+ function handleCopy(e: React.MouseEvent): void {
176
+ e.stopPropagation();
177
+ void window.navigator.clipboard.writeText(JSON.stringify(value, null, 2)).then(() => {
178
+ setCopied(true);
179
+ setTimeout(() => {
180
+ setCopied(false);
181
+ }, 2000);
182
+ });
183
+ }
184
+
185
+ return (
186
+ <button
187
+ type="button"
188
+ onClick={handleCopy}
189
+ className="opacity-0 group-hover/row:opacity-100 hover:bg-muted p-0.5 rounded transition-opacity shrink-0"
190
+ title="Copy to clipboard"
191
+ >
192
+ {copied ? (
193
+ <Check className="size-3 text-green-500" />
194
+ ) : (
195
+ <Copy className="size-3 text-muted-foreground" />
196
+ )}
197
+ </button>
198
+ );
199
+ }
200
+
201
+ type JsonNodeProps = {
202
+ name: string;
203
+ value: JsonValue;
204
+ level: number;
205
+ defaultExpandDepth: number;
206
+ isArrayItem: boolean;
207
+ };
208
+
209
+ function hasExpandableDescendant(value: JsonValue): boolean {
210
+ if (!isExpandable(value)) return false;
211
+ return getEntries(value).some(([, v]) => isExpandable(v));
212
+ }
213
+
214
+ function ExpandCollapseButton({
215
+ allExpanded,
216
+ onClick,
217
+ }: {
218
+ allExpanded: boolean;
219
+ onClick: (e: React.MouseEvent) => void;
220
+ }): JSX.Element {
221
+ return (
222
+ <button
223
+ type="button"
224
+ onClick={onClick}
225
+ className="opacity-0 group-hover/row:opacity-100 hover:bg-muted p-0.5 rounded transition-opacity shrink-0"
226
+ title={allExpanded ? "Collapse all" : "Expand all"}
227
+ >
228
+ {allExpanded ? (
229
+ <ChevronsUp className="size-3.5 text-muted-foreground" />
230
+ ) : (
231
+ <ChevronsDown className="size-3.5 text-muted-foreground" />
232
+ )}
233
+ </button>
234
+ );
235
+ }
236
+
237
+ function JsonNode({
238
+ name,
239
+ value,
240
+ level,
241
+ defaultExpandDepth,
242
+ isArrayItem,
243
+ }: JsonNodeProps): JSX.Element {
244
+ const [expanded, setExpanded] = useState(level < defaultExpandDepth);
245
+ const [childResetKey, setChildResetKey] = useState(0);
246
+ const [childDepthOverride, setChildDepthOverride] = useState<number | null>(null);
247
+ const [allExpanded, setAllExpanded] = useState(false);
248
+ const expandable = isExpandable(value);
249
+
250
+ const dataType = classifyValue(value);
251
+ const openBracket = dataType === "array" ? "[" : "{";
252
+ const closeBracket = dataType === "array" ? "]" : "}";
253
+
254
+ function handleExpandAll(e: React.MouseEvent): void {
255
+ e.stopPropagation();
256
+ if (allExpanded) {
257
+ setExpanded(false);
258
+ setChildDepthOverride(0);
259
+ setChildResetKey((k) => k + 1);
260
+ setAllExpanded(false);
261
+ } else {
262
+ setExpanded(true);
263
+ setChildDepthOverride(Number.POSITIVE_INFINITY);
264
+ setChildResetKey((k) => k + 1);
265
+ setAllExpanded(true);
266
+ }
267
+ }
268
+
269
+ const effectiveChildDepth = childDepthOverride ?? defaultExpandDepth;
270
+
271
+ return (
272
+ <div className={cn(level > 0 && "border-l border-border/50 ml-2")}>
273
+ <div
274
+ className={cn(
275
+ "flex items-start gap-1 py-0.5 px-1 -ml-1 rounded-sm group/row",
276
+ expandable && "cursor-pointer hover:bg-muted/50",
277
+ )}
278
+ onClick={
279
+ expandable
280
+ ? () => {
281
+ if (expanded) {
282
+ setAllExpanded(false);
283
+ }
284
+ setExpanded(!expanded);
285
+ }
286
+ : undefined
287
+ }
288
+ onKeyDown={
289
+ expandable
290
+ ? (e) => {
291
+ if (e.key === "Enter" || e.key === " ") {
292
+ e.preventDefault();
293
+ setExpanded(!expanded);
294
+ }
295
+ }
296
+ : undefined
297
+ }
298
+ role={expandable ? "button" : undefined}
299
+ tabIndex={expandable ? 0 : undefined}
300
+ >
301
+ {expandable ? (
302
+ <span className="w-4 h-5 flex items-center justify-center shrink-0">
303
+ {expanded ? (
304
+ <ChevronDown className="size-3 text-muted-foreground" />
305
+ ) : (
306
+ <ChevronRight className="size-3 text-muted-foreground" />
307
+ )}
308
+ </span>
309
+ ) : (
310
+ <span className="w-4 shrink-0" />
311
+ )}
312
+
313
+ <span className={cn("shrink-0", isArrayItem ? "text-muted-foreground" : "text-cyan-400")}>
314
+ {isArrayItem ? name : `"${name}"`}
315
+ </span>
316
+
317
+ <span className="text-muted-foreground shrink-0">{expandable ? "" : ":"}</span>
318
+
319
+ {expandable ? (
320
+ <span className="text-muted-foreground">
321
+ {openBracket}
322
+ <span className="text-muted-foreground/60 text-xs">
323
+ {" "}
324
+ {getItemCount(value)} {getItemCount(value) === 1 ? "item" : "items"} {closeBracket}
325
+ </span>
326
+ </span>
327
+ ) : (
328
+ <span className="min-w-0">
329
+ <PrimitiveValue value={value} />
330
+ </span>
331
+ )}
332
+
333
+ {expandable && hasExpandableDescendant(value) && (
334
+ <ExpandCollapseButton allExpanded={allExpanded} onClick={handleExpandAll} />
335
+ )}
336
+ <CopyButton value={value} />
337
+ </div>
338
+
339
+ {expandable && expanded && (
340
+ <div className="pl-4" key={childResetKey}>
341
+ {getEntries(value).map(([key, childValue]) => (
342
+ <JsonNode
343
+ key={key}
344
+ name={key}
345
+ value={childValue}
346
+ level={level + 1}
347
+ defaultExpandDepth={effectiveChildDepth}
348
+ isArrayItem={dataType === "array"}
349
+ />
350
+ ))}
351
+ <div className="text-muted-foreground py-0.5 px-1">{closeBracket}</div>
352
+ </div>
353
+ )}
354
+ </div>
355
+ );
356
+ }
357
+
358
+ export type JsonViewerProps = {
359
+ data: JsonValue;
360
+ defaultExpandDepth?: number;
361
+ className?: string;
362
+ showCopy?: boolean;
363
+ };
364
+
365
+ export function JsonViewer({
366
+ data,
367
+ defaultExpandDepth = 2,
368
+ className,
369
+ showCopy = false,
370
+ }: JsonViewerProps): JSX.Element {
371
+ const expandable = isExpandable(data);
372
+
373
+ if (!expandable) {
374
+ return (
375
+ <TooltipProvider>
376
+ <div className={cn("font-mono text-xs leading-relaxed", className)}>
377
+ <div className="flex items-center gap-1">
378
+ <PrimitiveValue value={data} />
379
+ {showCopy && <CopyValueButton value={data} />}
380
+ </div>
381
+ </div>
382
+ </TooltipProvider>
383
+ );
384
+ }
385
+
386
+ const dataType = classifyValue(data);
387
+ const isArray = dataType === "array";
388
+
389
+ return (
390
+ <TooltipProvider>
391
+ <div className={cn("font-mono text-xs leading-relaxed", className)}>
392
+ {showCopy && <CopyValueButton value={data} />}
393
+ {getEntries(data).map(([key, childValue]) => (
394
+ <JsonNode
395
+ key={key}
396
+ name={key}
397
+ value={childValue}
398
+ level={0}
399
+ defaultExpandDepth={defaultExpandDepth}
400
+ isArrayItem={isArray}
401
+ />
402
+ ))}
403
+ </div>
404
+ </TooltipProvider>
405
+ );
406
+ }
407
+
408
+ export type JsonViewerFromStringProps = {
409
+ text: string;
410
+ defaultExpandDepth?: number;
411
+ className?: string;
412
+ };
413
+
414
+ export function JsonViewerFromString({
415
+ text,
416
+ defaultExpandDepth = 2,
417
+ className,
418
+ }: JsonViewerFromStringProps): JSX.Element {
419
+ try {
420
+ const parsed: unknown = JSON.parse(text);
421
+ return (
422
+ <JsonViewer
423
+ data={safeJsonValue(parsed)}
424
+ defaultExpandDepth={defaultExpandDepth}
425
+ className={className}
426
+ />
427
+ );
428
+ } catch {
429
+ return (
430
+ <pre className={cn("font-mono text-xs whitespace-pre-wrap break-words", className)}>
431
+ {text}
432
+ </pre>
433
+ );
434
+ }
435
+ }
436
+
437
+ export function safeJsonValue(value: unknown): JsonValue {
438
+ if (value === null || value === undefined) return null;
439
+ switch (typeof value) {
440
+ case "string":
441
+ return value;
442
+ case "number":
443
+ return value;
444
+ case "boolean":
445
+ return value;
446
+ case "object": {
447
+ if (Array.isArray(value)) {
448
+ return value.map((item: unknown) => safeJsonValue(item));
449
+ }
450
+ const result: Record<string, JsonValue> = {};
451
+ for (const key of Object.keys(value)) {
452
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
453
+ result[key] = safeJsonValue(descriptor?.value);
454
+ }
455
+ return result;
456
+ }
457
+ case "bigint":
458
+ case "symbol":
459
+ case "function":
460
+ case "undefined":
461
+ return String(value);
462
+ }
463
+ return null;
464
+ }