bloby-bot 0.22.1 → 0.22.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-bloby/assets/{bloby-8izT4vax.js → bloby-DvSkie1b.js} +7 -5
- package/dist-bloby/assets/{highlighted-body-OFNGDK62-C3HbR2K9.js → highlighted-body-OFNGDK62-BbEkUgTM.js} +1 -1
- package/dist-bloby/assets/mermaid-GHXKKRXX-C0VBooMz.js +1 -0
- package/dist-bloby/bloby.html +1 -1
- package/package.json +5 -5
- package/supervisor/chat/src/hooks/useBlobyChat.ts +40 -1
- package/supervisor/index.ts +1 -1
- package/workspace/skills/chrome-extension/SKILL.md +48 -0
- package/workspace/skills/chrome-extension/background.js +77 -134
- package/workspace/skills/chrome-extension/content-script.js +156 -65
- package/workspace/skills/chrome-extension/manifest.json +3 -2
- package/workspace/skills/chrome-extension/panel/panel.html +4 -42
- package/workspace/skills/chrome-extension/panel/panel.js +123 -0
- package/dist-bloby/assets/mermaid-GHXKKRXX-BX4zpcbf.js +0 -1
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* Injected into every page. Responsibilities:
|
|
5
5
|
* - Renders the Bloby bubble (bottom-right corner)
|
|
6
6
|
* - Opens/closes the chat panel (iframe to extension's panel.html)
|
|
7
|
-
* - Extracts page context (URL, title, selection
|
|
7
|
+
* - Extracts page context (URL, title, selection, structured data)
|
|
8
|
+
* - Handles ExtensionAction commands from the agent (screenshot, fill form, type)
|
|
8
9
|
* - Bridges messages between the panel iframe and the background worker
|
|
9
10
|
*/
|
|
10
11
|
|
|
@@ -13,10 +14,6 @@
|
|
|
13
14
|
if (document.getElementById('bloby-ext-bubble')) return;
|
|
14
15
|
if (location.hostname.endsWith('bloby.bot')) return;
|
|
15
16
|
|
|
16
|
-
const BUBBLE_SIZE = 60;
|
|
17
|
-
const BUBBLE_MARGIN = 24;
|
|
18
|
-
const PANEL_WIDTH = 420;
|
|
19
|
-
|
|
20
17
|
let panelOpen = false;
|
|
21
18
|
let paired = false;
|
|
22
19
|
|
|
@@ -30,7 +27,20 @@
|
|
|
30
27
|
}
|
|
31
28
|
});
|
|
32
29
|
|
|
33
|
-
// Listen for pairing
|
|
30
|
+
// Listen for pairing from background worker
|
|
31
|
+
chrome.runtime.onMessage.addListener((msg) => {
|
|
32
|
+
if (msg.type === 'bloby:paired') {
|
|
33
|
+
console.log('[bloby-ext] Paired! Showing bubble.');
|
|
34
|
+
paired = true;
|
|
35
|
+
if (!document.getElementById('bloby-ext-bubble')) createBubble();
|
|
36
|
+
}
|
|
37
|
+
if (msg.type === 'bloby:unpaired') {
|
|
38
|
+
paired = false;
|
|
39
|
+
removeBubble();
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Listen for pairing changes via storage
|
|
34
44
|
chrome.storage.onChanged.addListener((changes) => {
|
|
35
45
|
if (changes.serverUrl) {
|
|
36
46
|
if (changes.serverUrl.newValue) {
|
|
@@ -52,48 +62,37 @@
|
|
|
52
62
|
bubble.addEventListener('click', togglePanel);
|
|
53
63
|
document.body.appendChild(bubble);
|
|
54
64
|
|
|
55
|
-
// Inner dot (gradient)
|
|
56
65
|
const dot = document.createElement('div');
|
|
57
66
|
dot.className = 'bloby-ext-bubble-dot';
|
|
58
67
|
bubble.appendChild(dot);
|
|
59
68
|
}
|
|
60
69
|
|
|
61
70
|
function removeBubble() {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const backdrop = document.getElementById('bloby-ext-backdrop');
|
|
67
|
-
if (backdrop) backdrop.remove();
|
|
71
|
+
document.getElementById('bloby-ext-bubble')?.remove();
|
|
72
|
+
document.getElementById('bloby-ext-panel')?.remove();
|
|
73
|
+
document.getElementById('bloby-ext-backdrop')?.remove();
|
|
74
|
+
panelOpen = false;
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
// ── Panel ──────────────────────────────────────────────────────────────────
|
|
71
78
|
|
|
72
79
|
function togglePanel() {
|
|
73
|
-
|
|
74
|
-
closePanel();
|
|
75
|
-
} else {
|
|
76
|
-
openPanel();
|
|
77
|
-
}
|
|
80
|
+
panelOpen ? closePanel() : openPanel();
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
function openPanel() {
|
|
81
84
|
if (document.getElementById('bloby-ext-panel')) {
|
|
82
|
-
|
|
83
|
-
panel.classList.add('bloby-ext-panel-open');
|
|
85
|
+
document.getElementById('bloby-ext-panel').classList.add('bloby-ext-panel-open');
|
|
84
86
|
panelOpen = true;
|
|
85
87
|
showBackdrop();
|
|
86
88
|
return;
|
|
87
89
|
}
|
|
88
90
|
|
|
89
|
-
// Create backdrop
|
|
90
91
|
showBackdrop();
|
|
91
92
|
|
|
92
|
-
// Create panel
|
|
93
93
|
const panel = document.createElement('div');
|
|
94
94
|
panel.id = 'bloby-ext-panel';
|
|
95
95
|
|
|
96
|
-
// Create iframe — loads the extension's own panel page
|
|
97
96
|
const iframe = document.createElement('iframe');
|
|
98
97
|
iframe.id = 'bloby-ext-iframe';
|
|
99
98
|
iframe.src = chrome.runtime.getURL('panel/panel.html');
|
|
@@ -102,13 +101,11 @@
|
|
|
102
101
|
|
|
103
102
|
document.body.appendChild(panel);
|
|
104
103
|
|
|
105
|
-
// Trigger slide-in animation on next frame
|
|
106
104
|
requestAnimationFrame(() => {
|
|
107
105
|
panel.classList.add('bloby-ext-panel-open');
|
|
108
106
|
panelOpen = true;
|
|
109
107
|
});
|
|
110
108
|
|
|
111
|
-
// Listen for messages from the panel iframe
|
|
112
109
|
window.addEventListener('message', handlePanelMessage);
|
|
113
110
|
}
|
|
114
111
|
|
|
@@ -134,49 +131,36 @@
|
|
|
134
131
|
|
|
135
132
|
function hideBackdrop() {
|
|
136
133
|
const backdrop = document.getElementById('bloby-ext-backdrop');
|
|
137
|
-
if (backdrop)
|
|
138
|
-
backdrop.classList.remove('bloby-ext-backdrop-visible');
|
|
139
|
-
}
|
|
134
|
+
if (backdrop) backdrop.classList.remove('bloby-ext-backdrop-visible');
|
|
140
135
|
}
|
|
141
136
|
|
|
142
|
-
// ──
|
|
137
|
+
// ── Message Bridge (panel ↔ content script) ────────────────────────────────
|
|
143
138
|
|
|
144
139
|
function handlePanelMessage(event) {
|
|
145
|
-
|
|
140
|
+
const iframe = document.getElementById('bloby-ext-iframe');
|
|
141
|
+
if (!iframe || event.source !== iframe.contentWindow) return;
|
|
146
142
|
|
|
147
143
|
const msg = event.data;
|
|
148
144
|
if (!msg?.type) return;
|
|
149
145
|
|
|
150
|
-
// Close panel request
|
|
151
146
|
if (msg.type === 'bloby:close') {
|
|
152
147
|
closePanel();
|
|
153
148
|
return;
|
|
154
149
|
}
|
|
155
150
|
|
|
156
|
-
// Forward chat messages to background worker → server
|
|
157
|
-
if (msg.type === 'bloby:send') {
|
|
158
|
-
chrome.runtime.sendMessage({ type: 'bloby:send', payload: msg.payload });
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
151
|
// Panel requests page context
|
|
163
152
|
if (msg.type === 'bloby:get-page-context') {
|
|
164
153
|
const context = getPageContext();
|
|
165
|
-
|
|
166
|
-
if (iframe?.contentWindow) {
|
|
167
|
-
iframe.contentWindow.postMessage({ type: 'bloby:page-context', context }, '*');
|
|
168
|
-
}
|
|
154
|
+
iframe.contentWindow.postMessage({ type: 'bloby:page-context', context }, '*');
|
|
169
155
|
return;
|
|
170
156
|
}
|
|
171
|
-
}
|
|
172
157
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
iframe.contentWindow.postMessage(msg, '*');
|
|
158
|
+
// Agent requests an action on the page
|
|
159
|
+
if (msg.type === 'bloby:extension-action') {
|
|
160
|
+
handleExtensionAction(msg.action, msg.data);
|
|
161
|
+
return;
|
|
178
162
|
}
|
|
179
|
-
}
|
|
163
|
+
}
|
|
180
164
|
|
|
181
165
|
// ── Page Context Extraction ────────────────────────────────────────────────
|
|
182
166
|
|
|
@@ -187,32 +171,139 @@
|
|
|
187
171
|
selection: window.getSelection()?.toString()?.slice(0, 2000) || '',
|
|
188
172
|
};
|
|
189
173
|
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const data = JSON.parse(
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
174
|
+
// Extract structured data (JSON-LD) — product info, articles, etc.
|
|
175
|
+
try {
|
|
176
|
+
const scripts = document.querySelectorAll('script[type="application/ld+json"]');
|
|
177
|
+
for (const script of scripts) {
|
|
178
|
+
const data = JSON.parse(script.textContent);
|
|
179
|
+
// Handle arrays (some sites wrap in array)
|
|
180
|
+
const items = Array.isArray(data) ? data : [data];
|
|
181
|
+
for (const item of items) {
|
|
182
|
+
if (item.name && !context.productName) context.productName = item.name;
|
|
183
|
+
if (item.offers?.price) context.price = item.offers.price;
|
|
184
|
+
if (item.offers?.priceCurrency) context.currency = item.offers.priceCurrency;
|
|
185
|
+
if (item.description && !context.description) context.description = item.description?.slice(0, 500);
|
|
186
|
+
if (item['@type'] === 'VideoObject' && item.name) context.videoTitle = item.name;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
} catch { /* ignore */ }
|
|
201
190
|
|
|
202
191
|
// Meta description fallback
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
context.description =
|
|
192
|
+
if (!context.description) {
|
|
193
|
+
const meta = document.querySelector('meta[name="description"]');
|
|
194
|
+
if (meta) context.description = meta.content?.slice(0, 500);
|
|
206
195
|
}
|
|
207
196
|
|
|
197
|
+
// Detect forms on the page
|
|
198
|
+
const forms = document.querySelectorAll('form');
|
|
199
|
+
if (forms.length > 0) {
|
|
200
|
+
context.hasForms = true;
|
|
201
|
+
context.formCount = forms.length;
|
|
202
|
+
// Extract visible form field labels/names
|
|
203
|
+
const fields = [];
|
|
204
|
+
forms.forEach((form) => {
|
|
205
|
+
form.querySelectorAll('input, select, textarea').forEach((el) => {
|
|
206
|
+
const name = el.getAttribute('name') || el.getAttribute('id') || el.getAttribute('placeholder') || '';
|
|
207
|
+
const type = el.getAttribute('type') || el.tagName.toLowerCase();
|
|
208
|
+
if (name && type !== 'hidden' && type !== 'submit') {
|
|
209
|
+
fields.push({ name, type });
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
if (fields.length > 0) context.formFields = fields.slice(0, 20); // cap at 20
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
console.log('[bloby-ext] Page context:', context);
|
|
208
217
|
return context;
|
|
209
218
|
}
|
|
210
219
|
|
|
220
|
+
// ── Extension Actions (agent → page) ───────────────────────────────────────
|
|
221
|
+
|
|
222
|
+
function handleExtensionAction(action, data) {
|
|
223
|
+
console.log(`[bloby-ext] Action: ${action}`, data);
|
|
224
|
+
const iframe = document.getElementById('bloby-ext-iframe');
|
|
225
|
+
|
|
226
|
+
switch (action) {
|
|
227
|
+
case 'screenshot': {
|
|
228
|
+
// Request screenshot via background worker (needs chrome.tabs API)
|
|
229
|
+
chrome.runtime.sendMessage({ type: 'bloby:screenshot' }, (response) => {
|
|
230
|
+
if (response?.dataUrl && iframe?.contentWindow) {
|
|
231
|
+
iframe.contentWindow.postMessage({
|
|
232
|
+
type: 'bloby:action-result',
|
|
233
|
+
action: 'screenshot',
|
|
234
|
+
result: response.dataUrl,
|
|
235
|
+
}, '*');
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
case 'read-page': {
|
|
242
|
+
// Extract main text content from the page
|
|
243
|
+
const text = document.body.innerText?.slice(0, 10000) || '';
|
|
244
|
+
if (iframe?.contentWindow) {
|
|
245
|
+
iframe.contentWindow.postMessage({
|
|
246
|
+
type: 'bloby:action-result',
|
|
247
|
+
action: 'read-page',
|
|
248
|
+
result: text,
|
|
249
|
+
}, '*');
|
|
250
|
+
}
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
case 'fill-form': {
|
|
255
|
+
// Fill form fields from agent-provided data
|
|
256
|
+
if (data?.fields && typeof data.fields === 'object') {
|
|
257
|
+
for (const [selector, value] of Object.entries(data.fields)) {
|
|
258
|
+
const el = document.querySelector(selector) ||
|
|
259
|
+
document.querySelector(`[name="${selector}"]`) ||
|
|
260
|
+
document.querySelector(`[id="${selector}"]`);
|
|
261
|
+
if (el) {
|
|
262
|
+
el.value = value;
|
|
263
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
264
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (iframe?.contentWindow) {
|
|
268
|
+
iframe.contentWindow.postMessage({
|
|
269
|
+
type: 'bloby:action-result',
|
|
270
|
+
action: 'fill-form',
|
|
271
|
+
result: 'done',
|
|
272
|
+
}, '*');
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
case 'click': {
|
|
279
|
+
// Click an element by selector
|
|
280
|
+
if (data?.selector) {
|
|
281
|
+
const el = document.querySelector(data.selector);
|
|
282
|
+
if (el) el.click();
|
|
283
|
+
}
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
case 'type': {
|
|
288
|
+
// Type into the currently focused input or a targeted selector
|
|
289
|
+
const target = data?.selector
|
|
290
|
+
? document.querySelector(data.selector)
|
|
291
|
+
: document.activeElement;
|
|
292
|
+
if (target && data?.text) {
|
|
293
|
+
target.value = data.text;
|
|
294
|
+
target.dispatchEvent(new Event('input', { bubbles: true }));
|
|
295
|
+
}
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
default:
|
|
300
|
+
console.warn(`[bloby-ext] Unknown action: ${action}`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
211
304
|
// ── Keyboard shortcut ──────────────────────────────────────────────────────
|
|
212
305
|
|
|
213
306
|
document.addEventListener('keydown', (e) => {
|
|
214
|
-
if (e.key === 'Escape' && panelOpen)
|
|
215
|
-
closePanel();
|
|
216
|
-
}
|
|
307
|
+
if (e.key === 'Escape' && panelOpen) closePanel();
|
|
217
308
|
});
|
|
218
309
|
})();
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
"description": "Your AI agent, on every page. Chat with Bloby from anywhere.",
|
|
6
6
|
"permissions": [
|
|
7
7
|
"storage",
|
|
8
|
-
"activeTab"
|
|
8
|
+
"activeTab",
|
|
9
|
+
"declarativeNetRequest"
|
|
9
10
|
],
|
|
10
11
|
"host_permissions": [
|
|
11
12
|
"https://*.bloby.bot/*",
|
|
@@ -34,7 +35,7 @@
|
|
|
34
35
|
],
|
|
35
36
|
"web_accessible_resources": [
|
|
36
37
|
{
|
|
37
|
-
"resources": ["panel/panel.html", "assets/*", "icons/*"],
|
|
38
|
+
"resources": ["panel/panel.html", "panel/panel.js", "assets/*", "icons/*"],
|
|
38
39
|
"matches": ["<all_urls>"]
|
|
39
40
|
}
|
|
40
41
|
],
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
.loading {
|
|
12
12
|
display: flex; align-items: center; justify-content: center;
|
|
13
13
|
height: 100vh; color: #a1a1aa; font-family: system-ui, sans-serif;
|
|
14
|
-
font-size: 14px;
|
|
14
|
+
font-size: 14px; flex-direction: column; gap: 8px;
|
|
15
15
|
}
|
|
16
16
|
.loading .dots span {
|
|
17
17
|
display: inline-block; width: 6px; height: 6px; border-radius: 50%;
|
|
@@ -23,52 +23,14 @@
|
|
|
23
23
|
0%, 80%, 100% { transform: scale(0); }
|
|
24
24
|
40% { transform: scale(1); }
|
|
25
25
|
}
|
|
26
|
+
.debug { font-size: 11px; color: #52525b; max-width: 90%; word-break: break-all; }
|
|
26
27
|
</style>
|
|
27
28
|
</head>
|
|
28
29
|
<body>
|
|
29
30
|
<div class="loading" id="loading">
|
|
30
31
|
<div class="dots"><span></span><span></span><span></span></div>
|
|
32
|
+
<div class="debug" id="debug"></div>
|
|
31
33
|
</div>
|
|
32
|
-
|
|
33
|
-
<script>
|
|
34
|
-
/**
|
|
35
|
-
* Panel page — loads the user's Bloby chat in an iframe.
|
|
36
|
-
* Reads serverUrl from chrome.storage.local.
|
|
37
|
-
*/
|
|
38
|
-
(async () => {
|
|
39
|
-
const data = await chrome.storage.local.get(['serverUrl', 'authToken']);
|
|
40
|
-
|
|
41
|
-
if (!data.serverUrl) {
|
|
42
|
-
document.getElementById('loading').innerHTML =
|
|
43
|
-
'<span style="color:#ef4444">Not paired. Click the Bloby icon in the toolbar to set up.</span>';
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Build the chat URL — same as the dashboard chat iframe
|
|
48
|
-
let chatUrl = `${data.serverUrl}/bloby/`;
|
|
49
|
-
if (data.authToken) {
|
|
50
|
-
chatUrl += `?token=${data.authToken}`;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Create iframe to the user's Bloby server
|
|
54
|
-
const iframe = document.createElement('iframe');
|
|
55
|
-
iframe.src = chatUrl;
|
|
56
|
-
iframe.allow = 'microphone';
|
|
57
|
-
iframe.style.cssText = 'width:100%;height:100%;border:none;';
|
|
58
|
-
|
|
59
|
-
iframe.onload = () => {
|
|
60
|
-
document.getElementById('loading').style.display = 'none';
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
document.body.appendChild(iframe);
|
|
64
|
-
|
|
65
|
-
// Forward close events from chat iframe to content script (parent of this panel)
|
|
66
|
-
window.addEventListener('message', (e) => {
|
|
67
|
-
if (e.data?.type === 'bloby:close') {
|
|
68
|
-
window.parent.postMessage({ type: 'bloby:close' }, '*');
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
})();
|
|
72
|
-
</script>
|
|
34
|
+
<script src="panel.js"></script>
|
|
73
35
|
</body>
|
|
74
36
|
</html>
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Panel page — loads the Bloby chat in an iframe and bridges page context.
|
|
3
|
+
*
|
|
4
|
+
* Architecture:
|
|
5
|
+
* Content script (host page) ↔ Panel (this page) ↔ Chat iframe (Bloby server)
|
|
6
|
+
*
|
|
7
|
+
* The panel:
|
|
8
|
+
* 1. Loads the Bloby chat in an iframe
|
|
9
|
+
* 2. Requests page context from the content script
|
|
10
|
+
* 3. Injects context into outgoing chat messages
|
|
11
|
+
* 4. Forwards ExtensionAction commands from chat to content script
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const debug = document.getElementById('debug');
|
|
15
|
+
function log(msg) {
|
|
16
|
+
console.log('[bloby-panel] ' + msg);
|
|
17
|
+
if (debug) debug.textContent = msg;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let chatIframe = null;
|
|
21
|
+
let currentPageContext = null;
|
|
22
|
+
|
|
23
|
+
(async () => {
|
|
24
|
+
try {
|
|
25
|
+
log('Reading config...');
|
|
26
|
+
const data = await chrome.storage.local.get(['serverUrl', 'authToken']);
|
|
27
|
+
|
|
28
|
+
if (!data.serverUrl) {
|
|
29
|
+
document.getElementById('loading').innerHTML =
|
|
30
|
+
'<span style="color:#ef4444">Not paired. Click the Bloby icon in the toolbar to set up.</span>';
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let chatUrl = data.serverUrl + '/bloby/';
|
|
35
|
+
if (data.authToken) {
|
|
36
|
+
chatUrl += '?token=' + data.authToken;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Add a marker so the chat app knows it's inside the extension
|
|
40
|
+
chatUrl += (chatUrl.includes('?') ? '&' : '?') + 'ext=1';
|
|
41
|
+
|
|
42
|
+
log('Loading: ' + chatUrl);
|
|
43
|
+
|
|
44
|
+
chatIframe = document.createElement('iframe');
|
|
45
|
+
chatIframe.id = 'bloby-chat-iframe';
|
|
46
|
+
chatIframe.src = chatUrl;
|
|
47
|
+
chatIframe.allow = 'microphone';
|
|
48
|
+
chatIframe.style.cssText = 'width:100%;height:100%;border:none;';
|
|
49
|
+
|
|
50
|
+
chatIframe.onload = () => {
|
|
51
|
+
log('Chat loaded!');
|
|
52
|
+
document.getElementById('loading').style.display = 'none';
|
|
53
|
+
// Request initial page context from content script
|
|
54
|
+
requestPageContext();
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
document.body.appendChild(chatIframe);
|
|
58
|
+
|
|
59
|
+
setTimeout(() => {
|
|
60
|
+
if (document.getElementById('loading').style.display !== 'none') {
|
|
61
|
+
log('Timeout loading chat');
|
|
62
|
+
}
|
|
63
|
+
}, 15000);
|
|
64
|
+
|
|
65
|
+
} catch (err) {
|
|
66
|
+
log('Error: ' + err.message);
|
|
67
|
+
}
|
|
68
|
+
})();
|
|
69
|
+
|
|
70
|
+
// ── Page Context ───────────────────────────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
function requestPageContext() {
|
|
73
|
+
// Ask the content script (parent of this panel) for page context
|
|
74
|
+
window.parent.postMessage({ type: 'bloby:get-page-context' }, '*');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ── Message Handling ───────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
window.addEventListener('message', (event) => {
|
|
80
|
+
const msg = event.data;
|
|
81
|
+
if (!msg?.type) return;
|
|
82
|
+
|
|
83
|
+
// Context response from content script
|
|
84
|
+
if (msg.type === 'bloby:page-context') {
|
|
85
|
+
currentPageContext = msg.context;
|
|
86
|
+
log('Page context received: ' + (msg.context?.url || 'none'));
|
|
87
|
+
|
|
88
|
+
// Forward to chat iframe so it can use it
|
|
89
|
+
if (chatIframe?.contentWindow) {
|
|
90
|
+
chatIframe.contentWindow.postMessage({
|
|
91
|
+
type: 'bloby:page-context',
|
|
92
|
+
context: msg.context,
|
|
93
|
+
}, '*');
|
|
94
|
+
}
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Action result from content script → forward to chat
|
|
99
|
+
if (msg.type === 'bloby:action-result') {
|
|
100
|
+
if (chatIframe?.contentWindow) {
|
|
101
|
+
chatIframe.contentWindow.postMessage(msg, '*');
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Close request from chat iframe → forward to content script
|
|
107
|
+
if (msg.type === 'bloby:close') {
|
|
108
|
+
window.parent.postMessage({ type: 'bloby:close' }, '*');
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Extension action from chat iframe → forward to content script
|
|
113
|
+
if (msg.type === 'bloby:extension-action') {
|
|
114
|
+
window.parent.postMessage(msg, '*');
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Chat requests fresh page context
|
|
119
|
+
if (msg.type === 'bloby:request-context') {
|
|
120
|
+
requestPageContext();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{i as e}from"./bloby-8izT4vax.js";export{e as Mermaid};
|