rift-ds 1.8.0 → 1.9.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.
- package/README.md +3 -3
- package/chat/ChatAgentPane.d.ts +8 -5
- package/chat/ChatAgentPane.js +4 -1
- package/chat/ChatArtifactCard.d.ts +13 -0
- package/chat/ChatArtifactCard.js +21 -0
- package/chat/ChatArtifactPane.d.ts +18 -0
- package/chat/ChatArtifactPane.js +43 -0
- package/chat/ChatProvider.d.ts +4 -2
- package/chat/ChatProvider.js +15 -0
- package/chat/ChatWidget.css +208 -21
- package/chat/ChatWidget.d.ts +3 -1
- package/chat/ChatWidget.js +287 -73
- package/chat/TurnContent.d.ts +3 -1
- package/chat/TurnContent.js +9 -2
- package/chat/artifacts.d.ts +5 -0
- package/chat/artifacts.js +14 -0
- package/chat/context.d.ts +18 -1
- package/chat/context.js +2 -0
- package/chat/demo-artifacts.d.ts +6 -0
- package/chat/demo-artifacts.js +80 -0
- package/chat/demo-card-components.d.ts +2 -0
- package/chat/demo-card-components.js +7 -0
- package/chat/demo-script.js +24 -2
- package/chat/demo.css +6 -0
- package/chat/demo.d.ts +1 -0
- package/chat/demo.js +4 -0
- package/chat/index.d.ts +6 -3
- package/chat/index.js +12 -2
- package/chat/queries.d.ts +10 -1
- package/chat/queries.js +5 -1
- package/chat/registry.json.d.ts +6 -1
- package/chat/registry.json.js +1 -1
- package/chat/types.d.ts +36 -0
- package/chat/types.js +2 -0
- package/components/AgentRail/AgentRail.css +14 -5
- package/components/AgentRail/AgentRail.d.ts +2 -0
- package/components/AgentRail/AgentRail.js +3 -2
- package/components/ArtifactCard/ArtifactCard.css +106 -0
- package/components/ArtifactCard/ArtifactCard.d.ts +27 -0
- package/components/ArtifactCard/ArtifactCard.js +29 -0
- package/components/ArtifactPanel/ArtifactPanel.css +159 -0
- package/components/ArtifactPanel/ArtifactPanel.d.ts +70 -0
- package/components/ArtifactPanel/ArtifactPanel.js +131 -0
- package/components/ThreadPanel/ThreadPanel.css +87 -33
- package/components/ThreadPanel/ThreadPanel.js +21 -10
- package/components/registry.json +16 -0
- package/components/registry.json.d.ts +16 -0
- package/components/registry.json.js +1 -1
- package/index.d.ts +2 -0
- package/index.js +4 -0
- package/package.json +1 -1
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { DemoDashboardArtifact } from "./demo-card-components.js";
|
|
4
|
+
const DEMO_ARTIFACT_RENDERERS = {
|
|
5
|
+
"demo-dashboard": () => /* @__PURE__ */ jsx(DemoDashboardArtifact, {})
|
|
6
|
+
};
|
|
7
|
+
const DEMO_DASHBOARD_ARTIFACT = {
|
|
8
|
+
id: "launch-dashboard",
|
|
9
|
+
title: "Launch dashboard",
|
|
10
|
+
kind: "demo-dashboard",
|
|
11
|
+
meta: "Dashboard",
|
|
12
|
+
icon: "space_dashboard"
|
|
13
|
+
};
|
|
14
|
+
const LAUNCH_PAGE_HTML = `<!doctype html>
|
|
15
|
+
<html lang="en">
|
|
16
|
+
<head>
|
|
17
|
+
<meta charset="utf-8">
|
|
18
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
19
|
+
<meta name="color-scheme" content="light dark">
|
|
20
|
+
<title>Acme Workspaces</title>
|
|
21
|
+
<style>
|
|
22
|
+
* { box-sizing: border-box; }
|
|
23
|
+
body { margin: 0; font-family: ui-sans-serif, system-ui, sans-serif; color: #14181f; background: #f6f4ef; line-height: 1.5; }
|
|
24
|
+
header { display: flex; align-items: center; justify-content: space-between; padding: 20px 32px; }
|
|
25
|
+
.brand { font-weight: 700; letter-spacing: -0.01em; }
|
|
26
|
+
nav { display: flex; gap: 20px; font-size: 14px; color: #55606e; }
|
|
27
|
+
.hero { padding: 56px 32px 40px; max-width: 720px; }
|
|
28
|
+
.eyebrow { font-size: 12px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: #7a5cff; }
|
|
29
|
+
h1 { margin: 12px 0 16px; font-size: clamp(32px, 6vw, 52px); line-height: 1.05; letter-spacing: -0.03em; }
|
|
30
|
+
.lede { margin: 0 0 28px; font-size: 18px; color: #414b58; max-width: 52ch; }
|
|
31
|
+
.cta { display: inline-block; padding: 12px 22px; border-radius: 999px; background: #14181f; color: #fff; font-weight: 600; font-size: 15px; }
|
|
32
|
+
.ghost { margin-left: 16px; font-size: 15px; font-weight: 600; color: #14181f; }
|
|
33
|
+
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 16px; padding: 8px 32px 48px; }
|
|
34
|
+
.card { padding: 20px; border-radius: 16px; background: #fff; border: 1px solid #e7e3da; }
|
|
35
|
+
.card h2 { margin: 0 0 6px; font-size: 16px; }
|
|
36
|
+
.card p { margin: 0; font-size: 14px; color: #55606e; }
|
|
37
|
+
footer { padding: 20px 32px 32px; font-size: 13px; color: #7b8592; }
|
|
38
|
+
@media (prefers-color-scheme: dark) {
|
|
39
|
+
body { color: #eef0f3; background: #101216; }
|
|
40
|
+
nav, .card p, footer { color: #98a1ad; }
|
|
41
|
+
.eyebrow { color: #a999ff; }
|
|
42
|
+
.lede { color: #c2c8d0; }
|
|
43
|
+
.cta { background: #eef0f3; color: #101216; }
|
|
44
|
+
.ghost { color: #eef0f3; }
|
|
45
|
+
.card { background: #181b21; border-color: #2a2e36; }
|
|
46
|
+
}
|
|
47
|
+
</style>
|
|
48
|
+
</head>
|
|
49
|
+
<body>
|
|
50
|
+
<header>
|
|
51
|
+
<span class="brand">Acme</span>
|
|
52
|
+
<nav><span>Product</span><span>Plans</span><span>Sign in</span></nav>
|
|
53
|
+
</header>
|
|
54
|
+
<section class="hero">
|
|
55
|
+
<span class="eyebrow">Launching this week</span>
|
|
56
|
+
<h1>One workspace for the whole team.</h1>
|
|
57
|
+
<p class="lede">Shared projects, guest seats and ninety days of version history, ready the moment your team joins.</p>
|
|
58
|
+
<span class="cta">Start the free trial</span><span class="ghost">See the plans</span>
|
|
59
|
+
</section>
|
|
60
|
+
<section class="grid">
|
|
61
|
+
<div class="card"><h2>Shared workspaces</h2><p>Every project in one place, open to the people who need it.</p></div>
|
|
62
|
+
<div class="card"><h2>Guest seats</h2><p>Bring in a client or a contractor without buying a full seat.</p></div>
|
|
63
|
+
<div class="card"><h2>Version history</h2><p>Ninety days of every change, one click from restored.</p></div>
|
|
64
|
+
</section>
|
|
65
|
+
<footer>Acme is a made-up company. This page is a demo artifact.</footer>
|
|
66
|
+
</body>
|
|
67
|
+
</html>`;
|
|
68
|
+
const DEMO_PAGE_ARTIFACT = {
|
|
69
|
+
id: "launch-page",
|
|
70
|
+
title: "Launch page",
|
|
71
|
+
kind: "html",
|
|
72
|
+
meta: "Web page",
|
|
73
|
+
icon: "web",
|
|
74
|
+
html: LAUNCH_PAGE_HTML
|
|
75
|
+
};
|
|
76
|
+
export {
|
|
77
|
+
DEMO_ARTIFACT_RENDERERS,
|
|
78
|
+
DEMO_DASHBOARD_ARTIFACT,
|
|
79
|
+
DEMO_PAGE_ARTIFACT
|
|
80
|
+
};
|
|
@@ -44,3 +44,5 @@ export declare function DemoStack({ card, renderers }: {
|
|
|
44
44
|
card: DemoStackCard;
|
|
45
45
|
renderers: ChatCardRenderers;
|
|
46
46
|
}): import("react").JSX.Element;
|
|
47
|
+
/** The dashboard's rows ask the chat, like the welcome board's. */
|
|
48
|
+
export declare function DemoDashboardArtifact(): import("react").JSX.Element;
|
|
@@ -8,6 +8,8 @@ import { CodeBlock } from "../components/CodeBlock/CodeBlock.js";
|
|
|
8
8
|
import { InterruptCard } from "../components/InterruptCard/InterruptCard.js";
|
|
9
9
|
import { MessageCard } from "../components/MessageCard/MessageCard.js";
|
|
10
10
|
import { ToolCall } from "../components/ToolCall/ToolCall.js";
|
|
11
|
+
import { useChatWidget } from "./context.js";
|
|
12
|
+
import { DemoBoard } from "./DemoWelcome.js";
|
|
11
13
|
const RETRY_RUN_MS = 1400;
|
|
12
14
|
function DemoToolCall({ card }) {
|
|
13
15
|
const [status, setStatus] = useState(card.status);
|
|
@@ -51,8 +53,13 @@ function DemoStack({ card, renderers }) {
|
|
|
51
53
|
return render ? /* @__PURE__ */ jsx("div", { children: render(item.data) }, index) : null;
|
|
52
54
|
}) });
|
|
53
55
|
}
|
|
56
|
+
function DemoDashboardArtifact() {
|
|
57
|
+
const { send } = useChatWidget();
|
|
58
|
+
return /* @__PURE__ */ jsx("div", { className: "ds-chat-demo__artifact-board", children: /* @__PURE__ */ jsx(DemoBoard, { onAsk: (prompt) => send(prompt) }) });
|
|
59
|
+
}
|
|
54
60
|
export {
|
|
55
61
|
DemoChart,
|
|
62
|
+
DemoDashboardArtifact,
|
|
56
63
|
DemoInterrupt,
|
|
57
64
|
DemoStack,
|
|
58
65
|
DemoToolCall
|
package/chat/demo-script.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { artifactCard } from "./artifacts.js";
|
|
2
|
+
import { DEMO_DASHBOARD_ARTIFACT, DEMO_PAGE_ARTIFACT } from "./demo-artifacts.js";
|
|
1
3
|
import { scriptChat } from "./sim.js";
|
|
2
4
|
const TRIAL_SIGNUPS = [
|
|
3
5
|
{ week: "W1", signups: 320 },
|
|
@@ -192,7 +194,27 @@ const DEMO_SCRIPT = scriptChat().user("How do I get started?").assistant(
|
|
|
192
194
|
]
|
|
193
195
|
}),
|
|
194
196
|
followups: ["Go ahead and invite the other three", "How is the trial going?", "How do I get started?"]
|
|
195
|
-
}).
|
|
197
|
+
}).user("Build me a launch dashboard").assistant(
|
|
198
|
+
"Here is the launch on one board: what is running now, the trial signups, the projects and what is waiting on you. The rows that ask a question will ask it here.",
|
|
199
|
+
{
|
|
200
|
+
steps: [
|
|
201
|
+
{ status: "Gathering the launch numbers", point: "Signups, seats and open work all belong on one board." },
|
|
202
|
+
{ status: "Laying out the dashboard", point: "Too much for a card; build it as an artifact." }
|
|
203
|
+
],
|
|
204
|
+
card: artifactCard(DEMO_DASHBOARD_ARTIFACT),
|
|
205
|
+
followups: ["Draft the launch page", "How is the trial going?", "Which plan fits a team of five?"]
|
|
206
|
+
}
|
|
207
|
+
).user("Draft the launch page").assistant(
|
|
208
|
+
"A first draft of the launch page is up beside the chat: the headline, the three things the Team plan adds, and one call to action. The source is behind the Code view.",
|
|
209
|
+
{
|
|
210
|
+
steps: [
|
|
211
|
+
{ status: "Reading the launch plan", point: "The page should lead with the workspace, then the plan." },
|
|
212
|
+
{ status: "Writing the page", point: "One document, its own styles, no scripts." }
|
|
213
|
+
],
|
|
214
|
+
card: artifactCard(DEMO_PAGE_ARTIFACT),
|
|
215
|
+
followups: ["Build me a launch dashboard", "What do the plans include?", "How is the trial going?"]
|
|
216
|
+
}
|
|
217
|
+
).fallback(
|
|
196
218
|
"The launch is on track overall. Three of the four readiness tracks are green, and the one item I would watch is the **legal review**, since it gates the pricing rollout.",
|
|
197
219
|
{
|
|
198
220
|
steps: [
|
|
@@ -203,7 +225,7 @@ const DEMO_SCRIPT = scriptChat().user("How do I get started?").assistant(
|
|
|
203
225
|
followups: ["Which track is behind?", "Who owns the legal review?", "What happens if pricing slips?"]
|
|
204
226
|
}
|
|
205
227
|
).fallback(
|
|
206
|
-
"This chat is a scripted preview of the widget, so free text gets one of a few written replies. The suggestion chips lead through the parts of the story that show off the rich content: a plan, a chart, a diff, a tool call and an
|
|
228
|
+
"This chat is a scripted preview of the widget, so free text gets one of a few written replies. The suggestion chips lead through the parts of the story that show off the rich content: a plan, a chart, a diff, a tool call, an approval and an artifact.",
|
|
207
229
|
{
|
|
208
230
|
steps: [{ status: "Reading the question", point: "Nothing scripted matches this one." }],
|
|
209
231
|
followups: ["How do I get started?", "What do the plans include?", "How is the trial going?"]
|
package/chat/demo.css
CHANGED
|
@@ -44,3 +44,9 @@
|
|
|
44
44
|
.ds-chat-demo__logo--three {
|
|
45
45
|
background-color: var(--color-chart-series-3);
|
|
46
46
|
}
|
|
47
|
+
|
|
48
|
+
/* The dashboard artifact: the welcome board, given the inset a panel's
|
|
49
|
+
live content brings for itself. */
|
|
50
|
+
.ds-chat-demo__artifact-board {
|
|
51
|
+
padding: var(--padding-400);
|
|
52
|
+
}
|
package/chat/demo.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SimTiming } from './sim';
|
|
2
2
|
import { ChatTransport } from './types';
|
|
3
3
|
export { DEMO_RENDERERS, type DemoProductCard, type DemoChartCard, type DemoSourcesCard, type DemoToolCallCard, type DemoDiffCard, type DemoPlanCard, type DemoInterruptCard, type DemoStackCard, } from './demo-cards';
|
|
4
|
+
export { DEMO_ARTIFACT_RENDERERS, DEMO_DASHBOARD_ARTIFACT, DEMO_PAGE_ARTIFACT, } from './demo-artifacts';
|
|
4
5
|
export { DEMO_SCRIPT, DEMO_STARTERS } from './demo-script';
|
|
5
6
|
export { DEMO_MENTIONS, DEMO_MODELS, DEMO_THREAD_GROUPS, DEMO_PROJECTS, DEMO_THREAD_NAMES, DEMO_THREADS, DEMO_WELCOME_OPTIONS, type DemoWelcomeVariant, } from './demo-fixtures';
|
|
6
7
|
export { demoAgentTabs } from './demo-agent';
|
package/chat/demo.js
CHANGED
|
@@ -2,13 +2,17 @@ import { createSimTransport } from "./sim.js";
|
|
|
2
2
|
import { DEMO_SCRIPT } from "./demo-script.js";
|
|
3
3
|
import { DEMO_STARTERS } from "./demo-script.js";
|
|
4
4
|
import { DEMO_RENDERERS } from "./demo-cards.js";
|
|
5
|
+
import { DEMO_ARTIFACT_RENDERERS, DEMO_DASHBOARD_ARTIFACT, DEMO_PAGE_ARTIFACT } from "./demo-artifacts.js";
|
|
5
6
|
import { DEMO_MENTIONS, DEMO_MODELS, DEMO_PROJECTS, DEMO_THREADS, DEMO_THREAD_GROUPS, DEMO_THREAD_NAMES, DEMO_WELCOME_OPTIONS } from "./demo-fixtures.js";
|
|
6
7
|
import { demoAgentTabs } from "./demo-agent.js";
|
|
7
8
|
import { DemoBoard, DemoWelcome } from "./DemoWelcome.js";
|
|
8
9
|
const createDemoTransport = (timing) => createSimTransport(DEMO_SCRIPT, timing);
|
|
9
10
|
export {
|
|
11
|
+
DEMO_ARTIFACT_RENDERERS,
|
|
12
|
+
DEMO_DASHBOARD_ARTIFACT,
|
|
10
13
|
DEMO_MENTIONS,
|
|
11
14
|
DEMO_MODELS,
|
|
15
|
+
DEMO_PAGE_ARTIFACT,
|
|
12
16
|
DEMO_PROJECTS,
|
|
13
17
|
DEMO_RENDERERS,
|
|
14
18
|
DEMO_SCRIPT,
|
package/chat/index.d.ts
CHANGED
|
@@ -19,8 +19,10 @@ export { ChatLauncher, CHAT_PANEL_ID, type ChatLauncherProps } from './ChatLaunc
|
|
|
19
19
|
export { ChatThreads, type ChatThreadsProps } from './ChatThreads';
|
|
20
20
|
export { ChatTabs, type ChatTabsProps } from './ChatTabs';
|
|
21
21
|
export { ChatAgentPane, type ChatAgentPaneProps } from './ChatAgentPane';
|
|
22
|
-
export {
|
|
23
|
-
export {
|
|
22
|
+
export { ChatArtifactPane, type ChatArtifactPaneProps } from './ChatArtifactPane';
|
|
23
|
+
export { ChatArtifactCard, type ChatArtifactCardProps } from './ChatArtifactCard';
|
|
24
|
+
export { useChatWidget, isInternalPath, createFeedbackPoster, type ChatView, type ChatArtifactRenderers, type ChatCardRenderers, type ChatLinkRenderer, type ChatWidgetContextValue, type FeedbackSink, } from './context';
|
|
25
|
+
export { DOCK_QUERY, TAKEOVER_QUERY, THREADS_RAIL_MIN_WIDTH, ASIDE_RAIL_MIN_WIDTH, ARTIFACT_MIN_COLUMN_WIDTH, ASIDE_WITH_THREADS_MIN_WIDTH, useTakeoverViewport, } from './queries';
|
|
24
26
|
export { useChat, DEFAULT_PLACEHOLDER_COPY, type ChatState, type ChatPlaceholderCopy, } from './useChat';
|
|
25
27
|
export { useThreads, THREAD_NAMING_DELAY_MS, type ThreadsOptions, type ThreadsState } from './threads';
|
|
26
28
|
export { createFetchTransport, DEFAULT_FETCH_COPY, FIRST_BYTE_TIMEOUT_MS, MAX_TRANSPORT_TURNS, type FetchTransportOptions, type FetchTransportCopy, } from './transport';
|
|
@@ -30,5 +32,6 @@ export { SUGGESTION_MAX_CHARS, fitsChip } from './suggestions';
|
|
|
30
32
|
export { STARTER_COUNT, pickStarters, namedStarter, hashString, mulberry32, } from './starters';
|
|
31
33
|
export { greetingFor, readGreeting, serverGreeting, subscribeClock, DEFAULT_GREETING_COPY, type GreetingCopy, } from './greeting';
|
|
32
34
|
export { CHAT_PLACEMENTS, chatPlacement, DOCK_MIN_WIDTH, DOCK_MAX_WIDTH, FLOAT_MAX_SCALE, FLOAT_EDGE_GAP, dockWidthFromDrag, isFloatDragStart, clampFloatPosition, type ChatPlacement, type ChatSide, } from './placement';
|
|
33
|
-
export {
|
|
35
|
+
export { artifactCard, cardArtifact } from './artifacts';
|
|
36
|
+
export { ARTIFACT_CARD_KIND, CHAT_EVENT_KINDS, type ChatArtifact, type ChatCard, type ChatEvent, type ChatTransport, type ChatTransportMessage, type ChatTurn, type ChatModelOption, type ChatFeedbackVerdict, type LivePhase, type LiveResponse, type Starter, } from './types';
|
|
34
37
|
export { chatWidget, chatWidgetRegistry, WIDGET_COUNT, type ChatWidgetMeta } from './registry';
|
package/chat/index.js
CHANGED
|
@@ -4,8 +4,10 @@ import { CHAT_PANEL_ID, ChatLauncher } from "./ChatLauncher.js";
|
|
|
4
4
|
import { ChatThreads } from "./ChatThreads.js";
|
|
5
5
|
import { ChatTabs } from "./ChatTabs.js";
|
|
6
6
|
import { ChatAgentPane } from "./ChatAgentPane.js";
|
|
7
|
+
import { ChatArtifactPane } from "./ChatArtifactPane.js";
|
|
8
|
+
import { ChatArtifactCard } from "./ChatArtifactCard.js";
|
|
7
9
|
import { createFeedbackPoster, isInternalPath, useChatWidget } from "./context.js";
|
|
8
|
-
import { ASIDE_RAIL_MIN_WIDTH, DOCK_QUERY, TAKEOVER_QUERY, THREADS_RAIL_MIN_WIDTH, useTakeoverViewport } from "./queries.js";
|
|
10
|
+
import { ARTIFACT_MIN_COLUMN_WIDTH, ASIDE_RAIL_MIN_WIDTH, ASIDE_WITH_THREADS_MIN_WIDTH, DOCK_QUERY, TAKEOVER_QUERY, THREADS_RAIL_MIN_WIDTH, useTakeoverViewport } from "./queries.js";
|
|
9
11
|
import { DEFAULT_PLACEHOLDER_COPY, useChat } from "./useChat.js";
|
|
10
12
|
import { THREAD_NAMING_DELAY_MS, useThreads } from "./threads.js";
|
|
11
13
|
import { DEFAULT_FETCH_COPY, FIRST_BYTE_TIMEOUT_MS, MAX_TRANSPORT_TURNS, createFetchTransport } from "./transport.js";
|
|
@@ -15,14 +17,20 @@ import { SUGGESTION_MAX_CHARS, fitsChip } from "./suggestions.js";
|
|
|
15
17
|
import { STARTER_COUNT, hashString, mulberry32, namedStarter, pickStarters } from "./starters.js";
|
|
16
18
|
import { DEFAULT_GREETING_COPY, greetingFor, readGreeting, serverGreeting, subscribeClock } from "./greeting.js";
|
|
17
19
|
import { CHAT_PLACEMENTS, DOCK_MAX_WIDTH, DOCK_MIN_WIDTH, FLOAT_EDGE_GAP, FLOAT_MAX_SCALE, chatPlacement, clampFloatPosition, dockWidthFromDrag, isFloatDragStart } from "./placement.js";
|
|
18
|
-
import {
|
|
20
|
+
import { artifactCard, cardArtifact } from "./artifacts.js";
|
|
21
|
+
import { ARTIFACT_CARD_KIND, CHAT_EVENT_KINDS } from "./types.js";
|
|
19
22
|
import { WIDGET_COUNT, chatWidget, chatWidgetRegistry } from "./registry.js";
|
|
20
23
|
export {
|
|
24
|
+
ARTIFACT_CARD_KIND,
|
|
25
|
+
ARTIFACT_MIN_COLUMN_WIDTH,
|
|
21
26
|
ASIDE_RAIL_MIN_WIDTH,
|
|
27
|
+
ASIDE_WITH_THREADS_MIN_WIDTH,
|
|
22
28
|
CHAT_EVENT_KINDS,
|
|
23
29
|
CHAT_PANEL_ID,
|
|
24
30
|
CHAT_PLACEMENTS,
|
|
25
31
|
ChatAgentPane,
|
|
32
|
+
ChatArtifactCard,
|
|
33
|
+
ChatArtifactPane,
|
|
26
34
|
ChatLauncher,
|
|
27
35
|
ChatProvider,
|
|
28
36
|
ChatTabs,
|
|
@@ -49,6 +57,8 @@ export {
|
|
|
49
57
|
THREADS_RAIL_MIN_WIDTH,
|
|
50
58
|
THREAD_NAMING_DELAY_MS,
|
|
51
59
|
WIDGET_COUNT,
|
|
60
|
+
artifactCard,
|
|
61
|
+
cardArtifact,
|
|
52
62
|
chatPlacement,
|
|
53
63
|
chatWidget,
|
|
54
64
|
chatWidgetRegistry,
|
package/chat/queries.d.ts
CHANGED
|
@@ -2,7 +2,16 @@ export declare const DOCK_QUERY = "(min-width: 1440px)";
|
|
|
2
2
|
export declare const TAKEOVER_QUERY = "(max-width: 719px)";
|
|
3
3
|
/** The widget's own width thresholds for seating its rails inline. */
|
|
4
4
|
export declare const THREADS_RAIL_MIN_WIDTH = 720;
|
|
5
|
-
|
|
5
|
+
/** The trailing seat's threshold, one number for both of its tenants: at
|
|
6
|
+
this width and up the agent pane and the artifact panel sit beside the
|
|
7
|
+
conversation, below it each rises as a bottom sheet. */
|
|
8
|
+
export declare const ASIDE_RAIL_MIN_WIDTH = 760;
|
|
9
|
+
/** Below this the inline threads rail yields its room to a trailing panel
|
|
10
|
+
seated inline, and goes back behind the header's hamburger. */
|
|
11
|
+
export declare const ASIDE_WITH_THREADS_MIN_WIDTH = 1040;
|
|
12
|
+
/** The floor either column keeps while an artifact is dragged wider or
|
|
13
|
+
narrower: the conversation never loses more than this, nor the panel. */
|
|
14
|
+
export declare const ARTIFACT_MIN_COLUMN_WIDTH = 320;
|
|
6
15
|
/** Whether the viewport is a phone width (TAKEOVER_QUERY), false on the
|
|
7
16
|
server and for the first client render. Shared by the launcher and any
|
|
8
17
|
host stage, which both hand the widget its phone layout. */
|
package/chat/queries.js
CHANGED
|
@@ -3,7 +3,9 @@ import { useSyncExternalStore } from "react";
|
|
|
3
3
|
const DOCK_QUERY = "(min-width: 1440px)";
|
|
4
4
|
const TAKEOVER_QUERY = "(max-width: 719px)";
|
|
5
5
|
const THREADS_RAIL_MIN_WIDTH = 720;
|
|
6
|
-
const ASIDE_RAIL_MIN_WIDTH =
|
|
6
|
+
const ASIDE_RAIL_MIN_WIDTH = 760;
|
|
7
|
+
const ASIDE_WITH_THREADS_MIN_WIDTH = 1040;
|
|
8
|
+
const ARTIFACT_MIN_COLUMN_WIDTH = 320;
|
|
7
9
|
const subscribeTakeover = (onChange) => {
|
|
8
10
|
const media = window.matchMedia(TAKEOVER_QUERY);
|
|
9
11
|
media.addEventListener("change", onChange);
|
|
@@ -14,7 +16,9 @@ function useTakeoverViewport() {
|
|
|
14
16
|
return useSyncExternalStore(subscribeTakeover, readTakeover, () => false);
|
|
15
17
|
}
|
|
16
18
|
export {
|
|
19
|
+
ARTIFACT_MIN_COLUMN_WIDTH,
|
|
17
20
|
ASIDE_RAIL_MIN_WIDTH,
|
|
21
|
+
ASIDE_WITH_THREADS_MIN_WIDTH,
|
|
18
22
|
DOCK_QUERY,
|
|
19
23
|
TAKEOVER_QUERY,
|
|
20
24
|
THREADS_RAIL_MIN_WIDTH,
|
package/chat/registry.json.d.ts
CHANGED
|
@@ -35,6 +35,8 @@ declare const _default: {
|
|
|
35
35
|
"AgentRail",
|
|
36
36
|
"AgentStatus",
|
|
37
37
|
"AiButton",
|
|
38
|
+
"ArtifactCard",
|
|
39
|
+
"ArtifactPanel",
|
|
38
40
|
"AttachmentDropZone",
|
|
39
41
|
"AttachmentGroup",
|
|
40
42
|
"ChatHeader",
|
|
@@ -44,6 +46,7 @@ declare const _default: {
|
|
|
44
46
|
"CodeBlock",
|
|
45
47
|
"Composer",
|
|
46
48
|
"DropdownMenu",
|
|
49
|
+
"EmptyState",
|
|
47
50
|
"Mention",
|
|
48
51
|
"ModelPicker",
|
|
49
52
|
"PixelAvatar",
|
|
@@ -59,7 +62,9 @@ declare const _default: {
|
|
|
59
62
|
"ChatLauncher",
|
|
60
63
|
"ChatThreads",
|
|
61
64
|
"ChatTabs",
|
|
62
|
-
"ChatAgentPane"
|
|
65
|
+
"ChatAgentPane",
|
|
66
|
+
"ChatArtifactPane",
|
|
67
|
+
"ChatArtifactCard"
|
|
63
68
|
]
|
|
64
69
|
}
|
|
65
70
|
]
|
package/chat/registry.json.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const $comment = "The widget registry: the fourth kind of thing the package ships, between the components and the templates. One member by design. `parts` names the library components the widget composes (held to src/components/registry.json by scripts/validate-chat-widget.mjs), `subpaths` the package exports it occupies (held to scripts/package-manifest.mjs), `category` the component category whose section and sidebar group its page joins (held to src/components/registry.json), `peers` the optional peer dependencies each subpath needs (held to package.json). The widget's page under /components, the /docs/chat page, the chat corpus and the MCP tools read this file; never type its facts elsewhere.";
|
|
2
|
-
const widgets = [{ "name": "ChatWidget", "slug": "ai-chat", "label": "AI chat", "category": "ai", "description": "The whole AI chat, assembled: the panel, the streaming replies, the history, the cards a reply can carry and the server handlers, set up with props.", "command": "init chat", "subpaths": { "./chat": "The widget, the provider, the launcher and the engine under them.", "./chat/server": "The streaming chat handler and its follow-ups and feedback siblings, for any web-standard route.", "./chat/upstash": "Rate limits, a daily budget and the exchange log over Upstash Redis, as the handlers' guardrails.", "./chat/demo": "A scripted conversation with rich cards and fixtures, for a preview that needs no key.", "./chat/chat.css": "The widget's stylesheet." }, "peers": { "./chat": ["react-markdown", "remark-gfm"], "./chat/server": ["@anthropic-ai/sdk"], "./chat/upstash": ["@upstash/ratelimit", "@upstash/redis"], "./chat/demo": ["recharts"] }, "parts": ["AgentRail", "AgentStatus", "AiButton", "AttachmentDropZone", "AttachmentGroup", "ChatHeader", "ChatMessage", "ChatThread", "CircularButton", "CodeBlock", "Composer", "DropdownMenu", "Mention", "ModelPicker", "PixelAvatar", "PromptSuggestions", "Prose", "Reasoning", "ThreadPanel", "ThreadTabs"], "documented": ["ChatWidget", "ChatProvider", "ChatLauncher", "ChatThreads", "ChatTabs", "ChatAgentPane"] }];
|
|
2
|
+
const widgets = [{ "name": "ChatWidget", "slug": "ai-chat", "label": "AI chat", "category": "ai", "description": "The whole AI chat, assembled: the panel, the streaming replies, the history, the cards a reply can carry and the server handlers, set up with props.", "command": "init chat", "subpaths": { "./chat": "The widget, the provider, the launcher and the engine under them.", "./chat/server": "The streaming chat handler and its follow-ups and feedback siblings, for any web-standard route.", "./chat/upstash": "Rate limits, a daily budget and the exchange log over Upstash Redis, as the handlers' guardrails.", "./chat/demo": "A scripted conversation with rich cards and fixtures, for a preview that needs no key.", "./chat/chat.css": "The widget's stylesheet." }, "peers": { "./chat": ["react-markdown", "remark-gfm"], "./chat/server": ["@anthropic-ai/sdk"], "./chat/upstash": ["@upstash/ratelimit", "@upstash/redis"], "./chat/demo": ["recharts"] }, "parts": ["AgentRail", "AgentStatus", "AiButton", "ArtifactCard", "ArtifactPanel", "AttachmentDropZone", "AttachmentGroup", "ChatHeader", "ChatMessage", "ChatThread", "CircularButton", "CodeBlock", "Composer", "DropdownMenu", "EmptyState", "Mention", "ModelPicker", "PixelAvatar", "PromptSuggestions", "Prose", "Reasoning", "ThreadPanel", "ThreadTabs"], "documented": ["ChatWidget", "ChatProvider", "ChatLauncher", "ChatThreads", "ChatTabs", "ChatAgentPane", "ChatArtifactPane", "ChatArtifactCard"] }];
|
|
3
3
|
const registry = {
|
|
4
4
|
$comment,
|
|
5
5
|
widgets
|
package/chat/types.d.ts
CHANGED
|
@@ -12,6 +12,42 @@ export interface ChatCard {
|
|
|
12
12
|
/** Whatever the renderer for `kind` expects. Must survive JSON. */
|
|
13
13
|
data: unknown;
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Something the chat made that is worth more room than a turn can give it: a
|
|
17
|
+
* dashboard, a document, a page, a site. It travels as an ordinary card of
|
|
18
|
+
* kind `ARTIFACT_CARD_KIND`, so any transport that can send a card can send
|
|
19
|
+
* one; the thread draws it as a tile, and the tile opens the artifact panel
|
|
20
|
+
* beside the conversation.
|
|
21
|
+
*/
|
|
22
|
+
export interface ChatArtifact {
|
|
23
|
+
/** Stable identity: the same id reopened is the same artifact. */
|
|
24
|
+
id: string;
|
|
25
|
+
/** The artifact's name, on the tile and in the panel's header. */
|
|
26
|
+
title: string;
|
|
27
|
+
/**
|
|
28
|
+
* How to draw it. `html` frames the `html` document, `url` frames the
|
|
29
|
+
* `url`, `code` shows the `code` alone; any other kind is drawn by the
|
|
30
|
+
* provider's `artifactRenderers`. An unknown kind with no renderer falls
|
|
31
|
+
* back to whichever of `html`, `url` and `code` it carries.
|
|
32
|
+
*/
|
|
33
|
+
kind: string;
|
|
34
|
+
/** A quiet line beside the name: what it is, or which version. */
|
|
35
|
+
meta?: string;
|
|
36
|
+
/** A Material Symbol name for the tile and the header. */
|
|
37
|
+
icon?: string;
|
|
38
|
+
/** A complete HTML document, drawn in a sandboxed frame. */
|
|
39
|
+
html?: string;
|
|
40
|
+
/** A page to frame. Only http(s) URLs and same-site paths are framed. */
|
|
41
|
+
url?: string;
|
|
42
|
+
/** The source, offered as the panel's Code view. An `html` artifact offers its document when this is absent. */
|
|
43
|
+
code?: string;
|
|
44
|
+
/** The source's language. */
|
|
45
|
+
language?: string;
|
|
46
|
+
/** Whatever a custom renderer for `kind` expects. Must survive JSON. */
|
|
47
|
+
data?: unknown;
|
|
48
|
+
}
|
|
49
|
+
/** The card kind an artifact travels as. Its `data` is a `ChatArtifact`. */
|
|
50
|
+
export declare const ARTIFACT_CARD_KIND = "artifact";
|
|
15
51
|
export type ChatEvent =
|
|
16
52
|
/** A live status step: the AgentStatus label, with an optional reasoning trace point. */
|
|
17
53
|
{
|
package/chat/types.js
CHANGED
|
@@ -38,11 +38,12 @@
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/* ============================================
|
|
41
|
-
COLLAPSE — the way out
|
|
42
|
-
belongs to the host
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
COLLAPSE — the way out. By default a chevron pinned to the LEADING
|
|
42
|
+
corner: the trailing one usually belongs to the host, since a rail seated
|
|
43
|
+
at a card's edge puts the card's own controls above it, and two crosses a
|
|
44
|
+
few pixels apart read as two ways to dismiss the same thing. A host that
|
|
45
|
+
has weighed that and wants the way out where a panel's close is looked
|
|
46
|
+
for takes the close variant below: a cross in the trailing corner.
|
|
46
47
|
============================================ */
|
|
47
48
|
|
|
48
49
|
.ds-agent-rail__collapse {
|
|
@@ -72,6 +73,14 @@
|
|
|
72
73
|
transition: background-color var(--motion-duration-base) var(--motion-ease-standard);
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
/* The close variant: a cross in the trailing corner, where a panel's own
|
|
77
|
+
way out usually sits. The host that asks for it has decided its controls
|
|
78
|
+
and this one do not collide. */
|
|
79
|
+
.ds-agent-rail__collapse--close {
|
|
80
|
+
left: auto;
|
|
81
|
+
right: var(--padding-300);
|
|
82
|
+
}
|
|
83
|
+
|
|
75
84
|
.ds-agent-rail__collapse .material-symbols-rounded {
|
|
76
85
|
--icon-size: var(--icon-size-600);
|
|
77
86
|
}
|
|
@@ -49,6 +49,8 @@ type AgentRailOwnProps = {
|
|
|
49
49
|
onCollapse?: () => void;
|
|
50
50
|
/** Accessible name for the collapse affordance. */
|
|
51
51
|
collapseLabel?: string;
|
|
52
|
+
/** How the collapse affordance is drawn. `chevron` (the default) pins a chevron to the leading corner, clear of a host's own controls. `close` pins a cross to the trailing corner, for a rail that is its card's only way out or whose host wants the way out where a panel's close usually is. */
|
|
53
|
+
collapseVariant?: 'chevron' | 'close';
|
|
52
54
|
/** Rendered under the pane, outside its scroll — a sign-out row, a quiet hint. */
|
|
53
55
|
footer?: React.ReactNode;
|
|
54
56
|
/** Additional CSS classes */
|
|
@@ -15,6 +15,7 @@ const AgentRail = React.forwardRef(
|
|
|
15
15
|
ariaLabel,
|
|
16
16
|
onCollapse,
|
|
17
17
|
collapseLabel = "Collapse the agent panel",
|
|
18
|
+
collapseVariant = "chevron",
|
|
18
19
|
footer,
|
|
19
20
|
className = "",
|
|
20
21
|
...rest
|
|
@@ -26,10 +27,10 @@ const AgentRail = React.forwardRef(
|
|
|
26
27
|
"button",
|
|
27
28
|
{
|
|
28
29
|
type: "button",
|
|
29
|
-
className: `${baseClass}__collapse`,
|
|
30
|
+
className: `${baseClass}__collapse${collapseVariant === "close" ? ` ${baseClass}__collapse--close` : ""}`,
|
|
30
31
|
"aria-label": collapseLabel,
|
|
31
32
|
onClick: onCollapse,
|
|
32
|
-
children: /* @__PURE__ */ jsx("span", { className: "material-symbols-rounded", "aria-hidden": "true", children: "chevron_right" })
|
|
33
|
+
children: /* @__PURE__ */ jsx("span", { className: "material-symbols-rounded", "aria-hidden": "true", children: collapseVariant === "close" ? "close" : "chevron_right" })
|
|
33
34
|
}
|
|
34
35
|
),
|
|
35
36
|
profile && /* @__PURE__ */ jsxs("header", { className: `${baseClass}__header`, children: [
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
ARTIFACT CARD COMPONENT
|
|
3
|
+
An artifact's place in a conversation: a mark, the name over its kind,
|
|
4
|
+
and a cue for what a press does. One row, the width of a card, and a
|
|
5
|
+
toggle button: pressed while its artifact is on stage in the panel.
|
|
6
|
+
============================================ */
|
|
7
|
+
|
|
8
|
+
/* Base */
|
|
9
|
+
|
|
10
|
+
.ds-artifact-card {
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
gap: var(--gap-300);
|
|
15
|
+
width: 100%;
|
|
16
|
+
max-width: 360px;
|
|
17
|
+
padding: var(--padding-300);
|
|
18
|
+
border: var(--border-025) solid var(--color-bg-container-border);
|
|
19
|
+
border-radius: var(--radius-600);
|
|
20
|
+
background-color: var(--color-bg-container-primary);
|
|
21
|
+
cursor: pointer;
|
|
22
|
+
text-align: left;
|
|
23
|
+
transition:
|
|
24
|
+
background-color var(--motion-duration-fast) var(--motion-ease-standard),
|
|
25
|
+
border-color var(--motion-duration-fast) var(--motion-ease-standard);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.ds-artifact-card:hover {
|
|
29
|
+
background-color: var(--color-action-passive-bg-hover);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.ds-artifact-card:focus-visible {
|
|
33
|
+
outline: 2px solid var(--color-action-primary-bg);
|
|
34
|
+
outline-offset: 2px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* States */
|
|
38
|
+
|
|
39
|
+
/* On stage, the tile holds the "on stage" fill ThreadPanel's active row and
|
|
40
|
+
ThreadTabs' active tab wear. */
|
|
41
|
+
.ds-artifact-card--open {
|
|
42
|
+
background-color: var(--color-bg-container-tertiary);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* The mark steps to the card's resting fill while the card holds the
|
|
46
|
+
on-stage one, so it stays a tile on the card instead of dissolving into
|
|
47
|
+
a fill of nearly its own value. */
|
|
48
|
+
.ds-artifact-card--open .ds-artifact-card__mark {
|
|
49
|
+
background-color: var(--color-bg-container-primary);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.ds-artifact-card__mark {
|
|
53
|
+
--icon-size: var(--icon-size-600);
|
|
54
|
+
display: flex;
|
|
55
|
+
flex: none;
|
|
56
|
+
align-items: center;
|
|
57
|
+
justify-content: center;
|
|
58
|
+
width: 40px;
|
|
59
|
+
height: 40px;
|
|
60
|
+
/* Concentric with the card: its radius less the padding between them,
|
|
61
|
+
computed from the live tokens so the corner holds under any theme's
|
|
62
|
+
radius scale, and squares off with a squared theme. */
|
|
63
|
+
border-radius: max(0px, calc(var(--radius-600) - var(--padding-300)));
|
|
64
|
+
background-color: var(--color-bg-container-secondary);
|
|
65
|
+
color: var(--color-icon-primary);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.ds-artifact-card__text {
|
|
69
|
+
display: flex;
|
|
70
|
+
flex: 1;
|
|
71
|
+
flex-direction: column;
|
|
72
|
+
min-width: 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.ds-artifact-card__title {
|
|
76
|
+
overflow: hidden;
|
|
77
|
+
text-overflow: ellipsis;
|
|
78
|
+
white-space: nowrap;
|
|
79
|
+
font-family: var(--font-paragraph-sm-emphasis-family);
|
|
80
|
+
font-size: var(--font-paragraph-sm-emphasis-size);
|
|
81
|
+
font-weight: var(--font-paragraph-sm-emphasis-weight);
|
|
82
|
+
line-height: var(--font-paragraph-sm-emphasis-line-height);
|
|
83
|
+
color: var(--color-text-primary);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.ds-artifact-card__meta {
|
|
87
|
+
overflow: hidden;
|
|
88
|
+
text-overflow: ellipsis;
|
|
89
|
+
white-space: nowrap;
|
|
90
|
+
font-family: var(--font-caption-family);
|
|
91
|
+
font-size: var(--font-caption-size);
|
|
92
|
+
font-weight: var(--font-caption-weight);
|
|
93
|
+
line-height: var(--font-caption-line-height);
|
|
94
|
+
color: var(--color-text-tertiary);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.ds-artifact-card__cue {
|
|
98
|
+
--icon-size: var(--icon-size-500);
|
|
99
|
+
flex: none;
|
|
100
|
+
color: var(--color-icon-secondary);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.ds-artifact-card:disabled {
|
|
104
|
+
opacity: 0.4;
|
|
105
|
+
cursor: not-allowed;
|
|
106
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
/** Props owned by ArtifactCard itself — everything else falls through to the root `<button>`. */
|
|
3
|
+
type ArtifactCardOwnProps = {
|
|
4
|
+
/** The artifact's name. Collides with the native `title` tooltip attribute on purpose: the card shows its name, it does not hide it in a tooltip. */
|
|
5
|
+
title: React.ReactNode;
|
|
6
|
+
/** A quiet line under the name: what kind of thing this is, or which version. */
|
|
7
|
+
meta?: React.ReactNode;
|
|
8
|
+
/** The leading mark — Material Symbol name (string) or custom element (ReactNode). */
|
|
9
|
+
icon?: string | React.ReactNode;
|
|
10
|
+
/** Whether this card's artifact is on stage in its panel. The card holds the on-stage fill, announces itself pressed, and its trailing cue turns from open to close. */
|
|
11
|
+
open?: boolean;
|
|
12
|
+
/** Additional CSS classes */
|
|
13
|
+
className?: string;
|
|
14
|
+
};
|
|
15
|
+
export interface ArtifactCardProps extends ArtifactCardOwnProps, Omit<React.ComponentPropsWithoutRef<'button'>, keyof ArtifactCardOwnProps | 'type'> {
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* ArtifactCard is an artifact's place in a conversation: one row naming
|
|
19
|
+
* something the chat made, which opens it in an ArtifactPanel. It is a
|
|
20
|
+
* toggle button, so `open` is the whole state and `onClick` the whole
|
|
21
|
+
* behaviour; the host decides what opening means.
|
|
22
|
+
*
|
|
23
|
+
* Hook-free and stateless, so it renders from a Server Component until it
|
|
24
|
+
* is given a handler.
|
|
25
|
+
*/
|
|
26
|
+
export declare const ArtifactCard: React.ForwardRefExoticComponent<ArtifactCardProps & React.RefAttributes<HTMLButtonElement>>;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import "./ArtifactCard.css";
|
|
4
|
+
import "../../fonts/material-symbols.css";
|
|
5
|
+
const baseClass = "ds-artifact-card";
|
|
6
|
+
const ArtifactCard = React.forwardRef(
|
|
7
|
+
({ title, meta, icon = "draft", open = false, className = "", ...rest }, ref) => /* @__PURE__ */ jsxs(
|
|
8
|
+
"button",
|
|
9
|
+
{
|
|
10
|
+
ref,
|
|
11
|
+
...rest,
|
|
12
|
+
type: "button",
|
|
13
|
+
className: [baseClass, open && `${baseClass}--open`, className].filter(Boolean).join(" "),
|
|
14
|
+
"aria-pressed": open,
|
|
15
|
+
children: [
|
|
16
|
+
typeof icon === "string" ? /* @__PURE__ */ jsx("span", { className: `${baseClass}__mark material-symbols-rounded`, "aria-hidden": "true", children: icon }) : /* @__PURE__ */ jsx("span", { className: `${baseClass}__mark`, children: icon }),
|
|
17
|
+
/* @__PURE__ */ jsxs("span", { className: `${baseClass}__text`, children: [
|
|
18
|
+
/* @__PURE__ */ jsx("span", { className: `${baseClass}__title`, children: title }),
|
|
19
|
+
meta && /* @__PURE__ */ jsx("span", { className: `${baseClass}__meta`, children: meta })
|
|
20
|
+
] }),
|
|
21
|
+
/* @__PURE__ */ jsx("span", { className: `${baseClass}__cue material-symbols-rounded`, "aria-hidden": "true", children: open ? "close" : "open_in_new_down" })
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
)
|
|
25
|
+
);
|
|
26
|
+
ArtifactCard.displayName = "ArtifactCard";
|
|
27
|
+
export {
|
|
28
|
+
ArtifactCard
|
|
29
|
+
};
|