From 28ed2c249ef93c5510880269796a71e22c72a614 Mon Sep 17 00:00:00 2001 From: Hakim Cassimally Date: Thu, 16 Apr 2026 13:50:04 +0100 Subject: [PATCH 1/7] DOC-14221 call new setClientContext API with hardcoded values for now --- src/js/12-chatbox.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/js/12-chatbox.js b/src/js/12-chatbox.js index f3c321b7..ccf7b4c1 100644 --- a/src/js/12-chatbox.js +++ b/src/js/12-chatbox.js @@ -43,6 +43,22 @@ iframeLoader.api.ping() // perform actions on the parent dependent on the chatbot loading. // document.getElementById('send-intent').setAttribute('disabled', false) + + iframeLoader.api.setClientContext({ + product: { value: 'capella', description: 'Couchbase Capella' }, + surface: { value: 'docs', description: 'Couchbase Docs' }, + service: { value: 'query', description: 'N1QL query service' }, + page: { + route: { value: '/query', description: 'Query workbench' }, + description: 'Query execution and tuning page', + }, + component: { + id: { value: 'server', description: 'Couchbase Server' }, + edition: { value: 'enterprise', description: 'Edition' }, + version: { value: '7.6.2', description: 'Server version' }, + description: 'Target runtime component', + }, + }) }) .catch(function (error) { console.error('chatbot UI failed to load', error, iframeOrigin) From a25f17386108f91ecb8f0f7ff1e6740500c84604 Mon Sep 17 00:00:00 2001 From: Hakim Cassimally Date: Thu, 16 Apr 2026 14:51:00 +0100 Subject: [PATCH 2/7] patching bundle file --- src/js/vendor/chatbox-ui.min.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/vendor/chatbox-ui.min.js b/src/js/vendor/chatbox-ui.min.js index 27f620a5..237840d0 100644 --- a/src/js/vendor/chatbox-ui.min.js +++ b/src/js/vendor/chatbox-ui.min.js @@ -1,2 +1,2 @@ /* eslint-disable */ -var ChatBotUiLoader;(()=>{var e={40:e=>{class t{constructor({config:e={},containerClass:t="lex-web-ui",elementId:i="lex-web-ui"}){this.elementId=i,this.config=e,this.containerClass=t,this.iframeElement=null,this.containerElement=null,this.credentials=null,this.isChatBotReady=!1,this.initIframeMessageHandlers()}load(e){this.config=t.mergeConfig(this.config,e),"iframe"in this.config||(this.config.iframe={});const i=this.config.iframe;return"iframeOrigin"in i&&i.iframeOrigin||(this.config.iframe.iframeOrigin=this.config.ui.parentOrigin||window.location.origin),void 0===i.shouldLoadIframeMinimized&&(this.config.iframe.shouldLoadIframeMinimized=!0),this.config.ui.parentOrigin||(this.config.ui.parentOrigin=this.config.iframe.iframeOrigin||window.location.origin),t.validateConfig(this.config)?Promise.all([this.initContainer(),this.setupIframeMessageListener()]).then((()=>this.initIframe())).then((()=>this.initParentToIframeApi())).then((()=>this.showIframe())):Promise.reject(new Error("config object is missing required fields"))}static validateConfig(e){const{iframe:t,ui:i}=e;return t?"iframeOrigin"in t&&t.iframeOrigin?"iframeSrcPath"in t&&t.iframeSrcPath?"parentOrigin"in i&&i.parentOrigin?"shouldLoadIframeMinimized"in t||(console.error("missing shouldLoadIframeMinimized config field"),!1):(console.error("missing parentOrigin config field"),!1):(console.error("missing iframeSrcPath config field"),!1):(console.error("missing iframeOrigin config field"),!1):(console.error("missing iframe config field"),!1)}initContainer(){return new Promise(((e,t)=>{if(!this.elementId||!this.containerClass)return t(new Error("invalid chatbot container parameters"));let i=document.getElementById(this.elementId);if(i)return console.warn("chatbot iframe container already exists"),this.containerElement=i,e(i);try{i=document.createElement("div"),i.classList.add(this.containerClass),i.setAttribute("id",this.elementId),document.body.appendChild(i)}catch(e){return t(new Error(`error initializing container: ${e}`))}return this.containerElement=i,e()}))}setupIframeMessageListener(){try{window.addEventListener("message",this.onMessageFromIframe.bind(this),!1)}catch(e){return Promise.reject(new Error(`could not add iframe message listener ${e}`))}return Promise.resolve()}onMessageFromIframe(e){const t="iframe"in this.config&&"string"==typeof this.config.iframe.iframeOrigin?this.config.iframe.iframeOrigin:window.location.origin;"data"in e&&"source"in e.data&&"lex-web-ui"!==e.data.source||(e.origin===t?e.ports&&Array.isArray(e.ports)&&e.ports.length?this.iframeMessageHandlers?e.data.event?Object.prototype.hasOwnProperty.call(this.iframeMessageHandlers,e.data.event)?this.iframeMessageHandlers[e.data.event].call(this,e):console.error("unknown message in event",e.data):console.error("event from iframe does not have the event field",e):console.error("invalid iframe message handler"):console.warn("postMessage not sent over MessageChannel",e):console.warn("postMessage from invalid origin",e.origin))}initIframe(){const{iframeOrigin:e,iframeSrcPath:t}=this.config.iframe;if(!e||!t)return Promise.reject(new Error("invalid iframe url fields"));const i=`${e}${t}`;if(!i)return Promise.reject(new Error("invalid iframe url"));if(!this.containerElement||!("appendChild"in this.containerElement))return Promise.reject(new Error("invalid node element to append iframe"));let r=this.containerElement.querySelector("iframe");if(r)return Promise.resolve(r);try{r=document.createElement("iframe"),r.setAttribute("src",i),r.setAttribute("frameBorder","0"),r.setAttribute("scrolling","no"),r.setAttribute("title","chatbot"),r.setAttribute("allow","microphone; clipboard-read; clipboard-write;"),this.containerElement.appendChild(r)}catch(e){return Promise.reject(new Error(`failed to initialize iframe element ${e}`))}return this.iframeElement=r,this.waitForIframe(r).then((()=>this.waitForChatBotReady()))}waitForIframe(){const e={timeoutInMs:2e4,timeoutId:null,onIframeLoaded:null,onIframeTimeout:null};return new Promise(((t,i)=>{e.onIframeLoaded=()=>(clearTimeout(e.timeoutId),this.iframeElement.removeEventListener("load",e.onIframeLoaded,!1),t()),e.onIframeTimeout=()=>(this.iframeElement.removeEventListener("load",e.onIframeLoaded,!1),i(new Error("iframe load timeout"))),e.timeoutId=setTimeout(e.onIframeTimeout,e.timeoutInMs),this.iframeElement.addEventListener("load",e.onIframeLoaded,!1)}))}waitForChatBotReady(){const e={timeoutId:null,intervalId:null,checkIsChtBotReady:null,onConfigEventTimeout:null};return new Promise(((t,i)=>{e.checkIsChatBotReady=()=>{this.isChatBotReady&&(clearTimeout(e.timeoutId),clearInterval(e.intervalId),t())},e.onConfigEventTimeout=()=>(clearInterval(e.intervalId),i(new Error("chatbot loading time out"))),e.timeoutId=setTimeout(e.onConfigEventTimeout,15e3),e.intervalId=setInterval(e.checkIsChatBotReady,500)}))}initIframeMessageHandlers(){this.iframeMessageHandlers={ready(e){this.isChatBotReady=!0,e.ports[0].postMessage({event:"resolve",type:e.data.event})},getCredentials(e){return this.getCredentials().then((t=>{const i=JSON.parse(JSON.stringify(t));e.ports[0].postMessage({event:"resolve",type:e.data.event,data:i})})).catch((t=>{console.error("failed to get credentials",t),e.ports[0].postMessage({event:"reject",type:e.data.event,error:"failed to get credentials"})}))},initIframeConfig(e){e.ports[0].postMessage({event:"resolve",type:e.data.event,data:this.config})},toggleMinimizeUi(e){this.toggleMinimizeUiClass().then((()=>e.ports[0].postMessage({event:"resolve",type:e.data.event}))).catch((t=>{console.error("failed to toggleMinimizeUi",t),e.ports[0].postMessage({event:"reject",type:e.data.event,error:"failed to toggleMinimizeUi"})}))},addSpaceForBubble(e){this.setSpaceForBubble(!0).then((()=>e.ports[0].postMessage({event:"resolve",type:e.data.event}))).catch((t=>{console.error("failed to addSpaceForBubble",t),e.ports[0].postMessage({event:"reject",type:e.data.event,error:"failed to addSpaceForBubble"})}))},removeSpaceForBubble(e){this.setSpaceForBubble(!1).then((()=>e.ports[0].postMessage({event:"resolve",type:e.data.event}))).catch((t=>{console.error("failed to removeSpaceForBubble",t),e.ports[0].postMessage({event:"reject",type:e.data.event,error:"failed to removeSpaceForBubble"})}))},toggleIsUiExpanded(e){this.toggleMaximizeUiClass().then((()=>e.ports[0].postMessage({event:"resolve",type:e.data.event}))).catch((t=>{console.error("failed to toggleIsUiExpanded",t),e.ports[0].postMessage({event:"reject",type:e.data.event,error:"failed to toggleIsUiExpanded"})}))},updateLexState(e){e.ports[0].postMessage({event:"resolve",type:e.data.event});const t=new CustomEvent("updatelexstate",{detail:e.data});document.dispatchEvent(t)}}}sendMessageToIframe(e){if(!this.iframeElement||!("contentWindow"in this.iframeElement)||!("postMessage"in this.iframeElement.contentWindow))return Promise.reject(new Error("invalid iframe element"));const{iframeOrigin:t}=this.config.iframe;return t?new Promise(((i,r)=>{const n=new MessageChannel;n.port1.onmessage=e=>{n.port1.close(),n.port2.close(),"resolve"===e.data.event?i(e.data):r(new Error(`iframe failed to handle message - ${e.data.error}`))},this.iframeElement.contentWindow.postMessage(e,t,[n.port2])})):Promise.reject(new Error("invalid iframe origin"))}toggleShowUiClass(){try{return this.containerElement.classList.toggle(`${this.containerClass}--show`),Promise.resolve()}catch(e){return Promise.reject(new Error(`failed to toggle show UI ${e}`))}}setSpaceForBubble(e=!1){try{return this.containerElement.classList.toggle("add-space-for-bubble",e),Promise.resolve()}catch(e){return Promise.reject(new Error(`failed to toggle space for bubble ${e}`))}}toggleMinimizeUiClass(){try{return this.containerElement.classList.toggle(`${this.containerClass}--minimize`),this.containerElement.classList.contains(`${this.containerClass}--minimize`)?localStorage.setItem(`${this.config.ui.toolbarTitle}lastUiIsMinimized`,"true"):localStorage.setItem(`${this.config.ui.toolbarTitle}lastUiIsMinimized`,"false"),Promise.resolve()}catch(e){return Promise.reject(new Error(`failed to toggle minimize UI ${e}`))}}toggleMaximizeUiClass(){try{return this.containerElement.classList.toggle(`${this.containerClass}--maximize`),this.containerElement.classList.contains(`${this.containerClass}--maximize`)?localStorage.setItem(`${this.config.ui.toolbarTitle}lastUiIsMaximized`,"true"):localStorage.setItem(`${this.config.ui.toolbarTitle}lastUiIsMaximized`,"false"),Promise.resolve()}catch(e){return Promise.reject(new Error(`failed to toggle maximize UI ${e}`))}}showIframe(){return Promise.resolve().then((()=>{this.config.iframe.shouldLoadIframeMinimized?(this.api.toggleMinimizeUi(),localStorage.setItem(`${this.config.ui.toolbarTitle}lastUiIsMinimized`,"true")):localStorage.getItem(`${this.config.ui.toolbarTitle}lastUiIsMinimized`)&&"true"===localStorage.getItem(`${this.config.ui.toolbarTitle}lastUiIsMinimized`)?this.api.toggleMinimizeUi():localStorage.getItem(`${this.config.ui.toolbarTitle}lastUiIsMinimized`)&&"false"===localStorage.getItem(`${this.config.ui.toolbarTitle}lastUiIsMinimized`)&&this.api.ping()})).then((()=>this.toggleShowUiClass()))}onMessageToIframe(e){return e&&"detail"in e&&e.detail&&"message"in e.detail?this.sendMessageToIframe(e.detail.message):Promise.reject(new Error("malformed message to iframe event"))}initParentToIframeApi(){return this.api={MESSAGE_TYPE_HUMAN:"human",MESSAGE_TYPE_BUTTON:"button",ping:()=>this.sendMessageToIframe({event:"ping"}),sendParentReady:()=>this.sendMessageToIframe({event:"parentReady"}),toggleMinimizeUi:()=>this.sendMessageToIframe({event:"toggleMinimizeUi"}),toggleIsUiExpanded:()=>this.sendMessageToIframe({event:"toggleIsUiExpanded"}),postText:(e,t)=>this.sendMessageToIframe({event:"postText",message:e,messageType:t}),deleteSession:()=>this.sendMessageToIframe({event:"deleteSession"}),startNewSession:()=>this.sendMessageToIframe({event:"startNewSession"}),setSessionAttribute:(e,t)=>this.sendMessageToIframe({event:"setSessionAttribute",key:e,value:t})},Promise.resolve().then((()=>{document.addEventListener("lexWebUiMessage",this.onMessageToIframe.bind(this),!1)})).then((()=>this.api.sendParentReady())).then((()=>{document.dispatchEvent(new CustomEvent("lexWebUiReady"))}))}static mergeConfig(e,i={}){function r(e){return"number"!=typeof e&&"boolean"!=typeof e&&(null==e||(void 0!==e.length?0===e.length:0===Object.keys(e).length))}return r(i)?{...e}:Object.keys(e).map((n=>{const o={};let s=e[n];return n in i&&!r(i[n])&&(s="object"==typeof e[n]?{...t.mergeConfig(i[n],e[n]),...t.mergeConfig(e[n],i[n])}:i[n]),o[n]=s,o})).reduce(((e,t)=>({...e,...t})),{})}}e.exports=t},203:(e,t,i)=>{const r=i(40);class n{constructor(e){const{baseUrl:t}=e;this.options=e,this.options.baseUrl=this.options.baseUrl&&"/"===t[t.length-1]?this.options.baseUrl:`${this.options.baseUrl}/`}load(e={}){return this.config=r.mergeConfig(this.config,e),this.compLoader.load(this.config)}}const o={baseUrl:"/",configEventTimeoutInMs:1e4,configUrl:"./lex-web-ui-loader-config.json",shouldIgnoreConfigWhenEmbedded:!0,shouldLoadConfigFromEvent:!1,shouldLoadConfigFromJsonFile:!0,shouldLoadMinDeps:!1,elementId:"lex-web-ui-iframe",containerClass:"lex-web-ui-iframe",iframeSrcPath:"/index.html#/?lexWebUiEmbed=true",shouldLoadConfigFromEvent:!0,shouldLoadMinDeps:!1},s={ui:{parentOrigin:""},iframe:{iframeOrigin:"",iframeSrcPath:""}},a={IframeLoader:class extends n{constructor(e={}){super({...o,...e}),this.config=s,this.compLoader=new r({config:this.config,containerClass:this.options.containerClass||"lex-web-ui",elementId:this.options.elementId||"lex-web-ui"})}load(e={}){return super.load(e).then((()=>{this.api=this.compLoader.api,this.config.iframe=this.config.iframe||{},this.config.iframe.iframeSrcPath=this.config.iframe.iframeSrcPath||this.mergeSrcPath(e)}))}mergeSrcPath(e){const{iframe:t}=e,i=t&&t.iframeSrcPath,{iframe:r}=this.config,n=r&&r.iframeSrcPath;return i||this.options.iframeSrcPath||n}}};e.exports=a}},t={},i=function i(r){var n=t[r];if(void 0!==n)return n.exports;var o=t[r]={exports:{}};return e[r](o,o.exports,i),o.exports}(203);ChatBotUiLoader=i})(); \ No newline at end of file +var ChatBotUiLoader;(()=>{var e={40:e=>{class t{constructor({config:e={},containerClass:t="lex-web-ui",elementId:i="lex-web-ui"}){this.elementId=i,this.config=e,this.containerClass=t,this.iframeElement=null,this.containerElement=null,this.credentials=null,this.isChatBotReady=!1,this.initIframeMessageHandlers()}load(e){this.config=t.mergeConfig(this.config,e),"iframe"in this.config||(this.config.iframe={});const i=this.config.iframe;return"iframeOrigin"in i&&i.iframeOrigin||(this.config.iframe.iframeOrigin=this.config.ui.parentOrigin||window.location.origin),void 0===i.shouldLoadIframeMinimized&&(this.config.iframe.shouldLoadIframeMinimized=!0),this.config.ui.parentOrigin||(this.config.ui.parentOrigin=this.config.iframe.iframeOrigin||window.location.origin),t.validateConfig(this.config)?Promise.all([this.initContainer(),this.setupIframeMessageListener()]).then((()=>this.initIframe())).then((()=>this.initParentToIframeApi())).then((()=>this.showIframe())):Promise.reject(new Error("config object is missing required fields"))}static validateConfig(e){const{iframe:t,ui:i}=e;return t?"iframeOrigin"in t&&t.iframeOrigin?"iframeSrcPath"in t&&t.iframeSrcPath?"parentOrigin"in i&&i.parentOrigin?"shouldLoadIframeMinimized"in t||(console.error("missing shouldLoadIframeMinimized config field"),!1):(console.error("missing parentOrigin config field"),!1):(console.error("missing iframeSrcPath config field"),!1):(console.error("missing iframeOrigin config field"),!1):(console.error("missing iframe config field"),!1)}initContainer(){return new Promise(((e,t)=>{if(!this.elementId||!this.containerClass)return t(new Error("invalid chatbot container parameters"));let i=document.getElementById(this.elementId);if(i)return console.warn("chatbot iframe container already exists"),this.containerElement=i,e(i);try{i=document.createElement("div"),i.classList.add(this.containerClass),i.setAttribute("id",this.elementId),document.body.appendChild(i)}catch(e){return t(new Error(`error initializing container: ${e}`))}return this.containerElement=i,e()}))}setupIframeMessageListener(){try{window.addEventListener("message",this.onMessageFromIframe.bind(this),!1)}catch(e){return Promise.reject(new Error(`could not add iframe message listener ${e}`))}return Promise.resolve()}onMessageFromIframe(e){const t="iframe"in this.config&&"string"==typeof this.config.iframe.iframeOrigin?this.config.iframe.iframeOrigin:window.location.origin;"data"in e&&"source"in e.data&&"lex-web-ui"!==e.data.source||(e.origin===t?e.ports&&Array.isArray(e.ports)&&e.ports.length?this.iframeMessageHandlers?e.data.event?Object.prototype.hasOwnProperty.call(this.iframeMessageHandlers,e.data.event)?this.iframeMessageHandlers[e.data.event].call(this,e):console.error("unknown message in event",e.data):console.error("event from iframe does not have the event field",e):console.error("invalid iframe message handler"):console.warn("postMessage not sent over MessageChannel",e):console.warn("postMessage from invalid origin",e.origin))}initIframe(){const{iframeOrigin:e,iframeSrcPath:t}=this.config.iframe;if(!e||!t)return Promise.reject(new Error("invalid iframe url fields"));const i=`${e}${t}`;if(!i)return Promise.reject(new Error("invalid iframe url"));if(!this.containerElement||!("appendChild"in this.containerElement))return Promise.reject(new Error("invalid node element to append iframe"));let r=this.containerElement.querySelector("iframe");if(r)return Promise.resolve(r);try{r=document.createElement("iframe"),r.setAttribute("src",i),r.setAttribute("frameBorder","0"),r.setAttribute("scrolling","no"),r.setAttribute("title","chatbot"),r.setAttribute("allow","microphone; clipboard-read; clipboard-write;"),this.containerElement.appendChild(r)}catch(e){return Promise.reject(new Error(`failed to initialize iframe element ${e}`))}return this.iframeElement=r,this.waitForIframe(r).then((()=>this.waitForChatBotReady()))}waitForIframe(){const e={timeoutInMs:2e4,timeoutId:null,onIframeLoaded:null,onIframeTimeout:null};return new Promise(((t,i)=>{e.onIframeLoaded=()=>(clearTimeout(e.timeoutId),this.iframeElement.removeEventListener("load",e.onIframeLoaded,!1),t()),e.onIframeTimeout=()=>(this.iframeElement.removeEventListener("load",e.onIframeLoaded,!1),i(new Error("iframe load timeout"))),e.timeoutId=setTimeout(e.onIframeTimeout,e.timeoutInMs),this.iframeElement.addEventListener("load",e.onIframeLoaded,!1)}))}waitForChatBotReady(){const e={timeoutId:null,intervalId:null,checkIsChtBotReady:null,onConfigEventTimeout:null};return new Promise(((t,i)=>{e.checkIsChatBotReady=()=>{this.isChatBotReady&&(clearTimeout(e.timeoutId),clearInterval(e.intervalId),t())},e.onConfigEventTimeout=()=>(clearInterval(e.intervalId),i(new Error("chatbot loading time out"))),e.timeoutId=setTimeout(e.onConfigEventTimeout,15e3),e.intervalId=setInterval(e.checkIsChatBotReady,500)}))}initIframeMessageHandlers(){this.iframeMessageHandlers={ready(e){this.isChatBotReady=!0,e.ports[0].postMessage({event:"resolve",type:e.data.event})},getCredentials(e){return this.getCredentials().then((t=>{const i=JSON.parse(JSON.stringify(t));e.ports[0].postMessage({event:"resolve",type:e.data.event,data:i})})).catch((t=>{console.error("failed to get credentials",t),e.ports[0].postMessage({event:"reject",type:e.data.event,error:"failed to get credentials"})}))},initIframeConfig(e){e.ports[0].postMessage({event:"resolve",type:e.data.event,data:this.config})},toggleMinimizeUi(e){this.toggleMinimizeUiClass().then((()=>e.ports[0].postMessage({event:"resolve",type:e.data.event}))).catch((t=>{console.error("failed to toggleMinimizeUi",t),e.ports[0].postMessage({event:"reject",type:e.data.event,error:"failed to toggleMinimizeUi"})}))},addSpaceForBubble(e){this.setSpaceForBubble(!0).then((()=>e.ports[0].postMessage({event:"resolve",type:e.data.event}))).catch((t=>{console.error("failed to addSpaceForBubble",t),e.ports[0].postMessage({event:"reject",type:e.data.event,error:"failed to addSpaceForBubble"})}))},removeSpaceForBubble(e){this.setSpaceForBubble(!1).then((()=>e.ports[0].postMessage({event:"resolve",type:e.data.event}))).catch((t=>{console.error("failed to removeSpaceForBubble",t),e.ports[0].postMessage({event:"reject",type:e.data.event,error:"failed to removeSpaceForBubble"})}))},toggleIsUiExpanded(e){this.toggleMaximizeUiClass().then((()=>e.ports[0].postMessage({event:"resolve",type:e.data.event}))).catch((t=>{console.error("failed to toggleIsUiExpanded",t),e.ports[0].postMessage({event:"reject",type:e.data.event,error:"failed to toggleIsUiExpanded"})}))},updateLexState(e){e.ports[0].postMessage({event:"resolve",type:e.data.event});const t=new CustomEvent("updatelexstate",{detail:e.data});document.dispatchEvent(t)}}}sendMessageToIframe(e){if(!this.iframeElement||!("contentWindow"in this.iframeElement)||!("postMessage"in this.iframeElement.contentWindow))return Promise.reject(new Error("invalid iframe element"));const{iframeOrigin:t}=this.config.iframe;return t?new Promise(((i,r)=>{const n=new MessageChannel;n.port1.onmessage=e=>{n.port1.close(),n.port2.close(),"resolve"===e.data.event?i(e.data):r(new Error(`iframe failed to handle message - ${e.data.error}`))},this.iframeElement.contentWindow.postMessage(e,t,[n.port2])})):Promise.reject(new Error("invalid iframe origin"))}toggleShowUiClass(){try{return this.containerElement.classList.toggle(`${this.containerClass}--show`),Promise.resolve()}catch(e){return Promise.reject(new Error(`failed to toggle show UI ${e}`))}}setSpaceForBubble(e=!1){try{return this.containerElement.classList.toggle("add-space-for-bubble",e),Promise.resolve()}catch(e){return Promise.reject(new Error(`failed to toggle space for bubble ${e}`))}}toggleMinimizeUiClass(){try{return this.containerElement.classList.toggle(`${this.containerClass}--minimize`),this.containerElement.classList.contains(`${this.containerClass}--minimize`)?localStorage.setItem(`${this.config.ui.toolbarTitle}lastUiIsMinimized`,"true"):localStorage.setItem(`${this.config.ui.toolbarTitle}lastUiIsMinimized`,"false"),Promise.resolve()}catch(e){return Promise.reject(new Error(`failed to toggle minimize UI ${e}`))}}toggleMaximizeUiClass(){try{return this.containerElement.classList.toggle(`${this.containerClass}--maximize`),this.containerElement.classList.contains(`${this.containerClass}--maximize`)?localStorage.setItem(`${this.config.ui.toolbarTitle}lastUiIsMaximized`,"true"):localStorage.setItem(`${this.config.ui.toolbarTitle}lastUiIsMaximized`,"false"),Promise.resolve()}catch(e){return Promise.reject(new Error(`failed to toggle maximize UI ${e}`))}}showIframe(){return Promise.resolve().then((()=>{this.config.iframe.shouldLoadIframeMinimized?(this.api.toggleMinimizeUi(),localStorage.setItem(`${this.config.ui.toolbarTitle}lastUiIsMinimized`,"true")):localStorage.getItem(`${this.config.ui.toolbarTitle}lastUiIsMinimized`)&&"true"===localStorage.getItem(`${this.config.ui.toolbarTitle}lastUiIsMinimized`)?this.api.toggleMinimizeUi():localStorage.getItem(`${this.config.ui.toolbarTitle}lastUiIsMinimized`)&&"false"===localStorage.getItem(`${this.config.ui.toolbarTitle}lastUiIsMinimized`)&&this.api.ping()})).then((()=>this.toggleShowUiClass()))}onMessageToIframe(e){return e&&"detail"in e&&e.detail&&"message"in e.detail?this.sendMessageToIframe(e.detail.message):Promise.reject(new Error("malformed message to iframe event"))}initParentToIframeApi(){return this.api={MESSAGE_TYPE_HUMAN:"human",MESSAGE_TYPE_BUTTON:"button",ping:()=>this.sendMessageToIframe({event:"ping"}),sendParentReady:()=>this.sendMessageToIframe({event:"parentReady"}),toggleMinimizeUi:()=>this.sendMessageToIframe({event:"toggleMinimizeUi"}),toggleIsUiExpanded:()=>this.sendMessageToIframe({event:"toggleIsUiExpanded"}),postText:(e,t)=>this.sendMessageToIframe({event:"postText",message:e,messageType:t}),deleteSession:()=>this.sendMessageToIframe({event:"deleteSession"}),startNewSession:()=>this.sendMessageToIframe({event:"startNewSession"}),setSessionAttribute:(e,t)=>this.sendMessageToIframe({event:"setSessionAttribute",key:e,value:t}),setClientContext:(e)=>this.sendMessageToIframe({event:"setClientContext",clientContext:e})},Promise.resolve().then((()=>{document.addEventListener("lexWebUiMessage",this.onMessageToIframe.bind(this),!1)})).then((()=>this.api.sendParentReady())).then((()=>{document.dispatchEvent(new CustomEvent("lexWebUiReady"))}))}static mergeConfig(e,i={}){function r(e){return"number"!=typeof e&&"boolean"!=typeof e&&(null==e||(void 0!==e.length?0===e.length:0===Object.keys(e).length))}return r(i)?{...e}:Object.keys(e).map((n=>{const o={};let s=e[n];return n in i&&!r(i[n])&&(s="object"==typeof e[n]?{...t.mergeConfig(i[n],e[n]),...t.mergeConfig(e[n],i[n])}:i[n]),o[n]=s,o})).reduce(((e,t)=>({...e,...t})),{})}}e.exports=t},203:(e,t,i)=>{const r=i(40);class n{constructor(e){const{baseUrl:t}=e;this.options=e,this.options.baseUrl=this.options.baseUrl&&"/"===t[t.length-1]?this.options.baseUrl:`${this.options.baseUrl}/`}load(e={}){return this.config=r.mergeConfig(this.config,e),this.compLoader.load(this.config)}}const o={baseUrl:"/",configEventTimeoutInMs:1e4,configUrl:"./lex-web-ui-loader-config.json",shouldIgnoreConfigWhenEmbedded:!0,shouldLoadConfigFromEvent:!1,shouldLoadConfigFromJsonFile:!0,shouldLoadMinDeps:!1,elementId:"lex-web-ui-iframe",containerClass:"lex-web-ui-iframe",iframeSrcPath:"/index.html#/?lexWebUiEmbed=true",shouldLoadConfigFromEvent:!0,shouldLoadMinDeps:!1},s={ui:{parentOrigin:""},iframe:{iframeOrigin:"",iframeSrcPath:""}},a={IframeLoader:class extends n{constructor(e={}){super({...o,...e}),this.config=s,this.compLoader=new r({config:this.config,containerClass:this.options.containerClass||"lex-web-ui",elementId:this.options.elementId||"lex-web-ui"})}load(e={}){return super.load(e).then((()=>{this.api=this.compLoader.api,this.config.iframe=this.config.iframe||{},this.config.iframe.iframeSrcPath=this.config.iframe.iframeSrcPath||this.mergeSrcPath(e)}))}mergeSrcPath(e){const{iframe:t}=e,i=t&&t.iframeSrcPath,{iframe:r}=this.config,n=r&&r.iframeSrcPath;return i||this.options.iframeSrcPath||n}}};e.exports=a}},t={},i=function i(r){var n=t[r];if(void 0!==n)return n.exports;var o=t[r]={exports:{}};return e[r](o,o.exports,i),o.exports}(203);ChatBotUiLoader=i})(); \ No newline at end of file From bdea72e2ac54f693229493a20fb3146677fee940 Mon Sep 17 00:00:00 2001 From: Hakim Cassimally Date: Thu, 16 Apr 2026 15:28:13 +0100 Subject: [PATCH 3/7] set clientContext based on page metadata --- src/js/12-chatbox.js | 52 +++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/src/js/12-chatbox.js b/src/js/12-chatbox.js index ccf7b4c1..d1159091 100644 --- a/src/js/12-chatbox.js +++ b/src/js/12-chatbox.js @@ -1,8 +1,9 @@ ;(function () { var iframeLoader = new window.ChatBotUiLoader.IframeLoader() - const iframeOrigin = document.head.querySelector( - 'meta[name="page-chatbot-origin"]')?.content || + const qs = (q) => document.head.querySelector(q)?.content + + const iframeOrigin = qs('meta[name="page-chatbot-origin"]') || 'https://d2sozpdiqok6m4.cloudfront.net' const origin = window.parent.origin @@ -36,29 +37,44 @@ }, } + const clientContext = { + product: { + value: qs('meta[name="docsearch:component"]'), + description: qs('meta[name="docsearch:component_title"]'), + }, + surface: { value: 'docs', description: 'Couchbase Docs' }, + service: { + value: qs('meta[name="docsearch:component"]'), + description: qs('meta[name="docsearch:component_title"]'), + }, + page: { + route: { + value: qs('meta[name="page-url"]'), + description: qs('meta[name="docsearch:breadcrumbs"]'), + }, + description: qs('meta[name="docsearch:breadcrumbs"]'), + }, + component: { + id: { + value: qs('meta[name="docsearch:component"]'), + description: qs('meta[name="docsearch:component_title"]'), + }, + edition: { value: qs('meta[name="docsearch:edition"]') }, + version: { value: qs('meta[name="docsearch:cversion"]') }, + description: qs('meta[name="docsearch:component_title"]'), + }, + } + // load the iframe iframeLoader .load(chatbotUiconfig) .then(function () { iframeLoader.api.ping() + // perform actions on the parent dependent on the chatbot loading. - // document.getElementById('send-intent').setAttribute('disabled', false) + iframeLoader.api.setClientContext(clientContext) - iframeLoader.api.setClientContext({ - product: { value: 'capella', description: 'Couchbase Capella' }, - surface: { value: 'docs', description: 'Couchbase Docs' }, - service: { value: 'query', description: 'N1QL query service' }, - page: { - route: { value: '/query', description: 'Query workbench' }, - description: 'Query execution and tuning page', - }, - component: { - id: { value: 'server', description: 'Couchbase Server' }, - edition: { value: 'enterprise', description: 'Edition' }, - version: { value: '7.6.2', description: 'Server version' }, - description: 'Target runtime component', - }, - }) + // document.getElementById('send-intent').setAttribute('disabled', false) }) .catch(function (error) { console.error('chatbot UI failed to load', error, iframeOrigin) From 44ecfbc1715c7b77ea98b7e988e7f500d970a320 Mon Sep 17 00:00:00 2001 From: Hakim Cassimally Date: Thu, 30 Apr 2026 15:09:12 +0100 Subject: [PATCH 4/7] Improve `docsearch:breadcrumbs` meta --- src/partials/crumbs-meta.hbs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/partials/crumbs-meta.hbs b/src/partials/crumbs-meta.hbs index 780b67e1..45e5466c 100644 --- a/src/partials/crumbs-meta.hbs +++ b/src/partials/crumbs-meta.hbs @@ -1,8 +1,11 @@ {{!-- NOTE add entry for current component, but not the home component or if the title matches the first breadcrumb. Logic extracted from crumbs.hbs --}} -{{~#unless (eq page.component.name 'home')~}} -{{~#unless (or (eq page.breadcrumbs.0.content page.component.title) (eq page.breadcrumbs.0.content page.componentVersion.title))~}} - {{{page.component.title}}} -{{~/unless~}} -{{~/unless~}} -{{~#each page.breadcrumbs}} / {{{./content}}}{{~/each}} \ No newline at end of file +{{~#unless (or + (eq page.component.name 'home') + (eq page.breadcrumbs.0.content page.component.title) + (eq page.breadcrumbs.0.content page.componentVersion.title))~}} + {{{page.component.title}}} {{{page.componentVersion.version}}} / {{/unless~}} +{{~#each page.breadcrumbs}} + {{~#unless @first}} / {{/unless~}} + {{{content}}} +{{~/each}} \ No newline at end of file From f0aa4f1a18c02fa0c2ac5494c17df0948ffa5e89 Mon Sep 17 00:00:00 2001 From: Hakim Cassimally Date: Thu, 30 Apr 2026 15:29:21 +0100 Subject: [PATCH 5/7] update ask-ai payload --- src/js/12-chatbox.js | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/js/12-chatbox.js b/src/js/12-chatbox.js index d1159091..20974386 100644 --- a/src/js/12-chatbox.js +++ b/src/js/12-chatbox.js @@ -39,28 +39,45 @@ const clientContext = { product: { - value: qs('meta[name="docsearch:component"]'), - description: qs('meta[name="docsearch:component_title"]'), + value: qs('meta[name="docsearch:component_title"]'), + description: 'The specific product the user is reading about', + }, + surface: { + value: 'docs', + description: 'The user is reading the Couchbase Docs', }, - surface: { value: 'docs', description: 'Couchbase Docs' }, service: { - value: qs('meta[name="docsearch:component"]'), - description: qs('meta[name="docsearch:component_title"]'), + value: qs('meta[name="docsearch:component_title"]'), + description: 'The specific product the user is reading about.', }, page: { route: { value: qs('meta[name="page-url"]'), - description: qs('meta[name="docsearch:breadcrumbs"]'), + description: 'The URL of the current page the user is reading', + }, + breadcrumbs: { + value: qs('meta[name="docsearch:breadcrumbs"]'), + description: 'The navigation path to the current page', }, - description: qs('meta[name="docsearch:breadcrumbs"]'), + title: { + value: qs('title'), + description: 'The title of the current page the user is reading', + }, + description: 'The specific page the user is on, which can give context for their query', }, component: { id: { value: qs('meta[name="docsearch:component"]'), - description: qs('meta[name="docsearch:component_title"]'), + description: `The user is reading about ${qs('meta[name="docsearch:component_title"]')}`, + }, + edition: { + value: qs('meta[name="docsearch:edition"]'), + description: 'The edition (e.g. Enterprise or Community) of the product the user is reading about', + }, + version: { + value: qs('meta[name="docsearch:cversion"]'), + description: 'The version of the product the user is reading about', }, - edition: { value: qs('meta[name="docsearch:edition"]') }, - version: { value: qs('meta[name="docsearch:cversion"]') }, description: qs('meta[name="docsearch:component_title"]'), }, } From 24d29a590df894818435ca3df7e06c1a66a614e5 Mon Sep 17 00:00:00 2001 From: Hakim Cassimally Date: Thu, 30 Apr 2026 16:13:40 +0100 Subject: [PATCH 6/7] fix title extraction --- src/js/12-chatbox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/12-chatbox.js b/src/js/12-chatbox.js index 20974386..1d014fc7 100644 --- a/src/js/12-chatbox.js +++ b/src/js/12-chatbox.js @@ -60,7 +60,7 @@ description: 'The navigation path to the current page', }, title: { - value: qs('title'), + value: document.head.querySelector('title')?.innerHTML, description: 'The title of the current page the user is reading', }, description: 'The specific page the user is on, which can give context for their query', From 67e27c08e28faff071a93c39529c4b3b931caf64 Mon Sep 17 00:00:00 2001 From: Hakim Cassimally Date: Fri, 1 May 2026 15:20:36 +0100 Subject: [PATCH 7/7] DOC-14221 fix some descriptions --- src/js/12-chatbox.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/js/12-chatbox.js b/src/js/12-chatbox.js index 1d014fc7..19feba23 100644 --- a/src/js/12-chatbox.js +++ b/src/js/12-chatbox.js @@ -67,8 +67,8 @@ }, component: { id: { - value: qs('meta[name="docsearch:component"]'), - description: `The user is reading about ${qs('meta[name="docsearch:component_title"]')}`, + value: qs('meta[name="docsearch:component_title"]'), + description: 'The component that the user is reading about', }, edition: { value: qs('meta[name="docsearch:edition"]'), @@ -78,7 +78,7 @@ value: qs('meta[name="docsearch:cversion"]'), description: 'The version of the product the user is reading about', }, - description: qs('meta[name="docsearch:component_title"]'), + description: 'The component that the user is reading about', }, }