@chatcops/widget 0.3.2 → 0.4.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/api/types.d.ts +4 -0
- package/dist/api/types.d.ts.map +1 -1
- package/dist/chatcops.esm.js +554 -48
- package/dist/chatcops.min.js +1 -1
- package/dist/dom/input.d.ts +1 -0
- package/dist/dom/input.d.ts.map +1 -1
- package/dist/dom/messages.d.ts +23 -2
- package/dist/dom/messages.d.ts.map +1 -1
- package/dist/dom/panel.d.ts +6 -1
- package/dist/dom/panel.d.ts.map +1 -1
- package/dist/dom/prechat-form.d.ts +26 -0
- package/dist/dom/prechat-form.d.ts.map +1 -0
- package/dist/i18n.d.ts +7 -0
- package/dist/i18n.d.ts.map +1 -1
- package/dist/storage.d.ts +4 -0
- package/dist/storage.d.ts.map +1 -1
- package/dist/widget.d.ts +37 -1
- package/dist/widget.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/chatcops.esm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
-
const widgetStyles = ":host{--cc-accent: #6366f1;--cc-accent-hover: #4f46e5;--cc-text: #ffffff;--cc-bg: #0a0a0a;--cc-bg-surface: #1a1a1a;--cc-bg-input: #2a2a2a;--cc-border: #333333;--cc-text-secondary: #a0a0a0;--cc-font: system-ui, -apple-system, sans-serif;--cc-radius: 12px;--cc-shadow: 0 8px 32px rgba(0, 0, 0, .4);--cc-fab-size: 56px;--cc-panel-width: 400px;--cc-panel-height: 560px;all:initial;font-family:var(--cc-font);font-size:14px;line-height:1.5;color:var(--cc-text);-webkit-font-smoothing:antialiased}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}.cc-fab{position:fixed;bottom:20px;right:20px;width:var(--cc-fab-size);height:var(--cc-fab-size);border-radius:50%;background:var(--cc-accent);border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 4px 16px #0000004d;transition:transform .2s ease,box-shadow .2s ease;z-index:10000;outline:none}.cc-fab:hover{transform:scale(1.05);box-shadow:0 6px 24px #0006}.cc-fab:active{transform:scale(.95)}.cc-fab.cc-left{right:auto;left:20px}.cc-fab svg{width:24px;height:24px;fill:var(--cc-text);transition:transform .3s ease}.cc-fab.cc-open svg{transform:rotate(90deg)}.cc-fab-badge{position:absolute;top:-2px;right:-2px;width:12px;height:12px;border-radius:50%;background:#ef4444;border:2px solid var(--cc-bg);display:none}.cc-fab-badge.cc-visible{display:block}@keyframes cc-pulse{0%,to{box-shadow:0 4px 16px #0000004d}50%{box-shadow:0 4px 16px #6366f180}}.cc-fab.cc-pulse{animation:cc-pulse 2s ease-in-out 3}.cc-panel{position:fixed;bottom:88px;right:20px;width:var(--cc-panel-width);height:var(--cc-panel-height);max-height:calc(100vh - 100px);background:var(--cc-bg);border-radius:var(--cc-radius);box-shadow:var(--cc-shadow);border:1px solid var(--cc-border);display:flex;flex-direction:column;overflow:hidden;z-index:9999;opacity:0;transform:translateY(16px) scale(.95);pointer-events:none;transition:opacity .2s ease,transform .2s ease}.cc-panel.cc-visible{opacity:1;transform:translateY(0) scale(1);pointer-events:auto}.cc-panel.cc-left{right:auto;left:20px}.cc-panel.cc-inline{position:relative;bottom:auto;right:auto;left:auto;width:100%;height:100%;max-height:100%;z-index:auto;opacity:1;transform:none;pointer-events:auto;transition:none}.cc-panel.cc-inline.cc-visible{transform:none}.cc-header{display:flex;align-items:center;padding:16px;background:var(--cc-bg-surface);border-bottom:1px solid var(--cc-border);flex-shrink:0}.cc-header-avatar{width:36px;height:36px;border-radius:50%;margin-right:12px;object-fit:cover}.cc-header-avatar-placeholder{width:36px;height:36px;border-radius:50%;margin-right:12px;background:var(--cc-accent);display:flex;align-items:center;justify-content:center}.cc-header-avatar-placeholder svg{width:20px;height:20px;fill:var(--cc-text)}.cc-header-info{flex:1}.cc-header-name{font-size:15px;font-weight:600;color:var(--cc-text)}.cc-header-subtitle{font-size:12px;color:var(--cc-text-secondary)}.cc-header-close{width:32px;height:32px;border-radius:8px;background:transparent;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .15s ease}.cc-header-close:hover{background:var(--cc-bg-input)}.cc-header-close svg{width:16px;height:16px;fill:var(--cc-text-secondary)}.cc-messages{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:12px}.cc-messages::-webkit-scrollbar{width:4px}.cc-messages::-webkit-scrollbar-track{background:transparent}.cc-messages::-webkit-scrollbar-thumb{background:var(--cc-border);border-radius:2px}.cc-message{max-width:85%;padding:10px 14px;border-radius:var(--cc-radius);word-wrap:break-word;overflow-wrap:break-word;font-size:14px;line-height:1.5}.cc-message-user{align-self:flex-end;background:var(--cc-accent);color:var(--cc-text);border-bottom-right-radius:4px}.cc-message-assistant{align-self:flex-start;background:var(--cc-bg-surface);color:var(--cc-text);border-bottom-left-radius:4px}.cc-message a{color:var(--cc-accent);text-decoration:underline}.cc-message-user a{color:var(--cc-text)}.cc-message code{background:#0003;padding:1px 4px;border-radius:4px;font-family:SF Mono,Cascadia Code,Consolas,monospace;font-size:13px}.cc-message pre{background:#0000004d;padding:8px 12px;border-radius:8px;overflow-x:auto;margin:4px 0}.cc-message pre code{background:transparent;padding:0}.cc-message ul,.cc-message ol{padding-left:20px;margin:4px 0}.cc-typing{display:flex;align-items:center;gap:4px;padding:10px 14px;align-self:flex-start;background:var(--cc-bg-surface);border-radius:var(--cc-radius);border-bottom-left-radius:4px}.cc-typing-dot{width:6px;height:6px;border-radius:50%;background:var(--cc-text-secondary);animation:cc-bounce 1.2s infinite}.cc-typing-dot:nth-child(2){animation-delay:.2s}.cc-typing-dot:nth-child(3){animation-delay:.4s}@keyframes cc-bounce{0%,60%,to{transform:translateY(0)}30%{transform:translateY(-4px)}}.cc-input-area{display:flex;align-items:flex-end;padding:12px 16px;border-top:1px solid var(--cc-border);background:var(--cc-bg);gap:8px}.cc-input{flex:1;background:var(--cc-bg-input);border:1px solid var(--cc-border);border-radius:8px;padding:8px 12px;color:var(--cc-text);font-family:var(--cc-font);font-size:14px;line-height:1.5;resize:none;outline:none;max-height:96px;overflow-y:auto;transition:border-color .15s ease}.cc-input:focus{border-color:var(--cc-accent);outline:2px solid var(--cc-accent);outline-offset:1px}.cc-input::placeholder{color:var(--cc-text-secondary)}.cc-send-btn{width:36px;height:36px;border-radius:8px;background:var(--cc-accent);border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:background .15s ease,opacity .15s ease}.cc-send-btn:hover{background:var(--cc-accent-hover)}.cc-send-btn:disabled{opacity:.4;cursor:not-allowed}.cc-send-btn svg{width:16px;height:16px;fill:var(--cc-text)}.cc-footer{padding:6px 16px;text-align:center;font-size:11px;color:var(--cc-text-secondary);border-top:1px solid var(--cc-border)}.cc-footer a{color:var(--cc-text-secondary);text-decoration:none}.cc-footer a:hover{text-decoration:underline}.cc-bubble{position:fixed;bottom:84px;right:20px;background:var(--cc-bg-surface);color:var(--cc-text);padding:10px 16px;border-radius:var(--cc-radius);box-shadow:var(--cc-shadow);border:1px solid var(--cc-border);max-width:260px;font-size:13px;line-height:1.4;z-index:9998;opacity:0;transform:translateY(8px);pointer-events:none;transition:opacity .3s ease,transform .3s ease;cursor:pointer}.cc-bubble.cc-visible{opacity:1;transform:translateY(0);pointer-events:auto}.cc-bubble.cc-left{right:auto;left:20px}.cc-bubble-close{position:absolute;top:-8px;right:-8px;width:20px;height:20px;border-radius:50%;background:var(--cc-bg);border:1px solid var(--cc-border);cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:10px;color:var(--cc-text-secondary)}@media(max-width:768px){.cc-panel:not(.cc-inline){width:100%;height:100%;max-height:100%;bottom:0;right:0;left:0;border-radius:0;border:none}.cc-panel.cc-left:not(.cc-inline){left:0}.cc-bubble{bottom:84px;right:16px;left:16px;max-width:none}}";
|
|
4
|
+
const widgetStyles = ":host{--cc-accent: #6366f1;--cc-accent-hover: #4f46e5;--cc-text: #ffffff;--cc-bg: #0a0a0a;--cc-bg-surface: #1a1a1a;--cc-bg-input: #2a2a2a;--cc-border: #333333;--cc-text-secondary: #a0a0a0;--cc-font: system-ui, -apple-system, sans-serif;--cc-radius: 12px;--cc-shadow: 0 8px 32px rgba(0, 0, 0, .4);--cc-fab-size: 56px;--cc-panel-width: 400px;--cc-panel-height: 560px;all:initial;font-family:var(--cc-font);font-size:14px;line-height:1.5;color:var(--cc-text);-webkit-font-smoothing:antialiased}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}.cc-fab{position:fixed;bottom:20px;right:20px;width:var(--cc-fab-size);height:var(--cc-fab-size);border-radius:50%;background:var(--cc-accent);border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 4px 16px #0000004d;transition:transform .2s ease,box-shadow .2s ease;z-index:10000;outline:none}.cc-fab:hover{transform:scale(1.05);box-shadow:0 6px 24px #0006}.cc-fab:active{transform:scale(.95)}.cc-fab.cc-left{right:auto;left:20px}.cc-fab svg{width:24px;height:24px;fill:var(--cc-text);transition:transform .3s ease}.cc-fab.cc-open svg{transform:rotate(90deg)}.cc-fab-badge{position:absolute;top:-2px;right:-2px;width:12px;height:12px;border-radius:50%;background:#ef4444;border:2px solid var(--cc-bg);display:none}.cc-fab-badge.cc-visible{display:block}@keyframes cc-pulse{0%,to{box-shadow:0 4px 16px #0000004d}50%{box-shadow:0 4px 16px #6366f180}}.cc-fab.cc-pulse{animation:cc-pulse 2s ease-in-out 3}.cc-panel{position:fixed;bottom:88px;right:20px;width:var(--cc-panel-width);height:var(--cc-panel-height);max-height:calc(100vh - 100px);background:var(--cc-bg);border-radius:var(--cc-radius);box-shadow:var(--cc-shadow);border:1px solid var(--cc-border);display:flex;flex-direction:column;overflow:hidden;z-index:9999;opacity:0;transform:translateY(16px) scale(.95);pointer-events:none;transition:opacity .2s ease,transform .2s ease}.cc-panel.cc-visible{opacity:1;transform:translateY(0) scale(1);pointer-events:auto}.cc-panel.cc-left{right:auto;left:20px}.cc-panel.cc-inline{position:relative;bottom:auto;right:auto;left:auto;width:100%;height:100%;max-height:100%;z-index:auto;opacity:1;transform:none;pointer-events:auto;transition:none}.cc-panel.cc-inline.cc-visible{transform:none}.cc-header{display:flex;align-items:center;padding:16px;background:var(--cc-bg-surface);border-bottom:1px solid var(--cc-border);flex-shrink:0}.cc-header-avatar{width:36px;height:36px;border-radius:50%;margin-right:12px;object-fit:cover}.cc-header-avatar-placeholder{width:36px;height:36px;border-radius:50%;margin-right:12px;background:var(--cc-accent);display:flex;align-items:center;justify-content:center}.cc-header-avatar-placeholder svg{width:20px;height:20px;fill:var(--cc-text)}.cc-header-info{flex:1}.cc-header-name{font-size:15px;font-weight:600;color:var(--cc-text)}.cc-header-subtitle{font-size:12px;color:var(--cc-text-secondary)}.cc-header-close{width:32px;height:32px;border-radius:8px;background:transparent;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .15s ease}.cc-header-close:hover{background:var(--cc-bg-input)}.cc-header-close svg{width:16px;height:16px;fill:var(--cc-text-secondary)}.cc-messages{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:12px}.cc-messages::-webkit-scrollbar{width:4px}.cc-messages::-webkit-scrollbar-track{background:transparent}.cc-messages::-webkit-scrollbar-thumb{background:var(--cc-border);border-radius:2px}.cc-message{max-width:85%;padding:10px 14px;border-radius:var(--cc-radius);word-wrap:break-word;overflow-wrap:break-word;font-size:14px;line-height:1.5;display:flex;flex-direction:column;gap:8px}.cc-message-user{align-self:flex-end;background:var(--cc-accent);color:var(--cc-text);border-bottom-right-radius:4px}.cc-message-assistant{align-self:flex-start;background:var(--cc-bg-surface);color:var(--cc-text);border-bottom-left-radius:4px}.cc-message a{color:var(--cc-accent);text-decoration:underline}.cc-message-user a{color:var(--cc-text)}.cc-message code{background:#0003;padding:1px 4px;border-radius:4px;font-family:SF Mono,Cascadia Code,Consolas,monospace;font-size:13px}.cc-message pre{background:#0000004d;padding:8px 12px;border-radius:8px;overflow-x:auto;margin:4px 0}.cc-message pre code{background:transparent;padding:0}.cc-message ul,.cc-message ol{padding-left:20px;margin:4px 0}.cc-message-actions{display:flex;align-items:center}.cc-message-retry,.cc-message-regenerate{display:inline-flex;align-items:center;gap:4px;padding:4px 10px;background:transparent;border:1px solid var(--cc-border);border-radius:6px;color:var(--cc-text-secondary);font-family:var(--cc-font);font-size:12px;cursor:pointer;transition:background .15s ease,color .15s ease,opacity .15s ease}.cc-message-retry:hover,.cc-message-regenerate:hover{background:var(--cc-bg-input);color:var(--cc-text)}.cc-message-retry svg,.cc-message-regenerate svg{fill:currentColor}.cc-message-regenerate{opacity:0}.cc-message:hover .cc-message-regenerate{opacity:1}.cc-message-retry{border-color:#ef4444;color:#ef4444}.cc-message-retry:hover{background:#ef44441a;color:#ef4444}.cc-typing{display:flex;align-items:center;gap:4px;padding:10px 14px;align-self:flex-start;background:var(--cc-bg-surface);border-radius:var(--cc-radius);border-bottom-left-radius:4px}.cc-typing-dot{width:6px;height:6px;border-radius:50%;background:var(--cc-text-secondary);animation:cc-bounce 1.2s infinite}.cc-typing-dot:nth-child(2){animation-delay:.2s}.cc-typing-dot:nth-child(3){animation-delay:.4s}@keyframes cc-bounce{0%,60%,to{transform:translateY(0)}30%{transform:translateY(-4px)}}.cc-input-area{display:flex;align-items:flex-end;padding:12px 16px;border-top:1px solid var(--cc-border);background:var(--cc-bg);gap:8px}.cc-input{flex:1;background:var(--cc-bg-input);border:1px solid var(--cc-border);border-radius:8px;padding:8px 12px;color:var(--cc-text);font-family:var(--cc-font);font-size:14px;line-height:1.5;resize:none;outline:none;max-height:96px;overflow-y:auto;transition:border-color .15s ease}.cc-input:focus{border-color:var(--cc-accent);outline:2px solid var(--cc-accent);outline-offset:1px}.cc-input::placeholder{color:var(--cc-text-secondary)}.cc-send-btn{width:36px;height:36px;border-radius:8px;background:var(--cc-accent);border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:background .15s ease,opacity .15s ease}.cc-send-btn:hover{background:var(--cc-accent-hover)}.cc-send-btn:disabled{opacity:.4;cursor:not-allowed}.cc-send-btn svg{width:16px;height:16px;fill:var(--cc-text)}.cc-footer{padding:6px 16px;text-align:center;font-size:11px;color:var(--cc-text-secondary);border-top:1px solid var(--cc-border)}.cc-footer a{color:var(--cc-text-secondary);text-decoration:none}.cc-footer a:hover{text-decoration:underline}.cc-bubble{position:fixed;bottom:84px;right:20px;background:var(--cc-bg-surface);color:var(--cc-text);padding:10px 16px;border-radius:var(--cc-radius);box-shadow:var(--cc-shadow);border:1px solid var(--cc-border);max-width:260px;font-size:13px;line-height:1.4;z-index:9998;opacity:0;transform:translateY(8px);pointer-events:none;transition:opacity .3s ease,transform .3s ease;cursor:pointer}.cc-bubble.cc-visible{opacity:1;transform:translateY(0);pointer-events:auto}.cc-bubble.cc-left{right:auto;left:20px}.cc-bubble-close{position:absolute;top:-8px;right:-8px;width:20px;height:20px;border-radius:50%;background:var(--cc-bg);border:1px solid var(--cc-border);cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:10px;color:var(--cc-text-secondary)}.cc-hidden{display:none}.cc-messages-wrapper{flex:1;display:flex;flex-direction:column;min-height:0}.cc-prechat{flex:1;overflow-y:auto;padding:24px 16px;display:flex;flex-direction:column;gap:16px}.cc-prechat-title{font-size:18px;font-weight:600;color:var(--cc-text)}.cc-prechat-subtitle{font-size:13px;color:var(--cc-text-secondary);margin-top:-8px}.cc-prechat-fields{display:flex;flex-direction:column;gap:14px}.cc-prechat-field{display:flex;flex-direction:column;gap:4px}.cc-prechat-label{font-size:13px;font-weight:500;color:var(--cc-text)}.cc-prechat-label .cc-required{color:#ef4444;margin-left:2px}.cc-prechat-input,.cc-prechat-select,.cc-prechat-textarea{background:var(--cc-bg-input);border:1px solid var(--cc-border);border-radius:8px;padding:8px 12px;color:var(--cc-text);font-family:var(--cc-font);font-size:14px;outline:none;transition:border-color .15s ease}.cc-prechat-input:focus,.cc-prechat-select:focus,.cc-prechat-textarea:focus{border-color:var(--cc-accent)}.cc-prechat-select{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}.cc-prechat-textarea{resize:none;line-height:1.5}.cc-prechat-error{font-size:12px;color:#ef4444;min-height:0}.cc-prechat-error:empty{display:none}.cc-prechat-submit{background:var(--cc-accent);color:var(--cc-text);border:none;border-radius:8px;padding:10px 16px;font-family:var(--cc-font);font-size:14px;font-weight:500;cursor:pointer;transition:background .15s ease;margin-top:8px}.cc-prechat-submit:hover{background:var(--cc-accent-hover)}.cc-prechat-submit:disabled{opacity:.5;cursor:not-allowed}@media(max-width:768px){.cc-panel:not(.cc-inline){width:100%;height:100%;max-height:100%;bottom:0;right:0;left:0;border-radius:0;border:none}.cc-panel.cc-left:not(.cc-inline){left:0}.cc-bubble{bottom:84px;right:16px;left:16px;max-width:none}}";
|
|
5
5
|
function createShadowRoot(container) {
|
|
6
6
|
const host = document.createElement("div");
|
|
7
7
|
host.id = "chatcops-root";
|
|
@@ -96,28 +96,29 @@ function renderMarkdown(input) {
|
|
|
96
96
|
html = html.replace(/\n/g, "<br>");
|
|
97
97
|
return html;
|
|
98
98
|
}
|
|
99
|
+
const RETRY_ICON = '<svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true"><path d="M17.65 6.35A7.96 7.96 0 0012 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0112 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>';
|
|
100
|
+
const REGENERATE_ICON = '<svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true"><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46A7.93 7.93 0 0020 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74A7.93 7.93 0 004 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/></svg>';
|
|
99
101
|
class Messages {
|
|
100
|
-
constructor(parent) {
|
|
102
|
+
constructor(parent, actions) {
|
|
101
103
|
__publicField(this, "container");
|
|
102
104
|
__publicField(this, "typingEl", null);
|
|
105
|
+
__publicField(this, "actions");
|
|
106
|
+
this.actions = actions;
|
|
103
107
|
this.container = document.createElement("div");
|
|
104
108
|
this.container.className = "cc-messages";
|
|
105
109
|
parent.appendChild(this.container);
|
|
106
110
|
}
|
|
107
111
|
addMessage(msg) {
|
|
108
112
|
this.removeTyping();
|
|
109
|
-
const el =
|
|
110
|
-
el.className = `cc-message cc-message-${msg.role}`;
|
|
111
|
-
el.dataset.id = msg.id;
|
|
112
|
-
el.innerHTML = msg.role === "assistant" ? renderMarkdown(msg.content) : this.escapeHtml(msg.content);
|
|
113
|
+
const el = this.createMessageElement(msg);
|
|
113
114
|
this.container.appendChild(el);
|
|
114
115
|
this.scrollToBottom();
|
|
115
116
|
return el;
|
|
116
117
|
}
|
|
117
|
-
updateMessage(
|
|
118
|
-
const el = this.container.querySelector(`[data-id="${id}"]`);
|
|
118
|
+
updateMessage(msg) {
|
|
119
|
+
const el = this.container.querySelector(`[data-id="${msg.id}"]`);
|
|
119
120
|
if (el) {
|
|
120
|
-
|
|
121
|
+
this.renderMessageElement(el, msg);
|
|
121
122
|
this.scrollToBottom();
|
|
122
123
|
}
|
|
123
124
|
}
|
|
@@ -139,9 +140,108 @@ class Messages {
|
|
|
139
140
|
this.container.innerHTML = "";
|
|
140
141
|
this.typingEl = null;
|
|
141
142
|
}
|
|
143
|
+
removeMessage(id) {
|
|
144
|
+
const el = this.container.querySelector(`[data-id="${id}"]`);
|
|
145
|
+
el == null ? void 0 : el.remove();
|
|
146
|
+
}
|
|
147
|
+
clearRegenerateButtons() {
|
|
148
|
+
this.container.querySelectorAll(".cc-message-regenerate").forEach((button) => button.remove());
|
|
149
|
+
this.cleanupEmptyActionContainers();
|
|
150
|
+
}
|
|
151
|
+
showRegenerateButton(messageId) {
|
|
152
|
+
var _a;
|
|
153
|
+
this.clearRegenerateButtons();
|
|
154
|
+
if (!((_a = this.actions) == null ? void 0 : _a.onRegenerate)) return;
|
|
155
|
+
const msgEl = this.container.querySelector(`[data-id="${messageId}"]`);
|
|
156
|
+
if (!msgEl || !msgEl.classList.contains("cc-message-assistant")) return;
|
|
157
|
+
const actions = this.ensureActionsContainer(msgEl);
|
|
158
|
+
actions.appendChild(this.createActionButton({
|
|
159
|
+
className: "cc-message-regenerate",
|
|
160
|
+
label: this.actions.regenerateLabel ?? "Regenerate",
|
|
161
|
+
icon: REGENERATE_ICON,
|
|
162
|
+
onClick: () => {
|
|
163
|
+
var _a2, _b;
|
|
164
|
+
return (_b = (_a2 = this.actions) == null ? void 0 : _a2.onRegenerate) == null ? void 0 : _b.call(_a2, messageId);
|
|
165
|
+
}
|
|
166
|
+
}));
|
|
167
|
+
}
|
|
168
|
+
setVisible(visible) {
|
|
169
|
+
this.container.classList.toggle("cc-hidden", !visible);
|
|
170
|
+
}
|
|
142
171
|
scrollToBottom() {
|
|
143
172
|
this.container.scrollTop = this.container.scrollHeight;
|
|
144
173
|
}
|
|
174
|
+
createMessageElement(msg) {
|
|
175
|
+
const el = document.createElement("div");
|
|
176
|
+
this.renderMessageElement(el, msg);
|
|
177
|
+
return el;
|
|
178
|
+
}
|
|
179
|
+
renderMessageElement(el, msg) {
|
|
180
|
+
el.className = `cc-message cc-message-${msg.role}`;
|
|
181
|
+
el.dataset.id = msg.id;
|
|
182
|
+
if (msg.status) {
|
|
183
|
+
el.dataset.status = msg.status;
|
|
184
|
+
} else {
|
|
185
|
+
delete el.dataset.status;
|
|
186
|
+
}
|
|
187
|
+
if (msg.errorType) {
|
|
188
|
+
el.dataset.errorType = msg.errorType;
|
|
189
|
+
} else {
|
|
190
|
+
delete el.dataset.errorType;
|
|
191
|
+
}
|
|
192
|
+
el.innerHTML = "";
|
|
193
|
+
const content = document.createElement("div");
|
|
194
|
+
content.className = "cc-message-content";
|
|
195
|
+
content.innerHTML = msg.role === "assistant" ? renderMarkdown(msg.content) : this.escapeHtml(msg.content);
|
|
196
|
+
el.appendChild(content);
|
|
197
|
+
const actions = this.createActionsContainer(msg);
|
|
198
|
+
if (actions) {
|
|
199
|
+
el.appendChild(actions);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
createActionsContainer(msg) {
|
|
203
|
+
var _a;
|
|
204
|
+
if (msg.status !== "error" || !((_a = this.actions) == null ? void 0 : _a.onRetry)) {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
const actions = document.createElement("div");
|
|
208
|
+
actions.className = "cc-message-actions";
|
|
209
|
+
actions.appendChild(this.createActionButton({
|
|
210
|
+
className: "cc-message-retry",
|
|
211
|
+
label: this.actions.retryLabel ?? "Retry",
|
|
212
|
+
icon: RETRY_ICON,
|
|
213
|
+
onClick: () => {
|
|
214
|
+
var _a2, _b;
|
|
215
|
+
return (_b = (_a2 = this.actions) == null ? void 0 : _a2.onRetry) == null ? void 0 : _b.call(_a2, msg.id);
|
|
216
|
+
}
|
|
217
|
+
}));
|
|
218
|
+
return actions;
|
|
219
|
+
}
|
|
220
|
+
ensureActionsContainer(msgEl) {
|
|
221
|
+
const existing = msgEl.querySelector(".cc-message-actions");
|
|
222
|
+
if (existing) {
|
|
223
|
+
return existing;
|
|
224
|
+
}
|
|
225
|
+
const actions = document.createElement("div");
|
|
226
|
+
actions.className = "cc-message-actions";
|
|
227
|
+
msgEl.appendChild(actions);
|
|
228
|
+
return actions;
|
|
229
|
+
}
|
|
230
|
+
cleanupEmptyActionContainers() {
|
|
231
|
+
this.container.querySelectorAll(".cc-message-actions").forEach((container) => {
|
|
232
|
+
if (!container.hasChildNodes()) {
|
|
233
|
+
container.remove();
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
createActionButton(options) {
|
|
238
|
+
const button = document.createElement("button");
|
|
239
|
+
button.type = "button";
|
|
240
|
+
button.className = options.className;
|
|
241
|
+
button.innerHTML = `${options.icon}<span>${this.escapeHtml(options.label)}</span>`;
|
|
242
|
+
button.addEventListener("click", options.onClick);
|
|
243
|
+
return button;
|
|
244
|
+
}
|
|
145
245
|
escapeHtml(text) {
|
|
146
246
|
const div = document.createElement("div");
|
|
147
247
|
div.textContent = text;
|
|
@@ -188,6 +288,9 @@ class Input {
|
|
|
188
288
|
this.sendBtn.disabled = true;
|
|
189
289
|
this.autoResize();
|
|
190
290
|
}
|
|
291
|
+
setVisible(visible) {
|
|
292
|
+
this.area.classList.toggle("cc-hidden", !visible);
|
|
293
|
+
}
|
|
191
294
|
send() {
|
|
192
295
|
const text = this.textarea.value.trim();
|
|
193
296
|
if (!text) return;
|
|
@@ -214,6 +317,7 @@ class Panel {
|
|
|
214
317
|
__publicField(this, "inline");
|
|
215
318
|
__publicField(this, "messages");
|
|
216
319
|
__publicField(this, "input");
|
|
320
|
+
__publicField(this, "messagesContainer");
|
|
217
321
|
this.inline = options.inline ?? false;
|
|
218
322
|
this.el = document.createElement("div");
|
|
219
323
|
this.el.className = "cc-panel";
|
|
@@ -250,7 +354,14 @@ class Panel {
|
|
|
250
354
|
header.appendChild(closeBtn);
|
|
251
355
|
}
|
|
252
356
|
this.el.appendChild(header);
|
|
253
|
-
|
|
357
|
+
if (options.preChatEnabled) {
|
|
358
|
+
this.messagesContainer = document.createElement("div");
|
|
359
|
+
this.messagesContainer.className = "cc-messages-wrapper";
|
|
360
|
+
this.el.appendChild(this.messagesContainer);
|
|
361
|
+
} else {
|
|
362
|
+
this.messagesContainer = this.el;
|
|
363
|
+
}
|
|
364
|
+
this.messages = new Messages(this.messagesContainer, options.messageActions);
|
|
254
365
|
this.input = new Input(this.el, {
|
|
255
366
|
placeholder: options.placeholder,
|
|
256
367
|
onSend: options.onSend
|
|
@@ -276,6 +387,12 @@ class Panel {
|
|
|
276
387
|
addMessage(msg) {
|
|
277
388
|
return this.messages.addMessage(msg);
|
|
278
389
|
}
|
|
390
|
+
hideMessages() {
|
|
391
|
+
this.messages.setVisible(false);
|
|
392
|
+
}
|
|
393
|
+
showMessages() {
|
|
394
|
+
this.messages.setVisible(true);
|
|
395
|
+
}
|
|
279
396
|
destroy() {
|
|
280
397
|
this.el.remove();
|
|
281
398
|
}
|
|
@@ -414,7 +531,11 @@ class ConversationStorage {
|
|
|
414
531
|
}
|
|
415
532
|
save(conversation) {
|
|
416
533
|
try {
|
|
417
|
-
|
|
534
|
+
const filtered = {
|
|
535
|
+
...conversation,
|
|
536
|
+
messages: conversation.messages.filter((m) => m.status !== "error").map(({ status, ...message }) => message)
|
|
537
|
+
};
|
|
538
|
+
localStorage.setItem(this.key, JSON.stringify(filtered));
|
|
418
539
|
} catch {
|
|
419
540
|
}
|
|
420
541
|
}
|
|
@@ -437,6 +558,19 @@ class ConversationStorage {
|
|
|
437
558
|
return crypto.randomUUID();
|
|
438
559
|
}
|
|
439
560
|
}
|
|
561
|
+
isPreChatCompleted(sessionId) {
|
|
562
|
+
try {
|
|
563
|
+
return sessionStorage.getItem(`chatcops-prechat-${sessionId}`) === "true";
|
|
564
|
+
} catch {
|
|
565
|
+
return false;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
setPreChatCompleted(sessionId) {
|
|
569
|
+
try {
|
|
570
|
+
sessionStorage.setItem(`chatcops-prechat-${sessionId}`, "true");
|
|
571
|
+
} catch {
|
|
572
|
+
}
|
|
573
|
+
}
|
|
440
574
|
}
|
|
441
575
|
function applyTheme(root, theme) {
|
|
442
576
|
const host = root.host;
|
|
@@ -489,13 +623,174 @@ const en = {
|
|
|
489
623
|
typingIndicator: "Typing...",
|
|
490
624
|
poweredBy: "Powered by ChatCops",
|
|
491
625
|
newConversation: "New conversation",
|
|
492
|
-
welcomeBubbleDefault: "Need help? Chat with us!"
|
|
626
|
+
welcomeBubbleDefault: "Need help? Chat with us!",
|
|
627
|
+
preChatTitle: "Before we start...",
|
|
628
|
+
preChatSubtitle: "Tell us a bit about yourself",
|
|
629
|
+
preChatSubmit: "Start Chat",
|
|
630
|
+
preChatRequired: "This field is required",
|
|
631
|
+
preChatInvalidEmail: "Please enter a valid email",
|
|
632
|
+
retryButton: "Retry",
|
|
633
|
+
regenerateButton: "Regenerate"
|
|
493
634
|
};
|
|
494
635
|
const locales = { en };
|
|
495
636
|
function getWidgetLocale(code, overrides) {
|
|
496
637
|
const base = locales[code] ?? en;
|
|
497
638
|
return overrides ? { ...base, ...overrides } : base;
|
|
498
639
|
}
|
|
640
|
+
class PreChatForm {
|
|
641
|
+
constructor(parent, options) {
|
|
642
|
+
__publicField(this, "container");
|
|
643
|
+
__publicField(this, "fieldElements", /* @__PURE__ */ new Map());
|
|
644
|
+
__publicField(this, "errorElements", /* @__PURE__ */ new Map());
|
|
645
|
+
__publicField(this, "submitBtn");
|
|
646
|
+
__publicField(this, "options");
|
|
647
|
+
this.options = options;
|
|
648
|
+
this.container = document.createElement("div");
|
|
649
|
+
this.container.className = "cc-prechat";
|
|
650
|
+
if (options.title) {
|
|
651
|
+
const title = document.createElement("div");
|
|
652
|
+
title.className = "cc-prechat-title";
|
|
653
|
+
title.textContent = options.title;
|
|
654
|
+
this.container.appendChild(title);
|
|
655
|
+
}
|
|
656
|
+
if (options.subtitle) {
|
|
657
|
+
const subtitle = document.createElement("div");
|
|
658
|
+
subtitle.className = "cc-prechat-subtitle";
|
|
659
|
+
subtitle.textContent = options.subtitle;
|
|
660
|
+
this.container.appendChild(subtitle);
|
|
661
|
+
}
|
|
662
|
+
const form = document.createElement("form");
|
|
663
|
+
form.className = "cc-prechat-fields";
|
|
664
|
+
form.addEventListener("submit", (e) => {
|
|
665
|
+
e.preventDefault();
|
|
666
|
+
this.handleSubmit();
|
|
667
|
+
});
|
|
668
|
+
for (const field of options.fields) {
|
|
669
|
+
form.appendChild(this.createField(field));
|
|
670
|
+
}
|
|
671
|
+
this.submitBtn = document.createElement("button");
|
|
672
|
+
this.submitBtn.type = "submit";
|
|
673
|
+
this.submitBtn.className = "cc-prechat-submit";
|
|
674
|
+
this.submitBtn.textContent = options.submitLabel;
|
|
675
|
+
form.appendChild(this.submitBtn);
|
|
676
|
+
this.container.appendChild(form);
|
|
677
|
+
parent.appendChild(this.container);
|
|
678
|
+
this.updateSubmitState();
|
|
679
|
+
const firstInput = this.fieldElements.values().next().value;
|
|
680
|
+
if (firstInput) {
|
|
681
|
+
setTimeout(() => firstInput.focus(), 100);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
createField(field) {
|
|
685
|
+
const wrapper = document.createElement("div");
|
|
686
|
+
wrapper.className = "cc-prechat-field";
|
|
687
|
+
const label = document.createElement("label");
|
|
688
|
+
label.className = "cc-prechat-label";
|
|
689
|
+
label.textContent = field.label;
|
|
690
|
+
if (field.required) {
|
|
691
|
+
const req = document.createElement("span");
|
|
692
|
+
req.className = "cc-required";
|
|
693
|
+
req.textContent = "*";
|
|
694
|
+
label.appendChild(req);
|
|
695
|
+
}
|
|
696
|
+
wrapper.appendChild(label);
|
|
697
|
+
let input;
|
|
698
|
+
if (field.type === "select") {
|
|
699
|
+
const select = document.createElement("select");
|
|
700
|
+
select.className = "cc-prechat-select";
|
|
701
|
+
const defaultOpt = document.createElement("option");
|
|
702
|
+
defaultOpt.value = "";
|
|
703
|
+
defaultOpt.textContent = field.placeholder ?? `Select ${field.label.toLowerCase()}...`;
|
|
704
|
+
defaultOpt.disabled = true;
|
|
705
|
+
defaultOpt.selected = true;
|
|
706
|
+
select.appendChild(defaultOpt);
|
|
707
|
+
for (const opt of field.options ?? []) {
|
|
708
|
+
const option = document.createElement("option");
|
|
709
|
+
option.value = opt;
|
|
710
|
+
option.textContent = opt;
|
|
711
|
+
select.appendChild(option);
|
|
712
|
+
}
|
|
713
|
+
input = select;
|
|
714
|
+
} else if (field.type === "textarea") {
|
|
715
|
+
const textarea = document.createElement("textarea");
|
|
716
|
+
textarea.className = "cc-prechat-textarea";
|
|
717
|
+
textarea.placeholder = field.placeholder ?? "";
|
|
718
|
+
textarea.rows = 3;
|
|
719
|
+
input = textarea;
|
|
720
|
+
} else {
|
|
721
|
+
const textInput = document.createElement("input");
|
|
722
|
+
textInput.type = field.type;
|
|
723
|
+
textInput.className = "cc-prechat-input";
|
|
724
|
+
textInput.placeholder = field.placeholder ?? "";
|
|
725
|
+
input = textInput;
|
|
726
|
+
}
|
|
727
|
+
input.name = field.name;
|
|
728
|
+
input.addEventListener("input", () => {
|
|
729
|
+
this.clearError(field.name);
|
|
730
|
+
this.updateSubmitState();
|
|
731
|
+
});
|
|
732
|
+
if (input instanceof HTMLSelectElement) {
|
|
733
|
+
input.addEventListener("change", () => {
|
|
734
|
+
this.clearError(field.name);
|
|
735
|
+
this.updateSubmitState();
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
wrapper.appendChild(input);
|
|
739
|
+
this.fieldElements.set(field.name, input);
|
|
740
|
+
const error = document.createElement("div");
|
|
741
|
+
error.className = "cc-prechat-error";
|
|
742
|
+
wrapper.appendChild(error);
|
|
743
|
+
this.errorElements.set(field.name, error);
|
|
744
|
+
return wrapper;
|
|
745
|
+
}
|
|
746
|
+
updateSubmitState() {
|
|
747
|
+
const allRequiredFilled = this.options.fields.every((field) => {
|
|
748
|
+
if (!field.required) return true;
|
|
749
|
+
const el = this.fieldElements.get(field.name);
|
|
750
|
+
return el ? el.value.trim().length > 0 : false;
|
|
751
|
+
});
|
|
752
|
+
this.submitBtn.disabled = !allRequiredFilled;
|
|
753
|
+
}
|
|
754
|
+
handleSubmit() {
|
|
755
|
+
const data = {};
|
|
756
|
+
let valid = true;
|
|
757
|
+
for (const field of this.options.fields) {
|
|
758
|
+
const el = this.fieldElements.get(field.name);
|
|
759
|
+
if (!el) continue;
|
|
760
|
+
const value = el.value.trim();
|
|
761
|
+
data[field.name] = value;
|
|
762
|
+
if (field.required && !value) {
|
|
763
|
+
this.showError(field.name, this.options.locale.preChatRequired);
|
|
764
|
+
valid = false;
|
|
765
|
+
continue;
|
|
766
|
+
}
|
|
767
|
+
if (field.type === "email" && value && !this.isValidEmail(value)) {
|
|
768
|
+
this.showError(field.name, this.options.locale.preChatInvalidEmail);
|
|
769
|
+
valid = false;
|
|
770
|
+
continue;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
if (valid) {
|
|
774
|
+
this.options.onSubmit(data);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
showError(fieldName, message) {
|
|
778
|
+
const el = this.errorElements.get(fieldName);
|
|
779
|
+
if (el) el.textContent = message;
|
|
780
|
+
}
|
|
781
|
+
clearError(fieldName) {
|
|
782
|
+
const el = this.errorElements.get(fieldName);
|
|
783
|
+
if (el) el.textContent = "";
|
|
784
|
+
}
|
|
785
|
+
isValidEmail(email) {
|
|
786
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
787
|
+
}
|
|
788
|
+
destroy() {
|
|
789
|
+
this.container.remove();
|
|
790
|
+
this.fieldElements.clear();
|
|
791
|
+
this.errorElements.clear();
|
|
792
|
+
}
|
|
793
|
+
}
|
|
499
794
|
class Widget {
|
|
500
795
|
constructor(config) {
|
|
501
796
|
__publicField(this, "config");
|
|
@@ -503,12 +798,16 @@ class Widget {
|
|
|
503
798
|
__publicField(this, "fab");
|
|
504
799
|
__publicField(this, "panel");
|
|
505
800
|
__publicField(this, "bubble");
|
|
801
|
+
__publicField(this, "preChatFormEl");
|
|
506
802
|
__publicField(this, "client");
|
|
507
803
|
__publicField(this, "storage");
|
|
508
804
|
__publicField(this, "locale");
|
|
509
805
|
__publicField(this, "conversationId");
|
|
510
806
|
__publicField(this, "messages", []);
|
|
807
|
+
__publicField(this, "userData");
|
|
808
|
+
__publicField(this, "userDataSent", false);
|
|
511
809
|
__publicField(this, "isStreaming", false);
|
|
810
|
+
__publicField(this, "retryRequests", /* @__PURE__ */ new Map());
|
|
512
811
|
__publicField(this, "eventHandlers", /* @__PURE__ */ new Map());
|
|
513
812
|
__publicField(this, "containerEl");
|
|
514
813
|
this.config = config;
|
|
@@ -534,7 +833,7 @@ class Widget {
|
|
|
534
833
|
return this.config.container;
|
|
535
834
|
}
|
|
536
835
|
init() {
|
|
537
|
-
var _a, _b, _c, _d;
|
|
836
|
+
var _a, _b, _c, _d, _e, _f;
|
|
538
837
|
if (this.isInline) {
|
|
539
838
|
this.containerEl = this.resolveContainer();
|
|
540
839
|
this.shadow = createShadowRoot(this.containerEl);
|
|
@@ -554,13 +853,24 @@ class Widget {
|
|
|
554
853
|
this.panel = new Panel(this.shadow, {
|
|
555
854
|
position,
|
|
556
855
|
inline: this.isInline,
|
|
856
|
+
preChatEnabled: ((_b = this.config.preChatForm) == null ? void 0 : _b.enabled) ?? false,
|
|
557
857
|
branding: {
|
|
558
|
-
name: ((
|
|
559
|
-
avatar: (
|
|
560
|
-
subtitle: ((
|
|
858
|
+
name: ((_c = this.config.branding) == null ? void 0 : _c.name) ?? "AI Assistant",
|
|
859
|
+
avatar: (_d = this.config.branding) == null ? void 0 : _d.avatar,
|
|
860
|
+
subtitle: ((_e = this.config.branding) == null ? void 0 : _e.subtitle) ?? "Online"
|
|
561
861
|
},
|
|
562
862
|
placeholder: this.config.placeholder ?? this.locale.inputPlaceholder,
|
|
563
863
|
footerText: this.locale.poweredBy,
|
|
864
|
+
messageActions: {
|
|
865
|
+
onRetry: (messageId) => {
|
|
866
|
+
void this.handleRetry(messageId);
|
|
867
|
+
},
|
|
868
|
+
onRegenerate: (messageId) => {
|
|
869
|
+
void this.handleRegenerate(messageId);
|
|
870
|
+
},
|
|
871
|
+
retryLabel: this.locale.retryButton,
|
|
872
|
+
regenerateLabel: this.locale.regenerateButton
|
|
873
|
+
},
|
|
564
874
|
onSend: (text) => this.handleSend(text),
|
|
565
875
|
onClose: () => this.close()
|
|
566
876
|
});
|
|
@@ -574,16 +884,13 @@ class Widget {
|
|
|
574
884
|
}
|
|
575
885
|
}
|
|
576
886
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
content: welcomeText
|
|
583
|
-
};
|
|
584
|
-
this.messages.push(welcomeMsg);
|
|
585
|
-
this.panel.addMessage(welcomeMsg);
|
|
887
|
+
const shouldShowPreChat = ((_f = this.config.preChatForm) == null ? void 0 : _f.enabled) && !this.isPreChatCompleted() && this.messages.length === 0;
|
|
888
|
+
if (shouldShowPreChat) {
|
|
889
|
+
this.showPreChatForm();
|
|
890
|
+
} else {
|
|
891
|
+
this.showChatView();
|
|
586
892
|
}
|
|
893
|
+
this.showLatestRegenerateButton();
|
|
587
894
|
if (!this.isInline && this.config.welcomeBubble) {
|
|
588
895
|
this.bubble = new WelcomeBubble(this.shadow, {
|
|
589
896
|
text: this.config.welcomeBubble.text,
|
|
@@ -629,10 +936,11 @@ class Widget {
|
|
|
629
936
|
}
|
|
630
937
|
}
|
|
631
938
|
destroy() {
|
|
632
|
-
var _a, _b;
|
|
633
|
-
(_a = this.
|
|
939
|
+
var _a, _b, _c;
|
|
940
|
+
(_a = this.preChatFormEl) == null ? void 0 : _a.destroy();
|
|
941
|
+
(_b = this.bubble) == null ? void 0 : _b.destroy();
|
|
634
942
|
this.panel.destroy();
|
|
635
|
-
(
|
|
943
|
+
(_c = this.fab) == null ? void 0 : _c.destroy();
|
|
636
944
|
destroyShadowRoot(this.containerEl);
|
|
637
945
|
this.eventHandlers.clear();
|
|
638
946
|
this.messages = [];
|
|
@@ -651,8 +959,56 @@ class Widget {
|
|
|
651
959
|
var _a;
|
|
652
960
|
(_a = this.eventHandlers.get(event)) == null ? void 0 : _a.forEach((h) => h(...args));
|
|
653
961
|
}
|
|
962
|
+
isPreChatCompleted() {
|
|
963
|
+
return this.storage.isPreChatCompleted(this.conversationId);
|
|
964
|
+
}
|
|
965
|
+
setPreChatCompleted() {
|
|
966
|
+
this.storage.setPreChatCompleted(this.conversationId);
|
|
967
|
+
}
|
|
968
|
+
showPreChatForm() {
|
|
969
|
+
const formConfig = this.config.preChatForm;
|
|
970
|
+
this.panel.hideMessages();
|
|
971
|
+
this.panel.input.setVisible(false);
|
|
972
|
+
this.preChatFormEl = new PreChatForm(this.panel.messagesContainer, {
|
|
973
|
+
title: formConfig.title ?? this.locale.preChatTitle,
|
|
974
|
+
subtitle: formConfig.subtitle ?? this.locale.preChatSubtitle,
|
|
975
|
+
fields: formConfig.fields,
|
|
976
|
+
submitLabel: formConfig.submitLabel ?? this.locale.preChatSubmit,
|
|
977
|
+
locale: this.locale,
|
|
978
|
+
onSubmit: (data) => this.handlePreChatSubmit(data)
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
handlePreChatSubmit(data) {
|
|
982
|
+
var _a, _b, _c;
|
|
983
|
+
this.userData = data;
|
|
984
|
+
this.setPreChatCompleted();
|
|
985
|
+
(_a = this.preChatFormEl) == null ? void 0 : _a.destroy();
|
|
986
|
+
this.preChatFormEl = void 0;
|
|
987
|
+
this.panel.showMessages();
|
|
988
|
+
this.panel.input.setVisible(true);
|
|
989
|
+
this.showChatView();
|
|
990
|
+
(_c = (_b = this.config).onPreChatSubmit) == null ? void 0 : _c.call(_b, data);
|
|
991
|
+
this.emit("preChatSubmit", data);
|
|
992
|
+
if (data.message) {
|
|
993
|
+
this.handleSend(data.message);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
showChatView() {
|
|
997
|
+
var _a;
|
|
998
|
+
if (this.messages.length === 0) {
|
|
999
|
+
const userName = (_a = this.userData) == null ? void 0 : _a.name;
|
|
1000
|
+
const welcomeText = userName ? `Hi ${userName}! ${this.config.welcomeMessage ?? this.locale.welcomeMessage}` : this.config.welcomeMessage ?? this.locale.welcomeMessage;
|
|
1001
|
+
const welcomeMsg = {
|
|
1002
|
+
id: "welcome",
|
|
1003
|
+
role: "assistant",
|
|
1004
|
+
content: welcomeText
|
|
1005
|
+
};
|
|
1006
|
+
this.messages.push(welcomeMsg);
|
|
1007
|
+
this.panel.addMessage(welcomeMsg);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
654
1010
|
async handleSend(text) {
|
|
655
|
-
var _a, _b
|
|
1011
|
+
var _a, _b;
|
|
656
1012
|
if (this.isStreaming) return;
|
|
657
1013
|
const userMsg = {
|
|
658
1014
|
id: crypto.randomUUID(),
|
|
@@ -663,33 +1019,97 @@ class Widget {
|
|
|
663
1019
|
this.panel.addMessage(userMsg);
|
|
664
1020
|
(_b = (_a = this.config).onMessage) == null ? void 0 : _b.call(_a, userMsg);
|
|
665
1021
|
this.emit("message", userMsg);
|
|
1022
|
+
await this.sendToServer(text, { userMessageId: userMsg.id });
|
|
1023
|
+
}
|
|
1024
|
+
async handleRetry(errorMessageId) {
|
|
1025
|
+
if (this.isStreaming) return;
|
|
1026
|
+
const errorIndex = this.messages.findIndex((message) => message.id === errorMessageId);
|
|
1027
|
+
if (errorIndex === -1) return;
|
|
1028
|
+
const retryRequest = this.retryRequests.get(errorMessageId);
|
|
1029
|
+
let userMessage = (retryRequest == null ? void 0 : retryRequest.userMessageId) ? this.messages.find((message) => message.id === retryRequest.userMessageId) : void 0;
|
|
1030
|
+
if (!userMessage) {
|
|
1031
|
+
for (let i = errorIndex - 1; i >= 0; i--) {
|
|
1032
|
+
if (this.messages[i].role === "user") {
|
|
1033
|
+
userMessage = this.messages[i];
|
|
1034
|
+
break;
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
if (!userMessage) return;
|
|
1039
|
+
this.panel.messages.removeMessage(errorMessageId);
|
|
1040
|
+
this.retryRequests.delete(errorMessageId);
|
|
1041
|
+
this.messages = this.messages.filter((message) => message.id !== errorMessageId);
|
|
1042
|
+
await this.sendToServer(userMessage.content, {
|
|
1043
|
+
regenerate: retryRequest == null ? void 0 : retryRequest.regenerate,
|
|
1044
|
+
userMessageId: (retryRequest == null ? void 0 : retryRequest.userMessageId) ?? userMessage.id
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
async handleRegenerate(assistantMessageId) {
|
|
1048
|
+
if (this.isStreaming) return;
|
|
1049
|
+
const assistantIndex = this.messages.findIndex((message) => message.id === assistantMessageId);
|
|
1050
|
+
if (assistantIndex === -1) return;
|
|
1051
|
+
const assistantMessage = this.messages[assistantIndex];
|
|
1052
|
+
if (assistantMessage.role !== "assistant") return;
|
|
1053
|
+
const userMessage = this.findUserMessageForAssistant(assistantIndex);
|
|
1054
|
+
if (!userMessage) return;
|
|
1055
|
+
this.panel.messages.clearRegenerateButtons();
|
|
1056
|
+
this.panel.messages.removeMessage(assistantMessageId);
|
|
1057
|
+
this.retryRequests.delete(assistantMessageId);
|
|
1058
|
+
this.messages = this.messages.filter((message) => message.id !== assistantMessageId);
|
|
1059
|
+
await this.sendToServer(userMessage.content, {
|
|
1060
|
+
regenerate: true,
|
|
1061
|
+
userMessageId: userMessage.id
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
async sendToServer(text, options = {}) {
|
|
1065
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
1066
|
+
if (this.isStreaming) return;
|
|
666
1067
|
this.isStreaming = true;
|
|
667
1068
|
this.panel.input.setDisabled(true);
|
|
1069
|
+
this.panel.messages.clearRegenerateButtons();
|
|
668
1070
|
this.panel.messages.showTyping();
|
|
669
1071
|
const assistantMsg = {
|
|
670
1072
|
id: crypto.randomUUID(),
|
|
671
1073
|
role: "assistant",
|
|
672
|
-
content: ""
|
|
1074
|
+
content: "",
|
|
1075
|
+
status: "streaming"
|
|
673
1076
|
};
|
|
674
|
-
const
|
|
1077
|
+
const includeUserData = !!(this.userData && !this.userDataSent);
|
|
1078
|
+
let firstChunk = true;
|
|
1079
|
+
let requestCompleted = false;
|
|
1080
|
+
let hasError = false;
|
|
675
1081
|
try {
|
|
676
|
-
|
|
677
|
-
for await (const chunk of this.client.sendMessage({
|
|
678
|
-
conversationId: this.conversationId,
|
|
679
|
-
message: text,
|
|
680
|
-
pageContext,
|
|
681
|
-
locale: this.config.locale
|
|
682
|
-
})) {
|
|
1082
|
+
for await (const chunk of this.client.sendMessage(this.createRequest(text, options, includeUserData))) {
|
|
683
1083
|
if (chunk.error) {
|
|
684
|
-
const
|
|
685
|
-
assistantMsg.content = this.
|
|
1084
|
+
const errorType = this.resolveErrorType(chunk.error);
|
|
1085
|
+
assistantMsg.content = this.getErrorMessage(errorType);
|
|
1086
|
+
assistantMsg.status = "error";
|
|
1087
|
+
assistantMsg.errorType = errorType;
|
|
1088
|
+
hasError = true;
|
|
1089
|
+
this.retryRequests.set(assistantMsg.id, {
|
|
1090
|
+
regenerate: options.regenerate,
|
|
1091
|
+
userMessageId: options.userMessageId
|
|
1092
|
+
});
|
|
686
1093
|
this.panel.messages.removeTyping();
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
1094
|
+
if (firstChunk) {
|
|
1095
|
+
this.panel.addMessage(assistantMsg);
|
|
1096
|
+
} else {
|
|
1097
|
+
this.panel.messages.updateMessage(assistantMsg);
|
|
1098
|
+
}
|
|
1099
|
+
this.messages.push(assistantMsg);
|
|
1100
|
+
const error = new Error(chunk.error);
|
|
1101
|
+
(_b = (_a = this.config).onError) == null ? void 0 : _b.call(_a, error);
|
|
1102
|
+
this.emit("error", error);
|
|
690
1103
|
break;
|
|
691
1104
|
}
|
|
692
|
-
if (chunk.done)
|
|
1105
|
+
if (chunk.done) {
|
|
1106
|
+
requestCompleted = true;
|
|
1107
|
+
break;
|
|
1108
|
+
}
|
|
1109
|
+
if (chunk.leadCaptured) {
|
|
1110
|
+
(_d = (_c = this.config).onLeadCaptured) == null ? void 0 : _d.call(_c, chunk.leadData);
|
|
1111
|
+
this.emit("leadCaptured", chunk.leadData);
|
|
1112
|
+
}
|
|
693
1113
|
if (chunk.content) {
|
|
694
1114
|
if (firstChunk) {
|
|
695
1115
|
this.panel.messages.removeTyping();
|
|
@@ -697,29 +1117,113 @@ class Widget {
|
|
|
697
1117
|
firstChunk = false;
|
|
698
1118
|
}
|
|
699
1119
|
assistantMsg.content += chunk.content;
|
|
700
|
-
this.panel.messages.updateMessage(assistantMsg
|
|
1120
|
+
this.panel.messages.updateMessage(assistantMsg);
|
|
701
1121
|
}
|
|
702
1122
|
}
|
|
703
|
-
if (
|
|
1123
|
+
if (!hasError && !requestCompleted) {
|
|
1124
|
+
requestCompleted = true;
|
|
1125
|
+
}
|
|
1126
|
+
if (!hasError && firstChunk) {
|
|
1127
|
+
this.panel.messages.removeTyping();
|
|
1128
|
+
}
|
|
1129
|
+
if (!hasError && assistantMsg.content) {
|
|
1130
|
+
assistantMsg.status = "complete";
|
|
1131
|
+
this.panel.messages.updateMessage(assistantMsg);
|
|
704
1132
|
this.messages.push(assistantMsg);
|
|
705
1133
|
(_f = (_e = this.config).onMessage) == null ? void 0 : _f.call(_e, assistantMsg);
|
|
706
1134
|
this.emit("message", assistantMsg);
|
|
1135
|
+
this.showLatestRegenerateButton();
|
|
707
1136
|
if (!this.isInline && !this.panel.isVisible) {
|
|
708
1137
|
(_g = this.fab) == null ? void 0 : _g.showBadge();
|
|
709
1138
|
}
|
|
710
1139
|
}
|
|
711
1140
|
} catch (err) {
|
|
712
|
-
this.panel.messages.removeTyping();
|
|
713
1141
|
assistantMsg.content = this.locale.errorGeneric;
|
|
1142
|
+
assistantMsg.status = "error";
|
|
1143
|
+
assistantMsg.errorType = "network";
|
|
1144
|
+
this.retryRequests.set(assistantMsg.id, {
|
|
1145
|
+
regenerate: options.regenerate,
|
|
1146
|
+
userMessageId: options.userMessageId
|
|
1147
|
+
});
|
|
1148
|
+
this.panel.messages.removeTyping();
|
|
714
1149
|
this.panel.addMessage(assistantMsg);
|
|
715
|
-
|
|
1150
|
+
this.messages.push(assistantMsg);
|
|
1151
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
1152
|
+
(_i = (_h = this.config).onError) == null ? void 0 : _i.call(_h, error);
|
|
1153
|
+
this.emit("error", error);
|
|
716
1154
|
} finally {
|
|
1155
|
+
if (includeUserData && requestCompleted) {
|
|
1156
|
+
this.userDataSent = true;
|
|
1157
|
+
}
|
|
717
1158
|
this.isStreaming = false;
|
|
718
1159
|
this.panel.input.setDisabled(false);
|
|
719
1160
|
this.panel.input.focus();
|
|
720
1161
|
this.saveHistory();
|
|
721
1162
|
}
|
|
722
1163
|
}
|
|
1164
|
+
createRequest(text, options, includeUserData) {
|
|
1165
|
+
const pageContext = this.config.pageContext !== false ? { url: window.location.href, title: document.title } : void 0;
|
|
1166
|
+
return {
|
|
1167
|
+
conversationId: this.conversationId,
|
|
1168
|
+
message: text,
|
|
1169
|
+
messageId: options.userMessageId,
|
|
1170
|
+
regenerate: options.regenerate,
|
|
1171
|
+
pageContext,
|
|
1172
|
+
locale: this.config.locale,
|
|
1173
|
+
...includeUserData ? { userData: this.userData } : {}
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
findUserMessageForAssistant(assistantIndex) {
|
|
1177
|
+
for (let i = assistantIndex - 1; i >= 0; i--) {
|
|
1178
|
+
if (this.messages[i].role === "user") {
|
|
1179
|
+
return this.messages[i];
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
return void 0;
|
|
1183
|
+
}
|
|
1184
|
+
findLatestRegeneratableAssistant() {
|
|
1185
|
+
for (let i = this.messages.length - 1; i >= 0; i--) {
|
|
1186
|
+
const message = this.messages[i];
|
|
1187
|
+
if (message.id === "welcome") {
|
|
1188
|
+
continue;
|
|
1189
|
+
}
|
|
1190
|
+
if (message.role !== "assistant" || message.status === "error") {
|
|
1191
|
+
return void 0;
|
|
1192
|
+
}
|
|
1193
|
+
return message;
|
|
1194
|
+
}
|
|
1195
|
+
return void 0;
|
|
1196
|
+
}
|
|
1197
|
+
showLatestRegenerateButton() {
|
|
1198
|
+
const assistantMessage = this.findLatestRegeneratableAssistant();
|
|
1199
|
+
if (assistantMessage) {
|
|
1200
|
+
this.panel.messages.showRegenerateButton(assistantMessage.id);
|
|
1201
|
+
} else {
|
|
1202
|
+
this.panel.messages.clearRegenerateButtons();
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
resolveErrorType(error) {
|
|
1206
|
+
switch (error) {
|
|
1207
|
+
case "rate_limit":
|
|
1208
|
+
case "network":
|
|
1209
|
+
case "provider_error":
|
|
1210
|
+
case "timeout":
|
|
1211
|
+
return error;
|
|
1212
|
+
default:
|
|
1213
|
+
return "provider_error";
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
getErrorMessage(errorType) {
|
|
1217
|
+
switch (errorType) {
|
|
1218
|
+
case "rate_limit":
|
|
1219
|
+
return this.locale.errorRateLimit;
|
|
1220
|
+
case "network":
|
|
1221
|
+
case "timeout":
|
|
1222
|
+
return this.locale.errorNetwork;
|
|
1223
|
+
default:
|
|
1224
|
+
return this.locale.errorGeneric;
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
723
1227
|
saveHistory() {
|
|
724
1228
|
if (this.config.persistHistory === false) return;
|
|
725
1229
|
const maxMessages = this.config.maxMessages ?? 50;
|
|
@@ -730,6 +1234,8 @@ class Widget {
|
|
|
730
1234
|
id: m.id,
|
|
731
1235
|
role: m.role,
|
|
732
1236
|
content: m.content,
|
|
1237
|
+
status: m.status,
|
|
1238
|
+
errorType: m.errorType,
|
|
733
1239
|
timestamp: Date.now()
|
|
734
1240
|
})),
|
|
735
1241
|
updatedAt: Date.now()
|