langfuse

Форк
0
131 строка · 3.7 Кб
1
import { useState } from "react";
2
import { Button } from "@/src/components/ui/button";
3
import { Check, ChevronsDownUp, ChevronsUpDown, Copy } from "lucide-react";
4
import { cn } from "@/src/utils/tailwind";
5
import { default as React18JsonView } from "react18-json-view";
6
import { deepParseJson } from "@/src/utils/json";
7
import { Skeleton } from "@/src/components/ui/skeleton";
8

9
export function JSONView(props: {
10
  json?: unknown;
11
  title?: string;
12
  className?: string;
13
  isLoading?: boolean;
14
}) {
15
  // some users ingest stringified json nested in json, parse it
16
  const parsedJson = deepParseJson(props.json);
17

18
  return (
19
    <div className={cn("rounded-md border", props.className)}>
20
      {props.title ? (
21
        <div className="border-b px-3 py-1 text-xs font-medium">
22
          {props.title}
23
        </div>
24
      ) : undefined}
25
      <div className="flex gap-2 whitespace-pre-wrap break-words p-3 text-xs">
26
        {props.isLoading ? (
27
          <Skeleton className="h-3 w-3/4" />
28
        ) : (
29
          <React18JsonView
30
            src={parsedJson}
31
            theme="github"
32
            collapseObjectsAfterLength={20}
33
            collapseStringsAfterLength={500}
34
            displaySize={"collapsed"}
35
            matchesURL={true}
36
            customizeCopy={(node) => stringifyJsonNode(node)}
37
            className="w-full"
38
          />
39
        )}
40
      </div>
41
    </div>
42
  );
43
}
44

45
export function CodeView(props: {
46
  content: string | undefined | null;
47
  className?: string;
48
  defaultCollapsed?: boolean;
49
  scrollable?: boolean;
50
  title?: string;
51
}) {
52
  const [isCopied, setIsCopied] = useState(false);
53
  const [isCollapsed, setCollapsed] = useState(props.defaultCollapsed);
54

55
  const handleCopy = () => {
56
    setIsCopied(true);
57
    void navigator.clipboard.writeText(props.content ?? "");
58
    setTimeout(() => setIsCopied(false), 1000);
59
  };
60

61
  const handleShowAll = () => setCollapsed(!isCollapsed);
62

63
  return (
64
    <div className={cn("max-w-full rounded-md border ", props.className)}>
65
      {props.title ? (
66
        <div className="border-b px-3 py-1 text-xs font-medium">
67
          {props.title}
68
        </div>
69
      ) : undefined}
70
      <div className="flex gap-2">
71
        <code
72
          className={cn(
73
            "relative flex-1 whitespace-pre-wrap break-all px-4 py-3 font-mono text-xs",
74
            isCollapsed ? `line-clamp-6` : "block",
75
            props.scrollable ? "max-h-60 overflow-y-scroll" : undefined,
76
          )}
77
        >
78
          {props.content}
79
        </code>
80
        <div className="flex gap-2 py-2 pr-2">
81
          {props.defaultCollapsed ? (
82
            <Button variant="secondary" size="xs" onClick={handleShowAll}>
83
              {isCollapsed ? (
84
                <ChevronsUpDown className="h-3 w-3" />
85
              ) : (
86
                <ChevronsDownUp className="h-3 w-3" />
87
              )}
88
            </Button>
89
          ) : undefined}
90
          <Button variant="secondary" size="xs" onClick={handleCopy}>
91
            {isCopied ? (
92
              <Check className="h-3 w-3" />
93
            ) : (
94
              <Copy className="h-3 w-3" />
95
            )}
96
          </Button>
97
        </div>
98
      </div>
99
    </div>
100
  );
101
}
102

103
function stringifyJsonNode(node: unknown) {
104
  // return single string nodes without quotes
105
  if (typeof node === "string") {
106
    return node;
107
  }
108

109
  try {
110
    return JSON.stringify(
111
      node,
112
      (key, value) => {
113
        switch (typeof value) {
114
          case "bigint":
115
            return String(value) + "n";
116
          case "number":
117
          case "boolean":
118
          case "object":
119
          case "string":
120
            return value as string;
121
          default:
122
            return String(value);
123
        }
124
      },
125
      4,
126
    );
127
  } catch (error) {
128
    console.error("JSON stringify error", error);
129
    return "Error: JSON.stringify failed";
130
  }
131
}
132

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.