@tangle-network/ui 11.11.2 → 11.11.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +9 -1
- package/dist/{chat-D-9Fm8A4.js → chat-CpzvkA6m.js} +7 -7
- package/dist/chat.d.ts +1 -1
- package/dist/chat.js +1 -1
- package/dist/{document-editor-pane-BcoABlhn.d.ts → document-editor-pane-DHM8DDhb.d.ts} +4 -1
- package/dist/{document-editor-pane-BxlPjaTP.js → document-editor-pane-LyanNypc.js} +6 -14
- package/dist/editor.d.ts +1 -1
- package/dist/editor.js +1 -1
- package/dist/{files-Bug33qCl.js → files-DgPc3vI2.js} +16 -7
- package/dist/files.d.ts +1 -1
- package/dist/files.js +1 -1
- package/dist/{index-DhYmSaLF.d.ts → index-CyGSLGVF.d.ts} +6 -17
- package/dist/{index-CUhfHT_V.d.ts → index-aoBxiKex.d.ts} +2 -2
- package/dist/index.d.ts +4 -3
- package/dist/index.js +6 -5
- package/dist/{openui-artifact-renderer-DrU1AdVk.js → openui-artifact-renderer-C3bmqfjX.js} +15 -20
- package/dist/openui-schema.d.ts +3 -0
- package/dist/openui-schema.js +2 -0
- package/dist/openui.d.ts +2 -1
- package/dist/openui.js +3 -2
- package/dist/{run-group-BACLbCFl.js → run-group-CxNskmHd.js} +1 -1
- package/dist/run.js +1 -1
- package/dist/schema-BFKgCGH-.d.ts +228 -0
- package/dist/schema-BpW9rSSG.js +9754 -0
- package/package.json +10 -3
- package/src/chat/user-message.test.tsx +9 -3
- package/src/chat/user-message.tsx +7 -5
- package/src/editor/document-editor-pane.tsx +5 -1
- package/src/files/file-artifact-pane.test.tsx +41 -0
- package/src/files/file-artifact-pane.tsx +3 -0
- package/src/files/file-preview.tsx +16 -3
- package/src/openui/index.ts +10 -0
- package/src/openui/openui-artifact-renderer.stories.tsx +36 -0
- package/src/openui/openui-artifact-renderer.tsx +19 -27
- package/src/openui/schema-compiled.ts +3 -0
- package/src/openui/schema-data.ts +95 -0
- package/src/openui/schema.test.tsx +74 -0
- package/src/openui/schema.ts +136 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/ui",
|
|
3
|
-
"version": "11.11.
|
|
3
|
+
"version": "11.11.4",
|
|
4
4
|
"description": "Generic React UI components for Tangle products — primitives, chat, run, files, editor, markdown.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,6 +47,11 @@
|
|
|
47
47
|
"import": "./dist/openui.js",
|
|
48
48
|
"default": "./dist/openui.js"
|
|
49
49
|
},
|
|
50
|
+
"./openui-schema": {
|
|
51
|
+
"types": "./dist/openui-schema.d.ts",
|
|
52
|
+
"import": "./dist/openui-schema.js",
|
|
53
|
+
"default": "./dist/openui-schema.js"
|
|
54
|
+
},
|
|
50
55
|
"./files": {
|
|
51
56
|
"types": "./dist/files.d.ts",
|
|
52
57
|
"import": "./dist/files.js",
|
|
@@ -134,7 +139,7 @@
|
|
|
134
139
|
"peerDependencies": {
|
|
135
140
|
"@hocuspocus/provider": ">=2.15.0 <5.0.0",
|
|
136
141
|
"@nanostores/react": ">=0.8.4 <3.0.0",
|
|
137
|
-
"@tangle-network/brand": "^1.
|
|
142
|
+
"@tangle-network/brand": "^1.9.0",
|
|
138
143
|
"@tiptap/core": ">=3.20.4 <4.0.0",
|
|
139
144
|
"@tiptap/extension-collaboration": ">=3.20.4 <4.0.0",
|
|
140
145
|
"@tiptap/extension-collaboration-caret": ">=3.7.0 <4.0.0",
|
|
@@ -191,6 +196,7 @@
|
|
|
191
196
|
"@types/react": "^19.3.0",
|
|
192
197
|
"@types/react-syntax-highlighter": "^15.5.13",
|
|
193
198
|
"@types/turndown": "^5.0.6",
|
|
199
|
+
"ajv": "8.17.1",
|
|
194
200
|
"nanostores": "^1.5.4",
|
|
195
201
|
"react": "^19.3.0",
|
|
196
202
|
"react-dom": "^19.3.0",
|
|
@@ -207,7 +213,8 @@
|
|
|
207
213
|
"design-system"
|
|
208
214
|
],
|
|
209
215
|
"scripts": {
|
|
210
|
-
"build": "tsdown && node ../../scripts/validate-dist.mjs",
|
|
216
|
+
"build": "node scripts/generate-openui-validators.mjs --check && tsdown && node ../../scripts/validate-dist.mjs && node scripts/probe-openui-worker.mjs",
|
|
217
|
+
"gen:openui-schema": "node scripts/generate-openui-validators.mjs",
|
|
211
218
|
"dev": "tsdown --watch"
|
|
212
219
|
}
|
|
213
220
|
}
|
|
@@ -3,13 +3,19 @@ import { describe, expect, it } from "vitest"
|
|
|
3
3
|
import { UserMessage } from "./user-message"
|
|
4
4
|
|
|
5
5
|
describe("UserMessage", () => {
|
|
6
|
-
it("renders the text in a
|
|
6
|
+
it("renders the text in a bordered card that wraps in narrow panes", () => {
|
|
7
7
|
const { container } = render(<UserMessage content="Run the tests." />)
|
|
8
8
|
expect(screen.getByText("Run the tests.")).toBeInTheDocument()
|
|
9
9
|
const bubble = container.querySelector(".rounded-2xl")
|
|
10
10
|
expect(bubble).not.toBeNull()
|
|
11
|
-
expect(bubble!.className).toContain("bg-
|
|
12
|
-
expect(bubble!.className).
|
|
11
|
+
expect(bubble!.className).toContain("bg-card")
|
|
12
|
+
expect(bubble!.className).toContain("border-border")
|
|
13
|
+
expect(container.querySelector(".group")?.className).toContain(
|
|
14
|
+
"min-w-0",
|
|
15
|
+
)
|
|
16
|
+
expect(screen.getByText("Run the tests.").className).toContain(
|
|
17
|
+
"break-words",
|
|
18
|
+
)
|
|
13
19
|
})
|
|
14
20
|
|
|
15
21
|
it("keeps the timestamp beside the bubble as a hover-revealed label", () => {
|
|
@@ -17,8 +17,8 @@ function formatTime(date: Date): string {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* The single user message bubble — a quiet, right-aligned
|
|
21
|
-
*
|
|
20
|
+
* The single user message bubble — a quiet, right-aligned card with a subtle
|
|
21
|
+
* semantic border (no loud fill, no uppercase label). The timestamp sits
|
|
22
22
|
* beside the bubble and appears on hover, so it costs no height. Used by both
|
|
23
23
|
* the run/message list (session-model `parts`) and AgentTimeline (direct
|
|
24
24
|
* `content`/`timestamp`).
|
|
@@ -36,8 +36,10 @@ export const UserMessage = memo(
|
|
|
36
36
|
|
|
37
37
|
return (
|
|
38
38
|
<div className="flex justify-end">
|
|
39
|
-
<div className="group flex max-w-[78%] flex-col items-end gap-2">
|
|
40
|
-
<div
|
|
39
|
+
<div className="group flex min-w-0 max-w-[78%] flex-col items-end gap-2">
|
|
40
|
+
<div
|
|
41
|
+
className="relative w-full min-w-0 rounded-2xl border border-border bg-card px-4 py-3"
|
|
42
|
+
>
|
|
41
43
|
{timestamp ? (
|
|
42
44
|
<span
|
|
43
45
|
data-user-message-time=""
|
|
@@ -46,7 +48,7 @@ export const UserMessage = memo(
|
|
|
46
48
|
{formatTime(timestamp)}
|
|
47
49
|
</span>
|
|
48
50
|
) : null}
|
|
49
|
-
<div className="whitespace-pre-wrap text-[var(--font-size-base)] leading-[1.5] text-foreground">
|
|
51
|
+
<div className="break-words whitespace-pre-wrap text-[var(--font-size-base)] leading-[1.5] text-foreground">
|
|
50
52
|
{text}
|
|
51
53
|
</div>
|
|
52
54
|
</div>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { type ReactNode, useEffect, useMemo, useState } from "react";
|
|
4
|
+
import type { UrlTransform } from "react-markdown";
|
|
4
5
|
import { PencilLine, Save, Users, Wifi, WifiOff } from "lucide-react";
|
|
5
6
|
import { Markdown } from "../markdown/markdown";
|
|
6
7
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../primitives/tabs";
|
|
@@ -36,6 +37,8 @@ export interface DocumentEditorPaneProps
|
|
|
36
37
|
tabs?: ArtifactPaneProps["tabs"];
|
|
37
38
|
toolbar?: ReactNode;
|
|
38
39
|
markdown?: string;
|
|
40
|
+
/** Transform Markdown links and images through the shared URL policy. */
|
|
41
|
+
urlTransform?: UrlTransform;
|
|
39
42
|
mode?: DocumentEditorMode;
|
|
40
43
|
defaultMode?: DocumentEditorMode;
|
|
41
44
|
onModeChange?: (mode: DocumentEditorMode) => void;
|
|
@@ -188,6 +191,7 @@ export function DocumentEditorPane({
|
|
|
188
191
|
tabs,
|
|
189
192
|
toolbar,
|
|
190
193
|
markdown = "",
|
|
194
|
+
urlTransform,
|
|
191
195
|
mode,
|
|
192
196
|
defaultMode = "preview",
|
|
193
197
|
onModeChange,
|
|
@@ -300,7 +304,7 @@ export function DocumentEditorPane({
|
|
|
300
304
|
previewClassName,
|
|
301
305
|
)}
|
|
302
306
|
>
|
|
303
|
-
<Markdown>{draft}</Markdown>
|
|
307
|
+
<Markdown urlTransform={urlTransform}>{draft}</Markdown>
|
|
304
308
|
</div>
|
|
305
309
|
);
|
|
306
310
|
|
|
@@ -113,6 +113,47 @@ describe("FileArtifactPane previews", () => {
|
|
|
113
113
|
expect(container.querySelector("strong")).toHaveTextContent("bold");
|
|
114
114
|
});
|
|
115
115
|
|
|
116
|
+
it("forwards Markdown URL transforms to file and rich editor previews", async () => {
|
|
117
|
+
const urlTransform = vi.fn((url: string, key: string) => (
|
|
118
|
+
key === "href" && url === "sibling.md"
|
|
119
|
+
? "/app/ws-1/vault?file=docs%2Fsibling.md"
|
|
120
|
+
: url
|
|
121
|
+
));
|
|
122
|
+
const readOnly = render(
|
|
123
|
+
<FileArtifactPane
|
|
124
|
+
filename="README.md"
|
|
125
|
+
content={"[Sibling](sibling.md)"}
|
|
126
|
+
urlTransform={urlTransform}
|
|
127
|
+
/>,
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
expect(screen.getByRole("link", { name: "Sibling" })).toHaveAttribute(
|
|
131
|
+
"href",
|
|
132
|
+
"/app/ws-1/vault?file=docs%2Fsibling.md",
|
|
133
|
+
);
|
|
134
|
+
readOnly.unmount();
|
|
135
|
+
|
|
136
|
+
render(
|
|
137
|
+
<FileArtifactPane
|
|
138
|
+
path="docs/README.md"
|
|
139
|
+
filename="README.md"
|
|
140
|
+
content={"[Sibling](sibling.md)"}
|
|
141
|
+
urlTransform={urlTransform}
|
|
142
|
+
editor={{ enabled: true, defaultMode: "preview" }}
|
|
143
|
+
/>,
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
expect(await screen.findByRole("link", { name: "Sibling" })).toHaveAttribute(
|
|
147
|
+
"href",
|
|
148
|
+
"/app/ws-1/vault?file=docs%2Fsibling.md",
|
|
149
|
+
);
|
|
150
|
+
expect(urlTransform).toHaveBeenCalledWith(
|
|
151
|
+
"sibling.md",
|
|
152
|
+
"href",
|
|
153
|
+
expect.objectContaining({ tagName: "a" }),
|
|
154
|
+
);
|
|
155
|
+
});
|
|
156
|
+
|
|
116
157
|
it("renders a binary file with no download link as one muted sentence", () => {
|
|
117
158
|
const onDownload = vi.fn();
|
|
118
159
|
const withDownload = render(
|
|
@@ -64,6 +64,7 @@ export function FileArtifactPane({
|
|
|
64
64
|
size,
|
|
65
65
|
onClose,
|
|
66
66
|
onDownload,
|
|
67
|
+
urlTransform,
|
|
67
68
|
path,
|
|
68
69
|
tabs = [],
|
|
69
70
|
activeTabId,
|
|
@@ -154,6 +155,7 @@ export function FileArtifactPane({
|
|
|
154
155
|
headerClassName={headerClassName}
|
|
155
156
|
hideTitleBlock={hideTitleBlock}
|
|
156
157
|
markdown={content ?? ""}
|
|
158
|
+
urlTransform={urlTransform}
|
|
157
159
|
mode={editor.mode}
|
|
158
160
|
defaultMode={editor.defaultMode}
|
|
159
161
|
onModeChange={editor.onModeChange}
|
|
@@ -193,6 +195,7 @@ export function FileArtifactPane({
|
|
|
193
195
|
blobUrl={blobUrl}
|
|
194
196
|
mimeType={mimeType}
|
|
195
197
|
size={size}
|
|
198
|
+
urlTransform={urlTransform}
|
|
196
199
|
onDownload={onDownload}
|
|
197
200
|
hideHeader={true}
|
|
198
201
|
/>
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
X,
|
|
23
23
|
type LucideIcon,
|
|
24
24
|
} from "lucide-react";
|
|
25
|
+
import type { UrlTransform } from "react-markdown";
|
|
25
26
|
import { focusRingInset } from "../lib/focus";
|
|
26
27
|
import { cn } from "../lib/utils";
|
|
27
28
|
import { Button } from "../primitives/button";
|
|
@@ -44,6 +45,8 @@ export interface FilePreviewProps {
|
|
|
44
45
|
mimeType?: string;
|
|
45
46
|
/** Size in bytes; download cards show it when known. */
|
|
46
47
|
size?: number;
|
|
48
|
+
/** Transform Markdown links and images through the shared URL policy. */
|
|
49
|
+
urlTransform?: UrlTransform;
|
|
47
50
|
onClose?: () => void;
|
|
48
51
|
onDownload?: () => void;
|
|
49
52
|
hideHeader?: boolean;
|
|
@@ -301,10 +304,16 @@ function TextPreview({ content }: { content: string }) {
|
|
|
301
304
|
);
|
|
302
305
|
}
|
|
303
306
|
|
|
304
|
-
function MarkdownPreview({
|
|
307
|
+
function MarkdownPreview({
|
|
308
|
+
content,
|
|
309
|
+
urlTransform,
|
|
310
|
+
}: {
|
|
311
|
+
content: string;
|
|
312
|
+
urlTransform?: UrlTransform;
|
|
313
|
+
}) {
|
|
305
314
|
return (
|
|
306
315
|
<div className="rounded-[var(--radius-md)] border border-border bg-background p-5">
|
|
307
|
-
<Markdown>{content}</Markdown>
|
|
316
|
+
<Markdown urlTransform={urlTransform}>{content}</Markdown>
|
|
308
317
|
</div>
|
|
309
318
|
);
|
|
310
319
|
}
|
|
@@ -358,6 +367,7 @@ function PreviewBody({
|
|
|
358
367
|
content,
|
|
359
368
|
blobUrl,
|
|
360
369
|
size,
|
|
370
|
+
urlTransform,
|
|
361
371
|
onDownload,
|
|
362
372
|
}: {
|
|
363
373
|
kind: FilePreviewKind;
|
|
@@ -365,6 +375,7 @@ function PreviewBody({
|
|
|
365
375
|
content?: string;
|
|
366
376
|
blobUrl?: string;
|
|
367
377
|
size?: number;
|
|
378
|
+
urlTransform?: UrlTransform;
|
|
368
379
|
onDownload?: () => void;
|
|
369
380
|
}) {
|
|
370
381
|
const hasText = typeof content === "string";
|
|
@@ -404,7 +415,7 @@ function PreviewBody({
|
|
|
404
415
|
if (hasText) return <CodePreview content={content} filename={filename} kind={kind} />;
|
|
405
416
|
return card(NO_INLINE_CONTENT);
|
|
406
417
|
case "markdown":
|
|
407
|
-
if (hasText) return <MarkdownPreview content={content} />;
|
|
418
|
+
if (hasText) return <MarkdownPreview content={content} urlTransform={urlTransform} />;
|
|
408
419
|
return card(NO_INLINE_CONTENT);
|
|
409
420
|
case "text":
|
|
410
421
|
if (hasText) return <TextPreview content={content} />;
|
|
@@ -423,6 +434,7 @@ export function FilePreview({
|
|
|
423
434
|
blobUrl,
|
|
424
435
|
mimeType,
|
|
425
436
|
size,
|
|
437
|
+
urlTransform,
|
|
426
438
|
onClose,
|
|
427
439
|
onDownload,
|
|
428
440
|
hideHeader = false,
|
|
@@ -473,6 +485,7 @@ export function FilePreview({
|
|
|
473
485
|
content={content}
|
|
474
486
|
blobUrl={blobUrl}
|
|
475
487
|
size={size}
|
|
488
|
+
urlTransform={urlTransform}
|
|
476
489
|
onDownload={onDownload}
|
|
477
490
|
/>
|
|
478
491
|
</div>
|
package/src/openui/index.ts
CHANGED
|
@@ -18,3 +18,13 @@ export {
|
|
|
18
18
|
type OpenUIGridNode,
|
|
19
19
|
type OpenUICardNode,
|
|
20
20
|
} from "./openui-artifact-renderer";
|
|
21
|
+
|
|
22
|
+
export {
|
|
23
|
+
OPENUI_NODE_TYPES,
|
|
24
|
+
OPENUI_NODE_JSON_SCHEMA,
|
|
25
|
+
findUnsupportedOpenUINode,
|
|
26
|
+
validateOpenUIJsonNode,
|
|
27
|
+
validateOpenUIJsonArtifact,
|
|
28
|
+
type OpenUISchemaIssue,
|
|
29
|
+
type OpenUISchemaValidation,
|
|
30
|
+
} from "./schema";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { OpenUIArtifactRenderer } from "./openui-artifact-renderer";
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof OpenUIArtifactRenderer> = {
|
|
5
|
+
title: "OpenUI/ArtifactRenderer",
|
|
6
|
+
component: OpenUIArtifactRenderer,
|
|
7
|
+
decorators: [(Story) => <div className="mx-auto max-w-2xl p-6"><Story /></div>],
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
type Story = StoryObj<typeof OpenUIArtifactRenderer>;
|
|
12
|
+
|
|
13
|
+
export const NestedCard: Story = {
|
|
14
|
+
args: {
|
|
15
|
+
schema: {
|
|
16
|
+
type: "card",
|
|
17
|
+
title: "Release proof",
|
|
18
|
+
children: [
|
|
19
|
+
{ type: "heading", text: "Result" },
|
|
20
|
+
{ type: "stack", children: [
|
|
21
|
+
{ type: "stat", label: "Checks", value: "3" },
|
|
22
|
+
{ type: "text", text: "The saved result remains visible after reload." },
|
|
23
|
+
] },
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const UnsupportedSavedNode: Story = {
|
|
30
|
+
args: {
|
|
31
|
+
schema: {
|
|
32
|
+
type: "card",
|
|
33
|
+
children: [{ type: "section", children: [{ type: "text", text: "This content must not disappear silently." }] }],
|
|
34
|
+
} as never,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../primitives/table";
|
|
14
14
|
import { CodeBlock } from "../primitives/code-block";
|
|
15
15
|
import { Markdown } from "../markdown/markdown";
|
|
16
|
+
import { findUnsupportedOpenUINode, type OPENUI_NODE_TYPES } from "./schema";
|
|
16
17
|
|
|
17
18
|
export type OpenUIPrimitive = string | number | boolean | null | undefined;
|
|
18
19
|
|
|
@@ -148,21 +149,14 @@ export interface OpenUIArtifactRendererProps {
|
|
|
148
149
|
className?: string;
|
|
149
150
|
}
|
|
150
151
|
|
|
151
|
-
|
|
152
|
-
"
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
"table",
|
|
160
|
-
"actions",
|
|
161
|
-
"separator",
|
|
162
|
-
"stack",
|
|
163
|
-
"grid",
|
|
164
|
-
"card",
|
|
165
|
-
]);
|
|
152
|
+
type RendererNodeTypesMatchSchema =
|
|
153
|
+
Exclude<OpenUIComponentNode["type"], (typeof OPENUI_NODE_TYPES)[number]> extends never
|
|
154
|
+
? Exclude<(typeof OPENUI_NODE_TYPES)[number], OpenUIComponentNode["type"]> extends never
|
|
155
|
+
? true
|
|
156
|
+
: never
|
|
157
|
+
: never;
|
|
158
|
+
const rendererNodeTypesMatchSchema: RendererNodeTypesMatchSchema = true;
|
|
159
|
+
void rendererNodeTypesMatchSchema;
|
|
166
160
|
|
|
167
161
|
const GAP_STYLES = {
|
|
168
162
|
sm: "gap-2",
|
|
@@ -200,16 +194,6 @@ function asArray<T>(value: unknown): T[] {
|
|
|
200
194
|
return Array.isArray(value) ? (value as T[]) : [];
|
|
201
195
|
}
|
|
202
196
|
|
|
203
|
-
function isOpenUIComponentNode(value: unknown): value is OpenUIComponentNode {
|
|
204
|
-
return (
|
|
205
|
-
typeof value === "object" &&
|
|
206
|
-
value !== null &&
|
|
207
|
-
"type" in value &&
|
|
208
|
-
typeof (value as { type?: unknown }).type === "string" &&
|
|
209
|
-
NODE_TYPES.has((value as { type: string }).type)
|
|
210
|
-
);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
197
|
function renderActions(actions: OpenUIAction[], onAction?: (action: OpenUIAction) => void) {
|
|
214
198
|
if (actions.length === 0) return null;
|
|
215
199
|
|
|
@@ -394,7 +378,7 @@ function renderNode(node: OpenUIComponentNode, onAction?: (action: OpenUIAction)
|
|
|
394
378
|
column.align === "right" && "text-right tabular-nums",
|
|
395
379
|
)}
|
|
396
380
|
>
|
|
397
|
-
{formatValue(row[column.key])}
|
|
381
|
+
{formatValue(Object.prototype.hasOwnProperty.call(row, column.key) ? row[column.key] : undefined)}
|
|
398
382
|
</TableCell>
|
|
399
383
|
))}
|
|
400
384
|
</TableRow>
|
|
@@ -510,7 +494,15 @@ export function OpenUIArtifactRenderer({
|
|
|
510
494
|
onAction,
|
|
511
495
|
className,
|
|
512
496
|
}: OpenUIArtifactRendererProps) {
|
|
513
|
-
const
|
|
497
|
+
const issue = findUnsupportedOpenUINode(schema);
|
|
498
|
+
if (issue) {
|
|
499
|
+
return (
|
|
500
|
+
<div role="alert" className={cn("rounded-[var(--radius-xl)] border border-border bg-card p-5 text-sm text-foreground", className)}>
|
|
501
|
+
Cannot display this saved view. {issue.message} Path: {issue.path}.
|
|
502
|
+
</div>
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
const nodes = Array.isArray(schema) ? schema : [schema];
|
|
514
506
|
|
|
515
507
|
if (nodes.length === 0) {
|
|
516
508
|
return (
|