@agentforge-io/chat-sdk 2.4.0-dev.4 → 2.4.0-dev.5
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/dist/ChatDrawer.d.ts +26 -44
- package/dist/ChatDrawer.js +46 -111
- package/package.json +3 -8
package/dist/ChatDrawer.d.ts
CHANGED
|
@@ -1,41 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `<ChatDrawer>` —
|
|
2
|
+
* `<ChatDrawer>` — fullscreen mobile chat shell.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Plain `position: fixed` modal. No Vaul, no Radix Dialog, no focus
|
|
5
|
+
* trap. Those libraries fought us repeatedly: Radix's `onPointerDownOutside`
|
|
6
|
+
* mis-classified the Send tap as "interact outside" and closed the
|
|
7
|
+
* drawer; Radix's focus trap moved focus to the wrapper when the send
|
|
8
|
+
* button disabled, collapsing the mobile keyboard.
|
|
6
9
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* scroll, address-bar transitions, autocorrect bar over the keyboard,
|
|
17
|
-
* focus loss on send button disable) are not. Vaul handles all of
|
|
18
|
-
* them (used by Linear, Vercel, etc.). It's declared as an OPTIONAL
|
|
19
|
-
* peerDependency so the SDK stays portable — if a host doesn't install
|
|
20
|
-
* Vaul, `<ChatDrawer>` falls back to a render that asks the host to
|
|
21
|
-
* install it. Most hosts already have Vaul (Radix design system).
|
|
22
|
-
*
|
|
23
|
-
* Why fullscreen (not snap points): the operator decision was that a
|
|
24
|
-
* conversation should occupy the whole device. No drag-to-dismiss,
|
|
25
|
-
* no peek of the page behind. Close via the explicit back button in
|
|
26
|
-
* the header. This is the "modal" feel — predictable, no accidental
|
|
27
|
-
* dismissals from a fast scroll.
|
|
28
|
-
*
|
|
29
|
-
* Why we still don't manage the chat session: the SDK's `ChatWidget`
|
|
30
|
-
* owns that, this component is purely the surround.
|
|
10
|
+
* What this gives us:
|
|
11
|
+
* - 100dvh (dynamic viewport height): the surface shrinks automatically
|
|
12
|
+
* when the on-screen keyboard appears, so the composer stays above
|
|
13
|
+
* the keys without any visualViewport bookkeeping.
|
|
14
|
+
* - No focus interception: the textarea owns its own focus. As long
|
|
15
|
+
* as the host's Send button uses `onPointerDown preventDefault`,
|
|
16
|
+
* focus never leaves the textarea and the keyboard never collapses.
|
|
17
|
+
* - No drag-to-dismiss, no click-outside, no ESC handling. The host
|
|
18
|
+
* closes via the back chevron in its header.
|
|
31
19
|
*/
|
|
32
20
|
import { type CSSProperties, type ReactNode } from 'react';
|
|
33
21
|
import { type ChatWidgetProps } from './react';
|
|
34
|
-
/**
|
|
35
|
-
* Two intake shapes — either drop in a pre-built `<ChatWidget>` as
|
|
36
|
-
* `chatSlot` (most hosts) or hand us the widget props and the drawer
|
|
37
|
-
* mounts the widget itself.
|
|
38
|
-
*/
|
|
39
22
|
type ChatSurface = {
|
|
40
23
|
widgetProps: ChatWidgetProps;
|
|
41
24
|
chatSlot?: never;
|
|
@@ -46,22 +29,21 @@ type ChatSurface = {
|
|
|
46
29
|
export type ChatDrawerProps = ChatSurface & {
|
|
47
30
|
/** Controlled visibility. */
|
|
48
31
|
open: boolean;
|
|
49
|
-
/** Fired when the drawer wants to close
|
|
50
|
-
*
|
|
32
|
+
/** Fired when the drawer wants to close. Today only the host's back
|
|
33
|
+
* chevron emits this — no auto-dismiss paths exist. Kept in the
|
|
34
|
+
* shape so the host doesn't have to change call sites. */
|
|
51
35
|
onOpenChange: (open: boolean) => void;
|
|
52
|
-
/** Sticky header above the chat panel.
|
|
53
|
-
* team identity card here. Optional — when omitted the drawer
|
|
54
|
-
* renders just the close button. */
|
|
36
|
+
/** Sticky header above the chat panel. */
|
|
55
37
|
header?: ReactNode;
|
|
56
|
-
/** Custom close button
|
|
57
|
-
*
|
|
38
|
+
/** Custom close button rendered ABOVE `header`. Default is the SDK's
|
|
39
|
+
* chevron-left. Pass `null` to opt out (when the host renders its
|
|
40
|
+
* own close affordance inline within `header`). */
|
|
58
41
|
closeButton?: ReactNode;
|
|
59
|
-
/** Extra class on the drawer surface
|
|
42
|
+
/** Extra class on the drawer surface. */
|
|
60
43
|
drawerClassName?: string;
|
|
61
|
-
/** CSS
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* here so the chat surface theme matches. */
|
|
44
|
+
/** CSS vars (`--af-bg`, `--af-fg`, `--af-bubble-*`, …) for the
|
|
45
|
+
* surface. The drawer renders into a portal, so the host's
|
|
46
|
+
* page-wrapper vars don't cascade in — re-declare them here. */
|
|
65
47
|
surfaceStyle?: CSSProperties & Record<string, string>;
|
|
66
48
|
};
|
|
67
49
|
export declare function ChatDrawer(props: ChatDrawerProps): JSX.Element | null;
|
package/dist/ChatDrawer.js
CHANGED
|
@@ -3,59 +3,36 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ChatDrawer = ChatDrawer;
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
/**
|
|
6
|
-
* `<ChatDrawer>` —
|
|
6
|
+
* `<ChatDrawer>` — fullscreen mobile chat shell.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* Plain `position: fixed` modal. No Vaul, no Radix Dialog, no focus
|
|
9
|
+
* trap. Those libraries fought us repeatedly: Radix's `onPointerDownOutside`
|
|
10
|
+
* mis-classified the Send tap as "interact outside" and closed the
|
|
11
|
+
* drawer; Radix's focus trap moved focus to the wrapper when the send
|
|
12
|
+
* button disabled, collapsing the mobile keyboard.
|
|
10
13
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* scroll, address-bar transitions, autocorrect bar over the keyboard,
|
|
21
|
-
* focus loss on send button disable) are not. Vaul handles all of
|
|
22
|
-
* them (used by Linear, Vercel, etc.). It's declared as an OPTIONAL
|
|
23
|
-
* peerDependency so the SDK stays portable — if a host doesn't install
|
|
24
|
-
* Vaul, `<ChatDrawer>` falls back to a render that asks the host to
|
|
25
|
-
* install it. Most hosts already have Vaul (Radix design system).
|
|
26
|
-
*
|
|
27
|
-
* Why fullscreen (not snap points): the operator decision was that a
|
|
28
|
-
* conversation should occupy the whole device. No drag-to-dismiss,
|
|
29
|
-
* no peek of the page behind. Close via the explicit back button in
|
|
30
|
-
* the header. This is the "modal" feel — predictable, no accidental
|
|
31
|
-
* dismissals from a fast scroll.
|
|
32
|
-
*
|
|
33
|
-
* Why we still don't manage the chat session: the SDK's `ChatWidget`
|
|
34
|
-
* owns that, this component is purely the surround.
|
|
14
|
+
* What this gives us:
|
|
15
|
+
* - 100dvh (dynamic viewport height): the surface shrinks automatically
|
|
16
|
+
* when the on-screen keyboard appears, so the composer stays above
|
|
17
|
+
* the keys without any visualViewport bookkeeping.
|
|
18
|
+
* - No focus interception: the textarea owns its own focus. As long
|
|
19
|
+
* as the host's Send button uses `onPointerDown preventDefault`,
|
|
20
|
+
* focus never leaves the textarea and the keyboard never collapses.
|
|
21
|
+
* - No drag-to-dismiss, no click-outside, no ESC handling. The host
|
|
22
|
+
* closes via the back chevron in its header.
|
|
35
23
|
*/
|
|
36
24
|
const react_1 = require("react");
|
|
37
25
|
const react_2 = require("./react");
|
|
38
|
-
// Lazy import of Vaul. We can't `import { Drawer } from 'vaul'` at
|
|
39
|
-
// the top of the file because vaul is an OPTIONAL peerDependency
|
|
40
|
-
// and the package shouldn't crash at import-time when the host
|
|
41
|
-
// hasn't installed it. We resolve at module evaluation but inside a
|
|
42
|
-
// try/catch so the missing-vaul case is just a runtime "please
|
|
43
|
-
// install vaul" warning instead of a build break.
|
|
44
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
-
let VaulDrawer = null;
|
|
46
|
-
try {
|
|
47
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
48
|
-
VaulDrawer = require('vaul').Drawer;
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
// vaul not installed — render a fallback in the component.
|
|
52
|
-
}
|
|
53
26
|
function ChatDrawer(props) {
|
|
54
27
|
const { open, onOpenChange, header, closeButton, drawerClassName, surfaceStyle, widgetProps, chatSlot, } = props;
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
28
|
+
// Defer the first paint until after mount so SSR doesn't try to
|
|
29
|
+
// render a portal target that doesn't exist yet.
|
|
30
|
+
const [mounted, setMounted] = (0, react_1.useState)(false);
|
|
31
|
+
(0, react_1.useEffect)(() => {
|
|
32
|
+
setMounted(true);
|
|
33
|
+
}, []);
|
|
34
|
+
// Lock body scroll while the drawer is open. Standard pattern: save
|
|
35
|
+
// the previous overflow, set hidden, restore on close/unmount.
|
|
59
36
|
(0, react_1.useEffect)(() => {
|
|
60
37
|
if (typeof document === 'undefined')
|
|
61
38
|
return;
|
|
@@ -67,73 +44,31 @@ function ChatDrawer(props) {
|
|
|
67
44
|
document.body.style.overflow = prev;
|
|
68
45
|
};
|
|
69
46
|
}, [open]);
|
|
70
|
-
if (!
|
|
71
|
-
// Host hasn't installed vaul. We log a one-time warning and
|
|
72
|
-
// render nothing. Most hosts that hit this never wanted the
|
|
73
|
-
// drawer in the first place (they're using ChatWidget inline).
|
|
74
|
-
if (open && typeof console !== 'undefined') {
|
|
75
|
-
console.warn('[@agentforge-io/chat-sdk] <ChatDrawer> requires `vaul` to render. ' +
|
|
76
|
-
'Install it with `npm install vaul` or `yarn add vaul`. ' +
|
|
77
|
-
'The drawer is a no-op until vaul is available.');
|
|
78
|
-
}
|
|
47
|
+
if (!mounted || !open)
|
|
79
48
|
return null;
|
|
80
|
-
}
|
|
81
49
|
const chatNode = chatSlot ?? (widgetProps ? ((0, jsx_runtime_1.jsx)(react_2.ChatWidget, { ...widgetProps, inline: true, variant: widgetProps.variant ?? 'bare' })) : null);
|
|
82
|
-
return ((0, jsx_runtime_1.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
flexDirection: 'column',
|
|
105
|
-
outline: 'none',
|
|
106
|
-
backgroundColor: 'var(--af-bg, #ffffff)',
|
|
107
|
-
color: 'var(--af-fg, inherit)',
|
|
108
|
-
// Re-apply the host's theme vars on the portalled
|
|
109
|
-
// surface so the chat widget below picks them up. The
|
|
110
|
-
// `--af-bg` declared here is what the chat panel reads
|
|
111
|
-
// for its message background; without this the drawer
|
|
112
|
-
// would strobe white over a dark themed page.
|
|
113
|
-
...surfaceStyle,
|
|
114
|
-
}, children: [(0, jsx_runtime_1.jsx)(VaulDrawer.Title, { style: {
|
|
115
|
-
position: 'absolute',
|
|
116
|
-
width: '1px',
|
|
117
|
-
height: '1px',
|
|
118
|
-
padding: 0,
|
|
119
|
-
margin: '-1px',
|
|
120
|
-
overflow: 'hidden',
|
|
121
|
-
clip: 'rect(0, 0, 0, 0)',
|
|
122
|
-
whiteSpace: 'nowrap',
|
|
123
|
-
border: 0,
|
|
124
|
-
}, children: "Chat" }), (0, jsx_runtime_1.jsxs)("div", { style: {
|
|
125
|
-
flexShrink: 0,
|
|
126
|
-
position: 'sticky',
|
|
127
|
-
top: 0,
|
|
128
|
-
zIndex: 1,
|
|
129
|
-
backgroundColor: 'var(--af-bg, #ffffff)',
|
|
130
|
-
borderBottom: '1px solid var(--af-border, rgba(15, 23, 42, 0.08))',
|
|
131
|
-
}, children: [closeButton === undefined ? ((0, jsx_runtime_1.jsx)(DefaultCloseButton, { onClose: () => onOpenChange(false) })) : (closeButton), header] }), (0, jsx_runtime_1.jsx)("div", { style: {
|
|
132
|
-
flex: 1,
|
|
133
|
-
minHeight: 0,
|
|
134
|
-
display: 'flex',
|
|
135
|
-
flexDirection: 'column',
|
|
136
|
-
}, "data-af-drawer-body": true, children: chatNode })] })] }) }));
|
|
50
|
+
return ((0, jsx_runtime_1.jsxs)("div", { role: "dialog", "aria-modal": "true", "aria-label": "Chat", className: `af-drawer-surface ${drawerClassName ?? ''}`, style: {
|
|
51
|
+
// 100dvh shrinks when the on-screen keyboard appears.
|
|
52
|
+
// `inset: 0` + `position: fixed` covers the viewport.
|
|
53
|
+
position: 'fixed',
|
|
54
|
+
inset: 0,
|
|
55
|
+
height: '100dvh',
|
|
56
|
+
zIndex: 2147483600,
|
|
57
|
+
display: 'flex',
|
|
58
|
+
flexDirection: 'column',
|
|
59
|
+
backgroundColor: 'var(--af-bg, #ffffff)',
|
|
60
|
+
color: 'var(--af-fg, inherit)',
|
|
61
|
+
...surfaceStyle,
|
|
62
|
+
}, children: [(0, jsx_runtime_1.jsxs)("div", { style: {
|
|
63
|
+
flexShrink: 0,
|
|
64
|
+
backgroundColor: 'var(--af-bg, #ffffff)',
|
|
65
|
+
borderBottom: '1px solid var(--af-border, rgba(15, 23, 42, 0.08))',
|
|
66
|
+
}, children: [closeButton === undefined ? ((0, jsx_runtime_1.jsx)(DefaultCloseButton, { onClose: () => onOpenChange(false) })) : (closeButton), header] }), (0, jsx_runtime_1.jsx)("div", { style: {
|
|
67
|
+
flex: 1,
|
|
68
|
+
minHeight: 0,
|
|
69
|
+
display: 'flex',
|
|
70
|
+
flexDirection: 'column',
|
|
71
|
+
}, "data-af-drawer-body": true, children: chatNode })] }));
|
|
137
72
|
}
|
|
138
73
|
function DefaultCloseButton({ onClose }) {
|
|
139
74
|
return ((0, jsx_runtime_1.jsx)("div", { style: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentforge-io/chat-sdk",
|
|
3
|
-
"version": "2.4.0-dev.
|
|
3
|
+
"version": "2.4.0-dev.5",
|
|
4
4
|
"description": "Framework-free chat session SDK for AgentForge public chat tokens. Headless — no DOM. Drop into any frontend (React, Vue, Svelte, vanilla) and listen for events.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -24,15 +24,11 @@
|
|
|
24
24
|
"clean": "rm -rf dist *.tgz"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
|
-
"react": ">=18.0.0"
|
|
28
|
-
"vaul": ">=1.0.0"
|
|
27
|
+
"react": ">=18.0.0"
|
|
29
28
|
},
|
|
30
29
|
"peerDependenciesMeta": {
|
|
31
30
|
"react": {
|
|
32
31
|
"optional": true
|
|
33
|
-
},
|
|
34
|
-
"vaul": {
|
|
35
|
-
"optional": true
|
|
36
32
|
}
|
|
37
33
|
},
|
|
38
34
|
"devDependencies": {
|
|
@@ -40,7 +36,6 @@
|
|
|
40
36
|
"@types/react": "^18.3.28",
|
|
41
37
|
"react": "^18.3.1",
|
|
42
38
|
"react-dom": "^18.3.1",
|
|
43
|
-
"typescript": "^5.0.0"
|
|
44
|
-
"vaul": "^1.1.2"
|
|
39
|
+
"typescript": "^5.0.0"
|
|
45
40
|
}
|
|
46
41
|
}
|