@dbx-tools/ui 0.9.112 → 0.9.117

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/index.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  // Hand edits are overwritten on the next watch; this file is read-only.
4
4
 
5
5
  export const PACKAGE_IDENTIFIER = "@dbx-tools/ui";
6
- export const PACKAGE_VERSION = "0.9.112";
6
+ export const PACKAGE_VERSION = "0.9.117";
7
7
  export * as authReactAuthClient from "./src/auth/react/auth-client.ts";
8
8
  export * as authReactAuthGate from "./src/auth/react/auth-gate.tsx";
9
9
  export * as authReactPasskeyManager from "./src/auth/react/passkey-manager.tsx";
@@ -38,7 +38,8 @@ export type { EmailComposeProps } from "./src/email/react/email-compose.tsx";
38
38
  export { joinAddresses, parseAddresses, attachmentNames } from "./src/email/react/fields.ts";
39
39
  export type { EmailDraft } from "./src/email/react/fields.ts";
40
40
  export type { BrandPreset, BrandPickerProps } from "./src/react/brand-picker.tsx";
41
- export { JsonBlock } from "./src/react/highlighted-code.tsx";
41
+ export { HighlightedCodeBlock, JsonBlock } from "./src/react/highlighted-code.tsx";
42
+ export type { DetectedCodeLanguage } from "./src/react/highlighted-code.tsx";
42
43
  export type { RecordPreviewProps } from "./src/react/record-preview.tsx";
43
44
  export type { RecordPreviewRow } from "./src/react/record-preview-data.ts";
44
45
  export type { SearchBoxProps } from "./src/search/react/search-box.tsx";
package/package.json CHANGED
@@ -30,11 +30,11 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@databricks/appkit-ui": "0.81.0",
33
- "@dbx-tools/shared-auth": "^0.9.112",
34
- "@dbx-tools/shared-core": "^0.9.112",
35
- "@dbx-tools/shared-email": "^0.9.112",
36
- "@dbx-tools/shared-email-template": "^0.9.112",
37
- "@dbx-tools/shared-search": "^0.9.112",
33
+ "@dbx-tools/shared-auth": "^0.9.117",
34
+ "@dbx-tools/shared-core": "^0.9.117",
35
+ "@dbx-tools/shared-email": "^0.9.117",
36
+ "@dbx-tools/shared-email-template": "^0.9.117",
37
+ "@dbx-tools/shared-search": "^0.9.117",
38
38
  "lucide-react": "^0.554.0",
39
39
  "shiki": "^3.0.0",
40
40
  "streamdown": "^2.5.0",
@@ -44,7 +44,7 @@
44
44
  "publishConfig": {
45
45
  "access": "public"
46
46
  },
47
- "version": "0.9.112",
47
+ "version": "0.9.117",
48
48
  "dbxToolsConfig": {
49
49
  "tags": [
50
50
  "ui"
@@ -54,6 +54,7 @@
54
54
  "exports": {
55
55
  "./react": "./src/react/index.ts",
56
56
  "./styles.css": "./src/styles.css",
57
+ "./*": "./src/*/index.ts",
57
58
  "./branding/react": "./src/branding/react/index.ts",
58
59
  "./branding/browser": "./src/branding/browser.ts",
59
60
  "./branding/assets": "./src/branding/generated/assets.ts",
@@ -0,0 +1,6 @@
1
+ // GENERATED by projen watch - DO NOT EDIT.
2
+ // Regenerated from the exporting modules in ./src.
3
+ // Hand edits are overwritten on the next watch; this file is read-only.
4
+
5
+ export * as assets from "./assets.ts";
6
+ export { dbxToolsAssetSvg, dbxToolsAssetDataUrls } from "./assets.ts";
@@ -0,0 +1,8 @@
1
+ // GENERATED by projen watch - DO NOT EDIT.
2
+ // Regenerated from the exporting modules in ./src.
3
+ // Hand edits are overwritten on the next watch; this file is read-only.
4
+
5
+ export * as browser from "./browser.ts";
6
+ export * as generated from "./generated/index.ts";
7
+ export { resolveBrandAsset } from "./browser.ts";
8
+ export type { BrandAssetResolver, ApplyBrandContextOptions } from "./browser.ts";
@@ -4,6 +4,7 @@
4
4
  * @module
5
5
  */
6
6
 
7
+ import { json } from "@dbx-tools/shared-core";
7
8
  import { useEffect, useState } from "react";
8
9
 
9
10
  import { cn } from "./appkit-ui.ts";
@@ -11,6 +12,30 @@ import { highlightToHtml } from "./shiki-plugin.ts";
11
12
 
12
13
  export { createShikiPlugin, highlightToHtml } from "./shiki-plugin.ts";
13
14
 
15
+ /** Code languages detected automatically in free-form text fields. */
16
+ export type DetectedCodeLanguage = "sql" | "python" | "typescript" | "bash" | "json";
17
+
18
+ /** Detect a supported code language from a complete free-form text value. */
19
+ export function detectCodeLanguage(source: string): DetectedCodeLanguage | undefined {
20
+ const trimmed = source.trim();
21
+ if (/^(?:select|with|insert|update|delete|merge|create|alter)\b/i.test(trimmed)) return "sql";
22
+ if (/^(?:from\s+\S+\s+import|import\s+\S+|def\s+\w+|class\s+\w+):?/m.test(trimmed)) {
23
+ return "python";
24
+ }
25
+ if (/^(?:const|let|var|interface|type|export|import)\b/m.test(trimmed)) return "typescript";
26
+ if (/^(?:#!.*\b(?:ba|z|k)?sh\b|(?:cd|ls|cat|grep|find|bun|npm|pnpm|yarn)\s)/m.test(trimmed)) {
27
+ return "bash";
28
+ }
29
+ if (
30
+ (trimmed.startsWith("{") || trimmed.startsWith("[")) &&
31
+ trimmed.length > 1 &&
32
+ json.parse(trimmed) !== undefined
33
+ ) {
34
+ return "json";
35
+ }
36
+ return undefined;
37
+ }
38
+
14
39
  /** Resolve syntax-highlighted HTML while preserving plaintext during lazy load. */
15
40
  export function useHighlightedHtml(source: string, language: string): string | null {
16
41
  const [highlighted, setHighlighted] = useState<{
@@ -32,21 +57,35 @@ export function useHighlightedHtml(source: string, language: string): string | n
32
57
  : null;
33
58
  }
34
59
 
35
- /** Render formatted JSON with the shared lazily loaded Shiki runtime. */
36
- export const JsonBlock = ({ json, className }: { json: string; className?: string }) => {
37
- const html = useHighlightedHtml(json, "json");
60
+ /** Render code with the shared lazily loaded Shiki runtime. */
61
+ export const HighlightedCodeBlock = ({
62
+ source,
63
+ language,
64
+ className,
65
+ }: {
66
+ source: string;
67
+ language: string;
68
+ className?: string;
69
+ }) => {
70
+ const html = useHighlightedHtml(source, language);
38
71
  return (
39
72
  <pre
73
+ data-language={language}
40
74
  className={cn(
41
75
  "max-w-full overflow-auto whitespace-pre-wrap break-words font-mono text-xs leading-relaxed",
42
76
  className,
43
77
  )}
44
78
  >
45
79
  {html === null ? (
46
- <code>{json}</code>
80
+ <code>{source}</code>
47
81
  ) : (
48
82
  <code data-dbx-highlighted-code dangerouslySetInnerHTML={{ __html: html }} />
49
83
  )}
50
84
  </pre>
51
85
  );
52
86
  };
87
+
88
+ /** Render formatted JSON with the shared lazily loaded Shiki runtime. */
89
+ export const JsonBlock = ({ json, className }: { json: string; className?: string }) => (
90
+ <HighlightedCodeBlock source={json} language="json" className={className} />
91
+ );
@@ -4,7 +4,7 @@
4
4
  * @module
5
5
  */
6
6
 
7
- import { stringUtils } from "@dbx-tools/shared-core";
7
+ import { json, object as objectUtils, stringUtils } from "@dbx-tools/shared-core";
8
8
 
9
9
  /** One field shown in the table view of {@link RecordPreview}. */
10
10
  export interface RecordPreviewRow {
@@ -39,6 +39,18 @@ export function formatRecordPreviewJson(value: unknown): string {
39
39
  }
40
40
  }
41
41
 
42
+ /** Parse a string whose complete value is a serialized JSON object or array. */
43
+ export function parseNestedJsonText(
44
+ value: unknown,
45
+ ): Record<string, unknown> | unknown[] | undefined {
46
+ if (typeof value !== "string") return undefined;
47
+ const trimmed = value.trim();
48
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return undefined;
49
+ const parsed = json.parse(trimmed);
50
+ if (Array.isArray(parsed)) return parsed;
51
+ return objectUtils.isRecord(parsed) ? parsed : undefined;
52
+ }
53
+
42
54
  /** YAML document fence at the start of a markdown file (`---` ... `---`). */
43
55
  const FRONTMATTER = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/;
44
56
 
@@ -21,11 +21,12 @@ import {
21
21
  TooltipTrigger,
22
22
  cn,
23
23
  } from "./appkit-ui.ts";
24
- import { JsonBlock } from "./highlighted-code.tsx";
24
+ import { detectCodeLanguage, HighlightedCodeBlock, JsonBlock } from "./highlighted-code.tsx";
25
25
  import {
26
26
  formatRecordPreviewJson,
27
27
  looksLikeMarkdown,
28
28
  markdownForPreview,
29
+ parseNestedJsonText,
29
30
  recordPreviewRows,
30
31
  type RecordPreviewRow,
31
32
  } from "./record-preview-data.ts";
@@ -35,6 +36,7 @@ export {
35
36
  formatRecordPreviewJson,
36
37
  looksLikeMarkdown,
37
38
  markdownForPreview,
39
+ parseNestedJsonText,
38
40
  recordPreviewRows,
39
41
  } from "./record-preview-data.ts";
40
42
 
@@ -42,7 +44,8 @@ export {
42
44
  const MARKDOWN_CELL_CLASSES =
43
45
  "min-w-0 max-w-full text-[11px] leading-snug [&_p]:my-0.5 [&_p]:leading-snug [&_ul]:my-0.5 [&_ol]:my-0.5 [&_li]:my-0 [&_h1]:my-1 [&_h1]:text-[11px] [&_h1]:font-semibold [&_h2]:my-1 [&_h2]:text-[11px] [&_h2]:font-semibold [&_h3]:my-1 [&_h3]:text-[11px] [&_h3]:font-semibold [&_pre]:my-1 [&_pre]:overflow-x-auto [&_pre]:rounded [&_pre]:bg-background/60 [&_pre]:p-1.5 [&_pre]:text-[10px] [&_code]:text-[10px]";
44
46
 
45
- const NESTED_DEPTH = 4;
47
+ const MAX_NESTED_DEPTH = 12;
48
+ const MAX_INDENT_DEPTH = 4;
46
49
 
47
50
  /** Props for {@link RecordPreview}. */
48
51
  export interface RecordPreviewProps {
@@ -60,87 +63,274 @@ export function RecordPreview({ value, className }: RecordPreviewProps) {
60
63
  return (
61
64
  <div
62
65
  className={cn(
63
- "relative min-w-0 overflow-hidden [&_[data-slot=table-container]]:overflow-x-hidden",
66
+ "relative min-w-0 [&_[data-slot=table-container]]:overflow-x-hidden",
64
67
  className,
65
68
  )}
66
69
  >
67
- <Tooltip>
68
- <TooltipTrigger asChild>
69
- <Button
70
- type="button"
71
- size="icon"
72
- variant="ghost"
73
- aria-pressed={raw}
74
- aria-label={raw ? "Show table" : "Show JSON"}
75
- className="absolute right-1 top-1 z-10 size-6"
76
- onClick={() => setRaw((current) => !current)}
77
- >
78
- {raw ? <Table2Icon className="size-3" /> : <BracesIcon className="size-3" />}
79
- </Button>
80
- </TooltipTrigger>
81
- <TooltipContent>{raw ? "Table" : "JSON"}</TooltipContent>
82
- </Tooltip>
83
- <div className="max-h-[inherit] min-w-0 overflow-auto">
84
- {raw ? (
85
- <JsonBlock json={rawJson} className="rounded bg-background/40 p-2 pr-8 text-[11px]" />
86
- ) : rows.length === 0 ? (
87
- <pre className="whitespace-pre-wrap break-words rounded bg-background/40 p-2 pr-8 font-mono text-[11px] leading-relaxed">
88
- {rawJson}
89
- </pre>
90
- ) : (
91
- <RecordTable rows={rows} depth={0} />
92
- )}
70
+ <div className="sticky top-1 z-10 float-right mr-1 h-0">
71
+ <Tooltip>
72
+ <TooltipTrigger asChild>
73
+ <Button
74
+ type="button"
75
+ size="icon"
76
+ variant="ghost"
77
+ aria-pressed={raw}
78
+ aria-label={raw ? "Show table" : "Show JSON"}
79
+ className="size-6 bg-background/85 backdrop-blur-sm"
80
+ onClick={() => setRaw((current) => !current)}
81
+ >
82
+ {raw ? <Table2Icon className="size-3" /> : <BracesIcon className="size-3" />}
83
+ </Button>
84
+ </TooltipTrigger>
85
+ <TooltipContent>{raw ? "Table" : "JSON"}</TooltipContent>
86
+ </Tooltip>
93
87
  </div>
88
+ {raw ? (
89
+ <JsonBlock json={rawJson} className="rounded bg-background/40 p-2 pr-8 text-[11px]" />
90
+ ) : rows.length === 0 ? (
91
+ <pre className="whitespace-pre-wrap break-words rounded bg-background/40 p-2 pr-8 font-mono text-[11px] leading-relaxed">
92
+ {rawJson}
93
+ </pre>
94
+ ) : (
95
+ <RecordTable rows={rows} />
96
+ )}
94
97
  </div>
95
98
  );
96
99
  }
97
100
 
98
- function RecordTable({ rows, depth }: { rows: RecordPreviewRow[]; depth: number }) {
101
+ function RecordTable({ rows }: { rows: RecordPreviewRow[] }) {
99
102
  if (rows.length === 0) return null;
100
103
  return (
101
- <Table className="table-fixed text-[11px]">
102
- <TableBody>
103
- {rows.map((row) => {
104
- const nested = isNestedValue(row.value) && depth < NESTED_DEPTH;
105
- return nested ? (
106
- <Fragment key={row.key}>
107
- <TableRow className="border-b-0 hover:bg-transparent">
108
- <TableHead
109
- colSpan={2}
110
- className="h-auto whitespace-normal px-2 pb-1 pt-2 font-medium text-muted-foreground"
111
- >
112
- {row.label}
113
- </TableHead>
114
- </TableRow>
115
- <TableRow className="hover:bg-transparent">
116
- <TableCell
117
- colSpan={2}
118
- className="min-w-0 whitespace-normal p-0 pb-1 pl-2 [overflow-wrap:anywhere]"
119
- >
120
- <RecordValue value={row.value} depth={depth} />
121
- </TableCell>
122
- </TableRow>
123
- </Fragment>
124
- ) : (
125
- <TableRow key={row.key} className="hover:bg-transparent">
126
- <TableHead className="h-auto w-28 align-top whitespace-normal px-2 py-1.5 font-medium text-muted-foreground sm:w-[8.5rem]">
127
- {row.label}
128
- </TableHead>
129
- <TableCell className="min-w-0 whitespace-normal px-2 py-1.5 pr-8 align-top [overflow-wrap:anywhere]">
130
- <RecordValue value={row.value} depth={depth} />
131
- </TableCell>
132
- </TableRow>
133
- );
134
- })}
135
- </TableBody>
136
- </Table>
104
+ <div className="min-w-0 px-2 pb-2">
105
+ <Table className="table-fixed text-[11px]">
106
+ <TableBody>
107
+ {rows.map((row) => (
108
+ <RecordRow key={row.key} row={row} depth={0} path={row.key} />
109
+ ))}
110
+ </TableBody>
111
+ </Table>
112
+ </div>
113
+ );
114
+ }
115
+
116
+ function RecordRow({
117
+ row,
118
+ depth,
119
+ path,
120
+ divided,
121
+ }: {
122
+ row: RecordPreviewRow;
123
+ depth: number;
124
+ path: string;
125
+ divided?: boolean;
126
+ }) {
127
+ const nested = nestedRecordValue(row.value);
128
+ if (!nested) {
129
+ return <FieldRow row={row} depth={depth} divided={divided} />;
130
+ }
131
+ return (
132
+ <Fragment>
133
+ <SectionRow
134
+ label={row.label}
135
+ depth={depth}
136
+ divided={divided}
137
+ parsedFromText={nested.parsedFromText}
138
+ />
139
+ <NestedRows value={nested.value} depth={depth + 1} path={path} />
140
+ </Fragment>
137
141
  );
138
142
  }
139
143
 
140
- function isNestedValue(value: unknown): boolean {
141
- if (value == null || typeof value !== "object") return false;
142
- if (!Array.isArray(value)) return true;
143
- return value.some((item) => item != null && typeof item === "object");
144
+ function FieldRow({
145
+ row,
146
+ depth,
147
+ divided,
148
+ }: {
149
+ row: RecordPreviewRow;
150
+ depth: number;
151
+ divided?: boolean;
152
+ }) {
153
+ return (
154
+ <TableRow className={cn("hover:bg-transparent", divided && "border-t-2 border-border/50")}>
155
+ <TableCell
156
+ colSpan={2}
157
+ className="min-w-0 whitespace-normal px-0 py-1.5 pr-8 align-top [overflow-wrap:anywhere]"
158
+ style={{ paddingLeft: rowIndent(depth) }}
159
+ >
160
+ <div className="grid min-w-0 gap-0.5 sm:grid-cols-[8.5rem_minmax(0,1fr)] sm:gap-2">
161
+ <div className="text-[10px] font-medium leading-snug text-muted-foreground sm:text-[11px]">
162
+ {row.label}
163
+ </div>
164
+ <div className="min-w-0">
165
+ <RecordValue value={row.value} depth={depth} />
166
+ </div>
167
+ </div>
168
+ </TableCell>
169
+ </TableRow>
170
+ );
171
+ }
172
+
173
+ function SectionRow({
174
+ label,
175
+ depth,
176
+ divided,
177
+ parsedFromText,
178
+ }: {
179
+ label: string;
180
+ depth: number;
181
+ divided?: boolean;
182
+ parsedFromText: boolean;
183
+ }) {
184
+ return (
185
+ <TableRow
186
+ className={cn("border-b-0 hover:bg-transparent", divided && "border-t-2 border-border/50")}
187
+ >
188
+ <TableHead
189
+ colSpan={2}
190
+ className="h-auto px-0 pb-1 pt-3 text-[10px] font-semibold uppercase tracking-wide text-foreground/85"
191
+ style={{ paddingLeft: rowIndent(depth) }}
192
+ >
193
+ <span className="inline-flex items-center gap-1">
194
+ {label}
195
+ {parsedFromText ? (
196
+ <BracesIcon
197
+ className="size-3 text-muted-foreground"
198
+ aria-label="JSON parsed from text"
199
+ />
200
+ ) : null}
201
+ </span>
202
+ </TableHead>
203
+ </TableRow>
204
+ );
205
+ }
206
+
207
+ function NestedRows({ value, depth, path }: { value: unknown; depth: number; path: string }) {
208
+ if (depth > MAX_NESTED_DEPTH) {
209
+ return <ValueRow value={value} depth={depth} />;
210
+ }
211
+ if (Array.isArray(value)) {
212
+ return (
213
+ <Fragment>
214
+ {value.map((item, index) => (
215
+ <ArrayItemRows
216
+ key={`${path}.${index}`}
217
+ value={item}
218
+ depth={depth}
219
+ path={`${path}.${index}`}
220
+ divided={index > 0}
221
+ />
222
+ ))}
223
+ </Fragment>
224
+ );
225
+ }
226
+ if (value != null && typeof value === "object") {
227
+ const rows = recordPreviewRows(value);
228
+ if (rows.length === 0) return <ValueRow value={value} depth={depth} />;
229
+ return (
230
+ <Fragment>
231
+ {rows.map((row) => (
232
+ <RecordRow
233
+ key={`${path}.${row.key}`}
234
+ row={row}
235
+ depth={depth}
236
+ path={`${path}.${row.key}`}
237
+ />
238
+ ))}
239
+ </Fragment>
240
+ );
241
+ }
242
+ return <ValueRow value={value} depth={depth} />;
243
+ }
244
+
245
+ function ArrayItemRows({
246
+ value,
247
+ depth,
248
+ path,
249
+ divided,
250
+ }: {
251
+ value: unknown;
252
+ depth: number;
253
+ path: string;
254
+ divided: boolean;
255
+ }) {
256
+ const nested = nestedRecordValue(value);
257
+ if (nested?.parsedFromText) {
258
+ return (
259
+ <Fragment>
260
+ <NestedTextMarkerRow depth={depth} divided={divided} />
261
+ <NestedRows value={nested.value} depth={depth + 1} path={path} />
262
+ </Fragment>
263
+ );
264
+ }
265
+ if (value != null && typeof value === "object") {
266
+ const rows = recordPreviewRows(value);
267
+ if (rows.length === 0) return <ValueRow value={value} depth={depth} divided={divided} />;
268
+ return (
269
+ <Fragment>
270
+ {rows.map((row, index) => (
271
+ <RecordRow
272
+ key={`${path}.${row.key}`}
273
+ row={row}
274
+ depth={depth}
275
+ path={`${path}.${row.key}`}
276
+ divided={divided && index === 0}
277
+ />
278
+ ))}
279
+ </Fragment>
280
+ );
281
+ }
282
+ return <ValueRow value={value} depth={depth} divided={divided} />;
283
+ }
284
+
285
+ function NestedTextMarkerRow({ depth, divided }: { depth: number; divided: boolean }) {
286
+ return (
287
+ <TableRow className={cn("border-b-0 hover:bg-transparent", divided && "border-t-2")}>
288
+ <TableHead
289
+ colSpan={2}
290
+ className="h-auto px-0 pb-0.5 pt-2 text-muted-foreground"
291
+ style={{ paddingLeft: rowIndent(depth) }}
292
+ >
293
+ <BracesIcon className="size-3" aria-label="JSON parsed from text" />
294
+ </TableHead>
295
+ </TableRow>
296
+ );
297
+ }
298
+
299
+ function ValueRow({ value, depth, divided }: { value: unknown; depth: number; divided?: boolean }) {
300
+ return (
301
+ <TableRow className={cn("hover:bg-transparent", divided && "border-t-2 border-border/50")}>
302
+ <TableCell
303
+ colSpan={2}
304
+ className="min-w-0 whitespace-normal px-0 py-1.5 pr-8 align-top [overflow-wrap:anywhere]"
305
+ style={{ paddingLeft: rowIndent(depth) }}
306
+ >
307
+ <RecordValue value={value} depth={depth} />
308
+ </TableCell>
309
+ </TableRow>
310
+ );
311
+ }
312
+
313
+ function rowIndent(depth: number): string {
314
+ return `${Math.min(depth, MAX_INDENT_DEPTH) * 0.9}rem`;
315
+ }
316
+
317
+ function nestedRecordValue(
318
+ value: unknown,
319
+ ): { value: Record<string, unknown> | unknown[]; parsedFromText: boolean } | undefined {
320
+ const parsed = parseNestedJsonText(value);
321
+ if (parsed) return { value: parsed, parsedFromText: true };
322
+ if (value == null || typeof value !== "object") return undefined;
323
+ if (Array.isArray(value)) {
324
+ const hasNestedValue = value.some(
325
+ (item) =>
326
+ (item != null && typeof item === "object") || parseNestedJsonText(item) !== undefined,
327
+ );
328
+ if (!hasNestedValue) return undefined;
329
+ }
330
+ return {
331
+ value: value as Record<string, unknown> | unknown[],
332
+ parsedFromText: false,
333
+ };
144
334
  }
145
335
 
146
336
  function RecordValue({ value, depth }: { value: unknown; depth: number }) {
@@ -151,6 +341,16 @@ function RecordValue({ value, depth }: { value: unknown; depth: number }) {
151
341
  return <span className="tabular-nums">{String(value)}</span>;
152
342
  }
153
343
  if (typeof value === "string") {
344
+ const language = detectCodeLanguage(value);
345
+ if (language === "sql") {
346
+ return (
347
+ <HighlightedCodeBlock
348
+ source={value}
349
+ language={language}
350
+ className="rounded bg-background/60 p-1.5 text-[10px]"
351
+ />
352
+ );
353
+ }
154
354
  if (looksLikeMarkdown(value)) {
155
355
  return (
156
356
  <div className={MARKDOWN_CELL_CLASSES}>
@@ -160,16 +360,13 @@ function RecordValue({ value, depth }: { value: unknown; depth: number }) {
160
360
  }
161
361
  return <span className="whitespace-pre-wrap break-words">{value}</span>;
162
362
  }
163
- if (typeof value === "object" && !Array.isArray(value) && depth < NESTED_DEPTH) {
164
- return <RecordTable rows={recordPreviewRows(value)} depth={depth + 1} />;
165
- }
166
363
  if (Array.isArray(value)) {
167
364
  if (value.every((item) => item == null || typeof item !== "object")) {
168
365
  return (
169
366
  <span className="whitespace-pre-wrap break-words">{value.map(String).join(", ")}</span>
170
367
  );
171
368
  }
172
- if (depth < NESTED_DEPTH) {
369
+ if (depth < MAX_NESTED_DEPTH) {
173
370
  return (
174
371
  <div className="min-w-0 divide-y divide-border/60">
175
372
  {value.map((item, index) => (