rift-ds 1.7.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 +31 -8
- package/bin/rift-ds.mjs +142 -29
- package/bin/templates/chat/.env.chat.example +21 -0
- package/bin/templates/chat/app/api/chat/feedback/route.ts +12 -0
- package/bin/templates/chat/app/api/chat/followups/route.ts +23 -0
- package/bin/templates/chat/app/api/chat/guardrails.ts +18 -0
- package/bin/templates/chat/app/api/chat/route.ts +31 -0
- package/bin/templates/chat/components/chat/ChatMount.tsx +52 -0
- package/bin/templates/chat/components/chat/models.ts +29 -0
- package/chat/AssistantTurn.d.ts +5 -0
- package/chat/AssistantTurn.js +82 -0
- package/chat/ChatAgentPane.css +56 -0
- package/chat/ChatAgentPane.d.ts +24 -0
- package/chat/ChatAgentPane.js +28 -0
- 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/ChatLauncher.d.ts +64 -0
- package/chat/ChatLauncher.js +347 -0
- package/chat/ChatProvider.d.ts +55 -0
- package/chat/ChatProvider.js +196 -0
- package/chat/ChatTabs.d.ts +13 -0
- package/chat/ChatTabs.js +24 -0
- package/chat/ChatThreads.d.ts +26 -0
- package/chat/ChatThreads.js +55 -0
- package/chat/ChatWidget.css +1259 -0
- package/chat/ChatWidget.d.ts +115 -0
- package/chat/ChatWidget.js +709 -0
- package/chat/DemoWelcome.d.ts +13 -0
- package/chat/DemoWelcome.js +131 -0
- package/chat/FollowupSuggestions.d.ts +23 -0
- package/chat/FollowupSuggestions.js +34 -0
- package/chat/ResponseActions.d.ts +18 -0
- package/chat/ResponseActions.js +79 -0
- package/chat/TurnContent.d.ts +11 -0
- package/chat/TurnContent.js +19 -0
- package/chat/artifacts.d.ts +5 -0
- package/chat/artifacts.js +14 -0
- package/chat/context.d.ts +82 -0
- package/chat/context.js +29 -0
- package/chat/demo-agent-panes.d.ts +5 -0
- package/chat/demo-agent-panes.js +49 -0
- package/chat/demo-agent.d.ts +3 -0
- package/chat/demo-agent.js +28 -0
- package/chat/demo-artifacts.d.ts +6 -0
- package/chat/demo-artifacts.js +80 -0
- package/chat/demo-card-components.d.ts +48 -0
- package/chat/demo-card-components.js +66 -0
- package/chat/demo-cards.d.ts +32 -0
- package/chat/demo-cards.js +59 -0
- package/chat/demo-fixtures.d.ts +20 -0
- package/chat/demo-fixtures.js +113 -0
- package/chat/demo-script.d.ts +6 -0
- package/chat/demo-script.js +237 -0
- package/chat/demo.css +52 -0
- package/chat/demo.d.ts +10 -0
- package/chat/demo.js +28 -0
- package/chat/followups.d.ts +41 -0
- package/chat/followups.js +55 -0
- package/chat/greeting.d.ts +11 -0
- package/chat/greeting.js +21 -0
- package/chat/index.d.ts +37 -0
- package/chat/index.js +90 -0
- package/chat/placement.d.ts +48 -0
- package/chat/placement.js +39 -0
- package/chat/queries.d.ts +18 -0
- package/chat/queries.js +26 -0
- package/chat/registry.d.ts +25 -0
- package/chat/registry.js +9 -0
- package/chat/registry.json.d.ts +74 -0
- package/chat/registry.json.js +11 -0
- package/chat/server.d.ts +217 -0
- package/chat/server.js +488 -0
- package/chat/sim.d.ts +71 -0
- package/chat/sim.js +125 -0
- package/chat/starters.d.ts +15 -0
- package/chat/starters.js +38 -0
- package/chat/suggestions.d.ts +22 -0
- package/chat/suggestions.js +9 -0
- package/chat/threads.d.ts +73 -0
- package/chat/threads.js +270 -0
- package/chat/transport.d.ts +49 -0
- package/chat/transport.js +105 -0
- package/chat/types.d.ts +246 -0
- package/chat/types.js +18 -0
- package/chat/upstash.d.ts +63 -0
- package/chat/upstash.js +147 -0
- package/chat/useChat.d.ts +64 -0
- package/chat/useChat.js +241 -0
- package/cli/templates/chat/app/api/chat/feedback/route.d.ts +3 -0
- package/cli/templates/chat/app/api/chat/followups/route.d.ts +3 -0
- package/cli/templates/chat/app/api/chat/guardrails.d.ts +10 -0
- package/cli/templates/chat/app/api/chat/route.d.ts +4 -0
- package/cli/templates/chat/components/chat/ChatMount.d.ts +1 -0
- package/cli/templates/chat/components/chat/models.d.ts +11 -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/ShaderField/ShaderField.d.ts +3 -2
- package/components/ShaderField/useShaderField.d.ts +2 -1
- package/components/ShaderField/useShaderField.js +6 -6
- 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/fonts/material-symbols.css +2 -1
- package/index.d.ts +2 -0
- package/index.js +4 -0
- package/package.json +39 -2
- package/tokens/presets/coral.css +18 -2
- package/tokens/presets/forest.css +18 -2
- package/tokens/presets/gold.css +18 -2
- package/tokens/presets/mono.css +18 -2
- package/tokens/presets/pink.css +18 -2
- package/tokens/presets/terminal.css +18 -2
- package/tokens/presets/violet.css +18 -2
- package/tokens/presets/warm.css +18 -2
- package/tokens/presets/zest.css +18 -2
- package/tokens/registry.json +6 -0
- package/tokens/registry.json.d.ts +6 -0
- package/tokens/registry.json.js +1 -1
- package/tokens/tokens-dark.css +10 -0
- package/tokens/tokens-light.css +10 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/* The built-in agent pane. The rail owns its shell; this only says how it
|
|
2
|
+
sits in the widget's bottom sheet, where it fills the width rather than
|
|
3
|
+
holding its own column and the sheet's rounded top edge is the surface. */
|
|
4
|
+
.ds-chat-agent-pane--sheet {
|
|
5
|
+
width: 100%;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/* The pane furniture a host's panes commonly carry: a date heading over a
|
|
9
|
+
run of rows, and a small card. Host content, offered as classes rather
|
|
10
|
+
than components so a pane stays whatever the host writes. */
|
|
11
|
+
.ds-chat-agent-pane__group {
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
gap: var(--gap-100);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.ds-chat-agent-pane__group + .ds-chat-agent-pane__group,
|
|
18
|
+
.ds-chat-agent-pane__card + .ds-chat-agent-pane__group {
|
|
19
|
+
margin-top: var(--gap-400);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.ds-chat-agent-pane__heading {
|
|
23
|
+
font-family: var(--font-overline-family);
|
|
24
|
+
font-size: var(--font-overline-size);
|
|
25
|
+
font-weight: var(--font-overline-weight);
|
|
26
|
+
line-height: var(--font-overline-line-height);
|
|
27
|
+
letter-spacing: var(--font-overline-letter-spacing);
|
|
28
|
+
text-transform: uppercase;
|
|
29
|
+
color: var(--color-text-tertiary);
|
|
30
|
+
padding: var(--padding-100) var(--padding-200);
|
|
31
|
+
margin: 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.ds-chat-agent-pane__card {
|
|
35
|
+
display: flex;
|
|
36
|
+
flex-direction: column;
|
|
37
|
+
gap: var(--gap-200);
|
|
38
|
+
padding: var(--padding-300);
|
|
39
|
+
background-color: var(--color-bg-container-secondary);
|
|
40
|
+
border-radius: var(--radius-400);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.ds-chat-agent-pane__card-row {
|
|
44
|
+
display: flex;
|
|
45
|
+
align-items: center;
|
|
46
|
+
justify-content: space-between;
|
|
47
|
+
gap: var(--gap-300);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.ds-chat-agent-pane__card-label {
|
|
51
|
+
font-family: var(--font-paragraph-sm-emphasis-family);
|
|
52
|
+
font-size: var(--font-paragraph-sm-emphasis-size);
|
|
53
|
+
font-weight: var(--font-paragraph-sm-emphasis-weight);
|
|
54
|
+
line-height: var(--font-paragraph-sm-emphasis-line-height);
|
|
55
|
+
color: var(--color-text-primary);
|
|
56
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { AgentRailProfile, AgentRailTab } from '../components/AgentRail/AgentRail';
|
|
2
|
+
export interface ChatAgentPaneProps {
|
|
3
|
+
/** The agent this pane belongs to: the portrait block at the top. */
|
|
4
|
+
profile: AgentRailProfile;
|
|
5
|
+
/** The panes, in display order: activity, approvals, automations, personalisation, or whatever the agent has. */
|
|
6
|
+
tabs: AgentRailTab[];
|
|
7
|
+
/** The tab on stage, when the host owns the state; otherwise the pane keeps its own. */
|
|
8
|
+
activeTab?: string;
|
|
9
|
+
/** Fires with the chosen tab's id. */
|
|
10
|
+
onTabChange?: (id: string) => void;
|
|
11
|
+
/** The widget seated the pane as a bottom sheet, where it fills the width instead of holding its own column. */
|
|
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;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The built-in agent pane: an AgentRail the widget seats on the
|
|
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.
|
|
23
|
+
*/
|
|
24
|
+
export declare function ChatAgentPane({ profile, tabs, activeTab, onTabChange, overlay, onClose }: ChatAgentPaneProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { AgentRail } from "../components/AgentRail/AgentRail.js";
|
|
5
|
+
import "./ChatAgentPane.css";
|
|
6
|
+
function ChatAgentPane({ profile, tabs, activeTab, onTabChange, overlay, onClose }) {
|
|
7
|
+
const [ownTab, setOwnTab] = useState(tabs[0]?.id ?? "");
|
|
8
|
+
const current = activeTab ?? ownTab;
|
|
9
|
+
return /* @__PURE__ */ jsx(
|
|
10
|
+
AgentRail,
|
|
11
|
+
{
|
|
12
|
+
className: overlay ? "ds-chat-agent-pane--sheet" : void 0,
|
|
13
|
+
profile,
|
|
14
|
+
tabs,
|
|
15
|
+
activeTab: current,
|
|
16
|
+
onCollapse: onClose,
|
|
17
|
+
collapseVariant: "close",
|
|
18
|
+
collapseLabel: "Close the agent panel",
|
|
19
|
+
onTabChange: (id) => {
|
|
20
|
+
setOwnTab(id);
|
|
21
|
+
onTabChange?.(id);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
ChatAgentPane
|
|
28
|
+
};
|
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { AiButtonSummary } from '../components/AiButton/AiButton';
|
|
3
|
+
import { ChatWidgetProps } from './ChatWidget';
|
|
4
|
+
/** The id the panel carries, for a host control's `aria-controls`. */
|
|
5
|
+
export declare const CHAT_PANEL_ID = "ds-chat-panel";
|
|
6
|
+
export interface ChatLauncherProps {
|
|
7
|
+
/** The floating button's text. */
|
|
8
|
+
label?: string;
|
|
9
|
+
/** The button's icon. */
|
|
10
|
+
icon?: string;
|
|
11
|
+
/**
|
|
12
|
+
* A TLDR panel the button summons on hover: a pre-written summary and
|
|
13
|
+
* prompt chips. A tapped chip opens the chat and sends the chip's
|
|
14
|
+
* `prompt`. Hover-capable devices only: the launcher never renders it on
|
|
15
|
+
* touch, and never in server HTML.
|
|
16
|
+
*/
|
|
17
|
+
summary?: AiButtonSummary & {
|
|
18
|
+
chips?: {
|
|
19
|
+
id: string;
|
|
20
|
+
label: string;
|
|
21
|
+
prompt: string;
|
|
22
|
+
}[];
|
|
23
|
+
};
|
|
24
|
+
/** Render nothing at all: for routes that carry no chrome. */
|
|
25
|
+
hidden?: boolean;
|
|
26
|
+
/** The panel's DOM id, for a host control's `aria-controls`. */
|
|
27
|
+
panelId?: string;
|
|
28
|
+
/**
|
|
29
|
+
* The attribute the launcher writes on `<html>` to say how the panel sits
|
|
30
|
+
* (`docked`, `docked-left`, `takeover`), so the host's stylesheet can pad
|
|
31
|
+
* the page or hide it. `false` writes none.
|
|
32
|
+
*/
|
|
33
|
+
hostAttribute?: string | false;
|
|
34
|
+
/** Accessible name for the panel. */
|
|
35
|
+
panelLabel?: string;
|
|
36
|
+
/** Everything the widget takes. The launcher sets the responsive ones itself. */
|
|
37
|
+
widget?: Omit<ChatWidgetProps, 'fullscreenEnabled' | 'placementEnabled' | 'compact' | 'phone'>;
|
|
38
|
+
/** Rendered inside the panel after the widget: for a host's extra chrome. */
|
|
39
|
+
children?: ReactNode;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The site-wide panel. Mount it once, after the page content, so it sits
|
|
43
|
+
* last in the tab order and never remounts on navigation, which is what
|
|
44
|
+
* lets a mid-stream answer keep streaming while the visitor reads a
|
|
45
|
+
* different page.
|
|
46
|
+
*
|
|
47
|
+
* Docked (wide screens) it is a non-modal complementary region: no scrim,
|
|
48
|
+
* no focus trap, the page stays fully usable beside it. Overlaying (below
|
|
49
|
+
* the threshold, and in fullscreen view) it is modal: scrim, focus trap,
|
|
50
|
+
* body scroll locked, Escape closes.
|
|
51
|
+
*
|
|
52
|
+
* The visitor picks the seat from the header's Chat position menu: either
|
|
53
|
+
* edge for the dock (and for the overlay below the threshold), or floating,
|
|
54
|
+
* a non-modal card that is moved by its header and sized by its edges.
|
|
55
|
+
* Phones get none of it: the takeover is the only form there.
|
|
56
|
+
*
|
|
57
|
+
* The host contract: the launcher sets `hostAttribute` on `<html>` and
|
|
58
|
+
* writes the chosen dock width to `--ds-chat-width` there; a host's
|
|
59
|
+
* stylesheet pads the page beside a docked panel and hides it behind a
|
|
60
|
+
* takeover from those two facts (the package's own stylesheet does not
|
|
61
|
+
* touch the host's body). The panel's geometry reads `--ds-chat-width`,
|
|
62
|
+
* `--ds-chat-inset` and `--ds-chat-float-height`, defaulted on `:root`.
|
|
63
|
+
*/
|
|
64
|
+
export declare function ChatLauncher({ label, icon, summary, hidden, panelId, hostAttribute, panelLabel, widget, children, }: ChatLauncherProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useSyncExternalStore, useLayoutEffect, useRef, useEffect, useState } from "react";
|
|
4
|
+
import { useScrollLock } from "../behaviors/useScrollLock.js";
|
|
5
|
+
import { AiButton } from "../components/AiButton/AiButton.js";
|
|
6
|
+
import { ChatWidget } from "./ChatWidget.js";
|
|
7
|
+
import { useChatWidget } from "./context.js";
|
|
8
|
+
import { isFloatDragStart, FLOAT_EDGE_GAP, FLOAT_MAX_SCALE, dockWidthFromDrag, clampFloatPosition } from "./placement.js";
|
|
9
|
+
import { DOCK_QUERY, TAKEOVER_QUERY } from "./queries.js";
|
|
10
|
+
import "./ChatWidget.css";
|
|
11
|
+
const CHAT_PANEL_ID = "ds-chat-panel";
|
|
12
|
+
const FLOAT_GRIPS = [
|
|
13
|
+
{ dir: "n", dx: 0, dy: -1 },
|
|
14
|
+
{ dir: "s", dx: 0, dy: 1 },
|
|
15
|
+
{ dir: "e", dx: 1, dy: 0 },
|
|
16
|
+
{ dir: "w", dx: -1, dy: 0 },
|
|
17
|
+
{ dir: "ne", dx: 1, dy: -1 },
|
|
18
|
+
{ dir: "nw", dx: -1, dy: -1 },
|
|
19
|
+
{ dir: "se", dx: 1, dy: 1 },
|
|
20
|
+
{ dir: "sw", dx: -1, dy: 1 }
|
|
21
|
+
];
|
|
22
|
+
const viewportBounds = () => ({
|
|
23
|
+
left: 0,
|
|
24
|
+
top: 0,
|
|
25
|
+
right: window.innerWidth,
|
|
26
|
+
bottom: window.innerHeight
|
|
27
|
+
});
|
|
28
|
+
const subscribeQuery = (query) => (onChange) => {
|
|
29
|
+
const media = window.matchMedia(query);
|
|
30
|
+
media.addEventListener("change", onChange);
|
|
31
|
+
return () => media.removeEventListener("change", onChange);
|
|
32
|
+
};
|
|
33
|
+
const subscribeDock = subscribeQuery(DOCK_QUERY);
|
|
34
|
+
const readDocked = () => window.matchMedia(DOCK_QUERY).matches;
|
|
35
|
+
const subscribeTakeover = subscribeQuery(TAKEOVER_QUERY);
|
|
36
|
+
const readTakeover = () => window.matchMedia(TAKEOVER_QUERY).matches;
|
|
37
|
+
function ChatLauncher({
|
|
38
|
+
label = "Ask",
|
|
39
|
+
icon = "forum",
|
|
40
|
+
summary,
|
|
41
|
+
hidden = false,
|
|
42
|
+
panelId = CHAT_PANEL_ID,
|
|
43
|
+
hostAttribute = "data-chat",
|
|
44
|
+
panelLabel = "Chat",
|
|
45
|
+
widget,
|
|
46
|
+
children
|
|
47
|
+
}) {
|
|
48
|
+
const { open, setOpen, panelPhase, view, side, floating, returnFocusRef, send } = useChatWidget();
|
|
49
|
+
const launchSuggestion = (id) => {
|
|
50
|
+
const chip = summary?.chips?.find((c) => c.id === id);
|
|
51
|
+
if (!chip) return;
|
|
52
|
+
setOpen(true);
|
|
53
|
+
send(chip.prompt);
|
|
54
|
+
};
|
|
55
|
+
const docked = useSyncExternalStore(subscribeDock, readDocked, () => false);
|
|
56
|
+
const takeover = useSyncExternalStore(subscribeTakeover, readTakeover, () => false);
|
|
57
|
+
const isFull = view === "full" || takeover;
|
|
58
|
+
const isFloating = floating && !isFull;
|
|
59
|
+
const isDocked = docked && !isFull && !isFloating;
|
|
60
|
+
const modal = open && !hidden && (isFull || !docked && !isFloating);
|
|
61
|
+
const showPanel = open && !hidden;
|
|
62
|
+
const renderPanel = (open || panelPhase === "closing") && !hidden;
|
|
63
|
+
useLayoutEffect(() => {
|
|
64
|
+
if (!hostAttribute) return;
|
|
65
|
+
const root = document.documentElement;
|
|
66
|
+
if (showPanel && takeover) {
|
|
67
|
+
const scrollY = window.scrollY;
|
|
68
|
+
root.setAttribute(hostAttribute, "takeover");
|
|
69
|
+
window.scrollTo({ top: 0, behavior: "instant" });
|
|
70
|
+
return () => {
|
|
71
|
+
root.removeAttribute(hostAttribute);
|
|
72
|
+
window.scrollTo({ top: scrollY, behavior: "instant" });
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (showPanel && isDocked) {
|
|
76
|
+
root.setAttribute(hostAttribute, side === "left" ? "docked-left" : "docked");
|
|
77
|
+
return () => root.removeAttribute(hostAttribute);
|
|
78
|
+
}
|
|
79
|
+
root.removeAttribute(hostAttribute);
|
|
80
|
+
}, [showPanel, isDocked, side, takeover, hostAttribute]);
|
|
81
|
+
useScrollLock(modal && !takeover);
|
|
82
|
+
const panelRef = useRef(null);
|
|
83
|
+
const wasOpen = useRef(false);
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
if (wasOpen.current && !open) {
|
|
86
|
+
const target = returnFocusRef.current;
|
|
87
|
+
if (target && target.isConnected) target.focus();
|
|
88
|
+
}
|
|
89
|
+
wasOpen.current = open;
|
|
90
|
+
}, [open, returnFocusRef]);
|
|
91
|
+
const dragRef = useRef(null);
|
|
92
|
+
const [resizing, setResizing] = useState(false);
|
|
93
|
+
const beginResize = (e) => {
|
|
94
|
+
if (e.pointerType === "mouse" && e.button !== 0 || dragRef.current) return;
|
|
95
|
+
const rect = panelRef.current?.getBoundingClientRect();
|
|
96
|
+
if (!rect) return;
|
|
97
|
+
dragRef.current = { startX: e.clientX, width: rect.width };
|
|
98
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
99
|
+
setResizing(true);
|
|
100
|
+
document.documentElement.setAttribute("data-chat-resizing", "");
|
|
101
|
+
};
|
|
102
|
+
const moveResize = (e) => {
|
|
103
|
+
const drag = dragRef.current;
|
|
104
|
+
if (!drag) return;
|
|
105
|
+
const width = dockWidthFromDrag(drag.width, e.clientX - drag.startX, side);
|
|
106
|
+
document.documentElement.style.setProperty("--ds-chat-width", `${width}px`);
|
|
107
|
+
};
|
|
108
|
+
const endResize = () => {
|
|
109
|
+
dragRef.current = null;
|
|
110
|
+
setResizing(false);
|
|
111
|
+
document.documentElement.removeAttribute("data-chat-resizing");
|
|
112
|
+
};
|
|
113
|
+
const [floatPos, setFloatPos] = useState(null);
|
|
114
|
+
const moveRef = useRef(null);
|
|
115
|
+
const clampFloat = (x, y, rect) => clampFloatPosition(x, y, rect, viewportBounds());
|
|
116
|
+
const beginMove = (e) => {
|
|
117
|
+
if (!isFloating || moveRef.current) return;
|
|
118
|
+
if (e.pointerType === "mouse" && e.button !== 0) return;
|
|
119
|
+
if (!isFloatDragStart(e.target)) return;
|
|
120
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
121
|
+
moveRef.current = { dx: e.clientX - rect.left, dy: e.clientY - rect.top, x: rect.left, y: rect.top };
|
|
122
|
+
try {
|
|
123
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
124
|
+
} catch {
|
|
125
|
+
}
|
|
126
|
+
document.documentElement.setAttribute("data-chat-resizing", "");
|
|
127
|
+
};
|
|
128
|
+
const moveMove = (e) => {
|
|
129
|
+
const move = moveRef.current;
|
|
130
|
+
if (!move) return;
|
|
131
|
+
const node = e.currentTarget;
|
|
132
|
+
const next = clampFloat(e.clientX - move.dx, e.clientY - move.dy, node.getBoundingClientRect());
|
|
133
|
+
move.x = next.x;
|
|
134
|
+
move.y = next.y;
|
|
135
|
+
node.style.setProperty("--ds-chat-float-x", `${next.x}px`);
|
|
136
|
+
node.style.setProperty("--ds-chat-float-y", `${next.y}px`);
|
|
137
|
+
};
|
|
138
|
+
const endMove = () => {
|
|
139
|
+
const move = moveRef.current;
|
|
140
|
+
if (!move) return;
|
|
141
|
+
moveRef.current = null;
|
|
142
|
+
setFloatPos({ x: move.x, y: move.y });
|
|
143
|
+
document.documentElement.removeAttribute("data-chat-resizing");
|
|
144
|
+
};
|
|
145
|
+
const [floatSize, setFloatSize] = useState(null);
|
|
146
|
+
const sizeRef = useRef(null);
|
|
147
|
+
const beginSize = (e, dx, dy) => {
|
|
148
|
+
if (e.pointerType === "mouse" && e.button !== 0 || sizeRef.current) return;
|
|
149
|
+
const rect = panelRef.current?.getBoundingClientRect();
|
|
150
|
+
if (!rect) return;
|
|
151
|
+
e.stopPropagation();
|
|
152
|
+
const root = getComputedStyle(document.documentElement);
|
|
153
|
+
const inset = parseFloat(root.getPropertyValue("--ds-chat-inset")) || 0;
|
|
154
|
+
const minW = parseFloat(root.getPropertyValue("--ds-chat-width")) || rect.width;
|
|
155
|
+
const restH = parseFloat(root.getPropertyValue("--ds-chat-float-height")) || rect.height;
|
|
156
|
+
const minH = Math.min(restH, window.innerHeight - inset * 2);
|
|
157
|
+
sizeRef.current = {
|
|
158
|
+
dx,
|
|
159
|
+
dy,
|
|
160
|
+
startX: e.clientX,
|
|
161
|
+
startY: e.clientY,
|
|
162
|
+
rect,
|
|
163
|
+
minW,
|
|
164
|
+
minH,
|
|
165
|
+
next: { x: rect.left, y: rect.top, w: rect.width, h: rect.height }
|
|
166
|
+
};
|
|
167
|
+
try {
|
|
168
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
169
|
+
} catch {
|
|
170
|
+
}
|
|
171
|
+
document.documentElement.setAttribute("data-chat-resizing", "");
|
|
172
|
+
};
|
|
173
|
+
const moveSize = (e) => {
|
|
174
|
+
const drag = sizeRef.current;
|
|
175
|
+
const node = panelRef.current;
|
|
176
|
+
if (!drag || !node) return;
|
|
177
|
+
const { rect, next } = drag;
|
|
178
|
+
if (drag.dx !== 0) {
|
|
179
|
+
const room = drag.dx > 0 ? window.innerWidth - FLOAT_EDGE_GAP - rect.left : rect.right - FLOAT_EDGE_GAP;
|
|
180
|
+
const max = Math.max(drag.minW, Math.min(drag.minW * FLOAT_MAX_SCALE, room));
|
|
181
|
+
next.w = Math.round(
|
|
182
|
+
Math.min(max, Math.max(drag.minW, rect.width + drag.dx * (e.clientX - drag.startX)))
|
|
183
|
+
);
|
|
184
|
+
next.x = Math.round(drag.dx > 0 ? rect.left : rect.right - next.w);
|
|
185
|
+
}
|
|
186
|
+
if (drag.dy !== 0) {
|
|
187
|
+
const room = drag.dy > 0 ? window.innerHeight - FLOAT_EDGE_GAP - rect.top : rect.bottom - FLOAT_EDGE_GAP;
|
|
188
|
+
const max = Math.max(drag.minH, Math.min(drag.minH * FLOAT_MAX_SCALE, room));
|
|
189
|
+
next.h = Math.round(
|
|
190
|
+
Math.min(max, Math.max(drag.minH, rect.height + drag.dy * (e.clientY - drag.startY)))
|
|
191
|
+
);
|
|
192
|
+
next.y = Math.round(drag.dy > 0 ? rect.top : rect.bottom - next.h);
|
|
193
|
+
}
|
|
194
|
+
node.style.setProperty("--ds-chat-float-x", `${next.x}px`);
|
|
195
|
+
node.style.setProperty("--ds-chat-float-y", `${next.y}px`);
|
|
196
|
+
node.style.setProperty("--ds-chat-float-w", `${next.w}px`);
|
|
197
|
+
node.style.setProperty("--ds-chat-float-h", `${next.h}px`);
|
|
198
|
+
};
|
|
199
|
+
const endSize = () => {
|
|
200
|
+
const drag = sizeRef.current;
|
|
201
|
+
if (!drag) return;
|
|
202
|
+
sizeRef.current = null;
|
|
203
|
+
setFloatPos({ x: drag.next.x, y: drag.next.y });
|
|
204
|
+
setFloatSize({ w: drag.next.w, h: drag.next.h });
|
|
205
|
+
document.documentElement.removeAttribute("data-chat-resizing");
|
|
206
|
+
};
|
|
207
|
+
useEffect(() => {
|
|
208
|
+
if (!isFloating || !floatPos) return;
|
|
209
|
+
const onResize = () => {
|
|
210
|
+
const rect = panelRef.current?.getBoundingClientRect();
|
|
211
|
+
if (!rect) return;
|
|
212
|
+
setFloatPos((pos) => {
|
|
213
|
+
if (!pos) return pos;
|
|
214
|
+
const next = clampFloat(pos.x, pos.y, rect);
|
|
215
|
+
return next.x === pos.x && next.y === pos.y ? pos : next;
|
|
216
|
+
});
|
|
217
|
+
};
|
|
218
|
+
window.addEventListener("resize", onResize);
|
|
219
|
+
return () => window.removeEventListener("resize", onResize);
|
|
220
|
+
}, [isFloating, floatPos]);
|
|
221
|
+
if (hidden) return null;
|
|
222
|
+
if (!renderPanel) {
|
|
223
|
+
return /* @__PURE__ */ jsx("div", { className: "ds-chat-launcher__fab", children: /* @__PURE__ */ jsx(
|
|
224
|
+
AiButton,
|
|
225
|
+
{
|
|
226
|
+
label,
|
|
227
|
+
icon,
|
|
228
|
+
"aria-expanded": false,
|
|
229
|
+
"aria-controls": panelId,
|
|
230
|
+
summary: summary ? {
|
|
231
|
+
overline: summary.overline,
|
|
232
|
+
title: summary.title,
|
|
233
|
+
caption: summary.caption,
|
|
234
|
+
text: summary.text,
|
|
235
|
+
suggestions: summary.suggestions ?? summary.chips?.map(({ id, label: chipLabel }) => ({ id, label: chipLabel }))
|
|
236
|
+
} : void 0,
|
|
237
|
+
onSummarySuggestion: launchSuggestion,
|
|
238
|
+
onClick: (event) => {
|
|
239
|
+
returnFocusRef.current = event.currentTarget;
|
|
240
|
+
setOpen(true);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
) });
|
|
244
|
+
}
|
|
245
|
+
const handleKeyDown = (event) => {
|
|
246
|
+
if (event.key === "Escape") {
|
|
247
|
+
setOpen(false);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (modal && event.key === "Tab" && panelRef.current) {
|
|
251
|
+
const focusables = panelRef.current.querySelectorAll(
|
|
252
|
+
'button:not([disabled]), a[href], textarea:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
|
253
|
+
);
|
|
254
|
+
if (focusables.length === 0) return;
|
|
255
|
+
const first = focusables[0];
|
|
256
|
+
const last = focusables[focusables.length - 1];
|
|
257
|
+
if (event.shiftKey && document.activeElement === first) {
|
|
258
|
+
event.preventDefault();
|
|
259
|
+
last.focus();
|
|
260
|
+
} else if (!event.shiftKey && document.activeElement === last) {
|
|
261
|
+
event.preventDefault();
|
|
262
|
+
first.focus();
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
const panelClass = [
|
|
267
|
+
"ds-chat-launcher__panel",
|
|
268
|
+
side === "left" ? "ds-chat-launcher__panel--left" : "",
|
|
269
|
+
isFloating ? "ds-chat-launcher__panel--floating" : "",
|
|
270
|
+
isFull ? "ds-chat-launcher__panel--full" : "",
|
|
271
|
+
panelPhase === "closing" ? "ds-chat-launcher__panel--closing" : ""
|
|
272
|
+
].filter(Boolean).join(" ");
|
|
273
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
274
|
+
modal && !isFull && /* @__PURE__ */ jsx("div", { className: "ds-chat-launcher__scrim", onClick: () => setOpen(false), "aria-hidden": "true" }),
|
|
275
|
+
/* @__PURE__ */ jsxs(
|
|
276
|
+
"div",
|
|
277
|
+
{
|
|
278
|
+
ref: panelRef,
|
|
279
|
+
id: panelId,
|
|
280
|
+
className: panelClass,
|
|
281
|
+
style: isFloating && (floatPos || floatSize) ? {
|
|
282
|
+
...floatPos && {
|
|
283
|
+
"--ds-chat-float-x": `${floatPos.x}px`,
|
|
284
|
+
"--ds-chat-float-y": `${floatPos.y}px`
|
|
285
|
+
},
|
|
286
|
+
...floatSize && {
|
|
287
|
+
"--ds-chat-float-w": `${floatSize.w}px`,
|
|
288
|
+
"--ds-chat-float-h": `${floatSize.h}px`
|
|
289
|
+
}
|
|
290
|
+
} : void 0,
|
|
291
|
+
onPointerDown: beginMove,
|
|
292
|
+
onPointerMove: moveMove,
|
|
293
|
+
onPointerUp: endMove,
|
|
294
|
+
onPointerCancel: endMove,
|
|
295
|
+
role: modal ? "dialog" : "complementary",
|
|
296
|
+
"aria-modal": modal || void 0,
|
|
297
|
+
"aria-label": panelLabel,
|
|
298
|
+
onKeyDown: handleKeyDown,
|
|
299
|
+
children: [
|
|
300
|
+
/* @__PURE__ */ jsx(
|
|
301
|
+
ChatWidget,
|
|
302
|
+
{
|
|
303
|
+
...widget,
|
|
304
|
+
fullscreenEnabled: !takeover,
|
|
305
|
+
placementEnabled: !takeover,
|
|
306
|
+
compact: takeover || !isFull,
|
|
307
|
+
phone: takeover
|
|
308
|
+
}
|
|
309
|
+
),
|
|
310
|
+
children,
|
|
311
|
+
isFloating && FLOAT_GRIPS.map(({ dir, dx, dy }) => /* @__PURE__ */ jsx(
|
|
312
|
+
"div",
|
|
313
|
+
{
|
|
314
|
+
className: "ds-chat-launcher__float-grip",
|
|
315
|
+
"data-dir": dir,
|
|
316
|
+
"aria-hidden": "true",
|
|
317
|
+
onPointerDown: (e) => beginSize(e, dx, dy),
|
|
318
|
+
onPointerMove: moveSize,
|
|
319
|
+
onPointerUp: endSize,
|
|
320
|
+
onPointerCancel: endSize
|
|
321
|
+
},
|
|
322
|
+
dir
|
|
323
|
+
))
|
|
324
|
+
]
|
|
325
|
+
}
|
|
326
|
+
),
|
|
327
|
+
showPanel && isDocked && /* @__PURE__ */ jsx(
|
|
328
|
+
"div",
|
|
329
|
+
{
|
|
330
|
+
className: [
|
|
331
|
+
"ds-chat-launcher__dock-handle",
|
|
332
|
+
side === "left" ? "ds-chat-launcher__dock-handle--left" : "",
|
|
333
|
+
resizing ? "ds-chat-launcher__dock-handle--resizing" : ""
|
|
334
|
+
].filter(Boolean).join(" "),
|
|
335
|
+
"aria-hidden": "true",
|
|
336
|
+
onPointerDown: beginResize,
|
|
337
|
+
onPointerMove: moveResize,
|
|
338
|
+
onPointerUp: endResize,
|
|
339
|
+
onPointerCancel: endResize
|
|
340
|
+
}
|
|
341
|
+
)
|
|
342
|
+
] });
|
|
343
|
+
}
|
|
344
|
+
export {
|
|
345
|
+
CHAT_PANEL_ID,
|
|
346
|
+
ChatLauncher
|
|
347
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { ChatArtifactRenderers, ChatCardRenderers, ChatLinkRenderer, FeedbackSink } from './context';
|
|
3
|
+
import { FollowupsSource } from './followups';
|
|
4
|
+
import { ChatPlacement } from './placement';
|
|
5
|
+
import { ThreadsOptions } from './threads';
|
|
6
|
+
import { ChatTransport } from './types';
|
|
7
|
+
import { ChatPlaceholderCopy } from './useChat';
|
|
8
|
+
export interface ChatProviderProps {
|
|
9
|
+
/**
|
|
10
|
+
* Where the conversation goes. Defaults to the fetch transport against
|
|
11
|
+
* `endpoint`, carrying the visitor's model pick. A script from
|
|
12
|
+
* `scriptChat()` runs the widget with no backend at all.
|
|
13
|
+
*/
|
|
14
|
+
transport?: ChatTransport;
|
|
15
|
+
/** The endpoint the default transport posts to. */
|
|
16
|
+
endpoint?: string;
|
|
17
|
+
/** The seat the panel opens in. */
|
|
18
|
+
defaultPlacement?: ChatPlacement;
|
|
19
|
+
/**
|
|
20
|
+
* Where follow-up questions come from after an answer commits: a source
|
|
21
|
+
* function, an endpoint for the built-in fetcher, or `false` for none.
|
|
22
|
+
* Defaults to the fetcher against `/api/chat/followups`.
|
|
23
|
+
*/
|
|
24
|
+
followups?: FollowupsSource | string | false;
|
|
25
|
+
/** The written questions offered when the source returns none. */
|
|
26
|
+
fallbackFollowups?: readonly string[];
|
|
27
|
+
/**
|
|
28
|
+
* Where a thumbs verdict goes: a sink function, an endpoint for the
|
|
29
|
+
* built-in poster, or `false` to hide the thumbs. Defaults to the poster
|
|
30
|
+
* against `/api/chat/feedback`. Thumbs only ever show on a turn the
|
|
31
|
+
* server logged (one with an exchange id).
|
|
32
|
+
*/
|
|
33
|
+
feedback?: FeedbackSink | string | false;
|
|
34
|
+
/** Draws `card` events by kind. */
|
|
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;
|
|
38
|
+
/** Renders a same-site link in an answer. The default is a plain anchor; a Next.js host passes its Link. */
|
|
39
|
+
renderLink?: ChatLinkRenderer;
|
|
40
|
+
/** Which hrefs in an answer become links. Everything else renders as text. */
|
|
41
|
+
isInternalHref?: (href: string) => boolean;
|
|
42
|
+
/** Seeds the thread lifecycle behind the `threads` and `tabs` slots. */
|
|
43
|
+
threads?: ThreadsOptions;
|
|
44
|
+
/** Replacement copy for the hook's placeholder replies. */
|
|
45
|
+
placeholders?: Partial<ChatPlaceholderCopy>;
|
|
46
|
+
children: ReactNode;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The chat's whole client state. Mount it once, high in the tree: a root
|
|
50
|
+
* layout never remounts on client-side navigation, so the transcript, a
|
|
51
|
+
* stream in flight, the half-typed draft and the open state survive
|
|
52
|
+
* clicking between pages for free. A hard reload starts clean by design:
|
|
53
|
+
* no storage, no persistence.
|
|
54
|
+
*/
|
|
55
|
+
export declare function ChatProvider({ transport, endpoint, defaultPlacement, followups, fallbackFollowups, feedback, renderers, artifactRenderers, renderLink, isInternalHref, threads: threadsOptions, placeholders, children, }: ChatProviderProps): import("react").JSX.Element;
|