Skip to content

Commit a037373

Browse files
authored
Merge pull request #761 from aiden-cao/feat/replace-inkeep-with-superintern
feat: Add SuperIntern AI chat widget
2 parents 2194697 + 015d0fe commit a037373

5 files changed

Lines changed: 255 additions & 146 deletions

File tree

docs/assets/js/inkeep-custom-button.js

Lines changed: 0 additions & 104 deletions
This file was deleted.

docs/assets/js/superintern.js

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
(function() {
2+
'use strict';
3+
4+
// ==================== Configuration ====================
5+
var CONFIG = {
6+
apiKey: 'sk_live_vEFiRm_CGXO3AUsmOjcu1H7lEnk32J1zvrQhcY8M',
7+
agentId: '243f7652-5185-4d9f-974b-b5b0d1ce29d2',
8+
modeShortId: 'web-qa-bnb',
9+
primaryBrandColor: '#f0b90b',
10+
botAvatar: 'https://static.bnbchain.org/home-ui/static/images/logo.svg',
11+
botName: 'Ask BNB Chain AI',
12+
introMessage: 'How do I get started?',
13+
quickQuestions: [
14+
'How do I get BNB?',
15+
'How do I get started with RWA?',
16+
'How do I start building or deploying a dApp?',
17+
'Where can I find funding or grants?'
18+
],
19+
disclaimerText: 'My answers might not always be 100% accurate. Double-check when in doubt.',
20+
disclaimerLink: 'https://www.bnbchain.org/en/ai-bot-disclaimer',
21+
analyticsEvent: 'click_AiBot_floatBtn'
22+
};
23+
24+
// ==================== State ====================
25+
var sdkButton = null;
26+
27+
// ==================== Helper Functions ====================
28+
function findSdkButton() {
29+
var buttons = document.querySelectorAll('button');
30+
for (var i = 0; i < buttons.length; i++) {
31+
var btn = buttons[i];
32+
if (!btn.closest('.ai-bot-wrapper') && !btn.closest('dialog')) {
33+
var text = btn.textContent || '';
34+
if (text.indexOf('Ask AI') !== -1) {
35+
return btn;
36+
}
37+
}
38+
}
39+
return null;
40+
}
41+
42+
function triggerSdkButton() {
43+
if (!sdkButton) {
44+
sdkButton = findSdkButton();
45+
}
46+
if (sdkButton) {
47+
sdkButton.click();
48+
}
49+
}
50+
51+
function trackAnalytics() {
52+
if (window.dataLayer) {
53+
window.dataLayer.push({ event: CONFIG.analyticsEvent });
54+
}
55+
}
56+
57+
// ==================== Event Binding ====================
58+
function bindCustomButton() {
59+
var wrapper = document.querySelector('.ai-bot-wrapper');
60+
if (!wrapper) return;
61+
62+
// Skip if already bound
63+
if (wrapper._boundClick) return;
64+
65+
// Remove existing listeners by cloning
66+
var newWrapper = wrapper.cloneNode(true);
67+
wrapper.parentNode.replaceChild(newWrapper, wrapper);
68+
69+
// Re-select after clone
70+
newWrapper = document.querySelector('.ai-bot-wrapper');
71+
if (!newWrapper) return;
72+
73+
newWrapper.addEventListener('click', function(e) {
74+
e.preventDefault();
75+
e.stopPropagation();
76+
triggerSdkButton();
77+
trackAnalytics();
78+
});
79+
80+
// Mark as bound
81+
newWrapper._boundClick = true;
82+
}
83+
84+
// ==================== SDK Initialization ====================
85+
function initSuperIntern() {
86+
if (!window.SuperIntern) {
87+
console.warn('SuperIntern SDK not loaded');
88+
return;
89+
}
90+
91+
window.SuperIntern.ChatButton({
92+
baseSettings: {
93+
apiKey: CONFIG.apiKey,
94+
environment: 'production',
95+
agentId: CONFIG.agentId,
96+
modeShortId: CONFIG.modeShortId,
97+
primaryBrandColor: CONFIG.primaryBrandColor,
98+
theme: { colorMode: { type: 'dark' } }
99+
},
100+
aiChatSettings: {
101+
botAvatarSrcUrl: CONFIG.botAvatar,
102+
aiAssistantName: CONFIG.botName,
103+
introMessage: CONFIG.introMessage,
104+
quickQuestions: CONFIG.quickQuestions,
105+
disclaimer: {
106+
text: CONFIG.disclaimerText,
107+
link: CONFIG.disclaimerLink
108+
}
109+
},
110+
modalSettings: {
111+
onOpenChange: function(isOpen) {
112+
console.log('Modal:', isOpen ? 'opened' : 'closed');
113+
}
114+
},
115+
label: 'Ask AI',
116+
// Visually hide SDK button (still functional for click events)
117+
style: {
118+
position: 'absolute',
119+
width: '1px',
120+
height: '1px',
121+
padding: '0',
122+
margin: '-1px',
123+
overflow: 'hidden',
124+
clip: 'rect(0,0,0,0)',
125+
border: '0',
126+
opacity: '0'
127+
}
128+
});
129+
130+
// Cache SDK button reference
131+
setTimeout(function() {
132+
sdkButton = findSdkButton();
133+
bindCustomButton();
134+
}, 100);
135+
}
136+
137+
// ==================== SPA Navigation Support ====================
138+
function rebindAfterNavigation() {
139+
// Re-find SDK button (it may have been re-rendered)
140+
sdkButton = null;
141+
setTimeout(function() {
142+
sdkButton = findSdkButton();
143+
bindCustomButton();
144+
}, 100);
145+
// Double-check after a longer delay for slow page transitions
146+
setTimeout(function() {
147+
if (!sdkButton) sdkButton = findSdkButton();
148+
bindCustomButton();
149+
}, 500);
150+
setTimeout(function() {
151+
if (!sdkButton) sdkButton = findSdkButton();
152+
bindCustomButton();
153+
}, 1000);
154+
}
155+
156+
function setupSpaSupport() {
157+
// Handle pushState
158+
var originalPushState = history.pushState;
159+
history.pushState = function() {
160+
originalPushState.apply(this, arguments);
161+
rebindAfterNavigation();
162+
};
163+
164+
// Handle replaceState
165+
var originalReplaceState = history.replaceState;
166+
history.replaceState = function() {
167+
originalReplaceState.apply(this, arguments);
168+
rebindAfterNavigation();
169+
};
170+
171+
// Handle popstate (back/forward)
172+
window.addEventListener('popstate', rebindAfterNavigation);
173+
174+
// Handle hashchange
175+
window.addEventListener('hashchange', rebindAfterNavigation);
176+
177+
// MutationObserver for DOM changes (covers Discourse and other SPAs)
178+
var observer = new MutationObserver(function(mutations) {
179+
var shouldRebind = false;
180+
for (var i = 0; i < mutations.length; i++) {
181+
var mutation = mutations[i];
182+
// Check if our wrapper was removed or major DOM changes occurred
183+
if (mutation.removedNodes.length > 0) {
184+
for (var j = 0; j < mutation.removedNodes.length; j++) {
185+
var node = mutation.removedNodes[j];
186+
if (node.nodeType === 1 &&
187+
(node.classList && node.classList.contains('ai-bot-wrapper') ||
188+
node.querySelector && node.querySelector('.ai-bot-wrapper'))) {
189+
shouldRebind = true;
190+
break;
191+
}
192+
}
193+
}
194+
}
195+
if (shouldRebind) {
196+
rebindAfterNavigation();
197+
}
198+
});
199+
200+
observer.observe(document.body, {
201+
childList: true,
202+
subtree: true
203+
});
204+
205+
// Periodic check as fallback (every 2 seconds)
206+
setInterval(function() {
207+
var wrapper = document.querySelector('.ai-bot-wrapper');
208+
if (wrapper && !wrapper._boundClick) {
209+
rebindAfterNavigation();
210+
}
211+
}, 2000);
212+
}
213+
214+
// ==================== Bootstrap ====================
215+
function init() {
216+
if (window.SuperIntern) {
217+
initSuperIntern();
218+
} else {
219+
window.addEventListener('load', initSuperIntern);
220+
}
221+
setupSpaSupport();
222+
}
223+
224+
if (document.readyState === 'loading') {
225+
document.addEventListener('DOMContentLoaded', init);
226+
} else {
227+
init();
228+
}
229+
})();

0 commit comments

Comments
 (0)