@syntrologie/adapt-chatbot 2.14.0 → 2.15.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/dist/ChatAssistantLit.d.ts +69 -0
- package/dist/ChatAssistantLit.d.ts.map +1 -0
- package/dist/ChatAssistantLit.js +389 -0
- package/dist/runtime-lit.d.ts +29 -0
- package/dist/runtime-lit.d.ts.map +1 -0
- package/dist/runtime-lit.js +32 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +10 -0
- package/package.json +8 -1
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Chatbot - ChatAssistantLit
|
|
3
|
+
*
|
|
4
|
+
* Lit web component equivalent of ChatAssistant.tsx.
|
|
5
|
+
* Renders a message list with auto-scroll, typing indicator, error banner,
|
|
6
|
+
* and a composer input — all without React or Shadow DOM.
|
|
7
|
+
*
|
|
8
|
+
* Custom element: <syntro-chat-assistant>
|
|
9
|
+
*
|
|
10
|
+
* Decorator-free: uses `static override properties` (tsconfig has no
|
|
11
|
+
* experimentalDecorators).
|
|
12
|
+
*
|
|
13
|
+
* Light DOM (createRenderRoot returns `this`) so host-page CSS variables
|
|
14
|
+
* and the widget's inline styles both apply without :host() friction.
|
|
15
|
+
*/
|
|
16
|
+
import { LitElement } from 'lit';
|
|
17
|
+
import type { ChatbotConfig, ChatbotWidgetRuntime, ChatMessage } from './types.js';
|
|
18
|
+
export interface ChatAssistantLitProps {
|
|
19
|
+
config: ChatbotConfig;
|
|
20
|
+
runtime: ChatbotWidgetRuntime;
|
|
21
|
+
tileId?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare class ChatAssistantElement extends LitElement {
|
|
24
|
+
#private;
|
|
25
|
+
createRenderRoot(): this;
|
|
26
|
+
static properties: {
|
|
27
|
+
config: {
|
|
28
|
+
attribute: boolean;
|
|
29
|
+
};
|
|
30
|
+
runtime: {
|
|
31
|
+
attribute: boolean;
|
|
32
|
+
};
|
|
33
|
+
tileId: {
|
|
34
|
+
type: StringConstructor;
|
|
35
|
+
};
|
|
36
|
+
_messages: {
|
|
37
|
+
state: boolean;
|
|
38
|
+
};
|
|
39
|
+
_isLoading: {
|
|
40
|
+
state: boolean;
|
|
41
|
+
};
|
|
42
|
+
_error: {
|
|
43
|
+
state: boolean;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
config: ChatbotConfig | undefined;
|
|
47
|
+
runtime: ChatbotWidgetRuntime | undefined;
|
|
48
|
+
tileId: string;
|
|
49
|
+
/** @internal */ _messages: ChatMessage[];
|
|
50
|
+
/** @internal */ _isLoading: boolean;
|
|
51
|
+
/** @internal */ _error: string | null;
|
|
52
|
+
connectedCallback(): void;
|
|
53
|
+
disconnectedCallback(): void;
|
|
54
|
+
firstUpdated(): void;
|
|
55
|
+
updated(changed: Map<string, unknown>): void;
|
|
56
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
57
|
+
/** Programmatically clear the conversation (mirrors useChat.clearMessages) */
|
|
58
|
+
clearMessages(): void;
|
|
59
|
+
}
|
|
60
|
+
export declare const registerChatAssistantElement: () => void;
|
|
61
|
+
declare global {
|
|
62
|
+
interface HTMLElementTagNameMap {
|
|
63
|
+
'syntro-chat-assistant': ChatAssistantElement;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export declare const ChatAssistantLitMountable: {
|
|
67
|
+
mount(container: HTMLElement, mountConfig?: Record<string, unknown>): () => void;
|
|
68
|
+
};
|
|
69
|
+
//# sourceMappingURL=ChatAssistantLit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatAssistantLit.d.ts","sourceRoot":"","sources":["../src/ChatAssistantLit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAQ,UAAU,EAAW,MAAM,KAAK,CAAC;AAKhD,OAAO,KAAK,EAEV,aAAa,EACb,oBAAoB,EACpB,WAAW,EACZ,MAAM,YAAY,CAAC;AAsIpB,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,oBAAoB,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD,qBAAa,oBAAqB,SAAQ,UAAU;;IAGzC,gBAAgB;IAMzB,OAAgB,UAAU;;;;;;;;;;;;;;;;;;;MAUxB;IAIF,MAAM,EAAE,aAAa,GAAG,SAAS,CAAa;IAC9C,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAAa;IACtD,MAAM,SAAoB;IAI1B,gBAAgB,CAAC,SAAS,EAAE,WAAW,EAAE,CAAM;IAC/C,gBAAgB,CAAC,UAAU,UAAS;IACpC,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAQ;IAUrC,iBAAiB,IAAI,IAAI;IAMzB,oBAAoB,IAAI,IAAI;IAc5B,YAAY,IAAI,IAAI;IAQpB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAuB5C,MAAM;IAqJf,8EAA8E;IAC9E,aAAa,IAAI,IAAI;CAWtB;AAQD,eAAO,MAAM,4BAA4B,QAAO,IAK/C,CAAC;AAEF,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,uBAAuB,EAAE,oBAAoB,CAAC;KAC/C;CACF;AAMD,eAAO,MAAM,yBAAyB;qBACnB,WAAW,gBAAgB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAiCpE,CAAC"}
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Chatbot - ChatAssistantLit
|
|
3
|
+
*
|
|
4
|
+
* Lit web component equivalent of ChatAssistant.tsx.
|
|
5
|
+
* Renders a message list with auto-scroll, typing indicator, error banner,
|
|
6
|
+
* and a composer input — all without React or Shadow DOM.
|
|
7
|
+
*
|
|
8
|
+
* Custom element: <syntro-chat-assistant>
|
|
9
|
+
*
|
|
10
|
+
* Decorator-free: uses `static override properties` (tsconfig has no
|
|
11
|
+
* experimentalDecorators).
|
|
12
|
+
*
|
|
13
|
+
* Light DOM (createRenderRoot returns `this`) so host-page CSS variables
|
|
14
|
+
* and the widget's inline styles both apply without :host() friction.
|
|
15
|
+
*/
|
|
16
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
17
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
18
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
19
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
20
|
+
};
|
|
21
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
22
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
23
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
24
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
25
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
26
|
+
};
|
|
27
|
+
var _ChatAssistantElement_instances, _ChatAssistantElement_batchHandle, _ChatAssistantElement_inputEl, _ChatAssistantElement_messageListEl, _ChatAssistantElement_renderBubble, _ChatAssistantElement_handleSubmit, _ChatAssistantElement_sendMessage;
|
|
28
|
+
import { html, LitElement, nothing } from 'lit';
|
|
29
|
+
import { styleMap } from 'lit/directives/style-map.js';
|
|
30
|
+
import { parseActions } from './actionParser.js';
|
|
31
|
+
import { sendMessage } from './apiClient.js';
|
|
32
|
+
// ============================================================================
|
|
33
|
+
// Customer-themable colors
|
|
34
|
+
//
|
|
35
|
+
// This adaptive ships to customer pages and must pull its palette from
|
|
36
|
+
// `--sc-*` CSS variables (same pattern as adaptive-faq / adaptive-nav), so
|
|
37
|
+
// customers can theme chat to match their site without shipping a fork.
|
|
38
|
+
//
|
|
39
|
+
// The TOKEN_* constants below are *fallbacks only* — they match Syntro
|
|
40
|
+
// design-system values (purple[4], base.white, slateGrey[7,8,10,12], red[4])
|
|
41
|
+
// so dev/test environments without a customer theme still look on-brand.
|
|
42
|
+
// Do NOT import from `@syntro/design-system` at runtime — adaptives keep it
|
|
43
|
+
// as a devDep, not a runtime dep.
|
|
44
|
+
// ============================================================================
|
|
45
|
+
const TOKEN_PURPLE_4 = '#6a59ce';
|
|
46
|
+
const TOKEN_WHITE = '#ffffff';
|
|
47
|
+
const TOKEN_SLATE_10 = '#cbd0d7';
|
|
48
|
+
const TOKEN_SLATE_12 = '#f6f7f9';
|
|
49
|
+
const TOKEN_SLATE_8 = '#87919f';
|
|
50
|
+
const TOKEN_RED_4 = '#ff2524';
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// ID generator (matches the one in useChat.ts)
|
|
53
|
+
// ============================================================================
|
|
54
|
+
let _nextId = 0;
|
|
55
|
+
function generateId() {
|
|
56
|
+
return `msg-${Date.now()}-${++_nextId}`;
|
|
57
|
+
}
|
|
58
|
+
// ============================================================================
|
|
59
|
+
// Style objects (mirrors ChatAssistant.tsx styles const)
|
|
60
|
+
// ============================================================================
|
|
61
|
+
const containerStyle = {
|
|
62
|
+
display: 'flex',
|
|
63
|
+
flexDirection: 'column',
|
|
64
|
+
height: '100%',
|
|
65
|
+
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
66
|
+
fontSize: '14px',
|
|
67
|
+
touchAction: 'none',
|
|
68
|
+
};
|
|
69
|
+
const messageListStyle = {
|
|
70
|
+
flex: '1',
|
|
71
|
+
overflowY: 'auto',
|
|
72
|
+
padding: '12px',
|
|
73
|
+
display: 'flex',
|
|
74
|
+
flexDirection: 'column',
|
|
75
|
+
gap: '8px',
|
|
76
|
+
};
|
|
77
|
+
const bubbleBaseStyle = {
|
|
78
|
+
maxWidth: '85%',
|
|
79
|
+
padding: '8px 12px',
|
|
80
|
+
borderRadius: '12px',
|
|
81
|
+
lineHeight: '1.5',
|
|
82
|
+
wordBreak: 'break-word',
|
|
83
|
+
};
|
|
84
|
+
const userBubbleExtra = {
|
|
85
|
+
alignSelf: 'flex-end',
|
|
86
|
+
backgroundColor: `var(--sc-color-primary, ${TOKEN_PURPLE_4})`,
|
|
87
|
+
color: `var(--sc-color-primary-text, ${TOKEN_WHITE})`,
|
|
88
|
+
borderBottomRightRadius: '4px',
|
|
89
|
+
};
|
|
90
|
+
const assistantBubbleExtra = {
|
|
91
|
+
alignSelf: 'flex-start',
|
|
92
|
+
backgroundColor: 'var(--sc-chat-assistant-bubble-bg, rgba(255, 255, 255, 0.08))',
|
|
93
|
+
color: `var(--sc-content-text-color, ${TOKEN_SLATE_10})`,
|
|
94
|
+
borderBottomLeftRadius: '4px',
|
|
95
|
+
};
|
|
96
|
+
const loadingDotsStyle = {
|
|
97
|
+
alignSelf: 'flex-start',
|
|
98
|
+
padding: '8px 16px',
|
|
99
|
+
backgroundColor: 'var(--sc-chat-loading-bg, rgba(255, 255, 255, 0.05))',
|
|
100
|
+
borderRadius: '12px',
|
|
101
|
+
color: `var(--sc-content-text-secondary-color, ${TOKEN_SLATE_8})`,
|
|
102
|
+
fontSize: '13px',
|
|
103
|
+
};
|
|
104
|
+
const errorBannerStyle = {
|
|
105
|
+
padding: '8px 12px',
|
|
106
|
+
backgroundColor: 'var(--sc-color-error-bg, rgba(239, 68, 68, 0.1))',
|
|
107
|
+
color: `var(--sc-color-error, ${TOKEN_RED_4})`,
|
|
108
|
+
fontSize: '13px',
|
|
109
|
+
borderRadius: '8px',
|
|
110
|
+
margin: '0 12px',
|
|
111
|
+
};
|
|
112
|
+
const inputFormStyle = {
|
|
113
|
+
display: 'flex',
|
|
114
|
+
gap: '8px',
|
|
115
|
+
padding: '12px',
|
|
116
|
+
borderTop: '1px solid var(--sc-content-border-color, rgba(255, 255, 255, 0.06))',
|
|
117
|
+
};
|
|
118
|
+
const inputStyle = {
|
|
119
|
+
flex: '1',
|
|
120
|
+
padding: '8px 12px',
|
|
121
|
+
borderRadius: '8px',
|
|
122
|
+
border: '1px solid var(--sc-content-border-color, rgba(255, 255, 255, 0.1))',
|
|
123
|
+
backgroundColor: 'var(--sc-chat-input-bg, rgba(0, 0, 0, 0.2))',
|
|
124
|
+
color: `var(--sc-content-heading-color, ${TOKEN_SLATE_12})`,
|
|
125
|
+
fontSize: '14px',
|
|
126
|
+
outline: 'none',
|
|
127
|
+
fontFamily: 'inherit',
|
|
128
|
+
};
|
|
129
|
+
const sendButtonStyle = {
|
|
130
|
+
padding: '8px 16px',
|
|
131
|
+
borderRadius: '8px',
|
|
132
|
+
border: 'none',
|
|
133
|
+
backgroundColor: `var(--sc-color-primary, ${TOKEN_PURPLE_4})`,
|
|
134
|
+
color: `var(--sc-color-primary-text, ${TOKEN_WHITE})`,
|
|
135
|
+
fontWeight: '600',
|
|
136
|
+
fontSize: '13px',
|
|
137
|
+
cursor: 'pointer',
|
|
138
|
+
whiteSpace: 'nowrap',
|
|
139
|
+
};
|
|
140
|
+
const sendButtonDisabledExtra = {
|
|
141
|
+
opacity: '0.5',
|
|
142
|
+
cursor: 'not-allowed',
|
|
143
|
+
};
|
|
144
|
+
// ============================================================================
|
|
145
|
+
// ChatAssistantElement — Lit element
|
|
146
|
+
// ============================================================================
|
|
147
|
+
export class ChatAssistantElement extends LitElement {
|
|
148
|
+
constructor() {
|
|
149
|
+
// ---------- Light DOM: inherit host styles, no shadow boundary ----------
|
|
150
|
+
super(...arguments);
|
|
151
|
+
_ChatAssistantElement_instances.add(this);
|
|
152
|
+
// ---------- Public input properties --------------------------------------
|
|
153
|
+
this.config = undefined;
|
|
154
|
+
this.runtime = undefined;
|
|
155
|
+
this.tileId = 'chatbot-widget';
|
|
156
|
+
// ---------- Internal reactive state -------------------------------------
|
|
157
|
+
/** @internal */ this._messages = [];
|
|
158
|
+
/** @internal */ this._isLoading = false;
|
|
159
|
+
/** @internal */ this._error = null;
|
|
160
|
+
// ---------- Private (non-reactive) fields --------------------------------
|
|
161
|
+
_ChatAssistantElement_batchHandle.set(this, null);
|
|
162
|
+
_ChatAssistantElement_inputEl.set(this, null);
|
|
163
|
+
_ChatAssistantElement_messageListEl.set(this, null);
|
|
164
|
+
// ---------- Event handlers ----------------------------------------------
|
|
165
|
+
_ChatAssistantElement_handleSubmit.set(this, (e) => {
|
|
166
|
+
e.preventDefault();
|
|
167
|
+
const input = __classPrivateFieldGet(this, _ChatAssistantElement_inputEl, "f") ?? this.querySelector('[data-chat-input]');
|
|
168
|
+
if (!input || !input.value.trim() || this._isLoading)
|
|
169
|
+
return;
|
|
170
|
+
const text = input.value;
|
|
171
|
+
input.value = '';
|
|
172
|
+
__classPrivateFieldGet(this, _ChatAssistantElement_instances, "m", _ChatAssistantElement_sendMessage).call(this, text);
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
createRenderRoot() {
|
|
176
|
+
return this;
|
|
177
|
+
}
|
|
178
|
+
// ---------- Lifecycle: connectedCallback --------------------------------
|
|
179
|
+
connectedCallback() {
|
|
180
|
+
super.connectedCallback();
|
|
181
|
+
}
|
|
182
|
+
// ---------- Lifecycle: disconnectedCallback -----------------------------
|
|
183
|
+
disconnectedCallback() {
|
|
184
|
+
super.disconnectedCallback();
|
|
185
|
+
// Revert any active actions on unmount (mirrors useChat cleanup)
|
|
186
|
+
if (__classPrivateFieldGet(this, _ChatAssistantElement_batchHandle, "f")?.isApplied()) {
|
|
187
|
+
__classPrivateFieldGet(this, _ChatAssistantElement_batchHandle, "f").revertAll().catch((err) => {
|
|
188
|
+
console.error('[ChatAssistantElement] Failed to revert actions on disconnect:', err);
|
|
189
|
+
});
|
|
190
|
+
__classPrivateFieldSet(this, _ChatAssistantElement_batchHandle, null, "f");
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// ---------- Lifecycle: firstUpdated -------------------------------------
|
|
194
|
+
firstUpdated() {
|
|
195
|
+
// Cache references to imperative DOM nodes once (elements never change identity)
|
|
196
|
+
__classPrivateFieldSet(this, _ChatAssistantElement_inputEl, this.querySelector('[data-chat-input]'), "f");
|
|
197
|
+
__classPrivateFieldSet(this, _ChatAssistantElement_messageListEl, this.querySelector('[data-message-list]'), "f");
|
|
198
|
+
}
|
|
199
|
+
// ---------- Lifecycle: updated ------------------------------------------
|
|
200
|
+
updated(changed) {
|
|
201
|
+
// Auto-scroll to bottom when messages change
|
|
202
|
+
if (changed.has('_messages') || changed.has('_isLoading')) {
|
|
203
|
+
if (__classPrivateFieldGet(this, _ChatAssistantElement_messageListEl, "f")) {
|
|
204
|
+
__classPrivateFieldGet(this, _ChatAssistantElement_messageListEl, "f").scrollTop = __classPrivateFieldGet(this, _ChatAssistantElement_messageListEl, "f").scrollHeight;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// Seed greeting when config is set after construction
|
|
208
|
+
if (changed.has('config') && this.config?.greeting && this._messages.length === 0) {
|
|
209
|
+
this._messages = [
|
|
210
|
+
{
|
|
211
|
+
id: generateId(),
|
|
212
|
+
role: 'assistant',
|
|
213
|
+
text: this.config.greeting,
|
|
214
|
+
timestamp: Date.now(),
|
|
215
|
+
},
|
|
216
|
+
];
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// ---------- Render -------------------------------------------------------
|
|
220
|
+
render() {
|
|
221
|
+
return html `
|
|
222
|
+
<div style=${styleMap(containerStyle)} data-testid="chat-assistant">
|
|
223
|
+
<!-- Message list -->
|
|
224
|
+
<div style=${styleMap(messageListStyle)} data-message-list>
|
|
225
|
+
${this._messages.map((msg) => __classPrivateFieldGet(this, _ChatAssistantElement_instances, "m", _ChatAssistantElement_renderBubble).call(this, msg))}
|
|
226
|
+
${this._isLoading ? html `<div style=${styleMap(loadingDotsStyle)}>Thinking...</div>` : nothing}
|
|
227
|
+
</div>
|
|
228
|
+
|
|
229
|
+
<!-- Error banner -->
|
|
230
|
+
${this._error
|
|
231
|
+
? html `<div style=${styleMap(errorBannerStyle)}>${this._error}</div>`
|
|
232
|
+
: nothing}
|
|
233
|
+
|
|
234
|
+
<!-- Composer -->
|
|
235
|
+
<form
|
|
236
|
+
style=${styleMap(inputFormStyle)}
|
|
237
|
+
@submit=${__classPrivateFieldGet(this, _ChatAssistantElement_handleSubmit, "f")}
|
|
238
|
+
>
|
|
239
|
+
<input
|
|
240
|
+
data-chat-input
|
|
241
|
+
style=${styleMap(inputStyle)}
|
|
242
|
+
placeholder="Ask anything..."
|
|
243
|
+
?disabled=${this._isLoading}
|
|
244
|
+
data-testid="chat-input"
|
|
245
|
+
/>
|
|
246
|
+
<button
|
|
247
|
+
type="submit"
|
|
248
|
+
?disabled=${this._isLoading}
|
|
249
|
+
style=${styleMap(this._isLoading ? { ...sendButtonStyle, ...sendButtonDisabledExtra } : sendButtonStyle)}
|
|
250
|
+
data-testid="chat-send"
|
|
251
|
+
>
|
|
252
|
+
Send
|
|
253
|
+
</button>
|
|
254
|
+
</form>
|
|
255
|
+
</div>
|
|
256
|
+
`;
|
|
257
|
+
}
|
|
258
|
+
// ---------- Public API --------------------------------------------------
|
|
259
|
+
/** Programmatically clear the conversation (mirrors useChat.clearMessages) */
|
|
260
|
+
clearMessages() {
|
|
261
|
+
this._messages = [];
|
|
262
|
+
this._error = null;
|
|
263
|
+
if (__classPrivateFieldGet(this, _ChatAssistantElement_batchHandle, "f")?.isApplied()) {
|
|
264
|
+
__classPrivateFieldGet(this, _ChatAssistantElement_batchHandle, "f").revertAll().catch((err) => {
|
|
265
|
+
console.error('[ChatAssistantElement] Failed to revert actions on clear:', err);
|
|
266
|
+
});
|
|
267
|
+
__classPrivateFieldSet(this, _ChatAssistantElement_batchHandle, null, "f");
|
|
268
|
+
}
|
|
269
|
+
sessionStorage.removeItem(`syntro:chatbot:history:${this.tileId}`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
_ChatAssistantElement_batchHandle = new WeakMap(), _ChatAssistantElement_inputEl = new WeakMap(), _ChatAssistantElement_messageListEl = new WeakMap(), _ChatAssistantElement_handleSubmit = new WeakMap(), _ChatAssistantElement_instances = new WeakSet(), _ChatAssistantElement_renderBubble = function _ChatAssistantElement_renderBubble(msg) {
|
|
273
|
+
const isUser = msg.role === 'user';
|
|
274
|
+
const bubbleStyle = isUser
|
|
275
|
+
? { ...bubbleBaseStyle, ...userBubbleExtra }
|
|
276
|
+
: { ...bubbleBaseStyle, ...assistantBubbleExtra };
|
|
277
|
+
return html `<div style=${styleMap(bubbleStyle)}>${msg.text}</div>`;
|
|
278
|
+
}, _ChatAssistantElement_sendMessage =
|
|
279
|
+
// ---------- Message sending logic (replaces useChat) --------------------
|
|
280
|
+
async function _ChatAssistantElement_sendMessage(text) {
|
|
281
|
+
const trimmed = text.trim();
|
|
282
|
+
if (!trimmed || !this.config || !this.runtime)
|
|
283
|
+
return;
|
|
284
|
+
this._error = null;
|
|
285
|
+
const userMessage = {
|
|
286
|
+
id: generateId(),
|
|
287
|
+
role: 'user',
|
|
288
|
+
text: trimmed,
|
|
289
|
+
timestamp: Date.now(),
|
|
290
|
+
};
|
|
291
|
+
this._messages = [...this._messages, userMessage];
|
|
292
|
+
this._isLoading = true;
|
|
293
|
+
// Fire custom event so parent components / SDK can observe submission
|
|
294
|
+
this.dispatchEvent(new CustomEvent('chat-message-sent', {
|
|
295
|
+
detail: { text: trimmed },
|
|
296
|
+
bubbles: true,
|
|
297
|
+
}));
|
|
298
|
+
try {
|
|
299
|
+
const maxHistory = this.config.maxHistory ?? 20;
|
|
300
|
+
const historySlice = [...this._messages].slice(-maxHistory).map((m) => ({
|
|
301
|
+
role: m.role,
|
|
302
|
+
content: m.text,
|
|
303
|
+
}));
|
|
304
|
+
const response = await sendMessage(this.config.backendUrl, {
|
|
305
|
+
message: trimmed,
|
|
306
|
+
history: historySlice,
|
|
307
|
+
mlflow_run_id: this.config.mlflowRunId,
|
|
308
|
+
});
|
|
309
|
+
const { displayText, actions } = parseActions(response.response);
|
|
310
|
+
const assistantMessage = {
|
|
311
|
+
id: generateId(),
|
|
312
|
+
role: 'assistant',
|
|
313
|
+
text: displayText,
|
|
314
|
+
timestamp: Date.now(),
|
|
315
|
+
};
|
|
316
|
+
this._messages = [...this._messages, assistantMessage];
|
|
317
|
+
// Execute actions if present (mirrors useChat action lifecycle)
|
|
318
|
+
if (actions.length > 0) {
|
|
319
|
+
if (__classPrivateFieldGet(this, _ChatAssistantElement_batchHandle, "f")?.isApplied()) {
|
|
320
|
+
await __classPrivateFieldGet(this, _ChatAssistantElement_batchHandle, "f").revertAll();
|
|
321
|
+
}
|
|
322
|
+
__classPrivateFieldSet(this, _ChatAssistantElement_batchHandle, await this.runtime.actions.applyBatch(actions), "f");
|
|
323
|
+
this.runtime.events.publish('chatbot.actions_applied', {
|
|
324
|
+
count: actions.length,
|
|
325
|
+
kinds: actions.map((a) => a.kind),
|
|
326
|
+
tileId: this.tileId,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
// Fire custom event with the assistant reply
|
|
330
|
+
this.dispatchEvent(new CustomEvent('chat-response-received', {
|
|
331
|
+
detail: { text: displayText, actions },
|
|
332
|
+
bubbles: true,
|
|
333
|
+
}));
|
|
334
|
+
}
|
|
335
|
+
catch (err) {
|
|
336
|
+
const message = err instanceof Error ? err.message : 'An unexpected error occurred';
|
|
337
|
+
this._error = message;
|
|
338
|
+
}
|
|
339
|
+
finally {
|
|
340
|
+
this._isLoading = false;
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
// ---------- Reactive property declarations (no decorators) ---------------
|
|
344
|
+
ChatAssistantElement.properties = {
|
|
345
|
+
// Public inputs
|
|
346
|
+
config: { attribute: false },
|
|
347
|
+
runtime: { attribute: false },
|
|
348
|
+
tileId: { type: String },
|
|
349
|
+
// Internal reactive state
|
|
350
|
+
_messages: { state: true },
|
|
351
|
+
_isLoading: { state: true },
|
|
352
|
+
_error: { state: true },
|
|
353
|
+
};
|
|
354
|
+
// ============================================================================
|
|
355
|
+
// Custom element registration
|
|
356
|
+
// ============================================================================
|
|
357
|
+
const TAG_NAME = 'syntro-chat-assistant';
|
|
358
|
+
export const registerChatAssistantElement = () => {
|
|
359
|
+
if (typeof window === 'undefined')
|
|
360
|
+
return;
|
|
361
|
+
if (!customElements.get(TAG_NAME)) {
|
|
362
|
+
customElements.define(TAG_NAME, ChatAssistantElement);
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
// ============================================================================
|
|
366
|
+
// Mountable Widget Interface (Lit version — no React dependency)
|
|
367
|
+
// ============================================================================
|
|
368
|
+
export const ChatAssistantLitMountable = {
|
|
369
|
+
mount(container, mountConfig) {
|
|
370
|
+
const { config, runtime, tileId = 'chatbot-widget', } = (mountConfig ?? {});
|
|
371
|
+
if (!config || !runtime) {
|
|
372
|
+
container.innerHTML = `<div style="padding: 16px; color: var(--sc-content-text-secondary-color, ${TOKEN_SLATE_8});">Chat widget requires config and runtime.</div>`;
|
|
373
|
+
return () => {
|
|
374
|
+
container.innerHTML = '';
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
registerChatAssistantElement();
|
|
378
|
+
const el = document.createElement('syntro-chat-assistant');
|
|
379
|
+
el.config = config;
|
|
380
|
+
el.runtime = runtime;
|
|
381
|
+
el.tileId = tileId;
|
|
382
|
+
// Stretch to fill the container
|
|
383
|
+
el.style.cssText = 'display:flex;flex-direction:column;height:100%;width:100%;';
|
|
384
|
+
container.appendChild(el);
|
|
385
|
+
return () => {
|
|
386
|
+
el.remove();
|
|
387
|
+
};
|
|
388
|
+
},
|
|
389
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Chatbot - Runtime Module (Lit)
|
|
3
|
+
*
|
|
4
|
+
* Runtime manifest for the AI chat assistant adaptive.
|
|
5
|
+
* Uses the Lit web component mountable instead of the React one.
|
|
6
|
+
* This is a widget-based adaptive with no action executors.
|
|
7
|
+
*/
|
|
8
|
+
export declare const runtime: {
|
|
9
|
+
id: string;
|
|
10
|
+
version: string;
|
|
11
|
+
name: string;
|
|
12
|
+
description: string;
|
|
13
|
+
/** No action executors — chatbot uses existing action kinds via applyBatch */
|
|
14
|
+
executors: never[];
|
|
15
|
+
/** Widget definitions for the runtime's WidgetRegistry */
|
|
16
|
+
widgets: {
|
|
17
|
+
id: string;
|
|
18
|
+
component: {
|
|
19
|
+
mount(container: HTMLElement, mountConfig?: Record<string, unknown>): () => void;
|
|
20
|
+
};
|
|
21
|
+
metadata: {
|
|
22
|
+
name: string;
|
|
23
|
+
description: string;
|
|
24
|
+
icon: string;
|
|
25
|
+
};
|
|
26
|
+
}[];
|
|
27
|
+
};
|
|
28
|
+
export default runtime;
|
|
29
|
+
//# sourceMappingURL=runtime-lit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-lit.d.ts","sourceRoot":"","sources":["../src/runtime-lit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,eAAO,MAAM,OAAO;;;;;IAMlB,8EAA8E;;IAG9E,0DAA0D;;;;;;;;;;;;CAY3D,CAAC;AAEF,eAAe,OAAO,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Chatbot - Runtime Module (Lit)
|
|
3
|
+
*
|
|
4
|
+
* Runtime manifest for the AI chat assistant adaptive.
|
|
5
|
+
* Uses the Lit web component mountable instead of the React one.
|
|
6
|
+
* This is a widget-based adaptive with no action executors.
|
|
7
|
+
*/
|
|
8
|
+
import { ChatAssistantLitMountable } from './ChatAssistantLit';
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// App Runtime Manifest
|
|
11
|
+
// ============================================================================
|
|
12
|
+
export const runtime = {
|
|
13
|
+
id: 'adaptive-chatbot',
|
|
14
|
+
version: '1.0.0',
|
|
15
|
+
name: 'Chat Assistant',
|
|
16
|
+
description: 'AI chat assistant with action execution capabilities',
|
|
17
|
+
/** No action executors — chatbot uses existing action kinds via applyBatch */
|
|
18
|
+
executors: [],
|
|
19
|
+
/** Widget definitions for the runtime's WidgetRegistry */
|
|
20
|
+
widgets: [
|
|
21
|
+
{
|
|
22
|
+
id: 'adaptive-chatbot:assistant',
|
|
23
|
+
component: ChatAssistantLitMountable,
|
|
24
|
+
metadata: {
|
|
25
|
+
name: 'Chat Assistant',
|
|
26
|
+
description: 'AI-powered chat assistant that can execute DOM actions',
|
|
27
|
+
icon: '💬',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
};
|
|
32
|
+
export default runtime;
|
package/dist/runtime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,eAAO,MAAM,OAAO;;;;;IAMlB,8EAA8E;;IAG9E,0DAA0D;;;;;;;;;;;;CAqB3D,CAAC;AAEF,eAAe,OAAO,CAAC"}
|
package/dist/runtime.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* This is a widget-based adaptive with no action executors.
|
|
6
6
|
*/
|
|
7
7
|
import { ChatAssistantMountableWidget } from './ChatAssistant';
|
|
8
|
+
import { ChatAssistantLitMountable } from './ChatAssistantLit';
|
|
8
9
|
// ============================================================================
|
|
9
10
|
// App Runtime Manifest
|
|
10
11
|
// ============================================================================
|
|
@@ -26,6 +27,15 @@ export const runtime = {
|
|
|
26
27
|
icon: '💬',
|
|
27
28
|
},
|
|
28
29
|
},
|
|
30
|
+
{
|
|
31
|
+
id: 'adaptive-chatbot:assistant-lit',
|
|
32
|
+
component: ChatAssistantLitMountable,
|
|
33
|
+
metadata: {
|
|
34
|
+
name: 'Chat Assistant (Lit)',
|
|
35
|
+
description: 'Lit web component chat assistant — no React dependency',
|
|
36
|
+
icon: '💬',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
29
39
|
],
|
|
30
40
|
};
|
|
31
41
|
export default runtime;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syntrologie/adapt-chatbot",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.15.0",
|
|
4
4
|
"description": "Adaptive Chatbot - AI chat assistant widget with action execution",
|
|
5
5
|
"license": "Proprietary",
|
|
6
6
|
"private": false,
|
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
"types": "./dist/runtime.d.ts",
|
|
28
28
|
"import": "./dist/runtime.js"
|
|
29
29
|
},
|
|
30
|
+
"./runtime-lit": {
|
|
31
|
+
"types": "./src/runtime-lit.ts",
|
|
32
|
+
"import": "./src/runtime-lit.ts"
|
|
33
|
+
},
|
|
30
34
|
"./schema": {
|
|
31
35
|
"types": "./dist/schema.d.ts",
|
|
32
36
|
"import": "./dist/schema.js"
|
|
@@ -64,12 +68,15 @@
|
|
|
64
68
|
},
|
|
65
69
|
"devDependencies": {
|
|
66
70
|
"@assistant-ui/react": "0.12.22",
|
|
71
|
+
"@open-wc/testing": "4.0.0",
|
|
72
|
+
"@open-wc/testing-helpers": "3.0.1",
|
|
67
73
|
"@syntro/design-system": "1.0.0",
|
|
68
74
|
"@syntrologie/shared-editor-ui": "*",
|
|
69
75
|
"@testing-library/react": "16.3.2",
|
|
70
76
|
"@types/react": "19.2.14",
|
|
71
77
|
"@types/react-dom": "19.2.3",
|
|
72
78
|
"jsdom": "26.1.0",
|
|
79
|
+
"lit": "3.3.2",
|
|
73
80
|
"react": "19.2.1",
|
|
74
81
|
"react-dom": "19.2.1",
|
|
75
82
|
"typescript": "5.9.3",
|