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