@dust-tt/sparkle 0.2.574 → 0.2.575
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/dist/cjs/index.js +1 -1
- package/dist/esm/components/markdown/PrettyJsonViewer.d.ts.map +1 -1
- package/dist/esm/components/markdown/PrettyJsonViewer.js +100 -22
- package/dist/esm/components/markdown/PrettyJsonViewer.js.map +1 -1
- package/package.json +1 -1
- package/src/components/markdown/PrettyJsonViewer.tsx +203 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PrettyJsonViewer.d.ts","sourceRoot":"","sources":["../../../../src/components/markdown/PrettyJsonViewer.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"PrettyJsonViewer.d.ts","sourceRoot":"","sources":["../../../../src/components/markdown/PrettyJsonViewer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAoBxC,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,SAAS,GACT,aAAa,EAAE,GACf;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAAA;CAAE,CAAC;AA4WrC,UAAU,eAAe;IACvB,IAAI,EAAE,aAAa,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AACD,wBAAgB,gBAAgB,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,eAAe,qBAqBpE"}
|
|
@@ -1,27 +1,69 @@
|
|
|
1
1
|
import { __read } from "tslib";
|
|
2
|
-
import React from "react";
|
|
2
|
+
import React, { useState } from "react";
|
|
3
3
|
import { Chip } from "../../components/Chip";
|
|
4
4
|
import { cn } from "../../lib/utils";
|
|
5
5
|
// Constants for consistent styling
|
|
6
6
|
var VALUE_CLASSES = "s-text-primary-700 dark:s-text-primary-700-night s-pt-1 s-text-sm";
|
|
7
7
|
var EMPTY_CLASSES = "s-text-primary-500 dark:s-text-primary-500-night s-pt-1 s-text-sm s-italic";
|
|
8
8
|
var INDENT_CLASSES = "s-border-structure-200 dark:s-border-structure-200-night s-max-w-full s-border-l s-pl-4 s-ml-4";
|
|
9
|
+
// Performance limits to prevent browser crashes.
|
|
10
|
+
// These limits are meant to be very conservative.
|
|
11
|
+
var MAX_OBJECT_DEPTH = 8;
|
|
12
|
+
var MAX_ARRAY_ITEMS = 128;
|
|
13
|
+
var MAX_OBJECT_KEYS = 64;
|
|
14
|
+
var MAX_STRING_LENGTH = 1024;
|
|
15
|
+
// Helper component for inline expand/collapse buttons with consistent styling.
|
|
16
|
+
function InlineExpandButton(_a) {
|
|
17
|
+
var label = _a.label, buttonText = _a.buttonText, onClick = _a.onClick, className = _a.className;
|
|
18
|
+
return (React.createElement("span", { className: cn(EMPTY_CLASSES, className) },
|
|
19
|
+
label,
|
|
20
|
+
" ",
|
|
21
|
+
React.createElement("button", { onClick: onClick, className: "s-cursor-pointer s-font-medium s-text-highlight hover:s-underline dark:s-text-highlight-night" }, buttonText)));
|
|
22
|
+
}
|
|
9
23
|
// Helper component for rendering key-value pairs with consistent styling.
|
|
10
24
|
function KeyValuePair(_a) {
|
|
11
|
-
var keyName = _a.keyName, value = _a.value, depth = _a.depth, chipColor = _a.chipColor, _b = _a.isRootLevel, isRootLevel = _b === void 0 ? false : _b;
|
|
25
|
+
var keyName = _a.keyName, value = _a.value, depth = _a.depth, chipColor = _a.chipColor, _b = _a.isRootLevel, isRootLevel = _b === void 0 ? false : _b, expandedPaths = _a.expandedPaths, setExpandedPaths = _a.setExpandedPaths, currentPath = _a.currentPath;
|
|
12
26
|
var isComplexValue = typeof value === "object" && value !== null;
|
|
13
27
|
if (isComplexValue) {
|
|
14
28
|
return (React.createElement(React.Fragment, null,
|
|
15
29
|
React.createElement(Chip, { size: "xs", color: chipColor, label: formatKey(keyName), className: "s-mb-2" }),
|
|
16
30
|
React.createElement("div", { className: cn("s-max-w-full", isRootLevel && "s-ml-4") },
|
|
17
|
-
React.createElement(JsonValue, { value: value, depth: depth + 1 }))));
|
|
31
|
+
React.createElement(JsonValue, { value: value, depth: depth + 1, expandedPaths: expandedPaths, setExpandedPaths: setExpandedPaths, currentPath: "".concat(currentPath, ".").concat(keyName) }))));
|
|
18
32
|
}
|
|
19
33
|
return (React.createElement("div", { className: cn("s-flex s-items-start", isRootLevel ? "s-gap-3" : "s-gap-2") },
|
|
20
34
|
React.createElement(Chip, { size: "xs", color: chipColor, label: formatKey(keyName) }),
|
|
21
|
-
React.createElement(JsonValue, { value: value, depth: depth + 1 })));
|
|
35
|
+
React.createElement(JsonValue, { value: value, depth: depth + 1, expandedPaths: expandedPaths, setExpandedPaths: setExpandedPaths, currentPath: "".concat(currentPath, ".").concat(keyName) })));
|
|
22
36
|
}
|
|
23
37
|
function JsonValue(_a) {
|
|
24
|
-
var
|
|
38
|
+
var _b, _c, _d, _e;
|
|
39
|
+
var value = _a.value, _f = _a.depth, depth = _f === void 0 ? 0 : _f, _g = _a.expandedPaths, expandedPaths = _g === void 0 ? new Set() : _g, setExpandedPaths = _a.setExpandedPaths, _h = _a.currentPath, currentPath = _h === void 0 ? "" : _h;
|
|
40
|
+
var handleToggleExpanded = function (path) {
|
|
41
|
+
if (!setExpandedPaths) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
setExpandedPaths(function (prev) {
|
|
45
|
+
var newSet = new Set(prev);
|
|
46
|
+
if (newSet.has(path)) {
|
|
47
|
+
newSet.delete(path);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
newSet.add(path);
|
|
51
|
+
}
|
|
52
|
+
return newSet;
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
if (depth >= MAX_OBJECT_DEPTH &&
|
|
56
|
+
typeof value === "object" &&
|
|
57
|
+
value !== null) {
|
|
58
|
+
var deepObjectPath_1 = "".concat(currentPath, ":deep");
|
|
59
|
+
var isExpanded = (_b = expandedPaths === null || expandedPaths === void 0 ? void 0 : expandedPaths.has(deepObjectPath_1)) !== null && _b !== void 0 ? _b : false;
|
|
60
|
+
if (isExpanded) {
|
|
61
|
+
// Render the full object/array when expanded, ignoring depth limit.
|
|
62
|
+
return (React.createElement(JsonValue, { value: value, depth: 0, expandedPaths: expandedPaths, setExpandedPaths: setExpandedPaths, currentPath: "".concat(currentPath, ":expanded") }));
|
|
63
|
+
}
|
|
64
|
+
return (React.createElement("div", { className: "s-flex s-items-center s-gap-1" },
|
|
65
|
+
React.createElement(InlineExpandButton, { label: "Maximum depth reached", buttonText: "expand", onClick: function () { return handleToggleExpanded(deepObjectPath_1); } })));
|
|
66
|
+
}
|
|
25
67
|
if (value === null || value === undefined) {
|
|
26
68
|
return React.createElement("span", { className: EMPTY_CLASSES }, "empty");
|
|
27
69
|
}
|
|
@@ -32,6 +74,17 @@ function JsonValue(_a) {
|
|
|
32
74
|
return React.createElement("span", { className: VALUE_CLASSES }, value);
|
|
33
75
|
}
|
|
34
76
|
if (typeof value === "string") {
|
|
77
|
+
if (value.length > MAX_STRING_LENGTH) {
|
|
78
|
+
var longStringPath_1 = "".concat(currentPath, ":longstring");
|
|
79
|
+
var isExpanded = (_c = expandedPaths === null || expandedPaths === void 0 ? void 0 : expandedPaths.has(longStringPath_1)) !== null && _c !== void 0 ? _c : false;
|
|
80
|
+
return (React.createElement("span", { className: "".concat(VALUE_CLASSES, " s-whitespace-pre-wrap s-break-normal") },
|
|
81
|
+
isExpanded ? value : value.substring(0, MAX_STRING_LENGTH),
|
|
82
|
+
!isExpanded && "…",
|
|
83
|
+
" ",
|
|
84
|
+
React.createElement("button", { onClick: function () { return handleToggleExpanded(longStringPath_1); }, className: "s-cursor-pointer s-font-medium s-text-highlight hover:s-underline dark:s-text-highlight-night" }, isExpanded
|
|
85
|
+
? "collapse"
|
|
86
|
+
: "expand (".concat((value.length - MAX_STRING_LENGTH).toLocaleString(), " more characters)"))));
|
|
87
|
+
}
|
|
35
88
|
return (React.createElement("span", { className: "".concat(VALUE_CLASSES, " s-whitespace-pre-wrap s-break-normal") }, value));
|
|
36
89
|
}
|
|
37
90
|
if (Array.isArray(value)) {
|
|
@@ -42,34 +95,58 @@ function JsonValue(_a) {
|
|
|
42
95
|
var isSimpleArray = value.every(function (item) { return typeof item !== "object" || item === null; });
|
|
43
96
|
if (isSimpleArray && value.length <= 5) {
|
|
44
97
|
return (React.createElement("span", { className: VALUE_CLASSES }, value.map(function (item, index) { return (React.createElement("span", { key: index },
|
|
45
|
-
React.createElement(JsonValue, { value: item, depth: depth + 1 }),
|
|
98
|
+
React.createElement(JsonValue, { value: item, depth: depth + 1, expandedPaths: expandedPaths, setExpandedPaths: setExpandedPaths, currentPath: "".concat(currentPath, "[").concat(index, "]") }),
|
|
46
99
|
index < value.length - 1 && ", ")); })));
|
|
47
100
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
101
|
+
// Truncate arrays that have too many items.
|
|
102
|
+
var arrayPath_1 = "".concat(currentPath, "[]");
|
|
103
|
+
var isExpanded = (_d = expandedPaths === null || expandedPaths === void 0 ? void 0 : expandedPaths.has(arrayPath_1)) !== null && _d !== void 0 ? _d : false;
|
|
104
|
+
var itemsToShow = isExpanded
|
|
105
|
+
? value.length
|
|
106
|
+
: Math.min(value.length, MAX_ARRAY_ITEMS);
|
|
107
|
+
var hasMore = value.length > MAX_ARRAY_ITEMS && !isExpanded;
|
|
108
|
+
return (React.createElement("div", { className: "s-mt-2" },
|
|
109
|
+
value.slice(0, itemsToShow).map(function (item, index) { return (React.createElement("div", { key: index, className: cn(INDENT_CLASSES) },
|
|
110
|
+
React.createElement("div", { className: "s-flex s-flex-col s-gap-2" },
|
|
111
|
+
React.createElement(Chip, { size: "xs", color: "primary", label: "Item ".concat(index + 1) }),
|
|
112
|
+
React.createElement("div", { className: "s-max-w-full" },
|
|
113
|
+
React.createElement(JsonValue, { value: item, depth: depth + 1, expandedPaths: expandedPaths, setExpandedPaths: setExpandedPaths, currentPath: "".concat(currentPath, "[").concat(index, "]") }))))); }),
|
|
114
|
+
hasMore && (React.createElement("div", { className: cn(INDENT_CLASSES) },
|
|
115
|
+
React.createElement(InlineExpandButton, { label: "".concat(value.length - itemsToShow, " more items"), buttonText: "expand", onClick: function () { return handleToggleExpanded(arrayPath_1); } })))));
|
|
53
116
|
}
|
|
54
117
|
if (typeof value === "object") {
|
|
55
118
|
var entries = Object.entries(value);
|
|
56
119
|
if (entries.length === 0) {
|
|
57
120
|
return React.createElement("span", { className: EMPTY_CLASSES }, "empty");
|
|
58
121
|
}
|
|
122
|
+
// Truncate objects with too many properties.
|
|
123
|
+
var objectPath_1 = "".concat(currentPath, "{}");
|
|
124
|
+
var isExpanded = (_e = expandedPaths === null || expandedPaths === void 0 ? void 0 : expandedPaths.has(objectPath_1)) !== null && _e !== void 0 ? _e : false;
|
|
125
|
+
var keysToShow = isExpanded
|
|
126
|
+
? entries.length
|
|
127
|
+
: Math.min(entries.length, MAX_OBJECT_KEYS);
|
|
128
|
+
var hasMore = entries.length > MAX_OBJECT_KEYS && !isExpanded;
|
|
129
|
+
var visibleEntries = entries.slice(0, keysToShow);
|
|
59
130
|
// For nested objects, use a card-like layout with vertical bars.
|
|
60
131
|
if (depth > 0) {
|
|
61
|
-
return (React.createElement("div", { className: "s-space-y-2" },
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
React.createElement(
|
|
65
|
-
|
|
132
|
+
return (React.createElement("div", { className: "s-space-y-2" },
|
|
133
|
+
visibleEntries.map(function (_a) {
|
|
134
|
+
var _b = __read(_a, 2), key = _b[0], val = _b[1];
|
|
135
|
+
return (React.createElement("div", { key: key, className: cn(INDENT_CLASSES) },
|
|
136
|
+
React.createElement(KeyValuePair, { keyName: key, value: val, depth: depth, chipColor: "info", expandedPaths: expandedPaths, setExpandedPaths: setExpandedPaths, currentPath: currentPath })));
|
|
137
|
+
}),
|
|
138
|
+
hasMore && (React.createElement("div", { className: cn(INDENT_CLASSES) },
|
|
139
|
+
React.createElement(InlineExpandButton, { label: "".concat(entries.length - keysToShow, " more properties"), buttonText: "expand", onClick: function () { return handleToggleExpanded(objectPath_1); } })))));
|
|
66
140
|
}
|
|
67
141
|
// Root level objects use a table-like layout.
|
|
68
|
-
return (React.createElement("div", { className: "s-max-w-full s-space-y-3" },
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
React.createElement(
|
|
72
|
-
|
|
142
|
+
return (React.createElement("div", { className: "s-max-w-full s-space-y-3" },
|
|
143
|
+
visibleEntries.map(function (_a) {
|
|
144
|
+
var _b = __read(_a, 2), key = _b[0], val = _b[1];
|
|
145
|
+
return (React.createElement("div", { key: key, className: cn("s-max-w-full s-border-b s-pb-3 last:s-border-0 last:s-pb-0", "s-border-structure-200 dark:s-border-structure-200-night") },
|
|
146
|
+
React.createElement(KeyValuePair, { keyName: key, value: val, depth: depth, chipColor: "highlight", isRootLevel: true, expandedPaths: expandedPaths, setExpandedPaths: setExpandedPaths, currentPath: currentPath })));
|
|
147
|
+
}),
|
|
148
|
+
hasMore && (React.createElement("div", { className: "s-border-structure-200 dark:s-border-structure-200-night s-border-t s-pt-3" },
|
|
149
|
+
React.createElement(InlineExpandButton, { label: "".concat(entries.length - keysToShow, " more properties"), buttonText: "expand", onClick: function () { return handleToggleExpanded(objectPath_1); } })))));
|
|
73
150
|
}
|
|
74
151
|
return (React.createElement("span", { className: cn("s-text-sm", "s-text-element-700 dark:s-text-element-700-night") }, String(value)));
|
|
75
152
|
}
|
|
@@ -85,8 +162,9 @@ function formatKey(key) {
|
|
|
85
162
|
}
|
|
86
163
|
export function PrettyJsonViewer(_a) {
|
|
87
164
|
var data = _a.data, className = _a.className;
|
|
165
|
+
var _b = __read(useState(new Set()), 2), expandedPaths = _b[0], setExpandedPaths = _b[1];
|
|
88
166
|
return (React.createElement("div", { className: cn("s-bg-structure-50 dark:s-bg-structure-50-night", "s-overflow-hidden s-rounded-lg s-px-4 s-py-4 s-text-base", className) },
|
|
89
167
|
React.createElement("div", { className: "s-max-w-full s-overflow-x-auto" },
|
|
90
|
-
React.createElement(JsonValue, { value: data }))));
|
|
168
|
+
React.createElement(JsonValue, { value: data, expandedPaths: expandedPaths, setExpandedPaths: setExpandedPaths, currentPath: "root" }))));
|
|
91
169
|
}
|
|
92
170
|
//# sourceMappingURL=PrettyJsonViewer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PrettyJsonViewer.js","sourceRoot":"","sources":["../../../../src/components/markdown/PrettyJsonViewer.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"PrettyJsonViewer.js","sourceRoot":"","sources":["../../../../src/components/markdown/PrettyJsonViewer.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useState } from "react";
|
|
2
2
|
|
|
3
3
|
import { Chip } from "@sparkle/components/Chip";
|
|
4
4
|
import { cn } from "@sparkle/lib/utils";
|
|
@@ -11,6 +11,13 @@ const EMPTY_CLASSES =
|
|
|
11
11
|
const INDENT_CLASSES =
|
|
12
12
|
"s-border-structure-200 dark:s-border-structure-200-night s-max-w-full s-border-l s-pl-4 s-ml-4";
|
|
13
13
|
|
|
14
|
+
// Performance limits to prevent browser crashes.
|
|
15
|
+
// These limits are meant to be very conservative.
|
|
16
|
+
const MAX_OBJECT_DEPTH = 8;
|
|
17
|
+
const MAX_ARRAY_ITEMS = 128;
|
|
18
|
+
const MAX_OBJECT_KEYS = 64;
|
|
19
|
+
const MAX_STRING_LENGTH = 1024;
|
|
20
|
+
|
|
14
21
|
export type JsonValueType =
|
|
15
22
|
| string
|
|
16
23
|
| number
|
|
@@ -20,6 +27,31 @@ export type JsonValueType =
|
|
|
20
27
|
| JsonValueType[]
|
|
21
28
|
| { [key: string]: JsonValueType };
|
|
22
29
|
|
|
30
|
+
// Helper component for inline expand/collapse buttons with consistent styling.
|
|
31
|
+
function InlineExpandButton({
|
|
32
|
+
label,
|
|
33
|
+
buttonText,
|
|
34
|
+
onClick,
|
|
35
|
+
className,
|
|
36
|
+
}: {
|
|
37
|
+
label: string;
|
|
38
|
+
buttonText: string;
|
|
39
|
+
onClick: () => void;
|
|
40
|
+
className?: string;
|
|
41
|
+
}) {
|
|
42
|
+
return (
|
|
43
|
+
<span className={cn(EMPTY_CLASSES, className)}>
|
|
44
|
+
{label}{" "}
|
|
45
|
+
<button
|
|
46
|
+
onClick={onClick}
|
|
47
|
+
className="s-cursor-pointer s-font-medium s-text-highlight hover:s-underline dark:s-text-highlight-night"
|
|
48
|
+
>
|
|
49
|
+
{buttonText}
|
|
50
|
+
</button>
|
|
51
|
+
</span>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
23
55
|
// Helper component for rendering key-value pairs with consistent styling.
|
|
24
56
|
function KeyValuePair({
|
|
25
57
|
keyName,
|
|
@@ -27,12 +59,18 @@ function KeyValuePair({
|
|
|
27
59
|
depth,
|
|
28
60
|
chipColor,
|
|
29
61
|
isRootLevel = false,
|
|
62
|
+
expandedPaths,
|
|
63
|
+
setExpandedPaths,
|
|
64
|
+
currentPath,
|
|
30
65
|
}: {
|
|
31
66
|
keyName: string;
|
|
32
67
|
value: JsonValueType;
|
|
33
68
|
depth: number;
|
|
34
69
|
chipColor: "info" | "highlight";
|
|
35
70
|
isRootLevel?: boolean;
|
|
71
|
+
expandedPaths?: Set<string>;
|
|
72
|
+
setExpandedPaths?: React.Dispatch<React.SetStateAction<Set<string>>>;
|
|
73
|
+
currentPath?: string;
|
|
36
74
|
}) {
|
|
37
75
|
const isComplexValue = typeof value === "object" && value !== null;
|
|
38
76
|
|
|
@@ -46,7 +84,13 @@ function KeyValuePair({
|
|
|
46
84
|
className="s-mb-2"
|
|
47
85
|
/>
|
|
48
86
|
<div className={cn("s-max-w-full", isRootLevel && "s-ml-4")}>
|
|
49
|
-
<JsonValue
|
|
87
|
+
<JsonValue
|
|
88
|
+
value={value}
|
|
89
|
+
depth={depth + 1}
|
|
90
|
+
expandedPaths={expandedPaths}
|
|
91
|
+
setExpandedPaths={setExpandedPaths}
|
|
92
|
+
currentPath={`${currentPath}.${keyName}`}
|
|
93
|
+
/>
|
|
50
94
|
</div>
|
|
51
95
|
</>
|
|
52
96
|
);
|
|
@@ -60,7 +104,13 @@ function KeyValuePair({
|
|
|
60
104
|
)}
|
|
61
105
|
>
|
|
62
106
|
<Chip size="xs" color={chipColor} label={formatKey(keyName)} />
|
|
63
|
-
<JsonValue
|
|
107
|
+
<JsonValue
|
|
108
|
+
value={value}
|
|
109
|
+
depth={depth + 1}
|
|
110
|
+
expandedPaths={expandedPaths}
|
|
111
|
+
setExpandedPaths={setExpandedPaths}
|
|
112
|
+
currentPath={`${currentPath}.${keyName}`}
|
|
113
|
+
/>
|
|
64
114
|
</div>
|
|
65
115
|
);
|
|
66
116
|
}
|
|
@@ -68,10 +118,63 @@ function KeyValuePair({
|
|
|
68
118
|
function JsonValue({
|
|
69
119
|
value,
|
|
70
120
|
depth = 0,
|
|
121
|
+
expandedPaths = new Set(),
|
|
122
|
+
setExpandedPaths,
|
|
123
|
+
currentPath = "",
|
|
71
124
|
}: {
|
|
72
125
|
value: JsonValueType;
|
|
73
126
|
depth?: number;
|
|
127
|
+
expandedPaths?: Set<string>;
|
|
128
|
+
setExpandedPaths?: React.Dispatch<React.SetStateAction<Set<string>>>;
|
|
129
|
+
currentPath?: string;
|
|
74
130
|
}) {
|
|
131
|
+
const handleToggleExpanded = (path: string) => {
|
|
132
|
+
if (!setExpandedPaths) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
setExpandedPaths((prev) => {
|
|
137
|
+
const newSet = new Set(prev);
|
|
138
|
+
if (newSet.has(path)) {
|
|
139
|
+
newSet.delete(path);
|
|
140
|
+
} else {
|
|
141
|
+
newSet.add(path);
|
|
142
|
+
}
|
|
143
|
+
return newSet;
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
if (
|
|
147
|
+
depth >= MAX_OBJECT_DEPTH &&
|
|
148
|
+
typeof value === "object" &&
|
|
149
|
+
value !== null
|
|
150
|
+
) {
|
|
151
|
+
const deepObjectPath = `${currentPath}:deep`;
|
|
152
|
+
const isExpanded = expandedPaths?.has(deepObjectPath) ?? false;
|
|
153
|
+
|
|
154
|
+
if (isExpanded) {
|
|
155
|
+
// Render the full object/array when expanded, ignoring depth limit.
|
|
156
|
+
return (
|
|
157
|
+
<JsonValue
|
|
158
|
+
value={value}
|
|
159
|
+
depth={0} // Reset depth to allow full rendering.
|
|
160
|
+
expandedPaths={expandedPaths}
|
|
161
|
+
setExpandedPaths={setExpandedPaths}
|
|
162
|
+
currentPath={`${currentPath}:expanded`}
|
|
163
|
+
/>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<div className="s-flex s-items-center s-gap-1">
|
|
169
|
+
<InlineExpandButton
|
|
170
|
+
label="Maximum depth reached"
|
|
171
|
+
buttonText="expand"
|
|
172
|
+
onClick={() => handleToggleExpanded(deepObjectPath)}
|
|
173
|
+
/>
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
75
178
|
if (value === null || value === undefined) {
|
|
76
179
|
return <span className={EMPTY_CLASSES}>empty</span>;
|
|
77
180
|
}
|
|
@@ -85,6 +188,28 @@ function JsonValue({
|
|
|
85
188
|
}
|
|
86
189
|
|
|
87
190
|
if (typeof value === "string") {
|
|
191
|
+
if (value.length > MAX_STRING_LENGTH) {
|
|
192
|
+
const longStringPath = `${currentPath}:longstring`;
|
|
193
|
+
const isExpanded = expandedPaths?.has(longStringPath) ?? false;
|
|
194
|
+
|
|
195
|
+
return (
|
|
196
|
+
<span
|
|
197
|
+
className={`${VALUE_CLASSES} s-whitespace-pre-wrap s-break-normal`}
|
|
198
|
+
>
|
|
199
|
+
{isExpanded ? value : value.substring(0, MAX_STRING_LENGTH)}
|
|
200
|
+
{!isExpanded && "…"}{" "}
|
|
201
|
+
<button
|
|
202
|
+
onClick={() => handleToggleExpanded(longStringPath)}
|
|
203
|
+
className="s-cursor-pointer s-font-medium s-text-highlight hover:s-underline dark:s-text-highlight-night"
|
|
204
|
+
>
|
|
205
|
+
{isExpanded
|
|
206
|
+
? "collapse"
|
|
207
|
+
: `expand (${(value.length - MAX_STRING_LENGTH).toLocaleString()} more characters)`}
|
|
208
|
+
</button>
|
|
209
|
+
</span>
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
88
213
|
return (
|
|
89
214
|
<span className={`${VALUE_CLASSES} s-whitespace-pre-wrap s-break-normal`}>
|
|
90
215
|
{value}
|
|
@@ -107,7 +232,13 @@ function JsonValue({
|
|
|
107
232
|
<span className={VALUE_CLASSES}>
|
|
108
233
|
{value.map((item, index) => (
|
|
109
234
|
<span key={index}>
|
|
110
|
-
<JsonValue
|
|
235
|
+
<JsonValue
|
|
236
|
+
value={item}
|
|
237
|
+
depth={depth + 1}
|
|
238
|
+
expandedPaths={expandedPaths}
|
|
239
|
+
setExpandedPaths={setExpandedPaths}
|
|
240
|
+
currentPath={`${currentPath}[${index}]`}
|
|
241
|
+
/>
|
|
111
242
|
{index < value.length - 1 && ", "}
|
|
112
243
|
</span>
|
|
113
244
|
))}
|
|
@@ -115,18 +246,41 @@ function JsonValue({
|
|
|
115
246
|
);
|
|
116
247
|
}
|
|
117
248
|
|
|
249
|
+
// Truncate arrays that have too many items.
|
|
250
|
+
const arrayPath = `${currentPath}[]`;
|
|
251
|
+
const isExpanded = expandedPaths?.has(arrayPath) ?? false;
|
|
252
|
+
const itemsToShow = isExpanded
|
|
253
|
+
? value.length
|
|
254
|
+
: Math.min(value.length, MAX_ARRAY_ITEMS);
|
|
255
|
+
const hasMore = value.length > MAX_ARRAY_ITEMS && !isExpanded;
|
|
256
|
+
|
|
118
257
|
return (
|
|
119
258
|
<div className="s-mt-2">
|
|
120
|
-
{value.map((item, index) => (
|
|
259
|
+
{value.slice(0, itemsToShow).map((item, index) => (
|
|
121
260
|
<div key={index} className={cn(INDENT_CLASSES)}>
|
|
122
261
|
<div className="s-flex s-flex-col s-gap-2">
|
|
123
262
|
<Chip size="xs" color="primary" label={`Item ${index + 1}`} />
|
|
124
263
|
<div className="s-max-w-full">
|
|
125
|
-
<JsonValue
|
|
264
|
+
<JsonValue
|
|
265
|
+
value={item}
|
|
266
|
+
depth={depth + 1}
|
|
267
|
+
expandedPaths={expandedPaths}
|
|
268
|
+
setExpandedPaths={setExpandedPaths}
|
|
269
|
+
currentPath={`${currentPath}[${index}]`}
|
|
270
|
+
/>
|
|
126
271
|
</div>
|
|
127
272
|
</div>
|
|
128
273
|
</div>
|
|
129
274
|
))}
|
|
275
|
+
{hasMore && (
|
|
276
|
+
<div className={cn(INDENT_CLASSES)}>
|
|
277
|
+
<InlineExpandButton
|
|
278
|
+
label={`${value.length - itemsToShow} more items`}
|
|
279
|
+
buttonText="expand"
|
|
280
|
+
onClick={() => handleToggleExpanded(arrayPath)}
|
|
281
|
+
/>
|
|
282
|
+
</div>
|
|
283
|
+
)}
|
|
130
284
|
</div>
|
|
131
285
|
);
|
|
132
286
|
}
|
|
@@ -137,20 +291,41 @@ function JsonValue({
|
|
|
137
291
|
return <span className={EMPTY_CLASSES}>empty</span>;
|
|
138
292
|
}
|
|
139
293
|
|
|
294
|
+
// Truncate objects with too many properties.
|
|
295
|
+
const objectPath = `${currentPath}{}`;
|
|
296
|
+
const isExpanded = expandedPaths?.has(objectPath) ?? false;
|
|
297
|
+
const keysToShow = isExpanded
|
|
298
|
+
? entries.length
|
|
299
|
+
: Math.min(entries.length, MAX_OBJECT_KEYS);
|
|
300
|
+
const hasMore = entries.length > MAX_OBJECT_KEYS && !isExpanded;
|
|
301
|
+
const visibleEntries = entries.slice(0, keysToShow);
|
|
302
|
+
|
|
140
303
|
// For nested objects, use a card-like layout with vertical bars.
|
|
141
304
|
if (depth > 0) {
|
|
142
305
|
return (
|
|
143
306
|
<div className="s-space-y-2">
|
|
144
|
-
{
|
|
307
|
+
{visibleEntries.map(([key, val]) => (
|
|
145
308
|
<div key={key} className={cn(INDENT_CLASSES)}>
|
|
146
309
|
<KeyValuePair
|
|
147
310
|
keyName={key}
|
|
148
311
|
value={val}
|
|
149
312
|
depth={depth}
|
|
150
313
|
chipColor="info"
|
|
314
|
+
expandedPaths={expandedPaths}
|
|
315
|
+
setExpandedPaths={setExpandedPaths}
|
|
316
|
+
currentPath={currentPath}
|
|
151
317
|
/>
|
|
152
318
|
</div>
|
|
153
319
|
))}
|
|
320
|
+
{hasMore && (
|
|
321
|
+
<div className={cn(INDENT_CLASSES)}>
|
|
322
|
+
<InlineExpandButton
|
|
323
|
+
label={`${entries.length - keysToShow} more properties`}
|
|
324
|
+
buttonText="expand"
|
|
325
|
+
onClick={() => handleToggleExpanded(objectPath)}
|
|
326
|
+
/>
|
|
327
|
+
</div>
|
|
328
|
+
)}
|
|
154
329
|
</div>
|
|
155
330
|
);
|
|
156
331
|
}
|
|
@@ -158,7 +333,7 @@ function JsonValue({
|
|
|
158
333
|
// Root level objects use a table-like layout.
|
|
159
334
|
return (
|
|
160
335
|
<div className="s-max-w-full s-space-y-3">
|
|
161
|
-
{
|
|
336
|
+
{visibleEntries.map(([key, val]) => (
|
|
162
337
|
<div
|
|
163
338
|
key={key}
|
|
164
339
|
className={cn(
|
|
@@ -172,9 +347,21 @@ function JsonValue({
|
|
|
172
347
|
depth={depth}
|
|
173
348
|
chipColor="highlight"
|
|
174
349
|
isRootLevel
|
|
350
|
+
expandedPaths={expandedPaths}
|
|
351
|
+
setExpandedPaths={setExpandedPaths}
|
|
352
|
+
currentPath={currentPath}
|
|
175
353
|
/>
|
|
176
354
|
</div>
|
|
177
355
|
))}
|
|
356
|
+
{hasMore && (
|
|
357
|
+
<div className="s-border-structure-200 dark:s-border-structure-200-night s-border-t s-pt-3">
|
|
358
|
+
<InlineExpandButton
|
|
359
|
+
label={`${entries.length - keysToShow} more properties`}
|
|
360
|
+
buttonText="expand"
|
|
361
|
+
onClick={() => handleToggleExpanded(objectPath)}
|
|
362
|
+
/>
|
|
363
|
+
</div>
|
|
364
|
+
)}
|
|
178
365
|
</div>
|
|
179
366
|
);
|
|
180
367
|
}
|
|
@@ -207,6 +394,8 @@ interface JsonViewerProps {
|
|
|
207
394
|
className?: string;
|
|
208
395
|
}
|
|
209
396
|
export function PrettyJsonViewer({ data, className }: JsonViewerProps) {
|
|
397
|
+
const [expandedPaths, setExpandedPaths] = useState<Set<string>>(new Set());
|
|
398
|
+
|
|
210
399
|
return (
|
|
211
400
|
<div
|
|
212
401
|
className={cn(
|
|
@@ -216,7 +405,12 @@ export function PrettyJsonViewer({ data, className }: JsonViewerProps) {
|
|
|
216
405
|
)}
|
|
217
406
|
>
|
|
218
407
|
<div className="s-max-w-full s-overflow-x-auto">
|
|
219
|
-
<JsonValue
|
|
408
|
+
<JsonValue
|
|
409
|
+
value={data}
|
|
410
|
+
expandedPaths={expandedPaths}
|
|
411
|
+
setExpandedPaths={setExpandedPaths}
|
|
412
|
+
currentPath="root"
|
|
413
|
+
/>
|
|
220
414
|
</div>
|
|
221
415
|
</div>
|
|
222
416
|
);
|