@chatcops/widget 0.4.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/api/client.d.ts +2 -0
- package/dist/api/client.d.ts.map +1 -1
- package/dist/chatcops.esm.js +352 -95
- package/dist/chatcops.min.js +1 -1
- package/dist/dom/bubble.d.ts +2 -0
- package/dist/dom/bubble.d.ts.map +1 -1
- package/dist/dom/fab.d.ts +5 -0
- package/dist/dom/fab.d.ts.map +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 +4 -0
- package/dist/dom/messages.d.ts.map +1 -1
- package/dist/dom/panel.d.ts +5 -0
- package/dist/dom/panel.d.ts.map +1 -1
- package/dist/dom/prechat-form.d.ts.map +1 -1
- package/dist/dom/shadow.d.ts +8 -1
- package/dist/dom/shadow.d.ts.map +1 -1
- package/dist/i18n.d.ts +6 -0
- package/dist/i18n.d.ts.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/namespace.d.ts +7 -0
- package/dist/namespace.d.ts.map +1 -0
- package/dist/storage.d.ts +5 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/theme.d.ts +14 -0
- package/dist/theme.d.ts.map +1 -1
- package/dist/widget.d.ts +18 -5
- package/dist/widget.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/chatcops.esm.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
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;transform:none;transition:none}.cc-panel.cc-inline:not(.cc-visible){visibility:hidden}.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 h1,.cc-message h2,.cc-message h3,.cc-message h4,.cc-message h5,.cc-message h6{font-size:15px;font-weight:600;line-height:1.4;margin:4px 0 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:focus-within .cc-message-regenerate,.cc-message-regenerate:focus-visible{opacity:1}@media(hover:none){.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-fab.cc-open{display:none}.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}}.cc-fab:focus-visible,.cc-header-close:focus-visible,.cc-send-btn:focus-visible,.cc-message-retry:focus-visible,.cc-message-regenerate:focus-visible,.cc-bubble-close:focus-visible,.cc-prechat-submit:focus-visible{outline:2px solid var(--cc-accent);outline-offset:3px}@media(prefers-reduced-motion:reduce){.cc-fab.cc-pulse,.cc-typing-dot{animation:none}.cc-panel,.cc-bubble{transition:none}}";
|
|
4
|
+
const widgetStyles = ":host{--cc-accent: #6366f1;--cc-accent-hover: #4f46e5;--cc-on-accent: #ffffff;--cc-accent-contrast: #6164ec;--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-on-accent);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;transform:none;transition:none}.cc-panel.cc-inline:not(.cc-visible){visibility:hidden}.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-on-accent)}.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-contrast);color:var(--cc-on-accent);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-on-accent)}.cc-message-error-note{margin-top:6px;font-size:12px;color:var(--cc-text-secondary)}.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 h1,.cc-message h2,.cc-message h3,.cc-message h4,.cc-message h5,.cc-message h6{font-size:15px;font-weight:600;line-height:1.4;margin:4px 0 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:focus-within .cc-message-regenerate,.cc-message-regenerate:focus-visible{opacity:1}@media(hover:none){.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-on-accent)}.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-sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}.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-contrast);color:var(--cc-on-accent);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-fab.cc-open{display:none}.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}}.cc-fab:focus-visible,.cc-header-close:focus-visible,.cc-send-btn:focus-visible,.cc-message-retry:focus-visible,.cc-message-regenerate:focus-visible,.cc-bubble-close:focus-visible,.cc-prechat-submit:focus-visible{outline:2px solid var(--cc-accent);outline-offset:3px}@media(prefers-reduced-motion:reduce){.cc-fab.cc-pulse,.cc-typing-dot{animation:none}.cc-panel,.cc-bubble{transition:none}}";
|
|
5
|
+
const HOST_ID = "chatcops-root";
|
|
5
6
|
function createShadowRoot(container) {
|
|
6
7
|
const host = document.createElement("div");
|
|
7
|
-
host.id =
|
|
8
|
+
host.id = nextHostId();
|
|
9
|
+
host.className = HOST_ID;
|
|
8
10
|
if (container) {
|
|
9
11
|
container.appendChild(host);
|
|
10
12
|
} else {
|
|
@@ -16,50 +18,14 @@ function createShadowRoot(container) {
|
|
|
16
18
|
shadow.appendChild(style);
|
|
17
19
|
return shadow;
|
|
18
20
|
}
|
|
19
|
-
function destroyShadowRoot(
|
|
20
|
-
|
|
21
|
-
const host = searchRoot.querySelector("#chatcops-root") ?? document.getElementById("chatcops-root");
|
|
22
|
-
if (host) host.remove();
|
|
21
|
+
function destroyShadowRoot(shadow) {
|
|
22
|
+
shadow == null ? void 0 : shadow.host.remove();
|
|
23
23
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
__publicField(this, "badge");
|
|
30
|
-
__publicField(this, "isOpen", false);
|
|
31
|
-
this.el = document.createElement("button");
|
|
32
|
-
this.el.className = "cc-fab cc-pulse";
|
|
33
|
-
this.el.setAttribute("aria-label", "Open chat");
|
|
34
|
-
if (options.position === "bottom-left") {
|
|
35
|
-
this.el.classList.add("cc-left");
|
|
36
|
-
}
|
|
37
|
-
this.el.innerHTML = CHAT_ICON;
|
|
38
|
-
this.badge = document.createElement("div");
|
|
39
|
-
this.badge.className = "cc-fab-badge";
|
|
40
|
-
this.el.appendChild(this.badge);
|
|
41
|
-
this.el.addEventListener("click", () => {
|
|
42
|
-
this.el.classList.remove("cc-pulse");
|
|
43
|
-
options.onClick();
|
|
44
|
-
});
|
|
45
|
-
root.appendChild(this.el);
|
|
46
|
-
}
|
|
47
|
-
setOpen(open) {
|
|
48
|
-
this.isOpen = open;
|
|
49
|
-
this.el.innerHTML = open ? CLOSE_ICON$1 : CHAT_ICON;
|
|
50
|
-
if (!open) this.el.appendChild(this.badge);
|
|
51
|
-
this.el.classList.toggle("cc-open", open);
|
|
52
|
-
this.el.setAttribute("aria-label", open ? "Close chat" : "Open chat");
|
|
53
|
-
}
|
|
54
|
-
showBadge() {
|
|
55
|
-
this.badge.classList.add("cc-visible");
|
|
56
|
-
}
|
|
57
|
-
hideBadge() {
|
|
58
|
-
this.badge.classList.remove("cc-visible");
|
|
59
|
-
}
|
|
60
|
-
destroy() {
|
|
61
|
-
this.el.remove();
|
|
62
|
-
}
|
|
24
|
+
function nextHostId() {
|
|
25
|
+
if (!document.getElementById(HOST_ID)) return HOST_ID;
|
|
26
|
+
let n = 2;
|
|
27
|
+
while (document.getElementById(`${HOST_ID}-${n}`)) n += 1;
|
|
28
|
+
return `${HOST_ID}-${n}`;
|
|
63
29
|
}
|
|
64
30
|
const ESCAPE_MAP = {
|
|
65
31
|
"&": "&",
|
|
@@ -133,6 +99,8 @@ class Messages {
|
|
|
133
99
|
this.actions = actions;
|
|
134
100
|
this.container = document.createElement("div");
|
|
135
101
|
this.container.className = "cc-messages";
|
|
102
|
+
this.container.setAttribute("role", "log");
|
|
103
|
+
this.container.setAttribute("aria-live", "polite");
|
|
136
104
|
parent.appendChild(this.container);
|
|
137
105
|
}
|
|
138
106
|
addMessage(msg) {
|
|
@@ -143,17 +111,18 @@ class Messages {
|
|
|
143
111
|
return el;
|
|
144
112
|
}
|
|
145
113
|
updateMessage(msg) {
|
|
146
|
-
const el = this.
|
|
114
|
+
const el = this.findMessage(msg.id);
|
|
147
115
|
if (el) {
|
|
148
116
|
this.renderMessageElement(el, msg);
|
|
149
117
|
this.scrollToBottom();
|
|
150
118
|
}
|
|
151
119
|
}
|
|
152
120
|
showTyping() {
|
|
121
|
+
var _a;
|
|
153
122
|
if (this.typingEl) return;
|
|
154
123
|
this.typingEl = document.createElement("div");
|
|
155
124
|
this.typingEl.className = "cc-typing";
|
|
156
|
-
this.typingEl.innerHTML =
|
|
125
|
+
this.typingEl.innerHTML = `<span class="cc-sr-only">${this.escapeHtml(((_a = this.actions) == null ? void 0 : _a.typingLabel) ?? "Typing...")}</span><div class="cc-typing-dot" aria-hidden="true"></div><div class="cc-typing-dot" aria-hidden="true"></div><div class="cc-typing-dot" aria-hidden="true"></div>`;
|
|
157
126
|
this.container.appendChild(this.typingEl);
|
|
158
127
|
this.scrollToBottom();
|
|
159
128
|
}
|
|
@@ -168,7 +137,7 @@ class Messages {
|
|
|
168
137
|
this.typingEl = null;
|
|
169
138
|
}
|
|
170
139
|
removeMessage(id) {
|
|
171
|
-
const el = this.
|
|
140
|
+
const el = this.findMessage(id);
|
|
172
141
|
el == null ? void 0 : el.remove();
|
|
173
142
|
}
|
|
174
143
|
clearRegenerateButtons() {
|
|
@@ -179,7 +148,7 @@ class Messages {
|
|
|
179
148
|
var _a;
|
|
180
149
|
this.clearRegenerateButtons();
|
|
181
150
|
if (!((_a = this.actions) == null ? void 0 : _a.onRegenerate)) return;
|
|
182
|
-
const msgEl = this.
|
|
151
|
+
const msgEl = this.findMessage(messageId);
|
|
183
152
|
if (!msgEl || !msgEl.classList.contains("cc-message-assistant")) return;
|
|
184
153
|
const actions = this.ensureActionsContainer(msgEl);
|
|
185
154
|
actions.appendChild(this.createActionButton({
|
|
@@ -195,6 +164,10 @@ class Messages {
|
|
|
195
164
|
setVisible(visible) {
|
|
196
165
|
this.container.classList.toggle("cc-hidden", !visible);
|
|
197
166
|
}
|
|
167
|
+
findMessage(id) {
|
|
168
|
+
const found = Array.from(this.container.children).find((el) => el.dataset.id === id);
|
|
169
|
+
return found ?? null;
|
|
170
|
+
}
|
|
198
171
|
scrollToBottom() {
|
|
199
172
|
this.container.scrollTop = this.container.scrollHeight;
|
|
200
173
|
}
|
|
@@ -211,6 +184,11 @@ class Messages {
|
|
|
211
184
|
} else {
|
|
212
185
|
delete el.dataset.status;
|
|
213
186
|
}
|
|
187
|
+
if (msg.status === "streaming") {
|
|
188
|
+
el.setAttribute("aria-busy", "true");
|
|
189
|
+
} else {
|
|
190
|
+
el.removeAttribute("aria-busy");
|
|
191
|
+
}
|
|
214
192
|
if (msg.errorType) {
|
|
215
193
|
el.dataset.errorType = msg.errorType;
|
|
216
194
|
} else {
|
|
@@ -221,6 +199,12 @@ class Messages {
|
|
|
221
199
|
content.className = "cc-message-content";
|
|
222
200
|
content.innerHTML = msg.role === "assistant" ? renderMarkdown(msg.content) : this.escapeHtml(msg.content);
|
|
223
201
|
el.appendChild(content);
|
|
202
|
+
if (msg.status === "error" && msg.errorText) {
|
|
203
|
+
const note = document.createElement("div");
|
|
204
|
+
note.className = "cc-message-error-note";
|
|
205
|
+
note.textContent = msg.errorText;
|
|
206
|
+
el.appendChild(note);
|
|
207
|
+
}
|
|
224
208
|
const actions = this.createActionsContainer(msg);
|
|
225
209
|
if (actions) {
|
|
226
210
|
el.appendChild(actions);
|
|
@@ -288,6 +272,7 @@ class Input {
|
|
|
288
272
|
this.textarea = document.createElement("textarea");
|
|
289
273
|
this.textarea.className = "cc-input";
|
|
290
274
|
this.textarea.placeholder = options.placeholder;
|
|
275
|
+
this.textarea.setAttribute("aria-label", options.placeholder);
|
|
291
276
|
this.textarea.rows = 1;
|
|
292
277
|
this.textarea.maxLength = MAX_MESSAGE_LENGTH;
|
|
293
278
|
this.textarea.addEventListener("input", () => this.autoResize());
|
|
@@ -295,7 +280,7 @@ class Input {
|
|
|
295
280
|
this.sendBtn = document.createElement("button");
|
|
296
281
|
this.sendBtn.className = "cc-send-btn";
|
|
297
282
|
this.sendBtn.disabled = true;
|
|
298
|
-
this.sendBtn.setAttribute("aria-label",
|
|
283
|
+
this.sendBtn.setAttribute("aria-label", options.sendLabel);
|
|
299
284
|
this.sendBtn.innerHTML = '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>';
|
|
300
285
|
this.sendBtn.addEventListener("click", () => this.send());
|
|
301
286
|
this.textarea.addEventListener("input", () => {
|
|
@@ -339,8 +324,9 @@ class Input {
|
|
|
339
324
|
this.textarea.style.height = Math.min(this.textarea.scrollHeight, maxHeight) + "px";
|
|
340
325
|
}
|
|
341
326
|
}
|
|
327
|
+
const PANEL_ID = "cc-panel";
|
|
342
328
|
const BOT_ICON = `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z"/></svg>`;
|
|
343
|
-
const CLOSE_ICON = `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/></svg>`;
|
|
329
|
+
const CLOSE_ICON$1 = `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/></svg>`;
|
|
344
330
|
class Panel {
|
|
345
331
|
constructor(root, options) {
|
|
346
332
|
__publicField(this, "el");
|
|
@@ -357,6 +343,15 @@ class Panel {
|
|
|
357
343
|
if (options.position === "bottom-left") {
|
|
358
344
|
this.el.classList.add("cc-left");
|
|
359
345
|
}
|
|
346
|
+
if (!options.inline) {
|
|
347
|
+
this.el.id = PANEL_ID;
|
|
348
|
+
this.el.setAttribute("role", "dialog");
|
|
349
|
+
this.el.setAttribute("aria-label", options.branding.name);
|
|
350
|
+
this.el.addEventListener("keydown", (e) => {
|
|
351
|
+
if (e.key === "Escape") options.onClose();
|
|
352
|
+
});
|
|
353
|
+
this.setHidden(true);
|
|
354
|
+
}
|
|
360
355
|
const header = document.createElement("div");
|
|
361
356
|
header.className = "cc-header";
|
|
362
357
|
if (options.branding.avatar) {
|
|
@@ -378,8 +373,8 @@ class Panel {
|
|
|
378
373
|
if (!options.inline) {
|
|
379
374
|
const closeBtn = document.createElement("button");
|
|
380
375
|
closeBtn.className = "cc-header-close";
|
|
381
|
-
closeBtn.setAttribute("aria-label",
|
|
382
|
-
closeBtn.innerHTML = CLOSE_ICON;
|
|
376
|
+
closeBtn.setAttribute("aria-label", options.closeLabel);
|
|
377
|
+
closeBtn.innerHTML = CLOSE_ICON$1;
|
|
383
378
|
closeBtn.addEventListener("click", options.onClose);
|
|
384
379
|
header.appendChild(closeBtn);
|
|
385
380
|
}
|
|
@@ -394,6 +389,7 @@ class Panel {
|
|
|
394
389
|
this.messages = new Messages(this.messagesContainer, options.messageActions);
|
|
395
390
|
this.input = new Input(this.el, {
|
|
396
391
|
placeholder: options.placeholder,
|
|
392
|
+
sendLabel: options.sendLabel,
|
|
397
393
|
onSend: options.onSend
|
|
398
394
|
});
|
|
399
395
|
const footer = document.createElement("div");
|
|
@@ -405,11 +401,16 @@ class Panel {
|
|
|
405
401
|
show() {
|
|
406
402
|
this.el.classList.add("cc-visible");
|
|
407
403
|
if (!this.inline) {
|
|
404
|
+
this.setHidden(false);
|
|
408
405
|
this.input.focus();
|
|
409
406
|
}
|
|
410
407
|
}
|
|
411
408
|
hide() {
|
|
412
409
|
this.el.classList.remove("cc-visible");
|
|
410
|
+
if (!this.inline) this.setHidden(true);
|
|
411
|
+
}
|
|
412
|
+
containsFocus() {
|
|
413
|
+
return this.el.contains(this.el.getRootNode().activeElement);
|
|
413
414
|
}
|
|
414
415
|
get isVisible() {
|
|
415
416
|
return this.el.classList.contains("cc-visible");
|
|
@@ -426,12 +427,68 @@ class Panel {
|
|
|
426
427
|
destroy() {
|
|
427
428
|
this.el.remove();
|
|
428
429
|
}
|
|
430
|
+
// A closed popup can't be focused or read by screen readers.
|
|
431
|
+
setHidden(hidden) {
|
|
432
|
+
this.el.toggleAttribute("inert", hidden);
|
|
433
|
+
if (hidden) this.el.setAttribute("aria-hidden", "true");
|
|
434
|
+
else this.el.removeAttribute("aria-hidden");
|
|
435
|
+
}
|
|
429
436
|
escapeHtml(text) {
|
|
430
437
|
const div = document.createElement("div");
|
|
431
438
|
div.textContent = text;
|
|
432
439
|
return div.innerHTML;
|
|
433
440
|
}
|
|
434
441
|
}
|
|
442
|
+
const CHAT_ICON = `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H5.17L4 17.17V4h16v12z"/><path d="M7 9h10v2H7zm0-3h10v2H7z"/></svg>`;
|
|
443
|
+
const CLOSE_ICON = `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/></svg>`;
|
|
444
|
+
class FAB {
|
|
445
|
+
constructor(root, options) {
|
|
446
|
+
__publicField(this, "el");
|
|
447
|
+
__publicField(this, "badge");
|
|
448
|
+
__publicField(this, "isOpen", false);
|
|
449
|
+
__publicField(this, "openLabel");
|
|
450
|
+
__publicField(this, "closeLabel");
|
|
451
|
+
this.openLabel = options.openLabel;
|
|
452
|
+
this.closeLabel = options.closeLabel;
|
|
453
|
+
this.el = document.createElement("button");
|
|
454
|
+
this.el.className = "cc-fab cc-pulse";
|
|
455
|
+
this.el.setAttribute("aria-label", this.openLabel);
|
|
456
|
+
this.el.setAttribute("aria-expanded", "false");
|
|
457
|
+
this.el.setAttribute("aria-controls", PANEL_ID);
|
|
458
|
+
if (options.position === "bottom-left") {
|
|
459
|
+
this.el.classList.add("cc-left");
|
|
460
|
+
}
|
|
461
|
+
this.el.innerHTML = CHAT_ICON;
|
|
462
|
+
this.badge = document.createElement("div");
|
|
463
|
+
this.badge.className = "cc-fab-badge";
|
|
464
|
+
this.el.appendChild(this.badge);
|
|
465
|
+
this.el.addEventListener("click", () => {
|
|
466
|
+
this.el.classList.remove("cc-pulse");
|
|
467
|
+
options.onClick();
|
|
468
|
+
});
|
|
469
|
+
root.appendChild(this.el);
|
|
470
|
+
}
|
|
471
|
+
setOpen(open) {
|
|
472
|
+
this.isOpen = open;
|
|
473
|
+
this.el.innerHTML = open ? CLOSE_ICON : CHAT_ICON;
|
|
474
|
+
if (!open) this.el.appendChild(this.badge);
|
|
475
|
+
this.el.classList.toggle("cc-open", open);
|
|
476
|
+
this.el.setAttribute("aria-label", open ? this.closeLabel : this.openLabel);
|
|
477
|
+
this.el.setAttribute("aria-expanded", String(open));
|
|
478
|
+
}
|
|
479
|
+
focus() {
|
|
480
|
+
this.el.focus();
|
|
481
|
+
}
|
|
482
|
+
showBadge() {
|
|
483
|
+
this.badge.classList.add("cc-visible");
|
|
484
|
+
}
|
|
485
|
+
hideBadge() {
|
|
486
|
+
this.badge.classList.remove("cc-visible");
|
|
487
|
+
}
|
|
488
|
+
destroy() {
|
|
489
|
+
this.el.remove();
|
|
490
|
+
}
|
|
491
|
+
}
|
|
435
492
|
const BUBBLE_SHOWN_KEY = "chatcops_bubble_shown";
|
|
436
493
|
class WelcomeBubble {
|
|
437
494
|
constructor(root, options) {
|
|
@@ -443,8 +500,11 @@ class WelcomeBubble {
|
|
|
443
500
|
this.el.classList.add("cc-left");
|
|
444
501
|
}
|
|
445
502
|
this.el.textContent = options.text;
|
|
503
|
+
this.setHidden(true);
|
|
446
504
|
const closeBtn = document.createElement("button");
|
|
505
|
+
closeBtn.type = "button";
|
|
447
506
|
closeBtn.className = "cc-bubble-close";
|
|
507
|
+
closeBtn.setAttribute("aria-label", options.dismissLabel);
|
|
448
508
|
closeBtn.innerHTML = "×";
|
|
449
509
|
closeBtn.addEventListener("click", (e) => {
|
|
450
510
|
e.stopPropagation();
|
|
@@ -465,15 +525,22 @@ class WelcomeBubble {
|
|
|
465
525
|
}
|
|
466
526
|
this.timerId = setTimeout(() => {
|
|
467
527
|
this.el.classList.add("cc-visible");
|
|
528
|
+
this.setHidden(false);
|
|
468
529
|
}, options.delay ?? 3e3);
|
|
469
530
|
}
|
|
470
531
|
hide() {
|
|
471
532
|
this.el.classList.remove("cc-visible");
|
|
533
|
+
this.setHidden(true);
|
|
472
534
|
if (this.timerId) {
|
|
473
535
|
clearTimeout(this.timerId);
|
|
474
536
|
this.timerId = null;
|
|
475
537
|
}
|
|
476
538
|
}
|
|
539
|
+
setHidden(hidden) {
|
|
540
|
+
this.el.toggleAttribute("inert", hidden);
|
|
541
|
+
if (hidden) this.el.setAttribute("aria-hidden", "true");
|
|
542
|
+
else this.el.removeAttribute("aria-hidden");
|
|
543
|
+
}
|
|
477
544
|
destroy() {
|
|
478
545
|
this.hide();
|
|
479
546
|
this.el.remove();
|
|
@@ -489,9 +556,37 @@ class ChatClient {
|
|
|
489
556
|
async *sendMessage(request, options = {}) {
|
|
490
557
|
var _a;
|
|
491
558
|
const controller = new AbortController();
|
|
492
|
-
const
|
|
559
|
+
const timeout = options.timeout ?? this.timeout;
|
|
560
|
+
let timeoutId;
|
|
561
|
+
let timedOut = false;
|
|
562
|
+
let reader;
|
|
493
563
|
const externalSignal = options.signal;
|
|
494
|
-
const onExternalAbort = () =>
|
|
564
|
+
const onExternalAbort = () => {
|
|
565
|
+
clearTimeout(timeoutId);
|
|
566
|
+
controller.abort();
|
|
567
|
+
};
|
|
568
|
+
const resetTimeout = () => {
|
|
569
|
+
clearTimeout(timeoutId);
|
|
570
|
+
if (controller.signal.aborted) return;
|
|
571
|
+
timeoutId = setTimeout(() => {
|
|
572
|
+
timedOut = true;
|
|
573
|
+
controller.abort();
|
|
574
|
+
}, timeout);
|
|
575
|
+
};
|
|
576
|
+
const cleanup = async () => {
|
|
577
|
+
clearTimeout(timeoutId);
|
|
578
|
+
externalSignal == null ? void 0 : externalSignal.removeEventListener("abort", onExternalAbort);
|
|
579
|
+
if (reader) {
|
|
580
|
+
const currentReader = reader;
|
|
581
|
+
reader = void 0;
|
|
582
|
+
try {
|
|
583
|
+
await currentReader.cancel();
|
|
584
|
+
} catch {
|
|
585
|
+
} finally {
|
|
586
|
+
currentReader.releaseLock();
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
};
|
|
495
590
|
if (externalSignal) {
|
|
496
591
|
if (externalSignal.aborted) {
|
|
497
592
|
controller.abort();
|
|
@@ -499,6 +594,7 @@ class ChatClient {
|
|
|
499
594
|
externalSignal.addEventListener("abort", onExternalAbort);
|
|
500
595
|
}
|
|
501
596
|
}
|
|
597
|
+
resetTimeout();
|
|
502
598
|
try {
|
|
503
599
|
const response = await fetch(this.apiUrl, {
|
|
504
600
|
method: "POST",
|
|
@@ -507,6 +603,7 @@ class ChatClient {
|
|
|
507
603
|
signal: controller.signal
|
|
508
604
|
});
|
|
509
605
|
if (!response.ok) {
|
|
606
|
+
await cleanup();
|
|
510
607
|
if (response.status === 429) {
|
|
511
608
|
yield { error: "rate_limit" };
|
|
512
609
|
return;
|
|
@@ -514,8 +611,9 @@ class ChatClient {
|
|
|
514
611
|
yield { error: `HTTP ${response.status}` };
|
|
515
612
|
return;
|
|
516
613
|
}
|
|
517
|
-
|
|
614
|
+
reader = (_a = response.body) == null ? void 0 : _a.getReader();
|
|
518
615
|
if (!reader) {
|
|
616
|
+
await cleanup();
|
|
519
617
|
yield { error: "No response body" };
|
|
520
618
|
return;
|
|
521
619
|
}
|
|
@@ -524,6 +622,7 @@ class ChatClient {
|
|
|
524
622
|
while (true) {
|
|
525
623
|
const { done, value } = await reader.read();
|
|
526
624
|
if (done) break;
|
|
625
|
+
if (value.byteLength > 0) resetTimeout();
|
|
527
626
|
buffer += decoder.decode(value, { stream: true });
|
|
528
627
|
const lines = buffer.split("\n");
|
|
529
628
|
buffer = lines.pop() ?? "";
|
|
@@ -532,6 +631,7 @@ class ChatClient {
|
|
|
532
631
|
if (!trimmed || !trimmed.startsWith("data: ")) continue;
|
|
533
632
|
const data = trimmed.slice(6);
|
|
534
633
|
if (data === "[DONE]") {
|
|
634
|
+
await cleanup();
|
|
535
635
|
yield { done: true };
|
|
536
636
|
return;
|
|
537
637
|
}
|
|
@@ -541,24 +641,29 @@ class ChatClient {
|
|
|
541
641
|
}
|
|
542
642
|
}
|
|
543
643
|
}
|
|
644
|
+
await cleanup();
|
|
544
645
|
yield { done: true };
|
|
545
646
|
} catch (err) {
|
|
647
|
+
await cleanup();
|
|
546
648
|
if (err instanceof DOMException && err.name === "AbortError") {
|
|
547
|
-
yield
|
|
649
|
+
yield timedOut ? { error: "timeout" } : { error: "aborted" };
|
|
548
650
|
} else {
|
|
549
651
|
yield { error: "network" };
|
|
550
652
|
}
|
|
551
653
|
} finally {
|
|
552
|
-
|
|
553
|
-
externalSignal == null ? void 0 : externalSignal.removeEventListener("abort", onExternalAbort);
|
|
654
|
+
await cleanup();
|
|
554
655
|
}
|
|
555
656
|
}
|
|
556
657
|
}
|
|
557
658
|
const KEY_PREFIX = "chatcops_conversation";
|
|
659
|
+
const SESSION_KEY_PREFIX = "chatcops_session_id";
|
|
558
660
|
class ConversationStorage {
|
|
661
|
+
/** Without a siteId the keys are the ones the widget has always used. */
|
|
559
662
|
constructor(siteId) {
|
|
560
663
|
__publicField(this, "key");
|
|
664
|
+
__publicField(this, "sessionKey");
|
|
561
665
|
this.key = siteId ? `${KEY_PREFIX}_${siteId}` : KEY_PREFIX;
|
|
666
|
+
this.sessionKey = siteId ? `${SESSION_KEY_PREFIX}_${siteId}` : SESSION_KEY_PREFIX;
|
|
562
667
|
}
|
|
563
668
|
load() {
|
|
564
669
|
try {
|
|
@@ -586,12 +691,11 @@ class ConversationStorage {
|
|
|
586
691
|
}
|
|
587
692
|
}
|
|
588
693
|
getSessionId() {
|
|
589
|
-
const SESSION_KEY = "chatcops_session_id";
|
|
590
694
|
try {
|
|
591
|
-
let id = sessionStorage.getItem(
|
|
695
|
+
let id = sessionStorage.getItem(this.sessionKey);
|
|
592
696
|
if (!id) {
|
|
593
697
|
id = crypto.randomUUID();
|
|
594
|
-
sessionStorage.setItem(
|
|
698
|
+
sessionStorage.setItem(this.sessionKey, id);
|
|
595
699
|
}
|
|
596
700
|
return id;
|
|
597
701
|
} catch {
|
|
@@ -605,18 +709,82 @@ class ConversationStorage {
|
|
|
605
709
|
return false;
|
|
606
710
|
}
|
|
607
711
|
}
|
|
608
|
-
setPreChatCompleted(sessionId) {
|
|
712
|
+
setPreChatCompleted(sessionId, data) {
|
|
609
713
|
try {
|
|
610
714
|
sessionStorage.setItem(`chatcops-prechat-${sessionId}`, "true");
|
|
611
715
|
} catch {
|
|
612
716
|
}
|
|
717
|
+
if (data) {
|
|
718
|
+
this.setPreChatData(sessionId, data);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
getPreChatData(sessionId) {
|
|
722
|
+
try {
|
|
723
|
+
const raw = sessionStorage.getItem(`chatcops-prechat-data-${sessionId}`);
|
|
724
|
+
if (!raw) return null;
|
|
725
|
+
const parsed = JSON.parse(raw);
|
|
726
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
727
|
+
return null;
|
|
728
|
+
}
|
|
729
|
+
return parsed;
|
|
730
|
+
} catch {
|
|
731
|
+
return null;
|
|
732
|
+
}
|
|
613
733
|
}
|
|
734
|
+
setPreChatData(sessionId, data) {
|
|
735
|
+
try {
|
|
736
|
+
sessionStorage.setItem(`chatcops-prechat-data-${sessionId}`, JSON.stringify(data));
|
|
737
|
+
} catch {
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
const DEFAULT = "";
|
|
742
|
+
const CLAIMS_KEY = Symbol.for("chatcops.storageClaims");
|
|
743
|
+
const store = globalThis;
|
|
744
|
+
const claims = store[CLAIMS_KEY] ?? (store[CLAIMS_KEY] = /* @__PURE__ */ new Map());
|
|
745
|
+
function inUse(name) {
|
|
746
|
+
const live = claims.get(name);
|
|
747
|
+
if (!live) return false;
|
|
748
|
+
for (const isLive of live) {
|
|
749
|
+
if (!isLive()) live.delete(isLive);
|
|
750
|
+
}
|
|
751
|
+
if (live.size === 0) claims.delete(name);
|
|
752
|
+
return live.size > 0;
|
|
753
|
+
}
|
|
754
|
+
function claimNamespace(siteId, mode, isLive) {
|
|
755
|
+
let name = siteId ?? DEFAULT;
|
|
756
|
+
if (siteId === void 0 && inUse(DEFAULT)) {
|
|
757
|
+
name = mode;
|
|
758
|
+
for (let n = 2; inUse(name); n += 1) name = `${mode}-${n}`;
|
|
759
|
+
console.warn(
|
|
760
|
+
`[chatcops] Another widget on this page already uses the default storage, so this one keeps its conversation under "${name}". Set a \`siteId\` on each widget to name them yourself.`
|
|
761
|
+
);
|
|
762
|
+
} else if (siteId !== void 0 && inUse(siteId)) {
|
|
763
|
+
console.warn(`[chatcops] Two widgets on this page use the siteId "${siteId}", so they share one conversation.`);
|
|
764
|
+
}
|
|
765
|
+
if (!claims.has(name)) claims.set(name, /* @__PURE__ */ new Set());
|
|
766
|
+
claims.get(name).add(isLive);
|
|
767
|
+
return {
|
|
768
|
+
siteId: name === DEFAULT ? void 0 : name,
|
|
769
|
+
release() {
|
|
770
|
+
var _a, _b;
|
|
771
|
+
(_a = claims.get(name)) == null ? void 0 : _a.delete(isLive);
|
|
772
|
+
if (((_b = claims.get(name)) == null ? void 0 : _b.size) === 0) claims.delete(name);
|
|
773
|
+
}
|
|
774
|
+
};
|
|
614
775
|
}
|
|
615
776
|
function applyTheme(root, theme) {
|
|
616
777
|
const host = root.host;
|
|
617
778
|
if (theme.accent) {
|
|
618
779
|
host.style.setProperty("--cc-accent", theme.accent);
|
|
619
780
|
host.style.setProperty("--cc-accent-hover", darken(theme.accent, 10));
|
|
781
|
+
const pair = accentTextColors(theme.accent);
|
|
782
|
+
if (pair) {
|
|
783
|
+
host.style.setProperty("--cc-on-accent", pair.onAccent);
|
|
784
|
+
host.style.setProperty("--cc-accent-contrast", pair.background);
|
|
785
|
+
} else {
|
|
786
|
+
host.style.setProperty("--cc-accent-contrast", theme.accent);
|
|
787
|
+
}
|
|
620
788
|
}
|
|
621
789
|
if (theme.textColor) {
|
|
622
790
|
host.style.setProperty("--cc-text", theme.textColor);
|
|
@@ -639,6 +807,34 @@ function toCssLength(value) {
|
|
|
639
807
|
const text = String(value).trim();
|
|
640
808
|
return /^(?:\d+(?:\.\d+)?|\.\d+)$/.test(text) ? `${text}px` : text;
|
|
641
809
|
}
|
|
810
|
+
const WHITE = "#ffffff";
|
|
811
|
+
const NEAR_BLACK = "#0a0a0a";
|
|
812
|
+
const MIN_TEXT_CONTRAST = 4.5;
|
|
813
|
+
function isHex(color) {
|
|
814
|
+
return /^#?[\da-f]{3}(?:[\da-f]{3})?$/i.test(color.trim());
|
|
815
|
+
}
|
|
816
|
+
function relativeLuminance(hex) {
|
|
817
|
+
const channels = parseHex(hex).map((value) => {
|
|
818
|
+
const c = value / 255;
|
|
819
|
+
return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
820
|
+
});
|
|
821
|
+
return 0.2126 * channels[0] + 0.7152 * channels[1] + 0.0722 * channels[2];
|
|
822
|
+
}
|
|
823
|
+
function contrastRatio(a, b) {
|
|
824
|
+
const [light, dark] = [relativeLuminance(a), relativeLuminance(b)].sort((x, y) => y - x);
|
|
825
|
+
return (light + 0.05) / (dark + 0.05);
|
|
826
|
+
}
|
|
827
|
+
function accentTextColors(accent) {
|
|
828
|
+
if (!isHex(accent)) return null;
|
|
829
|
+
const color = accent.trim().startsWith("#") ? accent.trim() : `#${accent.trim()}`;
|
|
830
|
+
if (contrastRatio(WHITE, color) >= MIN_TEXT_CONTRAST) return { onAccent: WHITE, background: color };
|
|
831
|
+
if (contrastRatio(NEAR_BLACK, color) >= MIN_TEXT_CONTRAST) return { onAccent: NEAR_BLACK, background: color };
|
|
832
|
+
for (let percent = 2; percent <= 60; percent += 2) {
|
|
833
|
+
const darker = darken(color, percent);
|
|
834
|
+
if (contrastRatio(WHITE, darker) >= MIN_TEXT_CONTRAST) return { onAccent: WHITE, background: darker };
|
|
835
|
+
}
|
|
836
|
+
return { onAccent: WHITE, background: NEAR_BLACK };
|
|
837
|
+
}
|
|
642
838
|
function parseHex(hex) {
|
|
643
839
|
if (!/^#?[\da-f]{3}(?:[\da-f]{3})?$/i.test(hex)) return [0, 0, 0];
|
|
644
840
|
const h = hex.replace("#", "");
|
|
@@ -659,10 +855,16 @@ function lighten(hex, percent) {
|
|
|
659
855
|
return toHex(Math.round(r + (255 - r) * f), Math.round(g + (255 - g) * f), Math.round(b + (255 - b) * f));
|
|
660
856
|
}
|
|
661
857
|
const en = {
|
|
662
|
-
welcomeMessage: "
|
|
858
|
+
welcomeMessage: "How can I help you today?",
|
|
663
859
|
inputPlaceholder: "Type a message...",
|
|
664
860
|
sendButton: "Send",
|
|
665
861
|
closeButton: "Close",
|
|
862
|
+
openChat: "Open chat",
|
|
863
|
+
defaultName: "AI Assistant",
|
|
864
|
+
defaultSubtitle: "Online",
|
|
865
|
+
greetingWithName: "Hi {name}!",
|
|
866
|
+
selectPlaceholder: "Select {label}...",
|
|
867
|
+
dismissBubble: "Dismiss",
|
|
666
868
|
errorGeneric: "Something went wrong. Please try again.",
|
|
667
869
|
errorNetwork: "Connection error. Please check your network.",
|
|
668
870
|
errorRateLimit: "Too many messages. Please wait a moment.",
|
|
@@ -751,7 +953,7 @@ class PreChatForm {
|
|
|
751
953
|
select.className = "cc-prechat-select";
|
|
752
954
|
const defaultOpt = document.createElement("option");
|
|
753
955
|
defaultOpt.value = "";
|
|
754
|
-
defaultOpt.textContent = field.placeholder ??
|
|
956
|
+
defaultOpt.textContent = field.placeholder ?? this.options.locale.selectPlaceholder.replace("{label}", field.label.toLowerCase());
|
|
755
957
|
defaultOpt.disabled = true;
|
|
756
958
|
defaultOpt.selected = true;
|
|
757
959
|
select.appendChild(defaultOpt);
|
|
@@ -867,17 +1069,18 @@ class Widget {
|
|
|
867
1069
|
constructor(config) {
|
|
868
1070
|
__publicField(this, "config");
|
|
869
1071
|
__publicField(this, "shadow");
|
|
1072
|
+
__publicField(this, "mounted", false);
|
|
870
1073
|
__publicField(this, "fab");
|
|
871
1074
|
__publicField(this, "panel");
|
|
872
1075
|
__publicField(this, "bubble");
|
|
873
1076
|
__publicField(this, "preChatFormEl");
|
|
874
1077
|
__publicField(this, "client");
|
|
875
1078
|
__publicField(this, "storage");
|
|
1079
|
+
__publicField(this, "namespace");
|
|
876
1080
|
__publicField(this, "locale");
|
|
877
1081
|
__publicField(this, "conversationId");
|
|
878
1082
|
__publicField(this, "messages", []);
|
|
879
1083
|
__publicField(this, "userData");
|
|
880
|
-
__publicField(this, "userDataSent", false);
|
|
881
1084
|
__publicField(this, "isStreaming", false);
|
|
882
1085
|
__publicField(this, "destroyed", false);
|
|
883
1086
|
__publicField(this, "abortController");
|
|
@@ -886,8 +1089,13 @@ class Widget {
|
|
|
886
1089
|
__publicField(this, "containerEl");
|
|
887
1090
|
__publicField(this, "autoOpenTimer");
|
|
888
1091
|
this.config = config;
|
|
889
|
-
this.client = new ChatClient({ apiUrl: config.apiUrl });
|
|
890
|
-
this.
|
|
1092
|
+
this.client = new ChatClient({ apiUrl: config.apiUrl, timeout: config.timeout });
|
|
1093
|
+
this.namespace = claimNamespace(
|
|
1094
|
+
config.siteId,
|
|
1095
|
+
config.mode ?? "popup",
|
|
1096
|
+
() => !this.destroyed && (!this.mounted || this.shadow.host.isConnected)
|
|
1097
|
+
);
|
|
1098
|
+
this.storage = new ConversationStorage(this.namespace.siteId);
|
|
891
1099
|
this.locale = getWidgetLocale(config.locale ?? "en", config.strings);
|
|
892
1100
|
this.conversationId = this.storage.getSessionId();
|
|
893
1101
|
}
|
|
@@ -908,13 +1116,14 @@ class Widget {
|
|
|
908
1116
|
return this.config.container;
|
|
909
1117
|
}
|
|
910
1118
|
init() {
|
|
911
|
-
var _a, _b, _c, _d, _e, _f;
|
|
1119
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
912
1120
|
if (this.isInline) {
|
|
913
1121
|
this.containerEl = this.resolveContainer();
|
|
914
1122
|
this.shadow = createShadowRoot(this.containerEl);
|
|
915
1123
|
} else {
|
|
916
1124
|
this.shadow = createShadowRoot();
|
|
917
1125
|
}
|
|
1126
|
+
this.mounted = true;
|
|
918
1127
|
if (this.config.theme) {
|
|
919
1128
|
applyTheme(this.shadow, this.config.theme);
|
|
920
1129
|
}
|
|
@@ -922,6 +1131,8 @@ class Widget {
|
|
|
922
1131
|
if (!this.isInline) {
|
|
923
1132
|
this.fab = new FAB(this.shadow, {
|
|
924
1133
|
position,
|
|
1134
|
+
openLabel: this.locale.openChat,
|
|
1135
|
+
closeLabel: this.locale.closeButton,
|
|
925
1136
|
onClick: () => this.toggle()
|
|
926
1137
|
});
|
|
927
1138
|
}
|
|
@@ -930,12 +1141,14 @@ class Widget {
|
|
|
930
1141
|
inline: this.isInline,
|
|
931
1142
|
preChatEnabled: ((_b = this.config.preChatForm) == null ? void 0 : _b.enabled) ?? false,
|
|
932
1143
|
branding: {
|
|
933
|
-
name: ((_c = this.config.branding) == null ? void 0 : _c.name) ??
|
|
1144
|
+
name: ((_c = this.config.branding) == null ? void 0 : _c.name) ?? this.locale.defaultName,
|
|
934
1145
|
avatar: (_d = this.config.branding) == null ? void 0 : _d.avatar,
|
|
935
|
-
subtitle: ((_e = this.config.branding) == null ? void 0 : _e.subtitle) ??
|
|
1146
|
+
subtitle: ((_e = this.config.branding) == null ? void 0 : _e.subtitle) ?? this.locale.defaultSubtitle
|
|
936
1147
|
},
|
|
937
1148
|
placeholder: this.config.placeholder ?? this.locale.inputPlaceholder,
|
|
938
1149
|
footerText: this.locale.poweredBy,
|
|
1150
|
+
closeLabel: this.locale.closeButton,
|
|
1151
|
+
sendLabel: this.locale.sendButton,
|
|
939
1152
|
messageActions: {
|
|
940
1153
|
onRetry: (messageId) => {
|
|
941
1154
|
void this.handleRetry(messageId);
|
|
@@ -944,7 +1157,8 @@ class Widget {
|
|
|
944
1157
|
void this.handleRegenerate(messageId);
|
|
945
1158
|
},
|
|
946
1159
|
retryLabel: this.locale.retryButton,
|
|
947
|
-
regenerateLabel: this.locale.regenerateButton
|
|
1160
|
+
regenerateLabel: this.locale.regenerateButton,
|
|
1161
|
+
typingLabel: this.locale.typingIndicator
|
|
948
1162
|
},
|
|
949
1163
|
onSend: (text) => this.handleSend(text),
|
|
950
1164
|
onClose: () => this.close()
|
|
@@ -959,7 +1173,13 @@ class Widget {
|
|
|
959
1173
|
}
|
|
960
1174
|
}
|
|
961
1175
|
}
|
|
962
|
-
|
|
1176
|
+
if ((_f = this.config.preChatForm) == null ? void 0 : _f.enabled) {
|
|
1177
|
+
const savedUserData = this.storage.getPreChatData(this.conversationId);
|
|
1178
|
+
if (savedUserData) {
|
|
1179
|
+
this.userData = savedUserData;
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
const shouldShowPreChat = ((_g = this.config.preChatForm) == null ? void 0 : _g.enabled) && !this.isPreChatCompleted() && this.messages.length === 0;
|
|
963
1183
|
if (shouldShowPreChat) {
|
|
964
1184
|
this.showPreChatForm();
|
|
965
1185
|
} else {
|
|
@@ -972,6 +1192,7 @@ class Widget {
|
|
|
972
1192
|
delay: this.config.welcomeBubble.delay,
|
|
973
1193
|
showOnce: this.config.welcomeBubble.showOnce ?? true,
|
|
974
1194
|
position,
|
|
1195
|
+
dismissLabel: this.locale.dismissBubble,
|
|
975
1196
|
onClick: () => this.open()
|
|
976
1197
|
});
|
|
977
1198
|
}
|
|
@@ -995,12 +1216,14 @@ class Widget {
|
|
|
995
1216
|
this.emit("open");
|
|
996
1217
|
}
|
|
997
1218
|
close() {
|
|
998
|
-
var _a, _b, _c;
|
|
1219
|
+
var _a, _b, _c, _d;
|
|
1220
|
+
const hadFocus = this.panel.containsFocus();
|
|
999
1221
|
this.panel.hide();
|
|
1000
1222
|
if (!this.isInline) {
|
|
1001
1223
|
(_a = this.fab) == null ? void 0 : _a.setOpen(false);
|
|
1224
|
+
if (hadFocus) (_b = this.fab) == null ? void 0 : _b.focus();
|
|
1002
1225
|
}
|
|
1003
|
-
(
|
|
1226
|
+
(_d = (_c = this.config).onClose) == null ? void 0 : _d.call(_c);
|
|
1004
1227
|
this.emit("close");
|
|
1005
1228
|
}
|
|
1006
1229
|
toggle() {
|
|
@@ -1020,15 +1243,18 @@ class Widget {
|
|
|
1020
1243
|
(_c = this.bubble) == null ? void 0 : _c.destroy();
|
|
1021
1244
|
this.panel.destroy();
|
|
1022
1245
|
(_d = this.fab) == null ? void 0 : _d.destroy();
|
|
1023
|
-
destroyShadowRoot(this.
|
|
1246
|
+
destroyShadowRoot(this.shadow);
|
|
1247
|
+
this.namespace.release();
|
|
1024
1248
|
this.eventHandlers.clear();
|
|
1025
1249
|
this.messages = [];
|
|
1250
|
+
this.userData = void 0;
|
|
1026
1251
|
}
|
|
1027
1252
|
on(event, handler) {
|
|
1028
1253
|
if (!this.eventHandlers.has(event)) {
|
|
1029
1254
|
this.eventHandlers.set(event, /* @__PURE__ */ new Set());
|
|
1030
1255
|
}
|
|
1031
1256
|
this.eventHandlers.get(event).add(handler);
|
|
1257
|
+
return () => this.off(event, handler);
|
|
1032
1258
|
}
|
|
1033
1259
|
off(event, handler) {
|
|
1034
1260
|
var _a;
|
|
@@ -1042,8 +1268,8 @@ class Widget {
|
|
|
1042
1268
|
isPreChatCompleted() {
|
|
1043
1269
|
return this.storage.isPreChatCompleted(this.conversationId);
|
|
1044
1270
|
}
|
|
1045
|
-
setPreChatCompleted() {
|
|
1046
|
-
this.storage.setPreChatCompleted(this.conversationId);
|
|
1271
|
+
setPreChatCompleted(data) {
|
|
1272
|
+
this.storage.setPreChatCompleted(this.conversationId, data);
|
|
1047
1273
|
}
|
|
1048
1274
|
showPreChatForm() {
|
|
1049
1275
|
const formConfig = this.config.preChatForm;
|
|
@@ -1059,15 +1285,16 @@ class Widget {
|
|
|
1059
1285
|
});
|
|
1060
1286
|
}
|
|
1061
1287
|
handlePreChatSubmit(data) {
|
|
1062
|
-
var _a, _b, _c;
|
|
1288
|
+
var _a, _b, _c, _d;
|
|
1289
|
+
if (!((_a = this.config.preChatForm) == null ? void 0 : _a.enabled)) return;
|
|
1063
1290
|
this.userData = data;
|
|
1064
|
-
this.setPreChatCompleted();
|
|
1065
|
-
(
|
|
1291
|
+
this.setPreChatCompleted(data);
|
|
1292
|
+
(_b = this.preChatFormEl) == null ? void 0 : _b.destroy();
|
|
1066
1293
|
this.preChatFormEl = void 0;
|
|
1067
1294
|
this.panel.showMessages();
|
|
1068
1295
|
this.panel.input.setVisible(true);
|
|
1069
1296
|
this.showChatView();
|
|
1070
|
-
(
|
|
1297
|
+
(_d = (_c = this.config).onPreChatSubmit) == null ? void 0 : _d.call(_c, data);
|
|
1071
1298
|
this.emit("preChatSubmit", data);
|
|
1072
1299
|
if (data.message) {
|
|
1073
1300
|
this.handleSend(data.message);
|
|
@@ -1077,7 +1304,8 @@ class Widget {
|
|
|
1077
1304
|
var _a;
|
|
1078
1305
|
if (this.messages.length === 0) {
|
|
1079
1306
|
const userName = (_a = this.userData) == null ? void 0 : _a.name;
|
|
1080
|
-
const
|
|
1307
|
+
const greeting = this.locale.greetingWithName.replace("{name}", () => userName);
|
|
1308
|
+
const welcomeText = userName ? `${greeting} ${this.config.welcomeMessage ?? this.locale.welcomeMessage}` : this.config.welcomeMessage ?? this.locale.welcomeMessage;
|
|
1081
1309
|
const welcomeMsg = {
|
|
1082
1310
|
id: "welcome",
|
|
1083
1311
|
role: "assistant",
|
|
@@ -1154,7 +1382,6 @@ class Widget {
|
|
|
1154
1382
|
content: "",
|
|
1155
1383
|
status: "streaming"
|
|
1156
1384
|
};
|
|
1157
|
-
const includeUserData = !!(this.userData && !this.userDataSent);
|
|
1158
1385
|
let firstChunk = true;
|
|
1159
1386
|
let requestCompleted = false;
|
|
1160
1387
|
let hasError = false;
|
|
@@ -1162,14 +1389,13 @@ class Widget {
|
|
|
1162
1389
|
this.abortController = abortController;
|
|
1163
1390
|
try {
|
|
1164
1391
|
for await (const chunk of this.client.sendMessage(
|
|
1165
|
-
this.createRequest(text, options
|
|
1392
|
+
this.createRequest(text, options),
|
|
1166
1393
|
{ signal: abortController.signal }
|
|
1167
1394
|
)) {
|
|
1168
1395
|
if (this.destroyed) break;
|
|
1169
1396
|
if (chunk.error) {
|
|
1170
1397
|
const errorType = this.resolveErrorType(chunk.error);
|
|
1171
|
-
assistantMsg
|
|
1172
|
-
assistantMsg.status = "error";
|
|
1398
|
+
this.markFailed(assistantMsg, this.getErrorMessage(errorType));
|
|
1173
1399
|
assistantMsg.errorType = errorType;
|
|
1174
1400
|
hasError = true;
|
|
1175
1401
|
this.retryRequests.set(assistantMsg.id, {
|
|
@@ -1226,15 +1452,18 @@ class Widget {
|
|
|
1226
1452
|
}
|
|
1227
1453
|
} catch (err) {
|
|
1228
1454
|
if (this.destroyed) return;
|
|
1229
|
-
assistantMsg
|
|
1230
|
-
assistantMsg.status = "error";
|
|
1455
|
+
this.markFailed(assistantMsg, this.locale.errorGeneric);
|
|
1231
1456
|
assistantMsg.errorType = "network";
|
|
1232
1457
|
this.retryRequests.set(assistantMsg.id, {
|
|
1233
1458
|
regenerate: options.regenerate,
|
|
1234
1459
|
userMessageId: options.userMessageId
|
|
1235
1460
|
});
|
|
1236
1461
|
this.panel.messages.removeTyping();
|
|
1237
|
-
|
|
1462
|
+
if (firstChunk) {
|
|
1463
|
+
this.panel.addMessage(assistantMsg);
|
|
1464
|
+
} else {
|
|
1465
|
+
this.panel.messages.updateMessage(assistantMsg);
|
|
1466
|
+
}
|
|
1238
1467
|
this.messages.push(assistantMsg);
|
|
1239
1468
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
1240
1469
|
(_i = (_h = this.config).onError) == null ? void 0 : _i.call(_h, error);
|
|
@@ -1244,9 +1473,6 @@ class Widget {
|
|
|
1244
1473
|
this.abortController = void 0;
|
|
1245
1474
|
}
|
|
1246
1475
|
if (!this.destroyed) {
|
|
1247
|
-
if (includeUserData && requestCompleted) {
|
|
1248
|
-
this.userDataSent = true;
|
|
1249
|
-
}
|
|
1250
1476
|
this.isStreaming = false;
|
|
1251
1477
|
this.panel.input.setDisabled(false);
|
|
1252
1478
|
this.panel.input.focus();
|
|
@@ -1254,8 +1480,10 @@ class Widget {
|
|
|
1254
1480
|
}
|
|
1255
1481
|
}
|
|
1256
1482
|
}
|
|
1257
|
-
createRequest(text, options
|
|
1483
|
+
createRequest(text, options) {
|
|
1484
|
+
var _a;
|
|
1258
1485
|
const pageContext = this.config.pageContext !== false ? { url: window.location.href, title: document.title } : void 0;
|
|
1486
|
+
const includeUserData = Boolean(((_a = this.config.preChatForm) == null ? void 0 : _a.enabled) && this.userData);
|
|
1259
1487
|
return {
|
|
1260
1488
|
conversationId: this.conversationId,
|
|
1261
1489
|
message: text,
|
|
@@ -1306,6 +1534,18 @@ class Widget {
|
|
|
1306
1534
|
return "provider_error";
|
|
1307
1535
|
}
|
|
1308
1536
|
}
|
|
1537
|
+
/**
|
|
1538
|
+
* Marks a reply as failed. Text that already streamed stays, and the error
|
|
1539
|
+
* shows as a note under it; an empty reply shows the error as its content.
|
|
1540
|
+
*/
|
|
1541
|
+
markFailed(message, errorMessage) {
|
|
1542
|
+
message.status = "error";
|
|
1543
|
+
if (message.content.trim()) {
|
|
1544
|
+
message.errorText = errorMessage;
|
|
1545
|
+
} else {
|
|
1546
|
+
message.content = errorMessage;
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1309
1549
|
getErrorMessage(errorType) {
|
|
1310
1550
|
switch (errorType) {
|
|
1311
1551
|
case "rate_limit":
|
|
@@ -1336,6 +1576,7 @@ class Widget {
|
|
|
1336
1576
|
}
|
|
1337
1577
|
}
|
|
1338
1578
|
let instance = null;
|
|
1579
|
+
const persistentHandlers = /* @__PURE__ */ new Map();
|
|
1339
1580
|
function parseAutoOpen(value) {
|
|
1340
1581
|
if (value === void 0) return void 0;
|
|
1341
1582
|
if (value === "" || value === "true") return true;
|
|
@@ -1349,6 +1590,11 @@ const ChatCops = {
|
|
|
1349
1590
|
instance.destroy();
|
|
1350
1591
|
}
|
|
1351
1592
|
instance = new Widget(config);
|
|
1593
|
+
for (const [event, handlers] of persistentHandlers) {
|
|
1594
|
+
for (const handler of handlers) {
|
|
1595
|
+
instance.on(event, handler);
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1352
1598
|
instance.init();
|
|
1353
1599
|
return instance;
|
|
1354
1600
|
},
|
|
@@ -1363,9 +1609,20 @@ const ChatCops = {
|
|
|
1363
1609
|
instance = null;
|
|
1364
1610
|
},
|
|
1365
1611
|
on(event, handler) {
|
|
1612
|
+
let handlers = persistentHandlers.get(event);
|
|
1613
|
+
if (!handlers) {
|
|
1614
|
+
handlers = /* @__PURE__ */ new Set();
|
|
1615
|
+
persistentHandlers.set(event, handlers);
|
|
1616
|
+
}
|
|
1617
|
+
handlers.add(handler);
|
|
1366
1618
|
instance == null ? void 0 : instance.on(event, handler);
|
|
1619
|
+
return () => {
|
|
1620
|
+
ChatCops.off(event, handler);
|
|
1621
|
+
};
|
|
1367
1622
|
},
|
|
1368
1623
|
off(event, handler) {
|
|
1624
|
+
var _a;
|
|
1625
|
+
(_a = persistentHandlers.get(event)) == null ? void 0 : _a.delete(handler);
|
|
1369
1626
|
instance == null ? void 0 : instance.off(event, handler);
|
|
1370
1627
|
}
|
|
1371
1628
|
};
|
|
@@ -1405,7 +1662,7 @@ function autoInit(script) {
|
|
|
1405
1662
|
const currentScript = typeof document !== "undefined" ? document.currentScript : null;
|
|
1406
1663
|
if (currentScript && currentScript.dataset.apiUrl) {
|
|
1407
1664
|
if (document.readyState === "loading") {
|
|
1408
|
-
document.addEventListener("DOMContentLoaded", () => autoInit(currentScript));
|
|
1665
|
+
document.addEventListener("DOMContentLoaded", () => autoInit(currentScript), { once: true });
|
|
1409
1666
|
} else {
|
|
1410
1667
|
autoInit(currentScript);
|
|
1411
1668
|
}
|