rift-ds 1.8.0 → 1.10.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/bin/templates/chat/app/api/chat/route.ts +3 -0
- package/chat/AssistantTurn.js +5 -2
- 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 +213 -25
- package/chat/ChatWidget.d.ts +12 -1
- package/chat/ChatWidget.js +302 -75
- package/chat/DemoWelcome.js +17 -5
- package/chat/FollowupSuggestions.d.ts +7 -4
- package/chat/FollowupSuggestions.js +5 -3
- package/chat/TurnContent.d.ts +3 -1
- package/chat/TurnContent.js +9 -2
- package/chat/TurnQuestion.d.ts +19 -0
- package/chat/TurnQuestion.js +38 -0
- 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 +277 -49
- package/chat/demo.css +6 -0
- package/chat/demo.d.ts +1 -0
- package/chat/demo.js +4 -0
- package/chat/followups.d.ts +2 -2
- package/chat/followups.js +1 -0
- package/chat/index.d.ts +7 -3
- package/chat/index.js +23 -2
- package/chat/queries.d.ts +12 -3
- package/chat/queries.js +8 -3
- package/chat/questions.d.ts +97 -0
- package/chat/questions.js +140 -0
- package/chat/registry.json.d.ts +9 -3
- package/chat/registry.json.js +1 -1
- package/chat/server.d.ts +19 -0
- package/chat/server.js +45 -1
- package/chat/sim.d.ts +12 -1
- package/chat/sim.js +19 -3
- package/chat/types.d.ts +94 -1
- package/chat/types.js +3 -0
- package/chat/useChat.d.ts +16 -1
- package/chat/useChat.js +39 -2
- package/components/AgentRail/AgentRail.css +14 -5
- package/components/AgentRail/AgentRail.d.ts +2 -0
- package/components/AgentRail/AgentRail.js +3 -2
- package/components/AlertDialog/AlertDialog.css +1 -1
- package/components/AnchorNav/AnchorNav.css +1 -1
- package/components/AppSidebar/AppSidebar.css +5 -5
- package/components/AppSidebar/AppSidebar.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/Attachment/Attachment.css +3 -1
- package/components/BottomNav/BottomNav.css +1 -1
- package/components/Chart/Chart.css +1 -0
- package/components/ChatThread/ChatThread.css +21 -0
- package/components/ChatThread/ChatThread.d.ts +12 -1
- package/components/ChatThread/ChatThread.js +40 -1
- package/components/ColorPicker/ColorPicker.css +1 -1
- package/components/Combobox/Combobox.css +1 -1
- package/components/CommandPalette/CommandPalette.css +1 -1
- package/components/Composer/Composer.css +1 -1
- package/components/ContextMenu/ContextMenu.css +1 -1
- package/components/Dialog/Dialog.css +1 -1
- package/components/Drawer/Drawer.css +1 -1
- package/components/Dropdown/Dropdown.css +1 -1
- package/components/DropdownMenu/DropdownMenu.css +1 -1
- package/components/FilterBar/FilterBar.css +1 -1
- package/components/HoverCard/HoverCard.css +1 -1
- package/components/Lightbox/Lightbox.css +1 -1
- package/components/ModelPicker/ModelPicker.css +1 -1
- package/components/Popover/Popover.css +1 -1
- package/components/PromptSuggestions/PromptSuggestions.css +67 -0
- package/components/PromptSuggestions/PromptSuggestions.d.ts +8 -0
- package/components/PromptSuggestions/PromptSuggestions.js +34 -26
- package/components/QuestionCard/QuestionCard.css +358 -0
- package/components/QuestionCard/QuestionCard.d.ts +136 -0
- package/components/QuestionCard/QuestionCard.js +371 -0
- package/components/RichDropdown/RichDropdown.css +1 -1
- package/components/SourceChip/SourceChip.css +1 -1
- package/components/ThreadPanel/ThreadPanel.css +87 -33
- package/components/ThreadPanel/ThreadPanel.js +21 -10
- package/components/TimePicker/TimePicker.css +1 -1
- package/components/Toast/Toast.css +1 -1
- package/components/Tooltip/Tooltip.css +1 -1
- package/components/TopAppBar/TopAppBar.css +1 -1
- package/components/registry.json +24 -0
- package/components/registry.json.d.ts +24 -0
- package/components/registry.json.js +1 -1
- package/index.d.ts +3 -0
- package/index.js +7 -0
- package/package.json +5 -1
- package/tokens/breakpoints.d.ts +60 -0
- package/tokens/breakpoints.js +92 -0
- package/tokens/registry.d.ts +1 -1
- package/tokens/registry.json +14 -0
- package/tokens/registry.json.d.ts +14 -0
- package/tokens/registry.json.js +1 -1
- package/tokens/tokens-light.css +22 -0
package/chat/ChatWidget.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
3
|
+
import { useRef, useEffect, useSyncExternalStore, useState, useId, useMemo } from "react";
|
|
4
4
|
import { AttachmentDropZone } from "../components/Attachment/AttachmentDropZone.js";
|
|
5
5
|
import { AttachmentGroup } from "../components/Attachment/AttachmentGroup.js";
|
|
6
6
|
import { useAttachments } from "../components/Attachment/useAttachments.js";
|
|
@@ -16,15 +16,19 @@ import { ModelPicker } from "../components/ModelPicker/ModelPicker.js";
|
|
|
16
16
|
import { PromptSuggestions } from "../components/PromptSuggestions/PromptSuggestions.js";
|
|
17
17
|
import { MOTION_SUGGESTIONS_THINK_MS, MOTION_SCROLL_SETTLE_MS } from "../tokens/motion.js";
|
|
18
18
|
import { AssistantTurn } from "./AssistantTurn.js";
|
|
19
|
+
import { cardArtifact } from "./artifacts.js";
|
|
19
20
|
import { ChatAgentPane } from "./ChatAgentPane.js";
|
|
21
|
+
import { ChatArtifactPane } from "./ChatArtifactPane.js";
|
|
20
22
|
import { ChatTabs } from "./ChatTabs.js";
|
|
21
23
|
import { ChatThreads } from "./ChatThreads.js";
|
|
22
|
-
import { useChatWidget } from "./context.js";
|
|
24
|
+
import { useChatWidget, ArtifactSeatContext } from "./context.js";
|
|
23
25
|
import { subscribeClock, readGreeting, serverGreeting } from "./greeting.js";
|
|
24
26
|
import { CHAT_PLACEMENTS, chatPlacement } from "./placement.js";
|
|
25
|
-
import { THREADS_RAIL_MIN_WIDTH, ASIDE_RAIL_MIN_WIDTH } from "./queries.js";
|
|
27
|
+
import { ASIDE_WITH_THREADS_MIN_WIDTH, ARTIFACT_MIN_COLUMN_WIDTH, THREADS_RAIL_MIN_WIDTH, ASIDE_RAIL_MIN_WIDTH } from "./queries.js";
|
|
26
28
|
import { fitsChip } from "./suggestions.js";
|
|
27
29
|
import { TurnCard } from "./TurnContent.js";
|
|
30
|
+
import { isBlockingQuestion } from "./questions.js";
|
|
31
|
+
import { ReplyBody } from "./TurnQuestion.js";
|
|
28
32
|
import "./ChatWidget.css";
|
|
29
33
|
const DEFAULT_MAX_ATTACHMENTS = 10;
|
|
30
34
|
const DEFAULT_MAX_ATTACHMENT_BYTES = 50 * 1024 * 1024;
|
|
@@ -38,7 +42,9 @@ function ChatWidget({
|
|
|
38
42
|
compact = false,
|
|
39
43
|
phone = false,
|
|
40
44
|
title = "Assistant",
|
|
45
|
+
flow = "top-down",
|
|
41
46
|
placeholder = "Ask anything",
|
|
47
|
+
questionsPlaceholder = "Answer the questions above, or skip them",
|
|
42
48
|
showStarters = true,
|
|
43
49
|
logo = null,
|
|
44
50
|
tagline = "How can I help you today?",
|
|
@@ -57,6 +63,7 @@ function ChatWidget({
|
|
|
57
63
|
tabs,
|
|
58
64
|
aside,
|
|
59
65
|
agent,
|
|
66
|
+
artifacts = false,
|
|
60
67
|
contextLabel,
|
|
61
68
|
widgets,
|
|
62
69
|
widgetsLayout = "fit",
|
|
@@ -83,8 +90,19 @@ function ChatWidget({
|
|
|
83
90
|
setDraft,
|
|
84
91
|
starterSeed,
|
|
85
92
|
starterRevealKey,
|
|
86
|
-
setStarterRevealKey
|
|
93
|
+
setStarterRevealKey,
|
|
94
|
+
artifact,
|
|
95
|
+
openArtifact,
|
|
96
|
+
closeArtifact
|
|
87
97
|
} = useChatWidget();
|
|
98
|
+
const questionsOpen = isBlockingQuestion(turns[turns.length - 1], live !== null);
|
|
99
|
+
const questionsWereOpen = useRef(questionsOpen);
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
if (questionsWereOpen.current && !questionsOpen) {
|
|
102
|
+
composerRef.current?.focus({ preventScroll: true });
|
|
103
|
+
}
|
|
104
|
+
questionsWereOpen.current = questionsOpen;
|
|
105
|
+
}, [questionsOpen]);
|
|
88
106
|
const lockedValues = modelPolicy?.locked ?? [];
|
|
89
107
|
const pick = chosenModel && !lockedValues.includes(chosenModel) ? chosenModel : null;
|
|
90
108
|
const pickerValue = pick ?? modelPolicy?.default ?? defaultModel ?? models?.[0]?.value;
|
|
@@ -132,17 +150,21 @@ function ChatWidget({
|
|
|
132
150
|
{
|
|
133
151
|
profile: withAgentMark(agent.profile ?? { name: title, status: "Connected" }, title),
|
|
134
152
|
tabs: agent.tabs,
|
|
135
|
-
overlay: ctx.overlay
|
|
153
|
+
overlay: ctx.overlay,
|
|
154
|
+
onClose: ctx.close
|
|
136
155
|
}
|
|
137
156
|
) : void 0);
|
|
138
157
|
const tabsNode = tabs === true ? /* @__PURE__ */ jsx(ChatTabs, {}) : tabs || null;
|
|
139
|
-
const hasRails = Boolean(renderThreads || renderAside);
|
|
158
|
+
const hasRails = Boolean(renderThreads || renderAside || artifacts);
|
|
140
159
|
const [threadsOpen, setThreadsOpen] = useState(false);
|
|
141
160
|
const [asideOpen, setAsideOpen] = useState(Boolean(agent?.defaultOpen));
|
|
142
161
|
const railsHostRef = useRef(null);
|
|
143
162
|
const [threadsWide, setThreadsWide] = useState(false);
|
|
144
163
|
const [asideWide, setAsideWide] = useState(false);
|
|
145
|
-
const
|
|
164
|
+
const [asideRoomy, setAsideRoomy] = useState(false);
|
|
165
|
+
const [artifactEmptyOpen, setArtifactEmptyOpen] = useState(false);
|
|
166
|
+
const [artifactExpandedWanted, setArtifactExpanded] = useState(false);
|
|
167
|
+
const wasAsideWideRef = useRef(true);
|
|
146
168
|
useEffect(() => {
|
|
147
169
|
if (!hasRails) return;
|
|
148
170
|
const host = railsHostRef.current;
|
|
@@ -153,20 +175,119 @@ function ChatWidget({
|
|
|
153
175
|
const asideRoom = width >= ASIDE_RAIL_MIN_WIDTH;
|
|
154
176
|
setThreadsWide(wide);
|
|
155
177
|
setAsideWide(asideRoom);
|
|
178
|
+
setAsideRoomy(width >= ASIDE_WITH_THREADS_MIN_WIDTH);
|
|
156
179
|
if (wide) setThreadsOpen(false);
|
|
157
|
-
if (wasAsideWideRef.current && !asideRoom)
|
|
180
|
+
if (wasAsideWideRef.current && !asideRoom) {
|
|
181
|
+
setAsideOpen(false);
|
|
182
|
+
setArtifactEmptyOpen(false);
|
|
183
|
+
setArtifactExpanded(false);
|
|
184
|
+
closeArtifact();
|
|
185
|
+
}
|
|
158
186
|
wasAsideWideRef.current = asideRoom;
|
|
159
187
|
};
|
|
160
188
|
measure();
|
|
161
189
|
const observer = new ResizeObserver(measure);
|
|
162
190
|
observer.observe(host);
|
|
163
191
|
return () => observer.disconnect();
|
|
164
|
-
}, [hasRails]);
|
|
192
|
+
}, [hasRails, closeArtifact]);
|
|
165
193
|
const closeThreads = () => setThreadsOpen(false);
|
|
194
|
+
const artifactId = useId();
|
|
195
|
+
const artifactShown = artifacts && (artifact !== null || artifactEmptyOpen);
|
|
196
|
+
const [heldArtifact, setHeldArtifact] = useState(artifact);
|
|
197
|
+
if (artifactShown && artifact !== heldArtifact) setHeldArtifact(artifact);
|
|
198
|
+
const artifactInline = artifactShown && asideWide;
|
|
199
|
+
const artifactExpanded = artifactInline && artifactExpandedWanted;
|
|
200
|
+
const closeArtifactPanel = () => {
|
|
201
|
+
setArtifactExpanded(false);
|
|
202
|
+
setArtifactEmptyOpen(false);
|
|
203
|
+
closeArtifact();
|
|
204
|
+
};
|
|
205
|
+
const [artifactWidth, setArtifactWidth] = useState(null);
|
|
206
|
+
const [artifactResizing, setArtifactResizing] = useState(false);
|
|
207
|
+
const artifactRailRef = useRef(null);
|
|
208
|
+
const artifactDragRef = useRef(null);
|
|
209
|
+
const beginArtifactResize = (e) => {
|
|
210
|
+
if (e.pointerType === "mouse" && e.button !== 0 || artifactDragRef.current) return;
|
|
211
|
+
const rail = artifactRailRef.current;
|
|
212
|
+
const column = railsHostRef.current?.querySelector(":scope > .ds-chat-widget");
|
|
213
|
+
if (!rail || !column) return;
|
|
214
|
+
const width = rail.getBoundingClientRect().width;
|
|
215
|
+
const spare = column.getBoundingClientRect().width - ARTIFACT_MIN_COLUMN_WIDTH;
|
|
216
|
+
artifactDragRef.current = { startX: e.clientX, width, max: width + Math.max(0, spare), next: width };
|
|
217
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
218
|
+
setArtifactResizing(true);
|
|
219
|
+
};
|
|
220
|
+
const moveArtifactResize = (e) => {
|
|
221
|
+
const drag = artifactDragRef.current;
|
|
222
|
+
if (!drag) return;
|
|
223
|
+
const grown = drag.width - (e.clientX - drag.startX);
|
|
224
|
+
drag.next = Math.round(Math.min(drag.max, Math.max(ARTIFACT_MIN_COLUMN_WIDTH, grown)));
|
|
225
|
+
railsHostRef.current?.style.setProperty("--ds-chat-artifact-width", `${drag.next}px`);
|
|
226
|
+
};
|
|
227
|
+
const endArtifactResize = () => {
|
|
228
|
+
const drag = artifactDragRef.current;
|
|
229
|
+
if (!drag) return;
|
|
230
|
+
artifactDragRef.current = null;
|
|
231
|
+
setArtifactWidth(drag.next);
|
|
232
|
+
setArtifactResizing(false);
|
|
233
|
+
};
|
|
234
|
+
const latestArtifact = useMemo(() => {
|
|
235
|
+
for (let index = turns.length - 1; index >= 0; index -= 1) {
|
|
236
|
+
const found = cardArtifact(turns[index].card);
|
|
237
|
+
if (found) return found;
|
|
238
|
+
}
|
|
239
|
+
return null;
|
|
240
|
+
}, [turns]);
|
|
241
|
+
const seenArtifactTurns = useRef(null);
|
|
242
|
+
const asideWideRef = useRef(asideWide);
|
|
243
|
+
useEffect(() => {
|
|
244
|
+
asideWideRef.current = asideWide;
|
|
245
|
+
}, [asideWide]);
|
|
246
|
+
useEffect(() => {
|
|
247
|
+
if (!artifacts) return;
|
|
248
|
+
const carrying = turns.filter((turn) => cardArtifact(turn.card));
|
|
249
|
+
if (seenArtifactTurns.current === null) {
|
|
250
|
+
seenArtifactTurns.current = new Set(carrying.map((turn) => turn.id));
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
for (const turn of carrying) {
|
|
254
|
+
if (seenArtifactTurns.current.has(turn.id)) continue;
|
|
255
|
+
seenArtifactTurns.current.add(turn.id);
|
|
256
|
+
const arrived = cardArtifact(turn.card);
|
|
257
|
+
if (arrived && asideWideRef.current) openArtifact(arrived);
|
|
258
|
+
}
|
|
259
|
+
}, [artifacts, turns, openArtifact]);
|
|
260
|
+
const trailingUp = artifactInline || Boolean(renderAside) && asideOpen && asideWide;
|
|
261
|
+
const threadsInline = threadsWide && !(trailingUp && (!asideRoomy || artifactExpanded));
|
|
262
|
+
const asideRailRef = useRef(null);
|
|
263
|
+
const asideMounted = Boolean(renderAside) && asideWide;
|
|
264
|
+
useEffect(() => {
|
|
265
|
+
if (!asideMounted) return;
|
|
266
|
+
const rail = asideRailRef.current;
|
|
267
|
+
const card = rail?.firstElementChild;
|
|
268
|
+
const pane = card?.firstElementChild;
|
|
269
|
+
if (!rail || !card || !pane) return;
|
|
270
|
+
const measure = () => {
|
|
271
|
+
const width = pane.offsetWidth + (card.offsetWidth - card.clientWidth);
|
|
272
|
+
if (pane.offsetWidth > 0) rail.style.setProperty("--ds-chat-aside-width", `${width}px`);
|
|
273
|
+
else rail.style.removeProperty("--ds-chat-aside-width");
|
|
274
|
+
};
|
|
275
|
+
measure();
|
|
276
|
+
const observer = new ResizeObserver(measure);
|
|
277
|
+
observer.observe(pane);
|
|
278
|
+
return () => observer.disconnect();
|
|
279
|
+
}, [asideMounted]);
|
|
166
280
|
const asideId = useId();
|
|
167
|
-
const asideShown = Boolean(renderAside) && asideOpen;
|
|
281
|
+
const asideShown = Boolean(renderAside) && asideOpen && !artifactShown;
|
|
168
282
|
const closeAside = () => setAsideOpen(false);
|
|
169
|
-
const toggleAside = () =>
|
|
283
|
+
const toggleAside = () => {
|
|
284
|
+
if (artifactShown) {
|
|
285
|
+
closeArtifactPanel();
|
|
286
|
+
setAsideOpen(true);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
setAsideOpen((current) => !current);
|
|
290
|
+
};
|
|
170
291
|
const startersNode = isEmpty && showStarters && starters.length > 0 && /* @__PURE__ */ jsx("div", { className: "ds-chat-widget__starters", children: /* @__PURE__ */ jsx(
|
|
171
292
|
PromptSuggestions,
|
|
172
293
|
{
|
|
@@ -182,8 +303,12 @@ function ChatWidget({
|
|
|
182
303
|
}
|
|
183
304
|
) });
|
|
184
305
|
const widgetsNode = isEmpty && widgets && /* @__PURE__ */ jsx("div", { className: "ds-chat-widget__widgets", children: widgets });
|
|
306
|
+
const trailingInline = asideShown && asideWide || artifactInline;
|
|
307
|
+
const actionsFloat = (Boolean(renderAside) || artifacts) && asideWide;
|
|
308
|
+
const showNewChat = !(renderThreads && threadsWide);
|
|
309
|
+
const actionCount = Number(showNewChat) + Number(Boolean(renderAside)) + Number(artifacts) + Number(placementEnabled) + Number(fullscreenEnabled) + Number(closeEnabled);
|
|
185
310
|
const headerActions = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
186
|
-
|
|
311
|
+
showNewChat && /* @__PURE__ */ jsx(
|
|
187
312
|
CircularButton,
|
|
188
313
|
{
|
|
189
314
|
icon: "edit_square",
|
|
@@ -210,6 +335,23 @@ function ChatWidget({
|
|
|
210
335
|
onClick: toggleAside
|
|
211
336
|
}
|
|
212
337
|
),
|
|
338
|
+
artifacts && /* @__PURE__ */ jsx(
|
|
339
|
+
CircularButton,
|
|
340
|
+
{
|
|
341
|
+
icon: artifactShown ? "right_panel_close" : "right_panel_open",
|
|
342
|
+
variant: "tertiary",
|
|
343
|
+
className: artifactShown ? "ds-chat-widget__agent-toggle--on" : void 0,
|
|
344
|
+
ariaLabel: artifactShown ? "Hide the artifact panel" : "Show the artifact panel",
|
|
345
|
+
tooltipPosition: "bottom",
|
|
346
|
+
"aria-expanded": artifactShown,
|
|
347
|
+
"aria-controls": artifactShown ? artifactId : void 0,
|
|
348
|
+
onClick: () => {
|
|
349
|
+
if (artifactShown) closeArtifactPanel();
|
|
350
|
+
else if (latestArtifact) openArtifact(latestArtifact);
|
|
351
|
+
else setArtifactEmptyOpen(true);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
),
|
|
213
355
|
placementEnabled && /* @__PURE__ */ jsx(
|
|
214
356
|
"span",
|
|
215
357
|
{
|
|
@@ -292,11 +434,12 @@ function ChatWidget({
|
|
|
292
434
|
"data-widgets-wide": widgetsNode && widgetsWidth === "wide" ? "" : void 0,
|
|
293
435
|
"data-scrolling": widgetsNode && widgetsLayout === "scroll" && rootScrolling || void 0,
|
|
294
436
|
onScroll: handleRootScroll,
|
|
295
|
-
"data-lifted-actions":
|
|
437
|
+
"data-lifted-actions": trailingInline || void 0,
|
|
296
438
|
onKeyDown: (e) => {
|
|
297
|
-
if (e.key === "Escape" && (threadsOpen || asideShown && !asideWide)) {
|
|
439
|
+
if (e.key === "Escape" && (threadsOpen || asideShown && !asideWide || artifactShown && !asideWide)) {
|
|
298
440
|
e.stopPropagation();
|
|
299
441
|
if (threadsOpen) closeThreads();
|
|
442
|
+
else if (artifactShown) closeArtifactPanel();
|
|
300
443
|
else closeAside();
|
|
301
444
|
return;
|
|
302
445
|
}
|
|
@@ -318,7 +461,7 @@ function ChatWidget({
|
|
|
318
461
|
ChatHeader,
|
|
319
462
|
{
|
|
320
463
|
title: /* @__PURE__ */ jsxs("span", { className: "ds-chat-widget__brand", children: [
|
|
321
|
-
renderThreads && !
|
|
464
|
+
renderThreads && !threadsInline && /* @__PURE__ */ jsx(
|
|
322
465
|
CircularButton,
|
|
323
466
|
{
|
|
324
467
|
icon: "menu",
|
|
@@ -334,13 +477,20 @@ function ChatWidget({
|
|
|
334
477
|
actions: (
|
|
335
478
|
/* With the agent rail seated inline the cluster moves to the
|
|
336
479
|
widget's own right edge (see headerActions). */
|
|
337
|
-
|
|
480
|
+
!actionsFloat ? headerActions : trailingInline ? void 0 : /* @__PURE__ */ jsx(
|
|
481
|
+
"span",
|
|
482
|
+
{
|
|
483
|
+
className: "ds-chat-widget__actions-spacer",
|
|
484
|
+
style: { "--ds-chat-action-count": actionCount },
|
|
485
|
+
"aria-hidden": "true"
|
|
486
|
+
}
|
|
487
|
+
)
|
|
338
488
|
)
|
|
339
489
|
}
|
|
340
490
|
),
|
|
341
491
|
tabsNode && /* @__PURE__ */ jsx("div", { className: "ds-chat-widget__tabs-row", children: tabsNode }),
|
|
342
492
|
/* @__PURE__ */ jsxs("div", { className: "ds-chat-widget__body", children: [
|
|
343
|
-
/* @__PURE__ */ jsx(ChatThread, { className: "ds-chat-widget__thread", children: [
|
|
493
|
+
/* @__PURE__ */ jsx(ArtifactSeatContext.Provider, { value: artifacts, children: /* @__PURE__ */ jsx(ChatThread, { className: "ds-chat-widget__thread", flow, children: [
|
|
344
494
|
...turns.map(
|
|
345
495
|
(turn) => turn.bare ? (
|
|
346
496
|
/* Conversation furniture (a day marker): the injected
|
|
@@ -357,14 +507,14 @@ function ChatWidget({
|
|
|
357
507
|
turn.content,
|
|
358
508
|
turn.card && /* @__PURE__ */ jsx(TurnCard, { card: turn.card })
|
|
359
509
|
] }),
|
|
360
|
-
children: mentions && turn.text ? /* @__PURE__ */ jsx(MentionText, { text: turn.text, sources: mentions }) : turn.text
|
|
510
|
+
children: turn.reply ? /* @__PURE__ */ jsx(ReplyBody, { reply: turn.reply, text: turn.text }) : mentions && turn.text ? /* @__PURE__ */ jsx(MentionText, { text: turn.text, sources: mentions }) : turn.text
|
|
361
511
|
},
|
|
362
512
|
turn.id
|
|
363
513
|
)
|
|
364
514
|
) : /* @__PURE__ */ jsx(AssistantTurn, { turn }, turn.id)
|
|
365
515
|
),
|
|
366
516
|
...live ? [/* @__PURE__ */ jsx(AssistantTurn, { live }, live.id)] : []
|
|
367
|
-
] }),
|
|
517
|
+
] }) }),
|
|
368
518
|
isEmpty && /* @__PURE__ */ jsx("div", { className: "ds-chat-widget__welcome", children: /* @__PURE__ */ jsxs("div", { className: "ds-chat-widget__greeting", children: [
|
|
369
519
|
/* @__PURE__ */ jsx("p", { className: "ds-chat-widget__hello", children: greeting }),
|
|
370
520
|
/* @__PURE__ */ jsx("p", { className: "ds-chat-widget__ask", children: tagline })
|
|
@@ -377,7 +527,8 @@ function ChatWidget({
|
|
|
377
527
|
Composer,
|
|
378
528
|
{
|
|
379
529
|
ref: composerRef,
|
|
380
|
-
placeholder,
|
|
530
|
+
placeholder: questionsOpen ? questionsPlaceholder : placeholder,
|
|
531
|
+
disabled: questionsOpen,
|
|
381
532
|
sendLabel: "Send",
|
|
382
533
|
aiGlow: true,
|
|
383
534
|
value: draft,
|
|
@@ -434,61 +585,137 @@ function ChatWidget({
|
|
|
434
585
|
}
|
|
435
586
|
);
|
|
436
587
|
if (!hasRails) return chatColumn;
|
|
437
|
-
return /* @__PURE__ */ jsxs(
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
"
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
588
|
+
return /* @__PURE__ */ jsxs(
|
|
589
|
+
"div",
|
|
590
|
+
{
|
|
591
|
+
ref: railsHostRef,
|
|
592
|
+
className: "ds-chat-widget__rails",
|
|
593
|
+
"data-artifact-expanded": artifactExpanded || void 0,
|
|
594
|
+
"data-artifact-sized": artifactWidth !== null || artifactResizing || void 0,
|
|
595
|
+
"data-artifact-resizing": artifactResizing || void 0,
|
|
596
|
+
style: artifactWidth !== null ? { "--ds-chat-artifact-width": `${artifactWidth}px` } : void 0,
|
|
597
|
+
onKeyDown: (event) => {
|
|
598
|
+
if (event.key === "Escape" && artifactExpanded) {
|
|
599
|
+
event.stopPropagation();
|
|
600
|
+
setArtifactExpanded(false);
|
|
601
|
+
}
|
|
602
|
+
},
|
|
603
|
+
children: [
|
|
604
|
+
renderThreads && threadsInline && /* @__PURE__ */ jsx("div", { className: "ds-chat-widget__threads-rail", children: renderThreads({ overlay: false, close: closeThreads }) }),
|
|
605
|
+
chatColumn,
|
|
606
|
+
actionsFloat && /* @__PURE__ */ jsx("div", { className: "ds-chat-widget__rail-actions", children: headerActions }),
|
|
607
|
+
renderAside && asideWide && /* @__PURE__ */ jsx(
|
|
608
|
+
"div",
|
|
609
|
+
{
|
|
610
|
+
ref: asideRailRef,
|
|
611
|
+
className: `ds-chat-widget__aside-rail ${asideShown ? "ds-chat-widget__aside-rail--open" : ""}`,
|
|
612
|
+
id: asideId,
|
|
613
|
+
inert: !asideShown || void 0,
|
|
614
|
+
children: /* @__PURE__ */ jsx("div", { className: "ds-chat-widget__aside-rail-inner", children: renderAside({ overlay: false, close: closeAside }) })
|
|
615
|
+
}
|
|
616
|
+
),
|
|
617
|
+
artifactInline && !artifactExpanded && /* @__PURE__ */ jsx(
|
|
618
|
+
"div",
|
|
619
|
+
{
|
|
620
|
+
className: `ds-chat-widget__artifact-grip ${artifactResizing ? "ds-chat-widget__artifact-grip--resizing" : ""}`,
|
|
621
|
+
"aria-hidden": "true",
|
|
622
|
+
onPointerDown: beginArtifactResize,
|
|
623
|
+
onPointerMove: moveArtifactResize,
|
|
624
|
+
onPointerUp: endArtifactResize,
|
|
625
|
+
onPointerCancel: endArtifactResize
|
|
626
|
+
}
|
|
627
|
+
),
|
|
628
|
+
artifacts && asideWide && /* @__PURE__ */ jsx(
|
|
629
|
+
"div",
|
|
630
|
+
{
|
|
631
|
+
ref: artifactRailRef,
|
|
632
|
+
className: `ds-chat-widget__artifact-rail ${artifactShown ? "ds-chat-widget__artifact-rail--open" : ""}`,
|
|
633
|
+
id: artifactId,
|
|
634
|
+
inert: !artifactShown || void 0,
|
|
635
|
+
children: /* @__PURE__ */ jsx("div", { className: "ds-chat-widget__artifact-rail-inner", children: /* @__PURE__ */ jsx(
|
|
636
|
+
ChatArtifactPane,
|
|
637
|
+
{
|
|
638
|
+
artifact: heldArtifact,
|
|
639
|
+
expanded: artifactExpanded,
|
|
640
|
+
onExpandedChange: setArtifactExpanded,
|
|
641
|
+
onClose: closeArtifactPanel
|
|
642
|
+
}
|
|
643
|
+
) })
|
|
644
|
+
}
|
|
645
|
+
),
|
|
646
|
+
artifacts && !asideWide && /* @__PURE__ */ jsxs(
|
|
647
|
+
"div",
|
|
648
|
+
{
|
|
649
|
+
className: `ds-chat-widget__aside-overlay ${artifactShown ? "ds-chat-widget__aside-overlay--open" : ""}`,
|
|
650
|
+
children: [
|
|
651
|
+
/* @__PURE__ */ jsx(
|
|
652
|
+
"button",
|
|
653
|
+
{
|
|
654
|
+
type: "button",
|
|
655
|
+
className: "ds-chat-widget__rail-scrim",
|
|
656
|
+
"aria-label": "Close the artifact panel",
|
|
657
|
+
tabIndex: artifactShown ? 0 : -1,
|
|
658
|
+
onClick: closeArtifactPanel
|
|
659
|
+
}
|
|
660
|
+
),
|
|
661
|
+
/* @__PURE__ */ jsxs(
|
|
662
|
+
"div",
|
|
663
|
+
{
|
|
664
|
+
className: "ds-chat-widget__aside-sheet ds-chat-widget__aside-sheet--artifact",
|
|
665
|
+
id: artifactId,
|
|
666
|
+
children: [
|
|
667
|
+
/* @__PURE__ */ jsx("span", { className: "ds-chat-widget__aside-grabber", "aria-hidden": "true" }),
|
|
668
|
+
/* @__PURE__ */ jsx("div", { className: "ds-chat-widget__aside-sheet-body", children: /* @__PURE__ */ jsx(ChatArtifactPane, { artifact: heldArtifact, onClose: closeArtifactPanel }) })
|
|
669
|
+
]
|
|
670
|
+
}
|
|
671
|
+
)
|
|
672
|
+
]
|
|
673
|
+
}
|
|
674
|
+
),
|
|
675
|
+
renderThreads && !threadsInline && /* @__PURE__ */ jsxs(
|
|
676
|
+
"div",
|
|
677
|
+
{
|
|
678
|
+
className: `ds-chat-widget__threads-overlay ${threadsOpen ? "ds-chat-widget__threads-overlay--open" : ""}`,
|
|
679
|
+
children: [
|
|
680
|
+
/* @__PURE__ */ jsx(
|
|
681
|
+
"button",
|
|
682
|
+
{
|
|
683
|
+
type: "button",
|
|
684
|
+
className: "ds-chat-widget__rail-scrim",
|
|
685
|
+
"aria-label": "Close the chat history",
|
|
686
|
+
tabIndex: threadsOpen ? 0 : -1,
|
|
687
|
+
onClick: closeThreads
|
|
688
|
+
}
|
|
689
|
+
),
|
|
690
|
+
/* @__PURE__ */ jsx("div", { className: "ds-chat-widget__threads-sheet", children: renderThreads({ overlay: true, close: closeThreads }) })
|
|
691
|
+
]
|
|
692
|
+
}
|
|
693
|
+
),
|
|
694
|
+
renderAside && !asideWide && /* @__PURE__ */ jsxs(
|
|
695
|
+
"div",
|
|
696
|
+
{
|
|
697
|
+
className: `ds-chat-widget__aside-overlay ${asideShown ? "ds-chat-widget__aside-overlay--open" : ""}`,
|
|
698
|
+
children: [
|
|
699
|
+
/* @__PURE__ */ jsx(
|
|
700
|
+
"button",
|
|
701
|
+
{
|
|
702
|
+
type: "button",
|
|
703
|
+
className: "ds-chat-widget__rail-scrim",
|
|
704
|
+
"aria-label": "Close the agent panel",
|
|
705
|
+
tabIndex: asideShown ? 0 : -1,
|
|
706
|
+
onClick: closeAside
|
|
707
|
+
}
|
|
708
|
+
),
|
|
709
|
+
/* @__PURE__ */ jsxs("div", { className: "ds-chat-widget__aside-sheet", id: asideId, children: [
|
|
710
|
+
/* @__PURE__ */ jsx("span", { className: "ds-chat-widget__aside-grabber", "aria-hidden": "true" }),
|
|
711
|
+
/* @__PURE__ */ jsx("div", { className: "ds-chat-widget__aside-sheet-body", children: renderAside({ overlay: true, close: closeAside }) })
|
|
712
|
+
] })
|
|
713
|
+
]
|
|
714
|
+
}
|
|
715
|
+
)
|
|
716
|
+
]
|
|
717
|
+
}
|
|
718
|
+
);
|
|
492
719
|
}
|
|
493
720
|
export {
|
|
494
721
|
ChatWidget
|
package/chat/DemoWelcome.js
CHANGED
|
@@ -61,7 +61,7 @@ function DemoBoard({ onAsk }) {
|
|
|
61
61
|
/* @__PURE__ */ jsx(WidgetRow, { title: "How is the trial going?", meta: "Ask", onClick: () => onAsk("How is the trial going?") })
|
|
62
62
|
] }),
|
|
63
63
|
/* @__PURE__ */ jsx(Widget, { title: "Projects", count: BOARD_PROJECTS.length, children: BOARD_PROJECTS.map(([name, state, when]) => /* @__PURE__ */ jsx(WidgetRow, { leading: /* @__PURE__ */ jsx(PixelAvatar, { name }), title: name, description: state, meta: when }, name)) }),
|
|
64
|
-
/* @__PURE__ */ jsxs(Widget, { title: "To do", count:
|
|
64
|
+
/* @__PURE__ */ jsxs(Widget, { title: "To do", count: 5, children: [
|
|
65
65
|
/* @__PURE__ */ jsxs(WidgetGroup, { label: "Waiting on you", children: [
|
|
66
66
|
/* @__PURE__ */ jsx(
|
|
67
67
|
WidgetRow,
|
|
@@ -70,7 +70,7 @@ function DemoBoard({ onAsk }) {
|
|
|
70
70
|
title: "Invite the other three",
|
|
71
71
|
description: "2 of 5 seats filled",
|
|
72
72
|
meta: "Today",
|
|
73
|
-
onClick: () => onAsk("
|
|
73
|
+
onClick: () => onAsk("Invite my team to a workspace")
|
|
74
74
|
}
|
|
75
75
|
),
|
|
76
76
|
/* @__PURE__ */ jsx(
|
|
@@ -80,7 +80,17 @@ function DemoBoard({ onAsk }) {
|
|
|
80
80
|
title: "Choose a plan",
|
|
81
81
|
description: "Trial ends in 9 days",
|
|
82
82
|
meta: "Oct 19",
|
|
83
|
-
onClick: () => onAsk("
|
|
83
|
+
onClick: () => onAsk("Help me pick a plan")
|
|
84
|
+
}
|
|
85
|
+
),
|
|
86
|
+
/* @__PURE__ */ jsx(
|
|
87
|
+
WidgetRow,
|
|
88
|
+
{
|
|
89
|
+
leading: /* @__PURE__ */ jsx(StatusDot, { variant: "info", size: "sm", "aria-hidden": "true" }),
|
|
90
|
+
title: "Plan the launch kickoff",
|
|
91
|
+
description: "A date, who comes, and how",
|
|
92
|
+
meta: "Oct 21",
|
|
93
|
+
onClick: () => onAsk("Plan our launch kickoff")
|
|
84
94
|
}
|
|
85
95
|
)
|
|
86
96
|
] }),
|
|
@@ -91,7 +101,8 @@ function DemoBoard({ onAsk }) {
|
|
|
91
101
|
leading: /* @__PURE__ */ jsx(StatusDot, { variant: "positive", size: "sm", "aria-hidden": "true" }),
|
|
92
102
|
title: "Shared calendar",
|
|
93
103
|
description: "Synced an hour ago",
|
|
94
|
-
meta: "On"
|
|
104
|
+
meta: "On",
|
|
105
|
+
onClick: () => onAsk("Find a time we can all meet")
|
|
95
106
|
}
|
|
96
107
|
),
|
|
97
108
|
/* @__PURE__ */ jsx(
|
|
@@ -100,7 +111,8 @@ function DemoBoard({ onAsk }) {
|
|
|
100
111
|
leading: /* @__PURE__ */ jsx(StatusDot, { variant: "neutral", size: "sm", "aria-hidden": "true" }),
|
|
101
112
|
title: "Mention notifications",
|
|
102
113
|
description: "Quiet hours, 6pm to 8am",
|
|
103
|
-
meta: "Paused"
|
|
114
|
+
meta: "Paused",
|
|
115
|
+
onClick: () => onAsk("Set up notifications for me")
|
|
104
116
|
}
|
|
105
117
|
)
|
|
106
118
|
] })
|
|
@@ -4,15 +4,16 @@
|
|
|
4
4
|
* one pattern for "here is something you could ask", whether the conversation
|
|
5
5
|
* has started or not. The only difference is where the questions come from.
|
|
6
6
|
*
|
|
7
|
-
* Tapping one sends it as the next message
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Tapping one sends it as the next message and keeps the row as the record
|
|
8
|
+
* of that choice: the chip checked, the rest dimmed and inert. A tap while a
|
|
9
|
+
* response is already streaming is ignored by `send`, the same as pressing
|
|
10
|
+
* enter in the composer would be, and leaves the row as it was.
|
|
10
11
|
*
|
|
11
12
|
* While the questions are still being generated, `pending` shows the row's
|
|
12
13
|
* shimmer placeholders in their place: a real loading state, unlike the
|
|
13
14
|
* welcome starters' staged one.
|
|
14
15
|
*/
|
|
15
|
-
export declare function FollowupSuggestions({ turnId, suggestions, pending, entrance, }: {
|
|
16
|
+
export declare function FollowupSuggestions({ turnId, suggestions, pending, entrance, chosen, }: {
|
|
16
17
|
/** The turn these belong to; makes each suggestion's id unique in the thread. */
|
|
17
18
|
turnId: string;
|
|
18
19
|
suggestions: string[];
|
|
@@ -20,4 +21,6 @@ export declare function FollowupSuggestions({ turnId, suggestions, pending, entr
|
|
|
20
21
|
pending?: boolean;
|
|
21
22
|
/** Play the chips' staggered pop-in. Off for rows remounted by a reopen, whose suggestions already arrived once. */
|
|
22
23
|
entrance?: boolean;
|
|
24
|
+
/** The label already sent from this row, which keeps the row as a record. */
|
|
25
|
+
chosen?: string;
|
|
23
26
|
}): import("react").JSX.Element;
|
|
@@ -6,9 +6,10 @@ function FollowupSuggestions({
|
|
|
6
6
|
turnId,
|
|
7
7
|
suggestions,
|
|
8
8
|
pending = false,
|
|
9
|
-
entrance = true
|
|
9
|
+
entrance = true,
|
|
10
|
+
chosen
|
|
10
11
|
}) {
|
|
11
|
-
const {
|
|
12
|
+
const { chooseFollowup } = useChatWidget();
|
|
12
13
|
const items = suggestions.map((label, index) => ({
|
|
13
14
|
id: `${turnId}-followup-${index}`,
|
|
14
15
|
label
|
|
@@ -22,9 +23,10 @@ function FollowupSuggestions({
|
|
|
22
23
|
pending,
|
|
23
24
|
entrance,
|
|
24
25
|
suggestions: items,
|
|
26
|
+
value: chosen !== void 0 ? items.find((item) => item.label === chosen)?.id : void 0,
|
|
25
27
|
onValueChange: (id) => {
|
|
26
28
|
const item = items.find((candidate) => candidate.id === id);
|
|
27
|
-
if (item)
|
|
29
|
+
if (item) chooseFollowup(turnId, item.label);
|
|
28
30
|
}
|
|
29
31
|
}
|
|
30
32
|
);
|
package/chat/TurnContent.d.ts
CHANGED
|
@@ -2,7 +2,9 @@ import { ChatCard } from './types';
|
|
|
2
2
|
/**
|
|
3
3
|
* A turn's `card`, drawn through the provider's renderers. A kind with no
|
|
4
4
|
* renderer draws nothing: a card is an enrichment of an answer that already
|
|
5
|
-
* stands in text, so an unknown kind degrades to the words alone.
|
|
5
|
+
* stands in text, so an unknown kind degrades to the words alone. The one
|
|
6
|
+
* built-in kind is the artifact, drawn as its tile unless the host has
|
|
7
|
+
* taken the kind over with a renderer of its own.
|
|
6
8
|
*/
|
|
7
9
|
export declare function TurnCard({ card }: {
|
|
8
10
|
card: ChatCard;
|
package/chat/TurnContent.js
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, Fragment } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
3
|
+
import { useContext } from "react";
|
|
4
|
+
import { cardArtifact } from "./artifacts.js";
|
|
5
|
+
import { ChatArtifactCard } from "./ChatArtifactCard.js";
|
|
6
|
+
import { useChatWidget, ArtifactSeatContext } from "./context.js";
|
|
4
7
|
function TurnCard({ card }) {
|
|
5
8
|
const { renderers } = useChatWidget();
|
|
9
|
+
const artifactSeat = useContext(ArtifactSeatContext);
|
|
6
10
|
const render = renderers[card.kind];
|
|
7
|
-
if (!render)
|
|
11
|
+
if (!render) {
|
|
12
|
+
const artifact = artifactSeat ? cardArtifact(card) : null;
|
|
13
|
+
return artifact ? /* @__PURE__ */ jsx(ChatArtifactCard, { artifact }) : null;
|
|
14
|
+
}
|
|
8
15
|
return /* @__PURE__ */ jsx(Fragment, { children: render(card.data) });
|
|
9
16
|
}
|
|
10
17
|
export {
|