@usereq/widget 0.2.25 → 1.0.0-experimental.1
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 +35 -145
- package/dist/embed.js +38 -0
- package/dist/embed.js.map +1 -0
- package/dist/embed.mjs +3185 -0
- package/dist/embed.mjs.map +1 -0
- package/package.json +35 -81
- package/dist/widget.js +0 -255
- package/src/chat-widget/chat-widget-appearance.ts +0 -134
- package/src/chat-widget/message-reveal.ts +0 -52
- package/src/chat-widget/stop-confirmation.ts +0 -288
- package/src/custom-element/agent-widget-element.tsx +0 -682
- package/src/embed.ts +0 -8
- package/src/index.ts +0 -9
- package/src/register.ts +0 -15
- package/src/renderer/index.ts +0 -6
- package/src/renderer/widget-runtime.tsx +0 -395
- package/src/runtime/api-origin.ts +0 -29
- package/src/runtime/api.ts +0 -123
- package/src/runtime/bootstrap.ts +0 -366
- package/src/runtime/debug.ts +0 -104
- package/src/runtime/messages.ts +0 -12
- package/src/runtime/session-storage.ts +0 -32
- package/src/runtime/trigger-rule.ts +0 -182
- package/src/shared/shadow-css.ts +0 -15
- package/src/shared/shadow-theme.ts +0 -115
- package/src/shared/stop-confirmation.ts +0 -20
- package/src/shared/widget-config.ts +0 -171
- package/src/styles/widget.css.ts +0 -83
- package/src/types.ts +0 -98
- package/src/vite-env.d.ts +0 -4
package/src/register.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { AgentWidgetElement } from "./custom-element/agent-widget-element";
|
|
2
|
-
|
|
3
|
-
declare global {
|
|
4
|
-
interface HTMLElementTagNameMap {
|
|
5
|
-
"usereq-agent-widget": AgentWidgetElement;
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function registerWidgetElement() {
|
|
10
|
-
if (!customElements.get("usereq-agent-widget")) {
|
|
11
|
-
customElements.define("usereq-agent-widget", AgentWidgetElement);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export { AgentWidgetElement };
|
package/src/renderer/index.ts
DELETED
|
@@ -1,395 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
|
|
3
|
-
import { ChatWidget } from "@usereq/ui/components/chat/widget";
|
|
4
|
-
import type {
|
|
5
|
-
ChatPanelConfirmation,
|
|
6
|
-
ChatPanelMessage,
|
|
7
|
-
ChatPanelStatus,
|
|
8
|
-
} from "@usereq/ui/components/chat/panel";
|
|
9
|
-
import { ChatFullscreenProvider } from "@usereq/ui/hooks/use-chat-fullscreen";
|
|
10
|
-
import type { ChatAppearance } from "@usereq/ui/lib/chat-appearance";
|
|
11
|
-
import type { ChatWidgetConfig } from "@usereq/ui/lib/chat-widget-config";
|
|
12
|
-
|
|
13
|
-
import type { WidgetMessage } from "../types";
|
|
14
|
-
import {
|
|
15
|
-
splitWidgetPlacement,
|
|
16
|
-
type WidgetAppearance,
|
|
17
|
-
type WidgetMobileHeight,
|
|
18
|
-
type WidgetPlacement,
|
|
19
|
-
type WidgetPreset,
|
|
20
|
-
type WidgetVariant,
|
|
21
|
-
type WidgetTriggerRule,
|
|
22
|
-
} from "../shared/widget-config";
|
|
23
|
-
import {
|
|
24
|
-
getPendingStopConfirmationId,
|
|
25
|
-
parseStopConfirmationEnvelope,
|
|
26
|
-
DEFAULT_STOP_CONVERSATION_PROMPT,
|
|
27
|
-
STOP_CONFIRMATION_KIND,
|
|
28
|
-
} from "../shared/stop-confirmation";
|
|
29
|
-
import { widgetCss } from "../styles/widget.css";
|
|
30
|
-
import { useWidgetTriggerGate } from "../runtime/trigger-rule";
|
|
31
|
-
|
|
32
|
-
export type WidgetRuntimeMode = "embed" | "preview";
|
|
33
|
-
|
|
34
|
-
export type WidgetRuntimeStatus =
|
|
35
|
-
| "idle"
|
|
36
|
-
| "booting"
|
|
37
|
-
| "ready"
|
|
38
|
-
| "sending"
|
|
39
|
-
| "error";
|
|
40
|
-
|
|
41
|
-
export type WidgetRuntimeProps = {
|
|
42
|
-
mode: WidgetRuntimeMode;
|
|
43
|
-
agentId: string;
|
|
44
|
-
variant: WidgetVariant;
|
|
45
|
-
preset?: WidgetPreset;
|
|
46
|
-
placement: WidgetPlacement;
|
|
47
|
-
appearance: WidgetAppearance;
|
|
48
|
-
agentAvatarUrl: string | null;
|
|
49
|
-
widgetTitle: string;
|
|
50
|
-
welcomeMessage: string | null;
|
|
51
|
-
spotlights: string[];
|
|
52
|
-
ctaLink?: string | null;
|
|
53
|
-
/**
|
|
54
|
-
* How much of the screen the open panel covers on mobile. Ignored on
|
|
55
|
-
* desktop, where the panel floats above the launcher regardless.
|
|
56
|
-
*/
|
|
57
|
-
mobileHeight?: WidgetMobileHeight;
|
|
58
|
-
open: boolean;
|
|
59
|
-
/**
|
|
60
|
-
* When true, the embedded panel is pinned open and the launcher is hidden.
|
|
61
|
-
* Mirrors `WidgetBehavior.alwaysOpen` and short-circuits the trigger-rule
|
|
62
|
-
* gate (a pinned widget has no "open" event to trigger on).
|
|
63
|
-
*/
|
|
64
|
-
alwaysOpen?: boolean;
|
|
65
|
-
status: WidgetRuntimeStatus;
|
|
66
|
-
messages: WidgetMessage[];
|
|
67
|
-
errorMessage: string | null;
|
|
68
|
-
triggerRule?: WidgetTriggerRule | null;
|
|
69
|
-
emptyAction?: { label: string; onClick: () => void; disabled?: boolean };
|
|
70
|
-
sendDisabled?: boolean;
|
|
71
|
-
onOpenChange: (open: boolean) => void;
|
|
72
|
-
onSendMessage: (content: string) => void;
|
|
73
|
-
onStopConversation?: () => void;
|
|
74
|
-
onConfirmationDecision?: (
|
|
75
|
-
confirmationId: string,
|
|
76
|
-
accepted: boolean,
|
|
77
|
-
) => void | Promise<void>;
|
|
78
|
-
portalContainer?: HTMLElement | DocumentFragment | null;
|
|
79
|
-
onLauncherClick?: () => void;
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const RUNTIME_TO_PANEL_STATUS: Record<WidgetRuntimeStatus, ChatPanelStatus> = {
|
|
83
|
-
idle: "idle",
|
|
84
|
-
booting: "booting",
|
|
85
|
-
ready: "idle",
|
|
86
|
-
sending: "sending",
|
|
87
|
-
error: "error",
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
export function WidgetRuntime({
|
|
91
|
-
mode,
|
|
92
|
-
agentId,
|
|
93
|
-
variant,
|
|
94
|
-
preset = "default",
|
|
95
|
-
placement,
|
|
96
|
-
appearance,
|
|
97
|
-
agentAvatarUrl,
|
|
98
|
-
widgetTitle,
|
|
99
|
-
welcomeMessage,
|
|
100
|
-
spotlights,
|
|
101
|
-
ctaLink,
|
|
102
|
-
mobileHeight = "full",
|
|
103
|
-
open,
|
|
104
|
-
alwaysOpen = false,
|
|
105
|
-
status,
|
|
106
|
-
messages,
|
|
107
|
-
errorMessage,
|
|
108
|
-
triggerRule,
|
|
109
|
-
emptyAction,
|
|
110
|
-
sendDisabled = false,
|
|
111
|
-
onOpenChange,
|
|
112
|
-
onSendMessage,
|
|
113
|
-
onStopConversation,
|
|
114
|
-
onConfirmationDecision,
|
|
115
|
-
portalContainer,
|
|
116
|
-
onLauncherClick,
|
|
117
|
-
}: WidgetRuntimeProps) {
|
|
118
|
-
// Always-open widgets bypass the trigger-rule gate — the panel renders
|
|
119
|
-
// unconditionally, so any "open on X seconds / on scroll / on click"
|
|
120
|
-
// configuration would be meaningless. Pass `null` to short-circuit it.
|
|
121
|
-
const { ready } = useWidgetTriggerGate(alwaysOpen ? null : triggerRule);
|
|
122
|
-
const { horizontal, vertical } = splitWidgetPlacement(placement);
|
|
123
|
-
|
|
124
|
-
const { panelMessages, confirmation } = React.useMemo(
|
|
125
|
-
() => splitConfirmationFromMessages(messages, onConfirmationDecision),
|
|
126
|
-
[messages, onConfirmationDecision],
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
const pendingConfirmationId = React.useMemo(
|
|
130
|
-
() => getPendingStopConfirmationId(messages),
|
|
131
|
-
[messages],
|
|
132
|
-
);
|
|
133
|
-
const confirmationPending = pendingConfirmationId != null;
|
|
134
|
-
const stopDisabled =
|
|
135
|
-
confirmationPending || status === "booting" || status === "sending";
|
|
136
|
-
const isSendDisabled =
|
|
137
|
-
sendDisabled || confirmationPending || status === "booting";
|
|
138
|
-
|
|
139
|
-
const chatConfig = React.useMemo<ChatWidgetConfig>(
|
|
140
|
-
() => ({
|
|
141
|
-
agentName: widgetTitle,
|
|
142
|
-
agentAvatarUrl,
|
|
143
|
-
variant: mapVariant(variant),
|
|
144
|
-
preset: mapPreset(preset),
|
|
145
|
-
placement,
|
|
146
|
-
appearance: toChatAppearance(appearance),
|
|
147
|
-
welcomeMessage,
|
|
148
|
-
spotlights,
|
|
149
|
-
ctaLink,
|
|
150
|
-
actionLabel: appearance.actionText || undefined,
|
|
151
|
-
showStopConversation: Boolean(onStopConversation),
|
|
152
|
-
mobileHeight,
|
|
153
|
-
}),
|
|
154
|
-
[
|
|
155
|
-
agentAvatarUrl,
|
|
156
|
-
appearance,
|
|
157
|
-
ctaLink,
|
|
158
|
-
mobileHeight,
|
|
159
|
-
onStopConversation,
|
|
160
|
-
placement,
|
|
161
|
-
preset,
|
|
162
|
-
spotlights,
|
|
163
|
-
variant,
|
|
164
|
-
welcomeMessage,
|
|
165
|
-
widgetTitle,
|
|
166
|
-
],
|
|
167
|
-
);
|
|
168
|
-
|
|
169
|
-
const handleSend = React.useCallback(
|
|
170
|
-
(value: string) => {
|
|
171
|
-
const trimmed = value.trim();
|
|
172
|
-
if (!trimmed || isSendDisabled) return;
|
|
173
|
-
onSendMessage(trimmed);
|
|
174
|
-
},
|
|
175
|
-
[isSendDisabled, onSendMessage],
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
const handleStop = React.useCallback(() => {
|
|
179
|
-
if (stopDisabled) return;
|
|
180
|
-
onStopConversation?.();
|
|
181
|
-
}, [onStopConversation, stopDisabled]);
|
|
182
|
-
|
|
183
|
-
const wrappedEmptyAction = React.useMemo(
|
|
184
|
-
() =>
|
|
185
|
-
emptyAction
|
|
186
|
-
? {
|
|
187
|
-
label: emptyAction.label,
|
|
188
|
-
disabled: emptyAction.disabled,
|
|
189
|
-
onClick: emptyAction.onClick,
|
|
190
|
-
}
|
|
191
|
-
: undefined,
|
|
192
|
-
[emptyAction],
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
if (!ready) {
|
|
196
|
-
return null;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const shellStyle = getWidgetShellStyle(mode, placement);
|
|
200
|
-
// Accept both HTMLElement (e.g. playground stage div) and DocumentFragment
|
|
201
|
-
// (e.g. the widget custom element's ShadowRoot). Radix Dialog handles both
|
|
202
|
-
// as portal containers.
|
|
203
|
-
const portalEl: HTMLElement | DocumentFragment | null =
|
|
204
|
-
portalContainer instanceof HTMLElement ||
|
|
205
|
-
portalContainer instanceof DocumentFragment
|
|
206
|
-
? portalContainer
|
|
207
|
-
: null;
|
|
208
|
-
|
|
209
|
-
return (
|
|
210
|
-
<>
|
|
211
|
-
<style>{widgetCss}</style>
|
|
212
|
-
<div
|
|
213
|
-
data-agent-id={agentId}
|
|
214
|
-
data-mode={mode}
|
|
215
|
-
data-horizontal={horizontal}
|
|
216
|
-
data-vertical={vertical}
|
|
217
|
-
data-variant={variant}
|
|
218
|
-
data-preset={preset}
|
|
219
|
-
data-open={String(open)}
|
|
220
|
-
data-status={status}
|
|
221
|
-
style={shellStyle}
|
|
222
|
-
className="widget-shell"
|
|
223
|
-
>
|
|
224
|
-
<div style={{ pointerEvents: "auto" }}>
|
|
225
|
-
<ChatFullscreenProvider>
|
|
226
|
-
<ChatWidget
|
|
227
|
-
config={chatConfig}
|
|
228
|
-
messages={panelMessages}
|
|
229
|
-
status={RUNTIME_TO_PANEL_STATUS[status]}
|
|
230
|
-
errorMessage={errorMessage}
|
|
231
|
-
open={open}
|
|
232
|
-
alwaysOpen={alwaysOpen}
|
|
233
|
-
onOpenChange={onOpenChange}
|
|
234
|
-
onSendMessage={handleSend}
|
|
235
|
-
onStopConversation={
|
|
236
|
-
onStopConversation ? handleStop : undefined
|
|
237
|
-
}
|
|
238
|
-
onLauncherClick={onLauncherClick}
|
|
239
|
-
emptyAction={wrappedEmptyAction}
|
|
240
|
-
confirmation={confirmation}
|
|
241
|
-
portalContainer={portalEl}
|
|
242
|
-
/>
|
|
243
|
-
</ChatFullscreenProvider>
|
|
244
|
-
</div>
|
|
245
|
-
</div>
|
|
246
|
-
</>
|
|
247
|
-
);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function mapVariant(variant: WidgetVariant): ChatWidgetConfig["variant"] {
|
|
251
|
-
return variant;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
function mapPreset(preset: WidgetPreset): ChatWidgetConfig["preset"] {
|
|
255
|
-
return preset;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* `WidgetAppearance` mirrors `ChatAppearance` field-for-field (see
|
|
260
|
-
* `chat-widget/chat-widget-appearance.ts` for why it's declared separately),
|
|
261
|
-
* so this is a structural pass-through rather than a field-by-field copy.
|
|
262
|
-
*
|
|
263
|
-
* It used to rebuild a three-key object, which meant that even once the API
|
|
264
|
-
* started sending the full theme, the surface and brand tokens were dropped
|
|
265
|
-
* again right here — one strip point behind the other.
|
|
266
|
-
*/
|
|
267
|
-
function toChatAppearance(appearance: WidgetAppearance): ChatAppearance {
|
|
268
|
-
// Deliberately NOT a cast. Returning the value unasserted makes the
|
|
269
|
-
// compiler verify that `WidgetAppearance` is still structurally
|
|
270
|
-
// assignable to `ChatAppearance` — so if the two ever drift (a token
|
|
271
|
-
// added to one and not the other), this fails typecheck instead of
|
|
272
|
-
// silently dropping the field the way the old field-by-field copy did.
|
|
273
|
-
return appearance;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
function isHtmlElement(
|
|
277
|
-
node: HTMLElement | DocumentFragment | null | undefined,
|
|
278
|
-
): node is HTMLElement {
|
|
279
|
-
return (
|
|
280
|
-
node != null && typeof node === "object" && "tagName" in (node as object)
|
|
281
|
-
);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Walks the message stream once: peels off stop-confirmation envelopes (they
|
|
286
|
-
* become the `confirmation` slot of ChatWidget) and leaves plain assistant /
|
|
287
|
-
* user messages in place. Only the latest *pending* confirmation surfaces —
|
|
288
|
-
* accepted/rejected envelopes are dropped because the runtime resets the
|
|
289
|
-
* session right after acceptance.
|
|
290
|
-
*/
|
|
291
|
-
function splitConfirmationFromMessages(
|
|
292
|
-
messages: WidgetMessage[],
|
|
293
|
-
onConfirmationDecision:
|
|
294
|
-
| ((id: string, accepted: boolean) => void | Promise<void>)
|
|
295
|
-
| undefined,
|
|
296
|
-
): {
|
|
297
|
-
panelMessages: ChatPanelMessage[];
|
|
298
|
-
confirmation: ChatPanelConfirmation | null;
|
|
299
|
-
} {
|
|
300
|
-
const panelMessages: ChatPanelMessage[] = [];
|
|
301
|
-
let confirmation: ChatPanelConfirmation | null = null;
|
|
302
|
-
|
|
303
|
-
for (const message of messages) {
|
|
304
|
-
const envelope = parseStopConfirmationEnvelope(message.content);
|
|
305
|
-
if (!envelope) {
|
|
306
|
-
panelMessages.push({
|
|
307
|
-
id: message.id,
|
|
308
|
-
role: message.role,
|
|
309
|
-
content: message.content,
|
|
310
|
-
});
|
|
311
|
-
continue;
|
|
312
|
-
}
|
|
313
|
-
if (envelope.kind !== STOP_CONFIRMATION_KIND) continue;
|
|
314
|
-
if (envelope.state !== "pending") continue;
|
|
315
|
-
confirmation = {
|
|
316
|
-
id: envelope.confirmationId,
|
|
317
|
-
prompt: envelope.prompt || DEFAULT_STOP_CONVERSATION_PROMPT,
|
|
318
|
-
state: "pending",
|
|
319
|
-
onDecision: onConfirmationDecision
|
|
320
|
-
? (decision) =>
|
|
321
|
-
void onConfirmationDecision(
|
|
322
|
-
envelope.confirmationId,
|
|
323
|
-
decision === "accept",
|
|
324
|
-
)
|
|
325
|
-
: undefined,
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
return { panelMessages, confirmation };
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
function getWidgetShellStyle(
|
|
333
|
-
mode: WidgetRuntimeMode,
|
|
334
|
-
placement: WidgetPlacement,
|
|
335
|
-
): React.CSSProperties {
|
|
336
|
-
const style: React.CSSProperties = {
|
|
337
|
-
pointerEvents: "none",
|
|
338
|
-
};
|
|
339
|
-
|
|
340
|
-
if (mode === "preview") {
|
|
341
|
-
const { horizontal, vertical } = splitWidgetPlacement(placement);
|
|
342
|
-
Object.assign(style, {
|
|
343
|
-
position: "relative",
|
|
344
|
-
display: "flex",
|
|
345
|
-
width: "100%",
|
|
346
|
-
minHeight: "720px",
|
|
347
|
-
padding: "24px",
|
|
348
|
-
overflow: "hidden",
|
|
349
|
-
alignItems: vertical === "top" ? "flex-start" : "flex-end",
|
|
350
|
-
justifyContent:
|
|
351
|
-
horizontal === "left"
|
|
352
|
-
? "flex-start"
|
|
353
|
-
: horizontal === "center"
|
|
354
|
-
? "center"
|
|
355
|
-
: "flex-end",
|
|
356
|
-
});
|
|
357
|
-
return style;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
Object.assign(style, {
|
|
361
|
-
position: "fixed",
|
|
362
|
-
zIndex: 2147483646,
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
switch (placement) {
|
|
366
|
-
case "top-left":
|
|
367
|
-
Object.assign(style, { left: "24px", top: "24px" });
|
|
368
|
-
break;
|
|
369
|
-
case "top-center":
|
|
370
|
-
Object.assign(style, {
|
|
371
|
-
left: "50%",
|
|
372
|
-
top: "24px",
|
|
373
|
-
transform: "translateX(-50%)",
|
|
374
|
-
});
|
|
375
|
-
break;
|
|
376
|
-
case "top-right":
|
|
377
|
-
Object.assign(style, { right: "24px", top: "24px" });
|
|
378
|
-
break;
|
|
379
|
-
case "bottom-left":
|
|
380
|
-
Object.assign(style, { left: "24px", bottom: "24px" });
|
|
381
|
-
break;
|
|
382
|
-
case "bottom-center":
|
|
383
|
-
Object.assign(style, {
|
|
384
|
-
left: "50%",
|
|
385
|
-
bottom: "24px",
|
|
386
|
-
transform: "translateX(-50%)",
|
|
387
|
-
});
|
|
388
|
-
break;
|
|
389
|
-
default:
|
|
390
|
-
Object.assign(style, { right: "24px", bottom: "24px" });
|
|
391
|
-
break;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
return style;
|
|
395
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
declare const __USEREQ_WIDGET_API_BASE__:
|
|
2
|
-
| string
|
|
3
|
-
| undefined;
|
|
4
|
-
|
|
5
|
-
type WidgetRuntimeGlobal = typeof globalThis & {
|
|
6
|
-
__USEREQ_WIDGET_API_BASE__?: string;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
function normalizeApiBase(value: string | undefined): string | null {
|
|
10
|
-
const trimmed = value?.trim();
|
|
11
|
-
if (!trimmed) return null;
|
|
12
|
-
|
|
13
|
-
return trimmed.replace(/\/+$/, "");
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function getWidgetApiBase(): string {
|
|
17
|
-
const compileTimeApiBase =
|
|
18
|
-
typeof __USEREQ_WIDGET_API_BASE__ !== "undefined"
|
|
19
|
-
? __USEREQ_WIDGET_API_BASE__
|
|
20
|
-
: undefined;
|
|
21
|
-
const runtimeApiBase = (globalThis as WidgetRuntimeGlobal)
|
|
22
|
-
.__USEREQ_WIDGET_API_BASE__;
|
|
23
|
-
|
|
24
|
-
return (
|
|
25
|
-
normalizeApiBase(compileTimeApiBase) ??
|
|
26
|
-
normalizeApiBase(runtimeApiBase) ??
|
|
27
|
-
"http://localhost:4000"
|
|
28
|
-
);
|
|
29
|
-
}
|
package/src/runtime/api.ts
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
WidgetConversation,
|
|
3
|
-
WidgetMessage,
|
|
4
|
-
WidgetPublicChatSendChatResponse,
|
|
5
|
-
WidgetPublicChatSendStopConfirmationResponse,
|
|
6
|
-
WidgetSessionConfig,
|
|
7
|
-
} from "../types";
|
|
8
|
-
import { getWidgetApiBase } from "./api-origin";
|
|
9
|
-
|
|
10
|
-
async function requestJson<T>(path: string, init: RequestInit): Promise<T> {
|
|
11
|
-
const response = await fetch(new URL(path, `${getWidgetApiBase()}/`).toString(), {
|
|
12
|
-
...init,
|
|
13
|
-
headers: {
|
|
14
|
-
"Content-Type": "application/json",
|
|
15
|
-
...(init.headers ?? {}),
|
|
16
|
-
},
|
|
17
|
-
credentials: "omit",
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
const text = await response.text();
|
|
21
|
-
const payload = text ? JSON.parse(text) : null;
|
|
22
|
-
if (!response.ok) {
|
|
23
|
-
throw new Error(
|
|
24
|
-
typeof payload === "object" && payload && "error" in payload
|
|
25
|
-
? String((payload as { error?: string }).error ?? response.statusText)
|
|
26
|
-
: response.statusText
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return payload as T;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export async function mintWidgetSession(input: {
|
|
34
|
-
agentId: string;
|
|
35
|
-
origin: string;
|
|
36
|
-
pageUrl?: string;
|
|
37
|
-
referrer?: string;
|
|
38
|
-
}): Promise<WidgetSessionConfig> {
|
|
39
|
-
return requestJson<WidgetSessionConfig>("/api/widget/sessions", {
|
|
40
|
-
method: "POST",
|
|
41
|
-
body: JSON.stringify(input),
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export async function startPublicChat(input: {
|
|
46
|
-
agentId: string;
|
|
47
|
-
sessionToken: string;
|
|
48
|
-
}): Promise<{
|
|
49
|
-
conversation: WidgetConversation;
|
|
50
|
-
welcomeMessage: WidgetMessage;
|
|
51
|
-
}> {
|
|
52
|
-
return requestJson("/api/conversations/public/start", {
|
|
53
|
-
method: "POST",
|
|
54
|
-
headers: { Authorization: `Bearer ${input.sessionToken}` },
|
|
55
|
-
body: JSON.stringify({ agentId: input.agentId }),
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export async function sendPublicChatMessage(input: {
|
|
60
|
-
conversationId: string;
|
|
61
|
-
sessionToken: string;
|
|
62
|
-
content: string;
|
|
63
|
-
}): Promise<WidgetPublicChatSendChatResponse> {
|
|
64
|
-
return requestJson(
|
|
65
|
-
`/api/conversations/public/${encodeURIComponent(input.conversationId)}/messages`,
|
|
66
|
-
{
|
|
67
|
-
method: "POST",
|
|
68
|
-
headers: { Authorization: `Bearer ${input.sessionToken}` },
|
|
69
|
-
body: JSON.stringify({
|
|
70
|
-
content: input.content,
|
|
71
|
-
}),
|
|
72
|
-
}
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export async function sendPublicChatConfirmation(input: {
|
|
77
|
-
conversationId: string;
|
|
78
|
-
sessionToken: string;
|
|
79
|
-
content: string;
|
|
80
|
-
}): Promise<WidgetPublicChatSendStopConfirmationResponse> {
|
|
81
|
-
return requestJson(
|
|
82
|
-
`/api/conversations/public/${encodeURIComponent(input.conversationId)}/messages`,
|
|
83
|
-
{
|
|
84
|
-
method: "POST",
|
|
85
|
-
headers: { Authorization: `Bearer ${input.sessionToken}` },
|
|
86
|
-
body: JSON.stringify({
|
|
87
|
-
content: input.content,
|
|
88
|
-
}),
|
|
89
|
-
}
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export async function getCurrentConversation(sessionToken: string): Promise<{
|
|
94
|
-
conversation: WidgetConversation | null;
|
|
95
|
-
}> {
|
|
96
|
-
return requestJson<{ conversation: WidgetConversation | null }>(
|
|
97
|
-
"/api/conversations/public/current",
|
|
98
|
-
{
|
|
99
|
-
method: "GET",
|
|
100
|
-
headers: { Authorization: `Bearer ${sessionToken}` },
|
|
101
|
-
}
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export async function listConversationMessages(input: {
|
|
106
|
-
conversationId: string;
|
|
107
|
-
sessionToken: string;
|
|
108
|
-
cursor?: string | null;
|
|
109
|
-
}): Promise<{
|
|
110
|
-
data: WidgetMessage[];
|
|
111
|
-
nextCursor: string | null;
|
|
112
|
-
hasMore: boolean;
|
|
113
|
-
}> {
|
|
114
|
-
const params = new URLSearchParams();
|
|
115
|
-
if (input.cursor) params.set("cursor", input.cursor);
|
|
116
|
-
return requestJson(
|
|
117
|
-
`/api/conversations/public/${encodeURIComponent(input.conversationId)}/messages?${params.toString()}`,
|
|
118
|
-
{
|
|
119
|
-
method: "GET",
|
|
120
|
-
headers: { Authorization: `Bearer ${input.sessionToken}` },
|
|
121
|
-
}
|
|
122
|
-
);
|
|
123
|
-
}
|