diff --git a/README.md b/README.md index 1ef67521c9..935d54a54a 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,23 @@ Hit `CTRL+C` in the terminal that is showing `hugo` activity to interrupt the se Style overrides are in `/assets/scss/_variables_project.scss`. Here we can override all the styles that ship with the Docsy theme. O +## Using the Chat Widget + +To use the chat widget (`static/chat-widget.js` and `static/chat-widget.css`): + +- Ensure both files are included in your HTML page. +- The widget communicates with a backend service. By default, it connects to `http://localhost:8018/docs-agent`. + - To use a different backend URL or port, set `window.DOCS_AGENT_BACKEND_URL` before loading the widget script: + + ```html + + + + ``` +- The chat widget will use this URL for all requests. + ## Troubleshooting As you run the website locally, you may run into the following error: diff --git a/hugo.yaml b/hugo.yaml index f98f84e7ed..6dfe6d9e3c 100644 --- a/hugo.yaml +++ b/hugo.yaml @@ -79,6 +79,7 @@ services: id: GTM-5BL5RTH params: + docs_agent_backend_url: "http://localhost:8018/docs-agent" #search: # algolia: # appId: 2D210VB5MP diff --git a/layouts/partials/footer.html b/layouts/partials/footer.html new file mode 100644 index 0000000000..ce5116c584 --- /dev/null +++ b/layouts/partials/footer.html @@ -0,0 +1,41 @@ +{{/* layouts/partials/footer.html */}} +{{/* This overrides the theme's footer partial */}} + + + + + + \ No newline at end of file diff --git a/static/chat-widget.css b/static/chat-widget.css new file mode 100644 index 0000000000..1fdb6e36f6 --- /dev/null +++ b/static/chat-widget.css @@ -0,0 +1,745 @@ +/* +Weights & Biases Official Brand Colors::after + +Primary Colors: +Name: Moon-900, Hex: 1A1D24, RGB: 23, 26, 31 +Name: Gold-450, Hex: FAC13C, RGB: 250, 193, 60 +Name: Moon-50, Hex: FAFAFA, RGB: 250, 250, 250 + +Secondary Colors: +Name: Teal-450, Hex:10BFCC, RGB: 16, 191, 204 +Name: Moon-450, Hex: 8E949E, RGB: 142, 191, 204 +*/ + +/* ChatUI-inspired Beautiful Chat Widget Styles (MIT License) */ +#chat-widget-btn { + position: fixed; bottom: 25px; right: 28px; + background: #FAC13C; border: none; border-radius: 50%; width: 60px; height: 60px; + box-shadow: 0 4px 24px rgba(23,26,31,0.18); + display: flex; align-items: center; justify-content: center; + cursor: pointer; z-index: 99999; + pointer-events: auto; + transition: background 0.2s, box-shadow 0.2s; + outline: none; +} +#chat-widget-btn:hover, #chat-widget-btn:focus { + background: #10BFCC; + box-shadow: 0 6px 32px rgba(23,26,31,0.25); +} +#chat-widget-window { + position: fixed; bottom: 85px; right: 30px; width: 800px; max-width: 99vw; + height: 600px; max-height: 90vh; z-index: 9999; + background: #1A1D24; + color: #FAFAFA; + border-radius: 18px 18px 10px 10px; + box-shadow: 0 8px 32px rgba(26,29,36,0.32); + display: flex; flex-direction: column; + overflow: hidden; + font-family: 'Source Sans Pro', 'Arial', 'Helvetica Neue', Arial, sans-serif !important; + font-size: 0.98em; + border: 1px solid #8E949E; +} +#chat-widget-header { + background: #23261F; color: #FAC13C; + padding: 12px 18px; border-radius: 18px 18px 0 0; + display: flex; align-items: center; justify-content: space-between; + font-weight: 600; font-size: 1em; + font-family: 'Source Sans Pro', 'Arial', 'Helvetica Neue', Arial, sans-serif !important; +} +.chat-widget-gradient-bar { + height: 4px; + width: 100%; + background: linear-gradient(90deg, #FAC13C 0%, #10BFCC 20%, #8E949E 40%, #FAC13C 60%, #10BFCC 80%, #FAC13C 100%); + background-size: 400% 100%; + animation: chat-widget-gradient-move 60s linear infinite; + margin: 0; + border: none; + border-radius: 0 0 8px 8px; + transition: animation-duration 0.2s; +} +.chat-widget-gradient-fast { + animation-duration: 1.2s !important; +} +@keyframes chat-widget-gradient-move { + 0% { background-position: 0% 50%; } + 100% { background-position: 100% 50%; } +} +#chat-widget-messages { + flex: 1; overflow-y: auto; padding: 18px 6px 0 18px; + background: transparent; +} +#chat-widget-close { + background: transparent; border: none; color: #1A1D24; font-size: 1.5em; cursor: pointer; + margin-left: 10px; line-height: 1; +} +#chat-widget-close-bottom { + margin-left: 10px; + background: transparent; + border: none; + color: #FAFAFA; + font-size: 1.4em; + cursor: pointer; + border-radius: 8px; + padding: 0 10px; + transition: background 0.18s, color 0.18s; + align-self: center; +} +#chat-widget-window.chat-widget-light #chat-widget-close-bottom { + color: #1A1D24; +} +#chat-widget-close-bottom:hover, #chat-widget-close-bottom:focus { + background: #FAC13C; + color: #1A1D24; +} +#chat-widget-form { + display: flex; border-top: 1px solid #8E949E; background: #1A1D24; padding: 10px 12px; +} +#chat-widget-window.chat-widget-light #chat-widget-form { + background: #8E949E !important; /* Moon-450 */ + border-top: 1px solid #D9DCE1 !important; +} +#chat-widget-window.chat-widget-light #chat-widget-input { + background: #FFFFFF !important; + color: #1A1D24 !important; + border: 1px solid #10BFCC !important; +} +#chat-widget-input { + flex: 1; border: 1px solid #10BFCC; border-radius: 10px; padding: 8px 12px; + font-size: 0.97em; + font-family: 'Source Sans Pro', 'Arial', 'Helvetica Neue', Arial, sans-serif !important; + outline: none; transition: border 0.2s, box-shadow 0.2s; + background: #23261F; color: #FAFAFA; + resize: none; + min-height: 36px; + max-height: 120px; + line-height: 1.4; + box-sizing: border-box; + font-family: inherit; + overflow-y: auto; + margin-right: 8px; +} +#chat-widget-input:focus { border: 1.5px solid #FAC13C; } +#chat-widget-input.chat-widget-input-overflow { + border: 1.5px solid #FF4D4F; + box-shadow: 0 0 0 2px rgba(255,77,79,0.15); +} +@keyframes shake { + 0% { transform: translateX(0); } + 20% { transform: translateX(-4px); } + 40% { transform: translateX(4px); } + 60% { transform: translateX(-4px); } + 80% { transform: translateX(4px); } + 100% { transform: translateX(0); } +} +#chat-widget-input.shake { + animation: shake 0.4s; +} +#chat-widget-form button { + margin-left: 10px; background: #10BFCC; color: #1A1D24; border: none; border-radius: 10px; + padding: 0 18px; font-size: 1em; cursor: pointer; display: flex; align-items: center; + transition: background 0.2s; +} +#chat-widget-form button:hover, #chat-widget-form button:focus { background: #FAC13C; color: #1A1D24; } +#chat-widget-form button[disabled], #chat-widget-form button:disabled { + opacity: 0.4; + cursor: not-allowed; +} +#chat-widget-form .chat-widget-send-icon path { + transition: fill 0.2s; +} +#chat-widget-window.chat-widget-light #chat-widget-form .chat-widget-send-icon path { + fill: #1A1D24; +} +#chat-widget-window.chat-widget-dark #chat-widget-form .chat-widget-send-icon path { + fill: #FAFAFA; +} +#chat-widget-window.chat-widget-light #chat-widget-form button:hover .chat-widget-send-icon path, +#chat-widget-window.chat-widget-light #chat-widget-form button:focus .chat-widget-send-icon path, +#chat-widget-window.chat-widget-dark #chat-widget-form button:hover .chat-widget-send-icon path, +#chat-widget-window.chat-widget-dark #chat-widget-form button:focus .chat-widget-send-icon path { + fill: #10BFCC; +} + +/* Chat bubble, avatar, and message row styles */ +.chat-widget-msg { + display: flex; align-items: flex-end; margin-bottom: 14px; +} +.chat-widget-avatar { + display: none; +} +.chat-widget-bubble { + box-shadow: none; + background: none; + color: inherit; + padding: 0; + margin: 0; + font-family: 'Source Sans Pro', 'Arial', 'Helvetica Neue', Arial, sans-serif !important; + font-size: 0.97em !important; +} +.chat-widget-user-bubble { + display: flex; + align-items: center; + gap: 1px; + background: #10BFCC; + color: #FAFAFA; + border-radius: 18px; + box-shadow: 0 2px 8px rgba(23,26,31,0.07); + padding: 8px 16px 4px 2px; + margin: 4px 0 4px 0; + max-width: 80%; + min-width: 48px; + justify-content: flex-start; +} +.chat-widget-avatar-inside { + display: flex; + align-items: flex-start; + justify-content: center; + width: 28px; + height: 28px; + margin: -5px 2px 0 0; + background: none; + border-radius: 50%; + overflow: visible; + box-shadow: none; + padding: 0; +} +.chat-widget-user-text { + display: block; + word-break: break-word; + font-size: 0.97em !important; + font-family: 'Source Sans Pro', 'Arial', 'Helvetica Neue', Arial, sans-serif !important; + padding: 0; + margin: 0; +} +.chat-widget-ai-content-with-avatar { + display: block; + width: 100%; + font-family: 'Source Sans Pro', 'Arial', 'Helvetica Neue', Arial, sans-serif !important; + font-size: 0.97em !important; +} +.chat-widget-ai-content { + width: 100%; + padding: 0 0 1px 2px; /* Adjust left padding to 2px for fine alignment */ + background: none; + color: #FAFAFA; + font-size: 0.97em !important; + font-family: 'Source Sans Pro', 'Arial', 'Helvetica Neue', Arial, sans-serif !important; + word-break: break-word; + display: block; + margin-bottom: 12px; +} +.chat-widget-ai-avatar-row { + margin-top: 4px; + margin-bottom: 4px; + display: block; + width: 100%; + height: 36px; + padding-left: 1px; /* Move AI avatar icon right by 1px */ + text-align: left !important; +} +.chat-widget-ai-avatar-row svg { + width: 36px; height: 36px; + display: block; + margin: 0; +} +.chat-widget-loading { + color: #8E949E; font-style: italic; +} +.chat-widget-error { + color: #c62828; font-style: italic; +} + +/* Support chat bubble for support agent */ +.chat-widget-support-bubble { + background: linear-gradient(90deg, #FFD6F9 0%, #F6E6FF 100%); + color: #A02C7D; + border-radius: 12px; + padding: 18px 20px 18px 20px; + margin: 12px 0 12px 54px; + box-shadow: 0 2px 14px 0 rgba(160,44,125,0.07); + font-size: 0.96em; + max-width: 80%; + position: relative; + border: 1.5px solid #F2C3F1; + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: center; + min-height: 64px; +} +.chat-widget-support-label { + font-size: 0.88em; + font-weight: 700; + color: #C73DD6; + margin-bottom: 8px; + letter-spacing: 0.02em; +} +.chat-widget-support-text { + font-size: 0.96em; + font-weight: 400; + color: #A02C7D; + margin: 0 0 6px 0; +} +.chat-widget-user-text-left { + margin-left: 2px; + padding-left: 0; + display: inline-block; +} + +/* Markdown headings and block elements in AI content */ +.chat-widget-ai-content h1, +.chat-widget-ai-content h2, +.chat-widget-ai-content h3, +.chat-widget-ai-content h4, +.chat-widget-ai-content h5, +.chat-widget-ai-content h6 { + font-weight: 700; + margin: 1.1em 0 0.6em 0; + color: #FAC13C; + line-height: 1.2; +} +.chat-widget-ai-content h1 { font-size: 2em; } +.chat-widget-ai-content h2 { font-size: 1.6em; } +.chat-widget-ai-content h3 { font-size: 1.3em; } +.chat-widget-ai-content h4 { font-size: 1.1em; } +.chat-widget-ai-content h5 { font-size: 1em; } +.chat-widget-ai-content h6 { font-size: 0.95em; } +.chat-widget-ai-content ul, +.chat-widget-ai-content ol { + margin: 0.7em 0 0.7em 2em; + padding: 0 0 0 1.2em; +} +.chat-widget-ai-content pre { + background: #23261F; + color: #FAFAFA; + border-radius: 8px; + padding: 12px 16px; + overflow-x: auto; + margin: 1em 0; +} + +/* Theme sync: override window, header, input based on .chat-widget-dark/.chat-widget-light */ +#chat-widget-window.chat-widget-dark { + background: #1A1D24 !important; + color: #FAFAFA !important; + box-shadow: 0 8px 32px rgba(26,29,36,0.32); +} +#chat-widget-window.chat-widget-dark #chat-widget-header { + background: #23261F !important; + color: #FAC13C !important; +} +#chat-widget-window.chat-widget-dark #chat-widget-input { + background: #23261F !important; + color: #FAFAFA !important; + border: 1px solid #10BFCC !important; +} +#chat-widget-window.chat-widget-light { + background: #FAFAFA !important; + color: #1A1D24 !important; + box-shadow: 0 8px 32px rgba(26,29,36,0.10); + border: 1px solid #D9DCE1 !important; +} +#chat-widget-window.chat-widget-light #chat-widget-header { + background: #F5F5F5 !important; + color: #FAC13C !important; +} +#chat-widget-window.chat-widget-light #chat-widget-input { + background: #F5F5F5 !important; + color: #1A1D24 !important; + border: 1px solid #10BFCC !important; +} + +/* Minimal light/dark mode for window, header, input only */ +@media (prefers-color-scheme: light) { + #chat-widget-window { + background: #FAFAFA; + color: #1A1D24; + box-shadow: 0 8px 32px rgba(26,29,36,0.10); + } + #chat-widget-header { + background: #F5F5F5; + color: #FAC13C; + } + #chat-widget-input { + background: #F5F5F5; + color: #1A1D24; + border: 1px solid #10BFCC; + } +} +@media (prefers-color-scheme: dark) { + #chat-widget-window { + background: #1A1D24; + color: #FAFAFA; + box-shadow: 0 8px 32px rgba(26,29,36,0.32); + } + #chat-widget-header { + background: #23261F; + color: #FAC13C; + } + #chat-widget-input { + background: #23261F; + color: #FAFAFA; + border: 1px solid #10BFCC; + } +} + +/* Code block appearance for dark/light mode */ +.chat-widget-pre-block { + background: #3A4256; + border-radius: 8px; + padding: 14px 16px 14px 16px; + margin: 8px 0; + overflow-x: auto; + position: relative; + box-shadow: 0 2px 8px rgba(16,191,204,0.04); + transition: background 0.3s; +} +@media (prefers-color-scheme: light) { + .chat-widget-pre-block { + background: #f6f7fa; + box-shadow: 0 2px 8px rgba(250,193,60,0.04); + } +} + +/* Markdown and code block styling */ +.chat-widget-bubble :is(h1, h2, h3, h4, h5, h6) { + margin: 0.5em 0 0.3em 0; + font-weight: 600; + color: #FAC13C; +} +.chat-widget-bubble p { + margin: 0.3em 0; +} +.chat-widget-bubble ul, .chat-widget-bubble ol { + margin: 0.5em 0 0.5em 1.2em; +} +.chat-widget-bubble a { + color: #10BFCC; + text-decoration: underline; + word-break: break-all; +} +.chat-widget-bubble code { + background: #23261F; + color: #FAC13C; + border-radius: 4px; + padding: 2px 6px; + font-size: 0.96em; +} +.chat-widget-pre-block pre { + background: none; + margin: 0; + padding: 1.1em 2.5em 1.1em 1.1em; + color: #FAFAFA; + font-size: 0.98em; + line-height: 1.5; + font-family: 'JetBrains Mono', 'Fira Mono', 'Menlo', 'Monaco', 'Consolas', monospace; + border: none; + border-radius: 10px; +} +.chat-widget-copy-btn { + position: absolute; + top: 8px; + right: 12px; + background: #10BFCC; + color: #1A1D24; + border: none; + border-radius: 6px; + padding: 2px 10px; + font-size: 0.95em; + cursor: pointer; + z-index: 2; + transition: background 0.18s, color 0.18s; + display: flex; + align-items: center; + gap: 4px; +} +.chat-widget-copy-btn:hover, .chat-widget-copy-btn.copied { + background: #FAC13C; + color: #1A1D24; +} +.chat-widget-copy-btn svg { + margin-right: 2px; +} + +/* Ensure SVG in chat button does not intercept pointer events */ +#chat-widget-btn svg, +#chat-widget-btn svg * { + pointer-events: none; +} +#chat-widget-btn svg { + width: 88px; + height: 88px; + display: block; +} + +/* Responsive */ +@media (max-width: 600px) { + #chat-widget-window { right: 4vw; width: 98vw; min-width: 0; max-width: 99vw; height: 80vh; } + #chat-widget-btn { right: 4vw; } +} + +/* Prevent footer center from intercepting pointer events over the chat widget */ +@media (min-width: 600px) { + .td-footer__center { + pointer-events: auto; + } + /* Only disable pointer events for the part overlapping the chat widget */ + .td-footer__center.overlapping-chat-widget { + pointer-events: none !important; + } +} + +/* Force all text in AI responses to use a visible light color (#FAFAFA) regardless of markdown or inherited styles */ +#chat-widget-window.chat-widget-light .chat-widget-ai-content-with-avatar, +#chat-widget-window.chat-widget-light .chat-widget-ai-content, +#chat-widget-window.chat-widget-light .chat-widget-ai-content * { + color: #23261F !important; + background: transparent !important; +} +#chat-widget-window.chat-widget-dark .chat-widget-ai-content-with-avatar, +#chat-widget-window.chat-widget-dark .chat-widget-ai-content, +#chat-widget-window.chat-widget-dark .chat-widget-ai-content * { + color: #FAFAFA !important; + background: transparent !important; +} + +/* Force all elements in .chat-widget-ai-content to have at most 4px margin below, and no extra bottom padding */ +.chat-widget-ai-content, +.chat-widget-ai-content > * { + margin-bottom: 4px !important; + padding-bottom: 0 !important; +} + +/* Wildly beautiful animated waiting blobs for AI response */ +/* Blob animation (wildly beautiful animated waiting blobs) commented out: replaced by spinning octagon */ +/* +.cw-blob { + width: 11px; + height: 11px; + border-radius: 50%; + background: linear-gradient(135deg, #FAC13C 0%, #10BFCC 60%, #8E949E 100%); + opacity: 0.85; + box-shadow: 0 0 12px 2px #FAC13C33, 0 0 20px 4px #10BFCC22; + animation: cw-blob-bounce 1.2s infinite cubic-bezier(.68,-0.55,.27,1.55); +} +.cw-blob1 { animation-delay: 0s; background: linear-gradient(120deg, #FAC13C, #10BFCC); } +.cw-blob2 { animation-delay: 0.18s; background: linear-gradient(120deg, #10BFCC, #FAC13C); } +.cw-blob3 { animation-delay: 0.36s; background: linear-gradient(120deg, #8E949E, #FAC13C); } +@keyframes cw-blob-bounce { + 0%, 100% { transform: translateY(0) scale(1); opacity: 0.85; } + 20% { transform: translateY(-10px) scale(1.15); opacity: 1; } + 40% { transform: translateY(2px) scale(0.98); opacity: 0.7; } + 60% { transform: translateY(-6px) scale(1.08); opacity: 0.9; } + 80% { transform: translateY(1px) scale(1.02); opacity: 0.8; } +} +*/ +.chat-widget-loading-animated { + display: flex; + align-items: center; + gap: 8px; + margin: 8px 0 4px 8px; + height: 28px; +} +.chat-widget-loading-animated-small { + display: flex; + justify-content: center; + gap: 4px !important; + margin: 4px 0 2px 0 !important; + height: 16px !important; +} + +.chat-widget-copy-all-btn { + display: block; + width: max-content; + margin: 10px auto 2px; + padding: 2px 8px 20px; + background: transparent; + color: currentColor; + border: 1px solid #8E949E; + border-radius: 4px; + font: inherit; + box-shadow: none; + transition: background 0.2s; +} + +/* Make AI content text visible in light mode */ +@media (prefers-color-scheme: light) { + .chat-widget-ai-content-with-avatar, + .chat-widget-ai-content, + .chat-widget-ai-content * { + color: #23261F !important; + background: transparent !important; + } +} + +.chat-widget-feedback-row { + display: flex; + gap: 16px; + justify-content: center; + align-items: center; + margin: 8px 0 8px 0; +} +.chat-widget-feedback-btn { + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 0.92em; + font-weight: 600; + padding: 6px 14px; + border-radius: 7px; + border: none; + cursor: pointer; + background: linear-gradient(90deg, #FFF8E1, #E0F7FA); + color: #23261F; + box-shadow: 0 2px 8px rgba(16,191,204,0.04); + transition: background 0.2s, color 0.2s; + margin: 0 4px; +} +.chat-widget-feedback-btn:disabled { + opacity: 0.5; + cursor: not-allowed; + background: #E0E0E0; + color: #8E949E; +} + +#chat-widget-chevron { + position: fixed; + z-index: 10000; + background: none; + border: none; + padding: 0; + cursor: pointer; + outline: none; + opacity: 0.85; + transition: opacity 0.2s, background 0.2s; + border-radius: 50%; + box-shadow: 0 2px 8px rgba(16,191,204,0.08); + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + /* Move chevron/circle down 5px */ + /* The JS positions it, so we add offset in JS */ +} +#chat-widget-chevron:hover, #chat-widget-chevron:focus { + background: #FAC13C; + opacity: 0.65; + box-shadow: none; +} +#chat-widget-chevron svg { + display: block; +} + +/* Rolling logo animation across chat bubble */ +.chat-widget-avatar-roll { + position: relative; + width: 100%; + overflow: hidden; + height: 32px !important; + --svg-width: 32px; /* default svg width for bounce calculation */ + animation: chat-widget-edge-glow calc(var(--roll-duration,8s) + 0.5s) linear infinite alternate; +} +.chat-widget-avatar-roll svg { + position: absolute; + top: 0; + left: 0; + transform-origin: center center; + /* Combined slide+spin: flips direction at edges */ + animation: chat-widget-roll var(--roll-duration,8s) linear infinite alternate !important; +} +@keyframes chat-widget-roll { + 0% { + left: 0; + transform: rotate(0deg) scaleX(0.8); + } + 5% { + transform: rotate(calc(var(--spin-end) * 0.05)) scaleX(1); + } + 95% { + transform: rotate(calc(var(--spin-end) * 0.95)) scaleX(1); + } + 100% { + left: calc(100% - var(--svg-width)); + transform: rotate(var(--spin-end,720deg)) scaleX(0.8); + } +} + +@keyframes chat-widget-edge-glow { + 0% { box-shadow: inset 8px 0 0px #10BFCC, inset 4px 0 0px #FAC13C; } + 5%, 95% { box-shadow: none; } + 100% { box-shadow: inset -8px 0 0px #10BFCC, inset -4px 0 0px #FAC13C; } +} + +/* Import Source Sans Pro and JetBrains Mono from Google Fonts */ +@import url('https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600,700&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600&display=swap'); + +#chat-widget-window, #chat-widget-btn, #chat-widget-header, #chat-widget-input, #chat-widget-form, #chat-widget-messages, .chat-widget-bubble, .chat-widget-user-text, .chat-widget-ai-content, .chat-widget-support-bubble { + font-family: 'Source Sans Pro', Arial, sans-serif !important; +} + +/* Use JetBrains Mono for code blocks in markdown */ +.chat-widget-ai-content code, .chat-widget-ai-content pre, .chat-widget-bubble code, .chat-widget-bubble pre { + font-family: 'JetBrains Mono', 'Fira Mono', 'Menlo', 'Monaco', 'Consolas', monospace !important; + font-size: 0.92em; + font-variant-ligatures: none; + letter-spacing: 0.01em; + background: #FAFAFA !important; /* Moon-50 */ + color: #23261F !important; +} + +/* Make sure code blocks are readable and friendly */ +.chat-widget-ai-content pre, .chat-widget-bubble pre { + background: #FAFAFA !important; /* Moon-50 */ + color: #23261F !important; + border-radius: 8px; + padding: 12px 16px; + overflow-x: auto; + margin: 1em 0; + font-size: 0.92em; +} + +.chat-widget-avatar-spin svg { + animation: chat-widget-spin 1.2s linear infinite; + display: block; + margin: 0 auto; +} +@keyframes chat-widget-spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* Clear Chat button in header */ +#chat-widget-header .chat-widget-header-btn { + background: transparent; + border: 1px solid currentColor; + border-radius: 4px; + color: inherit; + padding: 2px 6px; + cursor: pointer; + font: inherit; + font-size: 0.85em; + font-weight: normal; +} +#chat-widget-header .chat-widget-header-btn:hover { + background: transparent; + color: inherit; +} +/* Per-theme hover overlays */ +#chat-widget-window.chat-widget-light #chat-widget-header .chat-widget-header-btn:hover { + background: rgba(0,0,0,0.05); +} +#chat-widget-window.chat-widget-dark #chat-widget-header .chat-widget-header-btn:hover { + background: rgba(255,255,255,0.1); +} + +#chat-widget-header .chat-widget-header-title { + color: inherit; +} +#chat-widget-window.chat-widget-dark #chat-widget-header .chat-widget-header-title { + color: #FAFAFA !important; +} +#chat-widget-window.chat-widget-dark #chat-widget-header .chat-widget-header-btn { + color: #FAFAFA !important; +} diff --git a/static/chat-widget.js b/static/chat-widget.js new file mode 100644 index 0000000000..2c0773f0de --- /dev/null +++ b/static/chat-widget.js @@ -0,0 +1,739 @@ +// ChatUI-inspired Beautiful Chat Widget (MIT License) +(function () { + // --- UUID v4 Generator --- + function uuidv4() { + // https://stackoverflow.com/a/2117523/438386 + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); + } + + // --- HTML Escaping Helper --- + function escapeHtml(str) { + return String(str).replace(/[&<>"']/g, function (c) { + return {'&':'&','<':'<','>':'>','"':'"','\'':'''}[c]||c; + }); + } + + // Load marked.js from CDN if not present + if (!window.marked) { + var script = document.createElement('script'); + script.src = 'https://cdn.jsdelivr.net/npm/marked/marked.min.js'; + script.onload = main; + document.head.appendChild(script); + } else { + main(); + } + + // CC0 Bee SVG from SVG Repo: https://www.svgrepo.com/svg/228586/bees-bee + function beeSVG(size=28) { + return aiOctagonSVG(size); + } + + // Gradient Decahedron SVG for anonymous user + function userDecahedronSVG(size=28) { + // A decahedron (10-sided polygon) with a gradient fill + return ``; + } + + // Large black octagon for AI avatar (gold border, gold W&B logo centered, scaled up) + function aiOctagonSVG(size=72) { + // Larger octagon for chat-widget-btn + return ``; + } + + // Octagon with W&B logo for AI avatar + function wandbLogoOctagonSVG(size=44) { + // Even larger octagon (44x44), W&B logo remains the same size and position + return ``; + } + + // --- Support Widget Helper --- + function supportWidget(text) { + return ` +
+ `; + } + + function main() { + const BACKEND_URL = window.DOCS_AGENT_BACKEND_URL || 'http://localhost:8018/docs-agent'; // Change to your backend endpoint + + // --- Theme Detection & Sync with Site --- + function getSiteTheme() { + const html = document.documentElement; + const body = document.body; + // Bootstrap 5.3+ uses data-bs-theme, older may use class 'dark'/'light' + const attrTheme = html.getAttribute('data-bs-theme') || body.getAttribute('data-bs-theme'); + if (attrTheme === 'dark' || attrTheme === 'light') return attrTheme; + if (html.classList.contains('dark') || body.classList.contains('dark')) return 'dark'; + if (html.classList.contains('light') || body.classList.contains('light')) return 'light'; + return null; // fallback to system + } + function applyChatTheme(win) { + win.classList.remove('chat-widget-dark', 'chat-widget-light'); + const theme = getSiteTheme(); + if (theme === 'dark') win.classList.add('chat-widget-dark'); + else if (theme === 'light') win.classList.add('chat-widget-light'); + } + + // --- DOM Creation --- + // Floating button + const chatBtn = document.createElement('button'); + chatBtn.id = 'chat-widget-btn'; + chatBtn.setAttribute('aria-label', 'Open chat'); + chatBtn.innerHTML = wandbLogoOctagonSVG(88); + document.body.appendChild(chatBtn); + const chatBtnIcon = chatBtn.querySelector('svg'); + if (chatBtnIcon) { + chatBtnIcon.style.position = 'relative'; + chatBtnIcon.style.left = '0.75px'; + // chatBtnIcon.style.top = '0px'; + } + + // Chat window + const chatWin = document.createElement('div'); + chatWin.id = 'chat-widget-window'; + chatWin.innerHTML = ` +