Page Chatbot
A floating AI chatbot that can answer questions about the current page.
Code Snippet
1// ==============================================
2// 🤖 UNIVERSAL PAGE CHATBOT (Widget SDK)
3// ==============================================
4
5const logoBase64 = "data:image/png;base64,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";
6// --- STATE ---
7let chatHistory = [
8 { role: "assistant", text: "Hi! I'm your AI Page Assistant. Ask me anything about this page." }
9];
10let isThinking = false;
11let messageCount = 0;
12let pageSummary = "";
13
14// --- UI COMPONENTS ---
15/** @returns {WidgetComponent[]} */
16function chatView() {
17 /** @type {WidgetComponent[]} */
18 const chatBubbles = chatHistory.map((msg) => ({
19 type: "row",
20 justify: msg.role === "user" ? "end" : "start",
21 components: [
22 {
23 type: "column", gap: "sm", padding: msg.role === "assistant" ? "md" : "none",
24 bg: msg.role === "assistant" ? "card2" : "transparent", rounded: true,
25 components: [
26 {
27 type: "text",
28 text: msg.role === "user" ? "You" : "AI",
29 size: "sm", weight: "bold",
30 color: msg.role === "user" ? "accent" : "muted"
31 },
32 { type: "text", text: msg.text, size: "md", markdown: msg.role === "assistant" }
33 ]
34 }
35 ]
36 }));
37
38 return [
39 {
40 type: "column", padding: "sm", gap: "md",
41 components: [
42 // Header
43 {
44 type: "row", align: "center", justify: "between",
45 components: [
46 {
47 type: "row", align: "center", gap: "sm",
48 components: [
49 { type: "image", src: logoBase64, width: "24px", height: "24px", rounded: true },
50 ]
51 },
52 ]
53 },
54 { type: "divider" },
55
56 // Chat Area
57 {
58 type: "column", gap: "md", scroll: true, maxHeight: "350px",
59 components: [
60 ...chatBubbles,
61 ...(isThinking ? [
62 {
63 type: "row", align: "center", gap: "sm", padding: "sm",
64 components: [
65 { type: "text", text: "AI is thinking...", size: "sm", color: "muted" }
66 ]
67 }
68 ] : [])
69 ]
70 },
71 { type: "divider" },
72
73 // Input Area
74 {
75 type: "row", gap: "sm", align: "center",
76 components: [
77 { type: "input", id: "chatInput", placeholder: "Ask a question..." },
78 { type: "button", id: "sendBtn", icon: "Send", variant: "primary" }
79 ]
80 }
81 ]
82 }
83 ];
84}
85
86async function renderUI() {
87 await widget.show(chatView(), "bottom-right", { hideCloseButton: false });
88}
89
90// --- CORE LOGIC ---
91async function handleSend() {
92 const userText = await widget.getValue("chatInput");
93 if (!userText || !userText.trim()) return;
94
95 chatHistory.push({ role: "user", text: userText });
96 isThinking = true;
97 await renderUI();
98
99 if (messageCount >= 5) {
100 chatHistory.push({ role: "assistant", text: "Liked it? You can make it your own! Upgrade to tee Premium to unlock unlimited messages, custom UI, and the ability to edit this exact script." });
101 isThinking = false;
102 await renderUI();
103 return;
104 }
105
106 try {
107 if (!pageSummary) {
108 const rawText = await page.getText();
109 pageSummary = await ai.prompt(
110 "Summarize the main content, context, and key details of this webpage to be used as context for answering user questions later. \n\n" + rawText.substring(0, 15000),
111 "You are a helpful summarization assistant."
112 );
113 }
114
115 let prompt = "You are a helpful AI assistant residing on a webpage. Use the following webpage content to answer the user's latest question. Be concise.\n\nPAGE CONTENT:\n" + pageSummary + "\n\nCHAT HISTORY:\n";
116 for (const msg of chatHistory) {
117 prompt += msg.role.toUpperCase() + ": " + msg.text + "\n";
118 }
119 prompt += "ASSISTANT: ";
120
121 const response = await ai.prompt(prompt);
122 messageCount++;
123 chatHistory.push({ role: "assistant", text: response });
124 } catch (err) {
125 chatHistory.push({ role: "assistant", text: "Error: " + err.message });
126 }
127
128 isThinking = false;
129 await renderUI();
130}
131
132// --- EVENT BINDING ENGINE ---
133widget.on("sendBtn", "click", async () => {
134 await handleSend();
135});
136
137widget.on("chatInput", "enter", async () => {
138 await handleSend();
139});
140
141// --- LAUNCH ---
142await renderUI();
143await widget.waitForEvent("FOREVER_EVENT", "click");Submitted on 7/5/2026