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
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ An open source React design system built for AI products and coding agents: comp
|
|
|
13
13
|
|
|
14
14
|
## Why Rift DS
|
|
15
15
|
|
|
16
|
-
- **<!-- component-count -->
|
|
16
|
+
- **<!-- component-count -->152<!-- /component-count --> components.** Buttons, forms and tables through to charts, navigation and overlays, each typed and documented with live examples. React is the only required peer dependency.
|
|
17
17
|
- **AI patterns, the whole chat and templates, ready to use.** Composer, streaming chat thread, tool calls, reasoning and thread panel are in the box, the complete AI chat ships assembled as one widget that one command scaffolds, and the docs site has complete template screens built from them.
|
|
18
18
|
- **Fully themeable.** The shipped themes are complete looks to start from, each in light and dark. One `data-brand` attribute applies a theme, and a few CSS overrides make your own.
|
|
19
19
|
- **Ready for coding agents.** One command installs the agent skills, and a public MCP endpoint serves every component's props, so your agent builds with the library as it actually is. No key, no account.
|
|
@@ -94,13 +94,13 @@ import 'rift-ds/tokens/presets/presets.css';
|
|
|
94
94
|
|
|
95
95
|
## Components
|
|
96
96
|
|
|
97
|
-
<!-- component-count -->
|
|
97
|
+
<!-- component-count -->152<!-- /component-count --> components, including a chat set for AI products (Chat thread, Composer, Tool call, Reasoning, Thread panel) and Shader field, a WebGL2 ambient background whose light sources read your colour tokens at runtime, so it re-themes with everything else. Details and live examples are on the [docs site](https://rift-ds.com/components).
|
|
98
98
|
|
|
99
99
|
<details>
|
|
100
100
|
<summary>The full list</summary>
|
|
101
101
|
|
|
102
102
|
<!-- component-list:start -->
|
|
103
|
-
Accordion · Agent plan · Agent rail · Agent status · AI button · Alert · Alert dialog · Anchor nav · Animated number · App layout · App sidebar · Area chart · Attachment drop zone · Attachment group · Attachment tile · Attachment viewer · Avatar · Avatar group · Badge · Banner · Bar chart · Bottom nav · Breadcrumb · Button · Button group · Card · Card stack · Carousel · Chat header · Chat marker · Chat message · Chat thread · Checkbox · Chip · Circular button · Code block · Code diff · Colour picker · Combo chart · Combobox · Command palette · Composer · Contact card · Context menu · Contribution graph · Data table · Date input · Date picker · Dialog · Divider · Document chip · Drawer · Dropdown · Dropdown menu · Empty state · Entity card · Event calendar · Field · Figure · File input · Filter bar · Funnel chart · Gantt chart · Gauge · Globe · Hover card · Image compare · Input · Inspector colour picker · Inspector dropdown · Inspector input · Inspector section · Inspector segments · Inspector slider · Inspector toggle switch · Instructions · Interrupt card · Kbd · Legend tile · Lightbox · Line chart · Link list · Map callout · Map legend · Mention · Message actions · Message card · Meter · Model picker · Nav · Nav list · Notification centre · Number input · Pagination · Panel · Pie chart · Pin input · Pixel avatar · Popover · Progress bar · Prompt suggestions · Prose · Quote · Radar chart · Radial chart · Radio button · Rating · Reasoning · Rich dropdown · Scatter chart · Section title · Segmented control · Selection card · Shader field · Sidebar panel · Skeleton · Slider · Source chip · Source trail · Sparkline · Spinner · Split button · Split pane · Stacked bar chart · Stat · Status dot · Stepper · Streaming text · Swatch · Table · Tabs · Tag input · Textarea · Thread panel · Thread tabs · Time picker · Timeline · Toast · Toggle group · Toggle switch · Tool call · Toolbar · Tooltip · Top app bar · Tree view · Treemap · Usage card · Waveform · Widget · World map
|
|
103
|
+
Accordion · Agent plan · Agent rail · Agent status · AI button · Alert · Alert dialog · Anchor nav · Animated number · App layout · App sidebar · Area chart · Artifact card · Artifact panel · Attachment drop zone · Attachment group · Attachment tile · Attachment viewer · Avatar · Avatar group · Badge · Banner · Bar chart · Bottom nav · Breadcrumb · Button · Button group · Card · Card stack · Carousel · Chat header · Chat marker · Chat message · Chat thread · Checkbox · Chip · Circular button · Code block · Code diff · Colour picker · Combo chart · Combobox · Command palette · Composer · Contact card · Context menu · Contribution graph · Data table · Date input · Date picker · Dialog · Divider · Document chip · Drawer · Dropdown · Dropdown menu · Empty state · Entity card · Event calendar · Field · Figure · File input · Filter bar · Funnel chart · Gantt chart · Gauge · Globe · Hover card · Image compare · Input · Inspector colour picker · Inspector dropdown · Inspector input · Inspector section · Inspector segments · Inspector slider · Inspector toggle switch · Instructions · Interrupt card · Kbd · Legend tile · Lightbox · Line chart · Link list · Map callout · Map legend · Mention · Message actions · Message card · Meter · Model picker · Nav · Nav list · Notification centre · Number input · Pagination · Panel · Pie chart · Pin input · Pixel avatar · Popover · Progress bar · Prompt suggestions · Prose · Quote · Radar chart · Radial chart · Radio button · Rating · Reasoning · Rich dropdown · Scatter chart · Section title · Segmented control · Selection card · Shader field · Sidebar panel · Skeleton · Slider · Source chip · Source trail · Sparkline · Spinner · Split button · Split pane · Stacked bar chart · Stat · Status dot · Stepper · Streaming text · Swatch · Table · Tabs · Tag input · Textarea · Thread panel · Thread tabs · Time picker · Timeline · Toast · Toggle group · Toggle switch · Tool call · Toolbar · Tooltip · Top app bar · Tree view · Treemap · Usage card · Waveform · Widget · World map
|
|
104
104
|
<!-- component-list:end -->
|
|
105
105
|
|
|
106
106
|
</details>
|
package/chat/ChatAgentPane.d.ts
CHANGED
|
@@ -10,12 +10,15 @@ export interface ChatAgentPaneProps {
|
|
|
10
10
|
onTabChange?: (id: string) => void;
|
|
11
11
|
/** The widget seated the pane as a bottom sheet, where it fills the width instead of holding its own column. */
|
|
12
12
|
overlay: boolean;
|
|
13
|
+
/** Fires from the pane's own close button, a cross in its top trailing corner. Omit for a pane with no close of its own. */
|
|
14
|
+
onClose?: () => void;
|
|
13
15
|
}
|
|
14
16
|
/**
|
|
15
17
|
* The built-in agent pane: an AgentRail the widget seats on the
|
|
16
|
-
* conversation's trailing edge (inline
|
|
17
|
-
* below). A host hands it the agent's profile and panes; the widget owns
|
|
18
|
-
* whether it is up
|
|
19
|
-
*
|
|
18
|
+
* conversation's trailing edge (inline from ASIDE_RAIL_MIN_WIDTH, a bottom sheet
|
|
19
|
+
* below, the artifact panel's own rule). A host hands it the agent's profile and panes; the widget owns
|
|
20
|
+
* whether it is up. The header's agent toggle opens and closes it, and the
|
|
21
|
+
* pane carries a close of its own in its top corner, where a panel's way
|
|
22
|
+
* out is looked for.
|
|
20
23
|
*/
|
|
21
|
-
export declare function ChatAgentPane({ profile, tabs, activeTab, onTabChange, overlay }: ChatAgentPaneProps): import("react").JSX.Element;
|
|
24
|
+
export declare function ChatAgentPane({ profile, tabs, activeTab, onTabChange, overlay, onClose }: ChatAgentPaneProps): import("react").JSX.Element;
|
package/chat/ChatAgentPane.js
CHANGED
|
@@ -3,7 +3,7 @@ import { jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import { AgentRail } from "../components/AgentRail/AgentRail.js";
|
|
5
5
|
import "./ChatAgentPane.css";
|
|
6
|
-
function ChatAgentPane({ profile, tabs, activeTab, onTabChange, overlay }) {
|
|
6
|
+
function ChatAgentPane({ profile, tabs, activeTab, onTabChange, overlay, onClose }) {
|
|
7
7
|
const [ownTab, setOwnTab] = useState(tabs[0]?.id ?? "");
|
|
8
8
|
const current = activeTab ?? ownTab;
|
|
9
9
|
return /* @__PURE__ */ jsx(
|
|
@@ -13,6 +13,9 @@ function ChatAgentPane({ profile, tabs, activeTab, onTabChange, overlay }) {
|
|
|
13
13
|
profile,
|
|
14
14
|
tabs,
|
|
15
15
|
activeTab: current,
|
|
16
|
+
onCollapse: onClose,
|
|
17
|
+
collapseVariant: "close",
|
|
18
|
+
collapseLabel: "Close the agent panel",
|
|
16
19
|
onTabChange: (id) => {
|
|
17
20
|
setOwnTab(id);
|
|
18
21
|
onTabChange?.(id);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ChatArtifact } from './types';
|
|
2
|
+
export interface ChatArtifactCardProps {
|
|
3
|
+
/** The artifact this tile stands for. */
|
|
4
|
+
artifact: ChatArtifact;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* An artifact's place in the thread: an ArtifactCard wired to the provider,
|
|
8
|
+
* which opens its artifact in the artifact panel. Pressing the tile of the
|
|
9
|
+
* artifact already on stage closes the panel again, so the tile is one
|
|
10
|
+
* control both ways. The widget draws it for every `artifact` card; a host
|
|
11
|
+
* building its own thread renders it the same way.
|
|
12
|
+
*/
|
|
13
|
+
export declare function ChatArtifactCard({ artifact }: ChatArtifactCardProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { ArtifactCard } from "../components/ArtifactCard/ArtifactCard.js";
|
|
4
|
+
import { useChatWidget } from "./context.js";
|
|
5
|
+
function ChatArtifactCard({ artifact }) {
|
|
6
|
+
const { artifact: onStage, openArtifact, closeArtifact } = useChatWidget();
|
|
7
|
+
const open = onStage?.id === artifact.id;
|
|
8
|
+
return /* @__PURE__ */ jsx(
|
|
9
|
+
ArtifactCard,
|
|
10
|
+
{
|
|
11
|
+
title: artifact.title,
|
|
12
|
+
meta: artifact.meta,
|
|
13
|
+
icon: artifact.icon,
|
|
14
|
+
open,
|
|
15
|
+
onClick: () => open ? closeArtifact() : openArtifact(artifact)
|
|
16
|
+
}
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
export {
|
|
20
|
+
ChatArtifactCard
|
|
21
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ChatArtifact } from './types';
|
|
2
|
+
export interface ChatArtifactPaneProps {
|
|
3
|
+
/** The artifact on stage, or null for the empty state: the panel opened before the conversation has made anything. */
|
|
4
|
+
artifact: ChatArtifact | null;
|
|
5
|
+
/** Whether the widget is showing the pane at its expanded size. */
|
|
6
|
+
expanded?: boolean;
|
|
7
|
+
/** Fires with the next state from the expand toggle; omit where the seat has one size. */
|
|
8
|
+
onExpandedChange?: (expanded: boolean) => void;
|
|
9
|
+
/** Fires from the pane's close button; omit where another control closes the seat. */
|
|
10
|
+
onClose?: () => void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The built-in artifact pane: an ArtifactPanel drawn from a `ChatArtifact`.
|
|
14
|
+
* A kind the provider's `artifactRenderers` knows is live React content;
|
|
15
|
+
* otherwise the artifact's document, URL or source decides the preview. The
|
|
16
|
+
* widget owns the seat (beside the conversation, or a sheet over it).
|
|
17
|
+
*/
|
|
18
|
+
export declare function ChatArtifactPane({ artifact, expanded, onExpandedChange, onClose }: ChatArtifactPaneProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { ArtifactPanel } from "../components/ArtifactPanel/ArtifactPanel.js";
|
|
4
|
+
import { EmptyState } from "../components/EmptyState/EmptyState.js";
|
|
5
|
+
import { useChatWidget } from "./context.js";
|
|
6
|
+
const FRAMEABLE_URL = /^(?:https?:\/\/|\/(?!\/))/i;
|
|
7
|
+
function ChatArtifactPane({ artifact, expanded, onExpandedChange, onClose }) {
|
|
8
|
+
const { artifactRenderers } = useChatWidget();
|
|
9
|
+
if (!artifact) {
|
|
10
|
+
return /* @__PURE__ */ jsx(ArtifactPanel, { title: "Artifacts", icon: "space_dashboard", onClose, children: /* @__PURE__ */ jsx("div", { className: "ds-chat-widget__artifact-empty", children: /* @__PURE__ */ jsx(
|
|
11
|
+
EmptyState,
|
|
12
|
+
{
|
|
13
|
+
size: "compact",
|
|
14
|
+
icon: "draft",
|
|
15
|
+
title: "Nothing here yet",
|
|
16
|
+
description: "What the chat makes for you, like a dashboard or a page, opens in this panel."
|
|
17
|
+
}
|
|
18
|
+
) }) });
|
|
19
|
+
}
|
|
20
|
+
const custom = Object.hasOwn(artifactRenderers, artifact.kind);
|
|
21
|
+
const render = artifactRenderers[artifact.kind];
|
|
22
|
+
const src = artifact.url && FRAMEABLE_URL.test(artifact.url) ? artifact.url : void 0;
|
|
23
|
+
return /* @__PURE__ */ jsx(
|
|
24
|
+
ArtifactPanel,
|
|
25
|
+
{
|
|
26
|
+
title: artifact.title,
|
|
27
|
+
meta: artifact.meta,
|
|
28
|
+
icon: artifact.icon,
|
|
29
|
+
html: custom ? void 0 : artifact.html,
|
|
30
|
+
src: custom ? void 0 : src,
|
|
31
|
+
code: artifact.code ?? (custom ? void 0 : artifact.html),
|
|
32
|
+
language: artifact.language ?? (artifact.code === void 0 && artifact.html !== void 0 ? "html" : void 0),
|
|
33
|
+
expanded,
|
|
34
|
+
onExpandedChange,
|
|
35
|
+
onClose,
|
|
36
|
+
children: custom ? render(artifact) : void 0
|
|
37
|
+
},
|
|
38
|
+
artifact.id
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
export {
|
|
42
|
+
ChatArtifactPane
|
|
43
|
+
};
|
package/chat/ChatProvider.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
import { ChatCardRenderers, ChatLinkRenderer, FeedbackSink } from './context';
|
|
2
|
+
import { ChatArtifactRenderers, ChatCardRenderers, ChatLinkRenderer, FeedbackSink } from './context';
|
|
3
3
|
import { FollowupsSource } from './followups';
|
|
4
4
|
import { ChatPlacement } from './placement';
|
|
5
5
|
import { ThreadsOptions } from './threads';
|
|
@@ -33,6 +33,8 @@ export interface ChatProviderProps {
|
|
|
33
33
|
feedback?: FeedbackSink | string | false;
|
|
34
34
|
/** Draws `card` events by kind. */
|
|
35
35
|
renderers?: ChatCardRenderers;
|
|
36
|
+
/** Draws an artifact's preview by kind, for artifacts that are live React content rather than a document or a URL. */
|
|
37
|
+
artifactRenderers?: ChatArtifactRenderers;
|
|
36
38
|
/** Renders a same-site link in an answer. The default is a plain anchor; a Next.js host passes its Link. */
|
|
37
39
|
renderLink?: ChatLinkRenderer;
|
|
38
40
|
/** Which hrefs in an answer become links. Everything else renders as text. */
|
|
@@ -50,4 +52,4 @@ export interface ChatProviderProps {
|
|
|
50
52
|
* clicking between pages for free. A hard reload starts clean by design:
|
|
51
53
|
* no storage, no persistence.
|
|
52
54
|
*/
|
|
53
|
-
export declare function ChatProvider({ transport, endpoint, defaultPlacement, followups, fallbackFollowups, feedback, renderers, renderLink, isInternalHref, threads: threadsOptions, placeholders, children, }: ChatProviderProps): import("react").JSX.Element;
|
|
55
|
+
export declare function ChatProvider({ transport, endpoint, defaultPlacement, followups, fallbackFollowups, feedback, renderers, artifactRenderers, renderLink, isInternalHref, threads: threadsOptions, placeholders, children, }: ChatProviderProps): import("react").JSX.Element;
|
package/chat/ChatProvider.js
CHANGED
|
@@ -16,6 +16,7 @@ function ChatProvider({
|
|
|
16
16
|
fallbackFollowups = DEFAULT_FALLBACK_FOLLOWUPS,
|
|
17
17
|
feedback = "/api/chat/feedback",
|
|
18
18
|
renderers,
|
|
19
|
+
artifactRenderers,
|
|
19
20
|
renderLink = defaultRenderLink,
|
|
20
21
|
isInternalHref = isInternalPath,
|
|
21
22
|
threads: threadsOptions,
|
|
@@ -70,9 +71,14 @@ function ChatProvider({
|
|
|
70
71
|
);
|
|
71
72
|
const [starterRevealKey, setStarterRevealKey] = useState(null);
|
|
72
73
|
const [starterSeed, setStarterSeed] = useState(() => Math.random());
|
|
74
|
+
const [artifact, setArtifact] = useState(null);
|
|
75
|
+
const openArtifact = useCallback((next) => setArtifact(next), []);
|
|
76
|
+
const closeArtifact = useCallback(() => setArtifact(null), []);
|
|
77
|
+
if (artifact && chat.turns.length === 0 && !chat.live) setArtifact(null);
|
|
73
78
|
const { reset: resetChat } = chat;
|
|
74
79
|
const reset = useCallback(() => {
|
|
75
80
|
resetChat();
|
|
81
|
+
setArtifact(null);
|
|
76
82
|
setStarterSeed(Math.random());
|
|
77
83
|
setStarterRevealKey(null);
|
|
78
84
|
}, [resetChat]);
|
|
@@ -121,6 +127,7 @@ function ChatProvider({
|
|
|
121
127
|
const returnFocusRef = useRef(null);
|
|
122
128
|
const toggleOpen = useCallback(() => setOpen(!open), [open, setOpen]);
|
|
123
129
|
const renderersValue = useMemo(() => renderers ?? {}, [renderers]);
|
|
130
|
+
const artifactRenderersValue = useMemo(() => artifactRenderers ?? {}, [artifactRenderers]);
|
|
124
131
|
const value = useMemo(
|
|
125
132
|
() => ({
|
|
126
133
|
...chat,
|
|
@@ -146,6 +153,10 @@ function ChatProvider({
|
|
|
146
153
|
returnFocusRef,
|
|
147
154
|
threads,
|
|
148
155
|
renderers: renderersValue,
|
|
156
|
+
artifact,
|
|
157
|
+
openArtifact,
|
|
158
|
+
closeArtifact,
|
|
159
|
+
artifactRenderers: artifactRenderersValue,
|
|
149
160
|
renderLink,
|
|
150
161
|
isInternalHref,
|
|
151
162
|
submitFeedback
|
|
@@ -169,6 +180,10 @@ function ChatProvider({
|
|
|
169
180
|
draft,
|
|
170
181
|
threads,
|
|
171
182
|
renderersValue,
|
|
183
|
+
artifact,
|
|
184
|
+
openArtifact,
|
|
185
|
+
closeArtifact,
|
|
186
|
+
artifactRenderersValue,
|
|
172
187
|
renderLink,
|
|
173
188
|
isInternalHref,
|
|
174
189
|
submitFeedback
|
package/chat/ChatWidget.css
CHANGED
|
@@ -52,8 +52,9 @@
|
|
|
52
52
|
padding-inline: var(--ds-chat-widget-inset);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
/* With
|
|
56
|
-
|
|
55
|
+
/* With a trailing panel seated inline, the controls stand over the panel's
|
|
56
|
+
top inset and the header row has nothing 40px tall left in it, so it
|
|
57
|
+
would collapse to the title's own line. The row
|
|
57
58
|
holds the height its buttons were giving it: the control plus the
|
|
58
59
|
header's own block padding either side. */
|
|
59
60
|
.ds-chat-widget[data-lifted-actions] .ds-chat-header {
|
|
@@ -657,54 +658,240 @@
|
|
|
657
658
|
own rounded card, inset on three sides. The top margin is the header
|
|
658
659
|
row's exact height (the chat column's top padding, plus the header's own
|
|
659
660
|
block padding either side of a 40px control), so the panel begins where
|
|
660
|
-
that row ends and the
|
|
661
|
+
that row ends and the widget's controls sit in the clear above it. */
|
|
661
662
|
.ds-chat-widget__aside-rail {
|
|
663
|
+
display: flex;
|
|
662
664
|
flex: none;
|
|
665
|
+
/* The open and close glide, the artifact panel's own: the rail's width
|
|
666
|
+
runs from nothing to the pane's, and the card is the rail at every
|
|
667
|
+
frame, a whole rounded card growing from the trailing edge with its
|
|
668
|
+
content revealed behind the leading border. The target is the pane's
|
|
669
|
+
natural width, which the widget measures and hands over as
|
|
670
|
+
--ds-chat-aside-width (a rail holds a width of its own, so there is no
|
|
671
|
+
share of the row to animate as the artifact does). Closed, the rail
|
|
672
|
+
keeps no width, no inset and no border, and goes invisible once the
|
|
673
|
+
collapse has finished (the visibility delay), so nothing of it can be
|
|
674
|
+
seen, hovered or reached. The reduced-motion guard in tokens-motion.css
|
|
675
|
+
zeroes every duration here. */
|
|
676
|
+
width: 0;
|
|
677
|
+
min-width: 0;
|
|
663
678
|
min-height: 0;
|
|
664
679
|
margin: calc(var(--padding-200) + var(--padding-150) * 2 + 40px) 0 var(--padding-500) 0;
|
|
665
|
-
/* The open and close glide, the threads rail's own: a one-column grid
|
|
666
|
-
whose track runs 0fr to 1fr, so the pane's width animates without the
|
|
667
|
-
widget knowing it, while the pane's own card fades with it. Closed, the
|
|
668
|
-
rail keeps no width, no inset and no border, and goes invisible once
|
|
669
|
-
the collapse has finished (the visibility delay), so nothing of it can
|
|
670
|
-
be seen, hovered or reached. The reduced-motion guard in
|
|
671
|
-
tokens-motion.css zeroes every duration here. */
|
|
672
|
-
display: grid;
|
|
673
|
-
grid-template-columns: 0fr;
|
|
674
680
|
opacity: 0;
|
|
675
681
|
visibility: hidden;
|
|
682
|
+
overflow: hidden;
|
|
676
683
|
transition:
|
|
677
|
-
|
|
684
|
+
width var(--motion-duration-slow) var(--motion-ease-emphasized),
|
|
678
685
|
margin var(--motion-duration-slow) var(--motion-ease-emphasized),
|
|
679
686
|
opacity var(--motion-duration-base) var(--motion-ease-standard),
|
|
680
687
|
visibility 0s linear var(--motion-duration-slow);
|
|
681
688
|
}
|
|
682
689
|
|
|
683
690
|
.ds-chat-widget__aside-rail--open {
|
|
684
|
-
|
|
691
|
+
/* Before the first measurement there is no number to glide to, so the
|
|
692
|
+
rail simply takes its content's width. */
|
|
693
|
+
width: var(--ds-chat-aside-width, max-content);
|
|
685
694
|
margin-right: var(--padding-500);
|
|
686
695
|
opacity: 1;
|
|
687
696
|
visibility: visible;
|
|
688
697
|
transition:
|
|
689
|
-
|
|
698
|
+
width var(--motion-duration-slow) var(--motion-ease-emphasized),
|
|
690
699
|
margin var(--motion-duration-slow) var(--motion-ease-emphasized),
|
|
691
700
|
opacity var(--motion-duration-base) var(--motion-ease-standard) calc(var(--motion-duration-slow) - var(--motion-duration-base)),
|
|
692
701
|
visibility 0s linear 0s;
|
|
693
702
|
}
|
|
694
703
|
|
|
695
|
-
/* The pane's card
|
|
696
|
-
|
|
704
|
+
/* The pane's card fills the rail, so the two are one shape through the
|
|
705
|
+
glide; the pane inside keeps its own width and is clipped by the card. A
|
|
706
|
+
trailing panel is a solid surface, the page's own ground, not the
|
|
707
|
+
widget's glass: it is a place to read and work in, and what shows through
|
|
708
|
+
glass (the host's page, an ambient background) would sit under its
|
|
709
|
+
content. */
|
|
697
710
|
.ds-chat-widget__aside-rail-inner {
|
|
711
|
+
box-sizing: border-box;
|
|
698
712
|
display: flex;
|
|
713
|
+
flex: 1 0 auto;
|
|
714
|
+
width: 100%;
|
|
699
715
|
min-width: 0;
|
|
700
716
|
min-height: 0;
|
|
701
717
|
border: var(--border-025) solid var(--color-bg-container-border);
|
|
702
718
|
border-radius: var(--radius-600);
|
|
719
|
+
background-color: var(--color-bg-page-primary);
|
|
703
720
|
overflow: hidden;
|
|
704
721
|
}
|
|
705
722
|
|
|
706
|
-
/* The
|
|
707
|
-
|
|
723
|
+
/* The pane never squeezes with the card around it. */
|
|
724
|
+
.ds-chat-widget__aside-rail-inner > * {
|
|
725
|
+
flex: none;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/* ============================================
|
|
729
|
+
THE ARTIFACT PANEL — the trailing seat's other tenant. Wide widgets seat
|
|
730
|
+
it beside the conversation as a second column; narrow ones raise it as a
|
|
731
|
+
tall bottom sheet. Unlike the agent rail it has no width of its own: it
|
|
732
|
+
shares the row with the conversation, a little the larger of the two.
|
|
733
|
+
============================================ */
|
|
734
|
+
|
|
735
|
+
/* Any child of the row that is the conversation takes the room the rails
|
|
736
|
+
leave, and can shrink under an artifact. */
|
|
737
|
+
.ds-chat-widget__rails > .ds-chat-widget {
|
|
738
|
+
flex: 1 1 0;
|
|
739
|
+
min-width: 0;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
/* The artifact's card floats exactly as the agent rail's does: inset from
|
|
743
|
+
the widget's edges, beginning under the header row (the same top margin,
|
|
744
|
+
the row's own height), with the widget's lifted controls in the clear
|
|
745
|
+
above it. The glide is the flex share running from nothing, the card
|
|
746
|
+
fading in behind it; closed, the rail keeps no width and no inset and
|
|
747
|
+
goes invisible once the collapse has finished. The reduced-motion guard
|
|
748
|
+
in tokens-motion.css zeroes every duration here. */
|
|
749
|
+
.ds-chat-widget__artifact-rail {
|
|
750
|
+
display: flex;
|
|
751
|
+
flex: 0 1 0;
|
|
752
|
+
min-width: 0;
|
|
753
|
+
min-height: 0;
|
|
754
|
+
margin: calc(var(--padding-200) + var(--padding-150) * 2 + 40px) 0 var(--padding-500) 0;
|
|
755
|
+
opacity: 0;
|
|
756
|
+
visibility: hidden;
|
|
757
|
+
overflow: hidden;
|
|
758
|
+
transition:
|
|
759
|
+
flex-grow var(--motion-duration-slow) var(--motion-ease-emphasized),
|
|
760
|
+
margin var(--motion-duration-slow) var(--motion-ease-emphasized),
|
|
761
|
+
opacity var(--motion-duration-base) var(--motion-ease-standard),
|
|
762
|
+
visibility 0s linear var(--motion-duration-slow);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
.ds-chat-widget__artifact-rail--open {
|
|
766
|
+
flex-grow: 1.2;
|
|
767
|
+
margin-right: var(--padding-500);
|
|
768
|
+
opacity: 1;
|
|
769
|
+
visibility: visible;
|
|
770
|
+
transition:
|
|
771
|
+
flex-grow var(--motion-duration-slow) var(--motion-ease-emphasized),
|
|
772
|
+
margin var(--motion-duration-slow) var(--motion-ease-emphasized),
|
|
773
|
+
opacity var(--motion-duration-base) var(--motion-ease-standard) calc(var(--motion-duration-slow) - var(--motion-duration-base)),
|
|
774
|
+
visibility 0s linear 0s;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
.ds-chat-widget__artifact-rail-inner {
|
|
778
|
+
display: flex;
|
|
779
|
+
flex: 1;
|
|
780
|
+
min-width: 0;
|
|
781
|
+
min-height: 0;
|
|
782
|
+
border: var(--border-025) solid var(--color-bg-container-border);
|
|
783
|
+
border-radius: var(--radius-600);
|
|
784
|
+
/* Solid, the agent pane's ground: see the aside rail's card. */
|
|
785
|
+
background-color: var(--color-bg-page-primary);
|
|
786
|
+
overflow: hidden;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
/* The panel opened before the conversation has made anything. */
|
|
790
|
+
.ds-chat-widget__artifact-empty {
|
|
791
|
+
display: grid;
|
|
792
|
+
place-items: center;
|
|
793
|
+
min-height: 100%;
|
|
794
|
+
padding: var(--padding-400);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/* Sized by the grip: the panel holds the width the visitor gave it, and
|
|
798
|
+
gives it back before the conversation drops under its floor (the 320px
|
|
799
|
+
mirrors ARTIFACT_MIN_COLUMN_WIDTH in queries.ts). */
|
|
800
|
+
.ds-chat-widget__rails[data-artifact-sized] > .ds-chat-widget__artifact-rail--open {
|
|
801
|
+
flex: 0 1 var(--ds-chat-artifact-width);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
.ds-chat-widget__rails[data-artifact-sized] > .ds-chat-widget {
|
|
805
|
+
flex: 1 0 320px;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
/* The pointer drives the width frame by frame, so the glides stand down
|
|
809
|
+
and nothing under the drag selects. */
|
|
810
|
+
.ds-chat-widget__rails[data-artifact-resizing] {
|
|
811
|
+
user-select: none;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
.ds-chat-widget__rails[data-artifact-resizing] > .ds-chat-widget__artifact-rail {
|
|
815
|
+
transition: none;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
/* The grip: the dock's widen grip, restated for the card's leading edge. A
|
|
819
|
+
20px grab zone straddling the border, with a hover-revealed 4×48px pill.
|
|
820
|
+
The seat itself has no width, so the row's layout never knows it is
|
|
821
|
+
there; the zone spans the card's own height. */
|
|
822
|
+
.ds-chat-widget__artifact-grip {
|
|
823
|
+
position: relative;
|
|
824
|
+
z-index: 4;
|
|
825
|
+
flex: none;
|
|
826
|
+
width: 0;
|
|
827
|
+
margin: calc(var(--padding-200) + var(--padding-150) * 2 + 40px) 0 var(--padding-500) 0;
|
|
828
|
+
cursor: ew-resize;
|
|
829
|
+
touch-action: none;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
.ds-chat-widget__artifact-grip::before {
|
|
833
|
+
content: "";
|
|
834
|
+
position: absolute;
|
|
835
|
+
top: 0;
|
|
836
|
+
bottom: 0;
|
|
837
|
+
left: -10px;
|
|
838
|
+
width: 20px;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
.ds-chat-widget__artifact-grip::after {
|
|
842
|
+
content: "";
|
|
843
|
+
position: absolute;
|
|
844
|
+
top: calc(50% - 24px);
|
|
845
|
+
left: -2px;
|
|
846
|
+
width: 4px;
|
|
847
|
+
height: 48px;
|
|
848
|
+
border-radius: var(--radius-pill);
|
|
849
|
+
background: transparent;
|
|
850
|
+
transition: background var(--motion-duration-fast) var(--motion-ease-standard);
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
.ds-chat-widget__artifact-grip:hover::after,
|
|
854
|
+
.ds-chat-widget__artifact-grip--resizing::after {
|
|
855
|
+
background: var(--color-text-tertiary);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
.ds-chat-widget__artifact-grip:active::after {
|
|
859
|
+
background: var(--color-text-secondary);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
/* Expanded, the artifact has the widget: the conversation and the history
|
|
863
|
+
leave the row (and with them the tab order), and the card takes an inset
|
|
864
|
+
on its leading edge too. */
|
|
865
|
+
.ds-chat-widget__rails[data-artifact-expanded] > .ds-chat-widget,
|
|
866
|
+
.ds-chat-widget__rails[data-artifact-expanded] > .ds-chat-widget__threads-rail {
|
|
867
|
+
display: none;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
.ds-chat-widget__rails[data-artifact-expanded] > .ds-chat-widget__artifact-rail--open {
|
|
871
|
+
flex: 1 1 0;
|
|
872
|
+
margin-left: var(--padding-500);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/* The sheet runs nearly the widget's height: a preview has no natural
|
|
876
|
+
height to hug, and a short frame is no preview. The strip left above it
|
|
877
|
+
keeps the header, and so the way back, in view. */
|
|
878
|
+
.ds-chat-widget__aside-sheet--artifact {
|
|
879
|
+
height: 92%;
|
|
880
|
+
max-height: none;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
/* The room the floating controls take in the header row while no panel is
|
|
884
|
+
up: as many 40px controls as the cluster holds, a gap between each. */
|
|
885
|
+
.ds-chat-widget__actions-spacer {
|
|
886
|
+
flex: none;
|
|
887
|
+
width: calc(
|
|
888
|
+
var(--ds-chat-action-count) * 40px + (var(--ds-chat-action-count) - 1) * var(--gap-100)
|
|
889
|
+
);
|
|
890
|
+
height: 40px;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/* The widget's controls at the card's own top trailing corner, their one
|
|
894
|
+
home wherever a trailing panel can seat inline. */
|
|
708
895
|
.ds-chat-widget__rail-actions {
|
|
709
896
|
position: absolute;
|
|
710
897
|
top: calc(var(--padding-200) + var(--padding-150));
|
|
@@ -726,8 +913,8 @@
|
|
|
726
913
|
display: flex;
|
|
727
914
|
flex-direction: column;
|
|
728
915
|
max-height: 82%;
|
|
729
|
-
|
|
730
|
-
|
|
916
|
+
/* The same solid ground the panel has inline: one panel, two seats. */
|
|
917
|
+
background-color: var(--color-bg-page-primary);
|
|
731
918
|
border-top: var(--border-025) solid var(--color-bg-container-border);
|
|
732
919
|
border-radius: var(--radius-600) var(--radius-600) 0 0;
|
|
733
920
|
box-shadow: var(--shadow-floating);
|
package/chat/ChatWidget.d.ts
CHANGED
|
@@ -84,6 +84,8 @@ export interface ChatWidgetProps {
|
|
|
84
84
|
}) => ReactNode;
|
|
85
85
|
/** The built-in agent pane: who the agent is and what it has been doing. Seated like `aside`. */
|
|
86
86
|
agent?: ChatAgentOptions;
|
|
87
|
+
/** Seat the artifact panel: a reply that carries an artifact draws a tile in the thread, and the tile (or the header's artifact button) opens the artifact beside the conversation on a wide host and as a bottom sheet on a narrow one. The trailing seat holds one panel at a time, so an open artifact stands in for the agent pane. Off, none of it exists: no header button, no tile, no panel. */
|
|
88
|
+
artifacts?: boolean;
|
|
87
89
|
/** The composer's context note, naming what the chat is looking at. Omit for none. */
|
|
88
90
|
contextLabel?: string | null;
|
|
89
91
|
/** A widget slot on the welcome screen, under the composer and its
|
|
@@ -110,4 +112,4 @@ export interface ChatWidgetProps {
|
|
|
110
112
|
* a stage card, a whole page). It reads all of its state from ChatProvider,
|
|
111
113
|
* so the conversation is the same object wherever it shows.
|
|
112
114
|
*/
|
|
113
|
-
export declare function ChatWidget({ fullscreenEnabled, closeEnabled, placementEnabled, compact, phone, title, placeholder, showStarters, logo, tagline, starters: startersInput, startersKey, composerActions, models, defaultModel, lockedModelDescription, fileAttachments, maxAttachments, maxAttachmentBytes, mentions, threads, threadsProps, tabs, aside, agent, contextLabel, widgets, widgetsLayout, widgetsWidth, disclaimer, }: ChatWidgetProps): import("react").JSX.Element;
|
|
115
|
+
export declare function ChatWidget({ fullscreenEnabled, closeEnabled, placementEnabled, compact, phone, title, placeholder, showStarters, logo, tagline, starters: startersInput, startersKey, composerActions, models, defaultModel, lockedModelDescription, fileAttachments, maxAttachments, maxAttachmentBytes, mentions, threads, threadsProps, tabs, aside, agent, artifacts, contextLabel, widgets, widgetsLayout, widgetsWidth, disclaimer, }: ChatWidgetProps): import("react").JSX.Element;
|